summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Keiser <john@johnkeiser.com>2015-06-30 21:14:28 -0600
committerJohn Keiser <john@johnkeiser.com>2015-07-06 12:04:47 -0600
commit591b5599f5412faa382e45d035d76535cd93736a (patch)
tree3494785822024b7c4acc97adc4883c7a73e2eaaf
parentcfd2b1fa1b26e8b0aa92a5ba8bd294b96379b2fa (diff)
downloadchef-591b5599f5412faa382e45d035d76535cd93736a.tar.gz
Re-separate priority map and DSL handler map so that provides has veto power over priority
-rw-r--r--lib/chef/chef_class.rb22
-rw-r--r--lib/chef/platform/handler_map.rb45
-rw-r--r--lib/chef/platform/priority_map.rb55
-rw-r--r--lib/chef/platform/provider_handler_map.rb11
-rw-r--r--lib/chef/platform/resource_handler_map.rb11
-rw-r--r--lib/chef/platform/resource_priority_map.rb6
-rw-r--r--lib/chef/provider.rb2
-rw-r--r--lib/chef/provider/package.rb31
-rw-r--r--lib/chef/provider/package/aix.rb1
-rw-r--r--lib/chef/provider/package/apt.rb1
-rw-r--r--lib/chef/provider/package/homebrew.rb1
-rw-r--r--lib/chef/provider/package/ips.rb1
-rw-r--r--lib/chef/provider/package/macports.rb1
-rw-r--r--lib/chef/provider/package/pacman.rb1
-rw-r--r--lib/chef/provider/package/paludis.rb1
-rw-r--r--lib/chef/provider/package/portage.rb2
-rw-r--r--lib/chef/provider/package/smartos.rb1
-rw-r--r--lib/chef/provider/package/solaris.rb2
-rw-r--r--lib/chef/provider/package/yum.rb1
-rw-r--r--lib/chef/provider/package/zypper.rb1
-rw-r--r--lib/chef/provider/service.rb26
-rw-r--r--lib/chef/provider/service/debian.rb2
-rw-r--r--lib/chef/provider/service/insserv.rb2
-rw-r--r--lib/chef/provider/service/invokercd.rb2
-rw-r--r--lib/chef/provider/service/openbsd.rb2
-rw-r--r--lib/chef/provider/service/redhat.rb2
-rw-r--r--lib/chef/provider/service/upstart.rb3
-rw-r--r--lib/chef/provider_resolver.rb46
-rw-r--r--lib/chef/resource.rb4
-rw-r--r--lib/chef/resource_resolver.rb47
-rw-r--r--spec/integration/recipes/recipe_dsl_spec.rb959
-rw-r--r--spec/unit/lwrp_spec.rb8
-rw-r--r--spec/unit/provider_resolver_spec.rb7
-rw-r--r--spec/unit/resource_spec.rb6
34 files changed, 796 insertions, 517 deletions
diff --git a/lib/chef/chef_class.rb b/lib/chef/chef_class.rb
index a0c74f7aec..563e06434b 100644
--- a/lib/chef/chef_class.rb
+++ b/lib/chef/chef_class.rb
@@ -28,6 +28,8 @@
require 'chef/platform/provider_priority_map'
require 'chef/platform/resource_priority_map'
+require 'chef/platform/provider_handler_map'
+require 'chef/platform/resource_handler_map'
class Chef
class << self
@@ -160,20 +162,26 @@ class Chef
@node = nil
@provider_priority_map = nil
@resource_priority_map = nil
+ @provider_dsl_map = nil
+ @resource_dsl_map = nil
end
# @api private
def provider_priority_map
- @provider_priority_map ||= begin
- # these slurp in the resource+provider world, so be exceedingly lazy about requiring them
- Chef::Platform::ProviderPriorityMap.instance
- end
+ # these slurp in the resource+provider world, so be exceedingly lazy about requiring them
+ @provider_priority_map ||= Chef::Platform::ProviderPriorityMap.instance
end
# @api private
def resource_priority_map
- @resource_priority_map ||= begin
- Chef::Platform::ResourcePriorityMap.instance
- end
+ @resource_priority_map ||= Chef::Platform::ResourcePriorityMap.instance
+ end
+ # @api private
+ def provider_handler_map
+ @provider_handler_map ||= Chef::Platform::ProviderHandlerMap.instance
+ end
+ # @api private
+ def resource_handler_map
+ @resource_handler_map ||= Chef::Platform::ResourceHandlerMap.instance
end
end
diff --git a/lib/chef/platform/handler_map.rb b/lib/chef/platform/handler_map.rb
new file mode 100644
index 0000000000..001eb3dc8f
--- /dev/null
+++ b/lib/chef/platform/handler_map.rb
@@ -0,0 +1,45 @@
+#
+# Author:: John Keiser (<jkeiser@chef.io>)
+# Copyright:: Copyright (c) 2015 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef/node_map'
+
+class Chef
+ class Platform
+ class HandlerMap < Chef::NodeMap
+ #
+ # "provides" lines with identical filters sort by class name (ascending).
+ #
+ def compare_matchers(key, new_matcher, matcher)
+ 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/priority_map.rb b/lib/chef/platform/priority_map.rb
index 73554eafe1..0b050deb59 100644
--- a/lib/chef/platform/priority_map.rb
+++ b/lib/chef/platform/priority_map.rb
@@ -1,3 +1,21 @@
+#
+# Author:: John Keiser (<jkeiser@chef.io>)
+# Copyright:: Copyright (c) 2015 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
require 'chef/node_map'
class Chef
@@ -18,43 +36,6 @@ class Chef
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
-
- # @api private
- def includes_handler?(key, handler)
- return false if !map.has_key?(key)
- map[key].any? { |m| h = m[:value]; h.is_a?(Array) ? h.include?(handler) : h == handler }
- 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_handler_map.rb b/lib/chef/platform/provider_handler_map.rb
new file mode 100644
index 0000000000..37321268aa
--- /dev/null
+++ b/lib/chef/platform/provider_handler_map.rb
@@ -0,0 +1,11 @@
+require 'singleton'
+require 'chef/platform/handler_map'
+
+class Chef
+ class Platform
+ # @api private
+ class ProviderHandlerMap < Chef::Platform::HandlerMap
+ include Singleton
+ end
+ end
+end
diff --git a/lib/chef/platform/resource_handler_map.rb b/lib/chef/platform/resource_handler_map.rb
new file mode 100644
index 0000000000..532d4ff656
--- /dev/null
+++ b/lib/chef/platform/resource_handler_map.rb
@@ -0,0 +1,11 @@
+require 'singleton'
+require 'chef/platform/handler_map'
+
+class Chef
+ class Platform
+ # @api private
+ class ResourceHandlerMap < Chef::Platform::HandlerMap
+ include Singleton
+ end
+ end
+end
diff --git a/lib/chef/platform/resource_priority_map.rb b/lib/chef/platform/resource_priority_map.rb
index aa57e3ddf0..5cc86fd2e7 100644
--- a/lib/chef/platform/resource_priority_map.rb
+++ b/lib/chef/platform/resource_priority_map.rb
@@ -6,12 +6,6 @@ class Chef
# @api private
class ResourcePriorityMap < Chef::Platform::PriorityMap
include Singleton
-
- # @api private
- def get_priority_array(node, resource_name, canonical: nil)
- super(node, resource_name.to_sym, canonical: canonical)
- end
-
end
end
end
diff --git a/lib/chef/provider.rb b/lib/chef/provider.rb
index 280277d947..dcfc92645b 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.provider_priority_map.set(short_name, self, opts, &block)
+ Chef.provider_handler_map.set(short_name, self, opts, &block)
end
def self.provides?(node, resource)
diff --git a/lib/chef/provider/package.rb b/lib/chef/provider/package.rb
index aca8d0dc3b..880104bff7 100644
--- a/lib/chef/provider/package.rb
+++ b/lib/chef/provider/package.rb
@@ -491,37 +491,6 @@ class Chef
end
end
- # Set provider priority
- require 'chef/chef_class'
- require 'chef/provider/package/dpkg'
- require 'chef/provider/package/homebrew'
- require 'chef/provider/package/macports'
- require 'chef/provider/package/apt'
- require 'chef/provider/package/yum'
- require 'chef/provider/package/zypper'
- require 'chef/provider/package/portage'
- require 'chef/provider/package/pacman'
- require 'chef/provider/package/ips'
- require 'chef/provider/package/solaris'
- require 'chef/provider/package/smartos'
- require 'chef/provider/package/aix'
- require 'chef/provider/package/paludis'
-
- Chef.set_provider_priority_array :package, [ Homebrew, Macports ], os: "darwin"
-
- Chef.set_provider_priority_array :package, Apt, platform_family: "debian"
- Chef.set_provider_priority_array :package, Yum, platform_family: %w(rhel fedora)
- Chef.set_provider_priority_array :package, Zypper, platform_family: "suse"
- Chef.set_provider_priority_array :package, Portage, platform: "gentoo"
- Chef.set_provider_priority_array :package, Pacman, platform: "arch"
- Chef.set_provider_priority_array :package, Ips, platform: %w(openindiana opensolaris omnios solaris2)
- Chef.set_provider_priority_array :package, Solaris, platform: "nexentacore"
- Chef.set_provider_priority_array :package, Solaris, platform: "solaris2", platform_version: '< 5.11'
-
- Chef.set_provider_priority_array :package, SmartOS, platform: "smartos"
- Chef.set_provider_priority_array :package, Aix, platform: "aix"
- Chef.set_provider_priority_array :package, Paludis, platform: "exherbo"
-
private
def shell_out_with_timeout(*command_args)
diff --git a/lib/chef/provider/package/aix.rb b/lib/chef/provider/package/aix.rb
index b97db9d061..5165f4b4ea 100644
--- a/lib/chef/provider/package/aix.rb
+++ b/lib/chef/provider/package/aix.rb
@@ -26,6 +26,7 @@ class Chef
class Package
class Aix < Chef::Provider::Package
+ provides :package, os: "aix"
provides :bff_package, os: "aix"
include Chef::Mixin::GetSourceFromPackage
diff --git a/lib/chef/provider/package/apt.rb b/lib/chef/provider/package/apt.rb
index bd6ed283bf..e109c9966a 100644
--- a/lib/chef/provider/package/apt.rb
+++ b/lib/chef/provider/package/apt.rb
@@ -25,6 +25,7 @@ class Chef
class Package
class Apt < Chef::Provider::Package
+ provides :package, platform_family: "debian"
provides :apt_package, os: "linux"
# return [Hash] mapping of package name to Boolean value
diff --git a/lib/chef/provider/package/homebrew.rb b/lib/chef/provider/package/homebrew.rb
index beede1c916..e5c45f0a62 100644
--- a/lib/chef/provider/package/homebrew.rb
+++ b/lib/chef/provider/package/homebrew.rb
@@ -26,6 +26,7 @@ class Chef
class Package
class Homebrew < Chef::Provider::Package
+ provides :package, os: "darwin", override: true
provides :homebrew_package
include Chef::Mixin::HomebrewUser
diff --git a/lib/chef/provider/package/ips.rb b/lib/chef/provider/package/ips.rb
index 4d7f4a3583..96c2e711d4 100644
--- a/lib/chef/provider/package/ips.rb
+++ b/lib/chef/provider/package/ips.rb
@@ -27,6 +27,7 @@ class Chef
class Package
class Ips < Chef::Provider::Package
+ provides :package, platform: %w(openindiana opensolaris omnios solaris2)
provides :ips_package, os: "solaris2"
attr_accessor :virtual
diff --git a/lib/chef/provider/package/macports.rb b/lib/chef/provider/package/macports.rb
index e945211540..c7ea71ac8c 100644
--- a/lib/chef/provider/package/macports.rb
+++ b/lib/chef/provider/package/macports.rb
@@ -3,6 +3,7 @@ class Chef
class Package
class Macports < Chef::Provider::Package
+ provides :package, os: "darwin"
provides :macports_package
def load_current_resource
diff --git a/lib/chef/provider/package/pacman.rb b/lib/chef/provider/package/pacman.rb
index bf03e54656..01e3a9cc01 100644
--- a/lib/chef/provider/package/pacman.rb
+++ b/lib/chef/provider/package/pacman.rb
@@ -25,6 +25,7 @@ class Chef
class Package
class Pacman < Chef::Provider::Package
+ provides :package, platform: "arch"
provides :pacman_package, os: "linux"
def load_current_resource
diff --git a/lib/chef/provider/package/paludis.rb b/lib/chef/provider/package/paludis.rb
index 407e0d0110..2d6302515b 100644
--- a/lib/chef/provider/package/paludis.rb
+++ b/lib/chef/provider/package/paludis.rb
@@ -24,6 +24,7 @@ class Chef
class Package
class Paludis < Chef::Provider::Package
+ provides :package, platform: "exherbo"
provides :paludis_package, os: "linux"
def load_current_resource
diff --git a/lib/chef/provider/package/portage.rb b/lib/chef/provider/package/portage.rb
index 4ba0160bb0..95782a6774 100644
--- a/lib/chef/provider/package/portage.rb
+++ b/lib/chef/provider/package/portage.rb
@@ -25,6 +25,8 @@ class Chef
class Provider
class Package
class Portage < Chef::Provider::Package
+
+ provides :package, platform: "gentoo"
provides :portage_package
PACKAGE_NAME_PATTERN = %r{(?:([^/]+)/)?([^/]+)}
diff --git a/lib/chef/provider/package/smartos.rb b/lib/chef/provider/package/smartos.rb
index 0d5b801c96..71b8a9b9e1 100644
--- a/lib/chef/provider/package/smartos.rb
+++ b/lib/chef/provider/package/smartos.rb
@@ -29,6 +29,7 @@ class Chef
class SmartOS < Chef::Provider::Package
attr_accessor :is_virtual_package
+ provides :package, platform: "smartos"
provides :smartos_package, os: "solaris2", platform_family: "smartos"
def load_current_resource
diff --git a/lib/chef/provider/package/solaris.rb b/lib/chef/provider/package/solaris.rb
index 9b10403344..e62f37d27b 100644
--- a/lib/chef/provider/package/solaris.rb
+++ b/lib/chef/provider/package/solaris.rb
@@ -27,6 +27,8 @@ class Chef
include Chef::Mixin::GetSourceFromPackage
+ provides :package, platform: "nexentacore"
+ provides :package, platform: "solaris2", platform_version: '< 5.11'
provides :solaris_package, os: "solaris2"
# def initialize(*args)
diff --git a/lib/chef/provider/package/yum.rb b/lib/chef/provider/package/yum.rb
index 85c2ba683c..e8c0483741 100644
--- a/lib/chef/provider/package/yum.rb
+++ b/lib/chef/provider/package/yum.rb
@@ -28,6 +28,7 @@ class Chef
class Package
class Yum < Chef::Provider::Package
+ provides :package, platform_family: %w(rhel fedora)
provides :yum_package, os: "linux"
class RPMUtils
diff --git a/lib/chef/provider/package/zypper.rb b/lib/chef/provider/package/zypper.rb
index c2a3ac4ba8..ac42304ffb 100644
--- a/lib/chef/provider/package/zypper.rb
+++ b/lib/chef/provider/package/zypper.rb
@@ -29,6 +29,7 @@ class Chef
class Package
class Zypper < Chef::Provider::Package
+ provides :package, platform_family: "suse"
provides :zypper_package, os: "linux"
def load_current_resource
diff --git a/lib/chef/provider/service.rb b/lib/chef/provider/service.rb
index 9c523b5e66..15dd72cb04 100644
--- a/lib/chef/provider/service.rb
+++ b/lib/chef/provider/service.rb
@@ -188,29 +188,11 @@ class Chef
require 'chef/provider/service/upstart'
require 'chef/provider/service/debian'
require 'chef/provider/service/invokercd'
- require 'chef/provider/service/freebsd'
- require 'chef/provider/service/openbsd'
- require 'chef/provider/service/solaris'
- require 'chef/provider/service/macosx'
-
- def self.os(os, *providers)
- Chef.set_provider_priority_array(:service, providers, os: os)
- end
- def self.platform_family(platform_family, *providers)
- Chef.set_provider_priority_array(:service, providers, platform_family: platform_family)
- end
-
- os %w(freebsd netbsd), Freebsd
- os %w(openbsd), Openbsd
- os %w(solaris2), Solaris
- os %w(darwin), Macosx
- os %w(linux), Systemd, Insserv, Redhat
-
- platform_family %w(arch), Systemd, Arch
- platform_family %w(gentoo), Systemd, Gentoo
- platform_family %w(debian), Systemd, Upstart, Insserv, Debian, Invokercd
- platform_family %w(rhel fedora suse), Systemd, Insserv, Redhat
+ Chef.set_provider_priority_array :service, [ Systemd, Arch ], platform_family: 'arch'
+ Chef.set_provider_priority_array :service, [ Systemd, Gentoo ], platform_family: 'gentoo'
+ Chef.set_provider_priority_array :service, [ Systemd, Upstart, Insserv, Debian, Invokercd ], platform_family: 'debian'
+ Chef.set_provider_priority_array :service, [ Systemd, Insserv, Redhat ], platform_family: %w(rhel fedora suse)
end
end
end
diff --git a/lib/chef/provider/service/debian.rb b/lib/chef/provider/service/debian.rb
index c67f3f05da..46c23fdd34 100644
--- a/lib/chef/provider/service/debian.rb
+++ b/lib/chef/provider/service/debian.rb
@@ -22,6 +22,8 @@ class Chef
class Provider
class Service
class Debian < Chef::Provider::Service::Init
+ provides :service, platform_family: 'debian'
+
UPDATE_RC_D_ENABLED_MATCHES = /\/rc[\dS].d\/S|not installed/i
UPDATE_RC_D_PRIORITIES = /\/rc([\dS]).d\/([SK])(\d\d)/i
diff --git a/lib/chef/provider/service/insserv.rb b/lib/chef/provider/service/insserv.rb
index 4534e33f32..2fd2eac38e 100644
--- a/lib/chef/provider/service/insserv.rb
+++ b/lib/chef/provider/service/insserv.rb
@@ -24,6 +24,8 @@ class Chef
class Service
class Insserv < Chef::Provider::Service::Init
+ provides :service, platform_family: %w(debian rhel fedora suse)
+
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 fdf4cbc256..39022546b0 100644
--- a/lib/chef/provider/service/invokercd.rb
+++ b/lib/chef/provider/service/invokercd.rb
@@ -23,6 +23,8 @@ class Chef
class Service
class Invokercd < Chef::Provider::Service::Init
+ provides :service, platform_family: 'debian', override: true
+
def self.provides?(node, resource)
super && Chef::Platform::ServiceHelpers.service_resource_providers.include?(:invokercd)
end
diff --git a/lib/chef/provider/service/openbsd.rb b/lib/chef/provider/service/openbsd.rb
index d509ee10ff..173191e01c 100644
--- a/lib/chef/provider/service/openbsd.rb
+++ b/lib/chef/provider/service/openbsd.rb
@@ -26,7 +26,7 @@ class Chef
class Service
class Openbsd < Chef::Provider::Service::Init
- provides :service, os: [ "openbsd" ]
+ provides :service, os: "openbsd"
include Chef::Mixin::ShellOut
diff --git a/lib/chef/provider/service/redhat.rb b/lib/chef/provider/service/redhat.rb
index a8deb13aec..2330d88eb7 100644
--- a/lib/chef/provider/service/redhat.rb
+++ b/lib/chef/provider/service/redhat.rb
@@ -23,6 +23,8 @@ class Chef
class Service
class Redhat < Chef::Provider::Service::Init
+ provides :service, platform_family: %w(rhel fedora suse)
+
CHKCONFIG_ON = /\d:on/
CHKCONFIG_MISSING = /No such/
diff --git a/lib/chef/provider/service/upstart.rb b/lib/chef/provider/service/upstart.rb
index d8ce6af649..8809d1c708 100644
--- a/lib/chef/provider/service/upstart.rb
+++ b/lib/chef/provider/service/upstart.rb
@@ -25,6 +25,9 @@ class Chef
class Provider
class Service
class Upstart < Chef::Provider::Service::Simple
+
+ provides :service, platform_family: 'debian', override: true
+
UPSTART_STATE_FORMAT = /\w+ \(?(\w+)\)?[\/ ](\w+)/
def self.provides?(node, resource)
diff --git a/lib/chef/provider_resolver.rb b/lib/chef/provider_resolver.rb
index 8e731ff96a..2fd9d7e8eb 100644
--- a/lib/chef/provider_resolver.rb
+++ b/lib/chef/provider_resolver.rb
@@ -68,27 +68,39 @@ class Chef
potential_handlers.include?(provider_class)
end
- def self.includes_handler?(resource_name, provider_class)
- priority_map.includes_handler?(resource_name, provider_class)
- end
-
def enabled_handlers
- @enabled_handlers ||=
- potential_handlers.select { |handler| !overrode_provides?(handler) || handler.provides?(node, resource) }
+ potential_handlers.select { |handler| !overrode_provides?(handler) || handler.provides?(node, resource) }
end
# TODO deprecate this and allow actions to be passed as a filter to
# `provides` so we don't have to have two separate things.
# @api private
def supported_handlers
- @supported_handlers ||=
- enabled_handlers.select { |handler| handler.supports?(resource, action) }
+ enabled_handlers.select { |handler| handler.supports?(resource, action) }
end
private
def potential_handlers
- priority_map.list_handlers(node, resource.resource_name)
+ handler_map.list(node, resource.resource_name).uniq
+ end
+
+ # The list of handlers, with any in the priority_map moved to the front
+ def prioritized_handlers
+ @prioritized_handlers ||= begin
+ supported_handlers = self.supported_handlers
+ if supported_handlers.empty?
+ # if none of the providers specifically support the resource, we still need to pick one of the providers that are
+ # enabled on the node to handle the why-run use case. FIXME we should only do this in why-run mode then.
+ Chef::Log.debug "No providers responded true to `supports?` for action #{action} on resource #{resource}, falling back to enabled handlers so we can return something anyway."
+ supported_handlers = enabled_handlers
+ end
+
+ prioritized = priority_map.list(node, resource.resource_name).flatten(1)
+ prioritized &= supported_handlers # Filter the priority map by the actual enabled handlers
+ prioritized |= supported_handlers # Bring back any handlers that aren't in the priority map, at the *end* (ordered set)
+ prioritized
+ end
end
# if resource.provider is set, just return one of those objects
@@ -101,15 +113,7 @@ class Chef
def maybe_dynamic_provider_resolution(resource, action)
Chef::Log.debug "Providers for generic #{resource.resource_name} resource enabled on node include: #{enabled_handlers}"
- handlers = supported_handlers
- if handlers.empty?
- # if none of the providers specifically support the resource, we still need to pick one of the providers that are
- # enabled on the node to handle the why-run use case. FIXME we should only do this in why-run mode then.
- Chef::Log.debug "No providers responded true to `supports?` for action #{action} on resource #{resource}, falling back to enabled handlers so we can return something anyway."
- handlers = enabled_handlers
- end
-
- handler = handlers.first
+ handler = prioritized_handlers.first
if handler
Chef::Log.debug "Provider for action #{action} on resource #{resource} is #{handler}"
@@ -125,12 +129,12 @@ class Chef
Chef::Platform.find_provider_for_node(node, resource)
end
- def self.priority_map
+ def priority_map
Chef.provider_priority_map
end
- def priority_map
- Chef.provider_priority_map
+ def handler_map
+ Chef.provider_handler_map
end
def overrode_provides?(handler)
diff --git a/lib/chef/resource.rb b/lib/chef/resource.rb
index 5e9ba42703..ac6f5e8923 100644
--- a/lib/chef/resource.rb
+++ b/lib/chef/resource.rb
@@ -1550,7 +1550,7 @@ class Chef
remove_canonical_dsl
end
- result = Chef.resource_priority_map.set(name, self, options, &block)
+ result = Chef.resource_handler_map.set(name, self, options, &block)
Chef::DSL::Resources.add_resource_dsl(name)
result
end
@@ -1742,7 +1742,7 @@ class Chef
def self.remove_canonical_dsl
if @resource_name
- remaining = Chef.resource_priority_map.delete_canonical(@resource_name, self)
+ remaining = Chef.resource_handler_map.delete_canonical(@resource_name, self)
if !remaining
Chef::DSL::Resources.remove_resource_dsl(@resource_name)
end
diff --git a/lib/chef/resource_resolver.rb b/lib/chef/resource_resolver.rb
index 2f27e93a3b..47b3df18af 100644
--- a/lib/chef/resource_resolver.rb
+++ b/lib/chef/resource_resolver.rb
@@ -83,9 +83,9 @@ class Chef
# @api private use Chef::ResourceResolver.resolve instead.
def resolve
# log this so we know what resources will work for the generic resource on the node (early cut)
- Chef::Log.debug "Resources for generic #{resource_name} resource enabled on node include: #{enabled_handlers}"
+ Chef::Log.debug "Resources for generic #{resource_name} resource enabled on node include: #{prioritized_handlers}"
- handler = enabled_handlers.first
+ handler = prioritized_handlers.first
if handler
Chef::Log.debug "Resource for #{resource_name} is #{handler}"
@@ -98,8 +98,8 @@ class Chef
# @api private
def list
- Chef::Log.debug "Resources for generic #{resource_name} resource enabled on node include: #{enabled_handlers}"
- enabled_handlers
+ Chef::Log.debug "Resources for generic #{resource_name} resource enabled on node include: #{prioritized_handlers}"
+ prioritized_handlers
end
#
@@ -118,7 +118,7 @@ class Chef
#
# @api private
def self.includes_handler?(resource_name, resource_class)
- priority_map.includes_handler?(resource_name, resource_class)
+ handler_map.list(nil, resource_name).include?(resource_class)
end
protected
@@ -127,19 +127,38 @@ class Chef
Chef.resource_priority_map
end
+ def self.handler_map
+ Chef.resource_handler_map
+ end
+
def priority_map
Chef.resource_priority_map
end
+ def handler_map
+ Chef.resource_handler_map
+ end
+
# @api private
def potential_handlers
- priority_map.list_handlers(node, resource_name, canonical: canonical)
+ handler_map.list(node, resource_name, canonical: canonical).uniq
end
def enabled_handlers
potential_handlers.select { |handler| !overrode_provides?(handler) || handler.provides?(node, resource_name) }
end
+ def prioritized_handlers
+ @prioritized_handlers ||= begin
+ enabled_handlers = self.enabled_handlers
+
+ prioritized = priority_map.list(node, resource_name, canonical: canonical).flatten(1)
+ prioritized &= enabled_handlers # Filter the priority map by the actual enabled handlers
+ prioritized |= enabled_handlers # Bring back any handlers that aren't in the priority map, at the *end* (ordered set)
+ prioritized
+ end
+ end
+
def overrode_provides?(handler)
handler.method(:provides?).owner != Chef::Resource.method(:provides?).owner
end
@@ -151,17 +170,15 @@ class Chef
end
def enabled_handlers
- @enabled_handlers ||= begin
- handlers = super
- if handlers.empty?
- handlers = resources.select { |handler| overrode_provides?(handler) && handler.provides?(node, resource_name) }
- handlers.each do |handler|
- Chef::Log.deprecation("#{handler}.provides? returned true when asked if it provides DSL #{resource_name}, but provides #{resource_name.inspect} was never called!")
- Chef::Log.deprecation("In Chef 13, this will break: you must call provides to mark the names you provide, even if you also override provides? yourself.")
- end
+ handlers = super
+ if handlers.empty?
+ handlers = resources.select { |handler| overrode_provides?(handler) && handler.provides?(node, resource_name) }
+ handlers.each do |handler|
+ Chef::Log.deprecation("#{handler}.provides? returned true when asked if it provides DSL #{resource_name}, but provides #{resource_name.inspect} was never called!")
+ Chef::Log.deprecation("In Chef 13, this will break: you must call provides to mark the names you provide, even if you also override provides? yourself.")
end
- handlers
end
+ handlers
end
end
prepend Deprecated
diff --git a/spec/integration/recipes/recipe_dsl_spec.rb b/spec/integration/recipes/recipe_dsl_spec.rb
index fa38808e3e..4e80c5a738 100644
--- a/spec/integration/recipes/recipe_dsl_spec.rb
+++ b/spec/integration/recipes/recipe_dsl_spec.rb
@@ -401,370 +401,6 @@ describe "Recipe DSL methods" do
}.to raise_error(NoMethodError)
end
end
-
- context "with a resource named 'B' with resource name :two_classes_one_dsl" do
- let(:two_classes_one_dsl) { :"two_classes_one_dsl#{Namer.current_index}" }
- let(:resource_class) {
- result = Class.new(BaseThingy) do
- def self.name
- "B"
- end
- def self.to_s; name; end
- def self.inspect; name.inspect; end
- end
- result.resource_name two_classes_one_dsl
- result
- }
- before { resource_class } # pull on it so it gets defined before the recipe runs
-
- context "and another resource named 'A' with resource_name :two_classes_one_dsl" do
- let(:resource_class_a) {
- result = Class.new(BaseThingy) do
- def self.name
- "A"
- end
- def self.to_s; name; end
- def self.inspect; name.inspect; end
- end
- result.resource_name two_classes_one_dsl
- result
- }
- before { resource_class_a } # pull on it so it gets defined before the recipe runs
-
- it "two_classes_one_dsl resolves to A (alphabetically earliest)" do
- two_classes_one_dsl = self.two_classes_one_dsl
- recipe = converge {
- instance_eval("#{two_classes_one_dsl} 'blah'")
- }
- expect(recipe.logged_warnings).to eq ''
- expect(BaseThingy.created_resource).to eq resource_class_a
- end
-
- it "resource_matching_short_name returns B" do
- expect(Chef::Resource.resource_matching_short_name(two_classes_one_dsl)).to eq resource_class_a
- end
- end
-
- context "and another resource named 'Z' with resource_name :two_classes_one_dsl" do
- let(:resource_class_z) {
- result = Class.new(BaseThingy) do
- def self.name
- "Z"
- end
- def self.to_s; name; end
- def self.inspect; name.inspect; end
- end
- result.resource_name two_classes_one_dsl
- result
- }
- before { resource_class_z } # pull on it so it gets defined before the recipe runs
-
- it "two_classes_one_dsl resolves to B (alphabetically earliest)" do
- two_classes_one_dsl = self.two_classes_one_dsl
- recipe = converge {
- instance_eval("#{two_classes_one_dsl} 'blah'")
- }
- expect(recipe.logged_warnings).to eq ''
- expect(BaseThingy.created_resource).to eq resource_class
- end
-
- it "resource_matching_short_name returns B" do
- expect(Chef::Resource.resource_matching_short_name(two_classes_one_dsl)).to eq resource_class
- end
- end
-
- context "and another resource Blarghle with provides :two_classes_one_dsl, os: 'blarghle'" do
- let(:resource_class_blarghle) {
- result = Class.new(BaseThingy) do
- def self.name
- "Blarghle"
- end
- def self.to_s; name; end
- def self.inspect; name.inspect; end
- end
- result.resource_name two_classes_one_dsl
- result.provides two_classes_one_dsl, os: 'blarghle'
- result
- }
- before { resource_class_blarghle } # pull on it so it gets defined before the recipe runs
-
- it "on os = blarghle, two_classes_one_dsl resolves to Blarghle" do
- two_classes_one_dsl = self.two_classes_one_dsl
- recipe = converge {
- # this is an ugly way to test, make Cheffish expose node attrs
- run_context.node.automatic[:os] = 'blarghle'
- instance_eval("#{two_classes_one_dsl} 'blah' do; end")
- }
- expect(recipe.logged_warnings).to eq ''
- expect(BaseThingy.created_resource).to eq resource_class_blarghle
- end
-
- it "on os = linux, two_classes_one_dsl resolves to B" do
- two_classes_one_dsl = self.two_classes_one_dsl
- recipe = converge {
- # this is an ugly way to test, make Cheffish expose node attrs
- run_context.node.automatic[:os] = 'linux'
- instance_eval("#{two_classes_one_dsl} 'blah' do; end")
- }
- expect(recipe.logged_warnings).to eq ''
- expect(BaseThingy.created_resource).to eq resource_class
- end
- end
- end
-
- context "with a resource MyResource" do
- let(:resource_class) { Class.new(BaseThingy) do
- def self.called_provides
- @called_provides
- end
- def to_s
- "MyResource"
- end
- end }
- let(:my_resource) { :"my_resource#{Namer.current_index}" }
- let(:blarghle_blarghle_little_star) { :"blarghle_blarghle_little_star#{Namer.current_index}" }
-
- context "with resource_name :my_resource" do
- before {
- resource_class.resource_name my_resource
- }
-
- context "with provides? returning true to my_resource" do
- before {
- my_resource = self.my_resource
- resource_class.define_singleton_method(:provides?) do |node, resource_name|
- @called_provides = true
- resource_name == my_resource
- end
- }
-
- it "my_resource returns the resource and calls provides?, but does not emit a warning" do
- dsl_name = self.my_resource
- recipe = converge {
- instance_eval("#{dsl_name} 'foo'")
- }
- expect(recipe.logged_warnings).to eq ''
- expect(BaseThingy.created_resource).to eq resource_class
- expect(resource_class.called_provides).to be_truthy
- end
- end
-
- context "with provides? returning true to blarghle_blarghle_little_star and not resource_name" do
- before do
- blarghle_blarghle_little_star = self.blarghle_blarghle_little_star
- resource_class.define_singleton_method(:provides?) do |node, resource_name|
- @called_provides = true
- resource_name == blarghle_blarghle_little_star
- end
- end
-
- it "my_resource does not return the resource" do
- dsl_name = self.my_resource
- expect_converge {
- instance_eval("#{dsl_name} 'foo'")
- }.to raise_error(Chef::Exceptions::NoSuchResourceType)
- expect(resource_class.called_provides).to be_truthy
- end
-
- it "blarghle_blarghle_little_star 'foo' returns the resource and emits a warning" do
- dsl_name = self.blarghle_blarghle_little_star
- recipe = converge {
- instance_eval("#{dsl_name} 'foo'")
- }
- expect(recipe.logged_warnings).to include "WARN: #{resource_class}.provides? returned true when asked if it provides DSL #{dsl_name}, but provides :#{dsl_name} was never called!"
- expect(BaseThingy.created_resource).to eq resource_class
- expect(resource_class.called_provides).to be_truthy
- end
- end
-
- context "and a provider" do
- let(:provider_class) do
- Class.new(BaseThingy::Provider) do
- def self.name
- "MyProvider"
- end
- def self.to_s; name; end
- def self.inspect; name.inspect; end
- def self.called_provides
- @called_provides
- end
- end
- end
-
- before do
- resource_class.send(:define_method, :provider) { nil }
- end
-
- context "that provides :my_resource" do
- before do
- provider_class.provides my_resource
- end
-
- context "with supports? returning true" do
- before do
- provider_class.define_singleton_method(:supports?) { |resource,action| true }
- end
-
- it "my_resource runs the provider and does not emit a warning" do
- my_resource = self.my_resource
- recipe = converge {
- instance_eval("#{my_resource} 'foo'")
- }
- expect(recipe.logged_warnings).to eq ''
- expect(BaseThingy.created_provider).to eq provider_class
- end
-
- context "and another provider supporting :my_resource with supports? false" do
- let(:provider_class2) do
- Class.new(BaseThingy::Provider) do
- def self.name
- "MyProvider2"
- end
- def self.to_s; name; end
- def self.inspect; name.inspect; end
- def self.called_provides
- @called_provides
- end
- provides my_resource
- def self.supports?(resource, action)
- false
- end
- end
- end
-
- it "my_resource runs the first provider" do
- my_resource = self.my_resource
- recipe = converge {
- instance_eval("#{my_resource} 'foo'")
- }
- expect(recipe.logged_warnings).to eq ''
- expect(BaseThingy.created_provider).to eq provider_class
- end
- end
- end
-
- context "with supports? returning false" do
- before do
- provider_class.define_singleton_method(:supports?) { |resource,action| false }
- end
-
- # TODO no warning? ick
- it "my_resource runs the provider anyway" do
- my_resource = self.my_resource
- recipe = converge {
- instance_eval("#{my_resource} 'foo'")
- }
- expect(recipe.logged_warnings).to eq ''
- expect(BaseThingy.created_provider).to eq provider_class
- end
-
- context "and another provider supporting :my_resource with supports? true" do
- let(:provider_class2) do
- my_resource = self.my_resource
- Class.new(BaseThingy::Provider) do
- def self.name
- "MyProvider2"
- end
- def self.to_s; name; end
- def self.inspect; name.inspect; end
- def self.called_provides
- @called_provides
- end
- provides my_resource
- def self.supports?(resource, action)
- true
- end
- end
- end
- before { provider_class2 } # make sure the provider class shows up
-
- it "my_resource runs the other provider" do
- my_resource = self.my_resource
- recipe = converge {
- instance_eval("#{my_resource} 'foo'")
- }
- expect(recipe.logged_warnings).to eq ''
- expect(BaseThingy.created_provider).to eq provider_class2
- end
- end
- end
- end
-
- context "with provides? returning true" do
- before {
- my_resource = self.my_resource
- provider_class.define_singleton_method(:provides?) do |node, resource|
- @called_provides = true
- resource.declared_type == my_resource
- end
- }
-
- context "that provides :my_resource" do
- before {
- provider_class.provides my_resource
- }
-
- it "my_resource calls the provider (and calls provides?), but does not emit a warning" do
- my_resource = self.my_resource
- recipe = converge {
- instance_eval("#{my_resource} 'foo'")
- }
- expect(recipe.logged_warnings).to eq ''
- expect(BaseThingy.created_provider).to eq provider_class
- expect(provider_class.called_provides).to be_truthy
- end
- end
-
- context "that does not call provides :my_resource" do
- it "my_resource calls the provider (and calls provides?), and emits a warning" do
- my_resource = self.my_resource
- recipe = converge {
- instance_eval("#{my_resource} 'foo'")
- }
- expect(recipe.logged_warnings).to include("WARN: #{provider_class}.provides? returned true when asked if it provides DSL #{my_resource}, but provides :#{my_resource} was never called!")
- expect(BaseThingy.created_provider).to eq provider_class
- expect(provider_class.called_provides).to be_truthy
- end
- end
- end
-
- context "with provides? returning false to my_resource" do
- before {
- my_resource = self.my_resource
- provider_class.define_singleton_method(:provides?) do |node, resource|
- @called_provides = true
- false
- end
- }
-
- context "that provides :my_resource" do
- before {
- provider_class.provides my_resource
- }
-
- it "my_resource fails to find a provider (and calls provides)" do
- my_resource = self.my_resource
- expect_converge {
- instance_eval("#{my_resource} 'foo'")
- }.to raise_error(Chef::Exceptions::ProviderNotFound)
- expect(provider_class.called_provides).to be_truthy
- end
- end
-
- context "that does not provide :my_resource" do
- it "my_resource fails to find a provider (and calls provides)" do
- my_resource = self.my_resource
- expect_converge {
- instance_eval("#{my_resource} 'foo'")
- }.to raise_error(Chef::Exceptions::ProviderNotFound)
- expect(provider_class.called_provides).to be_truthy
- end
- end
- end
- end
- end
- end
-
end
end
@@ -1175,6 +811,601 @@ describe "Recipe DSL methods" do
end
end
end
+
+ context "with a resource named 'B' with resource name :two_classes_one_dsl" do
+ let(:two_classes_one_dsl) { :"two_classes_one_dsl#{Namer.current_index}" }
+ let(:resource_class) {
+ result = Class.new(BaseThingy) do
+ def self.name
+ "B"
+ end
+ def self.to_s; name; end
+ def self.inspect; name.inspect; end
+ end
+ result.resource_name two_classes_one_dsl
+ result
+ }
+ before { resource_class } # pull on it so it gets defined before the recipe runs
+
+ context "and another resource named 'A' with resource_name :two_classes_one_dsl" do
+ let(:resource_class_a) {
+ result = Class.new(BaseThingy) do
+ def self.name
+ "A"
+ end
+ def self.to_s; name; end
+ def self.inspect; name.inspect; end
+ end
+ result.resource_name two_classes_one_dsl
+ result
+ }
+ before { resource_class_a } # pull on it so it gets defined before the recipe runs
+
+ it "two_classes_one_dsl resolves to A (alphabetically earliest)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq resource_class_a
+ end
+
+ it "resource_matching_short_name returns B" do
+ expect(Chef::Resource.resource_matching_short_name(two_classes_one_dsl)).to eq resource_class_a
+ end
+ end
+
+ context "and another resource named 'Z' with resource_name :two_classes_one_dsl" do
+ let(:resource_class_z) {
+ result = Class.new(BaseThingy) do
+ def self.name
+ "Z"
+ end
+ def self.to_s; name; end
+ def self.inspect; name.inspect; end
+ end
+ result.resource_name two_classes_one_dsl
+ result
+ }
+ before { resource_class_z } # pull on it so it gets defined before the recipe runs
+
+ it "two_classes_one_dsl resolves to B (alphabetically earliest)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq resource_class
+ end
+
+ it "resource_matching_short_name returns B" do
+ expect(Chef::Resource.resource_matching_short_name(two_classes_one_dsl)).to eq resource_class
+ end
+
+ context "and a priority array [ Z, B ]" do
+ before do
+ Chef.set_resource_priority_array(two_classes_one_dsl, [ resource_class_z, resource_class ])
+ end
+
+ it "two_classes_one_dsl resolves to Z (respects the priority array)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq resource_class_z
+ end
+
+ it "resource_matching_short_name returns B" do
+ expect(Chef::Resource.resource_matching_short_name(two_classes_one_dsl)).to eq resource_class
+ end
+
+ context "when Z provides(:two_classes_one_dsl) { false }" do
+ before do
+ resource_class_z.provides(two_classes_one_dsl) { false }
+ end
+
+ it "two_classes_one_dsl resolves to B (picks the next thing in the priority array)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq resource_class
+ end
+
+ it "resource_matching_short_name returns B" do
+ expect(Chef::Resource.resource_matching_short_name(two_classes_one_dsl)).to eq resource_class
+ end
+ end
+ end
+
+ context "and priority arrays [ B ] and [ Z ]" do
+ before do
+ Chef.set_resource_priority_array(two_classes_one_dsl, [ resource_class ])
+ Chef.set_resource_priority_array(two_classes_one_dsl, [ resource_class_z ])
+ end
+
+ it "two_classes_one_dsl resolves to Z (respects the most recent priority array)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq resource_class_z
+ end
+
+ it "resource_matching_short_name returns B" do
+ expect(Chef::Resource.resource_matching_short_name(two_classes_one_dsl)).to eq resource_class
+ end
+
+ context "when Z provides(:two_classes_one_dsl) { false }" do
+ before do
+ resource_class_z.provides(two_classes_one_dsl) { false }
+ end
+
+ it "two_classes_one_dsl resolves to B (picks the first match from the other priority array)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq resource_class
+ end
+
+ it "resource_matching_short_name returns B" do
+ expect(Chef::Resource.resource_matching_short_name(two_classes_one_dsl)).to eq resource_class
+ end
+ end
+ end
+
+ context "and a priority array [ Z ]" do
+ before do
+ Chef.set_resource_priority_array(two_classes_one_dsl, [ resource_class_z ])
+ end
+
+ context "when Z provides(:two_classes_one_dsl) { false }" do
+ before do
+ resource_class_z.provides(two_classes_one_dsl) { false }
+ end
+
+ it "two_classes_one_dsl resolves to B (picks the first match outside the priority array)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq resource_class
+ end
+
+ it "resource_matching_short_name returns B" do
+ expect(Chef::Resource.resource_matching_short_name(two_classes_one_dsl)).to eq resource_class
+ end
+ end
+ end
+
+ end
+
+ context "and a provider named 'B' which provides :two_classes_one_dsl" do
+ before do
+ resource_class.send(:define_method, :provider) { nil }
+ end
+
+ let(:provider_class) {
+ result = Class.new(BaseThingy::Provider) do
+ def self.name
+ "B"
+ end
+ def self.to_s; name; end
+ def self.inspect; name.inspect; end
+ end
+ result.provides two_classes_one_dsl
+ result
+ }
+ before { provider_class } # pull on it so it gets defined before the recipe runs
+
+ context "and another provider named 'A'" do
+ let(:provider_class_a) {
+ result = Class.new(BaseThingy::Provider) do
+ def self.name
+ "A"
+ end
+ def self.to_s; name; end
+ def self.inspect; name.inspect; end
+ end
+ result
+ }
+ context "which provides :two_classes_one_dsl" do
+ before { provider_class_a.provides two_classes_one_dsl }
+
+ it "two_classes_one_dsl resolves to A (alphabetically earliest)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_provider).to eq provider_class_a
+ end
+ end
+ context "which provides(:two_classes_one_dsl) { false }" do
+ before { provider_class_a.provides(two_classes_one_dsl) { false } }
+
+ it "two_classes_one_dsl resolves to B (since A declined)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_provider).to eq provider_class
+ end
+ end
+ end
+
+ context "and another provider named 'Z'" do
+ let(:provider_class_z) {
+ result = Class.new(BaseThingy::Provider) do
+ def self.name
+ "Z"
+ end
+ def self.to_s; name; end
+ def self.inspect; name.inspect; end
+ end
+ result
+ }
+ before { provider_class_z } # pull on it so it gets defined before the recipe runs
+
+ context "which provides :two_classes_one_dsl" do
+ before { provider_class_z.provides two_classes_one_dsl }
+
+ it "two_classes_one_dsl resolves to B (alphabetically earliest)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_provider).to eq provider_class
+ end
+
+ context "with a priority array [ Z, B ]" do
+ before { Chef.set_provider_priority_array two_classes_one_dsl, [ provider_class_z, provider_class ] }
+
+ it "two_classes_one_dsl resolves to Z (respects the priority map)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_provider).to eq provider_class_z
+ end
+ end
+ end
+
+ context "which provides(:two_classes_one_dsl) { false }" do
+ before { provider_class_z.provides(two_classes_one_dsl) { false } }
+
+ context "with a priority array [ Z, B ]" do
+ before { Chef.set_provider_priority_array two_classes_one_dsl, [ provider_class_z, provider_class ] }
+
+ it "two_classes_one_dsl resolves to B (the next one in the priority map)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_provider).to eq provider_class
+ end
+ end
+
+ context "with priority arrays [ B ] and [ Z ]" do
+ before { Chef.set_provider_priority_array two_classes_one_dsl, [ provider_class_z ] }
+ before { Chef.set_provider_priority_array two_classes_one_dsl, [ provider_class ] }
+
+ it "two_classes_one_dsl resolves to B (the one in the next priority map)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_provider).to eq provider_class
+ end
+ end
+ end
+ end
+ end
+
+ context "and another resource Blarghle with provides :two_classes_one_dsl, os: 'blarghle'" do
+ let(:resource_class_blarghle) {
+ result = Class.new(BaseThingy) do
+ def self.name
+ "Blarghle"
+ end
+ def self.to_s; name; end
+ def self.inspect; name.inspect; end
+ end
+ result.resource_name two_classes_one_dsl
+ result.provides two_classes_one_dsl, os: 'blarghle'
+ result
+ }
+ before { resource_class_blarghle } # pull on it so it gets defined before the recipe runs
+
+ it "on os = blarghle, two_classes_one_dsl resolves to Blarghle" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ # this is an ugly way to test, make Cheffish expose node attrs
+ run_context.node.automatic[:os] = 'blarghle'
+ instance_eval("#{two_classes_one_dsl} 'blah' do; end")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq resource_class_blarghle
+ end
+
+ it "on os = linux, two_classes_one_dsl resolves to B" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ # this is an ugly way to test, make Cheffish expose node attrs
+ run_context.node.automatic[:os] = 'linux'
+ instance_eval("#{two_classes_one_dsl} 'blah' do; end")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq resource_class
+ end
+ end
+ end
+
+ context "with a resource MyResource" do
+ let(:resource_class) { Class.new(BaseThingy) do
+ def self.called_provides
+ @called_provides
+ end
+ def to_s
+ "MyResource"
+ end
+ end }
+ let(:my_resource) { :"my_resource#{Namer.current_index}" }
+ let(:blarghle_blarghle_little_star) { :"blarghle_blarghle_little_star#{Namer.current_index}" }
+
+ context "with resource_name :my_resource" do
+ before {
+ resource_class.resource_name my_resource
+ }
+
+ context "with provides? returning true to my_resource" do
+ before {
+ my_resource = self.my_resource
+ resource_class.define_singleton_method(:provides?) do |node, resource_name|
+ @called_provides = true
+ resource_name == my_resource
+ end
+ }
+
+ it "my_resource returns the resource and calls provides?, but does not emit a warning" do
+ dsl_name = self.my_resource
+ recipe = converge {
+ instance_eval("#{dsl_name} 'foo'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq resource_class
+ expect(resource_class.called_provides).to be_truthy
+ end
+ end
+
+ context "with provides? returning true to blarghle_blarghle_little_star and not resource_name" do
+ before do
+ blarghle_blarghle_little_star = self.blarghle_blarghle_little_star
+ resource_class.define_singleton_method(:provides?) do |node, resource_name|
+ @called_provides = true
+ resource_name == blarghle_blarghle_little_star
+ end
+ end
+
+ it "my_resource does not return the resource" do
+ dsl_name = self.my_resource
+ expect_converge {
+ instance_eval("#{dsl_name} 'foo'")
+ }.to raise_error(Chef::Exceptions::NoSuchResourceType)
+ expect(resource_class.called_provides).to be_truthy
+ end
+
+ it "blarghle_blarghle_little_star 'foo' returns the resource and emits a warning" do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ dsl_name = self.blarghle_blarghle_little_star
+ recipe = converge {
+ instance_eval("#{dsl_name} 'foo'")
+ }
+ expect(recipe.logged_warnings).to include "WARN: #{resource_class}.provides? returned true when asked if it provides DSL #{dsl_name}, but provides :#{dsl_name} was never called!"
+ expect(BaseThingy.created_resource).to eq resource_class
+ expect(resource_class.called_provides).to be_truthy
+ end
+ end
+
+ context "and a provider" do
+ let(:provider_class) do
+ Class.new(BaseThingy::Provider) do
+ def self.name
+ "MyProvider"
+ end
+ def self.to_s; name; end
+ def self.inspect; name.inspect; end
+ def self.called_provides
+ @called_provides
+ end
+ end
+ end
+
+ before do
+ resource_class.send(:define_method, :provider) { nil }
+ end
+
+ context "that provides :my_resource" do
+ before do
+ provider_class.provides my_resource
+ end
+
+ context "with supports? returning true" do
+ before do
+ provider_class.define_singleton_method(:supports?) { |resource,action| true }
+ end
+
+ it "my_resource runs the provider and does not emit a warning" do
+ my_resource = self.my_resource
+ recipe = converge {
+ instance_eval("#{my_resource} 'foo'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_provider).to eq provider_class
+ end
+
+ context "and another provider supporting :my_resource with supports? false" do
+ let(:provider_class2) do
+ Class.new(BaseThingy::Provider) do
+ def self.name
+ "MyProvider2"
+ end
+ def self.to_s; name; end
+ def self.inspect; name.inspect; end
+ def self.called_provides
+ @called_provides
+ end
+ provides my_resource
+ def self.supports?(resource, action)
+ false
+ end
+ end
+ end
+
+ it "my_resource runs the first provider" do
+ my_resource = self.my_resource
+ recipe = converge {
+ instance_eval("#{my_resource} 'foo'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_provider).to eq provider_class
+ end
+ end
+ end
+
+ context "with supports? returning false" do
+ before do
+ provider_class.define_singleton_method(:supports?) { |resource,action| false }
+ end
+
+ # TODO no warning? ick
+ it "my_resource runs the provider anyway" do
+ my_resource = self.my_resource
+ recipe = converge {
+ instance_eval("#{my_resource} 'foo'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_provider).to eq provider_class
+ end
+
+ context "and another provider supporting :my_resource with supports? true" do
+ let(:provider_class2) do
+ my_resource = self.my_resource
+ Class.new(BaseThingy::Provider) do
+ def self.name
+ "MyProvider2"
+ end
+ def self.to_s; name; end
+ def self.inspect; name.inspect; end
+ def self.called_provides
+ @called_provides
+ end
+ provides my_resource
+ def self.supports?(resource, action)
+ true
+ end
+ end
+ end
+ before { provider_class2 } # make sure the provider class shows up
+
+ it "my_resource runs the other provider" do
+ my_resource = self.my_resource
+ recipe = converge {
+ instance_eval("#{my_resource} 'foo'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_provider).to eq provider_class2
+ end
+ end
+ end
+ end
+
+ context "with provides? returning true" do
+ before {
+ my_resource = self.my_resource
+ provider_class.define_singleton_method(:provides?) do |node, resource|
+ @called_provides = true
+ resource.declared_type == my_resource
+ end
+ }
+
+ context "that provides :my_resource" do
+ before {
+ provider_class.provides my_resource
+ }
+
+ it "my_resource calls the provider (and calls provides?), but does not emit a warning" do
+ my_resource = self.my_resource
+ recipe = converge {
+ instance_eval("#{my_resource} 'foo'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_provider).to eq provider_class
+ expect(provider_class.called_provides).to be_truthy
+ end
+ end
+
+ context "that does not call provides :my_resource" do
+ it "my_resource calls the provider (and calls provides?), and emits a warning" do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ my_resource = self.my_resource
+ recipe = converge {
+ instance_eval("#{my_resource} 'foo'")
+ }
+ expect(recipe.logged_warnings).to include("WARN: #{provider_class}.provides? returned true when asked if it provides DSL #{my_resource}, but provides :#{my_resource} was never called!")
+ expect(BaseThingy.created_provider).to eq provider_class
+ expect(provider_class.called_provides).to be_truthy
+ end
+ end
+ end
+
+ context "with provides? returning false to my_resource" do
+ before {
+ my_resource = self.my_resource
+ provider_class.define_singleton_method(:provides?) do |node, resource|
+ @called_provides = true
+ false
+ end
+ }
+
+ context "that provides :my_resource" do
+ before {
+ provider_class.provides my_resource
+ }
+
+ it "my_resource fails to find a provider (and calls provides)" do
+ my_resource = self.my_resource
+ expect_converge {
+ instance_eval("#{my_resource} 'foo'")
+ }.to raise_error(Chef::Exceptions::ProviderNotFound)
+ expect(provider_class.called_provides).to be_truthy
+ end
+ end
+
+ context "that does not provide :my_resource" do
+ it "my_resource fails to find a provider (and calls provides)" do
+ my_resource = self.my_resource
+ expect_converge {
+ instance_eval("#{my_resource} 'foo'")
+ }.to raise_error(Chef::Exceptions::ProviderNotFound)
+ expect(provider_class.called_provides).to be_truthy
+ end
+ end
+ end
+ end
+ end
+ end
end
before(:all) { Namer.current_index = 0 }
diff --git a/spec/unit/lwrp_spec.rb b/spec/unit/lwrp_spec.rb
index 545524eb10..bcb64cb21e 100644
--- a/spec/unit/lwrp_spec.rb
+++ b/spec/unit/lwrp_spec.rb
@@ -439,7 +439,7 @@ describe "LWRP" do
end
it "sets itself as a provider for a resource of the same name" do
- found_providers = Chef::Platform::ProviderPriorityMap.instance.list_handlers(node, :lwrp_buck_passer)
+ found_providers = Chef::Platform::ProviderHandlerMap.instance.list(node, :lwrp_buck_passer)
# we bypass the per-file loading to get the file to load each time,
# which creates the LWRP class repeatedly. New things get prepended to
# the list of providers.
@@ -451,7 +451,7 @@ describe "LWRP" do
let(:lwrp_cookbok_name) { "l_w_r_p" }
it "sets itself as a provider for a resource of the same name" do
- found_providers = Chef::Platform::ProviderPriorityMap.instance.list_handlers(node, :l_w_r_p_buck_passer)
+ found_providers = Chef::Platform::ProviderHandlerMap.instance.list(node, :l_w_r_p_buck_passer)
expect(found_providers.size).to eq(1)
expect(found_providers.last).to eq(get_lwrp_provider(:l_w_r_p_buck_passer))
end
@@ -462,10 +462,10 @@ describe "LWRP" do
let(:lwrp_cookbok_name) { "l-w-r-p" }
it "sets itself as a provider for a resource of the same name" do
- incorrect_providers = Chef::Platform::ProviderPriorityMap.instance.list_handlers(node, :'l-w-r-p_buck_passer')
+ incorrect_providers = Chef::Platform::ProviderHandlerMap.instance.list(node, :'l-w-r-p_buck_passer')
expect(incorrect_providers).to eq([])
- found_providers = Chef::Platform::ProviderPriorityMap.instance.list_handlers(node, :l_w_r_p_buck_passer)
+ found_providers = Chef::Platform::ProviderHandlerMap.instance.list(node, :l_w_r_p_buck_passer)
expect(found_providers.first).to eq(get_lwrp_provider(:l_w_r_p_buck_passer))
end
end
diff --git a/spec/unit/provider_resolver_spec.rb b/spec/unit/provider_resolver_spec.rb
index e09b12a20a..88df4a20cc 100644
--- a/spec/unit/provider_resolver_spec.rb
+++ b/spec/unit/provider_resolver_spec.rb
@@ -90,13 +90,14 @@ describe Chef::ProviderResolver do
end
def self.expect_providers(**providers)
- providers.each do |name, expected_provider|
+ providers.each do |name, expected|
describe name.to_s do
let(:resource_name) { name }
tags = []
+ expected_provider = nil
expected_resource = nil
- Array(expected_provider).each do |p|
+ Array(expected).each do |p|
if p.is_a?(Class) && p <= Chef::Provider
expected_provider = p
elsif p.is_a?(Class) && p <= Chef::Resource
@@ -525,8 +526,8 @@ describe Chef::ProviderResolver do
# We want to check that these are unsupported:
apt_package: nil,
bff_package: nil,
- dsc_script: nil,
dpkg_package: nil,
+ dsc_script: nil,
ips_package: nil,
pacman_package: nil,
paludis_package: nil,
diff --git a/spec/unit/resource_spec.rb b/spec/unit/resource_spec.rb
index 1377950c99..b9ba80068b 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.resource_priority_map).to receive(:set).with(
+ expect(Chef.resource_handler_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.resource_priority_map).to receive(:set).with(
+ expect(Chef.resource_handler_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.resource_priority_map).to receive(:set).with(
+ expect(Chef.resource_handler_map).to receive(:set).with(
:tape_deck, Chef::Resource::Klz, {}
)
klz.provides :tape_deck