summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLamont Granquist <lamont@scriptkiddie.org>2015-07-09 09:00:32 -0700
committerLamont Granquist <lamont@scriptkiddie.org>2015-07-09 09:00:32 -0700
commitcdfc38fca8467c6677b7753b89e3165db3722a58 (patch)
treee6e5c58a322efe8bddf609ba7c40e40c8920a36e
parent906d0dd25069805491882df854ca62f94888504e (diff)
downloadchef-lcg/cloning-behavior.tar.gz
add tweakable cloning behaviorlcg/cloning-behavior
-rw-r--r--chef-config/lib/chef-config/config.rb8
-rw-r--r--lib/chef/client.rb18
-rw-r--r--lib/chef/resource.rb16
-rw-r--r--lib/chef/resource_builder.rb25
-rw-r--r--spec/unit/recipe_spec.rb107
-rw-r--r--spec/unit/resource_spec.rb3
6 files changed, 164 insertions, 13 deletions
diff --git a/chef-config/lib/chef-config/config.rb b/chef-config/lib/chef-config/config.rb
index 301a3ba0b6..3b392bd108 100644
--- a/chef-config/lib/chef-config/config.rb
+++ b/chef-config/lib/chef-config/config.rb
@@ -720,6 +720,14 @@ module ChefConfig
# break Chef community cookbooks and is very highly discouraged.
default :ruby_encoding, Encoding::UTF_8
+ # Default resource cloning behavior when an identically named resource is found on the resource collection.
+ # :clone - CHEF-3694-style resource cloning behavior
+ # :rewind - chef-rewind style behavior where the prior resource is updated in-place in the resource collection
+ # :none - no cloning.
+ #
+ # Setting the default to :rewind is not recommended.
+ default :default_resource_cloning_behavior, :clone
+
# If installed via an omnibus installer, this gives the path to the
# "embedded" directory which contains all of the software packaged with
# omnibus. This is used to locate the cacert.pem file on windows.
diff --git a/lib/chef/client.rb b/lib/chef/client.rb
index 3c86f52b4a..3aad0c9f80 100644
--- a/lib/chef/client.rb
+++ b/lib/chef/client.rb
@@ -381,13 +381,25 @@ class Chef
end
end
+ #
# Resource reporters send event information back to the chef server for
- # processing. Can only be called after we have a @rest object
+ # processing
+ #
# @api private
+ #
def register_reporters
+ # Audit and Resource Reporters set validate_utf8 to false so that they
+ # never fail if we accidentally send binary data through (validate_utf8
+ # will raise when set to true, and sanitizes to utf8 when false)
+ reporter_rest = Chef::REST.new(
+ config[:chef_server_url],
+ client_name,
+ config[:client_key],
+ validate_utf8: false,
+ )
[
- Chef::ResourceReporter.new(rest),
- Chef::Audit::AuditReporter.new(rest)
+ Chef::ResourceReporter.new(reporter_rest),
+ Chef::Audit::AuditReporter.new(reporter_rest)
].each do |r|
events.register(r)
end
diff --git a/lib/chef/resource.rb b/lib/chef/resource.rb
index ac6f5e8923..f0a7751743 100644
--- a/lib/chef/resource.rb
+++ b/lib/chef/resource.rb
@@ -127,6 +127,7 @@ class Chef
@default_guard_interpreter = :default
@elapsed_time = 0
@sensitive = false
+ @cloning_behavior = Chef::Config[:default_resource_cloning_behavior]
end
#
@@ -174,6 +175,21 @@ class Chef
end
end
+ #
+ # The kind of cloning behavior on the resource. Supported values are:
+ #
+ # :clone - old CHEF-3694 style resource cloning
+ # :rewind - overwrite the resource properties of the prior resource directly
+ # :none - no cloning at all
+ #
+ def cloning_behavior(arg=nil)
+ set_or_return(
+ :cloning_behavior,
+ arg,
+ kind_of: Symbol,
+ )
+ end
+
# Alias for normal assigment syntax.
alias_method :action=, :action
diff --git a/lib/chef/resource_builder.rb b/lib/chef/resource_builder.rb
index 9e9f7047a4..b1deedb00d 100644
--- a/lib/chef/resource_builder.rb
+++ b/lib/chef/resource_builder.rb
@@ -49,23 +49,30 @@ class Chef
def build(&block)
raise ArgumentError, "You must supply a name when declaring a #{type} resource" if name.nil?
- @resource = resource_class.new(name, run_context)
- if resource.resource_name.nil?
- raise Chef::Exceptions::InvalidResourceSpecification, "#{resource}.resource_name is `nil`! Did you forget to put `provides :blah` or `resource_name :blah` in your resource class?"
+ if prior_resource && prior_resource.cloning_behavior == :rewind
+ @resource = prior_resource
+ else
+ @resource = resource_class.new(name, run_context)
+ if resource.resource_name.nil?
+ raise Chef::Exceptions::InvalidResourceSpecification, "#{resource}.resource_name is `nil`! Did you forget to put `provides :blah` or `resource_name :blah` in your resource class?"
+ end
+ resource.source_line = created_at
+ resource.declared_type = type
end
- resource.source_line = created_at
- resource.declared_type = type
# If we have a resource like this one, we want to steal its state
# This behavior is very counter-intuitive and should be removed.
# See CHEF-3694, https://tickets.opscode.com/browse/CHEF-3694
# Moved to this location to resolve CHEF-5052, https://tickets.opscode.com/browse/CHEF-5052
- if prior_resource
+ if prior_resource && prior_resource.cloning_behavior == :clone
resource.load_from(prior_resource)
end
- resource.cookbook_name = cookbook_name
- resource.recipe_name = recipe_name
+ unless prior_resource && prior_resource.cloning_behavior == :rewind
+ resource.cookbook_name = cookbook_name
+ resource.recipe_name = recipe_name
+ end
+
# Determine whether this resource is being created in the context of an enclosing Provider
resource.enclosing_provider = enclosing_provider
@@ -77,7 +84,7 @@ class Chef
resource.instance_eval(&block) if block_given?
# emit a cloned resource warning if it is warranted
- if prior_resource
+ if prior_resource && prior_resource.cloning_behavior == :clone
if is_trivial_resource?(prior_resource) && identicalish_resources?(prior_resource, resource)
emit_harmless_cloning_debug
else
diff --git a/spec/unit/recipe_spec.rb b/spec/unit/recipe_spec.rb
index 511e7e9397..f60cb9c7de 100644
--- a/spec/unit/recipe_spec.rb
+++ b/spec/unit/recipe_spec.rb
@@ -404,6 +404,10 @@ describe Chef::Recipe do
end
end
+ before do
+ duplicated_resource # evaluate the lazy resources in the correct order
+ end
+
it "copies attributes from the first resource" do
expect(duplicated_resource.something).to eq("bvb09")
end
@@ -431,6 +435,109 @@ describe Chef::Recipe do
end
+ describe "resource cloning with :rewind" do
+ let(:second_recipe) do
+ Chef::Recipe.new("second_cb", "second_recipe", run_context)
+ end
+
+ let(:original_resource) do
+ recipe.zen_master("klopp") do
+ something "bvb09"
+ cloning_behavior :rewind
+ action :score
+ end
+ end
+
+ let(:duplicated_resource) do
+ original_resource
+ second_recipe.zen_master("klopp") do
+ something "else"
+ end
+ end
+
+ before do
+ duplicated_resource # evaluate the lazy resources in the correct order
+ end
+
+ it "the second resource points at the first resource" do
+ expect(duplicated_resource).to eq(original_resource)
+ end
+
+ it "the first resources property is updated" do
+ expect(duplicated_resource.something).to eq("else")
+ expect(original_resource.something).to eq("else")
+ end
+
+ it "the cookbook_name should come from the first resource" do
+ expect(original_resource.cookbook_name).to eq("hjk")
+ expect(original_resource.cookbook_name).to eq(duplicated_resource.cookbook_name)
+ end
+
+ it "the recipe_name should come from the first resource" do
+ expect(original_resource.recipe_name).to eq("test")
+ expect(original_resource.recipe_name).to eq(duplicated_resource.recipe_name)
+ end
+
+ it "the source_line should make sense" do
+ # hard to test if this is from the first or second resource
+ expect(original_resource.source_line).to include(__FILE__)
+ expect(original_resource.source_line).to eq(duplicated_resource.source_line)
+ end
+ end
+
+ describe "resource cloning with :none" do
+ let(:second_recipe) do
+ Chef::Recipe.new("second_cb", "second_recipe", run_context)
+ end
+
+ let(:original_resource) do
+ recipe.zen_master("klopp") do
+ something "bvb09"
+ cloning_behavior :none
+ action :score
+ end
+ end
+
+ let(:duplicated_resource) do
+ original_resource
+ second_recipe.zen_master("klopp") do
+ end
+ end
+
+ before do
+ duplicated_resource # evaluate the lazy resources in the correct order
+ end
+
+ it "the second resource is different from the first resource" do
+ expect(duplicated_resource).not_to eq(original_resource)
+ end
+
+ it "does not copy attributes from the first resource" do
+ expect(duplicated_resource.something).to eq(nil)
+ end
+
+ it "does not copy the action from the first resource" do
+ expect(original_resource.action).to eq([:score])
+ expect(duplicated_resource.action).to eq([:nothing])
+ end
+
+ it "does not copy the source location of the first resource" do
+ # sanity check source location:
+ expect(original_resource.source_line).to include(__FILE__)
+ expect(duplicated_resource.source_line).to include(__FILE__)
+ # actual test:
+ expect(original_resource.source_line).not_to eq(duplicated_resource.source_line)
+ end
+
+ it "sets the cookbook name on the cloned resource to that resource's cookbook" do
+ expect(duplicated_resource.cookbook_name).to eq("second_cb")
+ end
+
+ it "sets the recipe name on the cloned resource to that resoure's recipe" do
+ expect(duplicated_resource.recipe_name).to eq("second_recipe")
+ end
+ end
+
describe "resource definitions" do
it "should execute defined resources" do
crow_define = Chef::ResourceDefinition.new
diff --git a/spec/unit/resource_spec.rb b/spec/unit/resource_spec.rb
index b9ba80068b..ec6f45ba60 100644
--- a/spec/unit/resource_spec.rb
+++ b/spec/unit/resource_spec.rb
@@ -443,7 +443,8 @@ describe Chef::Resource do
:updated_by_last_action, :before, :supports,
:noop, :ignore_failure, :name, :source_line,
:action, :retries, :retry_delay, :elapsed_time,
- :default_guard_interpreter, :guard_interpreter, :sensitive ]
+ :default_guard_interpreter, :guard_interpreter, :sensitive,
+ :cloning_behavior ]
expect(hash.keys - expected_keys).to eq([])
expect(expected_keys - hash.keys).to eq([])
expect(hash[:name]).to eql("funk")