summaryrefslogtreecommitdiff
path: root/lib/chef
diff options
context:
space:
mode:
authorGregory Batye <gbatye@fb.com>2016-11-07 14:51:33 -0800
committerGregory Batye <gbatye@fb.com>2016-11-07 14:51:33 -0800
commit7387b08a2ca96ed99d9828d67259ad9fb10f513a (patch)
tree07d26ccc9a21c897412053ca122c5e2ad1884ad3 /lib/chef
parent3050a0594ddf3a6a65020df036226594014534b0 (diff)
parent1d61e460675a540536bd4fc893b4bc6aa7070f21 (diff)
downloadchef-7387b08a2ca96ed99d9828d67259ad9fb10f513a.tar.gz
Merge remote-tracking branch 'chef/master' into fix_osx_profile
Diffstat (limited to 'lib/chef')
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/cookbook_file.rb10
-rw-r--r--lib/chef/data_collector.rb92
-rw-r--r--lib/chef/data_collector/messages.rb3
-rw-r--r--lib/chef/event_dispatch/base.rb3
-rw-r--r--lib/chef/knife/core/generic_presenter.rb22
-rw-r--r--lib/chef/knife/node_show.rb5
-rw-r--r--lib/chef/knife/osc_user_show.rb1
-rw-r--r--lib/chef/knife/ssl_fetch.rb14
-rw-r--r--lib/chef/node.rb2
-rw-r--r--lib/chef/node/attribute.rb48
-rw-r--r--lib/chef/node/attribute_collections.rb18
-rw-r--r--lib/chef/node/immutable_collections.rb4
-rw-r--r--lib/chef/node/mixin/state_tracking.rb54
-rw-r--r--lib/chef/property.rb8
-rw-r--r--lib/chef/provider/package.rb36
-rw-r--r--lib/chef/provider/package/apt.rb20
-rw-r--r--lib/chef/provider/package/yum.rb20
-rw-r--r--lib/chef/provider/package/zypper.rb20
-rw-r--r--lib/chef/provider/service/upstart.rb34
-rw-r--r--lib/chef/provider/user/dscl.rb11
-rw-r--r--lib/chef/provider/user/solaris.rb17
-rw-r--r--lib/chef/provider/yum_repository.rb13
-rw-r--r--lib/chef/resource/package.rb2
-rw-r--r--lib/chef/rest.rb1
-rw-r--r--lib/chef/version.rb2
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
#