summaryrefslogtreecommitdiff
path: root/lib/chef/resource_resolver.rb
blob: 22fed7e587a3ffe5f8e34a2cba2004f8d791634a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#
# Author:: Lamont Granquist (<lamont@chef.io>)
# Copyright:: Copyright (c) 2015 Chef Software, 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/exceptions'
require 'chef/platform/resource_priority_map'

class Chef
  class ResourceResolver
    include Chef::Mixin::ConvertToClassName

    attr_reader :node
    attr_reader :resource
    attr_reader :action
    attr_reader :canonical

    def initialize(node, resource)
      @node = node
      @resource = resource.to_sym
    end

    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} resource enabled on node include: #{enabled_handlers}"

      handler = prioritized_handlers.first

      if handler
        Chef::Log.debug "Resource for #{resource} is #{handler}"
      else
        Chef::Log.debug "Dynamic resource resolver FAILED to resolve a resource for #{resource}"
      end

      handler
    end

    def list
      Chef::Log.debug "Resources for generic #{resource} resource enabled on node include: #{enabled_handlers}"
      Chef::Log.debug "Resources for #{resource}: #{prioritized_handlers}"
      prioritized_handlers
    end

    def provided_by?(resource_class)
      !prioritized_handlers.include?(resource_class)
    end

    #
    # Resolve a resource by name.
    #
    # @param resource_name [Symbol] The resource DSL name (e.g. `:file`).
    # @param node [Chef::Node] The node on which the resource will run. If not
    #   passed, will return the first match.
    #
    def self.resolve(resource_name, node: nil)
      new(node, resource_name).resolve
    end

    #
    # Resolve a list of all resources that implement the given DSL (in order of
    # preference).
    #
    # @param resource_name [Symbol] The resource DSL name (e.g. `:file`).
    # @param node [Chef::Node] The node on which the resource will run. If not
    #   passed, will return all resources (ignoring filters).
    #
    def self.list(resource_name, node: nil)
      new(node, resource_name).list
    end

    protected

    def priority_map
      Chef::Platform::ResourcePriorityMap.instance
    end

    def prioritized_handlers
      @prioritized_handlers ||=
        priority_map.list_handlers(node, resource)
    end

    module Deprecated
      # return a deterministically sorted list of Chef::Resource subclasses
      # @deprecated Now prioritized_handlers does its own work (more efficiently)
      def resources
        Chef::Resource.sorted_descendants
      end

      # A list of all handlers
      # @deprecated Now prioritized_handlers does its own work
      def enabled_handlers
        resources.select { |klass| klass.provides?(node, resource) }
      end

      protected

      # If there are no providers for a DSL, we search through the
      def prioritized_handlers
        @prioritized_handlers ||= super ||
          resources.select do |klass|
            # Don't bother calling provides? unless it's overriden. We already
            # know prioritized_handlers
            if klass.method(:provides?).owner != Chef::Resource && klass.provides?(node, resource)
              Chef::Log.deprecation("Resources #{provided.join(", ")} are marked as providing DSL #{resource}, but provides #{resource.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.")
              true
            end
          end
      end
    end
    prepend Deprecated
  end
end