summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Keiser <john@johnkeiser.com>2015-04-29 08:11:22 -0700
committerJohn Keiser <john@johnkeiser.com>2015-04-29 08:11:22 -0700
commit7b67135065d813940a8675dd080c28d06db04f09 (patch)
tree0244f53d24b9764c90ad04d749d1093b9d74f212
parent709747115b31427a289f04bf5a35e8ba342ae40a (diff)
downloadchef-jk/dsl_modules_instead_of_maps.tar.gz
Spike (not working) on modules instead of priority/provider mapsjk/dsl_modules_instead_of_maps
-rw-r--r--lib/chef/chef_class.rb109
-rw-r--r--lib/chef/dsl/providers.rb83
-rw-r--r--lib/chef/dsl/recipe.rb89
-rw-r--r--lib/chef/dsl/resource_creation.rb26
-rw-r--r--lib/chef/dsl/resources.rb87
-rw-r--r--lib/chef/mixin/callers.rb14
-rw-r--r--lib/chef/mixin/provides.rb103
-rw-r--r--lib/chef/node_filter.rb40
-rw-r--r--lib/chef/node_map.rb79
-rw-r--r--lib/chef/platform/provider_priority_map.rb72
-rw-r--r--lib/chef/provider.rb7
-rw-r--r--lib/chef/provider_resolver.rb104
-rw-r--r--lib/chef/resource.rb85
-rw-r--r--lib/chef/resource_resolver.rb103
-rw-r--r--lib/chef/run_context.rb46
15 files changed, 551 insertions, 496 deletions
diff --git a/lib/chef/chef_class.rb b/lib/chef/chef_class.rb
index d3f7ee55c7..f0c7693a49 100644
--- a/lib/chef/chef_class.rb
+++ b/lib/chef/chef_class.rb
@@ -33,50 +33,16 @@ class Chef
# Public API
#
- # Get the node object
- #
- # @return [Chef::Node] node object of the chef-client run
- attr_reader :node
-
# Get the run context
#
# @return [Chef::RunContext] run_context of the chef-client run
attr_reader :run_context
- # Get the array of providers associated with a resource_name for the current node
- #
- # @param resource_name [Symbol] name of the resource as a symbol
- # @return [Array<Class>] Priority Array of Provider Classes to use for the resource_name on the node
- def get_provider_priority_array(resource_name)
- @provider_priority_map.get_priority_array(node, resource_name).dup
- end
-
- # Get the array of resources associated with a resource_name for the current node
- #
- # @param resource_name [Symbol] name of the resource as a symbol
- # @return [Array<Class>] Priority Array of Resource Classes to use for the resource_name on the node
- def get_resource_priority_array(resource_name)
- @resource_priority_map.get_priority_array(node, resource_name).dup
- end
-
- # Set the array of providers associated with a resource_name for the current node
- #
- # @param resource_name [Symbol] name of the resource as a symbol
- # @param priority_array [Array<Class>] Array of Classes to set as the priority for resource_name on the node
- # @param filter [Hash] Chef::Nodearray-style filter
- # @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)
- @provider_priority_map.set_priority_array(resource_name, priority_array, *filter).dup
- end
-
- # Get the array of resources associated with a resource_name for the current node
+ # Get the node object
#
- # @param resource_name [Symbol] name of the resource as a symbol
- # @param priority_array [Array<Class>] Array of Classes to set as the priority for resource_name on the node
- # @param filter [Hash] Chef::Nodearray-style filter
- # @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)
- @resource_priority_map.set_priority_array(resource_name, priority_array, *filter).dup
+ # @return [Chef::Node] node object of the chef-client run
+ def node
+ run_context.node
end
#
@@ -85,30 +51,6 @@ class Chef
# *NOT* for public consumption ]
#
- # Sets the resource_priority_map
- #
- # @api private
- # @param resource_priority_map [Chef::Platform::ResourcePriorityMap]
- def set_resource_priority_map(resource_priority_map)
- @resource_priority_map = resource_priority_map
- end
-
- # Sets the provider_priority_map
- #
- # @api private
- # @param provider_priority_map [Chef::Platform::providerPriorityMap]
- def set_provider_priority_map(provider_priority_map)
- @provider_priority_map = provider_priority_map
- end
-
- # Sets the node object
- #
- # @api private
- # @param node [Chef::Node]
- def set_node(node)
- @node = node
- end
-
# Sets the run_context object
#
# @api private
@@ -122,9 +64,46 @@ class Chef
# @api private
def reset!
@run_context = nil
- @node = nil
- @provider_priority_map = nil
- @resource_priority_map = nil
end
+
+ module BackcompatBreak
+ # Get the array of providers associated with a resource_name for the current node
+ #
+ # @param resource_name [Symbol] name of the resource as a symbol
+ # @return [Array<Class>] Priority Array of Provider Classes to use for the resource_name on the node
+ def get_provider_priority_array(resource_name)
+ raise NotImplementedError, "this will no longer work"
+ end
+
+ # Get the array of resources associated with a resource_name for the current node
+ #
+ # @param resource_name [Symbol] name of the resource as a symbol
+ # @return [Array<Class>] Priority Array of Resource Classes to use for the resource_name on the node
+ def get_resource_priority_array(resource_name)
+ raise NotImplementedError, "this will no longer work"
+ end
+
+ # Set the array of providers associated with a resource_name for the current node
+ #
+ # @param resource_name [Symbol] name of the resource as a symbol
+ # @param priority_array [Array<Class>] Array of Classes to set as the priority for resource_name on the node
+ # @param filter [Hash] Chef::Nodearray-style filter
+ # @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)
+ raise NotImplementedError, "this will no longer work"
+ end
+
+ # Get the array of resources associated with a resource_name for the current node
+ #
+ # @param resource_name [Symbol] name of the resource as a symbol
+ # @param priority_array [Array<Class>] Array of Classes to set as the priority for resource_name on the node
+ # @param filter [Hash] Chef::Nodearray-style filter
+ # @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)
+ raise NotImplementedError, "this will no longer work"
+ end
+
+ end
+ include BackcompatBreak
end
end
diff --git a/lib/chef/dsl/providers.rb b/lib/chef/dsl/providers.rb
new file mode 100644
index 0000000000..2e2b14b245
--- /dev/null
+++ b/lib/chef/dsl/providers.rb
@@ -0,0 +1,83 @@
+class Chef
+ module DSL
+ #
+ # Methods in this module each return one Provider class (or none if there
+ # are no providers associated with the given DSL and action).
+ #
+ # run_context.provider_classes.service #=> Chef::Provider::RunitService
+ #
+ module Providers
+ module DeclaredProviders
+ end
+ include DeclaredProviders
+
+ #
+ # Platform-specific services with nice priority order
+ #
+ def service
+ return systemd_service if systemd_service
+
+ #
+ # Check for the BSDs, Solaris and Mac
+ #
+ case run_context.node[:os]
+ when 'freebsd', 'netbsd'
+ freebsd_service
+ when 'openbsd'
+ openbsd_service
+ when 'solaris2'
+ solaris_service
+ when 'darwin'
+ macosx_service
+ when 'linux'
+
+ #
+ # Different Linuxes are different
+ #
+ case run_context.node[:platform]
+ when 'arch'
+ arch_service
+ when 'gentoo'
+ gentoo_service
+ when 'debian'
+ # on debian-ish system if an upstart script exists that must win over sysv types
+ upstart_service ||
+ insserv_service ||
+ debian_service ||
+ invokercd_service
+ when 'rhel', 'fedora', 'suse'
+ insserv_service ||
+ redhat_service
+ else
+ insserv_service ||
+ redhat_service
+ end
+
+ end ||
+ super
+ end
+
+ #
+ # Platform-specific priority for packages
+ #
+ def package
+ case run_context.node[:os]
+ when 'darwin'
+ homebrew_package
+ end ||
+ super
+ end
+
+ def self.add_provider(name, priority, &block)
+ node_map.set(name, priority: priority, &block)
+ end
+
+ private
+
+ # This is the level at which we declare selectors
+ def self.node_map
+ @node_map ||= {}
+ end
+ end
+ end
+end
diff --git a/lib/chef/dsl/recipe.rb b/lib/chef/dsl/recipe.rb
index e17e008432..a5206819c4 100644
--- a/lib/chef/dsl/recipe.rb
+++ b/lib/chef/dsl/recipe.rb
@@ -34,59 +34,7 @@ class Chef
include Chef::Mixin::ShellOut
- # method_missing must live for backcompat purposes until Chef 13.
- def method_missing(method_symbol, *args, &block)
- #
- # If there is already DSL for this, someone must have called
- # method_missing manually. Not a fan. Not. A. Fan.
- #
- if respond_to?(method_symbol)
- Chef::Log.warn("Calling method_missing(#{method_symbol.inspect}) directly is deprecated in Chef 12 and will be removed in Chef 13.")
- Chef::Log.warn("Use public_send() or send() instead.")
- return send(method_symbol, *args, &block)
- end
-
- #
- # If a definition exists, then Chef::DSL::Definitions.add_definition was
- # never called. DEPRECATED.
- #
- if run_context.definitions.has_key?(method_symbol.to_sym)
- Chef::Log.warn("Definition #{method_symbol} (#{run_context.definitions[method_symbol.to_sym]}) was added to the run_context without calling Chef::DSL::Definitions.add_definition(#{method_symbol.to_sym.inspect}). This will become required in Chef 13.")
- Chef::DSL::Definitions.add_definition(method_symbol)
- return send(method_symbol, *args, &block)
- end
-
- #
- # See if the resource exists anyway. If the user had set
- # Chef::Resource::Blah = <resource>, a deprecation warning will be
- # emitted and the DSL method 'blah' will be added to the DSL.
- #
- resource_class = Chef::ResourceResolver.new(run_context ? run_context.node : nil, method_symbol).resolve
- if resource_class
- #
- # If the DSL method was *not* added, this is the case where the
- # matching class implements 'provides?' and matches resources that it
- # never declared "provides" for (which means we would never have
- # created DSL). Anything where we don't create DSL is deprecated.
- #
- if !respond_to?(method_symbol)
- Chef::Log.warn("#{resource_class} is marked as providing DSL #{method_symbol}, but provides #{method_symbol.inspect} was never called!")
- Chef::Log.warn("In Chef 13, this will break: you must call provides to mark the names you provide, even if you also override provides? yourself.")
- Chef::DSL::Resources.add_resource_dsl(method_symbol)
- end
- return send(method_symbol, *args, &block)
- end
-
- begin
- super
- rescue NoMethodError
- raise NoMethodError, "No resource or method named `#{method_symbol}' for #{describe_self_for_error}"
- rescue NameError
- raise NameError, "No resource, method, or local variable named `#{method_symbol}' for #{describe_self_for_error}"
- end
- end
-
- include Chef::DSL::Resources
+ include Chef::DSL::ResourceCreation
include Chef::DSL::Definitions
#
@@ -183,6 +131,41 @@ class Chef
raise Chef::Exceptions::ResourceNotFound, "exec was called, but you probably meant to use an execute resource. If not, please call Kernel#exec explicitly. The exec block called was \"#{args}\""
end
+ module Deprecated
+ # method_missing must live for backcompat purposes until Chef 13.
+ def method_missing(method_symbol, *args, &block)
+ #
+ # If there is already DSL for this, someone must have called
+ # method_missing manually. Not a fan. Not. A. Fan.
+ #
+ if respond_to?(method_symbol)
+ Chef::Log.warn("Calling method_missing(#{method_symbol.inspect}) directly is deprecated in Chef 12 and will be removed in Chef 13.")
+ Chef::Log.warn("Use public_send() or send() instead.")
+ return send(method_symbol, *args, &block)
+ end
+
+ #
+ # If a definition exists, then Chef::DSL::Definitions.add_definition was
+ # never called. DEPRECATED.
+ #
+ if run_context.definitions.has_key?(method_symbol.to_sym)
+ Chef::Log.warn("Definition #{method_symbol} (#{run_context.definitions[method_symbol.to_sym]}) was added to the run_context without calling Chef::DSL::Definitions.add_definition(#{method_symbol.to_sym.inspect}). This will become required in Chef 13.")
+ Chef::DSL::Definitions.add_definition(method_symbol)
+ return send(method_symbol, *args, &block)
+ end
+
+ begin
+ super
+ rescue NoMethodError
+ raise NoMethodError, "No resource or method named `#{method_symbol}' for #{describe_self_for_error}"
+ rescue NameError
+ raise NameError, "No resource, method, or local variable named `#{method_symbol}' for #{describe_self_for_error}"
+ end
+ end
+ end
+
+ include Deprecated
+
end
end
end
diff --git a/lib/chef/dsl/resource_creation.rb b/lib/chef/dsl/resource_creation.rb
new file mode 100644
index 0000000000..b43636e61d
--- /dev/null
+++ b/lib/chef/dsl/resource_creation.rb
@@ -0,0 +1,26 @@
+require 'chef/dsl/dsl_module'
+
+class Chef
+ module DSL
+ #
+ # Methods in this module each return one Resource class (or none if there
+ # are no resources associated with the given DSL).
+ #
+ # run_context.resource_classes.service #=> Chef::Resource::RunitService
+ #
+ module ResourceCreation
+ module AutomaticResourceCreation
+ end
+ include AutomaticResourceCreation
+
+ module Deprecated
+ def method_missing(name, *args, &block)
+ if Chef::DSL::Resources.find_deprecated_classes(name)
+ public_send(name, *args, &block)
+ end
+ end
+ end
+ include Deprecated
+ end
+ end
+end
diff --git a/lib/chef/dsl/resources.rb b/lib/chef/dsl/resources.rb
index 482b14e3aa..b309f96295 100644
--- a/lib/chef/dsl/resources.rb
+++ b/lib/chef/dsl/resources.rb
@@ -1,22 +1,87 @@
+require 'chef/dsl/dsl_module'
+
class Chef
module DSL
#
- # Module containing a method for each globally declared Resource
+ # Methods in this module each return one Resource class (or none if there
+ # are no resources associated with the given DSL).
#
- # Depends on declare_resource(name, created_at, &block)
+ # run_context.resource_classes.service #=> Chef::Resource::RunitService
#
- # @api private
module Resources
- def self.add_resource_dsl(dsl_name)
- module_eval <<-EOM, __FILE__, __LINE__+1
- def #{dsl_name}(name, created_at=nil, &block)
- declare_resource(#{dsl_name.inspect}, name, created_at || caller[0], &block)
- end
- EOM
+ module DeclaredResources
+ end
+ include DeclaredResources
+
+ def self.node_map
+ @node_map ||= {}
+ end
+
+ #
+ # Ensures that any resource classes added here are reflected in resource
+ # creation DSL.
+ #
+ module ReflectChangesInResourceCreation
+ # When resources are added here, we add the corresponding method to ResourceCreation
+ def method_added(name)
+ Chef::DSL::ResourceCreation::AutomaticResourceCreation.module_eval <<-EOM, __FILE__, __LINE__+1
+ def #{name}(name, &block)
+ declare_resource(#{name.inspect}, name, caller[0], &block)
+ end
+ EOM
+ end
+
+ def method_removed(name)
+ Chef::DSL::ResourceCreation::AutomaticResourceCreation.remove_method(name)
+ end
+ end
+
+ module DeclaredResources
+ extend ReflectChangesInResourceCreation
end
- def self.remove_resource_dsl(dsl_name)
- remove_method dsl_name if method_defined?(dsl_name)
+
+ extend ReflectChangesInResourceCreation
+
+
+ module Deprecated
+ include Chef::Mixin::ConvertToClassName
+
+ #
+ # Handles the deprecated search for Chef::Resource::Blah
+ # and for scanning all resources that have `provides?` but
+ # do not have `provides`.
+ #
+ def find_deprecated_resource_classes(name)
+ class_name = convert_to_class_name(method_symbol)
+ if Chef::Resource.const_defined?(class_name)
+ Chef::Log.warn("Class Chef::Resource::#{class_name} does not declare `provides #{resource.inspect}`.")
+ Chef::Log.warn("This will no longer work in Chef 13: you must use `provides` to provide DSL.")
+ chef_resource_class << Chef::Resource.const_get(class_name)
+ provider.provides chef_resource_class
+ end
+ providers = Chef::Resource.descendants.select { |r| r.provides?(node, method_symbol) }
+ if !providers.empty?
+ resource_classes += providers
+ Chef::Log.warn("#{resource_class} is marked as providing DSL #{method_symbol}, but provides #{method_symbol.inspect} was never called!")
+ Chef::Log.warn("In Chef 13, this will break: you must call provides to mark the names you provide, even if you also override provides? yourself.")
+ providers.each do |provider|
+ provider.provides(name) { provider.provides?(run_context.node, name) } unless chef_resource_class == provider
+ end
+ Chef::DSL::Resources.add_resource_dsl(method_symbol)
+ end
+
+ if Chef::Resource.const_defined?(class_name)
+ end
+ if
+ end
+ def method_missing(name, *args, &block)
+ if find_deprecated_reosurce_classes(name)
+ public_send(name, *args, &block)
+ end
+ end
end
+ include Deprecated
+
end
end
end
diff --git a/lib/chef/mixin/callers.rb b/lib/chef/mixin/callers.rb
new file mode 100644
index 0000000000..d6eea96780
--- /dev/null
+++ b/lib/chef/mixin/callers.rb
@@ -0,0 +1,14 @@
+class Chef
+ module Mixin
+ module Callers
+ def parse_caller(at)
+ if /^(.+?):(\d+)(?::in `(.*)')?/ =~ at
+ file = Regexp.last_match[1]
+ line = Regexp.last_match[2].to_i
+ method = Regexp.last_match[3]
+ [file, line, method]
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/mixin/provides.rb b/lib/chef/mixin/provides.rb
index b40a1bc23b..bc2fcded26 100644
--- a/lib/chef/mixin/provides.rb
+++ b/lib/chef/mixin/provides.rb
@@ -1,37 +1,102 @@
require 'chef/mixin/descendants_tracker'
+require 'set'
class Chef
module Mixin
+ #
+ # Lets objects cooperate to create a "named provider module," allowing many
+ # Resources / Providers / what have you to say "I would like to handle this
+ # DSL, but only on OS X / Linux / etc."
+ #
module Provides
- include Chef::Mixin::DescendantsTracker
-
- def node_map
- @node_map ||= Chef::NodeMap.new
+ #
+ # The DSL module we provide DSL to.
+ #
+ # @return [DSLModule] The DSL module we provide DSL to.
+ #
+ def provides_dsl_module
+ raise NotImplementedError, "#{self.class}.dsl_module"
end
- def provides(short_name, opts={}, &block)
- if !short_name.kind_of?(Symbol)
- # YAGNI: this is probably completely unnecessary and can be removed?
- Chef::Log.deprecation "Passing a non-Symbol to Chef::Resource#provides will be removed"
- if short_name.kind_of?(String)
- short_name.downcase!
- short_name.gsub!(/\s/, "_")
+ #
+ # Mark this resource as a provider for the given DSL.
+ #
+ # @param short_name [Symbol] The name of the DSL, e.g. `:file`
+ # @param os [String, Array[String]] The operating system on which you
+ # provide the DSL. Corresponds to `node[:os]`. May
+ # include positive matches ('ubuntu'), negative matches ('!windows') or
+ # :all.
+ # @param platform [String, Array[String]] The platform on which you
+ # provide the DSL. Corresponds to `node[:platform]`. May
+ # include positive matches ('ubuntu'), negative matches ('!windows') or
+ # :all.
+ # @param platform_family [String, Array[String]] The platform family on
+ # which you provide the DSL. Corresponds to `node[:platform_family]`. May
+ # include positive matches ('ubuntu'), negative matches ('!windows') or
+ # :all.
+ #
+ def provides(short_name, os: nil, platform: nil, platform_family: nil, &block)
+ short_name = short_name.to_sym
+ provides_dsl_module.node_map.set(short_name, self, opts, &block)
+
+ provides_dsl_module.module_eval <<-EOM, __FILE__, __LINE__+1
+ def #{name}
+ provides_dsl_module.node_map.get(short_name, run_context.node)
end
- short_name = short_name.to_sym
- end
- node_map.set(short_name, true, opts, &block)
+ EOM
end
def provides_nothing
- node_map.clear
+ provides_dsl_module.node_map.remove_if { |name, value, *other| value == self }
end
- # Check whether this resource provides the resource_name DSL for the given
- # node
- def provides?(node, resource_name)
- node_map.get(node, resource_name)
+ module Deprecated
+ include Chef::Mixin::DescendantsTracker
+
+ def node_map(warn: true)
+ Chef::Log.deprecation "Referencing node_map on #{self.class} is deprecated and will stop working in a future major version."
+ @node_map ||= Chef::NodeMap.new
+ end
+
+ # Check whether this resource provides the resource_name DSL for the given
+ # node
+ def provides?(node, resource_name)
+ node_map(warn: false).get(node, resource_name)
+ end
+
+ def provides(short_name, platform: nil, os: nil, platform_family: nil,
+ on_platform: nil, on_platforms: nil,
+ &block)
+ if !short_name.kind_of?(Symbol)
+ # YAGNI: this is probably completely unnecessary and can be removed?
+ Chef::Log.deprecation "Passing a non-Symbol to Chef::Resource#provides will be removed"
+ if short_name.kind_of?(String)
+ short_name.downcase!
+ short_name.gsub!(/\s/, "_")
+ end
+ short_name = short_name.to_sym
+ end
+
+ if on_platform
+ Chef::Log.deprecation "The #{key} option to provides has been deprecated"
+ end
+ if on_platforms
+ Chef::Log.deprecation "The #{key} option to provides has been deprecated"
+ end
+
+ super(short_name, os: os, platform_family: platform_family,
+ platform: platform || on_platform || on_platforms)
+
+ node_map.set(short_name, true, opts, &block)
+ end
+
+ def provides_nothing
+ node_map(warn: false).clear
+ super
+ end
end
+ prepend Deprecated
end
end
end
diff --git a/lib/chef/node_filter.rb b/lib/chef/node_filter.rb
new file mode 100644
index 0000000000..7df59b572b
--- /dev/null
+++ b/lib/chef/node_filter.rb
@@ -0,0 +1,40 @@
+class Chef
+ class NodeMapValue
+ def initialize(&filter)
+ end
+
+ def <=>(other)
+ priority <=> other.priority
+ end
+ def priority
+ Float::INFINITY
+ end
+ def value
+ filter.call(run_context)
+ end
+ end
+
+ class FilteredValue
+ def initialize(name, os: nil, platform: nil, platform_family: nil, &filter)
+ @name = name
+ @os = os
+ @platform = platform
+ @platform_family = platform_family
+ super(&filter)
+ end
+
+ def <=>(other)
+ if other.is_a?(FilteredValue)
+ if platform_family != other.platform_family
+ return platform_family ? -1 : 1
+ end
+ if platform != other.platform
+ return platform ? -1 : 1
+ end
+ if os != other.os
+ return os ? -1 : 1
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/node_map.rb b/lib/chef/node_map.rb
index 17fda4a271..527a33c2f5 100644
--- a/lib/chef/node_map.rb
+++ b/lib/chef/node_map.rb
@@ -47,14 +47,16 @@ class Chef
# @yield [node] Arbitrary node filter as a block which takes a node argument
# @return [NodeMap] Returns self for possible chaining
#
- def set(key, value, filters = {}, &block)
- validate_filter!(filters)
- deprecate_filter!(filters)
- @map[key] ||= []
+ def set(key, node_filter,
+ priority: Float::INFINITY,
+ &block)
+ Chef::Log.warn "The #{key} option to node_map has been deprecated" if DEPRECATED_OPTS.include?(key)
+
+ map[key] ||= SortedSet.new()
# we match on the first value we find, so we want to unshift so that the
# last setter wins
# FIXME: need a test for this behavior
- @map[key].unshift({ filters: filters, block: block, value: value })
+ map[key].unshift({ filters: filters, block: block, value: value })
self
end
@@ -70,14 +72,22 @@ class Chef
raise "first argument must be a Chef::Node" unless node.is_a?(Chef::Node)
return nil unless @map.has_key?(key)
@map[key].each do |matcher|
- if filters_match?(node, matcher[:filters]) &&
- block_matches?(node, matcher[:block])
- return matcher[:value]
+ if filters_match?(node, matcher[:filters])
+ result = block.nil? || block.call(node)
+ if result
+ return result == true ? matcher[:value] : result
+ end
end
end
nil
end
+ def delete_value(value)
+ @map.each do |key, matcher|
+ matchers.delete_if { |matcher| matcher[:value] == value }
+ end
+ end
+
def clear
result = @map.keys
@map.clear
@@ -86,21 +96,6 @@ class Chef
private
- # only allow valid filter options
- def validate_filter!(filters)
- filters.each_key do |key|
- # FIXME: real exception
- raise "Bad key #{key} in Chef::NodeMap filter expression" unless VALID_OPTS.include?(key)
- end
- end
-
- # warn on deprecated filter options
- def deprecate_filter!(filters)
- filters.each_key do |key|
- Chef::Log.warn "The #{key} option to node_map has been deprecated" if DEPRECATED_OPTS.include?(key)
- end
- end
-
# @todo: this works fine, but is probably hard to understand
def negative_match(filter, param)
# We support strings prefaced by '!' to mean 'not'. In particular, this is most useful
@@ -144,9 +139,41 @@ class Chef
return true
end
- def block_matches?(node, block)
- return true if block.nil?
- block.call node
+ module Deprecated
+ def set(name, value = nil,
+ on_platform: nil,
+ on_platforms: nil,
+ platform: nil,
+ os: nil,
+ platform_family: nil,
+ priority: Float::INFINITY,
+ &block)
+ if on_platform
+ Chef::Log.deprecation "The on_platform option to node_map has been deprecated"
+ end
+ if on_platforms
+ Chef::Log.deprecation "The on_platforms option to node_map has been deprecated"
+ end
+
+ # platform: 'ubuntu' > platform_family: 'debian' > os: 'linux'
+ priority ||= 2 if platform
+ priority ||= 3 if platform_family
+ priority ||= 4 if os
+ priority ||= 5
+
+ if os || platform_family || platform
+ block = proc do |node|
+ return false if os && negative_match?(os, node[:os])
+ return false if platform_family && negative_match?(platform_family, node[:platform_family])
+ return false if platform && negative_match?(platform, node[:platform])
+ result = block.call
+ return value || result
+ end
+ end
+
+ super(name, value, priority: priority, &block)
+ end
end
+ prepend Deprecated
end
end
diff --git a/lib/chef/platform/provider_priority_map.rb b/lib/chef/platform/provider_priority_map.rb
index 1539f61900..c3b20fe02d 100644
--- a/lib/chef/platform/provider_priority_map.rb
+++ b/lib/chef/platform/provider_priority_map.rb
@@ -4,10 +4,6 @@ class Chef
class ProviderPriorityMap
include Singleton
- def initialize
- load_default_map
- end
-
def get_priority_array(node, resource_name)
priority_map.get(node, resource_name.to_sym)
end
@@ -19,74 +15,6 @@ class Chef
def priority(*args)
priority_map.set(*args)
end
-
- private
-
- def load_default_map
- require 'chef/providers'
-
- #
- # Linux
- #
-
- # default block for linux O/Sen must come before platform_family exceptions
- priority :service, [
- Chef::Provider::Service::Systemd,
- Chef::Provider::Service::Insserv,
- Chef::Provider::Service::Redhat,
- ], os: "linux"
-
- priority :service, [
- Chef::Provider::Service::Systemd,
- Chef::Provider::Service::Arch,
- ], platform_family: "arch"
-
- priority :service, [
- Chef::Provider::Service::Systemd,
- Chef::Provider::Service::Gentoo,
- ], platform_family: "gentoo"
-
- priority :service, [
- # we can determine what systemd supports accurately
- Chef::Provider::Service::Systemd,
- # on debian-ish system if an upstart script exists that must win over sysv types
- Chef::Provider::Service::Upstart,
- Chef::Provider::Service::Insserv,
- Chef::Provider::Service::Debian,
- Chef::Provider::Service::Invokercd,
- ], platform_family: "debian"
-
- priority :service, [
- Chef::Provider::Service::Systemd,
- Chef::Provider::Service::Insserv,
- Chef::Provider::Service::Redhat,
- ], platform_family: [ "rhel", "fedora", "suse" ]
-
- #
- # BSDen
- #
-
- priority :service, Chef::Provider::Service::Freebsd, os: [ "freebsd", "netbsd" ]
- priority :service, Chef::Provider::Service::Openbsd, os: [ "openbsd" ]
-
- #
- # Solaris-en
- #
-
- priority :service, Chef::Provider::Service::Solaris, os: "solaris2"
-
- #
- # Mac
- #
-
- priority :service, Chef::Provider::Service::Macosx, os: "darwin"
- priority :package, Chef::Provider::Package::Homebrew, os: "darwin"
- end
-
- 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 65a56cf726..bd76e9e414 100644
--- a/lib/chef/provider.rb
+++ b/lib/chef/provider.rb
@@ -165,6 +165,13 @@ class Chef
converge_actions.add_action(descriptions, &block)
end
+ #
+ # The module Chef::Mixin::Provides will use to register providers.
+ #
+ def self.provides_dsl_module
+ Chef::DSL::Resources::DeclaredProviders
+ end
+
protected
def converge_actions
diff --git a/lib/chef/provider_resolver.rb b/lib/chef/provider_resolver.rb
index 867c3deca8..f3de2fbfc5 100644
--- a/lib/chef/provider_resolver.rb
+++ b/lib/chef/provider_resolver.rb
@@ -16,107 +16,13 @@
# limitations under the License.
#
-require 'chef/exceptions'
-require 'chef/platform/provider_priority_map'
-
class Chef
- class ProviderResolver
-
- attr_reader :node
- attr_reader :resource
- attr_reader :action
-
- def initialize(node, resource, action)
- @node = node
- @resource = resource
- @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|
- klass.provides?(node, resource.resource_name)
- 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
-
- private
-
- # if resource.provider is set, just return one of those objects
- def maybe_explicit_provider(resource)
- return nil unless resource.provider
- resource.provider
- end
-
- # 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 ]
+ module Deprecated
+ class ProviderResolver < Struct[:node, :resource]
+ def resolve
+ resource.provider || Chef.run_context.provider_classes.public_send(arg)
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?
-
- handlers[0]
- end
-
- # try the old static lookup of providers by platform
- def maybe_chef_platform_lookup(resource)
- 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
end
+ ProviderResolver = Deprecated::ProviderResolver
end
diff --git a/lib/chef/resource.rb b/lib/chef/resource.rb
index b20ee05cdb..d7dad9e734 100644
--- a/lib/chef/resource.rb
+++ b/lib/chef/resource.rb
@@ -675,7 +675,7 @@ class Chef
#
def provider(arg=nil)
klass = if arg.kind_of?(String) || arg.kind_of?(Symbol)
- lookup_provider_constant(arg)
+ Chef.run_context.provider_classes.public_send(arg)
else
arg
end
@@ -808,17 +808,6 @@ class Chef
end
#
- # The DSL name of this resource (e.g. `package` or `yum_package`)
- #
- # @return [String] The DSL name of this resource.
- def self.dsl_name
- if name
- name = self.name.split('::')[-1]
- convert_to_snake_case(name)
- end
- end
-
- #
# The name of this resource (e.g. `file`)
#
# @return [String] The name of this resource.
@@ -944,15 +933,6 @@ class Chef
run_context.notifies_delayed(Notification.new(resource_spec, action, self))
end
- class << self
- # back-compat
- # NOTE: that we do not support unregistering classes as descendents like
- # we used to for LWRP unloading because that was horrible and removed in
- # Chef-12.
- alias :resource_classes :descendants
- alias :find_subclass_by_name :find_descendants_by_name
- end
-
# If an unknown method is invoked, determine whether the enclosing Provider's
# lexical scope can fulfill the request. E.g. This happens when the Resource's
# block invokes new_resource.
@@ -964,22 +944,6 @@ class Chef
end
end
- def self.provides(name, *args, &block)
- super
- Chef::DSL::Resources.add_resource_dsl(name)
- end
-
- def self.provides_nothing
- unprovided_names = super
-
- unprovided_names.each do |name|
- resource = resource_matching_short_name(name)
- if !resource || resource == self
- Chef::DSL::Resources.remove_resource_dsl(name)
- end
- end
- end
-
# Helper for #notifies
def validate_resource_spec!(resource_spec)
run_context.resource_collection.validate_lookup_spec!(resource_spec)
@@ -1036,10 +1000,7 @@ class Chef
end
def provider_for_action(action)
- require 'chef/provider_resolver'
- provider = Chef::ProviderResolver.new(node, self, action).resolve.new(self, run_context)
- provider.action = action
- provider
+ Chef.run_context.provider_classes.public_send(resource.resource_name)
end
# ??? TODO Seems unused. Delete?
@@ -1129,20 +1090,38 @@ class Chef
Chef::ResourceResolver.new(Chef::Node.new, short_name).resolve
end
- private
-
- def lookup_provider_constant(name)
- begin
- self.class.provider_base.const_get(convert_to_class_name(name.to_s))
- rescue NameError => e
- if e.to_s =~ /#{Regexp.escape(self.class.provider_base.to_s)}/
- raise ArgumentError, "No provider found to match '#{name}'"
- else
- raise e
+ module Deprecated
+ #
+ # The DSL name of this resource (e.g. `package` or `yum_package`)
+ #
+ # @return [String] The DSL name of this resource.
+ def self.dsl_name
+ if name
+ name = self.name.split('::')[-1]
+ convert_to_snake_case(name)
end
end
end
+ include Deprecated
+
+ module DeprecatedClassMethods
+ include DescendantTracker
+ # back-compat
+ # NOTE: that we do not support unregistering classes as descendents like
+ # we used to for LWRP unloading because that was horrible and removed in
+ # Chef-12.
+ alias :resource_classes :descendants
+ alias :find_subclass_by_name :find_descendants_by_name
+ end
+ extend DeprecatedClassMethods
+
+ protected
+
+ #
+ # The module Chef::Mixin::Provides will use to register resources.
+ #
+ def self.provides_dsl_module
+ Chef::DSL::Resources::DeclaredResources
+ end
end
end
-
-require 'chef/resource_resolver'
diff --git a/lib/chef/resource_resolver.rb b/lib/chef/resource_resolver.rb
index c9bf2e189a..88ee218924 100644
--- a/lib/chef/resource_resolver.rb
+++ b/lib/chef/resource_resolver.rb
@@ -21,105 +21,12 @@ require 'chef/platform/resource_priority_map'
require 'chef/mixin/convert_to_class_name'
class Chef
- class ResourceResolver
- include Chef::Mixin::ConvertToClassName
-
- attr_reader :node
- attr_reader :resource
- attr_reader :action
-
- def initialize(node, resource)
- @node = node
- @resource = resource
- end
-
- # return a deterministically sorted list of Chef::Resource subclasses
- def resources
- @resources ||= Chef::Resource.descendants
- 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}"
-
- # if none of the resources specifically support the resource, we still need to pick one of the resources that are
- # enabled on the node to handle the why-run use case.
- handlers = enabled_handlers
-
- if handlers.size >= 2
- # this magic stack ranks the resources by where they appear in the resource_priority_map
- priority_list = [ get_priority_array(node, resource) ].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 resource precedence: #{handlers}, please use Chef.set_resource_priority_array to provide determinism"
- end
- handlers = handlers[0..0]
- end
-
- Chef::Log.debug "resources that survived replacement include: #{handlers}"
-
- raise Chef::Exceptions::AmbiguousResourceResolution.new(resource, handlers) if handlers.count >= 2
-
- Chef::Log.debug "dynamic resource resolver FAILED to resolve a resouce" if handlers.empty?
-
- return nil if handlers.empty?
-
- handlers[0]
- end
-
- # this cut looks at if the resource can handle the resource type on the node
- def enabled_handlers
- @enabled_handlers ||= begin
- enabled_handlers = resources.select do |klass|
- klass.provides?(node, resource)
- end.sort {|a,b| a.to_s <=> b.to_s }
-
- # Add Chef::Resource::X as a possibility if it is not a handler already
- check_for_deprecated_chef_resource_class(enabled_handlers)
-
- enabled_handlers
+ module Deprecated
+ class ResourceResolver < Struct[:node, :resource]
+ def resolve
+ Chef.run_context.resource_classes.public_send(resource)
end
end
-
- private
-
- #
- # Checks if the Chef::Resource::* class corresponding to the DSL name
- # exists, emits a deprecation warning, marks it as providing the given
- # short name and adds it to the DSL. This is used for method_missing
- # deprecation and ResourceResolver checking, when people have created
- # anonymous classes and assigned them to Chef::Resource::X.
- #
- # Returns the matched class, if it exists.
- #
- # @api private
- def check_for_deprecated_chef_resource_class(enabled_handlers)
- # If Chef::Resource::MyResource exists, but was not set, it won't have a
- # DSL name. Add the DSL method and warn about this pattern.
- class_name = convert_to_class_name(resource.to_s)
- if Chef::Resource.const_defined?(class_name)
- # If Chef::Resource::X already exists, and is *not* already marked as
- # providing this resource, mark it as providing the resource and add it
- # to the list of handlers for next time.
- resource_class = Chef::Resource.const_get(class_name)
- if resource_class <= Chef::Resource && !enabled_handlers.include?(resource_class)
- enabled_handlers << resource_class
- Chef::Log.warn("Class Chef::Resource::#{class_name} does not declare `provides #{resource.inspect}`.")
- Chef::Log.warn("This will no longer work in Chef 13: you must use `provides` to provide DSL.")
- resource_class.provides resource.to_sym
- end
- end
- end
-
- # dep injection hooks
- def get_priority_array(node, resource_name)
- resource_priority_map.get_priority_array(node, resource_name)
- end
-
- def resource_priority_map
- Chef::Platform::ResourcePriorityMap.instance
- end
end
+ ResourceResolver = Deprecated::ResourceResolver
end
diff --git a/lib/chef/run_context.rb b/lib/chef/run_context.rb
index 4f0215bfd4..29edaa262b 100644
--- a/lib/chef/run_context.rb
+++ b/lib/chef/run_context.rb
@@ -299,11 +299,57 @@ ERROR_MESSAGE
@reboot_info.size > 0
end
+ #
+ # The list of resource classes for this Chef run.
+ #
+ # @example
+ # ```ruby
+ # run_context.resource_classes.file
+ # #=> Chef::Resource::File
+ # ```
+ #
+ def resource_classes
+ @resource_classes ||= ResourceClasses.new(run_context)
+ end
+
+ #
+ # The list of provider classes for this Chef run.
+ #
+ # @example
+ # ```ruby
+ # run_context.provider_classes.file
+ # #=> Chef::Provider::File
+ # ```
+ #
+ def provider_classes
+ run_context = self
+ @provider_classes ||= Object.new.tap do
+ @run_context = run_context
+ attr_reader :run_context
+ extend Chef::DSL::Providers
+ end
+ end
+
private
def loaded_recipe(cookbook, recipe)
@loaded_recipes["#{cookbook}::#{recipe}"] = true
end
+ class ResourceClasses
+ def initialize(run_context)
+ @run_context = run_context
+ end
+ attr_reader :run_context
+ include Chef::DSL::Resources
+ end
+
+ class ProviderClasses
+ def initialize(run_context)
+ @run_context = run_context
+ end
+ attr_reader :run_context
+ include Chef::DSL::Providers
+ end
end
end