diff options
author | John Keiser <john@johnkeiser.com> | 2015-05-06 14:07:43 -0700 |
---|---|---|
committer | John Keiser <john@johnkeiser.com> | 2015-06-02 09:53:39 -0700 |
commit | fc710c26a95e74aa66bf0bee8923ee104593c97a (patch) | |
tree | b41e2ed1f80fa139d8b9c171b8e11eb4eed82c5f /lib/chef/provider_resolver.rb | |
parent | 9028823f7b046c4c081b1cb1df005d61fbfa1db2 (diff) | |
download | chef-fc710c26a95e74aa66bf0bee8923ee104593c97a.tar.gz |
Narrow resolvers to only look at parts of the map we support
Diffstat (limited to 'lib/chef/provider_resolver.rb')
-rw-r--r-- | lib/chef/provider_resolver.rb | 130 |
1 files changed, 73 insertions, 57 deletions
diff --git a/lib/chef/provider_resolver.rb b/lib/chef/provider_resolver.rb index 7c7bac0443..ea28062bac 100644 --- a/lib/chef/provider_resolver.rb +++ b/lib/chef/provider_resolver.rb @@ -119,33 +119,14 @@ class Chef @action = action end - # return a deterministically sorted list of Chef::Provider subclasses - def providers - @providers ||= Chef::Provider.descendants - end - def resolve maybe_explicit_provider(resource) || maybe_dynamic_provider_resolution(resource, action) || maybe_chef_platform_lookup(resource) end - # this cut looks at if the provider can handle the resource type on the node - def enabled_handlers - @enabled_handlers ||= - providers.select do |klass| - # NB: this is different from resource_resolver which must pass a resource_name - # FIXME: deprecate this and normalize on passing resource_name here - klass.provides?(node, resource) - end.sort {|a,b| a.to_s <=> b.to_s } - end - - # this cut looks at if the provider can handle the specific resource and action - def supported_handlers - @supported_handlers ||= - enabled_handlers.select do |klass| - klass.supports?(resource, action) - end + def provided_by?(provider_class) + prioritized_handlers.include?(provider_class) end private @@ -158,40 +139,37 @@ class Chef # try dynamically finding a provider based on querying the providers to see what they support def maybe_dynamic_provider_resolution(resource, action) - # log this so we know what providers will work for the generic resource on the node (early cut) - Chef::Log.debug "providers for generic #{resource.resource_name} resource enabled on node include: #{enabled_handlers}" - - # what providers were excluded by machine state (late cut) - Chef::Log.debug "providers that refused resource #{resource} were: #{enabled_handlers - supported_handlers}" - Chef::Log.debug "providers that support resource #{resource} include: #{supported_handlers}" - - # 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. - handlers = supported_handlers.empty? ? enabled_handlers : supported_handlers - Chef::Log.debug "no providers supported the resource, falling back to enabled handlers" if supported_handlers.empty? - - if handlers.count >= 2 - # this magic stack ranks the providers by where they appear in the provider_priority_map, it is mostly used - # to pick amongst N different ways to start init scripts on different debian/ubuntu systems. - priority_list = [ get_priority_array(node, resource.resource_name) ].flatten.compact - handlers = handlers.sort_by { |x| i = priority_list.index x; i.nil? ? Float::INFINITY : i } - if priority_list.index(handlers.first).nil? - # if we had more than one and we picked one with a precidence of infinity that means that the resource_priority_map - # entry for this resource is missing -- we should probably raise here and force resolution of the ambiguity. - Chef::Log.warn "Ambiguous provider precedence: #{handlers}, please use Chef.set_provider_priority_array to provide determinism" - end - handlers = [ handlers.first ] + Chef::Log.debug "Providers for generic #{resource.resource_name} resource enabled on node include: #{enabled_handlers}" + + # Get all the handlers in the priority bucket + handlers = prioritized_handlers + + # Narrow it down to handlers that return `true` to `provides?` + # TODO deprecate this and don't bother calling--the fact that they said + # `provides` should be enough. But we need to do it right now because + # some classes implement additional handling. + enabled_handlers = prioritized_handlers.select { |handler| handler.provides?(node, resource) } + + # Narrow it down to handlers that return `true` to `supports?` + # TODO deprecate this and allow actions to be passed as a filter to + # `provides` so we don't have to have two separate things. + supported_handlers = handlers.select { |handler| handler.supports?(resource, action) } + 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." + handler = enabled_handlers.first + else + handler = supported_handlers.first end - Chef::Log.debug "providers that survived replacement include: #{handlers}" - - raise Chef::Exceptions::AmbiguousProviderResolution.new(resource, handlers) if handlers.count >= 2 - - Chef::Log.debug "dynamic provider resolver FAILED to resolve a provider" if handlers.empty? - - return nil if handlers.empty? + if handler + Chef::Log.debug "Provider for action #{action} on resource #{resource} is #{handler}" + else + Chef::Log.debug "Dynamic provider resolver FAILED to resolve a provider for action #{action} on resource #{resource}" + end - handlers[0] + handler end # try the old static lookup of providers by platform @@ -199,13 +177,51 @@ class Chef Chef::Platform.find_provider_for_node(node, resource) end - # dep injection hooks - def get_priority_array(node, resource_name) - provider_priority_map.get_priority_array(node, resource_name) - end - def provider_priority_map Chef::Platform::ProviderPriorityMap.instance end + + def prioritized_handlers + @prioritized_handlers ||= + provider_priority_map.list_handlers(node, resource.resource_name).flatten(1).uniq + end + + module Deprecated + # return a deterministically sorted list of Chef::Provider subclasses + def providers + @providers ||= Chef::Provider.descendants + end + + # this cut looks at if the provider can handle the resource type on the node + def enabled_handlers + @enabled_handlers ||= + providers.select do |klass| + # NB: this is different from resource_resolver which must pass a resource_name + # FIXME: deprecate this and normalize on passing resource_name here + klass.provides?(node, resource) + end.sort {|a,b| a.to_s <=> b.to_s } + end + + # this cut looks at if the provider can handle the specific resource and action + def supported_handlers + @supported_handlers ||= + enabled_handlers.select do |klass| + klass.supports?(resource, action) + end + end + + # If there are no providers for a DSL, we search through the + def prioritized_handlers + @prioritized_handlers ||= super || begin + result = providers.select { |handler| handler.provides?(node, resource) }.sort_by(:name) + if !result.empty? + Chef::Log.deprecation("#{resource.resource_name.to_sym} is marked as providing DSL #{method_symbol}, but provides #{resource.resource_name.to_sym.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 + result + end + end + end + prepend Deprecated end end |