class Chef::ProviderResolver

Provider Resolution

Provider resolution is the process of taking a Resource object and an action, and determining the Provider class that should be instantiated to handle the action.

If the resource has its `provider` set, that is used.

Otherwise, we take the lists of Providers that have registered as providing the DSL through `provides :dsl_name, <filters>` or `Chef.set_resource_priority_array :dsl_name, <filters>`. We filter each list of Providers through:

  1. The filters it was registered with (such as `os: 'linux'` or `platform_family: 'debian'`)

  2. `provides?(node, resource)`

  3. `supports?(resource, action)`

Anything that passes the filter and returns `true` to provides and supports, is considered a match. The first matching Provider in the *most recently registered list* is selected and returned.

Attributes

action[R]
node[R]
resource[R]

Public Class Methods

new(node, resource, action) click to toggle source
# File lib/chef/provider_resolver.rb, line 53
def initialize(node, resource, action)
  @node = node
  @resource = resource
  @action = action
end

Public Instance Methods

enabled_handlers() click to toggle source
# File lib/chef/provider_resolver.rb, line 78
def enabled_handlers
  @enabled_handlers ||= potential_handlers.select { |handler| !overrode_provides?(handler) || handler.provides?(node, resource) }
end
provided_by?(provider_class) click to toggle source

Does NOT call provides? on the resource (it is assumed this is being called from provides?).

# File lib/chef/provider_resolver.rb, line 74
def provided_by?(provider_class)
  potential_handlers.include?(provider_class)
end
resolve() click to toggle source
# File lib/chef/provider_resolver.rb, line 59
def resolve
  resolved = maybe_explicit_provider(resource) ||
    maybe_custom_resource(resource) ||
    maybe_dynamic_provider_resolution(resource, action)

  if resolved.nil?
    raise(Chef::Exceptions::ProviderNotFound, "Cannot find a provider for #{resource}") if node.nil?

    raise(Chef::Exceptions::ProviderNotFound, "Cannot find a provider for #{resource} on #{node["platform"]} version #{node["platform_version"]}")
  end
  resolved
end
supported_handlers() click to toggle source

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

# File lib/chef/provider_resolver.rb, line 85
def supported_handlers
  enabled_handlers.select { |handler| handler.supports?(resource, action) }
end

Private Instance Methods

handler_map() click to toggle source
# File lib/chef/provider_resolver.rb, line 144
def handler_map
  Chef.provider_handler_map
end
maybe_custom_resource(resource) click to toggle source

if its a custom resource, just grab the action class

# File lib/chef/provider_resolver.rb, line 116
def maybe_custom_resource(resource)
  resource.class.action_class if resource.class.custom_resource?
end
maybe_dynamic_provider_resolution(resource, action) click to toggle source

try dynamically finding a provider based on querying the providers to see what they support

# File lib/chef/provider_resolver.rb, line 126
def maybe_dynamic_provider_resolution(resource, action)
  Chef::Log.trace "Providers for generic #{resource.resource_name} resource enabled on node include: #{enabled_handlers}"

  handler = prioritized_handlers.first

  if handler
    Chef::Log.trace "Provider for action #{action} on resource #{resource} is #{handler}"
  else
    Chef::Log.trace "Dynamic provider resolver FAILED to resolve a provider for action #{action} on resource #{resource}"
  end

  handler
end
maybe_explicit_provider(resource) click to toggle source

if resource.provider is set, just return one of those objects

# File lib/chef/provider_resolver.rb, line 121
def maybe_explicit_provider(resource)
  resource.provider
end
overrode_provides?(handler) click to toggle source
# File lib/chef/provider_resolver.rb, line 148
def overrode_provides?(handler)
  handler.method(:provides?).owner != Chef::Provider.method(:provides?).owner
end
potential_handlers() click to toggle source
# File lib/chef/provider_resolver.rb, line 91
def potential_handlers
  handler_map.list(node, resource.resource_name).uniq
end
prioritized_handlers() click to toggle source

The list of handlers, with any in the priority_map moved to the front

# File lib/chef/provider_resolver.rb, line 96
def prioritized_handlers
  @prioritized_handlers ||= begin
    supported_handlers = self.supported_handlers
    if supported_handlers.empty?
      # We always require a provider to be able to call define_resource_requirements on.  In the why-run case we need
      # a provider to say "assuming /etc/init.d/whatever would have been installed" and in the non-why-run case we
      # need to make a best guess at "cannot find /etc/init.d/whatever".  We are essentially defining a "default" provider
      # for the platform, which is the best we can do, but which might give misleading errors, but we cannot read minds.
      Chef::Log.trace "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
priority_map() click to toggle source
# File lib/chef/provider_resolver.rb, line 140
def priority_map
  Chef.provider_priority_map
end