diff options
author | John Keiser <john@johnkeiser.com> | 2015-06-16 16:27:52 -0700 |
---|---|---|
committer | John Keiser <john@johnkeiser.com> | 2015-06-18 15:07:48 -0700 |
commit | db59fd9392a97328df8d7ba7f1c2b1fc03058e76 (patch) | |
tree | a5ec9072139c642ffb39d80866512ad0150dfaf5 | |
parent | 59717769a12768c62aa18270a51b91c820b67010 (diff) | |
download | chef-db59fd9392a97328df8d7ba7f1c2b1fc03058e76.tar.gz |
Sort identical "provides" alphabetically (for backcompat)
- Warn when multiple providers try to provide the same thing
-rw-r--r-- | lib/chef/chef_class.rb | 8 | ||||
-rw-r--r-- | lib/chef/node_map.rb | 101 | ||||
-rw-r--r-- | lib/chef/platform/priority_map.rb | 50 | ||||
-rw-r--r-- | lib/chef/platform/provider_mapping.rb | 4 | ||||
-rw-r--r-- | lib/chef/platform/provider_priority_map.rb | 24 | ||||
-rw-r--r-- | lib/chef/platform/resource_priority_map.rb | 27 | ||||
-rw-r--r-- | lib/chef/provider.rb | 2 | ||||
-rw-r--r-- | lib/chef/provider/service/debian.rb | 2 | ||||
-rw-r--r-- | lib/chef/provider/service/insserv.rb | 2 | ||||
-rw-r--r-- | lib/chef/provider/service/invokercd.rb | 2 | ||||
-rw-r--r-- | lib/chef/provider/service/redhat.rb | 2 | ||||
-rw-r--r-- | lib/chef/provider/service/upstart.rb | 2 | ||||
-rw-r--r-- | lib/chef/provider/user.rb | 2 | ||||
-rw-r--r-- | lib/chef/resource.rb | 2 | ||||
-rw-r--r-- | lib/chef/resource/macports_package.rb | 3 | ||||
-rw-r--r-- | lib/chef/resource/package.rb | 5 | ||||
-rw-r--r-- | spec/integration/recipes/recipe_dsl_spec.rb | 215 | ||||
-rw-r--r-- | spec/unit/provider_resolver_spec.rb | 1 | ||||
-rw-r--r-- | spec/unit/recipe_spec.rb | 16 | ||||
-rw-r--r-- | spec/unit/resource_spec.rb | 6 |
20 files changed, 337 insertions, 139 deletions
diff --git a/lib/chef/chef_class.rb b/lib/chef/chef_class.rb index f1dd797c04..a0c74f7aec 100644 --- a/lib/chef/chef_class.rb +++ b/lib/chef/chef_class.rb @@ -58,7 +58,7 @@ class Chef # @return [Array<Class>] Priority Array of Provider Classes to use for the resource_name on the node # def get_provider_priority_array(resource_name) - result = provider_priority_map.get_priority_array(node, resource_name) + result = provider_priority_map.get_priority_array(node, resource_name.to_sym) result = result.dup if result result end @@ -71,7 +71,7 @@ class Chef # @return [Array<Class>] Priority Array of Resource Classes to use for the resource_name on the node # def get_resource_priority_array(resource_name) - result = resource_priority_map.get_priority_array(node, resource_name) + result = resource_priority_map.get_priority_array(node, resource_name.to_sym) result = result.dup if result result end @@ -86,7 +86,7 @@ class Chef # @return [Array<Class>] Modified Priority Array of Provider Classes to use for the resource_name on the node # def set_provider_priority_array(resource_name, priority_array, *filter, &block) - result = provider_priority_map.set_priority_array(resource_name, priority_array, *filter, &block) + result = provider_priority_map.set_priority_array(resource_name.to_sym, priority_array, *filter, &block) result = result.dup if result result end @@ -101,7 +101,7 @@ class Chef # @return [Array<Class>] Modified Priority Array of Resource Classes to use for the resource_name on the node # def set_resource_priority_array(resource_name, priority_array, *filter, &block) - result = resource_priority_map.set_priority_array(resource_name, priority_array, *filter, &block) + result = resource_priority_map.set_priority_array(resource_name.to_sym, priority_array, *filter, &block) result = result.dup if result result end diff --git a/lib/chef/node_map.rb b/lib/chef/node_map.rb index f547018a38..d5eed7c215 100644 --- a/lib/chef/node_map.rb +++ b/lib/chef/node_map.rb @@ -38,30 +38,35 @@ class Chef # # @return [NodeMap] Returns self for possible chaining # - def set(key, value, platform: nil, platform_version: nil, platform_family: nil, os: nil, on_platform: nil, on_platforms: nil, canonical: nil, &block) + def set(key, value, platform: nil, platform_version: nil, platform_family: nil, os: nil, on_platform: nil, on_platforms: nil, canonical: nil, override: nil, &block) Chef::Log.deprecation "The on_platform option to node_map has been deprecated" if on_platform Chef::Log.deprecation "The on_platforms option to node_map has been deprecated" if on_platforms platform ||= on_platform || on_platforms - filters = { platform: platform, platform_version: platform_version, platform_family: platform_family, os: os } - new_matcher = { filters: filters, block: block, value: value, canonical: canonical } - @map[key] ||= [] - # Decide where to insert the matcher; the new value is preferred over - # anything more specific (see `priority_of`) and is preferred over older - # values of the same specificity. (So all other things being equal, - # newest wins.) + filters = {} + filters[:platform] = platform if platform + filters[:platform_version] = platform_version if platform_version + filters[:platform_family] = platform_family if platform_family + filters[:os] = os if os + new_matcher = { value: value, filters: filters } + new_matcher[:block] = block if block + new_matcher[:canonical] = canonical if canonical + new_matcher[:override] = override if override + + # The map is sorted in order of preference already; we just need to find + # our place in it (just before the first value with the same preference level). insert_at = nil - @map[key].each_with_index do |matcher, index| - if specificity(new_matcher) >= specificity(matcher) - insert_at = index - break - end + @map[key] ||= [] + @map[key].each_with_index do |matcher,index| + cmp = compare_matchers(key, new_matcher, matcher) + insert_at ||= index if cmp && cmp <= 0 end if insert_at @map[key].insert(insert_at, new_matcher) else @map[key] << new_matcher end - self + insert_at ||= @map[key].size - 1 + @map end # @@ -116,30 +121,7 @@ class Chef remaining end - private - - # - # Gives a value for "how specific" the matcher is. - # Things which specify more specific filters get a higher number - # (platform_version > platform > platform_family > os); things - # with a block have higher specificity than similar things without - # a block. - # - def specificity(matcher) - if matcher[:filters][:platform_version] - specificity = 8 - elsif matcher[:filters][:platform] - specificity = 6 - elsif matcher[:filters][:platform_family] - specificity = 4 - elsif matcher[:filters][:os] - specificity = 2 - else - specificity = 0 - end - specificity += 1 if matcher[:block] - specificity - end + protected # # Succeeds if: @@ -197,5 +179,48 @@ class Chef return true if canonical.nil? !!canonical == !!matcher[:canonical] end + + def compare_matchers(key, new_matcher, matcher) + cmp = compare_matcher_properties(new_matcher, matcher) { |m| m[:filters][:block] } + return cmp if cmp != 0 + cmp = compare_matcher_properties(new_matcher, matcher) { |m| m[:filters][:platform_version] } + return cmp if cmp != 0 + cmp = compare_matcher_properties(new_matcher, matcher) { |m| m[:filters][:platform] } + return cmp if cmp != 0 + cmp = compare_matcher_properties(new_matcher, matcher) { |m| m[:filters][:platform_family] } + return cmp if cmp != 0 + cmp = compare_matcher_properties(new_matcher, matcher) { |m| m[:filters][:os] } + return cmp if cmp != 0 + cmp = compare_matcher_properties(new_matcher, matcher) { |m| m[:override] } + return cmp if cmp != 0 + # If all things are identical, return 0 + 0 + end + + def compare_matcher_properties(new_matcher, matcher) + a = yield(new_matcher) + b = yield(matcher) + + # Check for blcacklists ('!windows'). Those always come *after* positive + # whitelists. + a_negated = Array(a).any? { |f| f.is_a?(String) && f.start_with?('!') } + b_negated = Array(b).any? { |f| f.is_a?(String) && f.start_with?('!') } + if a_negated != b_negated + return 1 if a_negated + return -1 if b_negated + end + + # We treat false / true and nil / not-nil with the same comparison + a = nil if a == false + b = nil if b == false + cmp = a <=> b + # This is the case where one is non-nil, and one is nil. The one that is + # nil is "greater" (i.e. it should come last). + if cmp.nil? + return 1 if a.nil? + return -1 if b.nil? + end + cmp + end end end diff --git a/lib/chef/platform/priority_map.rb b/lib/chef/platform/priority_map.rb new file mode 100644 index 0000000000..3608975b51 --- /dev/null +++ b/lib/chef/platform/priority_map.rb @@ -0,0 +1,50 @@ +require 'chef/node_map' + +class Chef + class Platform + class PriorityMap < Chef::NodeMap + # @api private + def get_priority_array(node, key) + get(node, key) + end + + # @api private + def set_priority_array(key, priority_array, *filter, &block) + priority_array = Array(priority_array) + set(key, priority_array, *filter, &block) + priority_array + end + + # @api private + def list_handlers(node, key, **filters) + list(node, key, **filters).flatten(1).uniq + end + + # + # Priority maps have one extra precedence: priority arrays override "provides," + # and "provides" lines with identical filters sort by class name (ascending). + # + def compare_matchers(key, new_matcher, matcher) + # Priority arrays come before "provides" + if new_matcher[:value].is_a?(Array) != matcher[:value].is_a?(Array) + return new_matcher[:value].is_a?(Array) ? -1 : 1 + end + + cmp = super + if cmp == 0 + # Sort by class name (ascending) as well, if all other properties + # are exactly equal + if new_matcher[:value].is_a?(Class) && !new_matcher[:override] + cmp = compare_matcher_properties(new_matcher, matcher) { |m| m[:value].name } + if cmp < 0 + Chef::Log.warn "You are overriding #{key} on #{new_matcher[:filters].inspect} with #{new_matcher[:value].inspect}: used to be #{matcher[:value].inspect}. Use override: true if this is what you intended." + elsif cmp > 0 + Chef::Log.warn "You declared a new resource #{new_matcher[:value].inspect} for resource #{key}, but it comes alphabetically after #{matcher[:value].inspect} and has the same filters (#{new_matcher[:filters].inspect}), so it will not be used. Use override: true if you want to use it for #{key}." + end + end + end + cmp + end + end + end +end diff --git a/lib/chef/platform/provider_mapping.rb b/lib/chef/platform/provider_mapping.rb index e3a894c8ac..af17d8e1b4 100644 --- a/lib/chef/platform/provider_mapping.rb +++ b/lib/chef/platform/provider_mapping.rb @@ -201,8 +201,8 @@ class Chef begin result = Chef::Provider.const_get(class_name) - Chef::Log.warn("Class Chef::Provider::#{class_name} does not declare 'provides #{convert_to_snake_case(class_name).to_sym.inspect}'.") - Chef::Log.warn("This will no longer work in Chef 13: you must use 'provides' to provide DSL.") + Chef::Log.warn("Class Chef::Provider::#{class_name} does not declare 'resource_name #{convert_to_snake_case(class_name).to_sym.inspect}'.") + Chef::Log.warn("This will no longer work in Chef 13: you must use 'resource_name' to provide DSL.") rescue NameError end end diff --git a/lib/chef/platform/provider_priority_map.rb b/lib/chef/platform/provider_priority_map.rb index 9d703c9178..5599c74c2d 100644 --- a/lib/chef/platform/provider_priority_map.rb +++ b/lib/chef/platform/provider_priority_map.rb @@ -1,29 +1,11 @@ require 'singleton' +require 'chef/platform/priority_map' class Chef class Platform - class ProviderPriorityMap + # @api private + class ProviderPriorityMap < Chef::Platform::PriorityMap include Singleton - - def get_priority_array(node, resource_name) - priority_map.get(node, resource_name.to_sym) - end - - def set_priority_array(resource_name, priority_array, *filter, &block) - priority_map.set(resource_name.to_sym, Array(priority_array), *filter, &block) - end - - # @api private - def list_handlers(node, resource_name) - priority_map.list(node, resource_name.to_sym).flatten(1).uniq - end - - private - - def priority_map - require 'chef/node_map' - @priority_map ||= Chef::NodeMap.new - end end end end diff --git a/lib/chef/platform/resource_priority_map.rb b/lib/chef/platform/resource_priority_map.rb index fb08debc53..aa57e3ddf0 100644 --- a/lib/chef/platform/resource_priority_map.rb +++ b/lib/chef/platform/resource_priority_map.rb @@ -1,34 +1,17 @@ require 'singleton' +require 'chef/platform/priority_map' class Chef class Platform - class ResourcePriorityMap + # @api private + class ResourcePriorityMap < Chef::Platform::PriorityMap include Singleton - def get_priority_array(node, resource_name, canonical: nil) - priority_map.get(node, resource_name.to_sym, canonical: canonical) - end - - def set_priority_array(resource_name, priority_array, *filter, &block) - priority_map.set(resource_name.to_sym, Array(priority_array), *filter, &block) - end - # @api private - def delete_canonical(resource_name, resource_class) - priority_map.delete_canonical(resource_name, resource_class) - end - - # @api private - def list_handlers(*args) - priority_map.list(*args).flatten(1).uniq + def get_priority_array(node, resource_name, canonical: nil) + super(node, resource_name.to_sym, canonical: canonical) end - private - - def priority_map - require 'chef/node_map' - @priority_map ||= Chef::NodeMap.new - end end end end diff --git a/lib/chef/provider.rb b/lib/chef/provider.rb index e50e374804..131b72cd23 100644 --- a/lib/chef/provider.rb +++ b/lib/chef/provider.rb @@ -176,7 +176,7 @@ class Chef end def self.provides(short_name, opts={}, &block) - Chef.set_provider_priority_array(short_name, self, opts, &block) + Chef.provider_priority_map.set(short_name, self, opts, &block) end def self.provides?(node, resource) diff --git a/lib/chef/provider/service/debian.rb b/lib/chef/provider/service/debian.rb index 01505924cb..c67f3f05da 100644 --- a/lib/chef/provider/service/debian.rb +++ b/lib/chef/provider/service/debian.rb @@ -25,8 +25,6 @@ class Chef UPDATE_RC_D_ENABLED_MATCHES = /\/rc[\dS].d\/S|not installed/i UPDATE_RC_D_PRIORITIES = /\/rc([\dS]).d\/([SK])(\d\d)/i - provides :service, platform_family: "debian" - def self.provides?(node, resource) super && Chef::Platform::ServiceHelpers.service_resource_providers.include?(:debian) end diff --git a/lib/chef/provider/service/insserv.rb b/lib/chef/provider/service/insserv.rb index 31965a4bc6..4534e33f32 100644 --- a/lib/chef/provider/service/insserv.rb +++ b/lib/chef/provider/service/insserv.rb @@ -24,8 +24,6 @@ class Chef class Service class Insserv < Chef::Provider::Service::Init - provides :service, os: "linux" - def self.provides?(node, resource) super && Chef::Platform::ServiceHelpers.service_resource_providers.include?(:insserv) end diff --git a/lib/chef/provider/service/invokercd.rb b/lib/chef/provider/service/invokercd.rb index 5ff24e0dbb..fdf4cbc256 100644 --- a/lib/chef/provider/service/invokercd.rb +++ b/lib/chef/provider/service/invokercd.rb @@ -23,8 +23,6 @@ class Chef class Service class Invokercd < Chef::Provider::Service::Init - provides :service, platform_family: "debian" - def self.provides?(node, resource) super && Chef::Platform::ServiceHelpers.service_resource_providers.include?(:invokercd) end diff --git a/lib/chef/provider/service/redhat.rb b/lib/chef/provider/service/redhat.rb index 850953125e..a8deb13aec 100644 --- a/lib/chef/provider/service/redhat.rb +++ b/lib/chef/provider/service/redhat.rb @@ -26,8 +26,6 @@ class Chef CHKCONFIG_ON = /\d:on/ CHKCONFIG_MISSING = /No such/ - provides :service, platform_family: [ "rhel", "fedora", "suse" ] - def self.provides?(node, resource) super && Chef::Platform::ServiceHelpers.service_resource_providers.include?(:redhat) end diff --git a/lib/chef/provider/service/upstart.rb b/lib/chef/provider/service/upstart.rb index 8d4aa41035..d8ce6af649 100644 --- a/lib/chef/provider/service/upstart.rb +++ b/lib/chef/provider/service/upstart.rb @@ -27,8 +27,6 @@ class Chef class Upstart < Chef::Provider::Service::Simple UPSTART_STATE_FORMAT = /\w+ \(?(\w+)\)?[\/ ](\w+)/ - provides :service, os: "linux" - def self.provides?(node, resource) super && Chef::Platform::ServiceHelpers.service_resource_providers.include?(:upstart) end diff --git a/lib/chef/provider/user.rb b/lib/chef/provider/user.rb index ad92a72a0a..244b11db98 100644 --- a/lib/chef/provider/user.rb +++ b/lib/chef/provider/user.rb @@ -23,8 +23,6 @@ require 'etc' class Chef class Provider class User < Chef::Provider - provides :user - include Chef::Mixin::Command attr_accessor :user_exists, :locked diff --git a/lib/chef/resource.rb b/lib/chef/resource.rb index 10ad184506..418b0dc1ce 100644 --- a/lib/chef/resource.rb +++ b/lib/chef/resource.rb @@ -1143,7 +1143,7 @@ class Chef remove_canonical_dsl end - result = Chef.set_resource_priority_array(name, self, options, &block) + result = Chef.resource_priority_map.set(name, self, options, &block) Chef::DSL::Resources.add_resource_dsl(name) result end diff --git a/lib/chef/resource/macports_package.rb b/lib/chef/resource/macports_package.rb index 937839b6e1..5843016897 100644 --- a/lib/chef/resource/macports_package.rb +++ b/lib/chef/resource/macports_package.rb @@ -16,10 +16,11 @@ # limitations under the License. # +require 'chef/resource/package' + class Chef class Resource class MacportsPackage < Chef::Resource::Package - provides :package, os: "darwin" end end end diff --git a/lib/chef/resource/package.rb b/lib/chef/resource/package.rb index 1c6da75678..5be1c34b89 100644 --- a/lib/chef/resource/package.rb +++ b/lib/chef/resource/package.rb @@ -100,8 +100,3 @@ class Chef end end end - -require 'chef/chef_class' -require 'chef/resource/homebrew_package' - -Chef.set_resource_priority_array :package, Chef::Resource::HomebrewPackage, os: "darwin" diff --git a/spec/integration/recipes/recipe_dsl_spec.rb b/spec/integration/recipes/recipe_dsl_spec.rb index 03df3e7bb3..6bbb9a5c4c 100644 --- a/spec/integration/recipes/recipe_dsl_spec.rb +++ b/spec/integration/recipes/recipe_dsl_spec.rb @@ -91,7 +91,7 @@ describe "Recipe DSL methods" do recipe = converge { backcompat_thingy 'blah' do; end } - expect(recipe.logged_warnings).to eq '' + expect(recipe.logged_warnings).to match(/Class Chef::Provider::BackcompatThingy does not declare 'resource_name :backcompat_thingy'./) expect(BaseThingy.created_resource).not_to be_nil end end @@ -423,7 +423,7 @@ describe "Recipe DSL methods" do end EOM } - it "two_classes_one_dsl resolves to BlahModule::TwoClassesOneDsl (last declared)" do + it "two_classes_one_dsl resolves to BlahModule::TwoClassesOneDsl (alphabetical)" do dsl_method = self.dsl_method recipe = converge { instance_eval("#{dsl_method} 'blah' do; end") @@ -566,11 +566,11 @@ describe "Recipe DSL methods" do } - it "thingy3 works in a recipe and yields Foo::Thingy4 (the explicit one)" do + it "thingy3 works in a recipe and yields Thingy3 (the alphabetical one)" do recipe = converge { thingy3 'blah' do; end } - expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy4 + expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy3 end it "thingy4 does not work in a recipe" do @@ -580,16 +580,19 @@ describe "Recipe DSL methods" do end it "resource_matching_short_name returns Thingy4" do - expect(Chef::Resource.resource_matching_short_name(:thingy3)).to eq RecipeDSLSpecNamespace::Thingy4 + expect(Chef::Resource.resource_matching_short_name(:thingy3)).to eq RecipeDSLSpecNamespace::Thingy3 end end end - context "when Thingy5 has resource_name :thingy5" do + context "when Thingy5 has resource_name :thingy5 and provides :thingy5reverse, :thingy5_2 and :thingy5_2reverse" do before(:context) { class RecipeDSLSpecNamespace::Thingy5 < BaseThingy resource_name :thingy5 + provides :thingy5reverse + provides :thingy5_2 + provides :thingy5_2reverse end } @@ -618,17 +621,144 @@ describe "Recipe DSL methods" do expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy6 end - it "thingy5 works in a recipe and yields Foo::Thingy6 (the later one)" do + it "thingy5 works in a recipe and yields Foo::Thingy5 (the alphabetical one)" do recipe = converge { thingy5 'blah' do; end } - expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy6 + expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy5 end it "resource_matching_short_name returns Thingy5" do expect(Chef::Resource.resource_matching_short_name(:thingy5)).to eq RecipeDSLSpecNamespace::Thingy5 end + + context "and AThingy5 provides :thingy5reverse" do + before(:context) { + + class RecipeDSLSpecNamespace::AThingy5 < BaseThingy + resource_name :thingy5reverse + end + + } + + it "thingy5reverse works in a recipe and yields AThingy5 (the alphabetical one)" do + recipe = converge { + thingy5reverse 'blah' do; end + } + expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::AThingy5 + end + end + + context "and ZRecipeDSLSpecNamespace::Thingy5 provides :thingy5_2" do + before(:context) { + + module ZRecipeDSLSpecNamespace + class Thingy5 < BaseThingy + resource_name :thingy5_2 + end + end + + } + + it "thingy5_2 works in a recipe and yields the RecipeDSLSpaceNamespace one (the alphabetical one)" do + recipe = converge { + thingy5_2 'blah' do; end + } + expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy5 + end + end + + context "and ARecipeDSLSpecNamespace::Thingy5 provides :thingy5_2" do + before(:context) { + + module ARecipeDSLSpecNamespace + class Thingy5 < BaseThingy + resource_name :thingy5_2reverse + end + end + + } + + it "thingy5_2reverse works in a recipe and yields the ARecipeDSLSpaceNamespace one (the alphabetical one)" do + recipe = converge { + thingy5_2reverse 'blah' do; end + } + expect(BaseThingy.created_resource).to eq ARecipeDSLSpecNamespace::Thingy5 + end + end end + + context "when Thingy3 has resource_name :thingy3" do + before(:context) { + + class RecipeDSLSpecNamespace::Thingy3 < BaseThingy + resource_name :thingy3 + end + + } + + it "thingy3 works in a recipe" do + expect_recipe { + thingy3 'blah' do; end + }.to emit_no_warnings_or_errors + expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy3 + end + + context "and Thingy4 has resource_name :thingy3" do + before(:context) { + + class RecipeDSLSpecNamespace::Thingy4 < BaseThingy + resource_name :thingy3 + end + + } + + it "thingy3 works in a recipe and yields Thingy3 (the alphabetical one)" do + recipe = converge { + thingy3 'blah' do; end + } + expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy3 + end + + it "thingy4 does not work in a recipe" do + expect_converge { + thingy4 'blah' do; end + }.to raise_error(NoMethodError) + end + + it "resource_matching_short_name returns Thingy4" do + expect(Chef::Resource.resource_matching_short_name(:thingy3)).to eq RecipeDSLSpecNamespace::Thingy3 + end + end + + context "and Thingy4 has resource_name :thingy3" do + before(:context) { + + class RecipeDSLSpecNamespace::Thingy4 < BaseThingy + resource_name :thingy3 + end + + } + + it "thingy3 works in a recipe and yields Thingy3 (the alphabetical one)" do + recipe = converge { + thingy3 'blah' do; end + } + expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy3 + end + + it "thingy4 does not work in a recipe" do + expect_converge { + thingy4 'blah' do; end + }.to raise_error(NoMethodError) + end + + it "resource_matching_short_name returns Thingy4" do + expect(Chef::Resource.resource_matching_short_name(:thingy3)).to eq RecipeDSLSpecNamespace::Thingy3 + end + end + end + end context "when Thingy7 provides :thingy8" do @@ -657,11 +787,11 @@ describe "Recipe DSL methods" do expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy7 end - it "thingy8 works in a recipe and yields Thingy8 (the later one)" do + it "thingy8 works in a recipe and yields Thingy7 (alphabetical)" do recipe = converge { thingy8 'blah' do; end } - expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy8 + expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy7 end it "resource_matching_short_name returns Thingy8" do @@ -670,36 +800,36 @@ describe "Recipe DSL methods" do end end - context "when Thingy5 provides :thingy5, :twizzle and :twizzle2" do + context "when Thingy12 provides :thingy12, :twizzle and :twizzle2" do before(:context) { - class RecipeDSLSpecNamespace::Thingy5 < BaseThingy - resource_name :thingy5 + class RecipeDSLSpecNamespace::Thingy12 < BaseThingy + resource_name :thingy12 provides :twizzle provides :twizzle2 end } - it "thingy5 works in a recipe and yields Thingy5" do + it "thingy12 works in a recipe and yields Thingy12" do expect_recipe { - thingy5 'blah' do; end + thingy12 'blah' do; end }.to emit_no_warnings_or_errors - expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy5 + expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy12 end - it "twizzle works in a recipe and yields Thingy5" do + it "twizzle works in a recipe and yields Thingy12" do expect_recipe { twizzle 'blah' do; end }.to emit_no_warnings_or_errors - expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy5 + expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy12 end - it "twizzle2 works in a recipe and yields Thingy5" do + it "twizzle2 works in a recipe and yields Thingy12" do expect_recipe { twizzle2 'blah' do; end }.to emit_no_warnings_or_errors - expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy5 + expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy12 end end @@ -748,6 +878,51 @@ describe "Recipe DSL methods" do }.to raise_error(Chef::Exceptions::NoSuchResourceType) end end + + context "when Thingy9 provides :thingy9" do + before(:context) { + class RecipeDSLSpecNamespace::Thingy9 < BaseThingy + resource_name :thingy9 + end + } + + it "declaring a resource providing the same :thingy9 produces a warning" do + expect(Chef::Log).to receive(:warn).with("You declared a new resource RecipeDSLSpecNamespace::Thingy9AlternateProvider for resource thingy9, but it comes alphabetically after RecipeDSLSpecNamespace::Thingy9 and has the same filters ({}), so it will not be used. Use override: true if you want to use it for thingy9.") + class RecipeDSLSpecNamespace::Thingy9AlternateProvider < BaseThingy + resource_name :thingy9 + end + end + end + + context "when Thingy10 provides :thingy10" do + before(:context) { + class RecipeDSLSpecNamespace::Thingy10 < BaseThingy + resource_name :thingy10 + end + } + + it "declaring a resource providing the same :thingy10 with override: true does not produce a warning" do + expect(Chef::Log).not_to receive(:warn) + class RecipeDSLSpecNamespace::Thingy10AlternateProvider < BaseThingy + provides :thingy10, override: true + end + end + end + + context "when Thingy11 provides :thingy11" do + before(:context) { + class RecipeDSLSpecNamespace::Thingy11 < BaseThingy + resource_name :thingy10 + end + } + + it "declaring a resource providing the same :thingy11 with os: 'linux' does not produce a warning" do + expect(Chef::Log).not_to receive(:warn) + class RecipeDSLSpecNamespace::Thingy11AlternateProvider < BaseThingy + provides :thingy11, os: 'linux' + end + end + end end end diff --git a/spec/unit/provider_resolver_spec.rb b/spec/unit/provider_resolver_spec.rb index e18d69bc19..cd3d7713a7 100644 --- a/spec/unit/provider_resolver_spec.rb +++ b/spec/unit/provider_resolver_spec.rb @@ -482,7 +482,6 @@ describe Chef::ProviderResolver do python: Chef::Provider::Script, remote_directory: Chef::Provider::RemoteDirectory, route: Chef::Provider::Route, - rpm_package: Chef::Provider::Package::Rpm, ruby: Chef::Provider::Script, ruby_block: Chef::Provider::RubyBlock, script: Chef::Provider::Script, diff --git a/spec/unit/recipe_spec.rb b/spec/unit/recipe_spec.rb index 7579abf227..17ea498fe3 100644 --- a/spec/unit/recipe_spec.rb +++ b/spec/unit/recipe_spec.rb @@ -153,24 +153,24 @@ describe Chef::Recipe do Object.send(:remove_const, :TottenhamHotspur) end - it "selects one if it is the last declared" do - expect(Chef::Log).not_to receive(:warn) + it "selects the first one alphabetically" do + expect(Chef::Log).to receive(:warn).with("You declared a new resource TottenhamHotspur for resource football, but it comes alphabetically after Sounders and has the same filters ({:platform=>\"nbc_sports\"}), so it will not be used. Use override: true if you want to use it for football.") Sounders.provides :football, platform: "nbc_sports" TottenhamHotspur.provides :football, platform: "nbc_sports" res1 = recipe.football "club world cup" expect(res1.name).to eql("club world cup") - expect(res1).to be_a_kind_of(TottenhamHotspur) + expect(res1).to be_a_kind_of(Sounders) end - it "selects the other one if it is given priority" do - expect(Chef::Log).not_to receive(:warn) + it "selects the first one alphabetically even if the declaration order is reversed" do + expect(Chef::Log).to receive(:warn).with("You are overriding football2 on {:platform=>\"nbc_sports\"} with Sounders: used to be TottenhamHotspur. Use override: true if this is what you intended.") - TottenhamHotspur.provides :football, platform: "nbc_sports" - Sounders.provides :football, platform: "nbc_sports" + TottenhamHotspur.provides :football2, platform: "nbc_sports" + Sounders.provides :football2, platform: "nbc_sports" - res1 = recipe.football "club world cup" + res1 = recipe.football2 "club world cup" expect(res1.name).to eql("club world cup") expect(res1).to be_a_kind_of(Sounders) end diff --git a/spec/unit/resource_spec.rb b/spec/unit/resource_spec.rb index 87f7f8d929..8b0baff921 100644 --- a/spec/unit/resource_spec.rb +++ b/spec/unit/resource_spec.rb @@ -810,21 +810,21 @@ describe Chef::Resource do end it 'adds mappings for a single platform' do - expect(Chef).to receive(:set_resource_priority_array).with( + expect(Chef.resource_priority_map).to receive(:set).with( :dinobot, Chef::Resource::Klz, { platform: ['autobots'] } ) klz.provides :dinobot, platform: ['autobots'] end it 'adds mappings for multiple platforms' do - expect(Chef).to receive(:set_resource_priority_array).with( + expect(Chef.resource_priority_map).to receive(:set).with( :energy, Chef::Resource::Klz, { platform: ['autobots', 'decepticons']} ) klz.provides :energy, platform: ['autobots', 'decepticons'] end it 'adds mappings for all platforms' do - expect(Chef).to receive(:set_resource_priority_array).with( + expect(Chef.resource_priority_map).to receive(:set).with( :tape_deck, Chef::Resource::Klz, {} ) klz.provides :tape_deck |