diff options
author | Lamont Granquist <lamont@scriptkiddie.org> | 2014-10-24 10:45:43 -0700 |
---|---|---|
committer | Lamont Granquist <lamont@scriptkiddie.org> | 2014-10-24 10:45:43 -0700 |
commit | 97aaf5bbcdfd0810722b123bdc67e883a7ca8077 (patch) | |
tree | 25663bf1d4f53664b96844251091b51273ad84c7 | |
parent | cb1bcb1f08816f551f96e713624718f58da3c9b3 (diff) | |
download | chef-97aaf5bbcdfd0810722b123bdc67e883a7ca8077.tar.gz |
Chef-12 RC Provider Resolver
makes resource and provider class resolution more dynamic.
begins deprecation of Chef::Platform static mapping.
143 files changed, 1900 insertions, 1308 deletions
diff --git a/lib/chef/dsl/recipe.rb b/lib/chef/dsl/recipe.rb index 94b0d2d18b..120497d56e 100644 --- a/lib/chef/dsl/recipe.rb +++ b/lib/chef/dsl/recipe.rb @@ -17,7 +17,6 @@ # limitations under the License. # -require 'chef/resource_platform_map' require 'chef/mixin/convert_to_class_name' require 'chef/exceptions' diff --git a/lib/chef/exceptions.rb b/lib/chef/exceptions.rb index 67429ac5a2..25f08455fc 100644 --- a/lib/chef/exceptions.rb +++ b/lib/chef/exceptions.rb @@ -91,7 +91,11 @@ class Chef class ResourceNotFound < RuntimeError; end # Can't find a Resource of this type that is valid on this platform. - class NoSuchResourceType < NameError; end + class NoSuchResourceType < NameError + def initialize(short_name, node) + super "Cannot find a resource for #{short_name} on #{node[:platform]} version #{node[:platform_version]}" + end + end class InvalidResourceSpecification < ArgumentError; end class SolrConnectionError < RuntimeError; end @@ -355,5 +359,12 @@ class Chef end class InvalidSearchQuery < ArgumentError; end + + # Raised by Chef::ProviderResolver + class AmbiguousProviderResolution < RuntimeError + def initialize(resource, classes) + super "Found more than one provider for #{resource.resource_name} resource: #{classes}" + end + end end end diff --git a/lib/chef/json_compat.rb b/lib/chef/json_compat.rb index 0796984ab2..d0b3b4c7f8 100644 --- a/lib/chef/json_compat.rb +++ b/lib/chef/json_compat.rb @@ -152,7 +152,7 @@ class Chef when CHEF_RESOURCELIST Chef::ResourceCollection::ResourceList when /^Chef::Resource/ - Chef::Resource.find_subclass_by_name(json_class) + Chef::Resource.find_descendants_by_name(json_class) else raise Chef::Exceptions::JSON::ParseError, "Unsupported `json_class` type '#{json_class}'" end diff --git a/lib/chef/mixin/convert_to_class_name.rb b/lib/chef/mixin/convert_to_class_name.rb index f849b8de6a..19f229fdd3 100644 --- a/lib/chef/mixin/convert_to_class_name.rb +++ b/lib/chef/mixin/convert_to_class_name.rb @@ -61,6 +61,60 @@ class Chef base.to_s + (file_base == 'default' ? '' : "_#{file_base}") end + # Copied from rails activesupport. In ruby >= 2.0 const_get will just do this, so this can + # be deprecated and removed. + # + # MIT LICENSE is here: https://github.com/rails/rails/blob/master/activesupport/MIT-LICENSE + + # Tries to find a constant with the name specified in the argument string. + # + # 'Module'.constantize # => Module + # 'Test::Unit'.constantize # => Test::Unit + # + # The name is assumed to be the one of a top-level constant, no matter + # whether it starts with "::" or not. No lexical context is taken into + # account: + # + # C = 'outside' + # module M + # C = 'inside' + # C # => 'inside' + # 'C'.constantize # => 'outside', same as ::C + # end + # + # NameError is raised when the name is not in CamelCase or the constant is + # unknown. + def constantize(camel_cased_word) + names = camel_cased_word.split('::') + + # Trigger a built-in NameError exception including the ill-formed constant in the message. + Object.const_get(camel_cased_word) if names.empty? + + # Remove the first blank element in case of '::ClassName' notation. + names.shift if names.size > 1 && names.first.empty? + + names.inject(Object) do |constant, name| + if constant == Object + constant.const_get(name) + else + candidate = constant.const_get(name) + next candidate if constant.const_defined?(name, false) + next candidate unless Object.const_defined?(name) + + # Go down the ancestors to check if it is owned directly. The check + # stops when we reach Object or the end of ancestors tree. + constant = constant.ancestors.inject do |const, ancestor| + break const if ancestor == Object + break ancestor if ancestor.const_defined?(name, false) + const + end + + # owner is in Object, so raise + constant.const_get(name, false) + end + end + end + end end end diff --git a/lib/chef/mixin/descendants_tracker.rb b/lib/chef/mixin/descendants_tracker.rb new file mode 100644 index 0000000000..75d1f620d4 --- /dev/null +++ b/lib/chef/mixin/descendants_tracker.rb @@ -0,0 +1,82 @@ +# +# Copyright (c) 2005-2012 David Heinemeier Hansson +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + + +# This is lifted from rails activesupport (note the copyright above): +# https://github.com/rails/rails/blob/9f84e60ac9d7bf07d6ae1bc94f3941f5b8f1a228/activesupport/lib/active_support/descendants_tracker.rb + +class Chef + module Mixin + module DescendantsTracker + @@direct_descendants = {} + + class << self + def direct_descendants(klass) + @@direct_descendants[klass] || [] + end + + def descendants(klass) + arr = [] + accumulate_descendants(klass, arr) + arr + end + + def find_descendants_by_name(klass, name) + descendants(klass).first {|c| c.name == name } + end + + # This is the only method that is not thread safe, but is only ever called + # during the eager loading phase. + def store_inherited(klass, descendant) + (@@direct_descendants[klass] ||= []) << descendant + end + + private + + def accumulate_descendants(klass, acc) + if direct_descendants = @@direct_descendants[klass] + acc.concat(direct_descendants) + direct_descendants.each { |direct_descendant| accumulate_descendants(direct_descendant, acc) } + end + end + end + + def inherited(base) + DescendantsTracker.store_inherited(self, base) + super + end + + def direct_descendants + DescendantsTracker.direct_descendants(self) + end + + def find_descendants_by_name(name) + DescendantsTracker.find_descendants_by_name(self, name) + end + + def descendants + DescendantsTracker.descendants(self) + end + end + end +end diff --git a/lib/chef/node_map.rb b/lib/chef/node_map.rb new file mode 100644 index 0000000000..2ca6d9ba17 --- /dev/null +++ b/lib/chef/node_map.rb @@ -0,0 +1,146 @@ +# +# Author:: Lamont Granquist (<lamont@chef.io>) +# Copyright:: Copyright (c) 2014 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. +# + +class Chef + class NodeMap + + VALID_OPTS = [ + :on_platform, + :on_platforms, + :platform, + :os, + :platform_family, + ] + + DEPRECATED_OPTS = [ + :on_platform, + :on_platforms, + ] + + # Create a new NodeMap + # + def initialize + @map = {} + end + + # Set a key/value pair on the map with a filter. The filter must be true + # when applied to the node in order to retrieve the value. + # + # @param key [Object] Key to store + # @param value [Object] Value associated with the key + # @param filters [Hash] Node filter options to apply to key retrieval + # @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] ||= [] + # 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 }) + self + end + + # Get a value from the NodeMap via applying the node to the filters that + # were set on the key. + # + # @param node [Chef::Node] The Chef::Node object for the run + # @param key [Object] Key to look up + # @return [Object] Value + # + def get(node, key) + # FIXME: real exception + 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] + end + end + nil + end + + 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 + # for os matching on '!windows'. + negative_matches = filter.select { |f| f[0] == '!' } + return true if !negative_matches.empty? && negative_matches.include?('!' + param) + + # We support the symbol :all to match everything, for backcompat, but this can and should + # simply be ommitted. + positive_matches = filter.reject { |f| f[0] == '!' || f == :all } + return true if !positive_matches.empty? && !positive_matches.include?(param) + + # sorry double-negative: this means we pass this filter. + false + end + + def filters_match?(node, filters) + return true if filters.empty? + + # each filter is applied in turn. if any fail, then it shortcuts and returns false. + # if it passes or does not exist it succeeds and continues on. so multiple filters are + # effectively joined by 'and'. all filters can be single strings, or arrays which are + # effectively joined by 'or'. + + os_filter = [ filters[:os] ].flatten.compact + unless os_filter.empty? + return false if negative_match(os_filter, node[:os]) + end + + platform_family_filter = [ filters[:platform_family] ].flatten.compact + unless platform_family_filter.empty? + return false if negative_match(platform_family_filter, node[:platform_family]) + end + + # :on_platform and :on_platforms here are synonyms which are deprecated + platform_filter = [ filters[:platform] || filters[:on_platform] || filters[:on_platforms] ].flatten.compact + unless platform_filter.empty? + return false if negative_match(platform_filter, node[:platform]) + end + + return true + end + + def block_matches?(node, block) + return true if block.nil? + block.call node + end + end +end diff --git a/lib/chef/platform/provider_mapping.rb b/lib/chef/platform/provider_mapping.rb index e6d948276c..382df342f5 100644 --- a/lib/chef/platform/provider_mapping.rb +++ b/lib/chef/platform/provider_mapping.rb @@ -41,7 +41,6 @@ class Chef :mac_os_x => { :default => { :package => Chef::Provider::Package::Homebrew, - :service => Chef::Provider::Service::Macosx, :user => Chef::Provider::User::Dscl, :group => Chef::Provider::Group::Dscl } @@ -49,7 +48,6 @@ class Chef :mac_os_x_server => { :default => { :package => Chef::Provider::Package::Homebrew, - :service => Chef::Provider::Service::Macosx, :user => Chef::Provider::User::Dscl, :group => Chef::Provider::Group::Dscl } @@ -57,7 +55,6 @@ class Chef :freebsd => { :default => { :group => Chef::Provider::Group::Pw, - :service => Chef::Provider::Service::Freebsd, :user => Chef::Provider::User::Pw, :cron => Chef::Provider::Cron } @@ -203,7 +200,7 @@ class Chef :group => Chef::Provider::Group::Gpasswd }, "< 12.0" => { - :group => Chef::Provider::Group::Suse, + :group => Chef::Provider::Group::Suse, :service => Chef::Provider::Service::Redhat } }, @@ -276,7 +273,6 @@ class Chef :mswin => { :default => { :env => Chef::Provider::Env::Windows, - :service => Chef::Provider::Service::Windows, :user => Chef::Provider::User::Windows, :group => Chef::Provider::Group::Windows, :mount => Chef::Provider::Mount::Windows, @@ -287,7 +283,6 @@ class Chef :mingw32 => { :default => { :env => Chef::Provider::Env::Windows, - :service => Chef::Provider::Service::Windows, :user => Chef::Provider::User::Windows, :group => Chef::Provider::Group::Windows, :mount => Chef::Provider::Mount::Windows, @@ -298,7 +293,6 @@ class Chef :windows => { :default => { :env => Chef::Provider::Env::Windows, - :service => Chef::Provider::Service::Windows, :user => Chef::Provider::User::Windows, :group => Chef::Provider::Group::Windows, :mount => Chef::Provider::Mount::Windows, @@ -310,7 +304,6 @@ class Chef :openindiana => { :default => { :mount => Chef::Provider::Mount::Solaris, - :service => Chef::Provider::Service::Solaris, :package => Chef::Provider::Package::Ips, :cron => Chef::Provider::Cron::Solaris, :group => Chef::Provider::Group::Usermod @@ -319,7 +312,6 @@ class Chef :opensolaris => { :default => { :mount => Chef::Provider::Mount::Solaris, - :service => Chef::Provider::Service::Solaris, :package => Chef::Provider::Package::Ips, :cron => Chef::Provider::Cron::Solaris, :group => Chef::Provider::Group::Usermod @@ -328,7 +320,6 @@ class Chef :nexentacore => { :default => { :mount => Chef::Provider::Mount::Solaris, - :service => Chef::Provider::Service::Solaris, :package => Chef::Provider::Package::Solaris, :cron => Chef::Provider::Cron::Solaris, :group => Chef::Provider::Group::Usermod @@ -337,7 +328,6 @@ class Chef :omnios => { :default => { :mount => Chef::Provider::Mount::Solaris, - :service => Chef::Provider::Service::Solaris, :package => Chef::Provider::Package::Ips, :cron => Chef::Provider::Cron::Solaris, :group => Chef::Provider::Group::Usermod, @@ -347,7 +337,6 @@ class Chef :solaris2 => { :default => { :mount => Chef::Provider::Mount::Solaris, - :service => Chef::Provider::Service::Solaris, :package => Chef::Provider::Package::Ips, :cron => Chef::Provider::Cron::Solaris, :group => Chef::Provider::Group::Usermod, @@ -355,7 +344,6 @@ class Chef }, "< 5.11" => { :mount => Chef::Provider::Mount::Solaris, - :service => Chef::Provider::Service::Solaris, :package => Chef::Provider::Package::Solaris, :cron => Chef::Provider::Cron::Solaris, :group => Chef::Provider::Group::Usermod, @@ -365,7 +353,6 @@ class Chef :smartos => { :default => { :mount => Chef::Provider::Mount::Solaris, - :service => Chef::Provider::Service::Solaris, :package => Chef::Provider::Package::SmartOS, :cron => Chef::Provider::Cron::Solaris, :group => Chef::Provider::Group::Usermod @@ -373,7 +360,6 @@ class Chef }, :netbsd => { :default => { - :service => Chef::Provider::Service::Freebsd, :group => Chef::Provider::Group::Groupmod } }, @@ -407,29 +393,10 @@ class Chef } }, :default => { - :file => Chef::Provider::File, - :directory => Chef::Provider::Directory, - :link => Chef::Provider::Link, - :template => Chef::Provider::Template, - :remote_directory => Chef::Provider::RemoteDirectory, - :execute => Chef::Provider::Execute, :mount => Chef::Provider::Mount::Mount, - :script => Chef::Provider::Script, - :service => Chef::Provider::Service::Init, - :perl => Chef::Provider::Script, - :python => Chef::Provider::Script, - :ruby => Chef::Provider::Script, - :bash => Chef::Provider::Script, - :csh => Chef::Provider::Script, :user => Chef::Provider::User::Useradd, :group => Chef::Provider::Group::Gpasswd, - :http_request => Chef::Provider::HttpRequest, - :route => Chef::Provider::Route, :ifconfig => Chef::Provider::Ifconfig, - :ruby_block => Chef::Provider::RubyBlock, - :whyrun_safe_ruby_block => Chef::Provider::WhyrunSafeRubyBlock, - :erl_call => Chef::Provider::ErlCall, - :log => Chef::Provider::Log::ChefLog } } end diff --git a/lib/chef/platform/provider_priority_map.rb b/lib/chef/platform/provider_priority_map.rb new file mode 100644 index 0000000000..ccf6ef0bbe --- /dev/null +++ b/lib/chef/platform/provider_priority_map.rb @@ -0,0 +1,80 @@ + +require 'chef/providers' + +class Chef + class Platform + class ProviderPriorityMap + include Singleton + + def initialize + load_default_map + end + + def load_default_map + + # + # 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, [ + # on debian-ish system if an upstart script exists that always wins + Chef::Provider::Service::Upstart, + Chef::Provider::Service::Systemd, + 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" ] + + # + # Solaris-en + # + + priority :service, Chef::Provider::Service::Solaris, os: "solaris2" + + # + # Mac + # + + priority :service, Chef::Provider::Service::Macosx, os: "darwin" + end + + def priority_map + @priority_map ||= Chef::NodeMap.new + end + + def priority(*args) + priority_map.set(*args) + end + + end + end +end diff --git a/lib/chef/platform/service_helpers.rb b/lib/chef/platform/service_helpers.rb new file mode 100644 index 0000000000..440391843e --- /dev/null +++ b/lib/chef/platform/service_helpers.rb @@ -0,0 +1,113 @@ +# +# Author:: Lamont Granquist (<lamont@chef.io>) +# Copyright:: Copyright (c) 2014 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. +# + +# XXX: mixing shellout into a mixin into classes has to be code smell +require 'chef/mixin/shell_out' + +class Chef + class Platform + class ServiceHelpers + class << self + + include Chef::Mixin::ShellOut + + # This helper is mostly used to sort out the mess of different + # linux mechanisms that can be used to start services. It does + # not necessarily need to linux-specific, but currently all our + # other service providers are narrowly platform-specific with no + # alternatives. + def service_resource_providers + service_resource_providers = [] + + if ::File.exist?("/usr/sbin/update-rc.d") + service_resource_providers << :debian + end + + if ::File.exist?("/usr/sbin/invoke-rc.d") + service_resource_providers << :invokercd + end + + if ::File.exist?("/sbin/insserv") + service_resource_providers << :insserv + end + + # debian >= 6.0 has /etc/init but does not have upstart + if ::File.exist?("/etc/init") && ::File.exist?("/sbin/start") + service_resource_providers << :upstart + end + + if ::File.exist?("/sbin/chkconfig") + service_resource_providers << :redhat + end + + if ::File.exist?("/bin/systemctl") + # FIXME: look for systemd as init provider + service_resource_providers << :systemd + end + + service_resource_providers + end + + def config_for_service(service_name) + configs = [] + + if ::File.exist?("/etc/init.d/#{service_name}") + configs << :initd + end + + if ::File.exist?("/etc/init/#{service_name}.conf") + configs << :upstart + end + + if ::File.exist?("/etc/xinetd.d/#{service_name}") + configs << :xinetd + end + + if ::File.exist?("/etc/rc.d/#{service_name}") + configs << :etc_rcd + end + + if ::File.exist?("/usr/local/etc/rc.d/#{service_name}") + configs << :usr_local_etc_rcd + end + + if ::File.exist?("/bin/systemctl") && platform_has_systemd_unit?(service_name) + configs << :systemd + end + + configs + end + + private + + def extract_systemd_services(output) + # first line finds e.g. "sshd.service" + services = output.lines.split.map { |l| l.split[0] } + # this splits off the suffix after the last dot to return "sshd" + services += services.map { |s| s.sub(/(.*)\..*/, '\1') } + end + + def platform_has_systemd_unit?(service_name) + services = extract_systemd_services(shell_out!("systemctl --all").stdout) + + extract_systemd_services(shell_out!("systemctl --list-unit-files").stdout) + services.include?(service_name) + end + end + end + end +end diff --git a/lib/chef/provider.rb b/lib/chef/provider.rb index 7cfdf7cbe5..680fe9782f 100644 --- a/lib/chef/provider.rb +++ b/lib/chef/provider.rb @@ -22,60 +22,36 @@ require 'chef/mixin/convert_to_class_name' require 'chef/mixin/enforce_ownership_and_permissions' require 'chef/mixin/why_run' require 'chef/mixin/shell_out' +require 'chef/mixin/descendants_tracker' +require 'chef/platform/service_helpers' +require 'chef/node_map' class Chef class Provider include Chef::Mixin::WhyRun include Chef::Mixin::ShellOut - + extend Chef::Mixin::DescendantsTracker class << self - include Enumerable - - @@providers = [] - - attr_reader :implementations - attr_reader :supported_platforms - - def inherited(klass) - @@providers << klass + def node_map + @node_map ||= Chef::NodeMap.new end - def providers - @@providers + def provides(resource_name, opts={}, &block) + node_map.set(resource_name.to_sym, true, opts, &block) end - def each - providers.each { |provider| yield provider } - providers - end - - def implements(*resources) - options = resources.last.is_a?(Hash) ? resources.pop : {} - - @implementations = resources.map { |resource| resource.to_sym } - @supported_platforms = Array(options[:on_platforms] || :all) + # provides a node on the resource (early binding) + def provides?(node, resource) + node_map.get(node, resource.resource_name) end - def implements?(resource) - klass_name = resource.class.to_s.split('::').last - resource_name = klass_name.gsub(/([a-z0-9])([A-Z])/, '\1_\2').downcase - - implementations && implementations.include?(resource_name.to_sym) - end - - def supports_platform?(platform) - supported_platforms && ( - supported_platforms.include?(:all) || - supported_platforms.include?(platform.to_sym)) - end - - def enabled?(node) + # supports the given resource and action (late binding) + def supports?(resource, action) true end end - attr_accessor :new_resource attr_accessor :current_resource attr_accessor :run_context diff --git a/lib/chef/provider/breakpoint.rb b/lib/chef/provider/breakpoint.rb index 224e2758eb..663d558f66 100644 --- a/lib/chef/provider/breakpoint.rb +++ b/lib/chef/provider/breakpoint.rb @@ -20,6 +20,8 @@ class Chef class Provider class Breakpoint < Chef::Provider + provides :breakpoint + def load_current_resource end diff --git a/lib/chef/provider/cookbook_file.rb b/lib/chef/provider/cookbook_file.rb index 26d6ebf1d9..b501a9b41d 100644 --- a/lib/chef/provider/cookbook_file.rb +++ b/lib/chef/provider/cookbook_file.rb @@ -24,6 +24,8 @@ class Chef class Provider class CookbookFile < Chef::Provider::File + provides :cookbook_file + extend Chef::Deprecation::Warnings include Chef::Deprecation::Provider::CookbookFile add_deprecation_warnings_for(Chef::Deprecation::Provider::CookbookFile.instance_methods) diff --git a/lib/chef/provider/deploy/revision.rb b/lib/chef/provider/deploy/revision.rb index c98c1e5c75..62aa0e87f6 100644 --- a/lib/chef/provider/deploy/revision.rb +++ b/lib/chef/provider/deploy/revision.rb @@ -27,6 +27,8 @@ class Chef class Provider class Deploy class Revision < Chef::Provider::Deploy + provides :deploy_revision + provides :deploy_branch def all_releases sorted_releases diff --git a/lib/chef/provider/deploy/timestamped.rb b/lib/chef/provider/deploy/timestamped.rb index ce921161e0..ba3f6683f0 100644 --- a/lib/chef/provider/deploy/timestamped.rb +++ b/lib/chef/provider/deploy/timestamped.rb @@ -20,6 +20,8 @@ class Chef class Provider class Deploy class Timestamped < Chef::Provider::Deploy + provides :timestamped_deploy + provides :deploy protected diff --git a/lib/chef/provider/directory.rb b/lib/chef/provider/directory.rb index 067737b9d4..c9c3d466b9 100644 --- a/lib/chef/provider/directory.rb +++ b/lib/chef/provider/directory.rb @@ -27,6 +27,8 @@ class Chef class Provider class Directory < Chef::Provider::File + provides :directory + def whyrun_supported? true end diff --git a/lib/chef/provider/dsc_script.rb b/lib/chef/provider/dsc_script.rb index a70f4b5048..5db50e74b3 100644 --- a/lib/chef/provider/dsc_script.rb +++ b/lib/chef/provider/dsc_script.rb @@ -24,6 +24,9 @@ require 'chef/util/path_helper' class Chef class Provider class DscScript < Chef::Provider + + provides :dsc_script, os: "windows" + def initialize(dsc_resource, run_context) super(dsc_resource, run_context) @dsc_resource = dsc_resource diff --git a/lib/chef/provider/erl_call.rb b/lib/chef/provider/erl_call.rb index cdd494a243..f5855bcce6 100644 --- a/lib/chef/provider/erl_call.rb +++ b/lib/chef/provider/erl_call.rb @@ -25,6 +25,8 @@ class Chef class ErlCall < Chef::Provider include Chef::Mixin::Command + provides :erl_call + def initialize(node, new_resource) super(node, new_resource) end diff --git a/lib/chef/provider/execute.rb b/lib/chef/provider/execute.rb index 54632c0684..48b2a344d1 100644 --- a/lib/chef/provider/execute.rb +++ b/lib/chef/provider/execute.rb @@ -23,6 +23,8 @@ class Chef class Provider class Execute < Chef::Provider + provides :execute + def load_current_resource true end diff --git a/lib/chef/provider/file.rb b/lib/chef/provider/file.rb index 256248f240..a9390cc45c 100644 --- a/lib/chef/provider/file.rb +++ b/lib/chef/provider/file.rb @@ -54,6 +54,8 @@ class Chef include Chef::Deprecation::Provider::File add_deprecation_warnings_for(Chef::Deprecation::Provider::File.instance_methods) + provides :file + attr_reader :deployment_strategy attr_accessor :needs_creating diff --git a/lib/chef/provider/git.rb b/lib/chef/provider/git.rb index 2ef119e839..8418f22933 100644 --- a/lib/chef/provider/git.rb +++ b/lib/chef/provider/git.rb @@ -25,6 +25,8 @@ class Chef class Provider class Git < Chef::Provider + provides :git + def whyrun_supported? true end diff --git a/lib/chef/provider/group/pw.rb b/lib/chef/provider/group/pw.rb index 79a4160807..c39c20da67 100644 --- a/lib/chef/provider/group/pw.rb +++ b/lib/chef/provider/group/pw.rb @@ -21,9 +21,6 @@ class Chef class Group class Pw < Chef::Provider::Group - implements :group, - :on_platforms => :freebsd - def load_current_resource super end diff --git a/lib/chef/provider/http_request.rb b/lib/chef/provider/http_request.rb index ba54b10195..61aff434ed 100644 --- a/lib/chef/provider/http_request.rb +++ b/lib/chef/provider/http_request.rb @@ -23,6 +23,8 @@ class Chef class Provider class HttpRequest < Chef::Provider + provides :http_request + attr_accessor :http def whyrun_supported? diff --git a/lib/chef/provider/link.rb b/lib/chef/provider/link.rb index 639dc4f3ff..417d6a21b0 100644 --- a/lib/chef/provider/link.rb +++ b/lib/chef/provider/link.rb @@ -28,6 +28,8 @@ class Chef class Provider class Link < Chef::Provider + provides :link + include Chef::Mixin::EnforceOwnershipAndPermissions include Chef::Mixin::FileClass diff --git a/lib/chef/provider/log.rb b/lib/chef/provider/log.rb index 9379ceeefa..40eaf0aa28 100644 --- a/lib/chef/provider/log.rb +++ b/lib/chef/provider/log.rb @@ -25,6 +25,8 @@ class Chef # Chef log provider, allows logging to chef's logs from recipes class ChefLog < Chef::Provider + provides :log + def whyrun_supported? true end diff --git a/lib/chef/provider/package/aix.rb b/lib/chef/provider/package/aix.rb index da3e6d1684..88de4679ba 100644 --- a/lib/chef/provider/package/aix.rb +++ b/lib/chef/provider/package/aix.rb @@ -26,6 +26,8 @@ class Chef class Package class Aix < Chef::Provider::Package + provides :bff_package, os: "aix" + include Chef::Mixin::GetSourceFromPackage def define_resource_requirements diff --git a/lib/chef/provider/package/apt.rb b/lib/chef/provider/package/apt.rb index 0d91d0d1f0..eb2c038eaa 100644 --- a/lib/chef/provider/package/apt.rb +++ b/lib/chef/provider/package/apt.rb @@ -25,6 +25,8 @@ class Chef class Package class Apt < Chef::Provider::Package + provides :apt_package, os: "linux" + attr_accessor :is_virtual_package def load_current_resource diff --git a/lib/chef/provider/package/dpkg.rb b/lib/chef/provider/package/dpkg.rb index a1f1c797b1..3a9cecc660 100644 --- a/lib/chef/provider/package/dpkg.rb +++ b/lib/chef/provider/package/dpkg.rb @@ -30,6 +30,8 @@ class Chef DPKG_INSTALLED = /^Status: install ok installed/ DPKG_VERSION = /^Version: (.+)$/ + provides :dpkg_package, os: "linux" + include Chef::Mixin::GetSourceFromPackage def define_resource_requirements diff --git a/lib/chef/provider/package/easy_install.rb b/lib/chef/provider/package/easy_install.rb index 2af8a72e61..90727b738d 100644 --- a/lib/chef/provider/package/easy_install.rb +++ b/lib/chef/provider/package/easy_install.rb @@ -25,6 +25,8 @@ class Chef class Package class EasyInstall < Chef::Provider::Package + provides :easy_install_package + def install_check(name) check = false diff --git a/lib/chef/provider/package/freebsd.rb b/lib/chef/provider/package/freebsd.rb deleted file mode 100644 index f5ec0cec47..0000000000 --- a/lib/chef/provider/package/freebsd.rb +++ /dev/null @@ -1,154 +0,0 @@ -# -# Authors:: Bryan McLellan (btm@loftninjas.org) -# Matthew Landauer (matthew@openaustralia.org) -# Copyright:: Copyright (c) 2009 Bryan McLellan, Matthew Landauer -# 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/provider/package' -require 'chef/mixin/shell_out' -require 'chef/resource/package' -require 'chef/mixin/get_source_from_package' - -class Chef - class Provider - class Package - class Freebsd < Chef::Provider::Package - include Chef::Mixin::ShellOut - - include Chef::Mixin::GetSourceFromPackage - - implements :package, - :freebsd_package, - :on_platforms => :freebsd - - - def initialize(*args) - super - @current_resource = Chef::Resource::Package.new(@new_resource.name) - end - - def current_installed_version - pkg_info = shell_out!("pkg_info -E \"#{package_name}*\"", :env => nil, :returns => [0,1]) - pkg_info.stdout[/^#{Regexp.escape(package_name)}-(.+)/, 1] - end - - def port_path - case @new_resource.package_name - # When the package name starts with a '/' treat it as the full path to the ports directory - when /^\// - @new_resource.package_name - # Otherwise if the package name contains a '/' not at the start (like 'www/wordpress') treat as a relative - # path from /usr/ports - when /\// - "/usr/ports/#{@new_resource.package_name}" - # Otherwise look up the path to the ports directory using 'whereis' - else - whereis = shell_out!("whereis -s #{@new_resource.package_name}", :env => nil) - unless path = whereis.stdout[/^#{Regexp.escape(@new_resource.package_name)}:\s+(.+)$/, 1] - raise Chef::Exceptions::Package, "Could not find port with the name #{@new_resource.package_name}" - end - path - end - end - - def ports_makefile_variable_value(variable) - make_v = shell_out!("make -V #{variable}", :cwd => port_path, :env => nil, :returns => [0,1]) - make_v.stdout.strip.split($\).first # $\ is the line separator, i.e., newline - end - - def ports_candidate_version - ports_makefile_variable_value("PORTVERSION") - end - - def file_candidate_version_path - Dir["#{@new_resource.source}/#{@current_resource.package_name}*"][-1].to_s - end - - def file_candidate_version - file_candidate_version_path.split(/-/).last.split(/.tbz/).first - end - - def load_current_resource - @current_resource.package_name(@new_resource.package_name) - - @current_resource.version(current_installed_version) - Chef::Log.debug("#{@new_resource} current version is #{@current_resource.version}") if @current_resource.version - - case @new_resource.source - when /^http/, /^ftp/ - @candidate_version = "0.0.0" - when /^\// - @candidate_version = file_candidate_version - else - @candidate_version = ports_candidate_version - end - - Chef::Log.debug("#{@new_resource} ports candidate version is #{@candidate_version}") if @candidate_version - - @current_resource - end - - def latest_link_name - ports_makefile_variable_value("LATEST_LINK") - end - - # The name of the package (without the version number) as understood by pkg_add and pkg_info - def package_name - if ::File.exist?("/usr/ports/Makefile") - if ports_makefile_variable_value("PKGNAME") =~ /^(.+)-[^-]+$/ - $1 - else - raise Chef::Exceptions::Package, "Unexpected form for PKGNAME variable in #{port_path}/Makefile" - end - else - @new_resource.package_name - end - end - - def install_package(name, version) - unless @current_resource.version - case @new_resource.source - when /^ports$/ - shell_out!("make -DBATCH install", :timeout => 1200, :env => nil, :cwd => port_path).status - when /^http/, /^ftp/ - if @new_resource.source =~ /\/$/ - shell_out!("pkg_add -r #{package_name}", :env => { "PACKAGESITE" => @new_resource.source, 'LC_ALL' => nil }).status - else - shell_out!("pkg_add -r #{package_name}", :env => { "PACKAGEROOT" => @new_resource.source, 'LC_ALL' => nil }).status - end - Chef::Log.debug("#{@new_resource} installed from: #{@new_resource.source}") - when /^\// - shell_out!("pkg_add #{file_candidate_version_path}", :env => { "PKG_PATH" => @new_resource.source , 'LC_ALL'=>nil}).status - Chef::Log.debug("#{@new_resource} installed from: #{@new_resource.source}") - else - shell_out!("pkg_add -r #{latest_link_name}", :env => nil).status - end - end - end - - def remove_package(name, version) - # a version is mandatory - if version - shell_out!("pkg_delete #{package_name}-#{version}", :env => nil).status - else - shell_out!("pkg_delete #{package_name}-#{@current_resource.version}", :env => nil).status - end - end - - end - end - end -end diff --git a/lib/chef/provider/package/homebrew.rb b/lib/chef/provider/package/homebrew.rb index a9aeea1415..822f4c8a42 100644 --- a/lib/chef/provider/package/homebrew.rb +++ b/lib/chef/provider/package/homebrew.rb @@ -25,7 +25,11 @@ class Chef class Provider class Package class Homebrew < Chef::Provider::Package + + provides :homebrew_package, os: "mac_os_x" + include Chef::Mixin::HomebrewUser + def load_current_resource self.current_resource = Chef::Resource::Package.new(new_resource.name) current_resource.package_name(new_resource.package_name) diff --git a/lib/chef/provider/package/ips.rb b/lib/chef/provider/package/ips.rb index 4090507303..87022d770a 100644 --- a/lib/chef/provider/package/ips.rb +++ b/lib/chef/provider/package/ips.rb @@ -27,6 +27,8 @@ class Chef class Package class Ips < Chef::Provider::Package + provides :ips_package, os: "solaris2" + attr_accessor :virtual def define_resource_requirements diff --git a/lib/chef/provider/package/macports.rb b/lib/chef/provider/package/macports.rb index 05247e6d31..cd142eca42 100644 --- a/lib/chef/provider/package/macports.rb +++ b/lib/chef/provider/package/macports.rb @@ -2,6 +2,9 @@ class Chef class Provider class Package class Macports < Chef::Provider::Package + + provides :macports_package, os: "mac_os_x" + def load_current_resource @current_resource = Chef::Resource::Package.new(@new_resource.name) @current_resource.package_name(@new_resource.package_name) diff --git a/lib/chef/provider/package/pacman.rb b/lib/chef/provider/package/pacman.rb index a9ff0edf7f..85c0208d76 100644 --- a/lib/chef/provider/package/pacman.rb +++ b/lib/chef/provider/package/pacman.rb @@ -25,6 +25,8 @@ class Chef class Package class Pacman < Chef::Provider::Package + provides :pacman_package, os: "linux" + def load_current_resource @current_resource = Chef::Resource::Package.new(@new_resource.name) @current_resource.package_name(@new_resource.package_name) diff --git a/lib/chef/provider/package/paludis.rb b/lib/chef/provider/package/paludis.rb index f363b38e50..407e0d0110 100644 --- a/lib/chef/provider/package/paludis.rb +++ b/lib/chef/provider/package/paludis.rb @@ -24,6 +24,8 @@ class Chef class Package class Paludis < Chef::Provider::Package + provides :paludis_package, os: "linux" + def load_current_resource @current_resource = Chef::Resource::Package.new(@new_resource.package_name) @current_resource.package_name(@new_resource.package_name) @@ -45,7 +47,7 @@ class Chef @current_resource.version(res[2]) else @candidate_version = res[2] - @current_resource.version(nil) + @current_resource.version(nil) end end end diff --git a/lib/chef/provider/package/rpm.rb b/lib/chef/provider/package/rpm.rb index c0a6444252..131587e066 100644 --- a/lib/chef/provider/package/rpm.rb +++ b/lib/chef/provider/package/rpm.rb @@ -25,6 +25,8 @@ class Chef class Package class Rpm < Chef::Provider::Package + provides :rpm_package, os: [ "linux", "aix" ] + include Chef::Mixin::GetSourceFromPackage def define_resource_requirements diff --git a/lib/chef/provider/package/rubygems.rb b/lib/chef/provider/package/rubygems.rb index 6c7e1c066e..3c0ca40693 100644 --- a/lib/chef/provider/package/rubygems.rb +++ b/lib/chef/provider/package/rubygems.rb @@ -359,6 +359,9 @@ class Chef Chef::Log.logger end + provides :chef_gem + provides :gem_package + include Chef::Mixin::GetSourceFromPackage def initialize(new_resource, run_context=nil) diff --git a/lib/chef/provider/package/smartos.rb b/lib/chef/provider/package/smartos.rb index 19a6b9efef..7cef91953a 100644 --- a/lib/chef/provider/package/smartos.rb +++ b/lib/chef/provider/package/smartos.rb @@ -29,6 +29,8 @@ class Chef class SmartOS < Chef::Provider::Package attr_accessor :is_virtual_package + provides :smartos_package, os: "solaris2", platform_family: "smartos" + def load_current_resource Chef::Log.debug("#{@new_resource} loading current resource") @current_resource = Chef::Resource::Package.new(@new_resource.name) diff --git a/lib/chef/provider/package/solaris.rb b/lib/chef/provider/package/solaris.rb index 19f844b66a..53dd00dd07 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 :solaris_package, os: "solaris2" + # def initialize(*args) # super # @current_resource = Chef::Resource::Package.new(@new_resource.name) diff --git a/lib/chef/provider/package/yum.rb b/lib/chef/provider/package/yum.rb index e77319c254..505f5fd6a3 100644 --- a/lib/chef/provider/package/yum.rb +++ b/lib/chef/provider/package/yum.rb @@ -29,6 +29,8 @@ class Chef class Package class Yum < Chef::Provider::Package + provides :yum_package, os: "linux" + class RPMUtils class << self diff --git a/lib/chef/provider/remote_directory.rb b/lib/chef/provider/remote_directory.rb index 5bd1cb5493..9a7416e318 100644 --- a/lib/chef/provider/remote_directory.rb +++ b/lib/chef/provider/remote_directory.rb @@ -32,6 +32,8 @@ class Chef class Provider class RemoteDirectory < Chef::Provider::Directory + provides :remote_directory + include Chef::Mixin::FileClass def action_create @@ -63,7 +65,7 @@ class Chef def ls(path) files = Dir.glob(::File.join(Chef::Util::PathHelper.escape_glob(path), '**', '*'), ::File::FNM_DOTMATCH) - + # Remove current directory and previous directory files.reject! do |name| basename = Pathname.new(name).basename().to_s diff --git a/lib/chef/provider/route.rb b/lib/chef/provider/route.rb index 208a4f4139..72a5029a94 100644 --- a/lib/chef/provider/route.rb +++ b/lib/chef/provider/route.rb @@ -24,6 +24,8 @@ require 'ipaddr' class Chef::Provider::Route < Chef::Provider include Chef::Mixin::Command + provides :route + attr_accessor :is_running MASK = {'0.0.0.0' => '0', diff --git a/lib/chef/provider/ruby_block.rb b/lib/chef/provider/ruby_block.rb index b0d94a3f8d..eb93fd5708 100644 --- a/lib/chef/provider/ruby_block.rb +++ b/lib/chef/provider/ruby_block.rb @@ -20,6 +20,8 @@ class Chef class Provider class RubyBlock < Chef::Provider + provides :ruby_block + def whyrun_supported? true end diff --git a/lib/chef/provider/script.rb b/lib/chef/provider/script.rb index 4aacf4f524..1615517553 100644 --- a/lib/chef/provider/script.rb +++ b/lib/chef/provider/script.rb @@ -22,6 +22,12 @@ require 'chef/provider/execute' class Chef class Provider class Script < Chef::Provider::Execute + provides :bash + provides :csh + provides :perl + provides :python + provides :ruby + provides :script def initialize(new_resource, run_context) super diff --git a/lib/chef/provider/service/arch.rb b/lib/chef/provider/service/arch.rb index 9be5fb6fe3..888fb3fdf5 100644 --- a/lib/chef/provider/service/arch.rb +++ b/lib/chef/provider/service/arch.rb @@ -20,6 +20,12 @@ require 'chef/provider/service/init' class Chef::Provider::Service::Arch < Chef::Provider::Service::Init + provides :service, platform_family: "arch" + + def self.supports?(resource, action) + ::File.exist?("/etc/rc.d/#{resource.service_name}") + end + def initialize(new_resource, run_context) super @init_command = "/etc/rc.d/#{@new_resource.service_name}" diff --git a/lib/chef/provider/service/debian.rb b/lib/chef/provider/service/debian.rb index 1ebef90349..25b1960b26 100644 --- a/lib/chef/provider/service/debian.rb +++ b/lib/chef/provider/service/debian.rb @@ -25,13 +25,19 @@ class Chef UPDATE_RC_D_ENABLED_MATCHES = /\/rc[\dS].d\/S|not installed/i UPDATE_RC_D_PRIORITIES = /\/rc([\dS]).d\/([SK])(\d\d)/i + provides :service, platform_family: "debian" + + def self.supports?(resource, action) + Chef::Platform::ServiceHelpers.service_resource_providers.include?(:debian) + end + def load_current_resource super @priority_success = true @rcd_status = nil - @current_resource.priority(get_priority) - @current_resource.enabled(service_currently_enabled?(@current_resource.priority)) - @current_resource + current_resource.priority(get_priority) + current_resource.enabled(service_currently_enabled?(current_resource.priority)) + current_resource end def define_resource_requirements @@ -47,7 +53,7 @@ class Chef requirements.assert(:all_actions) do |a| a.assertion { @priority_success } - a.failure_message Chef::Exceptions::Service, "/usr/sbin/update-rc.d -n -f #{@current_resource.service_name} failed - #{@rcd_status.inspect}" + a.failure_message Chef::Exceptions::Service, "/usr/sbin/update-rc.d -n -f #{current_resource.service_name} failed - #{@rcd_status.inspect}" # This can happen if the service is not yet installed,so we'll fake it. a.whyrun ["Unable to determine priority of service, assuming service would have been correctly installed earlier in the run.", "Assigning temporary priorities to continue.", @@ -59,7 +65,7 @@ class Chef "3"=>[:start, "20"], "4"=>[:start, "20"], "5"=>[:start, "20"]} - @current_resource.priority(temp_priorities) + current_resource.priority(temp_priorities) end end end @@ -67,7 +73,7 @@ class Chef def get_priority priority = {} - @rcd_status = popen4("/usr/sbin/update-rc.d -n -f #{@current_resource.service_name} remove") do |pid, stdin, stdout, stderr| + @rcd_status = popen4("/usr/sbin/update-rc.d -n -f #{current_resource.service_name} remove") do |pid, stdin, stdout, stderr| [stdout, stderr].each do |iop| iop.each_line do |line| @@ -99,7 +105,7 @@ class Chef def service_currently_enabled?(priority) enabled = false priority.each { |runlevel, arguments| - Chef::Log.debug("#{@new_resource} runlevel #{runlevel}, action #{arguments[0]}, priority #{arguments[1]}") + Chef::Log.debug("#{new_resource} runlevel #{runlevel}, action #{arguments[0]}, priority #{arguments[1]}") # if we are in a update-rc.d default startup runlevel && we start in this runlevel if %w[ 1 2 3 4 5 S ].include?(runlevel) && arguments[0] == :start enabled = true @@ -111,63 +117,63 @@ class Chef # Override method from parent to ensure priority is up-to-date def action_enable - if @new_resource.priority.nil? + if new_resource.priority.nil? priority_ok = true else - priority_ok = @current_resource.priority == @new_resource.priority + priority_ok = @current_resource.priority == new_resource.priority end - if @current_resource.enabled and priority_ok - Chef::Log.debug("#{@new_resource} already enabled - nothing to do") + if current_resource.enabled and priority_ok + Chef::Log.debug("#{new_resource} already enabled - nothing to do") else - converge_by("enable service #{@new_resource}") do + converge_by("enable service #{new_resource}") do enable_service - Chef::Log.info("#{@new_resource} enabled") + Chef::Log.info("#{new_resource} enabled") end end load_new_resource_state - @new_resource.enabled(true) + new_resource.enabled(true) end def enable_service - if @new_resource.priority.is_a? Integer - shell_out!("/usr/sbin/update-rc.d -f #{@new_resource.service_name} remove") - shell_out!("/usr/sbin/update-rc.d #{@new_resource.service_name} defaults #{@new_resource.priority} #{100 - @new_resource.priority}") - elsif @new_resource.priority.is_a? Hash + if new_resource.priority.is_a? Integer + shell_out!("/usr/sbin/update-rc.d -f #{new_resource.service_name} remove") + shell_out!("/usr/sbin/update-rc.d #{new_resource.service_name} defaults #{new_resource.priority} #{100 - new_resource.priority}") + elsif new_resource.priority.is_a? Hash # we call the same command regardless of we're enabling or disabling # users passing a Hash are responsible for setting their own start priorities set_priority else # No priority, go with update-rc.d defaults - shell_out!("/usr/sbin/update-rc.d -f #{@new_resource.service_name} remove") - shell_out!("/usr/sbin/update-rc.d #{@new_resource.service_name} defaults") + shell_out!("/usr/sbin/update-rc.d -f #{new_resource.service_name} remove") + shell_out!("/usr/sbin/update-rc.d #{new_resource.service_name} defaults") end end def disable_service - if @new_resource.priority.is_a? Integer + if new_resource.priority.is_a? Integer # Stop processes in reverse order of start using '100 - start_priority' - shell_out!("/usr/sbin/update-rc.d -f #{@new_resource.service_name} remove") - shell_out!("/usr/sbin/update-rc.d -f #{@new_resource.service_name} stop #{100 - @new_resource.priority} 2 3 4 5 .") - elsif @new_resource.priority.is_a? Hash + shell_out!("/usr/sbin/update-rc.d -f #{new_resource.service_name} remove") + shell_out!("/usr/sbin/update-rc.d -f #{new_resource.service_name} stop #{100 - new_resource.priority} 2 3 4 5 .") + elsif new_resource.priority.is_a? Hash # we call the same command regardless of we're enabling or disabling # users passing a Hash are responsible for setting their own stop priorities set_priority else # no priority, using '100 - 20 (update-rc.d default)' to stop in reverse order of start - shell_out!("/usr/sbin/update-rc.d -f #{@new_resource.service_name} remove") - shell_out!("/usr/sbin/update-rc.d -f #{@new_resource.service_name} stop 80 2 3 4 5 .") + shell_out!("/usr/sbin/update-rc.d -f #{new_resource.service_name} remove") + shell_out!("/usr/sbin/update-rc.d -f #{new_resource.service_name} stop 80 2 3 4 5 .") end end def set_priority args = "" - @new_resource.priority.each do |level, o| + new_resource.priority.each do |level, o| action = o[0] priority = o[1] args += "#{action} #{priority} #{level} . " end - shell_out!("/usr/sbin/update-rc.d -f #{@new_resource.service_name} remove") - shell_out!("/usr/sbin/update-rc.d #{@new_resource.service_name} #{args}") + shell_out!("/usr/sbin/update-rc.d -f #{new_resource.service_name} remove") + shell_out!("/usr/sbin/update-rc.d #{new_resource.service_name} #{args}") end end end diff --git a/lib/chef/provider/service/freebsd.rb b/lib/chef/provider/service/freebsd.rb index e0d9696f61..e4a456ac25 100644 --- a/lib/chef/provider/service/freebsd.rb +++ b/lib/chef/provider/service/freebsd.rb @@ -27,8 +27,9 @@ class Chef attr_reader :enabled_state_found - implements :service, - :on_platforms => [:freebsd, :netbsd] + provides :service, os: [ "freebsd", "netbsd" ] + + include Chef::Mixin::ShellOut def initialize(new_resource, run_context) super diff --git a/lib/chef/provider/service/gentoo.rb b/lib/chef/provider/service/gentoo.rb index e2dff10994..3dab920f06 100644 --- a/lib/chef/provider/service/gentoo.rb +++ b/lib/chef/provider/service/gentoo.rb @@ -22,6 +22,9 @@ require 'chef/mixin/command' require 'chef/util/path_helper' class Chef::Provider::Service::Gentoo < Chef::Provider::Service::Init + + provides :service, platform_family: "gentoo" + def load_current_resource @new_resource.supports[:status] = true diff --git a/lib/chef/provider/service/init.rb b/lib/chef/provider/service/init.rb index 5d8bb5bb38..ab40a720f6 100644 --- a/lib/chef/provider/service/init.rb +++ b/lib/chef/provider/service/init.rb @@ -26,6 +26,8 @@ class Chef attr_accessor :init_command + provides :service, os: "!windows" + def initialize(new_resource, run_context) super @init_command = "/etc/init.d/#{@new_resource.service_name}" diff --git a/lib/chef/provider/service/insserv.rb b/lib/chef/provider/service/insserv.rb index 1ee817707a..df5a162a45 100644 --- a/lib/chef/provider/service/insserv.rb +++ b/lib/chef/provider/service/insserv.rb @@ -24,26 +24,32 @@ class Chef class Service class Insserv < Chef::Provider::Service::Init + provides :service, os: "linux" + + def self.supports?(resource, action) + Chef::Platform::ServiceHelpers.service_resource_providers.include?(:insserv) + end + def load_current_resource super - # Look for a /etc/rc.*/SnnSERVICE link to signifiy that the service would be started in a runlevel - if Dir.glob("/etc/rc**/S*#{Chef::Util::PathHelper.escape_glob(@current_resource.service_name)}").empty? - @current_resource.enabled false + # Look for a /etc/rc.*/SnnSERVICE link to signify that the service would be started in a runlevel + if Dir.glob("/etc/rc**/S*#{Chef::Util::PathHelper.escape_glob(current_resource.service_name)}").empty? + current_resource.enabled false else - @current_resource.enabled true + current_resource.enabled true end - @current_resource + current_resource end def enable_service() - shell_out!("/sbin/insserv -r -f #{@new_resource.service_name}") - shell_out!("/sbin/insserv -d -f #{@new_resource.service_name}") + shell_out!("/sbin/insserv -r -f #{new_resource.service_name}") + shell_out!("/sbin/insserv -d -f #{new_resource.service_name}") end def disable_service() - shell_out!("/sbin/insserv -r -f #{@new_resource.service_name}") + shell_out!("/sbin/insserv -r -f #{new_resource.service_name}") end end end diff --git a/lib/chef/provider/service/invokercd.rb b/lib/chef/provider/service/invokercd.rb index e6afa7272a..c7472211bc 100644 --- a/lib/chef/provider/service/invokercd.rb +++ b/lib/chef/provider/service/invokercd.rb @@ -23,6 +23,12 @@ class Chef class Service class Invokercd < Chef::Provider::Service::Init + provides :service, platform_family: "debian" + + def self.supports?(resource, action) + Chef::Platform::ServiceHelpers.service_resource_providers.include?(:invokerc) + end + def initialize(new_resource, run_context) super @init_command = "/usr/sbin/invoke-rc.d #{@new_resource.service_name}" diff --git a/lib/chef/provider/service/macosx.rb b/lib/chef/provider/service/macosx.rb index ad1535327b..10ad1aa29d 100644 --- a/lib/chef/provider/service/macosx.rb +++ b/lib/chef/provider/service/macosx.rb @@ -26,6 +26,8 @@ class Chef class Service class Macosx < Chef::Provider::Service::Simple + provides :service, os: "darwin" + def self.gather_plist_dirs locations = %w{/Library/LaunchAgents /Library/LaunchDaemons diff --git a/lib/chef/provider/service/redhat.rb b/lib/chef/provider/service/redhat.rb index 7a7b2a1c40..90744ae268 100644 --- a/lib/chef/provider/service/redhat.rb +++ b/lib/chef/provider/service/redhat.rb @@ -26,11 +26,17 @@ class Chef CHKCONFIG_ON = /\d:on/ CHKCONFIG_MISSING = /No such/ + provides :service, platform_family: [ "rhel", "fedora", "suse" ] + + def self.supports?(resource, action) + Chef::Platform::ServiceHelpers.service_resource_providers.include?(:redhat) + end + def initialize(new_resource, run_context) super - @init_command = "/sbin/service #{@new_resource.service_name}" - @new_resource.supports[:status] = true - @service_missing = false + @init_command = "/sbin/service #{@new_resource.service_name}" + @new_resource.supports[:status] = true + @service_missing = false end def define_resource_requirements diff --git a/lib/chef/provider/service/simple.rb b/lib/chef/provider/service/simple.rb index bd51d15f84..ee403ee163 100644 --- a/lib/chef/provider/service/simple.rb +++ b/lib/chef/provider/service/simple.rb @@ -25,6 +25,8 @@ class Chef class Service class Simple < Chef::Provider::Service + # this must be subclassed to be useful so does not directly implement :service + attr_reader :status_load_success def load_current_resource diff --git a/lib/chef/provider/service/solaris.rb b/lib/chef/provider/service/solaris.rb index f0584dcf6d..eaea6bb1ab 100644 --- a/lib/chef/provider/service/solaris.rb +++ b/lib/chef/provider/service/solaris.rb @@ -26,6 +26,8 @@ class Chef class Solaris < Chef::Provider::Service attr_reader :maintenance + provides :service, os: "solaris2" + def initialize(new_resource, run_context=nil) super @init_command = "/usr/sbin/svcadm" diff --git a/lib/chef/provider/service/systemd.rb b/lib/chef/provider/service/systemd.rb index 31feee65d4..311751ab9a 100644 --- a/lib/chef/provider/service/systemd.rb +++ b/lib/chef/provider/service/systemd.rb @@ -20,6 +20,13 @@ require 'chef/resource/service' require 'chef/provider/service/simple' class Chef::Provider::Service::Systemd < Chef::Provider::Service::Simple + + provides :service, os: "linux" + + def self.supports?(resource, action) + Chef::Platform::ServiceHelpers.service_resource_providers.include?(:systemd) + end + def load_current_resource @current_resource = Chef::Resource::Service.new(@new_resource.name) @current_resource.service_name(@new_resource.service_name) diff --git a/lib/chef/provider/service/upstart.rb b/lib/chef/provider/service/upstart.rb index 670bf9e5f8..41bd850d6a 100644 --- a/lib/chef/provider/service/upstart.rb +++ b/lib/chef/provider/service/upstart.rb @@ -27,6 +27,13 @@ class Chef class Upstart < Chef::Provider::Service::Simple UPSTART_STATE_FORMAT = /\w+ \(?(\w+)\)?[\/ ](\w+)/ + provides :service, os: "linux" + + def self.supports?(resource, action) + Chef::Platform::ServiceHelpers.service_resource_providers.include?(:upstart) && + Chef::Platform::ServiceHelpers.config_for_service(resource.service_name).include?(:upstart) + end + # Upstart does more than start or stop a service, creating multiple 'states' [1] that a service can be in. # In chef, when we ask a service to start, we expect it to have started before performing the next step # since we have top down dependencies. Which is to say we may follow witha resource next that requires diff --git a/lib/chef/provider/service/windows.rb b/lib/chef/provider/service/windows.rb index d31aad4c9d..4b1d2079ec 100644 --- a/lib/chef/provider/service/windows.rb +++ b/lib/chef/provider/service/windows.rb @@ -25,6 +25,10 @@ end class Chef::Provider::Service::Windows < Chef::Provider::Service + provides :service, os: "windows" + + include Chef::Mixin::ShellOut + #Win32::Service.get_start_type AUTO_START = 'auto start' MANUAL = 'demand start' diff --git a/lib/chef/provider/subversion.rb b/lib/chef/provider/subversion.rb index 6cf31c8ec8..f4a0e6fc13 100644 --- a/lib/chef/provider/subversion.rb +++ b/lib/chef/provider/subversion.rb @@ -27,6 +27,8 @@ class Chef class Provider class Subversion < Chef::Provider + provides :subversion + SVN_INFO_PATTERN = /^([\w\s]+): (.+)$/ include Chef::Mixin::Command diff --git a/lib/chef/provider/template.rb b/lib/chef/provider/template.rb index 48cc45f3a8..1e759074b9 100644 --- a/lib/chef/provider/template.rb +++ b/lib/chef/provider/template.rb @@ -25,6 +25,7 @@ require 'chef/deprecation/warnings' class Chef class Provider class Template < Chef::Provider::File + provides :template extend Chef::Deprecation::Warnings include Chef::Deprecation::Provider::Template diff --git a/lib/chef/provider/user/pw.rb b/lib/chef/provider/user/pw.rb index 0dc80103da..fe71e93561 100644 --- a/lib/chef/provider/user/pw.rb +++ b/lib/chef/provider/user/pw.rb @@ -23,9 +23,6 @@ class Chef class User class Pw < Chef::Provider::User - implements :user, - :on_platforms => :freebsd - def load_current_resource super raise Chef::Exceptions::User, "Could not find binary /usr/sbin/pw for #{@new_resource}" unless ::File.exists?("/usr/sbin/pw") diff --git a/lib/chef/provider/whyrun_safe_ruby_block.rb b/lib/chef/provider/whyrun_safe_ruby_block.rb index e5f35debd7..3b95752cc4 100644 --- a/lib/chef/provider/whyrun_safe_ruby_block.rb +++ b/lib/chef/provider/whyrun_safe_ruby_block.rb @@ -19,6 +19,8 @@ class Chef class Provider class WhyrunSafeRubyBlock < Chef::Provider::RubyBlock + provides :whyrun_safe_ruby_block + def action_run @new_resource.block.call @new_resource.updated_by_last_action(true) diff --git a/lib/chef/provider_resolver.rb b/lib/chef/provider_resolver.rb index 91b85e3aa0..c819b0c87f 100644 --- a/lib/chef/provider_resolver.rb +++ b/lib/chef/provider_resolver.rb @@ -16,42 +16,88 @@ # limitations under the License. # +require 'chef/exceptions' +require 'chef/platform/provider_priority_map' + class Chef class ProviderResolver attr_reader :node - attr_reader :providers def initialize(node) @node = node - @providers = [] - @loaded = false end - def load(reload = false) - return if loaded? && !reload + # return a deterministically sorted list of Chef::Provider subclasses + def providers + Chef::Provider.descendants.sort {|a,b| a.to_s <=> b.to_s } + end - @providers = [] if reload + def resolve(resource, action) + maybe_explicit_provider(resource) || + maybe_dynamic_provider_resolution(resource, action) || + maybe_chef_platform_lookup(resource) + end - Chef::Provider.each do |provider| - @providers << provider if provider.supports_platform?(@node[:platform]) - end + private - @loaded = true + # if resource.provider is set, just return one of those objects + def maybe_explicit_provider(resource) + return nil unless resource.provider + resource.provider end - def loaded? - !!@loaded - end + # try dynamically finding a provider based on querying the providers to see what they support + def maybe_dynamic_provider_resolution(resource, action) + # this cut only depends on the node value and is going to be static for all nodes + # will contain all providers that could possibly support a resource on a node + enabled_handlers = providers.select do |klass| + klass.provides?(node, resource) + end - def resolve(resource) - self.load if !loaded? + # 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}" - providers = @providers.find_all do |provider| - provider.enabled?(node) && provider.implements?(resource) + # ask all the enabled providers if they can actually support the resource + supported_handlers = enabled_handlers.select do |klass| + klass.supports?(resource, action) end - resource.evaluate_providers(providers) + # 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}" + + handlers = supported_handlers.empty? ? enabled_handlers : supported_handlers + + if handlers.count >= 2 + priority_list = [ get_provider_priority_map(resource.resource_name, node) ].flatten.compact + + handlers = handlers.sort_by { |x| i = priority_list.index x; i.nil? ? Float::INFINITY : i } + + handlers = [ handlers.first ] + end + + Chef::Log.debug "providers that survived replacement include: #{handlers}" + + raise Chef::Exceptions::AmbiguousProviderResolution.new(resource, handlers) if handlers.count >= 2 + + 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_provider_priority_map(resource_name, node) + provider_priority_map.get(node, resource_name) + end + + def provider_priority_map + Chef::Platform::ProviderPriorityMap.instance.priority_map end end end diff --git a/lib/chef/providers.rb b/lib/chef/providers.rb index bceb1f7106..c4f1ce769d 100644 --- a/lib/chef/providers.rb +++ b/lib/chef/providers.rb @@ -75,13 +75,13 @@ require 'chef/provider/package/smartos' require 'chef/provider/package/aix' require 'chef/provider/service/arch' -require 'chef/provider/service/debian' require 'chef/provider/service/freebsd' require 'chef/provider/service/gentoo' require 'chef/provider/service/init' -require 'chef/provider/service/insserv' require 'chef/provider/service/invokercd' +require 'chef/provider/service/debian' require 'chef/provider/service/redhat' +require 'chef/provider/service/insserv' require 'chef/provider/service/simple' require 'chef/provider/service/systemd' require 'chef/provider/service/upstart' diff --git a/lib/chef/resource.rb b/lib/chef/resource.rb index 5099446335..c38f36aa72 100644 --- a/lib/chef/resource.rb +++ b/lib/chef/resource.rb @@ -27,11 +27,12 @@ require 'chef/guard_interpreter/resource_guard_interpreter' require 'chef/resource/conditional' require 'chef/resource/conditional_action_not_nothing' require 'chef/resource_collection' -require 'chef/resource_platform_map' +require 'chef/node_map' require 'chef/node' require 'chef/platform' require 'chef/mixin/deprecation' +require 'chef/mixin/descendants_tracker' class Chef class Resource @@ -133,6 +134,7 @@ F include Chef::Mixin::Deprecation extend Chef::Mixin::ConvertToClassName + extend Chef::Mixin::DescendantsTracker if Module.method(:const_defined?).arity == 1 def self.strict_const_defined?(const) @@ -144,24 +146,13 @@ F end end - # Track all subclasses of Resource. This is used so names can be looked up - # when attempting to deserialize from JSON. (See: json_compat) - def self.resource_classes - # Using a class variable here ensures we have one variable to track - # subclasses shared by the entire class hierarchy; without this, each - # subclass would have its own list of subclasses. - @@resource_classes ||= [] - end - - # Callback when subclass is defined. Adds subclass to list of subclasses. - def self.inherited(subclass) - resource_classes << subclass - end - - # Look up a subclass by +class_name+ which should be a string that matches - # `Subclass.name` - def self.find_subclass_by_name(class_name) - resource_classes.first {|c| c.name == class_name } + 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 # Set or return the list of "state attributes" implemented by the Resource @@ -687,21 +678,10 @@ F raise ArgumentError, "nil is not a valid action for resource #{self}" if action.nil? end - def evaluate_providers(providers) - provider = providers.first - end - def provider_for_action(action) - # leverage new platform => short_name => resource - # which requires explicitly setting provider in - # resource class - if self.provider - provider = self.provider.new(self, self.run_context) - provider.action = action - provider - else # fall back to old provider resolution - Chef::Platform.provider_for_resource(self, action) - end + provider = run_context.provider_resolver.resolve(self, action).new(self, run_context) + provider.action = action + provider end def custom_exception_message(e) @@ -776,8 +756,8 @@ F @provider_base ||= Chef::Provider end - def self.platform_map - @@platform_map ||= PlatformMap.new + def self.node_map + @@node_map ||= NodeMap.new end # Maps a short_name (and optionally a platform and version) to a @@ -786,66 +766,55 @@ F # (I'm looking at you Chef::Resource::Package) # Ex: # class WindowsFile < Chef::Resource - # provides :file, :on_platforms => ["windows"] + # provides :file, os: "linux", platform_family: "rhel", platform: "redhat" + # provides :file, os: "!windows + # provides :file, os: [ "linux", "aix" ] + # provides :file, os: "solaris2" do |node| + # node['platform_version'].to_f <= 5.11 + # end # # ...other stuff # end # - # TODO: 2011-11-02 schisamo - platform_version support - def self.provides(short_name, opts={}) + def self.provides(short_name, opts={}, &block) short_name_sym = short_name if short_name.kind_of?(String) + # YAGNI: this is probably completely unnecessary and can be removed? + Chef::Log.warn "[DEPRECATION] Passing a String to Chef::Resource#provides will be removed" short_name.downcase! short_name.gsub!(/\s/, "_") short_name_sym = short_name.to_sym end - if opts.has_key?(:on_platforms) - platforms = [opts[:on_platforms]].flatten - platforms.each do |p| - p = :default if :all == p.to_sym - platform_map.set( - :platform => p.to_sym, - :short_name => short_name_sym, - :resource => self - ) - end - else - platform_map.set( - :short_name => short_name_sym, - :resource => self - ) - end + node_map.set(short_name_sym, constantize(self.name), opts, &block) end - # Returns a resource based on a short_name anda platform and version. - # + # Returns a resource based on a short_name and node # # ==== Parameters # short_name<Symbol>:: short_name of the resource (ie :directory) - # platform<Symbol,String>:: platform name - # version<String>:: platform version + # node<Chef::Node>:: Node object to look up platform and version in # # === Returns # <Chef::Resource>:: returns the proper Chef::Resource class - def self.resource_for_platform(short_name, platform=nil, version=nil) - platform_map.get(short_name, platform, version) + def self.resource_for_node(short_name, node) + klass = node_map.get(node, short_name) || + resource_matching_short_name(short_name) + raise Chef::Exceptions::NoSuchResourceType.new(short_name, node) if klass.nil? + klass end - # Returns a resource based on a short_name and a node's - # platform and version. - # + # Returns the class of a Chef::Resource based on the short name # ==== Parameters # short_name<Symbol>:: short_name of the resource (ie :directory) - # node<Chef::Node>:: Node object to look up platform and version in # # === Returns # <Chef::Resource>:: returns the proper Chef::Resource class - def self.resource_for_node(short_name, node) + def self.resource_matching_short_name(short_name) begin - platform, version = Chef::Platform.find_platform_and_version(node) - rescue ArgumentError + rname = convert_to_class_name(short_name.to_s) + Chef::Resource.const_get(rname) + rescue NameError + nil end - resource = resource_for_platform(short_name, platform, version) - resource end private diff --git a/lib/chef/resource/apt_package.rb b/lib/chef/resource/apt_package.rb index 050cf838ae..f944825ac3 100644 --- a/lib/chef/resource/apt_package.rb +++ b/lib/chef/resource/apt_package.rb @@ -23,10 +23,12 @@ class Chef class Resource class AptPackage < Chef::Resource::Package + provides :apt_package + provides :package, os: "linux", platform_family: [ "debian" ] + def initialize(name, run_context=nil) super @resource_name = :apt_package - @provider = Chef::Provider::Package::Apt @default_release = nil end diff --git a/lib/chef/resource/bash.rb b/lib/chef/resource/bash.rb index c56de5fe20..0add0ce501 100644 --- a/lib/chef/resource/bash.rb +++ b/lib/chef/resource/bash.rb @@ -17,6 +17,7 @@ # require 'chef/resource/script' +require 'chef/provider/script' class Chef class Resource diff --git a/lib/chef/resource/bff_package.rb b/lib/chef/resource/bff_package.rb index 2d78483e4b..917f0d1d50 100644 --- a/lib/chef/resource/bff_package.rb +++ b/lib/chef/resource/bff_package.rb @@ -26,7 +26,6 @@ class Chef def initialize(name, run_context=nil) super @resource_name = :bff_package - @provider = Chef::Provider::Package::Aix end end diff --git a/lib/chef/resource/breakpoint.rb b/lib/chef/resource/breakpoint.rb index 83c397bd5b..b2210262d2 100644 --- a/lib/chef/resource/breakpoint.rb +++ b/lib/chef/resource/breakpoint.rb @@ -28,7 +28,7 @@ class Chef super(@name, *args) @action = "break" @allowed_actions << :break - @provider = Chef::Provider::Breakpoint + @resource_name = :breakpoint end end end diff --git a/lib/chef/resource/chef_gem.rb b/lib/chef/resource/chef_gem.rb index af45cb93e6..b4ead11f1d 100644 --- a/lib/chef/resource/chef_gem.rb +++ b/lib/chef/resource/chef_gem.rb @@ -23,13 +23,12 @@ class Chef class Resource class ChefGem < Chef::Resource::Package::GemPackage - provides :chef_gem, :on_platforms => :all + provides :chef_gem def initialize(name, run_context=nil) super @resource_name = :chef_gem @gem_binary = RbConfig::CONFIG['bindir'] + "/gem" - @provider = Chef::Provider::Package::Rubygems end # The chef_gem resources is for installing gems to the current gem environment only for use by Chef cookbooks. diff --git a/lib/chef/resource/cookbook_file.rb b/lib/chef/resource/cookbook_file.rb index 2709cf64f4..7be353b648 100644 --- a/lib/chef/resource/cookbook_file.rb +++ b/lib/chef/resource/cookbook_file.rb @@ -27,7 +27,7 @@ class Chef class CookbookFile < Chef::Resource::File include Chef::Mixin::Securable - provides :cookbook_file, :on_platforms => :all + provides :cookbook_file def initialize(name, run_context=nil) super @@ -36,7 +36,6 @@ class Chef @action = "create" @source = ::File.basename(name) @cookbook = nil - @provider = Chef::Provider::CookbookFile end def source(source_filename=nil) diff --git a/lib/chef/resource/csh.rb b/lib/chef/resource/csh.rb index 95aa8afd7a..36659c349b 100644 --- a/lib/chef/resource/csh.rb +++ b/lib/chef/resource/csh.rb @@ -17,6 +17,7 @@ # require 'chef/resource/script' +require 'chef/provider/script' class Chef class Resource diff --git a/lib/chef/resource/deploy.rb b/lib/chef/resource/deploy.rb index a08e8aeeea..4252aa230f 100644 --- a/lib/chef/resource/deploy.rb +++ b/lib/chef/resource/deploy.rb @@ -77,7 +77,6 @@ class Chef @shallow_clone = false @scm_provider = Chef::Provider::Git @svn_force_export = false - @provider = Chef::Provider::Deploy::Timestamped @allowed_actions.push(:force_deploy, :deploy, :rollback) @additional_remotes = Hash[] @keep_releases = 5 diff --git a/lib/chef/resource/deploy_revision.rb b/lib/chef/resource/deploy_revision.rb index ceac26e91a..e144ce2162 100644 --- a/lib/chef/resource/deploy_revision.rb +++ b/lib/chef/resource/deploy_revision.rb @@ -22,14 +22,19 @@ class Chef # Convenience class for using the deploy resource with the revision # deployment strategy (provider) class DeployRevision < Chef::Resource::Deploy + + provides :deploy_revision + def initialize(*args, &block) super @resource_name = :deploy_revision - @provider = Chef::Provider::Deploy::Revision end end class DeployBranch < Chef::Resource::DeployRevision + + provides :deploy_branch + def initialize(*args, &block) super @resource_name = :deploy_branch diff --git a/lib/chef/resource/directory.rb b/lib/chef/resource/directory.rb index 423c0bbe27..1ab7f0d16d 100644 --- a/lib/chef/resource/directory.rb +++ b/lib/chef/resource/directory.rb @@ -32,7 +32,7 @@ class Chef include Chef::Mixin::Securable - provides :directory, :on_platforms => :all + provides :directory def initialize(name, run_context=nil) super @@ -41,7 +41,6 @@ class Chef @action = :create @recursive = false @allowed_actions.push(:create, :delete) - @provider = Chef::Provider::Directory end def recursive(arg=nil) diff --git a/lib/chef/resource/dpkg_package.rb b/lib/chef/resource/dpkg_package.rb index 2fb5b5c249..35a47e8a82 100644 --- a/lib/chef/resource/dpkg_package.rb +++ b/lib/chef/resource/dpkg_package.rb @@ -23,10 +23,11 @@ class Chef class Resource class DpkgPackage < Chef::Resource::Package + provides :dpkg_package, os: "linux" + def initialize(name, run_context=nil) super @resource_name = :dpkg_package - @provider = Chef::Provider::Package::Dpkg end end diff --git a/lib/chef/resource/dsc_script.rb b/lib/chef/resource/dsc_script.rb index 76ac6659d6..82907c25db 100644 --- a/lib/chef/resource/dsc_script.rb +++ b/lib/chef/resource/dsc_script.rb @@ -22,13 +22,12 @@ class Chef class Resource class DscScript < Chef::Resource - provides :dsc_script, :on_platforms => ["windows"] + provides :dsc_script, platform: "windows" def initialize(name, run_context=nil) super @allowed_actions.push(:run) @action = :run - @provider = Chef::Provider::DscScript @resource_name = :dsc_script end diff --git a/lib/chef/resource/easy_install_package.rb b/lib/chef/resource/easy_install_package.rb index f25e1ac22f..5286e9a289 100644 --- a/lib/chef/resource/easy_install_package.rb +++ b/lib/chef/resource/easy_install_package.rb @@ -22,10 +22,11 @@ class Chef class Resource class EasyInstallPackage < Chef::Resource::Package + provides :easy_install_package + def initialize(name, run_context=nil) super @resource_name = :easy_install_package - @provider = Chef::Provider::Package::EasyInstall end def easy_install_binary(arg=nil) diff --git a/lib/chef/resource/erl_call.rb b/lib/chef/resource/erl_call.rb index 959856af66..24009d51c7 100644 --- a/lib/chef/resource/erl_call.rb +++ b/lib/chef/resource/erl_call.rb @@ -18,6 +18,7 @@ # require 'chef/resource' +require 'chef/provider/erl_call' class Chef class Resource diff --git a/lib/chef/resource/execute.rb b/lib/chef/resource/execute.rb index ae118b1c9e..980035b079 100644 --- a/lib/chef/resource/execute.rb +++ b/lib/chef/resource/execute.rb @@ -18,6 +18,7 @@ # require 'chef/resource' +require 'chef/provider/execute' class Chef class Resource diff --git a/lib/chef/resource/file.rb b/lib/chef/resource/file.rb index 3c1f4785ef..16491f9bc8 100644 --- a/lib/chef/resource/file.rb +++ b/lib/chef/resource/file.rb @@ -19,7 +19,6 @@ require 'chef/resource' require 'chef/platform/query_helpers' -require 'chef/provider/file' require 'chef/mixin/securable' class Chef @@ -38,7 +37,7 @@ class Chef attr_writer :checksum - provides :file, :on_platforms => :all + provides :file def initialize(name, run_context=nil) super @@ -47,7 +46,6 @@ class Chef @backup = 5 @action = "create" @allowed_actions.push(:create, :delete, :touch, :create_if_missing) - @provider = Chef::Provider::File @atomic_update = Chef::Config[:file_atomic_update] @force_unlink = false @manage_symlink_source = nil diff --git a/lib/chef/resource/freebsd_package.rb b/lib/chef/resource/freebsd_package.rb index 957c25caf1..9c8db506f8 100644 --- a/lib/chef/resource/freebsd_package.rb +++ b/lib/chef/resource/freebsd_package.rb @@ -29,7 +29,7 @@ class Chef class FreebsdPackage < Chef::Resource::Package include Chef::Mixin::ShellOut - provides :package, :on_platforms => ["freebsd"] + provides :package, platform: "freebsd" def initialize(name, run_context=nil) super diff --git a/lib/chef/resource/gem_package.rb b/lib/chef/resource/gem_package.rb index 6def7b6653..631aa13f56 100644 --- a/lib/chef/resource/gem_package.rb +++ b/lib/chef/resource/gem_package.rb @@ -22,10 +22,11 @@ class Chef class Resource class GemPackage < Chef::Resource::Package + provides :gem_package + def initialize(name, run_context=nil) super @resource_name = :gem_package - @provider = Chef::Provider::Package::Rubygems end def source(arg=nil) diff --git a/lib/chef/resource/git.rb b/lib/chef/resource/git.rb index 774bb24f24..7156873315 100644 --- a/lib/chef/resource/git.rb +++ b/lib/chef/resource/git.rb @@ -22,10 +22,11 @@ class Chef class Resource class Git < Chef::Resource::Scm + provides :git + def initialize(name, run_context=nil) super @resource_name = :git - @provider = Chef::Provider::Git @additional_remotes = Hash[] end diff --git a/lib/chef/resource/homebrew_package.rb b/lib/chef/resource/homebrew_package.rb index e1d50c1739..952552e3a8 100644 --- a/lib/chef/resource/homebrew_package.rb +++ b/lib/chef/resource/homebrew_package.rb @@ -25,10 +25,11 @@ class Chef class Resource class HomebrewPackage < Chef::Resource::Package + provides :homebrew_package, os: "mac_os_x" + def initialize(name, run_context=nil) super @resource_name = :homebrew_package - @provider = Chef::Provider::Package::Homebrew @homebrew_user = nil end diff --git a/lib/chef/resource/http_request.rb b/lib/chef/resource/http_request.rb index 47f6286fb4..ccb0a26629 100644 --- a/lib/chef/resource/http_request.rb +++ b/lib/chef/resource/http_request.rb @@ -18,6 +18,7 @@ # require 'chef/resource' +require 'chef/provider/http_request' class Chef class Resource diff --git a/lib/chef/resource/ips_package.rb b/lib/chef/resource/ips_package.rb index 88c6e9a538..77b3387946 100644 --- a/lib/chef/resource/ips_package.rb +++ b/lib/chef/resource/ips_package.rb @@ -22,10 +22,12 @@ require 'chef/provider/package/ips' class Chef class Resource class IpsPackage < ::Chef::Resource::Package + + provides :ips_package, os: "solaris2" + def initialize(name, run_context = nil) super(name, run_context) @resource_name = :ips_package - @provider = Chef::Provider::Package::Ips @allowed_actions = [ :install, :remove, :upgrade ] @accept_license = false end diff --git a/lib/chef/resource/link.rb b/lib/chef/resource/link.rb index e53b386a74..8726eded1d 100644 --- a/lib/chef/resource/link.rb +++ b/lib/chef/resource/link.rb @@ -25,7 +25,7 @@ class Chef class Link < Chef::Resource include Chef::Mixin::Securable - provides :link, :on_platform => :all + provides :link identity_attr :target_file @@ -40,7 +40,6 @@ class Chef @link_type = :symbolic @target_file = name @allowed_actions.push(:create, :delete) - @provider = Chef::Provider::Link end def to(arg=nil) diff --git a/lib/chef/resource/log.rb b/lib/chef/resource/log.rb index 391c3b5393..7f970a87a4 100644 --- a/lib/chef/resource/log.rb +++ b/lib/chef/resource/log.rb @@ -16,6 +16,10 @@ # See the License for the specific language governing permissions and # limitations under the License. # + +require 'chef/resource' +require 'chef/provider/log' + class Chef class Resource class Log < Chef::Resource diff --git a/lib/chef/resource/macports_package.rb b/lib/chef/resource/macports_package.rb index c9434c9e69..bdc8698155 100644 --- a/lib/chef/resource/macports_package.rb +++ b/lib/chef/resource/macports_package.rb @@ -19,10 +19,12 @@ class Chef class Resource class MacportsPackage < Chef::Resource::Package + + provides :macports_package, os: "mac_os_x" + def initialize(name, run_context=nil) super @resource_name = :macports_package - @provider = Chef::Provider::Package::Macports end end end diff --git a/lib/chef/resource/pacman_package.rb b/lib/chef/resource/pacman_package.rb index 2894e415ac..4c45dd004f 100644 --- a/lib/chef/resource/pacman_package.rb +++ b/lib/chef/resource/pacman_package.rb @@ -22,10 +22,11 @@ class Chef class Resource class PacmanPackage < Chef::Resource::Package + provides :pacman_package, os: "linux" + def initialize(name, run_context=nil) super @resource_name = :pacman_package - @provider = Chef::Provider::Package::Pacman end end diff --git a/lib/chef/resource/paludis_package.rb b/lib/chef/resource/paludis_package.rb index fde25e69b3..7eddf8690b 100644 --- a/lib/chef/resource/paludis_package.rb +++ b/lib/chef/resource/paludis_package.rb @@ -22,10 +22,12 @@ require 'chef/provider/package/paludis' class Chef class Resource class PaludisPackage < Chef::Resource::Package + + provides :paludis_package, os: "linux" + def initialize(name, run_context=nil) super(name, run_context) @resource_name = :paludis_package - @provider = Chef::Provider::Package::Paludis @allowed_actions = [ :install, :remove, :upgrade ] @timeout = 3600 end diff --git a/lib/chef/resource/perl.rb b/lib/chef/resource/perl.rb index 546f639e1f..c4bdb6e130 100644 --- a/lib/chef/resource/perl.rb +++ b/lib/chef/resource/perl.rb @@ -17,6 +17,7 @@ # require 'chef/resource/script' +require 'chef/provider/script' class Chef class Resource diff --git a/lib/chef/resource/python.rb b/lib/chef/resource/python.rb index f340afdb39..b1f23d13ce 100644 --- a/lib/chef/resource/python.rb +++ b/lib/chef/resource/python.rb @@ -1,4 +1,3 @@ -# # Author:: Adam Jacob (<adam@opscode.com>) # Copyright:: Copyright (c) 2008 Opscode, Inc. # License:: Apache License, Version 2.0 @@ -17,6 +16,7 @@ # require 'chef/resource/script' +require 'chef/provider/script' class Chef class Resource diff --git a/lib/chef/resource/remote_directory.rb b/lib/chef/resource/remote_directory.rb index 0f7e0eb5de..d4108da47a 100644 --- a/lib/chef/resource/remote_directory.rb +++ b/lib/chef/resource/remote_directory.rb @@ -26,7 +26,7 @@ class Chef class RemoteDirectory < Chef::Resource::Directory include Chef::Mixin::Securable - provides :remote_directory, :on_platforms => :all + provides :remote_directory identity_attr :path @@ -48,7 +48,6 @@ class Chef @overwrite = true @allowed_actions.push(:create, :create_if_missing, :delete) @cookbook = nil - @provider = Chef::Provider::RemoteDirectory end if Chef::Platform.windows? diff --git a/lib/chef/resource/remote_file.rb b/lib/chef/resource/remote_file.rb index 6334b1bf44..46516fd3fb 100644 --- a/lib/chef/resource/remote_file.rb +++ b/lib/chef/resource/remote_file.rb @@ -26,7 +26,7 @@ class Chef class RemoteFile < Chef::Resource::File include Chef::Mixin::Securable - provides :remote_file, :on_platforms => :all + provides :remote_file def initialize(name, run_context=nil) super diff --git a/lib/chef/resource/rpm_package.rb b/lib/chef/resource/rpm_package.rb index 200a9633ce..8634f1e25d 100644 --- a/lib/chef/resource/rpm_package.rb +++ b/lib/chef/resource/rpm_package.rb @@ -23,10 +23,11 @@ class Chef class Resource class RpmPackage < Chef::Resource::Package + provides :rpm_package, os: [ "linux", "aix" ] + def initialize(name, run_context=nil) super @resource_name = :rpm_package - @provider = Chef::Provider::Package::Rpm end end diff --git a/lib/chef/resource/ruby.rb b/lib/chef/resource/ruby.rb index 605d27b00d..2b2aa0249d 100644 --- a/lib/chef/resource/ruby.rb +++ b/lib/chef/resource/ruby.rb @@ -17,6 +17,7 @@ # require 'chef/resource/script' +require 'chef/provider/script' class Chef class Resource diff --git a/lib/chef/resource/ruby_block.rb b/lib/chef/resource/ruby_block.rb index d9b8954a90..a9cbf234cf 100644 --- a/lib/chef/resource/ruby_block.rb +++ b/lib/chef/resource/ruby_block.rb @@ -17,6 +17,9 @@ # limitations under the License. # +require 'chef/resource' +require 'chef/provider/ruby_block' + class Chef class Resource class RubyBlock < Chef::Resource diff --git a/lib/chef/resource/script.rb b/lib/chef/resource/script.rb index ff7d23f075..479295922c 100644 --- a/lib/chef/resource/script.rb +++ b/lib/chef/resource/script.rb @@ -18,6 +18,7 @@ # require 'chef/resource/execute' +require 'chef/provider/script' class Chef class Resource diff --git a/lib/chef/resource/service.rb b/lib/chef/resource/service.rb index 4d64c3e3f4..36df7c859a 100644 --- a/lib/chef/resource/service.rb +++ b/lib/chef/resource/service.rb @@ -46,10 +46,6 @@ class Chef @action = "nothing" @supports = { :restart => false, :reload => false, :status => false } @allowed_actions.push(:enable, :disable, :start, :stop, :restart, :reload) - - if(run_context && run_context.node[:init_package] == "systemd") - @provider = Chef::Provider::Service::Systemd - end end def service_name(arg=nil) diff --git a/lib/chef/resource/smartos_package.rb b/lib/chef/resource/smartos_package.rb index 0f4f6d8b0a..99b3b953b0 100644 --- a/lib/chef/resource/smartos_package.rb +++ b/lib/chef/resource/smartos_package.rb @@ -23,16 +23,15 @@ class Chef class Resource class SmartosPackage < Chef::Resource::Package + provides :smartos_package + provides :package, os: "solaris2", platform_family: "smartos" + def initialize(name, run_context=nil) super @resource_name = :smartos_package - @provider = Chef::Provider::Package::SmartOS end end end end -# Backwards compatability -# @todo remove in Chef 12 -Chef::Resource::SmartOSPackage = Chef::Resource::SmartosPackage diff --git a/lib/chef/resource/solaris_package.rb b/lib/chef/resource/solaris_package.rb index 3513703076..94be4693b6 100644 --- a/lib/chef/resource/solaris_package.rb +++ b/lib/chef/resource/solaris_package.rb @@ -24,10 +24,16 @@ class Chef class Resource class SolarisPackage < Chef::Resource::Package + provides :solaris_package + provides :package, os: "solaris2", platform_family: "nexentacore" + provides :package, os: "solaris2", platform_family: "solaris2" do |node| + # on >= Solaris 11 we default to IPS packages instead + node[:platform_version].to_f <= 5.10 + end + def initialize(name, run_context=nil) super @resource_name = :solaris_package - @provider = Chef::Provider::Package::Solaris end end diff --git a/lib/chef/resource/subversion.rb b/lib/chef/resource/subversion.rb index 44158cb080..3afbe0baaf 100644 --- a/lib/chef/resource/subversion.rb +++ b/lib/chef/resource/subversion.rb @@ -28,7 +28,6 @@ class Chef @svn_arguments = '--no-auth-cache' @svn_info_args = '--no-auth-cache' @resource_name = :subversion - @provider = Chef::Provider::Subversion allowed_actions << :force_export end diff --git a/lib/chef/resource/template.rb b/lib/chef/resource/template.rb index 8473f5b677..8c9607ee07 100644 --- a/lib/chef/resource/template.rb +++ b/lib/chef/resource/template.rb @@ -27,7 +27,7 @@ class Chef class Template < Chef::Resource::File include Chef::Mixin::Securable - provides :template, :on_platforms => :all + provides :template attr_reader :inline_helper_blocks attr_reader :inline_helper_modules @@ -40,7 +40,6 @@ class Chef @cookbook = nil @local = false @variables = Hash.new - @provider = Chef::Provider::Template @inline_helper_blocks = {} @inline_helper_modules = [] @helper_modules = [] diff --git a/lib/chef/resource/timestamped_deploy.rb b/lib/chef/resource/timestamped_deploy.rb index 4032ae9854..b2109db85c 100644 --- a/lib/chef/resource/timestamped_deploy.rb +++ b/lib/chef/resource/timestamped_deploy.rb @@ -18,13 +18,12 @@ class Chef class Resource - # Convenience class for using the deploy resource with the timestamped # deployment strategy (provider) class TimestampedDeploy < Chef::Resource::Deploy + provides :timestamped_deploy def initialize(*args, &block) super(*args, &block) - @provider = Chef::Provider::Deploy::Timestamped end end end diff --git a/lib/chef/resource/whyrun_safe_ruby_block.rb b/lib/chef/resource/whyrun_safe_ruby_block.rb index ddb9d91dc3..6fa5383f5d 100644 --- a/lib/chef/resource/whyrun_safe_ruby_block.rb +++ b/lib/chef/resource/whyrun_safe_ruby_block.rb @@ -23,7 +23,6 @@ class Chef def initialize(name, run_context=nil) super @resource_name = :whyrun_safe_ruby_block - @provider = Chef::Provider::WhyrunSafeRubyBlock end end diff --git a/lib/chef/resource/windows_package.rb b/lib/chef/resource/windows_package.rb index 8bd41e0cb7..c563ba5fdc 100644 --- a/lib/chef/resource/windows_package.rb +++ b/lib/chef/resource/windows_package.rb @@ -24,12 +24,11 @@ class Chef class Resource class WindowsPackage < Chef::Resource::Package - provides :package, :on_platforms => ["windows"] + provides :package, platform: "windows" def initialize(name, run_context=nil) super @allowed_actions = [ :install, :remove ] - @provider = Chef::Provider::Package::Windows @resource_name = :windows_package @source ||= source(@package_name) @@ -76,4 +75,3 @@ class Chef end end end - diff --git a/lib/chef/resource/windows_service.rb b/lib/chef/resource/windows_service.rb index 5ed8e76cbd..49495117ee 100644 --- a/lib/chef/resource/windows_service.rb +++ b/lib/chef/resource/windows_service.rb @@ -25,7 +25,7 @@ class Chef # Until #1773 is resolved, you need to manually specify the windows_service resource # to use action :configure_startup and attribute startup_type - # provides :service, :on_platforms => ["windows"] + provides :service, platform: "windows" identity_attr :service_name @@ -34,7 +34,6 @@ class Chef def initialize(name, run_context=nil) super @resource_name = :windows_service - @provider = Chef::Provider::Service::Windows @allowed_actions.push(:configure_startup) @startup_type = :automatic end diff --git a/lib/chef/resource/yum_package.rb b/lib/chef/resource/yum_package.rb index dff70bcf62..8fbca9b097 100644 --- a/lib/chef/resource/yum_package.rb +++ b/lib/chef/resource/yum_package.rb @@ -23,10 +23,12 @@ class Chef class Resource class YumPackage < Chef::Resource::Package + provides :yum_package + provides :package, os: "linux", platform_family: [ "rhel", "fedora" ] + def initialize(name, run_context=nil) super @resource_name = :yum_package - @provider = Chef::Provider::Package::Yum @flush_cache = { :before => false, :after => false } @allow_downgrade = false end diff --git a/lib/chef/resource_platform_map.rb b/lib/chef/resource_platform_map.rb deleted file mode 100644 index a678f5be4b..0000000000 --- a/lib/chef/resource_platform_map.rb +++ /dev/null @@ -1,151 +0,0 @@ -# -# Author:: Seth Chisamore (<schisamo@opscode.com>) -# Copyright:: Copyright (c) 2011 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/mixin/params_validate' -require 'chef/mixin/convert_to_class_name' - -class Chef - class Resource - class PlatformMap - - include Chef::Mixin::ParamsValidate - include Chef::Mixin::ConvertToClassName - - attr_reader :map - - def initialize(map={:default => {}}) - @map = map - end - - def filter(platform, version) - resource_map = map[:default].clone - platform_sym = platform - if platform.kind_of?(String) - platform.downcase! - platform.gsub!(/\s/, "_") - platform_sym = platform.to_sym - end - - if map.has_key?(platform_sym) - if map[platform_sym].has_key?(version) - if map[platform_sym].has_key?(:default) - resource_map.merge!(map[platform_sym][:default]) - end - resource_map.merge!(map[platform_sym][version]) - elsif map[platform_sym].has_key?(:default) - resource_map.merge!(map[platform_sym][:default]) - end - end - resource_map - end - - def set(args) - validate( - args, - { - :platform => { - :kind_of => Symbol, - :required => false - }, - :version => { - :kind_of => String, - :required => false - }, - :short_name => { - :kind_of => Symbol, - :required => true - }, - :resource => { - :kind_of => [ String, Symbol, Class ], - :required => true - } - } - ) - if args.has_key?(:platform) - if args.has_key?(:version) - if map.has_key?(args[:platform]) - if map[args[:platform]].has_key?(args[:version]) - map[args[:platform]][args[:version]][args[:short_name].to_sym] = args[:resource] - else - map[args[:platform]][args[:version]] = { - args[:short_name].to_sym => args[:resource] - } - end - else - map[args[:platform]] = { - args[:version] => { - args[:short_name].to_sym => args[:resource] - } - } - end - else - if map.has_key?(args[:platform]) - if map[args[:platform]].has_key?(:default) - map[args[:platform]][:default][args[:short_name].to_sym] = args[:resource] - else - map[args[:platform]] = { :default => { args[:short_name].to_sym => args[:resource] } } - end - else - map[args[:platform]] = { - :default => { - args[:short_name].to_sym => args[:resource] - } - } - end - end - else - if map.has_key?(:default) - map[:default][args[:short_name].to_sym] = args[:resource] - else - map[:default] = { - args[:short_name].to_sym => args[:resource] - } - end - end - end - - def get(short_name, platform=nil, version=nil) - resource_klass = platform_resource(short_name, platform, version) || - resource_matching_short_name(short_name) - - raise Exceptions::NoSuchResourceType, "Cannot find a resource for #{short_name} on #{platform} version #{version}" if resource_klass.nil? - - resource_klass - end - - private - - def platform_resource(short_name, platform, version) - pmap = filter(platform, version) - rtkey = short_name.kind_of?(Chef::Resource) ? short_name.resource_name.to_sym : short_name - - pmap.has_key?(rtkey) ? pmap[rtkey] : nil - end - - def resource_matching_short_name(short_name) - begin - rname = convert_to_class_name(short_name.to_s) - Chef::Resource.const_get(rname) - rescue NameError - nil - end - end - - end - end -end diff --git a/lib/chef/run_context.rb b/lib/chef/run_context.rb index 3564be62ff..18d353ac61 100644 --- a/lib/chef/run_context.rb +++ b/lib/chef/run_context.rb @@ -51,7 +51,8 @@ class Chef # recipes, which is triggered by #load. (See also: CookbookCompiler) attr_accessor :resource_collection - attr_reader :provider_resolver + # Chef::ProviderResolver for this run + attr_accessor :provider_resolver # A Hash containing the immediate notifications triggered by resources # during the converge phase of the chef run. @@ -94,7 +95,6 @@ class Chef def load(run_list_expansion) @cookbook_compiler = CookbookCompiler.new(self, run_list_expansion, events) @cookbook_compiler.compile - @provider_resolver.load end # Adds an immediate notification to the diff --git a/lib/chef/runner.rb b/lib/chef/runner.rb index 3467cfe717..6125fe59e1 100644 --- a/lib/chef/runner.rb +++ b/lib/chef/runner.rb @@ -43,10 +43,6 @@ class Chef @run_context.events end - def provider_resolver - @run_context.provider_resolver - end - # Determine the appropriate provider for the given resource, then # execute it. def run_action(resource, action, notification_type=nil, notifying_resource=nil) @@ -82,7 +78,6 @@ class Chef # Execute each resource. run_context.resource_collection.execute_each_resource do |resource| - provider_resolver.resolve(resource) Array(resource.action).each {|action| run_action(resource, action)} end diff --git a/spec/support/lib/chef/provider/snakeoil.rb b/spec/support/lib/chef/provider/snakeoil.rb index e9d01f654f..485d37329f 100644 --- a/spec/support/lib/chef/provider/snakeoil.rb +++ b/spec/support/lib/chef/provider/snakeoil.rb @@ -19,6 +19,7 @@ class Chef class Provider class SnakeOil < Chef::Provider + def load_current_resource true end diff --git a/spec/support/lib/chef/resource/zen_follower.rb b/spec/support/lib/chef/resource/zen_follower.rb index 713f122f06..ddc289e48d 100644 --- a/spec/support/lib/chef/resource/zen_follower.rb +++ b/spec/support/lib/chef/resource/zen_follower.rb @@ -22,7 +22,7 @@ class Chef class Resource class ZenFollower < Chef::Resource - provides :follower, :on_platforms => ["zen"] + provides :follower, platform: "zen" def initialize(name, run_context=nil) @resource_name = :zen_follower diff --git a/spec/support/shared/unit/resource/static_provider_resolution.rb b/spec/support/shared/unit/resource/static_provider_resolution.rb new file mode 100644 index 0000000000..147852598a --- /dev/null +++ b/spec/support/shared/unit/resource/static_provider_resolution.rb @@ -0,0 +1,71 @@ +# +# Author:: Lamont Granquist (<lamont@chef.io>) +# Copyright:: Copyright (c) 2014 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. +# + + + +# +# This is for typical "static" provider resolution which maps resources onto +# providers based only on the node data. Its not really 'static' because it +# all goes through the Chef::ProviderResolver, but the effective result is +# a static mapping for the node (unlike the service resource which is +# complicated). +# +def static_provider_resolution(opts={}) + action = opts[:action] + provider_class = opts[:provider] + resource_class = opts[:resource] + name = opts[:name] + os = opts[:os] + platform_family = opts[:platform_family] + platform_version = opts[:platform_version] + + describe resource_class, "static provider initialization" do + let(:node) { + node = Chef::Node.new + node.automatic_attrs[:os] = os + node.automatic_attrs[:platform_family] = platform_family + node.automatic_attrs[:platform_version] = platform_version + node + } + let(:events) { Chef::EventDispatch::Dispatcher.new } + let(:provider_resolver) { Chef::ProviderResolver.new(node) } + let(:run_context) { + run_context = Chef::RunContext.new(node, {}, events) + run_context.provider_resolver = provider_resolver + run_context + } + let(:resource) { resource_class.new("foo", run_context) } + + it "should return a #{resource_class}" do + expect(resource).to be_a_kind_of(resource_class) + end + + it "should set the resource_name to #{name}" do + expect(resource.resource_name).to eql(name) + end + + it "should leave the provider nil" do + expect(resource.provider).to eql(nil) + end + + it "should resolve to a #{provider_class}" do + expect(resource.provider_for_action(action)).to be_a(provider_class) + end + end +end + diff --git a/spec/unit/node_map_spec.rb b/spec/unit/node_map_spec.rb new file mode 100644 index 0000000000..fe7372961b --- /dev/null +++ b/spec/unit/node_map_spec.rb @@ -0,0 +1,155 @@ +# +# Author:: Lamont Granquist (<lamont@chef.io>) +# Copyright:: Copyright (c) 2014 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 'spec_helper' +require 'chef/node_map' + +describe Chef::NodeMap do + + let(:node_map) { Chef::NodeMap.new } + + let(:node) { Chef::Node.new } + + describe "with a bad filter name" do + it "should raise an error" do + expect{ node_map.set(node, :thing, on_platform_family: 'rhel') }.to raise_error + end + end + + describe "when no matchers are set at all" do + before do + node_map.set(:thing, :foo) + end + + it "returns the value" do + expect(node_map.get(node, :thing)).to eql(:foo) + end + + it "returns nil for keys that do not exist" do + expect(node_map.get(node, :other_thing)).to eql(nil) + end + end + + describe "filtering by os" do + before do + node_map.set(:thing, :foo, os: ["windows"]) + node_map.set(:thing, :bar, os: "linux") + end + it "returns the correct value for windows" do + allow(node).to receive(:[]).with(:os).and_return("windows") + expect(node_map.get(node, :thing)).to eql(:foo) + end + it "returns the correct value for linux" do + allow(node).to receive(:[]).with(:os).and_return("linux") + expect(node_map.get(node, :thing)).to eql(:bar) + end + it "returns nil for a non-matching os" do + allow(node).to receive(:[]).with(:os).and_return("freebsd") + expect(node_map.get(node, :thing)).to eql(nil) + end + end + + describe "rejecting an os" do + before do + node_map.set(:thing, :foo, os: "!windows") + end + it "returns nil for windows" do + allow(node).to receive(:[]).with(:os).and_return("windows") + expect(node_map.get(node, :thing)).to eql(nil) + end + it "returns the correct value for linux" do + allow(node).to receive(:[]).with(:os).and_return("linux") + expect(node_map.get(node, :thing)).to eql(:foo) + end + end + + describe "filtering by os and platform_family" do + before do + node_map.set(:thing, :bar, os: "linux", platform_family: "rhel") + end + + it "returns the correct value when both match" do + allow(node).to receive(:[]).with(:os).and_return("linux") + allow(node).to receive(:[]).with(:platform_family).and_return("rhel") + expect(node_map.get(node, :thing)).to eql(:bar) + end + + it "returns nil for a non-matching os" do + allow(node).to receive(:[]).with(:os).and_return("freebsd") + expect(node_map.get(node, :thing)).to eql(nil) + end + + it "returns nil when the platform_family does not match" do + allow(node).to receive(:[]).with(:os).and_return("linux") + allow(node).to receive(:[]).with(:platform_family).and_return("debian") + expect(node_map.get(node, :thing)).to eql(nil) + end + end + + describe "with a block doing platform_version checks" do + before do + node_map.set(:thing, :foo, platform_family: "rhel") do |node| + node[:platform_version].to_i >= 7 + end + end + + it "returns the value when the node matches" do + allow(node).to receive(:[]).with(:platform_family).and_return("rhel") + allow(node).to receive(:[]).with(:platform_version).and_return("7.0") + expect(node_map.get(node, :thing)).to eql(:foo) + end + + it "returns nil when the block does not match" do + allow(node).to receive(:[]).with(:platform_family).and_return("rhel") + allow(node).to receive(:[]).with(:platform_version).and_return("6.4") + expect(node_map.get(node, :thing)).to eql(nil) + end + + it "returns nil when the platform_family filter does not match" do + allow(node).to receive(:[]).with(:platform_family).and_return("debian") + allow(node).to receive(:[]).with(:platform_version).and_return("7.0") + expect(node_map.get(node, :thing)).to eql(nil) + end + + it "returns nil when both do not match" do + allow(node).to receive(:[]).with(:platform_family).and_return("debian") + allow(node).to receive(:[]).with(:platform_version).and_return("6.0") + expect(node_map.get(node, :thing)).to eql(nil) + end + end + + describe "resource back-compat testing" do + it "should handle :on_platforms => :all" do + node_map.set(:chef_gem, :foo, :on_platforms => :all) + allow(node).to receive(:[]).with(:platform).and_return("windows") + expect(node_map.get(node, :chef_gem)).to eql(:foo) + end + it "should handle :on_platforms => [ 'windows' ]" do + node_map.set(:dsc_script, :foo, :on_platforms => [ 'windows' ]) + allow(node).to receive(:[]).with(:platform).and_return("windows") + expect(node_map.get(node, :dsc_script)).to eql(:foo) + end + it "should handle :on_platform => :all" do + node_map.set(:link, :foo, :on_platform => :all) + allow(node).to receive(:[]).with(:platform).and_return("windows") + expect(node_map.get(node, :link)).to eql(:foo) + end + end + +end + diff --git a/spec/unit/provider/log_spec.rb b/spec/unit/provider/log_spec.rb index a270ee4822..1ecc633ce1 100644 --- a/spec/unit/provider/log_spec.rb +++ b/spec/unit/provider/log_spec.rb @@ -32,10 +32,6 @@ describe Chef::Provider::Log::ChefLog do let(:provider) { Chef::Provider::Log::ChefLog.new(new_resource, run_context) } - it "should be registered with the default platform hash" do - expect(Chef::Platform.platforms[:default][:log]).not_to be_nil - end - it "should write the string to the Chef::Log object at default level (info)" do expect(Chef::Log).to receive(:info).with(log_str).and_return(true) provider.run_action(:write) diff --git a/spec/unit/provider/package/apt_spec.rb b/spec/unit/provider/package/apt_spec.rb index a94b248418..90e9dd6d3f 100644 --- a/spec/unit/provider/package/apt_spec.rb +++ b/spec/unit/provider/package/apt_spec.rb @@ -229,7 +229,7 @@ SHOWPKG_STDOUT @new_resource = nil @new_resource = Chef::Resource::AptPackage.new("irssi", @run_context) @new_resource.default_release("lenny-backports") - + @new_resource.provider = @provider @provider.new_resource = @new_resource @provider.should_receive(:shell_out!).with( diff --git a/spec/unit/provider_resolver_spec.rb b/spec/unit/provider_resolver_spec.rb index bc65bdc13c..927cca4f58 100644 --- a/spec/unit/provider_resolver_spec.rb +++ b/spec/unit/provider_resolver_spec.rb @@ -1,6 +1,6 @@ # -# Author:: Richard Manyanza (<liseki@nyikacraftsmen.com>) -# Copyright:: Copyright (c) 2014 Richard Manyanza. +# Author:: Lamont Granquist (<lamont@getchef.com>) +# Copyright:: Copyright (c) 2014 Opscode, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,74 +17,370 @@ # require 'spec_helper' +require 'chef/mixin/convert_to_class_name' + +include Chef::Mixin::ConvertToClassName describe Chef::ProviderResolver do - before(:each) do - @node = Chef::Node.new - @provider_resolver = Chef::ProviderResolver.new(@node) + + let(:node) do + node = Chef::Node.new + allow(node).to receive(:[]).with(:os).and_return(os) + allow(node).to receive(:[]).with(:platform_family).and_return(platform_family) + allow(node).to receive(:[]).with(:platform).and_return(platform) + allow(node).to receive(:[]).with(:platform_version).and_return(platform_version) + allow(node).to receive(:is_a?).and_return(Chef::Node) + node end - describe "Initialization" do - it "should not load providers" do - @provider_resolver.loaded?.should be_false + let(:provider_resolver) { Chef::ProviderResolver.new(node) } + + let(:action) { :start } + + let(:resolved_provider) { provider_resolver.resolve(resource, action) } + + let(:provider) { nil } + + let(:resource_name) { :service } + + let(:resource) { double(Chef::Resource, provider: provider, resource_name: resource_name) } + + describe "resolving service resource" do + def stub_service_providers(*services) + services ||= [] + allow(Chef::Platform::ServiceHelpers).to receive(:service_resource_providers) + .and_return(services) end - end + def stub_service_configs(*configs) + configs ||= [] + allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") + .and_return(configs) + end - describe "Loading providers" do - end + before do + expect(provider_resolver).not_to receive(:maybe_chef_platform_lookup) + allow(resource).to receive(:service_name).and_return("ntp") + end + + shared_examples_for "a debian platform with upstart and update-rc.d" do + before do + stub_service_providers(:debian, :invokercd, :upstart) + end + + it "when only the SysV init script exists, it returns a Service::Debian provider" do + allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") + .and_return( [ :initd ] ) + expect(resolved_provider).to eql(Chef::Provider::Service::Debian) + end + it "when both SysV and Upstart scripts exist, it returns a Service::Upstart provider" do + allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") + .and_return( [ :initd, :upstart ] ) + expect(resolved_provider).to eql(Chef::Provider::Service::Upstart) + end + + it "when only the Upstart script exists, it returns a Service::Upstart provider" do + allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") + .and_return( [ :upstart ] ) + expect(resolved_provider).to eql(Chef::Provider::Service::Upstart) + end - describe "on FreeBSD" do - before(:each) do - @node.normal[:platform] = :freebsd + it "when both do not exist, it calls the old style provider resolver and returns a Debian Provider" do + allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") + .and_return( [ ] ) + expect(resolved_provider).to eql(Chef::Provider::Service::Debian) + end end - describe "loading" do - before(:each) do - @provider_resolver.load + shared_examples_for "a debian platform using the insserv provider" do + context "with a default install" do + before do + stub_service_providers(:debian, :invokercd, :insserv) + end + + it "uses the Service::Insserv Provider to manage sysv init scripts" do + allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") + .and_return( [ :initd ] ) + expect(resolved_provider).to eql(Chef::Provider::Service::Insserv) + end end - it "should load FreeBSD providers" do - providers = [ - Chef::Provider::User::Pw, - Chef::Provider::Group::Pw, - Chef::Provider::Service::Freebsd, - Chef::Provider::Package::Freebsd, - Chef::Provider::Cron - ] + context "when the user has installed upstart" do + before do + stub_service_providers(:debian, :invokercd, :insserv, :upstart) + end + + it "when only the SysV init script exists, it returns a Service::Debian provider" do + allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") + .and_return( [ :initd ] ) + expect(resolved_provider).to eql(Chef::Provider::Service::Insserv) + end + + it "when both SysV and Upstart scripts exist, it returns a Service::Upstart provider" do + allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") + .and_return( [ :initd, :upstart ] ) + expect(resolved_provider).to eql(Chef::Provider::Service::Upstart) + end - @provider_resolver.providers.length.should == providers.length - providers.each do |provider| - @provider_resolver.providers.include?(provider).should be_true + it "when only the Upstart script exists, it returns a Service::Upstart provider" do + allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") + .and_return( [ :upstart ] ) + expect(resolved_provider).to eql(Chef::Provider::Service::Upstart) + end + + it "when both do not exist, it calls the old style provider resolver and returns a Debian Provider" do + allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") + .and_return( [ ] ) + expect(resolved_provider).to eql(Chef::Provider::Service::Insserv) end end end - describe "resolving" do - it "should handle user" do - user = Chef::Resource::User.new('toor') - @provider_resolver.resolve(user).should == Chef::Provider::User::Pw + describe "on Linux" do + end + + describe "on Ubuntu 14.04" do + let(:os) { "linux" } + let(:platform) { "ubuntu" } + let(:platform_family) { "debian" } + let(:platform_version) { "14.04" } + + it_behaves_like "a debian platform with upstart and update-rc.d" + end + + describe "on Ubuntu 10.04" do + let(:os) { "linux" } + let(:platform) { "ubuntu" } + let(:platform_family) { "debian" } + let(:platform_version) { "10.04" } + + it_behaves_like "a debian platform with upstart and update-rc.d" + end + + # old debian uses the Debian provider (does not have insserv or upstart, or update-rc.d???) + describe "on Debian 4.0" do + let(:os) { "linux" } + let(:platform) { "debian" } + let(:platform_family) { "debian" } + let(:platform_version) { "4.0" } + + #it_behaves_like "a debian platform using the debian provider" + end + + # Debian replaced the debian provider with insserv in the FIXME:VERSION distro + describe "on Debian 7.0" do + let(:os) { "linux" } + let(:platform) { "debian" } + let(:platform_family) { "debian" } + let(:platform_version) { "7.0" } + + it_behaves_like "a debian platform using the insserv provider" + end + + %w{solaris2 openindiana opensolaris nexentacore omnios smartos}.each do |platform| + describe "on #{platform}" do + let(:os) { "solaris2" } + let(:platform) { platform } + let(:platform_family) { platform } + let(:platform_version) { "5.11" } + + it "returns a Solaris provider" do + stub_service_providers + stub_service_configs + expect(resolved_provider).to eql(Chef::Provider::Service::Solaris) + end + + it "always returns a Solaris provider" do + # no matter what we stub on the next two lines we should get a Solaris provider + stub_service_providers(:debian, :invokercd, :insserv, :upstart, :redhat, :systemd) + stub_service_configs(:initd, :upstart, :xinetd, :user_local_etc_rcd, :systemd) + expect(resolved_provider).to eql(Chef::Provider::Service::Solaris) + end + end + end + + %w{mswin mingw32 windows}.each do |platform| + describe "on #{platform}" do + let(:os) { "windows" } + let(:platform) { platform } + let(:platform_family) { "windows" } + let(:platform_version) { "5.11" } + + it "returns a Windows provider" do + stub_service_providers + stub_service_configs + expect(resolved_provider).to eql(Chef::Provider::Service::Windows) + end + + it "always returns a Windows provider" do + # no matter what we stub on the next two lines we should get a Windows provider + stub_service_providers(:debian, :invokercd, :insserv, :upstart, :redhat, :systemd) + stub_service_configs(:initd, :upstart, :xinetd, :user_local_etc_rcd, :systemd) + expect(resolved_provider).to eql(Chef::Provider::Service::Windows) + end end + end - it "should handle group" do - group = Chef::Resource::Group.new('ops') - @provider_resolver.resolve(group).should == Chef::Provider::Group::Pw + %w{mac_os_x mac_os_x_server}.each do |platform| + describe "on #{platform}" do + let(:os) { "darwin" } + let(:platform) { platform } + let(:platform_family) { "mac_os_x" } + let(:platform_version) { "10.9.2" } + + it "returns a Macosx provider" do + stub_service_providers + stub_service_configs + expect(resolved_provider).to eql(Chef::Provider::Service::Macosx) + end + + it "always returns a Macosx provider" do + # no matter what we stub on the next two lines we should get a Macosx provider + stub_service_providers(:debian, :invokercd, :insserv, :upstart, :redhat, :systemd) + stub_service_configs(:initd, :upstart, :xinetd, :user_local_etc_rcd, :systemd) + expect(resolved_provider).to eql(Chef::Provider::Service::Macosx) + end end + end - it "should handle service" do - service = Chef::Resource::Service.new('nginx') - @provider_resolver.resolve(service).should == Chef::Provider::Service::Freebsd + %w{freebsd netbsd}.each do |platform| + describe "on #{platform}" do + let(:os) { platform } + let(:platform) { platform } + let(:platform_family) { platform } + let(:platform_version) { "10.0-RELEASE" } + + it "returns a Freebsd provider if it finds the /usr/local/etc/rc.d initscript" do + stub_service_providers + stub_service_configs(:usr_local_etc_rcd) + expect(resolved_provider).to eql(Chef::Provider::Service::Freebsd) + end + + it "returns a Freebsd provider if it finds the /etc/rc.d initscript" do + stub_service_providers + stub_service_configs(:etc_rcd) + expect(resolved_provider).to eql(Chef::Provider::Service::Freebsd) + end + + it "always returns a Freebsd provider if it finds the /usr/local/etc/rc.d initscript" do + # should only care about :usr_local_etc_rcd stub in the service configs + stub_service_providers(:debian, :invokercd, :insserv, :upstart, :redhat, :systemd) + stub_service_configs(:usr_local_etc_rcd, :initd, :upstart, :xinetd, :systemd) + expect(resolved_provider).to eql(Chef::Provider::Service::Freebsd) + end + + it "always returns a Freebsd provider if it finds the /usr/local/etc/rc.d initscript" do + # should only care about :etc_rcd stub in the service configs + stub_service_providers(:debian, :invokercd, :insserv, :upstart, :redhat, :systemd) + stub_service_configs(:etc_rcd, :initd, :upstart, :xinetd, :systemd) + expect(resolved_provider).to eql(Chef::Provider::Service::Freebsd) + end + + it "foo" do + stub_service_providers + stub_service_configs + expect(resolved_provider).to eql(Chef::Provider::Service::Freebsd) + end end + end + + end + + describe "resolving static providers" do + def resource_class(resource) + Chef::Resource.const_get(convert_to_class_name(resource.to_s)) + end + static_mapping = { + apt_package: Chef::Provider::Package::Apt, + bash: Chef::Provider::Script, + bff_package: Chef::Provider::Package::Aix, + breakpoint: Chef::Provider::Breakpoint, + chef_gem: Chef::Provider::Package::Rubygems, + cookbook_file: Chef::Provider::CookbookFile, + csh: Chef::Provider::Script, + deploy: Chef::Provider::Deploy::Timestamped, + deploy_revision: Chef::Provider::Deploy::Revision, + directory: Chef::Provider::Directory, + dpkg_package: Chef::Provider::Package::Dpkg, + dsc_script: Chef::Provider::DscScript, + easy_install_package: Chef::Provider::Package::EasyInstall, + erl_call: Chef::Provider::ErlCall, + execute: Chef::Provider::Execute, + file: Chef::Provider::File, + gem_package: Chef::Provider::Package::Rubygems, + git: Chef::Provider::Git, + homebrew_package: Chef::Provider::Package::Homebrew, + http_request: Chef::Provider::HttpRequest, + ips_package: Chef::Provider::Package::Ips, + link: Chef::Provider::Link, + log: Chef::Provider::Log::ChefLog, + macports_package: Chef::Provider::Package::Macports, + pacman_package: Chef::Provider::Package::Pacman, + paludis_package: Chef::Provider::Package::Paludis, + perl: Chef::Provider::Script, + portage_package: Chef::Provider::Package::Portage, + python: Chef::Provider::Script, + remote_directory: Chef::Provider::RemoteDirectory, + route: Chef::Provider::Route, + rpm_package: Chef::Provider::Package::Rpm, + ruby: Chef::Provider::Script, + ruby_block: Chef::Provider::RubyBlock, + script: Chef::Provider::Script, + smartos_package: Chef::Provider::Package::SmartOS, + solaris_package: Chef::Provider::Package::Solaris, + subversion: Chef::Provider::Subversion, + template: Chef::Provider::Template, + timestamped_deploy: Chef::Provider::Deploy::Timestamped, + whyrun_safe_ruby_block: Chef::Provider::WhyrunSafeRubyBlock, + windows_package: Chef::Provider::Package::Windows, + windows_service: Chef::Provider::Service::Windows, + yum_package: Chef::Provider::Package::Yum, + } - it "should handle package" do - package = Chef::Resource::Package.new('zsh') - @provider_resolver.resolve(package).should == Chef::Provider::Package::Freebsd + describe "on Ubuntu 14.04" do + let(:os) { "linux" } + let(:platform) { "ubuntu" } + let(:platform_family) { "debian" } + let(:platform_version) { "14.04" } + + supported_providers = [ + :apt_package, :bash, :breakpoint, :chef_gem, :cookbook_file, :csh, :deploy, + :deploy_revision, :directory, :dpkg_package, :easy_install_package, + :erl_call, :execute, :file, :gem_package, :git, :http_request, :link, :log, :pacman_package, :paludis_package, + :perl, :python, :remote_directory, :route, :rpm_package, :ruby, :ruby_block, :script, + :subversion, :template, :timestamped_deploy, :whyrun_safe_ruby_block, :yum_package, + ] + + supported_providers.each do |static_resource| + static_provider = static_mapping[static_resource] + context "when the resource is a #{static_resource}" do + let(:resource) { double(Chef::Resource, provider: nil, resource_name: static_resource) } + let(:action) { :start } # in reality this doesn't matter much + it "should resolve to a #{static_provider} provider" do + expect(provider_resolver).not_to receive(:maybe_chef_platform_lookup) + expect(resolved_provider).to eql(static_provider) + end + end end - it "should handle cron" do - cron = Chef::Resource::Cron.new('security_status_report') - @provider_resolver.resolve(cron).should == Chef::Provider::Cron + unsupported_providers = [ + :bff_package, :dsc_script, :homebrew_package, :ips_package, :macports_package, + :smartos_package, :solaris_package, :windows_package, + :windows_service, + ] + + unsupported_providers.each do |static_resource| + static_provider = static_mapping[static_resource] + context "when the resource is a #{static_resource}" do + let(:resource) { double(Chef::Resource, provider: nil, resource_name: static_resource) } + let(:action) { :start } # in reality this doesn't matter much + it "should fall back into the old provider mapper code and hooks" do + retval = Object.new + expect(provider_resolver).to receive(:maybe_chef_platform_lookup).and_return(retval) + expect(resolved_provider).to equal(retval) + end + end end end end diff --git a/spec/unit/recipe_spec.rb b/spec/unit/recipe_spec.rb index cb7581b5fb..0e660dc0cc 100644 --- a/spec/unit/recipe_spec.rb +++ b/spec/unit/recipe_spec.rb @@ -119,7 +119,8 @@ describe Chef::Recipe do describe "should locate platform mapped resources" do it "locate resource for particular platform" do - Object.const_set('ShaunTheSheep', Class.new(Chef::Resource){ provides :laughter, :on_platforms => ["television"] }) + ShaunTheSheep = Class.new(Chef::Resource) + ShaunTheSheep.provides :laughter, :on_platforms => ["television"] node.automatic[:platform] = "television" node.automatic[:platform_version] = "123" res = recipe.laughter "timmy" @@ -128,7 +129,8 @@ describe Chef::Recipe do end it "locate a resource for all platforms" do - Object.const_set("YourMom", Class.new(Chef::Resource){ provides :love_and_caring }) + YourMom = Class.new(Chef::Resource) + YourMom.provides :love_and_caring res = recipe.love_and_caring "mommy" res.name.should eql("mommy") res.kind_of?(YourMom) @@ -188,16 +190,7 @@ describe Chef::Recipe do # zen_follower resource has this: # provides :follower, :on_platforms => ["zen"] before do - node.stub(:[]) do |key| - case key - when :platform - :zen - when :platform_version - "1.0.0" - else - nil - end - end + node.automatic_attrs[:platform] = "zen" end let(:resource_follower) do diff --git a/spec/unit/resource/apt_package_spec.rb b/spec/unit/resource/apt_package_spec.rb index 58b007c327..9503e0cbe1 100644 --- a/spec/unit/resource/apt_package_spec.rb +++ b/spec/unit/resource/apt_package_spec.rb @@ -17,25 +17,22 @@ # require 'spec_helper' +require 'support/shared/unit/resource/static_provider_resolution' describe Chef::Resource::AptPackage, "initialize" do + static_provider_resolution( + resource: Chef::Resource::AptPackage, + provider: Chef::Provider::Package::Apt, + name: :apt_package, + action: :install, + os: "linux", + ) + before(:each) do @resource = Chef::Resource::AptPackage.new("foo") end - it "should return a Chef::Resource::AptPackage" do - @resource.should be_a_kind_of(Chef::Resource::AptPackage) - end - - it "should set the resource_name to :apt_package" do - @resource.resource_name.should eql(:apt_package) - end - - it "should set the provider to Chef::Provider::Package::Apt" do - @resource.provider.should eql(Chef::Provider::Package::Apt) - end - it "should support default_release" do @resource.default_release("lenny-backports") @resource.default_release.should eql("lenny-backports") diff --git a/spec/unit/resource/breakpoint_spec.rb b/spec/unit/resource/breakpoint_spec.rb index 412211a038..ed655b84a6 100644 --- a/spec/unit/resource/breakpoint_spec.rb +++ b/spec/unit/resource/breakpoint_spec.rb @@ -17,9 +17,17 @@ # require 'spec_helper' +require 'support/shared/unit/resource/static_provider_resolution' describe Chef::Resource::Breakpoint do + static_provider_resolution( + resource: Chef::Resource::Breakpoint, + provider: Chef::Provider::Breakpoint, + name: :breakpoint, + action: :break, + ) + before do @breakpoint = Chef::Resource::Breakpoint.new end @@ -36,8 +44,4 @@ describe Chef::Resource::Breakpoint do @breakpoint.name.should match(/breakpoint_spec\.rb\:[\d]{2}\:in \`new\'$/) end - it "uses the breakpoint provider" do - @breakpoint.provider.should == Chef::Provider::Breakpoint - end - end diff --git a/spec/unit/resource/chef_gem_spec.rb b/spec/unit/resource/chef_gem_spec.rb index dda65f8741..6a419b3f3b 100644 --- a/spec/unit/resource/chef_gem_spec.rb +++ b/spec/unit/resource/chef_gem_spec.rb @@ -18,24 +18,17 @@ # require 'spec_helper' +require 'support/shared/unit/resource/static_provider_resolution' describe Chef::Resource::ChefGem, "initialize" do - before(:each) do - @resource = Chef::Resource::ChefGem.new("foo") - end - - it "should return a Chef::Resource::ChefGem" do - @resource.should be_a_kind_of(Chef::Resource::ChefGem) - end - - it "should set the resource_name to :chef_gem" do - @resource.resource_name.should eql(:chef_gem) - end + static_provider_resolution( + resource: Chef::Resource::ChefGem, + provider: Chef::Provider::Package::Rubygems, + name: :chef_gem, + action: :install, + ) - it "should set the provider to Chef::Provider::Package::Rubygems" do - @resource.provider.should eql(Chef::Provider::Package::Rubygems) - end end describe Chef::Resource::ChefGem, "gem_binary" do diff --git a/spec/unit/resource/deploy_revision_spec.rb b/spec/unit/resource/deploy_revision_spec.rb index 1f509992aa..d136aa251e 100644 --- a/spec/unit/resource/deploy_revision_spec.rb +++ b/spec/unit/resource/deploy_revision_spec.rb @@ -17,31 +17,26 @@ # require 'spec_helper' +require 'support/shared/unit/resource/static_provider_resolution' describe Chef::Resource::DeployRevision do - it "defaults to the revision deploy provider" do - @resource = Chef::Resource::DeployRevision.new("deploy _this_!") - @resource.provider.should == Chef::Provider::Deploy::Revision - end - - it "has a name of deploy_revision" do - @resource = Chef::Resource::DeployRevision.new("deploy _this_!") - @resource.resource_name.should == :deploy_revision - end + static_provider_resolution( + resource: Chef::Resource::DeployRevision, + provider: Chef::Provider::Deploy::Revision, + name: :deploy_revision, + action: :deploy, + ) end describe Chef::Resource::DeployBranch do - it "defaults to the revision deploy provider" do - @resource = Chef::Resource::DeployBranch.new("deploy _this_!") - @resource.provider.should == Chef::Provider::Deploy::Revision - end - - it "has a name of deploy_branch" do - @resource = Chef::Resource::DeployBranch.new("deploy _this_!") - @resource.resource_name.should == :deploy_branch - end + static_provider_resolution( + resource: Chef::Resource::DeployBranch, + provider: Chef::Provider::Deploy::Revision, + name: :deploy_branch, + action: :deploy, + ) end diff --git a/spec/unit/resource/deploy_spec.rb b/spec/unit/resource/deploy_spec.rb index 7cc25ed41c..914ea19030 100644 --- a/spec/unit/resource/deploy_spec.rb +++ b/spec/unit/resource/deploy_spec.rb @@ -17,9 +17,18 @@ # require 'spec_helper' +require 'support/shared/unit/resource/static_provider_resolution' describe Chef::Resource::Deploy do + static_provider_resolution( + resource: Chef::Resource::Deploy, + provider: Chef::Provider::Deploy::Timestamped, + name: :deploy, + action: :deploy, + ) + + class << self def resource_has_a_string_attribute(attr_name) it "has a String attribute for #{attr_name.to_s}" do @@ -196,10 +205,6 @@ describe Chef::Resource::Deploy do @resource.restart.should == restart_like_this end - it "defaults to using the Deploy::Timestamped provider" do - @resource.provider.should == Chef::Provider::Deploy::Timestamped - end - it "allows providers to be set with a full class name" do @resource.provider Chef::Provider::Deploy::Timestamped @resource.provider.should == Chef::Provider::Deploy::Timestamped diff --git a/spec/unit/resource/dpkg_package_spec.rb b/spec/unit/resource/dpkg_package_spec.rb index 9ef498a577..931e6763bd 100644 --- a/spec/unit/resource/dpkg_package_spec.rb +++ b/spec/unit/resource/dpkg_package_spec.rb @@ -17,22 +17,16 @@ # require 'spec_helper' +require 'support/shared/unit/resource/static_provider_resolution' describe Chef::Resource::DpkgPackage, "initialize" do - before(:each) do - @resource = Chef::Resource::DpkgPackage.new("foo") - end + static_provider_resolution( + resource: Chef::Resource::DpkgPackage, + provider: Chef::Provider::Package::Dpkg, + name: :dpkg_package, + action: :install, + os: 'linux', + ) - it "should return a Chef::Resource::DpkgPackage" do - @resource.should be_a_kind_of(Chef::Resource::DpkgPackage) - end - - it "should set the resource_name to :dpkg_package" do - @resource.resource_name.should eql(:dpkg_package) - end - - it "should set the provider to Chef::Provider::Package::Dpkg" do - @resource.provider.should eql(Chef::Provider::Package::Dpkg) - end end diff --git a/spec/unit/resource/easy_install_package_spec.rb b/spec/unit/resource/easy_install_package_spec.rb index 9682c8177b..d3a5f4a0fe 100644 --- a/spec/unit/resource/easy_install_package_spec.rb +++ b/spec/unit/resource/easy_install_package_spec.rb @@ -17,30 +17,21 @@ # require 'spec_helper' +require 'support/shared/unit/resource/static_provider_resolution' describe Chef::Resource::EasyInstallPackage, "initialize" do + static_provider_resolution( + resource: Chef::Resource::EasyInstallPackage, + provider: Chef::Provider::Package::EasyInstall, + name: :easy_install_package, + action: :install, + ) + before(:each) do @resource = Chef::Resource::EasyInstallPackage.new("foo") end - it "should create a new Chef::Resource::EasyInstallPackage" do - @resource.should be_a_kind_of(Chef::Resource) - @resource.should be_a_kind_of(Chef::Resource::EasyInstallPackage) - end - - it "should return a Chef::Resource::EasyInstallPackage" do - @resource.should be_a_kind_of(Chef::Resource::EasyInstallPackage) - end - - it "should set the resource_name to :easy_install_package" do - @resource.resource_name.should eql(:easy_install_package) - end - - it "should set the provider to Chef::Provider::Package::EasyInstall" do - @resource.provider.should eql(Chef::Provider::Package::EasyInstall) - end - it "should allow you to set the easy_install_binary attribute" do @resource.easy_install_binary "/opt/local/bin/easy_install" @resource.easy_install_binary.should eql("/opt/local/bin/easy_install") diff --git a/spec/unit/resource/gem_package_spec.rb b/spec/unit/resource/gem_package_spec.rb index 98703455de..01c4fb6106 100644 --- a/spec/unit/resource/gem_package_spec.rb +++ b/spec/unit/resource/gem_package_spec.rb @@ -17,24 +17,17 @@ # require 'spec_helper' +require 'support/shared/unit/resource/static_provider_resolution' describe Chef::Resource::GemPackage, "initialize" do - before(:each) do - @resource = Chef::Resource::GemPackage.new("foo") - end - - it "should return a Chef::Resource::GemPackage" do - @resource.should be_a_kind_of(Chef::Resource::GemPackage) - end - - it "should set the resource_name to :gem_package" do - @resource.resource_name.should eql(:gem_package) - end + static_provider_resolution( + resource: Chef::Resource::GemPackage, + provider: Chef::Provider::Package::Rubygems, + name: :gem_package, + action: :install, + ) - it "should set the provider to Chef::Provider::Package::Rubygems" do - @resource.provider.should eql(Chef::Provider::Package::Rubygems) - end end describe Chef::Resource::GemPackage, "gem_binary" do diff --git a/spec/unit/resource/git_spec.rb b/spec/unit/resource/git_spec.rb index 95a30c28a4..da7aee1014 100644 --- a/spec/unit/resource/git_spec.rb +++ b/spec/unit/resource/git_spec.rb @@ -17,9 +17,17 @@ # require 'spec_helper' +require 'support/shared/unit/resource/static_provider_resolution' describe Chef::Resource::Git do + static_provider_resolution( + resource: Chef::Resource::Git, + provider: Chef::Provider::Git, + name: :git, + action: :sync, + ) + before(:each) do @git = Chef::Resource::Git.new("my awesome webapp") end @@ -29,10 +37,6 @@ describe Chef::Resource::Git do @git.should be_an_instance_of(Chef::Resource::Git) end - it "uses the git provider" do - @git.provider.should eql(Chef::Provider::Git) - end - it "uses aliases revision as branch" do @git.branch "HEAD" @git.revision.should eql("HEAD") diff --git a/spec/unit/resource/homebrew_package_spec.rb b/spec/unit/resource/homebrew_package_spec.rb index bb657607b7..bb248d1189 100644 --- a/spec/unit/resource/homebrew_package_spec.rb +++ b/spec/unit/resource/homebrew_package_spec.rb @@ -16,26 +16,19 @@ # require 'spec_helper' +require 'support/shared/unit/resource/static_provider_resolution' describe Chef::Resource::HomebrewPackage, 'initialize' do - let(:resource) { Chef::Resource::HomebrewPackage.new('emacs') } - - it 'returns a Chef::Resource::HomebrewPackage' do - expect(resource).to be_a_kind_of(Chef::Resource::HomebrewPackage) - end - - it 'sets the resource_name to :homebrew_package' do - expect(resource.resource_name).to eql(:homebrew_package) - end + static_provider_resolution( + resource: Chef::Resource::HomebrewPackage, + provider: Chef::Provider::Package::Homebrew, + name: :homebrew_package, + action: :install, + os: "mac_os_x", + ) - it 'sets the provider to Chef::Provider::Package::Homebrew' do - expect(resource.provider).to eql(Chef::Provider::Package::Homebrew) - end - - it 'sets the homebrew_user to nil' do - expect(resource.homebrew_user).to eql(nil) - end + let(:resource) { Chef::Resource::HomebrewPackage.new('emacs') } shared_examples 'home_brew user set and returned' do it 'returns the configured homebrew_user' do diff --git a/spec/unit/resource/ips_package_spec.rb b/spec/unit/resource/ips_package_spec.rb index 61661922fa..8718c5c093 100644 --- a/spec/unit/resource/ips_package_spec.rb +++ b/spec/unit/resource/ips_package_spec.rb @@ -17,25 +17,22 @@ # require 'spec_helper' +require 'support/shared/unit/resource/static_provider_resolution' describe Chef::Resource::IpsPackage, "initialize" do + static_provider_resolution( + resource: Chef::Resource::IpsPackage, + provider: Chef::Provider::Package::Ips, + name: :ips_package, + action: :install, + os: "solaris2", + ) + before(:each) do @resource = Chef::Resource::IpsPackage.new("crypto/gnupg") end - it "should return a Chef::Resource::IpsPackage" do - @resource.should be_a_kind_of(Chef::Resource::IpsPackage) - end - - it "should set the resource_name to :ips_package" do - @resource.resource_name.should eql(:ips_package) - end - - it "should set the provider to Chef::Provider::Package::Ips" do - @resource.provider.should eql(Chef::Provider::Package::Ips) - end - it "should support accept_license" do @resource.accept_license(true) @resource.accept_license.should eql(true) diff --git a/spec/unit/resource/macports_package_spec.rb b/spec/unit/resource/macports_package_spec.rb index 7e2e200487..0a203b2e97 100644 --- a/spec/unit/resource/macports_package_spec.rb +++ b/spec/unit/resource/macports_package_spec.rb @@ -17,21 +17,16 @@ # require 'spec_helper' +require 'support/shared/unit/resource/static_provider_resolution' describe Chef::Resource::MacportsPackage, "initialize" do - before(:each) do - @resource = Chef::Resource::MacportsPackage.new("foo") - end - it "should return a Chef::Resource::MacportsPackage" do - @resource.should be_a_kind_of(Chef::Resource::MacportsPackage) - end + static_provider_resolution( + resource: Chef::Resource::MacportsPackage, + provider: Chef::Provider::Package::Macports, + name: :macports_package, + action: :install, + os: "mac_os_x", + ) - it "should set the resource_name to :macports_package" do - @resource.resource_name.should eql(:macports_package) - end - - it "should set the provider to Chef::Provider::Package::Macports" do - @resource.provider.should eql(Chef::Provider::Package::Macports) - end end diff --git a/spec/unit/resource/pacman_package_spec.rb b/spec/unit/resource/pacman_package_spec.rb index ec5feeb82c..975863d04f 100644 --- a/spec/unit/resource/pacman_package_spec.rb +++ b/spec/unit/resource/pacman_package_spec.rb @@ -17,22 +17,16 @@ # require 'spec_helper' +require 'support/shared/unit/resource/static_provider_resolution' describe Chef::Resource::PacmanPackage, "initialize" do - before(:each) do - @resource = Chef::Resource::PacmanPackage.new("foo") - end + static_provider_resolution( + resource: Chef::Resource::PacmanPackage, + provider: Chef::Provider::Package::Pacman, + name: :pacman_package, + action: :install, + os: "linux", + ) - it "should return a Chef::Resource::PacmanPackage" do - @resource.should be_a_kind_of(Chef::Resource::PacmanPackage) - end - - it "should set the resource_name to :pacman_package" do - @resource.resource_name.should eql(:pacman_package) - end - - it "should set the provider to Chef::Provider::Package::Pacman" do - @resource.provider.should eql(Chef::Provider::Package::Pacman) - end end diff --git a/spec/unit/resource/rpm_package_spec.rb b/spec/unit/resource/rpm_package_spec.rb index 25930a5484..d209c6a5a2 100644 --- a/spec/unit/resource/rpm_package_spec.rb +++ b/spec/unit/resource/rpm_package_spec.rb @@ -17,22 +17,18 @@ # require 'spec_helper' +require 'support/shared/unit/resource/static_provider_resolution' describe Chef::Resource::RpmPackage, "initialize" do - before(:each) do - @resource = Chef::Resource::RpmPackage.new("foo") + %w{linux aix}.each do |os| + static_provider_resolution( + resource: Chef::Resource::RpmPackage, + provider: Chef::Provider::Package::Rpm, + name: :rpm_package, + action: :install, + os: os + ) end - it "should return a Chef::Resource::RpmPackage" do - @resource.should be_a_kind_of(Chef::Resource::RpmPackage) - end - - it "should set the resource_name to :rpm_package" do - @resource.resource_name.should eql(:rpm_package) - end - - it "should set the provider to Chef::Provider::Package::Rpm" do - @resource.provider.should eql(Chef::Provider::Package::Rpm) - end end diff --git a/spec/unit/resource/service_spec.rb b/spec/unit/resource/service_spec.rb index ec62d012bb..9b779e5830 100644 --- a/spec/unit/resource/service_spec.rb +++ b/spec/unit/resource/service_spec.rb @@ -29,20 +29,10 @@ describe Chef::Resource::Service do @resource.should be_a_kind_of(Chef::Resource) @resource.should be_a_kind_of(Chef::Resource::Service) end - + it "should not set a provider unless node[:init_package] is defined as systemd" do @resource.provider.should == nil end - - it "should set the provider to Chef::Provider::Service::Systemd if node[:init_package] is systemd" do - node = Chef::Node.new - node.set[:init_package] = "systemd" - cookbook_collection = Chef::CookbookCollection.new([]) - events = Chef::EventDispatch::Dispatcher.new - run_context = Chef::RunContext.new(node, cookbook_collection, events) - @resource = Chef::Resource::Service.new("chef", run_context) - @resource.provider.should == Chef::Provider::Service::Systemd - end it "should set the service_name to the first argument to new" do @resource.service_name.should eql("chef") diff --git a/spec/unit/resource/smartos_package_spec.rb b/spec/unit/resource/smartos_package_spec.rb index 391713c8ff..c2cf546dd5 100644 --- a/spec/unit/resource/smartos_package_spec.rb +++ b/spec/unit/resource/smartos_package_spec.rb @@ -16,23 +16,18 @@ # limitations under the License. # -require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper")) +require 'spec_helper' +require 'support/shared/unit/resource/static_provider_resolution' describe Chef::Resource::SmartosPackage, "initialize" do - before(:each) do - @resource = Chef::Resource::SmartosPackage.new("foo") - end + static_provider_resolution( + resource: Chef::Resource::SmartosPackage, + provider: Chef::Provider::Package::SmartOS, + name: :smartos_package, + action: :install, + os: "solaris2", + platform_family: "smartos", + ) - it "should return a Chef::Resource::SmartosPackage" do - @resource.should be_a_kind_of(Chef::Resource::SmartosPackage) - end - - it "should set the resource_name to :smartos_package" do - @resource.resource_name.should eql(:smartos_package) - end - - it "should set the provider to Chef::Provider::Package::SmartOS" do - @resource.provider.should eql(Chef::Provider::Package::SmartOS) - end end diff --git a/spec/unit/resource/solaris_package_spec.rb b/spec/unit/resource/solaris_package_spec.rb index 6d0260ab5a..ab4e03807d 100644 --- a/spec/unit/resource/solaris_package_spec.rb +++ b/spec/unit/resource/solaris_package_spec.rb @@ -17,41 +17,26 @@ # require 'spec_helper' +require 'support/shared/unit/resource/static_provider_resolution' describe Chef::Resource::SolarisPackage, "initialize" do - before(:each) do - @resource = Chef::Resource::SolarisPackage.new("foo") - end - - it "should return a Chef::Resource::SolarisPackage object" do - @resource.should be_a_kind_of(Chef::Resource::SolarisPackage) + %w{solaris2 nexentacore}.each do |platform_family| + static_provider_resolution( + resource: Chef::Resource::SolarisPackage, + provider: Chef::Provider::Package::Solaris, + name: :solaris_package, + action: :install, + os: "solaris2", + platform_family: platform_family, + ) end - it "should not raise any Error when valid number of arguments are provided" do - expect { Chef::Resource::SolarisPackage.new("foo") }.to_not raise_error - end - - it "should raise ArgumentError when incorrect number of arguments are provided" do - expect { Chef::Resource::SolarisPackage.new }.to raise_error(ArgumentError) + before(:each) do + @resource = Chef::Resource::SolarisPackage.new("foo") end it "should set the package_name to the name provided" do @resource.package_name.should eql("foo") end - - it "should set the resource_name to :solaris_package" do - @resource.resource_name.should eql(:solaris_package) - end - - it "should set the run_context to the run_context provided" do - @run_context = double() - @run_context.stub(:node) - resource = Chef::Resource::SolarisPackage.new("foo", @run_context) - resource.run_context.should eql(@run_context) - end - - it "should set the provider to Chef::Provider::Package::Solaris" do - @resource.provider.should eql(Chef::Provider::Package::Solaris) - end end diff --git a/spec/unit/resource/subversion_spec.rb b/spec/unit/resource/subversion_spec.rb index ae06ce665a..a52d8421c6 100644 --- a/spec/unit/resource/subversion_spec.rb +++ b/spec/unit/resource/subversion_spec.rb @@ -17,9 +17,17 @@ # require 'spec_helper' +require 'support/shared/unit/resource/static_provider_resolution' describe Chef::Resource::Subversion do + static_provider_resolution( + resource: Chef::Resource::Subversion, + provider: Chef::Provider::Subversion, + name: :subversion, + action: :install, + ) + before do @svn = Chef::Resource::Subversion.new("ohai, svn project!") end @@ -29,10 +37,6 @@ describe Chef::Resource::Subversion do @svn.should be_a_kind_of(Chef::Resource::Scm) end - it "uses the subversion provider" do - @svn.provider.should eql(Chef::Provider::Subversion) - end - it "allows the force_export action" do @svn.allowed_actions.should include(:force_export) end diff --git a/spec/unit/resource/timestamped_deploy_spec.rb b/spec/unit/resource/timestamped_deploy_spec.rb index f380ffca87..2af9d8bb0f 100644 --- a/spec/unit/resource/timestamped_deploy_spec.rb +++ b/spec/unit/resource/timestamped_deploy_spec.rb @@ -18,11 +18,37 @@ require 'spec_helper' -describe Chef::Resource::TimestampedDeploy do +describe Chef::Resource::TimestampedDeploy, "initialize" do - it "defaults to the TimestampedDeploy provider" do - @resource = Chef::Resource::TimestampedDeploy.new("stuff") - @resource.provider.should == Chef::Provider::Deploy::Timestamped + let(:node) { + node = Chef::Node.new + node.automatic_attrs[:os] = 'linux' + node.automatic_attrs[:platform_family] = 'rhel' + node + } + let(:events) { Chef::EventDispatch::Dispatcher.new } + let(:provider_resolver) { Chef::ProviderResolver.new(node) } + let(:run_context) { + run_context = Chef::RunContext.new(node, {}, events) + run_context.provider_resolver = provider_resolver + run_context + } + let(:resource) { Chef::Resource::TimestampedDeploy.new("stuff", run_context) } + + it "should return a Chef::Resource::TimestampedDeploy" do + expect(resource).to be_a_kind_of(Chef::Resource::TimestampedDeploy) + end + + it "should set the resource_name to :timestamped_deploy" do + expect(resource.resource_name).to eql(:deploy) end + it "should leave the provider nil" do + expect(resource.provider).to eql(nil) + end + + it "should resolve to a Chef::Provider::Deploy::Timestamped" do + expect(resource.provider_for_action(:install)).to be_a(Chef::Provider::Deploy::Timestamped) + end end + diff --git a/spec/unit/resource/yum_package_spec.rb b/spec/unit/resource/yum_package_spec.rb index 57ab4dfcd9..7e1979fdfd 100644 --- a/spec/unit/resource/yum_package_spec.rb +++ b/spec/unit/resource/yum_package_spec.rb @@ -17,24 +17,19 @@ # require 'spec_helper' +require 'support/shared/unit/resource/static_provider_resolution' describe Chef::Resource::YumPackage, "initialize" do - before(:each) do - @resource = Chef::Resource::YumPackage.new("foo") - end - - it "should return a Chef::Resource::YumPackage" do - @resource.should be_a_kind_of(Chef::Resource::YumPackage) - end - - it "should set the resource_name to :yum_package" do - @resource.resource_name.should eql(:yum_package) - end + static_provider_resolution( + resource: Chef::Resource::YumPackage, + provider: Chef::Provider::Package::Yum, + name: :yum_package, + action: :install, + os: 'linux', + platform_family: 'rhel', + ) - it "should set the provider to Chef::Provider::Package::Yum" do - @resource.provider.should eql(Chef::Provider::Package::Yum) - end end describe Chef::Resource::YumPackage, "arch" do diff --git a/spec/unit/resource_platform_map_spec.rb b/spec/unit/resource_platform_map_spec.rb deleted file mode 100644 index 99673d868f..0000000000 --- a/spec/unit/resource_platform_map_spec.rb +++ /dev/null @@ -1,164 +0,0 @@ -# -# Author:: Seth Chisamore (<schisamo@opscode.com>) -# Copyright:: Copyright (c) 2011 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 'spec_helper' - -describe Chef::Resource::PlatformMap do - - before(:each) do - @platform_map = Chef::Resource::PlatformMap.new({ - :windows => { - "6.1" => { - :file => "softiefile", - :else => "thing" - }, - :default => { - :file => Chef::Resource::File, - :ping => "pong", - :cat => "nice" - } - }, - :pop_tron => { - }, - :default => { - :soundwave => "lazerbeak", - :directory => Chef::Resource::Directory, - } - }) - end - - describe 'filtering the map' do - it "returns resources for platform and version" do - pmap = @platform_map.filter("Windows", "6.1") - pmap.should be_a_kind_of(Hash) - pmap[:file].should eql("softiefile") - end - - it "returns platform default resources if version does not exist" do - pmap = @platform_map.filter("windows", "1") - pmap.should be_a_kind_of(Hash) - pmap[:file].should eql(Chef::Resource::File) - end - - it "returns global default resources if none exist for plaform" do - pmap = @platform_map.filter("pop_tron", "1") - pmap.should be_a_kind_of(Hash) - pmap[:directory].should eql(Chef::Resource::Directory) - end - - it "returns global default resources if platform does not exist" do - pmap = @platform_map.filter("BeOS", "1") - pmap.should be_a_kind_of(Hash) - pmap[:soundwave].should eql("lazerbeak") - end - - it "returns a merged map of platform version and plaform default resources" do - pmap = @platform_map.filter("Windows", "6.1") - pmap[:file].should eql("softiefile") - pmap[:ping].should eql("pong") - end - - it "returns a merged map of platform specific version and global defaults" do - pmap = @platform_map.filter("Windows", "6.1") - pmap[:file].should eql("softiefile") - pmap[:soundwave].should eql("lazerbeak") - end - end - - describe 'finding a resource' do - it "returns a resource for a platform directly by short name" do - @platform_map.get(:file, "windows", "6.1").should eql("softiefile") - end - - it "returns a default resource if platform and version don't exist" do - @platform_map.get(:remote_file).should eql(Chef::Resource::RemoteFile) - end - - it "raises an exception if a resource cannot be found" do - lambda { @platform_map.get(:coffee, "windows", "6.1")}.should raise_error(NameError) - end - - it "returns a resource with a Chef::Resource object" do - kitty = Chef::Resource::Cat.new("loulou") - @platform_map.get(kitty, "windows", "6.1").should eql("nice") - end - end - - describe 'building the map' do - it "allows passing of a resource map at creation time" do - @new_map = Chef::Resource::PlatformMap.new({:the_dude => {:default => 'abides'}}) - @new_map.map[:the_dude][:default].should eql("abides") - end - - it "defaults to a resource map with :default key" do - @new_map = Chef::Resource::PlatformMap.new - @new_map.map.has_key?(:default) - end - - it "updates the resource map with a map" do - @platform_map.set( - :platform => :darwin, - :version => "9.2.2", - :short_name => :file, - :resource => "masterful" - ) - @platform_map.map[:darwin]["9.2.2"][:file].should eql("masterful") - - @platform_map.set( - :platform => :darwin, - :short_name => :file, - :resource => "masterful" - ) - @platform_map.map[:darwin][:default][:file].should eql("masterful") - - @platform_map.set( - :short_name => :file, - :resource => "masterful" - ) - @platform_map.map[:default][:file].should eql("masterful") - - @platform_map.set( - :platform => :hero, - :version => "9.2.2", - :short_name => :file, - :resource => "masterful" - ) - @platform_map.map[:hero]["9.2.2"][:file].should eql("masterful") - - @platform_map.set( - :short_name => :file, - :resource => "masterful" - ) - @platform_map.map[:default][:file].should eql("masterful") - - @platform_map.set( - :short_name => :file, - :resource => "masterful" - ) - @platform_map.map[:default][:file].should eql("masterful") - - @platform_map.set( - :platform => :neurosis, - :short_name => :package, - :resource => "masterful" - ) - @platform_map.map[:neurosis][:default][:package].should eql("masterful") - end - end - -end diff --git a/spec/unit/resource_spec.rb b/spec/unit/resource_spec.rb index 584ec8175c..2163cf181e 100644 --- a/spec/unit/resource_spec.rb +++ b/spec/unit/resource_spec.rb @@ -692,24 +692,33 @@ describe Chef::Resource do describe "building the platform map" do + let(:klz) { Class.new(Chef::Resource) } + + before do + Chef::Resource::Klz = klz + end + + after do + Chef::Resource.send(:remove_const, :Klz) + end + it 'adds mappings for a single platform' do - klz = Class.new(Chef::Resource) - Chef::Resource.platform_map.should_receive(:set).with( - :platform => :autobots, :short_name => :dinobot, :resource => klz + expect(Chef::Resource.node_map).to receive(:set).with( + :dinobot, Chef::Resource::Klz, { platform: ['autobots'] } ) - klz.provides :dinobot, :on_platforms => ['autobots'] + klz.provides :dinobot, platform: ['autobots'] end it 'adds mappings for multiple platforms' do - klz = Class.new(Chef::Resource) - Chef::Resource.platform_map.should_receive(:set).twice - klz.provides :energy, :on_platforms => ['autobots','decepticons'] + expect(Chef::Resource.node_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 - klz = Class.new(Chef::Resource) - Chef::Resource.platform_map.should_receive(:set).with( - :short_name => :tape_deck, :resource => klz + expect(Chef::Resource.node_map).to receive(:set).with( + :tape_deck, Chef::Resource::Klz, {} ) klz.provides :tape_deck end @@ -717,28 +726,26 @@ describe Chef::Resource do end describe "lookups from the platform map" do + let(:klz1) { Class.new(Chef::Resource) } + let(:klz2) { Class.new(Chef::Resource) } before(:each) do + Chef::Resource::Klz1 = klz1 + Chef::Resource::Klz2 = klz2 @node = Chef::Node.new @node.name("bumblebee") @node.automatic[:platform] = "autobots" @node.automatic[:platform_version] = "6.1" - Object.const_set('Soundwave', Class.new(Chef::Resource)) - Object.const_set('Grimlock', Class.new(Chef::Resource){ provides :dinobot, :on_platforms => ['autobots'] }) + Object.const_set('Soundwave', klz1) + klz2.provides :dinobot, :on_platforms => ['autobots'] + Object.const_set('Grimlock', klz2) end after(:each) do Object.send(:remove_const, :Soundwave) Object.send(:remove_const, :Grimlock) - end - - describe "resource_for_platform" do - it 'return a resource by short_name and platform' do - Chef::Resource.resource_for_platform(:dinobot,'autobots','6.1').should eql(Grimlock) - end - it "returns a resource by short_name if nothing else matches" do - Chef::Resource.resource_for_node(:soundwave, @node).should eql(Soundwave) - end + Chef::Resource.send(:remove_const, :Klz1) + Chef::Resource.send(:remove_const, :Klz2) end describe "resource_for_node" do diff --git a/spec/unit/runner_spec.rb b/spec/unit/runner_spec.rb index 68d8b0e011..9bd4199418 100644 --- a/spec/unit/runner_spec.rb +++ b/spec/unit/runner_spec.rb @@ -81,322 +81,329 @@ end describe Chef::Runner do - before(:each) do - @node = Chef::Node.new - @node.name "latte" - @node.automatic[:platform] = "mac_os_x" - @node.automatic[:platform_version] = "10.5.1" - @events = Chef::EventDispatch::Dispatcher.new - @run_context = Chef::RunContext.new(@node, Chef::CookbookCollection.new({}), @events) - @first_resource = Chef::Resource::Cat.new("loulou1", @run_context) - @run_context.resource_collection << @first_resource - Chef::Platform.set( - :resource => :cat, - :provider => Chef::Provider::SnakeOil - ) - @runner = Chef::Runner.new(@run_context) + let(:node) do + node = Chef::Node.new + node.name "latte" + node.automatic[:platform] = "mac_os_x" + node.automatic[:platform_version] = "10.5.1" + node end - it "should pass each resource in the collection to a provider" do - @run_context.resource_collection.should_receive(:execute_each_resource).once - @runner.converge - end + let(:events) { Chef::EventDispatch::Dispatcher.new } + let(:run_context) { Chef::RunContext.new(node, Chef::CookbookCollection.new({}), events) } + let(:first_resource) { Chef::Resource::Cat.new("loulou1", run_context) } + let(:runner) { Chef::Runner.new(run_context) } - it "should use the provider specified by the resource (if it has one)" do - provider = Chef::Provider::Easy.new(@run_context.resource_collection[0], @run_context) - # Expect provider to be called twice, because will fall back to old provider lookup - @run_context.resource_collection[0].should_receive(:provider).twice.and_return(Chef::Provider::Easy) - Chef::Provider::Easy.should_receive(:new).once.and_return(provider) - @runner.converge + before do + run_context.resource_collection << first_resource end - it "should use the platform provider if it has one" do - Chef::Platform.should_receive(:find_provider_for_node).once.and_return(Chef::Provider::SnakeOil) - @runner.converge - end + context "when we fall through to old Chef::Platform resolution" do + before do + # set up old Chef::Platform resolution instead of provider_resolver + Chef::Platform.set( + :resource => :cat, + :provider => Chef::Provider::SnakeOil + ) + allow(run_context.provider_resolver).to receive(:maybe_dynamic_provider_resolution).with(first_resource, anything()).and_return(nil) + end - it "should run the action for each resource" do - Chef::Platform.should_receive(:find_provider_for_node).once.and_return(Chef::Provider::SnakeOil) - provider = Chef::Provider::SnakeOil.new(@run_context.resource_collection[0], @run_context) - provider.should_receive(:action_sell).once.and_return(true) - Chef::Provider::SnakeOil.should_receive(:new).once.and_return(provider) - @runner.converge + it "should use the platform provider if it has one" do + expect(Chef::Platform).to receive(:find_provider_for_node).with(node, first_resource).and_call_original + runner.converge + end end - it "should raise exceptions as thrown by a provider" do - provider = Chef::Provider::SnakeOil.new(@run_context.resource_collection[0], @run_context) - Chef::Provider::SnakeOil.stub(:new).once.and_return(provider) - provider.stub(:action_sell).once.and_raise(ArgumentError) - lambda { @runner.converge }.should raise_error(ArgumentError) - end + context "when we are doing dynamic provider resolution" do - it "should not raise exceptions thrown by providers if the resource has ignore_failure set to true" do - @run_context.resource_collection[0].stub(:ignore_failure).and_return(true) - provider = Chef::Provider::SnakeOil.new(@run_context.resource_collection[0], @run_context) - Chef::Provider::SnakeOil.stub(:new).once.and_return(provider) - provider.stub(:action_sell).once.and_raise(ArgumentError) - lambda { @runner.converge }.should_not raise_error - end + it "should pass each resource in the collection to a provider" do + expect(run_context.resource_collection).to receive(:execute_each_resource).once + runner.converge + end - it "should retry with the specified delay if retries are specified" do - @first_resource.retries 3 - provider = Chef::Provider::SnakeOil.new(@run_context.resource_collection[0], @run_context) - Chef::Provider::SnakeOil.stub(:new).once.and_return(provider) - provider.stub(:action_sell).and_raise(ArgumentError) - @first_resource.should_receive(:sleep).with(2).exactly(3).times - lambda { @runner.converge }.should raise_error(ArgumentError) - end + it "should use the provider specified by the resource (if it has one)" do + provider = Chef::Provider::Easy.new(run_context.resource_collection[0], run_context) + # Expect provider to be called twice, because will fall back to old provider lookup + expect(run_context.resource_collection[0]).to receive(:provider).twice.and_return(Chef::Provider::Easy) + expect(Chef::Provider::Easy).to receive(:new).once.and_return(provider) + runner.converge + end - it "should execute immediate actions on changed resources" do - notifying_resource = Chef::Resource::Cat.new("peanut", @run_context) - notifying_resource.action = :purr # only action that will set updated on the resource + it "should run the action for each resource" do + provider = Chef::Provider::SnakeOil.new(run_context.resource_collection[0], run_context) + expect(provider).to receive(:action_sell).once.and_return(true) + expect(Chef::Provider::SnakeOil).to receive(:new).once.and_return(provider) + runner.converge + end - @run_context.resource_collection << notifying_resource - @first_resource.action = :nothing # won't be updated unless notified by other resource + it "should raise exceptions as thrown by a provider" do + provider = Chef::Provider::SnakeOil.new(run_context.resource_collection[0], run_context) + allow(Chef::Provider::SnakeOil).to receive(:new).once.and_return(provider) + allow(provider).to receive(:action_sell).once.and_raise(ArgumentError) + expect { runner.converge }.to raise_error(ArgumentError) + end - notifying_resource.notifies(:purr, @first_resource, :immediately) + it "should not raise exceptions thrown by providers if the resource has ignore_failure set to true" do + allow(run_context.resource_collection[0]).to receive(:ignore_failure).and_return(true) + provider = Chef::Provider::SnakeOil.new(run_context.resource_collection[0], run_context) + allow(Chef::Provider::SnakeOil).to receive(:new).once.and_return(provider) + allow(provider).to receive(:action_sell).once.and_raise(ArgumentError) + expect { runner.converge }.not_to raise_error + end - @runner.converge + it "should retry with the specified delay if retries are specified" do + first_resource.retries 3 + provider = Chef::Provider::SnakeOil.new(run_context.resource_collection[0], run_context) + allow(Chef::Provider::SnakeOil).to receive(:new).once.and_return(provider) + allow(provider).to receive(:action_sell).and_raise(ArgumentError) + expect(first_resource).to receive(:sleep).with(2).exactly(3).times + expect { runner.converge }.to raise_error(ArgumentError) + end - @first_resource.should be_updated - end + it "should execute immediate actions on changed resources" do + notifying_resource = Chef::Resource::Cat.new("peanut", run_context) + notifying_resource.action = :purr # only action that will set updated on the resource - it "should follow a chain of actions" do - @first_resource.action = :nothing + run_context.resource_collection << notifying_resource + first_resource.action = :nothing # won't be updated unless notified by other resource - middle_resource = Chef::Resource::Cat.new("peanut", @run_context) - middle_resource.action = :nothing - @run_context.resource_collection << middle_resource - middle_resource.notifies(:purr, @first_resource, :immediately) + notifying_resource.notifies(:purr, first_resource, :immediately) - last_resource = Chef::Resource::Cat.new("snuffles", @run_context) - last_resource.action = :purr - @run_context.resource_collection << last_resource - last_resource.notifies(:purr, middle_resource, :immediately) + runner.converge - @runner.converge + expect(first_resource).to be_updated + end - last_resource.should be_updated # by action(:purr) - middle_resource.should be_updated # by notification from last_resource - @first_resource.should be_updated # by notification from middle_resource - end + it "should follow a chain of actions" do + first_resource.action = :nothing - it "should execute delayed actions on changed resources" do - @first_resource.action = :nothing - second_resource = Chef::Resource::Cat.new("peanut", @run_context) - second_resource.action = :purr + middle_resource = Chef::Resource::Cat.new("peanut", run_context) + middle_resource.action = :nothing + run_context.resource_collection << middle_resource + middle_resource.notifies(:purr, first_resource, :immediately) - @run_context.resource_collection << second_resource - second_resource.notifies(:purr, @first_resource, :delayed) + last_resource = Chef::Resource::Cat.new("snuffles", run_context) + last_resource.action = :purr + run_context.resource_collection << last_resource + last_resource.notifies(:purr, middle_resource, :immediately) - @runner.converge + runner.converge - @first_resource.should be_updated - end + expect(last_resource).to be_updated # by action(:purr) + expect(middle_resource).to be_updated # by notification from last_resource + expect(first_resource).to be_updated # by notification from middle_resource + end - it "should execute delayed notifications when a failure occurs in the chef client run" do - @first_resource.action = :nothing - second_resource = Chef::Resource::Cat.new("peanut", @run_context) - second_resource.action = :purr + it "should execute delayed actions on changed resources" do + first_resource.action = :nothing + second_resource = Chef::Resource::Cat.new("peanut", run_context) + second_resource.action = :purr - @run_context.resource_collection << second_resource - second_resource.notifies(:purr, @first_resource, :delayed) + run_context.resource_collection << second_resource + second_resource.notifies(:purr, first_resource, :delayed) - third_resource = FailureResource.new("explode", @run_context) - @run_context.resource_collection << third_resource + runner.converge - lambda {@runner.converge}.should raise_error(FailureProvider::ChefClientFail) + expect(first_resource).to be_updated + end - @first_resource.should be_updated - end + it "should execute delayed notifications when a failure occurs in the chef client run" do + first_resource.action = :nothing + second_resource = Chef::Resource::Cat.new("peanut", run_context) + second_resource.action = :purr - it "should execute delayed notifications when a failure occurs in a notification" do - @first_resource.action = :nothing - second_resource = Chef::Resource::Cat.new("peanut", @run_context) - second_resource.action = :purr + run_context.resource_collection << second_resource + second_resource.notifies(:purr, first_resource, :delayed) - @run_context.resource_collection << second_resource + third_resource = FailureResource.new("explode", run_context) + run_context.resource_collection << third_resource - third_resource = FailureResource.new("explode", @run_context) - third_resource.action = :nothing - @run_context.resource_collection << third_resource + expect { runner.converge }.to raise_error(FailureProvider::ChefClientFail) - second_resource.notifies(:fail, third_resource, :delayed) - second_resource.notifies(:purr, @first_resource, :delayed) + expect(first_resource).to be_updated + end - lambda {@runner.converge}.should raise_error(FailureProvider::ChefClientFail) + it "should execute delayed notifications when a failure occurs in a notification" do + first_resource.action = :nothing + second_resource = Chef::Resource::Cat.new("peanut", run_context) + second_resource.action = :purr - @first_resource.should be_updated - end + run_context.resource_collection << second_resource - it "should execute delayed notifications when a failure occurs in multiple notifications" do - @first_resource.action = :nothing - second_resource = Chef::Resource::Cat.new("peanut", @run_context) - second_resource.action = :purr + third_resource = FailureResource.new("explode", run_context) + third_resource.action = :nothing + run_context.resource_collection << third_resource - @run_context.resource_collection << second_resource + second_resource.notifies(:fail, third_resource, :delayed) + second_resource.notifies(:purr, first_resource, :delayed) - third_resource = FailureResource.new("explode", @run_context) - third_resource.action = :nothing - @run_context.resource_collection << third_resource + expect {runner.converge}.to raise_error(FailureProvider::ChefClientFail) - fourth_resource = FailureResource.new("explode again", @run_context) - fourth_resource.action = :nothing - @run_context.resource_collection << fourth_resource + expect(first_resource).to be_updated + end - second_resource.notifies(:fail, third_resource, :delayed) - second_resource.notifies(:fail, fourth_resource, :delayed) - second_resource.notifies(:purr, @first_resource, :delayed) + it "should execute delayed notifications when a failure occurs in multiple notifications" do + first_resource.action = :nothing + second_resource = Chef::Resource::Cat.new("peanut", run_context) + second_resource.action = :purr - exception = nil - begin - @runner.converge - rescue => e - exception = e - end - exception.should be_a(Chef::Exceptions::MultipleFailures) + run_context.resource_collection << second_resource + + third_resource = FailureResource.new("explode", run_context) + third_resource.action = :nothing + run_context.resource_collection << third_resource + + fourth_resource = FailureResource.new("explode again", run_context) + fourth_resource.action = :nothing + run_context.resource_collection << fourth_resource + + second_resource.notifies(:fail, third_resource, :delayed) + second_resource.notifies(:fail, fourth_resource, :delayed) + second_resource.notifies(:purr, first_resource, :delayed) + + exception = nil + begin + runner.converge + rescue => e + exception = e + end + expect(exception).to be_a(Chef::Exceptions::MultipleFailures) - expected_message =<<-E + expected_message =<<-E Multiple failures occurred: * FailureProvider::ChefClientFail occurred in delayed notification: [explode] (dynamically defined) had an error: FailureProvider::ChefClientFail: chef had an error of some sort * FailureProvider::ChefClientFail occurred in delayed notification: [explode again] (dynamically defined) had an error: FailureProvider::ChefClientFail: chef had an error of some sort -E - exception.message.should == expected_message + E + expect(exception.message).to eq(expected_message) - @first_resource.should be_updated - end - - it "does not duplicate delayed notifications" do - SnitchyProvider.clear_action_record - - Chef::Platform.set( - :resource => :cat, - :provider => SnitchyProvider - ) + expect(first_resource).to be_updated + end - @first_resource.action = :nothing + it "does not duplicate delayed notifications" do + SnitchyProvider.clear_action_record - second_resource = Chef::Resource::Cat.new("peanut", @run_context) - second_resource.action = :first_action - @run_context.resource_collection << second_resource + first_resource.action = :nothing + first_resource.provider = SnitchyProvider - third_resource = Chef::Resource::Cat.new("snickers", @run_context) - third_resource.action = :first_action - @run_context.resource_collection << third_resource + second_resource = Chef::Resource::Cat.new("peanut", run_context) + second_resource.action = :first_action + second_resource.provider = SnitchyProvider + run_context.resource_collection << second_resource - second_resource.notifies(:second_action, @first_resource, :delayed) - second_resource.notifies(:third_action, @first_resource, :delayed) + third_resource = Chef::Resource::Cat.new("snickers", run_context) + third_resource.action = :first_action + third_resource.provider = SnitchyProvider + run_context.resource_collection << third_resource - third_resource.notifies(:second_action, @first_resource, :delayed) - third_resource.notifies(:third_action, @first_resource, :delayed) + second_resource.notifies(:second_action, first_resource, :delayed) + second_resource.notifies(:third_action, first_resource, :delayed) - @runner.converge - # resources 2 and 3 call :first_action in the course of normal resource - # execution, and schedule delayed actions :second and :third on the first - # resource. The duplicate actions should "collapse" to a single notification - # and order should be preserved. - SnitchyProvider.all_actions_called.should == [:first, :first, :second, :third] - end + third_resource.notifies(:second_action, first_resource, :delayed) + third_resource.notifies(:third_action, first_resource, :delayed) - it "executes delayed notifications in the order they were declared" do - SnitchyProvider.clear_action_record + runner.converge + # resources 2 and 3 call :first_action in the course of normal resource + # execution, and schedule delayed actions :second and :third on the first + # resource. The duplicate actions should "collapse" to a single notification + # and order should be preserved. + expect(SnitchyProvider.all_actions_called).to eq([:first, :first, :second, :third]) + end - Chef::Platform.set( - :resource => :cat, - :provider => SnitchyProvider - ) + it "executes delayed notifications in the order they were declared" do + SnitchyProvider.clear_action_record - @first_resource.action = :nothing + first_resource.action = :nothing + first_resource.provider = SnitchyProvider - second_resource = Chef::Resource::Cat.new("peanut", @run_context) - second_resource.action = :first_action - @run_context.resource_collection << second_resource + second_resource = Chef::Resource::Cat.new("peanut", run_context) + second_resource.action = :first_action + second_resource.provider = SnitchyProvider + run_context.resource_collection << second_resource - third_resource = Chef::Resource::Cat.new("snickers", @run_context) - third_resource.action = :first_action - @run_context.resource_collection << third_resource + third_resource = Chef::Resource::Cat.new("snickers", run_context) + third_resource.action = :first_action + third_resource.provider = SnitchyProvider + run_context.resource_collection << third_resource - second_resource.notifies(:second_action, @first_resource, :delayed) - second_resource.notifies(:second_action, @first_resource, :delayed) + second_resource.notifies(:second_action, first_resource, :delayed) + second_resource.notifies(:second_action, first_resource, :delayed) - third_resource.notifies(:third_action, @first_resource, :delayed) - third_resource.notifies(:third_action, @first_resource, :delayed) + third_resource.notifies(:third_action, first_resource, :delayed) + third_resource.notifies(:third_action, first_resource, :delayed) - @runner.converge - SnitchyProvider.all_actions_called.should == [:first, :first, :second, :third] - end + runner.converge + expect(SnitchyProvider.all_actions_called).to eq([:first, :first, :second, :third]) + end - it "does not fire notifications if the resource was not updated by the last action executed" do - # REGRESSION TEST FOR CHEF-1452 - SnitchyProvider.clear_action_record + it "does not fire notifications if the resource was not updated by the last action executed" do + # REGRESSION TEST FOR CHEF-1452 + SnitchyProvider.clear_action_record - Chef::Platform.set( - :resource => :cat, - :provider => SnitchyProvider - ) + first_resource.action = :first_action + first_resource.provider = SnitchyProvider - @first_resource.action = :first_action + second_resource = Chef::Resource::Cat.new("peanut", run_context) + second_resource.action = :nothing + second_resource.provider = SnitchyProvider + run_context.resource_collection << second_resource - second_resource = Chef::Resource::Cat.new("peanut", @run_context) - second_resource.action = :nothing - @run_context.resource_collection << second_resource + third_resource = Chef::Resource::Cat.new("snickers", run_context) + third_resource.action = :nothing + third_resource.provider = SnitchyProvider + run_context.resource_collection << third_resource - third_resource = Chef::Resource::Cat.new("snickers", @run_context) - third_resource.action = :nothing - @run_context.resource_collection << third_resource + first_resource.notifies(:second_action, second_resource, :immediately) + second_resource.notifies(:third_action, third_resource, :immediately) - @first_resource.notifies(:second_action, second_resource, :immediately) - second_resource.notifies(:third_action, third_resource, :immediately) + runner.converge - @runner.converge + # All of the resources should only fire once: + expect(SnitchyProvider.all_actions_called).to eq([:first, :second, :third]) - # All of the resources should only fire once: - SnitchyProvider.all_actions_called.should == [:first, :second, :third] + # all of the resources should be marked as updated for reporting purposes + expect(first_resource).to be_updated + expect(second_resource).to be_updated + expect(third_resource).to be_updated + end - # all of the resources should be marked as updated for reporting purposes - @first_resource.should be_updated - second_resource.should be_updated - third_resource.should be_updated - end + it "should check a resource's only_if and not_if if notified by another resource" do + first_resource.action = :buy - it "should check a resource's only_if and not_if if notified by another resource" do - @first_resource.action = :buy + only_if_called_times = 0 + first_resource.only_if {only_if_called_times += 1; true} - only_if_called_times = 0 - @first_resource.only_if {only_if_called_times += 1; true} + not_if_called_times = 0 + first_resource.not_if {not_if_called_times += 1; false} - not_if_called_times = 0 - @first_resource.not_if {not_if_called_times += 1; false} + second_resource = Chef::Resource::Cat.new("carmel", run_context) + run_context.resource_collection << second_resource + second_resource.notifies(:purr, first_resource, :delayed) + second_resource.action = :purr - second_resource = Chef::Resource::Cat.new("carmel", @run_context) - @run_context.resource_collection << second_resource - second_resource.notifies(:purr, @first_resource, :delayed) - second_resource.action = :purr + # hits only_if first time when the resource is run in order, second on notify + runner.converge - # hits only_if first time when the resource is run in order, second on notify - @runner.converge + expect(only_if_called_times).to eq(2) + expect(not_if_called_times).to eq(2) + end - only_if_called_times.should == 2 - not_if_called_times.should == 2 - end + it "should resolve resource references in notifications when resources are defined lazily" do + first_resource.action = :nothing - it "should resolve resource references in notifications when resources are defined lazily" do - @first_resource.action = :nothing + lazy_resources = lambda { + last_resource = Chef::Resource::Cat.new("peanut", run_context) + run_context.resource_collection << last_resource + last_resource.notifies(:purr, first_resource.to_s, :delayed) + last_resource.action = :purr + } + second_resource = Chef::Resource::RubyBlock.new("myblock", run_context) + run_context.resource_collection << second_resource + second_resource.block { lazy_resources.call } - lazy_resources = lambda { - last_resource = Chef::Resource::Cat.new("peanut", @run_context) - @run_context.resource_collection << last_resource - last_resource.notifies(:purr, @first_resource.to_s, :delayed) - last_resource.action = :purr - } - second_resource = Chef::Resource::RubyBlock.new("myblock", @run_context) - @run_context.resource_collection << second_resource - second_resource.block { lazy_resources.call } + runner.converge - @runner.converge + expect(first_resource).to be_updated + end - @first_resource.should be_updated end - end - |