summaryrefslogtreecommitdiff
path: root/lib/chef
diff options
context:
space:
mode:
Diffstat (limited to 'lib/chef')
-rw-r--r--lib/chef/api_client/registration.rb4
-rw-r--r--lib/chef/config.rb8
-rw-r--r--lib/chef/event_loggers/windows_eventlog.rb11
-rw-r--r--lib/chef/knife/bootstrap.rb15
-rw-r--r--lib/chef/mixin/deep_merge.rb9
-rw-r--r--lib/chef/mixin/which.rb37
-rw-r--r--lib/chef/node.rb16
-rw-r--r--lib/chef/node/attribute.rb107
-rw-r--r--lib/chef/node/attribute_collections.rb112
-rw-r--r--lib/chef/platform/provider_priority_map.rb5
-rw-r--r--lib/chef/platform/service_helpers.rb45
-rw-r--r--lib/chef/provider/service/aixinit.rb2
-rw-r--r--lib/chef/provider/service/arch.rb2
-rw-r--r--lib/chef/provider/service/debian.rb6
-rw-r--r--lib/chef/provider/service/init.rb4
-rw-r--r--lib/chef/provider/service/insserv.rb6
-rw-r--r--lib/chef/provider/service/invokercd.rb6
-rw-r--r--lib/chef/provider/service/redhat.rb6
-rw-r--r--lib/chef/provider/service/systemd.rb82
-rw-r--r--lib/chef/provider/service/upstart.rb7
-rw-r--r--lib/chef/provider_resolver.rb46
-rw-r--r--lib/chef/resource.rb3
-rw-r--r--lib/chef/resources.rb7
-rw-r--r--lib/chef/run_context.rb5
-rw-r--r--lib/chef/shell.rb12
-rw-r--r--lib/chef/util/selinux.rb12
26 files changed, 443 insertions, 132 deletions
diff --git a/lib/chef/api_client/registration.rb b/lib/chef/api_client/registration.rb
index 213d0b7f49..8a5885eff3 100644
--- a/lib/chef/api_client/registration.rb
+++ b/lib/chef/api_client/registration.rb
@@ -153,7 +153,9 @@ class Chef
def file_flags
base_flags = File::CREAT|File::TRUNC|File::RDWR
# Windows doesn't have symlinks, so it doesn't have NOFOLLOW
- base_flags |= File::NOFOLLOW if defined?(File::NOFOLLOW)
+ if defined?(File::NOFOLLOW) && !Chef::Config[:follow_client_key_symlink]
+ base_flags |= File::NOFOLLOW
+ end
base_flags
end
end
diff --git a/lib/chef/config.rb b/lib/chef/config.rb
index dc04026acb..d3871c38e8 100644
--- a/lib/chef/config.rb
+++ b/lib/chef/config.rb
@@ -396,6 +396,12 @@ class Chef
# If chef-zero is enabled, this defaults to nil (no authentication).
default(:client_key) { chef_zero.enabled ? nil : platform_specific_path("/etc/chef/client.pem") }
+ # When registering the client, should we allow the client key location to
+ # be a symlink? eg: /etc/chef/client.pem -> /etc/chef/prod-client.pem
+ # If the path of the key goes through a directory like /tmp this should
+ # never be set to true or its possibly an easily exploitable security hole.
+ default :follow_client_key_symlink, false
+
# This secret is used to decrypt encrypted data bag items.
default(:encrypted_data_bag_secret) do
if File.exist?(platform_specific_path("/etc/chef/encrypted_data_bag_secret"))
@@ -491,7 +497,7 @@ class Chef
default :ssh_gateway, nil
default :bootstrap_version, nil
default :bootstrap_proxy, nil
- default :bootstrap_template, "chef-full"
+ default :bootstrap_template, nil
default :secret, nil
default :secret_file, nil
default :identity_file, nil
diff --git a/lib/chef/event_loggers/windows_eventlog.rb b/lib/chef/event_loggers/windows_eventlog.rb
index b8d279594b..6d45d4fab4 100644
--- a/lib/chef/event_loggers/windows_eventlog.rb
+++ b/lib/chef/event_loggers/windows_eventlog.rb
@@ -26,7 +26,6 @@ if Chef::Platform::windows? and not Chef::Platform::windows_server_2003?
end
require 'win32/eventlog'
- include Win32
end
class Chef
@@ -51,12 +50,12 @@ class Chef
end
def initialize
- @eventlog = EventLog::open('Application')
+ @eventlog = ::Win32::EventLog::open('Application')
end
def run_start(version)
@eventlog.report_event(
- :event_type => EventLog::INFO_TYPE,
+ :event_type => ::Win32::EventLog::INFO_TYPE,
:source => SOURCE,
:event_id => RUN_START_EVENT_ID,
:data => [version]
@@ -66,7 +65,7 @@ class Chef
def run_started(run_status)
@run_status = run_status
@eventlog.report_event(
- :event_type => EventLog::INFO_TYPE,
+ :event_type => ::Win32::EventLog::INFO_TYPE,
:source => SOURCE,
:event_id => RUN_STARTED_EVENT_ID,
:data => [run_status.run_id]
@@ -75,7 +74,7 @@ class Chef
def run_completed(node)
@eventlog.report_event(
- :event_type => EventLog::INFO_TYPE,
+ :event_type => ::Win32::EventLog::INFO_TYPE,
:source => SOURCE,
:event_id => RUN_COMPLETED_EVENT_ID,
:data => [@run_status.run_id, @run_status.elapsed_time.to_s]
@@ -88,7 +87,7 @@ class Chef
#Exception backtrace: %5
def run_failed(e)
@eventlog.report_event(
- :event_type => EventLog::ERROR_TYPE,
+ :event_type => ::Win32::EventLog::ERROR_TYPE,
:source => SOURCE,
:event_id => RUN_FAILED_EVENT_ID,
:data => [@run_status.run_id,
diff --git a/lib/chef/knife/bootstrap.rb b/lib/chef/knife/bootstrap.rb
index f159c9105b..19a329199d 100644
--- a/lib/chef/knife/bootstrap.rb
+++ b/lib/chef/knife/bootstrap.rb
@@ -194,13 +194,15 @@ class Chef
:description => "Verify the SSL cert for HTTPS requests to the Chef server API.",
:boolean => true
+ def default_bootstrap_template
+ "chef-full"
+ end
+
def bootstrap_template
- # For some reason knife.merge_configs doesn't pick up the default values from
- # Chef::Config[:knife][:bootstrap_template] unless Chef::Config[:knife][:bootstrap_template]
- # is forced to pick up the values before calling merge_configs.
- # We therefore have Chef::Config[:knife][:bootstrap_template] to pick up the defaults
- # if no option is specified.
- config[:bootstrap_template] || config[:distro] || config[:template_file] || Chef::Config[:knife][:bootstrap_template]
+ # The order here is important. We want to check if we have the new Chef 12 option is set first.
+ # Knife cloud plugins unfortunately all set a default option for the :distro so it should be at
+ # the end.
+ config[:bootstrap_template] || config[:template_file] || config[:distro] || default_bootstrap_template
end
def find_template
@@ -210,7 +212,6 @@ class Chef
if File.exists?(template)
Chef::Log.debug("Using the specified bootstrap template: #{File.dirname(template)}")
return template
-
end
# Otherwise search the template directories until we find the right one
diff --git a/lib/chef/mixin/deep_merge.rb b/lib/chef/mixin/deep_merge.rb
index 5e3327a526..a8a4737758 100644
--- a/lib/chef/mixin/deep_merge.rb
+++ b/lib/chef/mixin/deep_merge.rb
@@ -29,6 +29,7 @@ class Chef
class InvalidSubtractiveMerge < ArgumentError; end
+
OLD_KNOCKOUT_PREFIX = "!merge:".freeze
# Regex to match the "knockout prefix" that was used to indicate
@@ -85,12 +86,8 @@ class Chef
when Hash
if dest.kind_of?(Hash)
source.each do |src_key, src_value|
- if dest.has_key? src_key
- if dest[src_key].nil?
- dest[src_key] = nil
- else
- dest[src_key] = deep_merge!(src_value, dest[src_key])
- end
+ if dest[src_key]
+ dest[src_key] = deep_merge!(src_value, dest[src_key])
else # dest[src_key] doesn't exist so we take whatever source has
raise_if_knockout_used!(src_value)
dest[src_key] = src_value
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 <lamont@getchef.io>
+# 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/node.rb b/lib/chef/node.rb
index 5f788af4d4..dbb7852586 100644
--- a/lib/chef/node.rb
+++ b/lib/chef/node.rb
@@ -42,6 +42,8 @@ class Chef
extend Forwardable
def_delegators :attributes, :keys, :each_key, :each_value, :key?, :has_key?
+ def_delegators :attributes, :rm, :rm_default, :rm_normal, :rm_override
+ def_delegators :attributes, :default!, :normal!, :override!, :force_default!, :force_override!
attr_accessor :recipe_list, :run_state, :override_runlist
@@ -146,13 +148,6 @@ class Chef
attributes.default
end
- # Set a force default attribute. Intermediate mashes will be created by
- # auto-vivify if necessary.
- def default!
- attributes.set_unless_value_present = false
- attributes.default!
- end
-
# Set a default attribute of this node, auto-vivifying any mashes that are
# missing, but if the final value already exists, don't set it
def default_unless
@@ -167,13 +162,6 @@ class Chef
attributes.override
end
- # Set a force override attribute. Intermediate mashes will be created by
- # auto-vivify if needed.
- def override!
- attributes.set_unless_value_present = false
- attributes.override!
- end
-
# Set an override attribute of this node, auto-vivifying any mashes that
# are missing, but if the final value already exists, don't set it
def override_unless
diff --git a/lib/chef/node/attribute.rb b/lib/chef/node/attribute.rb
index 66569cf0e1..3eb6449046 100644
--- a/lib/chef/node/attribute.rb
+++ b/lib/chef/node/attribute.rb
@@ -58,7 +58,6 @@ class Chef
:@force_default
]
-
OVERRIDE_COMPONENTS = [
:@override,
:@role_override,
@@ -146,7 +145,6 @@ class Chef
METHOD_DEFN
end
-
# return the cookbook level default attribute component
attr_reader :default
@@ -159,11 +157,6 @@ class Chef
# return the force_default level attribute component
attr_reader :force_default
- # default! is the "advertised" method for force_default, but is
- # implemented as an alias because instance variables can't (easily) have
- # +!+ characters.
- alias :default! :force_default
-
# return the "normal" level attribute component
attr_reader :normal
@@ -179,11 +172,6 @@ class Chef
# return the force override level attribute component
attr_reader :force_override
- # +override!+ is the "advertised" method for +force_override+ but is
- # implemented as an alias because instance variables can't easily have
- # +!+ characters.
- alias :override! :force_override
-
# return the automatic level attribute component
attr_reader :automatic
@@ -311,6 +299,100 @@ class Chef
@automatic = VividMash.new(self, new_data)
end
+ #
+ # Deleting attributes
+ #
+
+ # clears attributes from all precedence levels
+ def rm(*args)
+ # just easier to compute our retval, rather than collect+merge sub-retvals
+ ret = args.inject(merged_attributes) do |attr, arg|
+ if attr.nil? || !attr.respond_to?(:[])
+ nil
+ else
+ begin
+ attr[arg]
+ rescue TypeError
+ raise TypeError, "Wrong type in index of attribute (did you use a Hash index on an Array?)"
+ end
+ end
+ end
+ rm_default(*args)
+ rm_normal(*args)
+ rm_override(*args)
+ ret
+ end
+
+ # does <level>['foo']['bar'].delete('baz')
+ def remove_from_precedence_level(level, *args, key)
+ multimash = level.element(*args)
+ multimash.nil? ? nil : multimash.delete(key)
+ end
+
+ private :remove_from_precedence_level
+
+ # clears attributes from all default precedence levels
+ #
+ # equivalent to: force_default!['foo']['bar'].delete('baz')
+ def rm_default(*args)
+ reset
+ remove_from_precedence_level(force_default!(autovivify: false), *args)
+ end
+
+ # clears attributes from normal precedence
+ #
+ # equivalent to: normal!['foo']['bar'].delete('baz')
+ def rm_normal(*args)
+ reset
+ remove_from_precedence_level(normal!(autovivify: false), *args)
+ end
+
+ # clears attributes from all override precedence levels
+ #
+ # equivalent to: force_override!['foo']['bar'].delete('baz')
+ def rm_override(*args)
+ reset
+ remove_from_precedence_level(force_override!(autovivify: false), *args)
+ end
+
+ #
+ # Replacing attributes without merging
+ #
+
+ # sets default attributes without merging
+ def default!(opts={})
+ reset
+ MultiMash.new(self, @default, [], opts)
+ end
+
+ # sets normal attributes without merging
+ def normal!(opts={})
+ reset
+ MultiMash.new(self, @normal, [], opts)
+ end
+
+ # sets override attributes without merging
+ def override!(opts={})
+ reset
+ MultiMash.new(self, @override, [], opts)
+ end
+
+ # clears from all default precedence levels and then sets force_default
+ def force_default!(opts={})
+ reset
+ MultiMash.new(self, @force_default, [@default, @env_default, @role_default], opts)
+ end
+
+ # clears from all override precedence levels and then sets force_override
+ def force_override!(opts={})
+ reset
+ MultiMash.new(self, @force_override, [@override, @env_override, @role_override], opts)
+ end
+
+ #
+ # Accessing merged attributes
+ #
+
def merged_attributes
@merged_attributes ||= begin
components = [merge_defaults, @normal, merge_overrides, @automatic]
@@ -391,7 +473,6 @@ class Chef
end
end
-
end
end
diff --git a/lib/chef/node/attribute_collections.rb b/lib/chef/node/attribute_collections.rb
index f09b02b106..c8bc618762 100644
--- a/lib/chef/node/attribute_collections.rb
+++ b/lib/chef/node/attribute_collections.rb
@@ -209,5 +209,117 @@ class Chef
end
+ # == MultiMash
+ # This is a Hash-like object that contains multiple VividMashes in it. Its
+ # purpose is so that the user can descend into the mash and delete a subtree
+ # from all of the Mash objects (used to delete all values in a subtree from
+ # default, force_default, role_default and env_default at the same time). The
+ # assignment operator strictly does assignment (does no merging) and works
+ # by deleting the subtree and then assigning to the last mash which passed in
+ # the initializer.
+ #
+ # A lot of the complexity of this class comes from the fact that at any key
+ # value some or all of the mashes may walk off their ends and become nil or
+ # true or something. The schema may change so that one precidence leve may
+ # be 'true' object and another may be a VividMash. It is also possible that
+ # one or many of them may transition from VividMashes to Hashes or Arrays.
+ #
+ # It also supports the case where you may be deleting a key using node.rm
+ # in which case if intermediate keys all walk off into nil then you don't want
+ # to be autovivifying keys as you go. On the other hand you may be using
+ # node.force_default! in which case you'll wind up with a []= operator at the
+ # end and you want autovivification, so we conditionally have to support either
+ # operation.
+ #
+ # @todo: can we have an autovivify class that decorates a class that doesn't
+ # autovivify or something so that the code is less awful?
+ #
+ class MultiMash
+ attr_reader :root
+ attr_reader :mashes
+ attr_reader :opts
+ attr_reader :primary_mash
+
+ # Initialize with an array of mashes. For the delete return value to work
+ # properly the mashes must come from the same attribute level (i.e. all
+ # override or all default, but not a mix of both).
+ def initialize(root, primary_mash, mashes, opts={})
+ @root = root
+ @primary_mash = primary_mash
+ @mashes = mashes
+ @opts = opts
+ @opts[:autovivify] = true if @opts[:autovivify].nil?
+ end
+
+ def [](key)
+ # handle the secondary mashes
+ new_mashes = []
+ mashes.each do |mash|
+ new_mash = safe_evalute_key(mash, key)
+ # secondary mashes never autovivify so once they fall into nil, we just stop tracking them
+ new_mashes.push(new_mash) unless new_mash.nil?
+ end
+
+ new_primary_mash = safe_evalute_key(primary_mash, key)
+
+ if new_primary_mash.nil? && @opts[:autovivify]
+ primary_mash[key] = VividMash.new(root)
+ new_primary_mash = primary_mash[key]
+ end
+
+ MultiMash.new(root, new_primary_mash, new_mashes, opts)
+ end
+
+ def []=(key, value)
+ if primary_mash.nil?
+ # This theoretically should never happen since node#force_default! setter methods will autovivify and
+ # node#rm methods do not end in #[]= operators.
+ raise TypeError, "No autovivification was specified initially on a method chain ending in assignment"
+ end
+ ret = delete(key)
+ primary_mash[key] = value
+ ret
+ end
+
+ # mash.element('foo', 'bar') is the same as mash['foo']['bar']
+ def element(key = nil, *subkeys)
+ return self if key.nil?
+ submash = self[key]
+ subkeys.empty? ? submash : submash.element(*subkeys)
+ end
+
+ def delete(key)
+ # the return value is a deep merge which is correct semantics when
+ # merging between attributes on the same level (this would be incorrect
+ # if passed both override and default attributes which would need hash_only
+ # merging).
+ ret = mashes.inject(Mash.new) do |merged, mash|
+ Chef::Mixin::DeepMerge.merge(merged, mash)
+ end
+ ret = Chef::Mixin::DeepMerge.merge(ret, primary_mash)
+ mashes.each do |mash|
+ mash.delete(key) if mash.respond_to?(:delete)
+ end
+ primary_mash.delete(key) if primary_mash.respond_to?(:delete)
+ ret[key]
+ end
+
+ private
+
+ def safe_evalute_key(mash, key)
+ if mash.respond_to?(:[])
+ if mash.respond_to?(:has_key?)
+ if mash.has_key?(key)
+ return mash[key] if mash[key].respond_to?(:[])
+ end
+ elsif !mash[key].nil?
+ return mash[key] if mash[key].respond_to?(:[])
+ end
+ end
+ return nil
+ 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/resources.rb b/lib/chef/resources.rb
index 5b938095c6..696dbbdc55 100644
--- a/lib/chef/resources.rb
+++ b/lib/chef/resources.rb
@@ -78,3 +78,10 @@ require 'chef/resource/windows_package'
require 'chef/resource/yum_package'
require 'chef/resource/lwrp_base'
require 'chef/resource/bff_package'
+
+begin
+ # Optional resources chef_node, chef_client, machine, machine_image, etc.
+ require 'cheffish'
+ require 'chef/provisioning'
+rescue LoadError
+end
diff --git a/lib/chef/run_context.rb b/lib/chef/run_context.rb
index 41fd11e6eb..8f7296822c 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'
@@ -54,9 +53,6 @@ class Chef
# The list of control groups to execute during the audit phase
attr_accessor :controls_groups
- # 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
@@ -91,7 +87,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/shell.rb b/lib/chef/shell.rb
index 33c10e22a6..c5b8296c63 100644
--- a/lib/chef/shell.rb
+++ b/lib/chef/shell.rb
@@ -308,9 +308,17 @@ FOOTER
elsif ENV['HOME'] && ::File.exist?(File.join(ENV['HOME'], '.chef', 'chef_shell.rb'))
File.join(ENV['HOME'], '.chef', 'chef_shell.rb')
elsif config[:solo]
- "/etc/chef/solo.rb"
+ if Chef::Platform.windows?
+ "C:\\chef\\solo.rb"
+ else
+ "/etc/chef/solo.rb"
+ end
elsif config[:client]
- "/etc/chef/client.rb"
+ if Chef::Platform.windows?
+ "C:\\chef\\client.rb"
+ else
+ "/etc/chef/client.rb"
+ end
else
nil
end
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
-