From 0fb0ef6a1c367d64e8d6b52e823038d75a025e3c Mon Sep 17 00:00:00 2001 From: Serdar Sutay Date: Sat, 8 Nov 2014 22:45:28 -0800 Subject: Merge pull request #2336 from opscode/lcg/12-systemd-fixes fix systemd for Ubuntu 14.10 Conflicts: CHANGELOG.md spec/unit/provider/service/systemd_service_spec.rb --- CHANGELOG.md | 1 + lib/chef/mixin/which.rb | 37 +++ lib/chef/platform/provider_priority_map.rb | 5 +- lib/chef/platform/service_helpers.rb | 45 ++- lib/chef/provider/service/aixinit.rb | 2 +- lib/chef/provider/service/arch.rb | 2 +- lib/chef/provider/service/debian.rb | 6 +- lib/chef/provider/service/init.rb | 4 + lib/chef/provider/service/insserv.rb | 6 +- lib/chef/provider/service/invokercd.rb | 6 +- lib/chef/provider/service/redhat.rb | 6 +- lib/chef/provider/service/systemd.rb | 82 +++-- lib/chef/provider/service/upstart.rb | 7 +- lib/chef/provider_resolver.rb | 46 ++- lib/chef/resource.rb | 3 +- lib/chef/run_context.rb | 5 - lib/chef/util/selinux.rb | 12 +- .../unit/resource/static_provider_resolution.rb | 7 +- spec/unit/provider/service/systemd_service_spec.rb | 331 +++++++++++---------- spec/unit/provider_resolver_spec.rb | 185 +++++++++++- spec/unit/resource/timestamped_deploy_spec.rb | 37 +-- spec/unit/runner_spec.rb | 4 +- 22 files changed, 553 insertions(+), 286 deletions(-) create mode 100644 lib/chef/mixin/which.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index aed9f16163..0e99ff425b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -237,6 +237,7 @@ * The Windows env provider will delete elements even if they are only in ENV (and not in the registry) * Allow events to be logged to Windows Event Log * Fixed bug in env resource where a value containing the delimiter could never correctly match the existing values +* More intelligent service check for systemd on Ubuntu 14.10. ## 11.16.4 diff --git a/lib/chef/mixin/which.rb b/lib/chef/mixin/which.rb new file mode 100644 index 0000000000..4179c97b62 --- /dev/null +++ b/lib/chef/mixin/which.rb @@ -0,0 +1,37 @@ +#-- +# Author:: Lamont Granquist +# Copyright:: Copyright (c) 2010 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +class Chef + module Mixin + module Which + def which(cmd, opts = {}) + extra_path = + if opts[:extra_path].nil? + [ '/bin', '/usr/bin', '/sbin', '/usr/sbin' ] + else + [ opts[:extra_path] ].flatten + end + paths = ENV['PATH'].split(File::PATH_SEPARATOR) + extra_path + paths.each do |path| + filename = File.join(path, cmd) + return filename if File.executable?(filename) + end + false + end + end + end +end diff --git a/lib/chef/platform/provider_priority_map.rb b/lib/chef/platform/provider_priority_map.rb index ccf6ef0bbe..765dd74859 100644 --- a/lib/chef/platform/provider_priority_map.rb +++ b/lib/chef/platform/provider_priority_map.rb @@ -34,9 +34,10 @@ class Chef ], platform_family: "gentoo" priority :service, [ - # on debian-ish system if an upstart script exists that always wins - Chef::Provider::Service::Upstart, + # we can determine what systemd supports accurately Chef::Provider::Service::Systemd, + # on debian-ish system if an upstart script exists that must win over sysv types + Chef::Provider::Service::Upstart, Chef::Provider::Service::Insserv, Chef::Provider::Service::Debian, Chef::Provider::Service::Invokercd, diff --git a/lib/chef/platform/service_helpers.rb b/lib/chef/platform/service_helpers.rb index 440391843e..dc0a808c06 100644 --- a/lib/chef/platform/service_helpers.rb +++ b/lib/chef/platform/service_helpers.rb @@ -18,6 +18,7 @@ # XXX: mixing shellout into a mixin into classes has to be code smell require 'chef/mixin/shell_out' +require 'chef/mixin/which' class Chef class Platform @@ -25,12 +26,21 @@ class Chef class << self include Chef::Mixin::ShellOut + include Chef::Mixin::Which # This helper is mostly used to sort out the mess of different # linux mechanisms that can be used to start services. It does # not necessarily need to linux-specific, but currently all our # other service providers are narrowly platform-specific with no # alternatives. + # + # NOTE: if a system has (for example) chkconfig installed then we + # should report that chkconfig is installed. The fact that a system + # may also have systemd installed does not mean that we do not + # report that systemd is also installed. This module is purely for + # discovery of all the alternatives, handling the priority of the + # different services is NOT a design concern of this module. + # def service_resource_providers service_resource_providers = [] @@ -55,8 +65,7 @@ class Chef service_resource_providers << :redhat end - if ::File.exist?("/bin/systemctl") - # FIXME: look for systemd as init provider + if systemd_sanity_check? service_resource_providers << :systemd end @@ -86,7 +95,7 @@ class Chef configs << :usr_local_etc_rcd end - if ::File.exist?("/bin/systemctl") && platform_has_systemd_unit?(service_name) + if systemd_sanity_check? && platform_has_systemd_unit?(service_name) configs << :systemd end @@ -95,17 +104,37 @@ class Chef private - def extract_systemd_services(output) + def systemctl_path + if @systemctl_path.nil? + @systemctl_path = which("systemctl") + end + @systemctl_path + end + + def systemd_sanity_check? + systemctl_path && File.exist?("/proc/1/comm") && File.open("/proc/1/comm").gets.chomp == "systemd" + end + + def extract_systemd_services(command) + output = shell_out!(command).stdout # first line finds e.g. "sshd.service" - services = output.lines.split.map { |l| l.split[0] } + services = [] + output.each_line do |line| + fields = line.split + services << fields[0] if fields[1] == "loaded" || fields[1] == "not-found" + end # this splits off the suffix after the last dot to return "sshd" - services += services.map { |s| s.sub(/(.*)\..*/, '\1') } + services += services.select {|s| s.match(/\.service$/) }.map { |s| s.sub(/(.*)\.service$/, '\1') } + rescue Mixlib::ShellOut::ShellCommandFailed + false end def platform_has_systemd_unit?(service_name) - services = extract_systemd_services(shell_out!("systemctl --all").stdout) + - extract_systemd_services(shell_out!("systemctl --list-unit-files").stdout) + services = extract_systemd_services("#{systemctl_path} --all") + + extract_systemd_services("#{systemctl_path} list-unit-files") services.include?(service_name) + rescue Mixlib::ShellOut::ShellCommandFailed + false end end end diff --git a/lib/chef/provider/service/aixinit.rb b/lib/chef/provider/service/aixinit.rb index ab4b8e5406..19beac79f0 100644 --- a/lib/chef/provider/service/aixinit.rb +++ b/lib/chef/provider/service/aixinit.rb @@ -114,4 +114,4 @@ class Chef end end end -end \ No newline at end of file +end diff --git a/lib/chef/provider/service/arch.rb b/lib/chef/provider/service/arch.rb index 888fb3fdf5..e7fbcc820c 100644 --- a/lib/chef/provider/service/arch.rb +++ b/lib/chef/provider/service/arch.rb @@ -23,7 +23,7 @@ class Chef::Provider::Service::Arch < Chef::Provider::Service::Init provides :service, platform_family: "arch" def self.supports?(resource, action) - ::File.exist?("/etc/rc.d/#{resource.service_name}") + Chef::Platform::ServiceHelpers.config_for_service(resource.service_name).include?(:etc_rcd) end def initialize(new_resource, run_context) diff --git a/lib/chef/provider/service/debian.rb b/lib/chef/provider/service/debian.rb index 25b1960b26..01505924cb 100644 --- a/lib/chef/provider/service/debian.rb +++ b/lib/chef/provider/service/debian.rb @@ -27,8 +27,12 @@ class Chef provides :service, platform_family: "debian" + def self.provides?(node, resource) + super && Chef::Platform::ServiceHelpers.service_resource_providers.include?(:debian) + end + def self.supports?(resource, action) - Chef::Platform::ServiceHelpers.service_resource_providers.include?(:debian) + Chef::Platform::ServiceHelpers.config_for_service(resource.service_name).include?(:initd) end def load_current_resource diff --git a/lib/chef/provider/service/init.rb b/lib/chef/provider/service/init.rb index ab40a720f6..0a219a69e1 100644 --- a/lib/chef/provider/service/init.rb +++ b/lib/chef/provider/service/init.rb @@ -28,6 +28,10 @@ class Chef provides :service, os: "!windows" + def self.supports?(resource, action) + Chef::Platform::ServiceHelpers.config_for_service(resource.service_name).include?(:initd) + end + def initialize(new_resource, run_context) super @init_command = "/etc/init.d/#{@new_resource.service_name}" diff --git a/lib/chef/provider/service/insserv.rb b/lib/chef/provider/service/insserv.rb index df5a162a45..31965a4bc6 100644 --- a/lib/chef/provider/service/insserv.rb +++ b/lib/chef/provider/service/insserv.rb @@ -26,8 +26,12 @@ class Chef provides :service, os: "linux" + def self.provides?(node, resource) + super && Chef::Platform::ServiceHelpers.service_resource_providers.include?(:insserv) + end + def self.supports?(resource, action) - Chef::Platform::ServiceHelpers.service_resource_providers.include?(:insserv) + Chef::Platform::ServiceHelpers.config_for_service(resource.service_name).include?(:initd) end def load_current_resource diff --git a/lib/chef/provider/service/invokercd.rb b/lib/chef/provider/service/invokercd.rb index c7472211bc..5ff24e0dbb 100644 --- a/lib/chef/provider/service/invokercd.rb +++ b/lib/chef/provider/service/invokercd.rb @@ -25,8 +25,12 @@ class Chef provides :service, platform_family: "debian" + def self.provides?(node, resource) + super && Chef::Platform::ServiceHelpers.service_resource_providers.include?(:invokercd) + end + def self.supports?(resource, action) - Chef::Platform::ServiceHelpers.service_resource_providers.include?(:invokerc) + Chef::Platform::ServiceHelpers.config_for_service(resource.service_name).include?(:initd) end def initialize(new_resource, run_context) diff --git a/lib/chef/provider/service/redhat.rb b/lib/chef/provider/service/redhat.rb index 90744ae268..850953125e 100644 --- a/lib/chef/provider/service/redhat.rb +++ b/lib/chef/provider/service/redhat.rb @@ -28,8 +28,12 @@ class Chef provides :service, platform_family: [ "rhel", "fedora", "suse" ] + def self.provides?(node, resource) + super && Chef::Platform::ServiceHelpers.service_resource_providers.include?(:redhat) + end + def self.supports?(resource, action) - Chef::Platform::ServiceHelpers.service_resource_providers.include?(:redhat) + Chef::Platform::ServiceHelpers.config_for_service(resource.service_name).include?(:initd) end def initialize(new_resource, run_context) diff --git a/lib/chef/provider/service/systemd.rb b/lib/chef/provider/service/systemd.rb index 311751ab9a..9085ffde2e 100644 --- a/lib/chef/provider/service/systemd.rb +++ b/lib/chef/provider/service/systemd.rb @@ -18,87 +18,95 @@ require 'chef/resource/service' require 'chef/provider/service/simple' +require 'chef/mixin/which' class Chef::Provider::Service::Systemd < Chef::Provider::Service::Simple + include Chef::Mixin::Which + provides :service, os: "linux" + attr_accessor :status_check_success + + def self.provides?(node, resource) + super && Chef::Platform::ServiceHelpers.service_resource_providers.include?(:systemd) + end + def self.supports?(resource, action) - Chef::Platform::ServiceHelpers.service_resource_providers.include?(:systemd) + Chef::Platform::ServiceHelpers.config_for_service(resource.service_name).include?(:systemd) end def load_current_resource - @current_resource = Chef::Resource::Service.new(@new_resource.name) - @current_resource.service_name(@new_resource.service_name) + @current_resource = Chef::Resource::Service.new(new_resource.name) + current_resource.service_name(new_resource.service_name) @status_check_success = true - if @new_resource.status_command - Chef::Log.debug("#{@new_resource} you have specified a status command, running..") + if new_resource.status_command + Chef::Log.debug("#{new_resource} you have specified a status command, running..") - unless shell_out(@new_resource.status_command).error? - @current_resource.running(true) + unless shell_out(new_resource.status_command).error? + current_resource.running(true) else @status_check_success = false - @current_resource.running(false) - @current_resource.enabled(false) - nil + current_resource.running(false) + current_resource.enabled(false) end else - @current_resource.running(is_active?) + current_resource.running(is_active?) end - @current_resource.enabled(is_enabled?) - @current_resource + current_resource.enabled(is_enabled?) + current_resource end def define_resource_requirements shared_resource_requirements requirements.assert(:all_actions) do |a| - a.assertion { @status_check_success } + a.assertion { status_check_success } # We won't stop in any case, but in whyrun warn and tell what we're doing. - a.whyrun ["Failed to determine status of #{@new_resource}, using command #{@new_resource.status_command}.", + a.whyrun ["Failed to determine status of #{new_resource}, using command #{new_resource.status_command}.", "Assuming service would have been installed and is disabled"] end end def start_service - if @current_resource.running - Chef::Log.debug("#{@new_resource} already running, not starting") + if current_resource.running + Chef::Log.debug("#{new_resource} already running, not starting") else - if @new_resource.start_command + if new_resource.start_command super else - shell_out_with_systems_locale!("/bin/systemctl start #{@new_resource.service_name}") + shell_out_with_systems_locale!("#{systemctl_path} start #{new_resource.service_name}") end end end def stop_service - unless @current_resource.running - Chef::Log.debug("#{@new_resource} not running, not stopping") + unless current_resource.running + Chef::Log.debug("#{new_resource} not running, not stopping") else - if @new_resource.stop_command + if new_resource.stop_command super else - shell_out_with_systems_locale!("/bin/systemctl stop #{@new_resource.service_name}") + shell_out_with_systems_locale!("#{systemctl_path} stop #{new_resource.service_name}") end end end def restart_service - if @new_resource.restart_command + if new_resource.restart_command super else - shell_out_with_systems_locale!("/bin/systemctl restart #{@new_resource.service_name}") + shell_out_with_systems_locale!("#{systemctl_path} restart #{new_resource.service_name}") end end def reload_service - if @new_resource.reload_command + if new_resource.reload_command super else - if @current_resource.running - shell_out_with_systems_locale!("/bin/systemctl reload #{@new_resource.service_name}") + if current_resource.running + shell_out_with_systems_locale!("#{systemctl_path} reload #{new_resource.service_name}") else start_service end @@ -106,18 +114,28 @@ class Chef::Provider::Service::Systemd < Chef::Provider::Service::Simple end def enable_service - shell_out!("/bin/systemctl enable #{@new_resource.service_name}") + shell_out!("#{systemctl_path} enable #{new_resource.service_name}") end def disable_service - shell_out!("/bin/systemctl disable #{@new_resource.service_name}") + shell_out!("#{systemctl_path} disable #{new_resource.service_name}") end def is_active? - shell_out("/bin/systemctl is-active #{@new_resource.service_name} --quiet").exitstatus == 0 + shell_out("#{systemctl_path} is-active #{new_resource.service_name} --quiet").exitstatus == 0 end def is_enabled? - shell_out("/bin/systemctl is-enabled #{@new_resource.service_name} --quiet").exitstatus == 0 + shell_out("#{systemctl_path} is-enabled #{new_resource.service_name} --quiet").exitstatus == 0 end + + private + + def systemctl_path + if @systemctl_path.nil? + @systemctl_path = which("systemctl") + end + @systemctl_path + end + end diff --git a/lib/chef/provider/service/upstart.rb b/lib/chef/provider/service/upstart.rb index 41bd850d6a..3a3ddb2385 100644 --- a/lib/chef/provider/service/upstart.rb +++ b/lib/chef/provider/service/upstart.rb @@ -29,9 +29,12 @@ class Chef provides :service, os: "linux" + def self.provides?(node, resource) + super && Chef::Platform::ServiceHelpers.service_resource_providers.include?(:upstart) + end + def self.supports?(resource, action) - Chef::Platform::ServiceHelpers.service_resource_providers.include?(:upstart) && - Chef::Platform::ServiceHelpers.config_for_service(resource.service_name).include?(:upstart) + Chef::Platform::ServiceHelpers.config_for_service(resource.service_name).include?(:upstart) end # Upstart does more than start or stop a service, creating multiple 'states' [1] that a service can be in. diff --git a/lib/chef/provider_resolver.rb b/lib/chef/provider_resolver.rb index c819b0c87f..247102f191 100644 --- a/lib/chef/provider_resolver.rb +++ b/lib/chef/provider_resolver.rb @@ -23,22 +23,42 @@ class Chef class ProviderResolver attr_reader :node + attr_reader :resource + attr_reader :action - def initialize(node) + def initialize(node, resource, action) @node = node + @resource = resource + @action = action end # return a deterministically sorted list of Chef::Provider subclasses def providers - Chef::Provider.descendants.sort {|a,b| a.to_s <=> b.to_s } + @providers ||= Chef::Provider.descendants.sort {|a,b| a.to_s <=> b.to_s } end - def resolve(resource, action) + def resolve maybe_explicit_provider(resource) || maybe_dynamic_provider_resolution(resource, action) || maybe_chef_platform_lookup(resource) end + # this cut looks at if the provider can handle the resource type on the node + def enabled_handlers + @enabled_handlers ||= + providers.select do |klass| + klass.provides?(node, resource) + end + end + + # this cut looks at if the provider can handle the specific resource and action + def supported_handlers + @supported_handlers ||= + enabled_handlers.select do |klass| + klass.supports?(resource, action) + end + end + private # if resource.provider is set, just return one of those objects @@ -49,31 +69,23 @@ class Chef # try dynamically finding a provider based on querying the providers to see what they support def maybe_dynamic_provider_resolution(resource, action) - # this cut only depends on the node value and is going to be static for all nodes - # will contain all providers that could possibly support a resource on a node - enabled_handlers = providers.select do |klass| - klass.provides?(node, resource) - end - # log this so we know what providers will work for the generic resource on the node (early cut) Chef::Log.debug "providers for generic #{resource.resource_name} resource enabled on node include: #{enabled_handlers}" - # ask all the enabled providers if they can actually support the resource - supported_handlers = enabled_handlers.select do |klass| - klass.supports?(resource, action) - end - # what providers were excluded by machine state (late cut) Chef::Log.debug "providers that refused resource #{resource} were: #{enabled_handlers - supported_handlers}" Chef::Log.debug "providers that support resource #{resource} include: #{supported_handlers}" + # if none of the providers specifically support the resource, we still need to pick one of the providers that are + # enabled on the node to handle the why-run use case. handlers = supported_handlers.empty? ? enabled_handlers : supported_handlers + Chef::Log.debug "no providers supported the resource, falling back to enabled handlers" if supported_handlers.empty? if handlers.count >= 2 + # this magic stack ranks the providers by where they appear in the provider_priority_map, it is mostly used + # to pick amongst N different ways to start init scripts on different debian/ubuntu systems. priority_list = [ get_provider_priority_map(resource.resource_name, node) ].flatten.compact - handlers = handlers.sort_by { |x| i = priority_list.index x; i.nil? ? Float::INFINITY : i } - handlers = [ handlers.first ] end @@ -81,6 +93,8 @@ class Chef raise Chef::Exceptions::AmbiguousProviderResolution.new(resource, handlers) if handlers.count >= 2 + Chef::Log.debug "dynamic provider resolver FAILED to resolve a provider" if handlers.empty? + return nil if handlers.empty? handlers[0] diff --git a/lib/chef/resource.rb b/lib/chef/resource.rb index c38f36aa72..8d964da66d 100644 --- a/lib/chef/resource.rb +++ b/lib/chef/resource.rb @@ -29,6 +29,7 @@ require 'chef/resource/conditional_action_not_nothing' require 'chef/resource_collection' require 'chef/node_map' require 'chef/node' +require 'chef/provider_resolver' require 'chef/platform' require 'chef/mixin/deprecation' @@ -679,7 +680,7 @@ F end def provider_for_action(action) - provider = run_context.provider_resolver.resolve(self, action).new(self, run_context) + provider = Chef::ProviderResolver.new(node, self, action).resolve.new(self, run_context) provider.action = action provider end diff --git a/lib/chef/run_context.rb b/lib/chef/run_context.rb index 18d353ac61..1a2d7ba3a3 100644 --- a/lib/chef/run_context.rb +++ b/lib/chef/run_context.rb @@ -18,7 +18,6 @@ # limitations under the License. require 'chef/resource_collection' -require 'chef/provider_resolver' require 'chef/cookbook_version' require 'chef/node' require 'chef/role' @@ -51,9 +50,6 @@ class Chef # recipes, which is triggered by #load. (See also: CookbookCompiler) attr_accessor :resource_collection - # Chef::ProviderResolver for this run - attr_accessor :provider_resolver - # A Hash containing the immediate notifications triggered by resources # during the converge phase of the chef run. attr_accessor :immediate_notification_collection @@ -87,7 +83,6 @@ class Chef @node.run_context = self @cookbook_compiler = nil - @provider_resolver = Chef::ProviderResolver.new(@node) end # Triggers the compile phase of the chef run. Implemented by diff --git a/lib/chef/util/selinux.rb b/lib/chef/util/selinux.rb index 92d5756552..778da042e3 100644 --- a/lib/chef/util/selinux.rb +++ b/lib/chef/util/selinux.rb @@ -21,6 +21,7 @@ # limitations under the License. require 'chef/mixin/shell_out' +require 'chef/mixin/which' class Chef class Util @@ -32,6 +33,7 @@ class Chef module Selinux include Chef::Mixin::ShellOut + include Chef::Mixin::Which # We want to initialize below variables once during a # chef-client run therefore they are class variables. @@ -67,15 +69,6 @@ class Chef @@selinuxenabled_path end - def which(cmd) - paths = ENV['PATH'].split(File::PATH_SEPARATOR) + [ '/bin', '/usr/bin', '/sbin', '/usr/sbin' ] - paths.each do |path| - filename = File.join(path, cmd) - return filename if File.executable?(filename) - end - false - end - def check_selinux_enabled? if selinuxenabled_path cmd = shell_out!(selinuxenabled_path, :returns => [0,1]) @@ -97,4 +90,3 @@ class Chef end end end - diff --git a/spec/support/shared/unit/resource/static_provider_resolution.rb b/spec/support/shared/unit/resource/static_provider_resolution.rb index 147852598a..2bc4c70d95 100644 --- a/spec/support/shared/unit/resource/static_provider_resolution.rb +++ b/spec/support/shared/unit/resource/static_provider_resolution.rb @@ -43,12 +43,7 @@ def static_provider_resolution(opts={}) node } let(:events) { Chef::EventDispatch::Dispatcher.new } - let(:provider_resolver) { Chef::ProviderResolver.new(node) } - let(:run_context) { - run_context = Chef::RunContext.new(node, {}, events) - run_context.provider_resolver = provider_resolver - run_context - } + let(:run_context) { Chef::RunContext.new(node, {}, events) } let(:resource) { resource_class.new("foo", run_context) } it "should return a #{resource_class}" do diff --git a/spec/unit/provider/service/systemd_service_spec.rb b/spec/unit/provider/service/systemd_service_spec.rb index 2fed6bfd11..5ba3bed184 100644 --- a/spec/unit/provider/service/systemd_service_spec.rb +++ b/spec/unit/provider/service/systemd_service_spec.rb @@ -19,238 +19,253 @@ require 'spec_helper' describe Chef::Provider::Service::Systemd do + + let(:node) { Chef::Node.new } + + let(:events) { Chef::EventDispatch::Dispatcher.new } + + let(:run_context) { Chef::RunContext.new(node, {}, events) } + + let(:service_name) { "rsyslog.service" } + + let(:new_resource) { Chef::Resource::Service.new(service_name) } + + let(:provider) { Chef::Provider::Service::Systemd.new(new_resource, run_context) } + + let(:shell_out_success) do + double('shell_out_with_systems_locale', :exitstatus => 0, :error? => false) + end + + let(:shell_out_failure) do + double('shell_out_with_systems_locale', :exitstatus => 1, :error? => true) + end + + let(:current_resource) { Chef::Resource::Service.new(service_name) } + before(:each) do - @node = Chef::Node.new - @events = Chef::EventDispatch::Dispatcher.new - @run_context = Chef::RunContext.new(@node, {}, @events) - @new_resource = Chef::Resource::Service.new('rsyslog.service') - @provider = Chef::Provider::Service::Systemd.new(@new_resource, @run_context) - - @shell_out_success = double('shell_out_with_systems_locale', - :exitstatus => 0, :error? => false) - @shell_out_failure = double('shell_out_with_systems_locale', - :exitstatus => 1, :error? => true) + allow(Chef::Resource::Service).to receive(:new).with(service_name).and_return(current_resource) end describe "load_current_resource" do - before(:each) do - @current_resource = Chef::Resource::Service.new('rsyslog.service') - allow(Chef::Resource::Service).to receive(:new).and_return(@current_resource) - allow(@provider).to receive(:is_active?).and_return(false) - allow(@provider).to receive(:is_enabled?).and_return(false) + before(:each) do + allow(provider).to receive(:is_active?).and_return(false) + allow(provider).to receive(:is_enabled?).and_return(false) end it "should create a current resource with the name of the new resource" do - expect(Chef::Resource::Service).to receive(:new).and_return(@current_resource) - @provider.load_current_resource + expect(Chef::Resource::Service).to receive(:new).with(new_resource.name).and_return(current_resource) + provider.load_current_resource end it "should set the current resources service name to the new resources service name" do - expect(@current_resource).to receive(:service_name).with(@new_resource.service_name) - @provider.load_current_resource + provider.load_current_resource + expect(current_resource.service_name).to eql(service_name) end it "should check if the service is running" do - expect(@provider).to receive(:is_active?) - @provider.load_current_resource + expect(provider).to receive(:is_active?) + provider.load_current_resource end it "should set running to true if the service is running" do - allow(@provider).to receive(:is_active?).and_return(true) - expect(@current_resource).to receive(:running).with(true) - @provider.load_current_resource + allow(provider).to receive(:is_active?).and_return(true) + provider.load_current_resource + expect(current_resource.running).to be true end it "should set running to false if the service is not running" do - allow(@provider).to receive(:is_active?).and_return(false) - expect(@current_resource).to receive(:running).with(false) - @provider.load_current_resource + allow(provider).to receive(:is_active?).and_return(false) + provider.load_current_resource + expect(current_resource.running).to be false end describe "when a status command has been specified" do before do - allow(@new_resource).to receive(:status_command).and_return("/bin/chefhasmonkeypants status") + allow(new_resource).to receive(:status_command).and_return("/bin/chefhasmonkeypants status") end it "should run the services status command if one has been specified" do - allow(@provider).to receive(:shell_out).and_return(@shell_out_success) - expect(@current_resource).to receive(:running).with(true) - @provider.load_current_resource + allow(provider).to receive(:shell_out).and_return(shell_out_success) + provider.load_current_resource + expect(current_resource.running).to be true end it "should run the services status command if one has been specified and properly set status check state" do - allow(@provider).to receive(:shell_out).with("/bin/chefhasmonkeypants status").and_return(@shell_out_success) - @provider.load_current_resource - expect(@provider.instance_variable_get("@status_check_success")).to be_true + allow(provider).to receive(:shell_out).with("/bin/chefhasmonkeypants status").and_return(shell_out_success) + provider.load_current_resource + expect(provider.status_check_success).to be true end it "should set running to false if a status command fails" do - allow(@provider).to receive(:shell_out).and_return(@shell_out_failure) - expect(@current_resource).to receive(:running).with(false) - @provider.load_current_resource + allow(provider).to receive(:shell_out).and_return(shell_out_failure) + provider.load_current_resource + expect(current_resource.running).to be false end it "should update state to indicate status check failed when a status command fails" do - allow(@provider).to receive(:shell_out).and_return(@shell_out_failure) - @provider.load_current_resource - expect(@provider.instance_variable_get("@status_check_success")).to be_false + allow(provider).to receive(:shell_out).and_return(shell_out_failure) + provider.load_current_resource + expect(provider.status_check_success).to be false end end it "should check if the service is enabled" do - expect(@provider).to receive(:is_enabled?) - @provider.load_current_resource + expect(provider).to receive(:is_enabled?) + provider.load_current_resource end it "should set enabled to true if the service is enabled" do - allow(@provider).to receive(:is_enabled?).and_return(true) - expect(@current_resource).to receive(:enabled).with(true) - @provider.load_current_resource + allow(provider).to receive(:is_enabled?).and_return(true) + provider.load_current_resource + expect(current_resource.enabled).to be true end it "should set enabled to false if the service is not enabled" do - allow(@provider).to receive(:is_enabled?).and_return(false) - expect(@current_resource).to receive(:enabled).with(false) - @provider.load_current_resource + allow(provider).to receive(:is_enabled?).and_return(false) + provider.load_current_resource + expect(current_resource.enabled).to be false end it "should return the current resource" do - expect(@provider.load_current_resource).to eql(@current_resource) + expect(provider.load_current_resource).to eql(current_resource) end end - describe "start and stop service" do - before(:each) do - @current_resource = Chef::Resource::Service.new('rsyslog.service') - allow(Chef::Resource::Service).to receive(:new).and_return(@current_resource) - @provider.current_resource = @current_resource - end - - it "should call the start command if one is specified" do - allow(@new_resource).to receive(:start_command).and_return("/sbin/rsyslog startyousillysally") - expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/rsyslog startyousillysally") - @provider.start_service - end + def setup_current_resource + provider.current_resource = current_resource + current_resource.service_name(service_name) + end - it "should call '/bin/systemctl start service_name' if no start command is specified" do - expect(@provider).to receive(:shell_out_with_systems_locale!).with("/bin/systemctl start #{@new_resource.service_name}").and_return(@shell_out_success) - @provider.start_service - end + %w{/usr/bin/systemctl /bin/systemctl}.each do |systemctl_path| + describe "when systemctl path is #{systemctl_path}" do + before(:each) do + setup_current_resource + allow(provider).to receive(:which).with("systemctl").and_return(systemctl_path) + end - it "should not call '/bin/systemctl start service_name' if it is already running" do - allow(@current_resource).to receive(:running).and_return(true) - expect(@provider).not_to receive(:shell_out_with_systems_locale!).with("/bin/systemctl start #{@new_resource.service_name}") - @provider.start_service - end + describe "start and stop service" do - it "should call the restart command if one is specified" do - allow(@current_resource).to receive(:running).and_return(true) - allow(@new_resource).to receive(:restart_command).and_return("/sbin/rsyslog restartyousillysally") - expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/rsyslog restartyousillysally") - @provider.restart_service - end + it "should call the start command if one is specified" do + allow(new_resource).to receive(:start_command).and_return("/sbin/rsyslog startyousillysally") + expect(provider).to receive(:shell_out_with_systems_locale!).with("/sbin/rsyslog startyousillysally") + provider.start_service + end - it "should call '/bin/systemctl restart service_name' if no restart command is specified" do - allow(@current_resource).to receive(:running).and_return(true) - expect(@provider).to receive(:shell_out_with_systems_locale!).with("/bin/systemctl restart #{@new_resource.service_name}").and_return(@shell_out_success) - @provider.restart_service - end + it "should call '#{systemctl_path} start service_name' if no start command is specified" do + expect(provider).to receive(:shell_out_with_systems_locale!).with("#{systemctl_path} start #{service_name}").and_return(shell_out_success) + provider.start_service + end - describe "reload service" do - context "when a reload command is specified" do - it "should call the reload command" do - allow(@current_resource).to receive(:running).and_return(true) - allow(@new_resource).to receive(:reload_command).and_return("/sbin/rsyslog reloadyousillysally") - expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/rsyslog reloadyousillysally") - @provider.reload_service + it "should not call '#{systemctl_path} start service_name' if it is already running" do + current_resource.running(true) + expect(provider).not_to receive(:shell_out_with_systems_locale!).with("#{systemctl_path} start #{service_name}") + provider.start_service end - end - context "when a reload command is not specified" do - it "should call '/bin/systemctl reload service_name' if the service is running" do - allow(@current_resource).to receive(:running).and_return(true) - expect(@provider).to receive(:shell_out_with_systems_locale!).with("/bin/systemctl reload #{@new_resource.service_name}").and_return(@shell_out_success) - @provider.reload_service + it "should call the restart command if one is specified" do + current_resource.running(true) + allow(new_resource).to receive(:restart_command).and_return("/sbin/rsyslog restartyousillysally") + expect(provider).to receive(:shell_out_with_systems_locale!).with("/sbin/rsyslog restartyousillysally") + provider.restart_service end - it "should start the service if the service is not running" do - allow(@current_resource).to receive(:running).and_return(false) - expect(@provider).to receive(:start_service).and_return(true) - @provider.reload_service + it "should call '#{systemctl_path} restart service_name' if no restart command is specified" do + current_resource.running(true) + expect(provider).to receive(:shell_out_with_systems_locale!).with("#{systemctl_path} restart #{service_name}").and_return(shell_out_success) + provider.restart_service end - end - end - it "should call the stop command if one is specified" do - allow(@current_resource).to receive(:running).and_return(true) - allow(@new_resource).to receive(:stop_command).and_return("/sbin/rsyslog stopyousillysally") - expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/rsyslog stopyousillysally") - @provider.stop_service - end + describe "reload service" do + context "when a reload command is specified" do + it "should call the reload command" do + current_resource.running(true) + allow(new_resource).to receive(:reload_command).and_return("/sbin/rsyslog reloadyousillysally") + expect(provider).to receive(:shell_out_with_systems_locale!).with("/sbin/rsyslog reloadyousillysally") + provider.reload_service + end + end + + context "when a reload command is not specified" do + it "should call '#{systemctl_path} reload service_name' if the service is running" do + current_resource.running(true) + expect(provider).to receive(:shell_out_with_systems_locale!).with("#{systemctl_path} reload #{service_name}").and_return(shell_out_success) + provider.reload_service + end + + it "should start the service if the service is not running" do + current_resource.running(false) + expect(provider).to receive(:start_service).and_return(true) + provider.reload_service + end + end + end - it "should call '/bin/systemctl stop service_name' if no stop command is specified" do - allow(@current_resource).to receive(:running).and_return(true) - expect(@provider).to receive(:shell_out_with_systems_locale!).with("/bin/systemctl stop #{@new_resource.service_name}").and_return(@shell_out_success) - @provider.stop_service - end + it "should call the stop command if one is specified" do + current_resource.running(true) + allow(new_resource).to receive(:stop_command).and_return("/sbin/rsyslog stopyousillysally") + expect(provider).to receive(:shell_out_with_systems_locale!).with("/sbin/rsyslog stopyousillysally") + provider.stop_service + end - it "should not call '/bin/systemctl stop service_name' if it is already stopped" do - allow(@current_resource).to receive(:running).and_return(false) - expect(@provider).not_to receive(:shell_out_with_systems_locale!).with("/bin/systemctl stop #{@new_resource.service_name}") - @provider.stop_service - end - end + it "should call '#{systemctl_path} stop service_name' if no stop command is specified" do + current_resource.running(true) + expect(provider).to receive(:shell_out_with_systems_locale!).with("#{systemctl_path} stop #{service_name}").and_return(shell_out_success) + provider.stop_service + end - describe "enable and disable service" do - before(:each) do - @current_resource = Chef::Resource::Service.new('rsyslog.service') - allow(Chef::Resource::Service).to receive(:new).and_return(@current_resource) - @provider.current_resource = @current_resource - end + it "should not call '#{systemctl_path} stop service_name' if it is already stopped" do + current_resource.running(false) + expect(provider).not_to receive(:shell_out_with_systems_locale!).with("#{systemctl_path} stop #{service_name}") + provider.stop_service + end + end - it "should call '/bin/systemctl enable service_name' to enable the service" do - expect(@provider).to receive(:shell_out!).with("/bin/systemctl enable #{@new_resource.service_name}").and_return(@shell_out_success) - @provider.enable_service - end + describe "enable and disable service" do + before(:each) do + provider.current_resource = current_resource + current_resource.service_name(service_name) + allow(provider).to receive(:which).with("systemctl").and_return("#{systemctl_path}") + end - it "should call '/bin/systemctl disable service_name' to disable the service" do - expect(@provider).to receive(:shell_out!).with("/bin/systemctl disable #{@new_resource.service_name}").and_return(@shell_out_success) - @provider.disable_service - end - end + it "should call '#{systemctl_path} enable service_name' to enable the service" do + expect(provider).to receive(:shell_out!).with("#{systemctl_path} enable #{service_name}").and_return(shell_out_success) + provider.enable_service + end - describe "is_active?" do - before(:each) do - @current_resource = Chef::Resource::Service.new('rsyslog.service') - allow(Chef::Resource::Service).to receive(:new).and_return(@current_resource) - end + it "should call '#{systemctl_path} disable service_name' to disable the service" do + expect(provider).to receive(:shell_out!).with("#{systemctl_path} disable #{service_name}").and_return(shell_out_success) + provider.disable_service + end + end - it "should return true if '/bin/systemctl is-active service_name' returns 0" do - expect(@provider).to receive(:shell_out).with('/bin/systemctl is-active rsyslog.service --quiet').and_return(@shell_out_success) - expect(@provider.is_active?).to be_true - end + describe "is_active?" do + before(:each) do + provider.current_resource = current_resource + current_resource.service_name(service_name) + allow(provider).to receive(:which).with("systemctl").and_return("#{systemctl_path}") + end - it "should return false if '/bin/systemctl is-active service_name' returns anything except 0" do - expect(@provider).to receive(:shell_out).with('/bin/systemctl is-active rsyslog.service --quiet').and_return(@shell_out_failure) - expect(@provider.is_active?).to be_false - end - end + it "should return true if '#{systemctl_path} is-active service_name' returns 0" do + expect(provider).to receive(:shell_out).with("#{systemctl_path} is-active #{service_name} --quiet").and_return(shell_out_success) + expect(provider.is_active?).to be true + end - describe "is_enabled?" do - before(:each) do - @current_resource = Chef::Resource::Service.new('rsyslog.service') - allow(Chef::Resource::Service).to receive(:new).and_return(@current_resource) - end + it "should return false if '#{systemctl_path} is-active service_name' returns anything except 0" do + expect(provider).to receive(:shell_out).with("#{systemctl_path} is-active #{service_name} --quiet").and_return(shell_out_failure) + expect(provider.is_active?).to be false + end + end - it "should return true if '/bin/systemctl is-enabled service_name' returns 0" do - expect(@provider).to receive(:shell_out).with('/bin/systemctl is-enabled rsyslog.service --quiet').and_return(@shell_out_success) - expect(@provider.is_enabled?).to be_true - end + it "should return true if '#{systemctl_path} is-enabled service_name' returns 0" do + expect(provider).to receive(:shell_out).with("#{systemctl_path} is-enabled #{service_name} --quiet").and_return(shell_out_success) + expect(provider.is_enabled?).to be true + end - it "should return false if '/bin/systemctl is-enabled service_name' returns anything except 0" do - expect(@provider).to receive(:shell_out).with('/bin/systemctl is-enabled rsyslog.service --quiet').and_return(@shell_out_failure) - expect(@provider.is_enabled?).to be_false + it "should return false if '#{systemctl_path} is-enabled service_name' returns anything except 0" do + expect(provider).to receive(:shell_out).with("#{systemctl_path} is-enabled #{service_name} --quiet").and_return(shell_out_failure) + expect(provider.is_enabled?).to be false + end end end end diff --git a/spec/unit/provider_resolver_spec.rb b/spec/unit/provider_resolver_spec.rb index 927cca4f58..c56207c554 100644 --- a/spec/unit/provider_resolver_spec.rb +++ b/spec/unit/provider_resolver_spec.rb @@ -33,11 +33,11 @@ describe Chef::ProviderResolver do node end - let(:provider_resolver) { Chef::ProviderResolver.new(node) } + let(:provider_resolver) { Chef::ProviderResolver.new(node, resource, action) } let(:action) { :start } - let(:resolved_provider) { provider_resolver.resolve(resource, action) } + let(:resolved_provider) { provider_resolver.resolve } let(:provider) { nil } @@ -63,11 +63,34 @@ describe Chef::ProviderResolver do allow(resource).to receive(:service_name).and_return("ntp") end - shared_examples_for "a debian platform with upstart and update-rc.d" do + shared_examples_for "an ubuntu platform with upstart, update-rc.d and systemd" do before do - stub_service_providers(:debian, :invokercd, :upstart) + stub_service_providers(:debian, :invokercd, :upstart, :systemd) + end + + it "when only the SysV init script exists, it returns a Service::Debian provider" do + allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") + .and_return( [ :initd, :systemd ] ) + expect(resolved_provider).to eql(Chef::Provider::Service::Systemd) + end + + it "when both SysV and Upstart scripts exist, it returns a Service::Upstart provider" do + allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") + .and_return( [ :initd, :upstart, :systemd ] ) + expect(resolved_provider).to eql(Chef::Provider::Service::Systemd) + end + + it "when only the Upstart script exists, it returns a Service::Upstart provider" do + allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") + .and_return( [ :upstart, :systemd ] ) + expect(resolved_provider).to eql(Chef::Provider::Service::Systemd) end + it "when both do not exist, it calls the old style provider resolver and returns a Debian Provider" do + allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") + .and_return( [ :systemd ] ) + expect(resolved_provider).to eql(Chef::Provider::Service::Systemd) + end it "when only the SysV init script exists, it returns a Service::Debian provider" do allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") .and_return( [ :initd ] ) @@ -89,7 +112,137 @@ describe Chef::ProviderResolver do it "when both do not exist, it calls the old style provider resolver and returns a Debian Provider" do allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") .and_return( [ ] ) - expect(resolved_provider).to eql(Chef::Provider::Service::Debian) + expect(resolved_provider).to eql(Chef::Provider::Service::Systemd) + end + end + + shared_examples_for "an ubuntu platform with upstart and update-rc.d" do + before do + stub_service_providers(:debian, :invokercd, :upstart) + end + + # needs to be handled by the highest priority init.d handler + context "when only the SysV init script exists" do + before do + allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") + .and_return( [ :initd ] ) + end + + it "enables init, invokercd, debian and upstart providers" do + expect(provider_resolver.enabled_handlers).to include( + Chef::Provider::Service::Debian, + Chef::Provider::Service::Init, + Chef::Provider::Service::Invokercd, + Chef::Provider::Service::Upstart, + ) + end + + it "supports all the enabled handlers except for upstart" do + expect(provider_resolver.supported_handlers).to include( + Chef::Provider::Service::Debian, + Chef::Provider::Service::Init, + Chef::Provider::Service::Invokercd, + ) + expect(provider_resolver.supported_handlers).to_not include( + Chef::Provider::Service::Upstart, + ) + end + + it "returns a Service::Debian provider" do + expect(resolved_provider).to eql(Chef::Provider::Service::Debian) + end + end + + # on ubuntu this must be handled by upstart, the init script will exit 1 and fail + context "when both SysV and Upstart scripts exist" do + before do + allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") + .and_return( [ :initd, :upstart ] ) + end + + it "enables init, invokercd, debian and upstart providers" do + expect(provider_resolver.enabled_handlers).to include( + Chef::Provider::Service::Debian, + Chef::Provider::Service::Init, + Chef::Provider::Service::Invokercd, + Chef::Provider::Service::Upstart, + ) + end + + it "supports all the enabled handlers" do + expect(provider_resolver.supported_handlers).to include( + Chef::Provider::Service::Debian, + Chef::Provider::Service::Init, + Chef::Provider::Service::Invokercd, + Chef::Provider::Service::Upstart, + ) + end + + it "returns a Service::Upstart provider" do + expect(resolved_provider).to eql(Chef::Provider::Service::Upstart) + end + end + + # this case is a pure-upstart script which is easy + context "when only the Upstart script exists" do + before do + allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") + .and_return( [ :upstart ] ) + end + + it "enables init, invokercd, debian and upstart providers" do + expect(provider_resolver.enabled_handlers).to include( + Chef::Provider::Service::Debian, + Chef::Provider::Service::Init, + Chef::Provider::Service::Invokercd, + Chef::Provider::Service::Upstart, + ) + end + + it "supports only the upstart handler" do + expect(provider_resolver.supported_handlers).to include( + Chef::Provider::Service::Upstart, + ) + expect(provider_resolver.supported_handlers).to_not include( + Chef::Provider::Service::Debian, + Chef::Provider::Service::Init, + Chef::Provider::Service::Invokercd, + ) + end + + it "returns a Service::Upstart provider" do + expect(resolved_provider).to eql(Chef::Provider::Service::Upstart) + end + end + + # this case is important to get correct for why-run when no config is setup + context "when both do not exist" do + before do + allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") + .and_return( [ ] ) + end + + it "enables init, invokercd, debian and upstart providers" do + expect(provider_resolver.enabled_handlers).to include( + Chef::Provider::Service::Debian, + Chef::Provider::Service::Init, + Chef::Provider::Service::Invokercd, + Chef::Provider::Service::Upstart, + ) + end + + it "no providers claim to support the resource" do + expect(provider_resolver.supported_handlers).to_not include( + Chef::Provider::Service::Upstart, + Chef::Provider::Service::Debian, + Chef::Provider::Service::Init, + Chef::Provider::Service::Invokercd, + ) + end + + it "returns a Debian Provider" do + expect(resolved_provider).to eql(Chef::Provider::Service::Upstart) + end end end @@ -104,6 +257,12 @@ describe Chef::ProviderResolver do .and_return( [ :initd ] ) expect(resolved_provider).to eql(Chef::Provider::Service::Insserv) end + + it "uses the Service::Insserv Provider when there is no config" do + allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") + .and_return( [ ] ) + expect(resolved_provider).to eql(Chef::Provider::Service::Insserv) + end end context "when the user has installed upstart" do @@ -111,7 +270,7 @@ describe Chef::ProviderResolver do stub_service_providers(:debian, :invokercd, :insserv, :upstart) end - it "when only the SysV init script exists, it returns a Service::Debian provider" do + it "when only the SysV init script exists, it returns an Insserv provider" do allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") .and_return( [ :initd ] ) expect(resolved_provider).to eql(Chef::Provider::Service::Insserv) @@ -132,12 +291,18 @@ describe Chef::ProviderResolver do it "when both do not exist, it calls the old style provider resolver and returns a Debian Provider" do allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") .and_return( [ ] ) - expect(resolved_provider).to eql(Chef::Provider::Service::Insserv) + expect(resolved_provider).to eql(Chef::Provider::Service::Upstart) end end end - describe "on Linux" do + describe "on Ubuntu 14.10" do + let(:os) { "linux" } + let(:platform) { "ubuntu" } + let(:platform_family) { "debian" } + let(:platform_version) { "14.04" } + + it_behaves_like "an ubuntu platform with upstart, update-rc.d and systemd" end describe "on Ubuntu 14.04" do @@ -146,7 +311,7 @@ describe Chef::ProviderResolver do let(:platform_family) { "debian" } let(:platform_version) { "14.04" } - it_behaves_like "a debian platform with upstart and update-rc.d" + it_behaves_like "an ubuntu platform with upstart and update-rc.d" end describe "on Ubuntu 10.04" do @@ -155,7 +320,7 @@ describe Chef::ProviderResolver do let(:platform_family) { "debian" } let(:platform_version) { "10.04" } - it_behaves_like "a debian platform with upstart and update-rc.d" + it_behaves_like "an ubuntu platform with upstart and update-rc.d" end # old debian uses the Debian provider (does not have insserv or upstart, or update-rc.d???) diff --git a/spec/unit/resource/timestamped_deploy_spec.rb b/spec/unit/resource/timestamped_deploy_spec.rb index 2af9d8bb0f..eca6c570d4 100644 --- a/spec/unit/resource/timestamped_deploy_spec.rb +++ b/spec/unit/resource/timestamped_deploy_spec.rb @@ -20,35 +20,14 @@ require 'spec_helper' describe Chef::Resource::TimestampedDeploy, "initialize" do - let(:node) { - node = Chef::Node.new - node.automatic_attrs[:os] = 'linux' - node.automatic_attrs[:platform_family] = 'rhel' - node - } - let(:events) { Chef::EventDispatch::Dispatcher.new } - let(:provider_resolver) { Chef::ProviderResolver.new(node) } - let(:run_context) { - run_context = Chef::RunContext.new(node, {}, events) - run_context.provider_resolver = provider_resolver - run_context - } - let(:resource) { Chef::Resource::TimestampedDeploy.new("stuff", run_context) } + static_provider_resolution( + resource: Chef::Resource::TimestampedDeploy, + provider: Chef::Provider::Deploy::Timestamped, + name: :deploy, + action: :deploy, + os: 'linux', + platform_family: 'rhel', + ) - it "should return a Chef::Resource::TimestampedDeploy" do - expect(resource).to be_a_kind_of(Chef::Resource::TimestampedDeploy) - end - - it "should set the resource_name to :timestamped_deploy" do - expect(resource.resource_name).to eql(:deploy) - end - - it "should leave the provider nil" do - expect(resource.provider).to eql(nil) - end - - it "should resolve to a Chef::Provider::Deploy::Timestamped" do - expect(resource.provider_for_action(:install)).to be_a(Chef::Provider::Deploy::Timestamped) - end end diff --git a/spec/unit/runner_spec.rb b/spec/unit/runner_spec.rb index 9bd4199418..b30f818da1 100644 --- a/spec/unit/runner_spec.rb +++ b/spec/unit/runner_spec.rb @@ -99,13 +99,15 @@ describe Chef::Runner do end context "when we fall through to old Chef::Platform resolution" do + let(:provider_resolver) { Chef::ProviderResolver.new(node, first_resource, nil) } before do # set up old Chef::Platform resolution instead of provider_resolver Chef::Platform.set( :resource => :cat, :provider => Chef::Provider::SnakeOil ) - allow(run_context.provider_resolver).to receive(:maybe_dynamic_provider_resolution).with(first_resource, anything()).and_return(nil) + allow(Chef::ProviderResolver).to receive(:new).and_return(provider_resolver) + allow(provider_resolver).to receive(:maybe_dynamic_provider_resolution).with(first_resource, anything()).and_return(nil) end it "should use the platform provider if it has one" do -- cgit v1.2.1