diff options
author | Gregory Batye <gbatye@fb.com> | 2016-11-07 14:51:33 -0800 |
---|---|---|
committer | Gregory Batye <gbatye@fb.com> | 2016-11-07 14:51:33 -0800 |
commit | 7387b08a2ca96ed99d9828d67259ad9fb10f513a (patch) | |
tree | 07d26ccc9a21c897412053ca122c5e2ad1884ad3 /lib/chef | |
parent | 3050a0594ddf3a6a65020df036226594014534b0 (diff) | |
parent | 1d61e460675a540536bd4fc893b4bc6aa7070f21 (diff) | |
download | chef-7387b08a2ca96ed99d9828d67259ad9fb10f513a.tar.gz |
Merge remote-tracking branch 'chef/master' into fix_osx_profile
Diffstat (limited to 'lib/chef')
25 files changed, 342 insertions, 118 deletions
diff --git a/lib/chef/chef_fs/file_system/chef_server/cookbook_file.rb b/lib/chef/chef_fs/file_system/chef_server/cookbook_file.rb index 426cc62039..ad50054dc9 100644 --- a/lib/chef/chef_fs/file_system/chef_server/cookbook_file.rb +++ b/lib/chef/chef_fs/file_system/chef_server/cookbook_file.rb @@ -39,17 +39,13 @@ class Chef def read begin tmpfile = rest.streaming_request(file[:url]) + File.open(tmpfile, "rb") { |f| f.read } rescue Timeout::Error => e raise Chef::ChefFS::FileSystem::OperationFailedError.new(:read, self, e, "Timeout reading #{file[:url]}: #{e}") rescue Net::HTTPServerException => e raise Chef::ChefFS::FileSystem::OperationFailedError.new(:read, self, e, "#{e.message} retrieving #{file[:url]}") - end - - begin - tmpfile.open - tmpfile.read - ensure - tmpfile.close! + rescue Errno::ENOENT + raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!) end end diff --git a/lib/chef/data_collector.rb b/lib/chef/data_collector.rb index dbb0b3771a..da7aeee3b8 100644 --- a/lib/chef/data_collector.rb +++ b/lib/chef/data_collector.rb @@ -19,20 +19,66 @@ # require "uri" +require "chef/server_api" +require "chef/http/simple_json" require "chef/event_dispatch/base" require "chef/data_collector/messages" require "chef/data_collector/resource_report" require "ostruct" +require "set" class Chef # == Chef::DataCollector # Provides methods for determinine whether a reporter should be registered. class DataCollector + + # Whether or not to enable data collection: + # * always disabled for why run mode + # * disabled when the user sets `Chef::Config[:data_collector][:mode]` to a + # value that excludes the mode (client or solo) that we are running as + # * disabled in solo mode if the user did not configure the auth token + # * disabled if `Chef::Config[:data_collector][:server_url]` is set to a + # falsey value def self.register_reporter? - Chef::Config[:data_collector][:server_url] && - !Chef::Config[:why_run] && - self.reporter_enabled_for_current_mode? + if why_run? + Chef::Log.debug("data collector is disabled for why run mode") + return false + end + unless reporter_enabled_for_current_mode? + Chef::Log.debug("data collector is configured to only run in " \ + "#{Chef::Config[:data_collector][:mode].inspect} modes, disabling it") + return false + end + unless data_collector_url_configured? + Chef::Log.debug("data collector URL is not configured, disabling data collector") + return false + end + if solo? && !token_auth_configured? + Chef::Log.debug("Data collector token must be configured to use Chef Automate data collector with Chef Solo") + end + if !solo? && token_auth_configured? + Chef::Log.warn("Data collector token authentication is not recommended for client-server mode" \ + "Please upgrade Chef Server to 12.11.0 and remove the token from your config file " \ + "to use key based authentication instead") + end + true + end + + def self.data_collector_url_configured? + !!Chef::Config[:data_collector][:server_url] + end + + def self.why_run? + !!Chef::Config[:why_run] + end + + def self.token_auth_configured? + !!Chef::Config[:data_collector][:token] + end + + def self.solo? + !!Chef::Config[:solo] || !!Chef::Config[:local_mode] end def self.reporter_enabled_for_current_mode? @@ -54,7 +100,7 @@ class Chef class Reporter < EventDispatch::Base attr_reader :all_resource_reports, :status, :exception, :error_descriptions, :expanded_run_list, :run_context, :run_status, :http, - :current_resource_report, :enabled + :current_resource_report, :enabled, :deprecations def initialize validate_data_collector_server_url! @@ -63,8 +109,10 @@ class Chef @current_resource_loaded = nil @error_descriptions = {} @expanded_run_list = {} - @http = Chef::HTTP.new(data_collector_server_url) + @deprecations = Set.new @enabled = true + + @http = setup_http_client end # see EventDispatch::Base#run_started @@ -79,7 +127,7 @@ class Chef disable_reporter_on_error do send_to_data_collector( - Chef::DataCollector::Messages.run_start_message(current_run_status).to_json + Chef::DataCollector::Messages.run_start_message(current_run_status) ) end end @@ -223,8 +271,27 @@ class Chef ) end + # see EventDispatch::Base#deprecation + # Append a received deprecation to the list of deprecations + def deprecation(message, location = caller(2..2)[0]) + add_deprecation(message, location) + end + private + # Selects the type of HTTP client to use based on whether we are using + # token-based or signed header authentication. Token authentication is + # intended to be used primarily for Chef Solo in which case no signing + # key will be available (in which case `Chef::ServerAPI.new()` would + # raise an exception. + def setup_http_client + if data_collector_token.nil? + Chef::ServerAPI.new(data_collector_server_url) + else + Chef::HTTP::SimpleJSON.new(data_collector_server_url) + end + end + # # Yields to the passed-in block (which is expected to be some interaction # with the DataCollector endpoint). If some communication failure occurs, @@ -255,7 +322,9 @@ class Chef Chef::Log.error(msg) raise else - Chef::Log.warn(msg) + # Make the message non-scary for folks who don't have automate: + msg << " (This is normal if you do not have Chef Automate)" + Chef::Log.info(msg) end end @@ -288,8 +357,9 @@ class Chef expanded_run_list: expanded_run_list, resources: all_resource_reports, status: opts[:status], - error_descriptions: error_descriptions - ).to_json + error_descriptions: error_descriptions, + deprecations: deprecations.to_a + ) ) end @@ -340,6 +410,10 @@ class Chef @error_descriptions = discription_hash end + def add_deprecation(message, location) + @deprecations << { message: message, location: location } + end + def create_resource_report(new_resource, action, current_resource = nil) Chef::DataCollector::ResourceReport.new( new_resource, diff --git a/lib/chef/data_collector/messages.rb b/lib/chef/data_collector/messages.rb index 8c2a84b580..e0dfd6cb67 100644 --- a/lib/chef/data_collector/messages.rb +++ b/lib/chef/data_collector/messages.rb @@ -66,7 +66,7 @@ class Chef "entity_uuid" => node_uuid, "expanded_run_list" => reporter_data[:expanded_run_list], "id" => run_status.run_id, - "message_version" => "1.0.0", + "message_version" => "1.1.0", "message_type" => "run_converge", "node" => run_status.node, "node_name" => run_status.node.name, @@ -80,6 +80,7 @@ class Chef "status" => reporter_data[:status], "total_resource_count" => reporter_data[:resources].count, "updated_resource_count" => reporter_data[:resources].select { |r| r.report_data["status"] == "updated" }.count, + "deprecations" => reporter_data[:deprecations], } message["error"] = { diff --git a/lib/chef/event_dispatch/base.rb b/lib/chef/event_dispatch/base.rb index 04c960c7af..926bbe24b5 100644 --- a/lib/chef/event_dispatch/base.rb +++ b/lib/chef/event_dispatch/base.rb @@ -421,6 +421,9 @@ class Chef def msg(message) end + # Called when an attribute is changed by simple assignment + def attribute_changed(precedence, keys, value) + end end end end diff --git a/lib/chef/knife/core/generic_presenter.rb b/lib/chef/knife/core/generic_presenter.rb index 3f5c0712d0..861bf1510d 100644 --- a/lib/chef/knife/core/generic_presenter.rb +++ b/lib/chef/knife/core/generic_presenter.rb @@ -28,12 +28,19 @@ class Chef # :nodoc: def self.included(includer) includer.class_eval do - @attrs_to_show = [] + option :field_separator, + :short => "-S SEPARATOR", + :long => "--field-separator SEPARATOR", + :description => "Character separator used to delineate nesting in --attribute filters (default \".\")" + option :attribute, :short => "-a ATTR1 [-a ATTR2]", :long => "--attribute ATTR1 [--attribute ATTR2] ", - :proc => lambda { |val| @attrs_to_show << val }, - :description => "Show one or more attributes" + :description => "Show one or more attributes", + :proc => Proc.new { |a| + Chef::Config[:knife][:attribute] ||= [] + Chef::Config[:knife][:attribute].push(a) + } end end end @@ -173,8 +180,15 @@ class Chef config[:attribute] || config[:run_list] end + # GenericPresenter is used in contexts where MultiAttributeReturnOption + # is not, so we need to set the default value here rather than as part + # of the CLI option. + def attribute_field_separator + config[:field_separator] || "." + end + def extract_nested_value(data, nested_value_spec) - nested_value_spec.split(".").each do |attr| + nested_value_spec.split(attribute_field_separator).each do |attr| data = if data.is_a?(Array) data[attr.to_i] diff --git a/lib/chef/knife/node_show.rb b/lib/chef/knife/node_show.rb index c616b8ab72..3092b3fc27 100644 --- a/lib/chef/knife/node_show.rb +++ b/lib/chef/knife/node_show.rb @@ -55,11 +55,6 @@ class Chef node = Chef::Node.load(@node_name) output(format_for_display(node)) - self.class.attrs_to_show = [] - end - - def self.attrs_to_show=(attrs) - @attrs_to_show = attrs end end end diff --git a/lib/chef/knife/osc_user_show.rb b/lib/chef/knife/osc_user_show.rb index 22e9bf4dcd..5350837ad3 100644 --- a/lib/chef/knife/osc_user_show.rb +++ b/lib/chef/knife/osc_user_show.rb @@ -48,7 +48,6 @@ class Chef user = Chef::User.load(@user_name) output(format_for_display(user)) end - end end end diff --git a/lib/chef/knife/ssl_fetch.rb b/lib/chef/knife/ssl_fetch.rb index 5af1a905d5..238796c804 100644 --- a/lib/chef/knife/ssl_fetch.rb +++ b/lib/chef/knife/ssl_fetch.rb @@ -89,8 +89,11 @@ class Chef def cn_of(certificate) subject = certificate.subject - cn_field_tuple = subject.to_a.find { |field| field[0] == "CN" } - cn_field_tuple[1] + if cn_field_tuple = subject.to_a.find { |field| field[0] == "CN" } + cn_field_tuple[1] + else + nil + end end # Convert the CN of a certificate into something that will work well as a @@ -117,9 +120,10 @@ class Chef def write_cert(cert) FileUtils.mkdir_p(trusted_certs_dir) cn = cn_of(cert) - filename = File.join(trusted_certs_dir, "#{normalize_cn(cn)}.crt") - ui.msg("Adding certificate for #{cn} in #{filename}") - File.open(filename, File::CREAT | File::TRUNC | File::RDWR, 0644) do |f| + filename = cn.nil? ? "#{host}_#{Time.new.to_i}" : normalize_cn(cn) + full_path = File.join(trusted_certs_dir, "#{filename}.crt") + ui.msg("Adding certificate for #{filename} in #{full_path}") + File.open(full_path, File::CREAT | File::TRUNC | File::RDWR, 0644) do |f| f.print(cert.to_s) end end diff --git a/lib/chef/node.rb b/lib/chef/node.rb index 34a92d325b..7351a7bfa5 100644 --- a/lib/chef/node.rb +++ b/lib/chef/node.rb @@ -78,7 +78,7 @@ class Chef @policy_name = nil @policy_group = nil - @attributes = Chef::Node::Attribute.new({}, {}, {}, {}) + @attributes = Chef::Node::Attribute.new({}, {}, {}, {}, self) @run_state = {} end diff --git a/lib/chef/node/attribute.rb b/lib/chef/node/attribute.rb index 2d6aff0b21..d5b0ee5d72 100644 --- a/lib/chef/node/attribute.rb +++ b/lib/chef/node/attribute.rb @@ -187,21 +187,22 @@ class Chef # return the automatic level attribute component attr_reader :automatic - def initialize(normal, default, override, automatic) - @default = VividMash.new(default, self) - @env_default = VividMash.new({}, self) - @role_default = VividMash.new({}, self) - @force_default = VividMash.new({}, self) + def initialize(normal, default, override, automatic, node = nil) + @default = VividMash.new(default, self, node, :default) + @env_default = VividMash.new({}, self, node, :env_default) + @role_default = VividMash.new({}, self, node, :role_default) + @force_default = VividMash.new({}, self, node, :force_default) - @normal = VividMash.new(normal, self) + @normal = VividMash.new(normal, self, node, :normal) - @override = VividMash.new(override, self) - @role_override = VividMash.new({}, self) - @env_override = VividMash.new({}, self) - @force_override = VividMash.new({}, self) + @override = VividMash.new(override, self, node, :override) + @role_override = VividMash.new({}, self, node, :role_override) + @env_override = VividMash.new({}, self, node, :env_override) + @force_override = VividMash.new({}, self, node, :force_override) - @automatic = VividMash.new(automatic, self) - super() + @automatic = VividMash.new(automatic, self, node, :automatic) + + super(nil, self, node, :merged) end # Debug what's going on with an attribute. +args+ is a path spec to the @@ -232,59 +233,59 @@ class Chef # Set the cookbook level default attribute component to +new_data+. def default=(new_data) reset - @default = VividMash.new(new_data, self) + @default = VividMash.new(new_data, self, __node__, :default) end # Set the role level default attribute component to +new_data+ def role_default=(new_data) reset - @role_default = VividMash.new(new_data, self) + @role_default = VividMash.new(new_data, self, __node__, :role_default) end # Set the environment level default attribute component to +new_data+ def env_default=(new_data) reset - @env_default = VividMash.new(new_data, self) + @env_default = VividMash.new(new_data, self, __node__, :env_default) end # Set the force_default (+default!+) level attributes to +new_data+ def force_default=(new_data) reset - @force_default = VividMash.new(new_data, self) + @force_default = VividMash.new(new_data, self, __node__, :force_default) end # Set the normal level attribute component to +new_data+ def normal=(new_data) reset - @normal = VividMash.new(new_data, self) + @normal = VividMash.new(new_data, self, __node__, :normal) end # Set the cookbook level override attribute component to +new_data+ def override=(new_data) reset - @override = VividMash.new(new_data, self) + @override = VividMash.new(new_data, self, __node__, :override) end # Set the role level override attribute component to +new_data+ def role_override=(new_data) reset - @role_override = VividMash.new(new_data, self) + @role_override = VividMash.new(new_data, self, __node__, :role_override) end # Set the environment level override attribute component to +new_data+ def env_override=(new_data) reset - @env_override = VividMash.new(new_data, self) + @env_override = VividMash.new(new_data, self, __node__, :env_override) end def force_override=(new_data) reset - @force_override = VividMash.new(new_data, self) + @force_override = VividMash.new(new_data, self, __node__, :force_override) end def automatic=(new_data) reset - @automatic = VividMash.new(new_data, self) + @automatic = VividMash.new(new_data, self, __node__, :automatic) end # @@ -480,6 +481,7 @@ class Chef if symbol == :to_ary merged_attributes.send(symbol, *args) elsif args.empty? + puts symbol Chef.log_deprecation %q{method access to node attributes (node.foo.bar) is deprecated and will be removed in Chef 13, please use bracket syntax (node["foo"]["bar"])} if key?(symbol) self[symbol] @@ -565,7 +567,7 @@ class Chef return nil if components.compact.empty? - components.inject(ImmutableMash.new({}, self)) do |merged, component| + components.inject(ImmutableMash.new({}, self, __node__, :merged)) do |merged, component| Chef::Mixin::DeepMerge.hash_only_merge!(merged, component) end end diff --git a/lib/chef/node/attribute_collections.rb b/lib/chef/node/attribute_collections.rb index b01b447978..694b5fbc3a 100644 --- a/lib/chef/node/attribute_collections.rb +++ b/lib/chef/node/attribute_collections.rb @@ -34,7 +34,6 @@ class Chef :compact!, :default=, :default_proc=, - :delete, :delete_at, :delete_if, :fill, @@ -69,6 +68,11 @@ class Chef end end + def delete(key, &block) + send_reset_cache(__path__, key) + super + end + def initialize(data = []) super(data) map! { |e| convert_value(e) } @@ -94,9 +98,9 @@ class Chef when AttrArray value when Hash - VividMash.new(value, __root__) + VividMash.new(value, __root__, __node__, __precedence__) when Array - AttrArray.new(value, __root__) + AttrArray.new(value, __root__, __node__, __precedence__) else value end @@ -143,7 +147,7 @@ class Chef # object. def delete(key, &block) - send_reset_cache(__path__ + [ key ]) + send_reset_cache(__path__, key) super end @@ -170,7 +174,7 @@ class Chef def []=(key, value) ret = super - send_reset_cache(__path__ + [ key ]) + send_reset_cache(__path__, key) ret end @@ -209,9 +213,9 @@ class Chef when AttrArray value when Hash - VividMash.new(value, __root__) + VividMash.new(value, __root__, __node__, __precedence__) when Array - AttrArray.new(value, __root__) + AttrArray.new(value, __root__, __node__, __precedence__) else value end diff --git a/lib/chef/node/immutable_collections.rb b/lib/chef/node/immutable_collections.rb index 938135cbee..dad712e078 100644 --- a/lib/chef/node/immutable_collections.rb +++ b/lib/chef/node/immutable_collections.rb @@ -27,9 +27,9 @@ class Chef def immutablize(value) case value when Hash - ImmutableMash.new(value, __root__) + ImmutableMash.new(value, __root__, __node__, __precedence__) when Array - ImmutableArray.new(value, __root__) + ImmutableArray.new(value, __root__, __node__, __precedence__) else value end diff --git a/lib/chef/node/mixin/state_tracking.rb b/lib/chef/node/mixin/state_tracking.rb index 9be102eeeb..469d4d300f 100644 --- a/lib/chef/node/mixin/state_tracking.rb +++ b/lib/chef/node/mixin/state_tracking.rb @@ -21,33 +21,30 @@ class Chef module StateTracking attr_reader :__path__ attr_reader :__root__ + attr_reader :__node__ + attr_reader :__precedence__ - NULL = Object.new - - def initialize(data = NULL, root = self) + def initialize(data = nil, root = self, node = nil, precedence = nil) # __path__ and __root__ must be nil when we call super so it knows # to avoid resetting the cache on construction - data == NULL ? super() : super(data) + data.nil? ? super() : super(data) @__path__ = [] @__root__ = root + @__node__ = node + @__precedence__ = precedence end def [](key) ret = super - if ret.is_a?(StateTracking) - ret.__path__ = __path__ + [ convert_key(key) ] - ret.__root__ = __root__ - end - ret + next_path = [ __path__, convert_key(key) ].flatten.compact + copy_state_to(ret, next_path) end def []=(key, value) ret = super - if ret.is_a?(StateTracking) - ret.__path__ = __path__ + [ convert_key(key) ] - ret.__root__ = __root__ - end - ret + next_path = [ __path__, convert_key(key) ].flatten.compact + send_attribute_changed_event(next_path, value) + copy_state_to(ret, next_path) end protected @@ -60,10 +57,35 @@ class Chef @__root__ = root end + def __precedence__=(precedence) + @__precedence__ = precedence + end + + def __node__=(node) + @__node__ = node + end + private - def send_reset_cache(path = __path__) - __root__.reset_cache(path.first) if !__root__.nil? && __root__.respond_to?(:reset_cache) && !path.nil? + def send_attribute_changed_event(next_path, value) + if __node__ && __node__.run_context && __node__.run_context.events + __node__.run_context.events.attribute_changed(__precedence__, next_path, value) + end + end + + def send_reset_cache(path = nil, key = nil) + next_path = [ path, key ].flatten.compact + __root__.reset_cache(next_path.first) if !__root__.nil? && __root__.respond_to?(:reset_cache) && !next_path.nil? + end + + def copy_state_to(ret, next_path) + if ret.is_a?(StateTracking) + ret.__path__ = next_path + ret.__root__ = __root__ + ret.__node__ = __node__ + ret.__precedence__ = __precedence__ + end + ret end end end diff --git a/lib/chef/property.rb b/lib/chef/property.rb index a357ba9ee3..9433326b5b 100644 --- a/lib/chef/property.rb +++ b/lib/chef/property.rb @@ -522,22 +522,22 @@ class Chef # stack trace if you use `define_method`. declared_in.class_eval <<-EOM, __FILE__, __LINE__ + 1 def #{name}(value=NOT_PASSED) - raise "Property #{name} of \#{self} cannot be passed a block! If you meant to create a resource named #{name} instead, you'll need to first rename the property." if block_given? + raise "Property `#{name}` of `\#{self}` was incorrectly passed a block. Possible property-resource collision. To call a resource named `#{name}` either rename the property or else use `declare_resource(:#{name}, ...)`" if block_given? self.class.properties[#{name.inspect}].call(self, value) end def #{name}=(value) - raise "Property #{name} of \#{self} cannot be passed a block! If you meant to create a resource named #{name} instead, you'll need to first rename the property." if block_given? + raise "Property `#{name}` of `\#{self}` was incorrectly passed a block. Possible property-resource collision. To call a resource named `#{name}` either rename the property or else use `declare_resource(:#{name}, ...)`" if block_given? self.class.properties[#{name.inspect}].set(self, value) end EOM rescue SyntaxError # If the name is not a valid ruby name, we use define_method. declared_in.define_method(name) do |value = NOT_PASSED, &block| - raise "Property #{name} of #{self} cannot be passed a block! If you meant to create a resource named #{name} instead, you'll need to first rename the property." if block + raise "Property `#{name}` of `#{self}` was incorrectly passed a block! Possible property-resource collision. To call a resource named `#{name}` either rename the property or else use `declare_resource(:#{name}, ...)`" if block self.class.properties[name].call(self, value) end declared_in.define_method("#{name}=") do |value, &block| - raise "Property #{name} of #{self} cannot be passed a block! If you meant to create a resource named #{name} instead, you'll need to first rename the property." if block + raise "Property `#{name}` of `#{self}` was incorrectly passed a block! Possible property-resource collision. To call a resource named `#{name}` either rename the property or else use `declare_resource(:#{name}, ...)`" if block self.class.properties[name].set(self, value) end end diff --git a/lib/chef/provider/package.rb b/lib/chef/provider/package.rb index 3fed63c914..048807dd05 100644 --- a/lib/chef/provider/package.rb +++ b/lib/chef/provider/package.rb @@ -220,6 +220,34 @@ class Chef end end + def action_lock + if package_locked(@new_resource.name, @new_resource.version) == false + description = @new_resource.version ? "version #{@new_resource.version} of " : "" + converge_by("lock #{description}package #{@current_resource.package_name}") do + multipackage_api_adapter(@current_resource.package_name, @new_resource.version) do |name, version| + lock_package(name, version) + Chef::Log.info("#{@new_resource} locked") + end + end + else + Chef::Log.debug("#{new_resource} is already locked") + end + end + + def action_unlock + if package_locked(@new_resource.name, @new_resource.version) == true + description = @new_resource.version ? "version #{@new_resource.version} of " : "" + converge_by("unlock #{description}package #{@current_resource.package_name}") do + multipackage_api_adapter(@current_resource.package_name, @new_resource.version) do |name, version| + unlock_package(name, version) + Chef::Log.info("#{@new_resource} unlocked") + end + end + else + Chef::Log.debug("#{new_resource} is already unlocked") + end + end + # @todo use composition rather than inheritance def multipackage_api_adapter(name, version) @@ -254,6 +282,14 @@ class Chef raise( Chef::Exceptions::UnsupportedAction, "#{self} does not support :reconfig" ) end + def lock_package(name, version) + raise( Chef::Exceptions::UnsupportedAction, "#{self} does not support :lock" ) + end + + def unlock_package(name, version) + raise( Chef::Exceptions::UnsupportedAction, "#{self} does not support :unlock" ) + end + # used by subclasses. deprecated. use #a_to_s instead. def expand_options(options) options ? " #{options}" : "" diff --git a/lib/chef/provider/package/apt.rb b/lib/chef/provider/package/apt.rb index 8af089e14a..1c8ed8bc94 100644 --- a/lib/chef/provider/package/apt.rb +++ b/lib/chef/provider/package/apt.rb @@ -70,6 +70,18 @@ class Chef @candidate_version ||= get_candidate_versions end + def package_locked(name, version) + islocked = false + locked = shell_out_with_timeout!("apt-mark showhold") + locked.stdout.each_line do |line| + line_package = line.strip + if line_package == name + islocked = true + end + end + return islocked + end + def install_package(name, version) package_name = name.zip(version).map do |n, v| package_data[n][:virtual] ? n : "#{n}=#{v}" @@ -105,6 +117,14 @@ class Chef run_noninteractive("dpkg-reconfigure", name) end + def lock_package(name, version) + run_noninteractive("apt-mark", new_resource.options, "hold", name) + end + + def unlock_package(name, version) + run_noninteractive("apt-mark", new_resource.options, "unhold", name) + end + private # Runs command via shell_out with magic environment to disable diff --git a/lib/chef/provider/package/yum.rb b/lib/chef/provider/package/yum.rb index 74d52946f7..022fdcae09 100644 --- a/lib/chef/provider/package/yum.rb +++ b/lib/chef/provider/package/yum.rb @@ -123,6 +123,18 @@ class Chef end end + def package_locked(name, version) + islocked = false + locked = shell_out_with_timeout!("yum versionlock") + locked.stdout.each_line do |line| + line_package = line.sub(/-[^-]*-[^-]*$/, "").split(":").last.strip + if line_package == name + islocked = true + end + end + return islocked + end + # Standard Provider methods for Parent # @@ -369,6 +381,14 @@ class Chef remove_package(name, version) end + def lock_package(name, version) + yum_command("-d0 -e0 -y#{expand_options(@new_resource.options)} versionlock add #{name}") + end + + def unlock_package(name, version) + yum_command("-d0 -e0 -y#{expand_options(@new_resource.options)} versionlock delete #{name}") + end + private def parse_arch(package_name) diff --git a/lib/chef/provider/package/zypper.rb b/lib/chef/provider/package/zypper.rb index e20a7332f7..edad45c3e4 100644 --- a/lib/chef/provider/package/zypper.rb +++ b/lib/chef/provider/package/zypper.rb @@ -75,6 +75,18 @@ class Chef end end + def package_locked(name, version) + islocked = false + locked = shell_out_with_timeout!("zypper locks") + locked.stdout.each_line do |line| + line_package = line.split("|").shift(2).last.strip + if line_package == name + islocked = true + end + end + return islocked + end + def load_current_resource @current_resource = Chef::Resource::ZypperPackage.new(new_resource.name) current_resource.package_name(new_resource.package_name) @@ -107,6 +119,14 @@ class Chef zypper_package("remove --clean-deps", name, version) end + def lock_package(name, version) + zypper_package("addlock", name, version) + end + + def unlock_package(name, version) + zypper_package("removelock", name, version) + end + private def zip(names, versions) diff --git a/lib/chef/provider/service/upstart.rb b/lib/chef/provider/service/upstart.rb index 6e2a3b6473..9c0d97d376 100644 --- a/lib/chef/provider/service/upstart.rb +++ b/lib/chef/provider/service/upstart.rb @@ -26,6 +26,9 @@ class Chef class Service class Upstart < Chef::Provider::Service::Simple + # to maintain a local state of service across restart's internal calls + attr_accessor :upstart_service_running + provides :service, platform_family: "debian", override: true do |node| Chef::Platform::ServiceHelpers.service_resource_providers.include?(:upstart) end @@ -110,23 +113,23 @@ class Chef begin if shell_out!(@new_resource.status_command).exitstatus == 0 - @current_resource.running true + @upstart_service_running = true end rescue @command_success = false - @current_resource.running false + @upstart_service_running = false nil end else begin if upstart_goal_state == "start" - @current_resource.running true + @upstart_service_running = true else - @current_resource.running false + @upstart_service_running = false end rescue Chef::Exceptions::Exec @command_success = false - @current_resource.running false + @upstart_service_running = false nil end end @@ -153,13 +156,14 @@ class Chef @current_resource.enabled false end + @current_resource.running @upstart_service_running @current_resource end def start_service # Calling start on a service that is already started will return 1 # Our 'goal' when we call start is to ensure the service is started - if @current_resource.running + if @upstart_service_running Chef::Log.debug("#{@new_resource} already running, not starting") else if @new_resource.start_command @@ -168,12 +172,14 @@ class Chef shell_out_with_systems_locale!("/sbin/start #{@job}") end end + + @upstart_service_running = true end def stop_service # Calling stop on a service that is already stopped will return 1 # Our 'goal' when we call stop is to ensure the service is stopped - unless @current_resource.running + unless @upstart_service_running Chef::Log.debug("#{@new_resource} not running, not stopping") else if @new_resource.stop_command @@ -182,6 +188,8 @@ class Chef shell_out_with_systems_locale!("/sbin/stop #{@job}") end end + + @upstart_service_running = false end def restart_service @@ -189,13 +197,19 @@ class Chef super # Upstart always provides restart functionality so we don't need to mimic it with stop/sleep/start. # Older versions of upstart would fail on restart if the service was currently stopped, check for that. LP:430883 + # But for safe working of latest upstart job config being loaded, 'restart' can't be used as per link + # http://upstart.ubuntu.com/cookbook/#restart (it doesn't uses latest jon config from disk but retains old) else - if @current_resource.running - shell_out_with_systems_locale!("/sbin/restart #{@job}") + if @upstart_service_running + stop_service + sleep 1 + start_service else start_service end end + + @upstart_service_running = true end def reload_service @@ -205,6 +219,8 @@ class Chef # upstart >= 0.6.3-4 supports reload (HUP) shell_out_with_systems_locale!("/sbin/reload #{@job}") end + + @upstart_service_running = true end # https://bugs.launchpad.net/upstart/+bug/94065 diff --git a/lib/chef/provider/user/dscl.rb b/lib/chef/provider/user/dscl.rb index 01203c0d9f..16d60ba116 100644 --- a/lib/chef/provider/user/dscl.rb +++ b/lib/chef/provider/user/dscl.rb @@ -51,6 +51,11 @@ class Chef provides :dscl_user provides :user, os: "darwin" + # Just-in-case a recipe calls the user dscl provider without specifying + # a gid property. Avoids chown issues in move_home when the manage_home + # property is in use. #5393 + STAFF_GROUP_ID = 20 + def define_resource_requirements super @@ -264,12 +269,12 @@ user password using shadow hash.") # # Sets the group id for the user using dscl. Fails if a group doesn't # exist on the system with given group id. If `gid` is not specified, it - # sets a default Mac user group "staff", with id 20. + # sets a default Mac user group "staff", with id 20 using the CONSTANT # def dscl_set_gid if new_resource.gid.nil? # XXX: mutates the new resource - new_resource.gid(20) + new_resource.gid(STAFF_GROUP_ID) elsif !new_resource.gid.to_s.match(/^\d+$/) begin possible_gid = run_dscl("read /Groups/#{new_resource.gid} PrimaryGroupID").split(" ").last @@ -329,7 +334,7 @@ user password using shadow hash.") def move_home Chef::Log.debug("#{new_resource} moving #{self} home from #{current_resource.home} to #{new_resource.home}") - + new_resource.gid(STAFF_GROUP_ID) if new_resource.gid.nil? src = current_resource.home FileUtils.mkdir_p(new_resource.home) files = ::Dir.glob("#{Chef::Util::PathHelper.escape_glob_dir(src)}/*", ::File::FNM_DOTMATCH) - ["#{src}/.", "#{src}/.."] diff --git a/lib/chef/provider/user/solaris.rb b/lib/chef/provider/user/solaris.rb index 04567e6bca..7aa0ceb93a 100644 --- a/lib/chef/provider/user/solaris.rb +++ b/lib/chef/provider/user/solaris.rb @@ -46,21 +46,14 @@ class Chef end def check_lock - shadow_line = shell_out!("getent", "shadow", new_resource.username).stdout.strip rescue nil + user = IO.read(@password_file).match(/^#{Regexp.escape(@new_resource.username)}:([^:]*):/) - # if the command fails we return nil, this can happen if the user - # in question doesn't exist - return nil if shadow_line.nil? + # If we're in whyrun mode, and the user is not created, we assume it will be + return false if whyrun_mode? && user.nil? - # convert "dave:NP:16507::::::\n" to "NP" - fields = shadow_line.split(":") + raise Chef::Exceptions::User, "Cannot determine if #{@new_resource} is locked!" if user.nil? - # '*LK*...' and 'LK' are both considered locked, - # so look for LK at the beginning of the shadow entry - # optionally surrounded by '*' - @locked = !!fields[1].match(/^\*?LK\*?/) - - @locked + @locked = user[1].start_with?("*LK*") end def lock_user diff --git a/lib/chef/provider/yum_repository.rb b/lib/chef/provider/yum_repository.rb index 09ff2c5512..be4d43f7ad 100644 --- a/lib/chef/provider/yum_repository.rb +++ b/lib/chef/provider/yum_repository.rb @@ -76,18 +76,17 @@ class Chef end action :delete do + # clean the repo cache first + declare_resource(:execute, "yum clean all #{new_resource.repositoryid}") do + command "yum clean all --disablerepo=* --enablerepo=#{new_resource.repositoryid}" + only_if "yum repolist all | grep -P '^#{new_resource.repositoryid}([ \t]|$)'" + end + declare_resource(:file, "/etc/yum.repos.d/#{new_resource.repositoryid}.repo") do action :delete - notifies :run, "execute[yum clean all #{new_resource.repositoryid}]", :immediately notifies :create, "ruby_block[yum-cache-reload-#{new_resource.repositoryid}]", :immediately end - declare_resource(:execute, "yum clean all #{new_resource.repositoryid}") do - command "yum clean all --disablerepo=* --enablerepo=#{new_resource.repositoryid}" - only_if "yum repolist | grep -P '^#{new_resource.repositoryid}([ \t]|$)'" - action :nothing - end - declare_resource(:ruby_block, "yum-cache-reload-#{new_resource.repositoryid}") do block { Chef::Provider::Package::Yum::YumCache.instance.reload } action :nothing diff --git a/lib/chef/resource/package.rb b/lib/chef/resource/package.rb index 32339e1a24..0738107339 100644 --- a/lib/chef/resource/package.rb +++ b/lib/chef/resource/package.rb @@ -25,7 +25,7 @@ class Chef resource_name :package default_action :install - allowed_actions :install, :upgrade, :remove, :purge, :reconfig + allowed_actions :install, :upgrade, :remove, :purge, :reconfig, :lock, :unlock def initialize(name, *args) # We capture name here, before it gets coerced to name diff --git a/lib/chef/rest.rb b/lib/chef/rest.rb index a3c5c66b8a..b3793418ce 100644 --- a/lib/chef/rest.rb +++ b/lib/chef/rest.rb @@ -37,6 +37,7 @@ require "chef/config" require "chef/exceptions" require "chef/platform/query_helpers" require "chef/http/remote_request_id" +require "chef/chef_class" class Chef diff --git a/lib/chef/version.rb b/lib/chef/version.rb index 5105e5355e..b6fc2ca5a4 100644 --- a/lib/chef/version.rb +++ b/lib/chef/version.rb @@ -21,7 +21,7 @@ class Chef CHEF_ROOT = File.expand_path("../..", __FILE__) - VERSION = "12.16.25" + VERSION = "12.16.49" end # |