summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Keiser <john@johnkeiser.com>2015-05-13 14:56:09 -0700
committerJohn Keiser <john@johnkeiser.com>2015-05-13 14:56:09 -0700
commitb086721ca70750277c407fd0cc573a08f076649f (patch)
treeba58fa54264d520f6d4b9ae201176f5727e462ad
parentc46776d45cd5a732441a4dc0bf0552e71bb500ee (diff)
parent34638bf82798bdcf01c28fbde6ac68dd1949c301 (diff)
downloadchef-b086721ca70750277c407fd0cc573a08f076649f.tar.gz
Deprecate Chef::Resource::FooBar -> "foo_bar" DSL convention;
use methods to handle DSL rather than method_missing
-rw-r--r--.gitignore1
-rw-r--r--CHANGELOG.md6
-rw-r--r--DOC_CHANGES.md27
-rw-r--r--Gemfile1
-rw-r--r--Rakefile3
-rw-r--r--chef.gemspec4
-rw-r--r--external_tests/chef-rewind.gemfile5
-rw-r--r--external_tests/chef-sugar.gemfile6
-rw-r--r--external_tests/chefspec.gemfile7
-rw-r--r--external_tests/foodcritic.gemfile9
-rw-r--r--external_tests/halite.gemfile8
-rw-r--r--external_tests/poise.gemfile7
-rw-r--r--lib/chef/dsl/definitions.rb44
-rw-r--r--lib/chef/dsl/recipe.rb85
-rw-r--r--lib/chef/dsl/resources.rb28
-rw-r--r--lib/chef/mixin/provides.rb9
-rw-r--r--lib/chef/platform/provider_mapping.rb12
-rw-r--r--lib/chef/provider.rb28
-rw-r--r--lib/chef/provider/cron/unix.rb1
-rw-r--r--lib/chef/provider/lwrp_base.rb134
-rw-r--r--lib/chef/provider/ohai.rb1
-rw-r--r--lib/chef/provider/reboot.rb1
-rw-r--r--lib/chef/provider/registry_key.rb2
-rw-r--r--lib/chef/provider/remote_file.rb1
-rw-r--r--lib/chef/provider_resolver.rb87
-rw-r--r--lib/chef/resource.rb64
-rw-r--r--lib/chef/resource/bash.rb1
-rw-r--r--lib/chef/resource/bff_package.rb3
-rw-r--r--lib/chef/resource/breakpoint.rb1
-rw-r--r--lib/chef/resource/csh.rb1
-rw-r--r--lib/chef/resource/deploy.rb1
-rw-r--r--lib/chef/resource/erl_call.rb1
-rw-r--r--lib/chef/resource/execute.rb1
-rw-r--r--lib/chef/resource/http_request.rb1
-rw-r--r--lib/chef/resource/ifconfig.rb3
-rw-r--r--lib/chef/resource/log.rb3
-rw-r--r--lib/chef/resource/lwrp_base.rb193
-rw-r--r--lib/chef/resource/ohai.rb1
-rw-r--r--lib/chef/resource/package.rb1
-rw-r--r--lib/chef/resource/perl.rb1
-rw-r--r--lib/chef/resource/portage_package.rb1
-rw-r--r--lib/chef/resource/python.rb1
-rw-r--r--lib/chef/resource/reboot.rb2
-rw-r--r--lib/chef/resource/registry_key.rb1
-rw-r--r--lib/chef/resource/route.rb3
-rw-r--r--lib/chef/resource/ruby.rb1
-rw-r--r--lib/chef/resource/ruby_block.rb1
-rw-r--r--lib/chef/resource/scm.rb1
-rw-r--r--lib/chef/resource/script.rb1
-rw-r--r--lib/chef/resource/service.rb1
-rw-r--r--lib/chef/resource/subversion.rb1
-rw-r--r--lib/chef/resource/whyrun_safe_ruby_block.rb1
-rw-r--r--lib/chef/resource/windows_script.rb1
-rw-r--r--lib/chef/resource_definition.rb1
-rw-r--r--lib/chef/resource_reporter.rb2
-rw-r--r--lib/chef/resource_resolver.rb30
-rw-r--r--spec/data/lwrp/providers/buck_passer.rb20
-rw-r--r--spec/data/lwrp/providers/buck_passer_2.rb20
-rw-r--r--spec/data/lwrp/providers/embedded_resource_accesses_providers_scope.rb16
-rw-r--r--spec/data/lwrp_override/resources/foo.rb5
-rw-r--r--spec/integration/recipes/lwrp_inline_resources_spec.rb2
-rw-r--r--spec/integration/recipes/provider_choice.rb41
-rw-r--r--spec/integration/recipes/recipe_dsl_spec.rb259
-rw-r--r--spec/spec_helper.rb3
-rw-r--r--spec/support/lib/chef/resource/cat.rb1
-rw-r--r--spec/support/lib/chef/resource/one_two_three_four.rb2
-rw-r--r--spec/support/lib/chef/resource/zen_follower.rb1
-rw-r--r--spec/support/lib/chef/resource/zen_master.rb2
-rw-r--r--spec/support/shared/integration/integration_helper.rb11
-rw-r--r--spec/unit/guard_interpreter/resource_guard_interpreter_spec.rb2
-rw-r--r--spec/unit/lwrp_spec.rb144
-rw-r--r--spec/unit/recipe_spec.rb2
-rw-r--r--spec/unit/resource/batch_spec.rb1
-rw-r--r--spec/unit/resource/powershell_spec.rb1
-rw-r--r--spec/unit/resource_spec.rb64
-rw-r--r--tasks/external_tests.rb29
76 files changed, 1165 insertions, 303 deletions
diff --git a/.gitignore b/.gitignore
index ecba9f4030..6588499305 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,7 @@ pkg
*~
# you should check in your Gemfile.lock in applications, and not in gems
+external_tests/*.lock
Gemfile.lock
Gemfile.local
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a8d53622ef..a933d1b70a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -33,6 +33,8 @@
* [Issue #3055](https://github.com/chef/chef/issues/3055) Fix regex parsing for recipe failures on Windows
* [pr#3345](https://github.com/chef/chef/pull/3345) Windows Event log logger
* [pr#3336](https://github.com/chef/chef/pull/3336) Remote file understands UNC paths
+* [pr#3269](https://github.com/chef/chef/pull/3269): Deprecate automatic recipe
+ DSL for classes in `Chef::Resource`
## 12.3.0
@@ -78,14 +80,14 @@
## 12.1.2
* [Issue 3022](https://github.com/chef/chef/issues/3022): Homebrew Cask install fails
- FIXME (remove on 12.2.0 release): 3022 was only merged to 12-stable and #3077 or its descendant should fix this
+ FIXME (remove on 12.2.0 release): 3022 was only merged to 12-stable and #3077 or its descendant should fix this
* [Issue 3059](https://github.com/chef/chef/issues/3059): Chef 12.1.1 yum_package silently fails
* [Issue 3078](https://github.com/chef/chef/issues/3078): Compat break in audit-mode changes
## 12.1.1
* [**Phil Dibowitz**](https://github.com/jaymzh):
[Issue 3008](https://github.com/chef/chef/issues/3008) Allow people to pass in `source` to package
-* [Issue 3011](https://github.com/chef/chef/issues/3011) `package` provider base should include
+* [Issue 3011](https://github.com/chef/chef/issues/3011) `package` provider base should include
`Chef::Mixin::Command` as there are still providers that use it.
* [**Ranjib Dey**](https://github.com/ranjib):
[Issue 3019](https://github.com/chef/chef/issues/3019) Fix data fetching when explicit attributes are passed
diff --git a/DOC_CHANGES.md b/DOC_CHANGES.md
index 6b58871418..b0a8e0aaaf 100644
--- a/DOC_CHANGES.md
+++ b/DOC_CHANGES.md
@@ -5,3 +5,30 @@ Example Doc Change:
### Headline for the required change
Description of the required change.
-->
+
+### Resources must now use `provides` to declare recipe DSL
+
+Resources declared in `Chef::Resource` namespace will no longer get recipe DSL
+automatically. Instead, explicit `provides` is required in order to have DSL:
+
+```ruby
+module MyModule
+ class MyResource < Chef::Resource
+ provides :my_resource
+ end
+end
+```
+
+Authors of HWRPs need to be aware that in the future all resources and providers will be required to include a provides line. Starting with Chef 12.4.0 any HWRPs in the `Chef::Resource` or `Chef::Provider` namespaces that do not have provides lines will trigger deprecation warning messages when called. The LWRPBase code does `provides` automatically so LWRP authors and users who write classes that inherit from LWRPBase do not need to explicitly include provides lines.
+
+Users are encouraged to declare resources in their own namespaces instead of putting them in the special `Chef::Resource` namespace.
+
+### LWRPs are no longer automatically placed in the `Chef::Resource` namespace
+
+Starting with Chef 12.4.0, accessing an LWRP class by name from the `Chef::Resource` namespace will trigger a deprecation warning message. This means that if your cookbook includes the LWRP `mycookbook/resources/myresource.rb`, you will no longer be able to extend or reference `Chef::Resource::MycookbookMyresource` in Ruby code. LWRP recipe DSL does not change: the LWRP will still be available to recipes as `mycookbook_myresource`.
+
+You can still get the LWRP class by calling `Chef::ResourceResolver.resolve(:mycookbook_myresource)`.
+
+The primary aim here is clearing out the `Chef::Resource` namespace.
+
+References to these classes is deprecated (and will emit a warning) in Chef 12, and will be removed in Chef 13.
diff --git a/Gemfile b/Gemfile
index 1418235ebc..8cb8f22e32 100644
--- a/Gemfile
+++ b/Gemfile
@@ -10,6 +10,7 @@ end
group(:development, :test) do
gem "simplecov"
gem 'rack', "~> 1.5.1"
+ gem 'cheffish', "~> 1.2"
gem 'ruby-shadow', :platforms => :ruby unless RUBY_PLATFORM.downcase.match(/(aix|cygwin)/)
end
diff --git a/Rakefile b/Rakefile
index e9e345dbf8..d07ee0c247 100644
--- a/Rakefile
+++ b/Rakefile
@@ -22,7 +22,8 @@ require File.dirname(__FILE__) + '/lib/chef/version'
require 'rubygems'
require 'rubygems/package_task'
require 'rdoc/task'
-require './tasks/rspec.rb'
+require_relative 'tasks/rspec'
+require_relative 'tasks/external_tests'
GEM_NAME = "chef"
diff --git a/chef.gemspec b/chef.gemspec
index 7cf6380062..a02548c1da 100644
--- a/chef.gemspec
+++ b/chef.gemspec
@@ -44,9 +44,9 @@ Gem::Specification.new do |s|
s.add_dependency "syslog-logger", "~> 1.6"
s.add_development_dependency "rack"
+ s.add_development_dependency "cheffish", "~> 1.1"
- # Rake 10.2 drops Ruby 1.8 support
- s.add_development_dependency "rake", "~> 10.1.0"
+ s.add_development_dependency "rake", "~> 10.1"
s.bindir = "bin"
s.executables = %w( chef-client chef-solo knife chef-shell chef-apply )
diff --git a/external_tests/chef-rewind.gemfile b/external_tests/chef-rewind.gemfile
new file mode 100644
index 0000000000..39f7d6e0e8
--- /dev/null
+++ b/external_tests/chef-rewind.gemfile
@@ -0,0 +1,5 @@
+source 'https://rubygems.org'
+
+gemspec(name: 'chef', path: "../")
+
+gem 'chef-rewind', github: 'thommay/chef-rewind'
diff --git a/external_tests/chef-sugar.gemfile b/external_tests/chef-sugar.gemfile
new file mode 100644
index 0000000000..31ef3bb5b8
--- /dev/null
+++ b/external_tests/chef-sugar.gemfile
@@ -0,0 +1,6 @@
+source 'https://rubygems.org'
+
+gemspec(name: 'chef', path: "../")
+
+gem 'chef-sugar', github: 'sethvargo/chef-sugar'
+gem 'chefspec'
diff --git a/external_tests/chefspec.gemfile b/external_tests/chefspec.gemfile
new file mode 100644
index 0000000000..fb7878afbd
--- /dev/null
+++ b/external_tests/chefspec.gemfile
@@ -0,0 +1,7 @@
+source 'https://rubygems.org'
+
+gemspec(name: 'chef', path: "../")
+
+gem 'chefspec', github: 'sethvargo/chefspec', group: :development
+gem 'aruba'
+gem 'yard'
diff --git a/external_tests/foodcritic.gemfile b/external_tests/foodcritic.gemfile
new file mode 100644
index 0000000000..a2b71a0d8c
--- /dev/null
+++ b/external_tests/foodcritic.gemfile
@@ -0,0 +1,9 @@
+source 'https://rubygems.org'
+
+gemspec(name: 'chef', path: "../")
+
+gem 'foodcritic', github: 'acrmp/foodcritic'
+gem 'cucumber'
+gem 'rubocop'
+gem 'simplecov'
+gem 'minitest'
diff --git a/external_tests/halite.gemfile b/external_tests/halite.gemfile
new file mode 100644
index 0000000000..cd8cd05668
--- /dev/null
+++ b/external_tests/halite.gemfile
@@ -0,0 +1,8 @@
+source 'https://rubygems.org'
+
+gemspec(name: 'chef', path: "../")
+
+gem 'poise', github: 'poise/poise'
+gem 'halite', github: 'poise/halite'
+gem 'poise-boiler', github: 'poise/poise-boiler'
+gem 'rspec-command', github: 'coderanger/rspec-command'
diff --git a/external_tests/poise.gemfile b/external_tests/poise.gemfile
new file mode 100644
index 0000000000..7d274b7a29
--- /dev/null
+++ b/external_tests/poise.gemfile
@@ -0,0 +1,7 @@
+source 'https://rubygems.org'
+
+gemspec(name: 'chef', path: "../")
+
+gem 'poise', github: 'poise/poise'
+gem 'halite', github: 'poise/halite'
+gem 'poise-boiler', github: 'poise/poise-boiler'
diff --git a/lib/chef/dsl/definitions.rb b/lib/chef/dsl/definitions.rb
new file mode 100644
index 0000000000..1358f67720
--- /dev/null
+++ b/lib/chef/dsl/definitions.rb
@@ -0,0 +1,44 @@
+class Chef
+ module DSL
+ #
+ # Module containing a method for each declared definition
+ #
+ # Depends on declare_resource(name, created_at, &block)
+ #
+ # @api private
+ #
+ module Definitions
+ def self.add_definition(dsl_name)
+ module_eval <<-EOM, __FILE__, __LINE__+1
+ def #{dsl_name}(*args, &block)
+ evaluate_resource_definition(#{dsl_name.inspect}, *args, &block)
+ end
+ EOM
+ end
+
+ # @api private
+ def has_resource_definition?(name)
+ run_context.definitions.has_key?(name)
+ end
+
+ # Processes the arguments and block as a resource definition.
+ #
+ # @api private
+ def evaluate_resource_definition(definition_name, *args, &block)
+
+ # This dupes the high level object, but we still need to dup the params
+ new_def = run_context.definitions[definition_name].dup
+
+ new_def.params = new_def.params.dup
+ new_def.node = run_context.node
+ # This sets up the parameter overrides
+ new_def.instance_eval(&block) if block
+
+ new_recipe = Chef::Recipe.new(cookbook_name, recipe_name, run_context)
+ new_recipe.params = new_def.params
+ new_recipe.params[:name] = args[0]
+ new_recipe.instance_eval(&new_def.recipe)
+ end
+ end
+ end
+end
diff --git a/lib/chef/dsl/recipe.rb b/lib/chef/dsl/recipe.rb
index c22f053292..97f5088b5d 100644
--- a/lib/chef/dsl/recipe.rb
+++ b/lib/chef/dsl/recipe.rb
@@ -21,6 +21,8 @@ require 'chef/mixin/convert_to_class_name'
require 'chef/exceptions'
require 'chef/resource_builder'
require 'chef/mixin/shell_out'
+require 'chef/dsl/resources'
+require 'chef/dsl/definitions'
class Chef
module DSL
@@ -31,49 +33,62 @@ class Chef
module Recipe
include Chef::Mixin::ShellOut
- include Chef::Mixin::ConvertToClassName
+ # method_missing must live for backcompat purposes until Chef 13.
def method_missing(method_symbol, *args, &block)
- # If we have a definition that matches, we want to use that instead. This should
- # let you do some really crazy over-riding of "native" types, if you really want
- # to.
- if has_resource_definition?(method_symbol)
- evaluate_resource_definition(method_symbol, *args, &block)
- elsif have_resource_class_for?(method_symbol)
- # Otherwise, we're rocking the regular resource call route.
- declare_resource(method_symbol, args[0], caller[0], &block)
- else
- begin
- super
- rescue NoMethodError
- raise NoMethodError, "No resource or method named `#{method_symbol}' for #{describe_self_for_error}"
- rescue NameError
- raise NameError, "No resource, method, or local variable named `#{method_symbol}' for #{describe_self_for_error}"
- end
+ #
+ # If there is already DSL for this, someone must have called
+ # method_missing manually. Not a fan. Not. A. Fan.
+ #
+ if respond_to?(method_symbol)
+ Chef::Log.deprecation("Calling method_missing(#{method_symbol.inspect}) directly is deprecated in Chef 12 and will be removed in Chef 13.")
+ Chef::Log.deprecation("Use public_send() or send() instead.")
+ return send(method_symbol, *args, &block)
end
- end
-
- def has_resource_definition?(name)
- run_context.definitions.has_key?(name)
- end
-
- # Processes the arguments and block as a resource definition.
- def evaluate_resource_definition(definition_name, *args, &block)
- # This dupes the high level object, but we still need to dup the params
- new_def = run_context.definitions[definition_name].dup
+ #
+ # If a definition exists, then Chef::DSL::Definitions.add_definition was
+ # never called. DEPRECATED.
+ #
+ if run_context.definitions.has_key?(method_symbol.to_sym)
+ Chef::Log.deprecation("Definition #{method_symbol} (#{run_context.definitions[method_symbol.to_sym]}) was added to the run_context without calling Chef::DSL::Definitions.add_definition(#{method_symbol.to_sym.inspect}). This will become required in Chef 13.")
+ Chef::DSL::Definitions.add_definition(method_symbol)
+ return send(method_symbol, *args, &block)
+ end
- new_def.params = new_def.params.dup
- new_def.node = run_context.node
- # This sets up the parameter overrides
- new_def.instance_eval(&block) if block
+ #
+ # See if the resource exists anyway. If the user had set
+ # Chef::Resource::Blah = <resource>, a deprecation warning will be
+ # emitted and the DSL method 'blah' will be added to the DSL.
+ #
+ resource_class = Chef::ResourceResolver.new(run_context ? run_context.node : nil, method_symbol).resolve
+ if resource_class
+ #
+ # If the DSL method was *not* added, this is the case where the
+ # matching class implements 'provides?' and matches resources that it
+ # never declared "provides" for (which means we would never have
+ # created DSL). Anything where we don't create DSL is deprecated.
+ #
+ if !respond_to?(method_symbol)
+ Chef::Log.deprecation("#{resource_class} is marked as providing DSL #{method_symbol}, but provides #{method_symbol.inspect} was never called!")
+ Chef::Log.deprecation("In Chef 13, this will break: you must call provides to mark the names you provide, even if you also override provides? yourself.")
+ Chef::DSL::Resources.add_resource_dsl(method_symbol)
+ end
+ return send(method_symbol, *args, &block)
+ end
- new_recipe = Chef::Recipe.new(cookbook_name, recipe_name, run_context)
- new_recipe.params = new_def.params
- new_recipe.params[:name] = args[0]
- new_recipe.instance_eval(&new_def.recipe)
+ begin
+ super
+ rescue NoMethodError
+ raise NoMethodError, "No resource or method named `#{method_symbol}' for #{describe_self_for_error}"
+ rescue NameError
+ raise NameError, "No resource, method, or local variable named `#{method_symbol}' for #{describe_self_for_error}"
+ end
end
+ include Chef::DSL::Resources
+ include Chef::DSL::Definitions
+
#
# Instantiates a resource (via #build_resource), then adds it to the
# resource collection. Note that resource classes are looked up directly,
diff --git a/lib/chef/dsl/resources.rb b/lib/chef/dsl/resources.rb
new file mode 100644
index 0000000000..4072ff2c89
--- /dev/null
+++ b/lib/chef/dsl/resources.rb
@@ -0,0 +1,28 @@
+class Chef
+ module DSL
+ #
+ # Module containing a method for each globally declared Resource
+ #
+ # Depends on declare_resource(name, created_at, &block)
+ #
+ # @api private
+ module Resources
+ def self.add_resource_dsl(dsl_name)
+ begin
+ module_eval(<<-EOM, __FILE__, __LINE__+1)
+ def #{dsl_name}(name, created_at=nil, &block)
+ declare_resource(#{dsl_name.inspect}, name, created_at || caller[0], &block)
+ end
+ EOM
+ rescue SyntaxError
+ define_method(dsl_name.to_sym) do |name, created_at=nil, &block|
+ declare_resource(dsl_name, name, created_at || caller[0], &block)
+ end
+ end
+ end
+ def self.remove_resource_dsl(dsl_name)
+ remove_method(dsl_name) if method_defined?(dsl_name)
+ end
+ end
+ end
+end
diff --git a/lib/chef/mixin/provides.rb b/lib/chef/mixin/provides.rb
index bc25af62b2..c39c53a190 100644
--- a/lib/chef/mixin/provides.rb
+++ b/lib/chef/mixin/provides.rb
@@ -23,11 +23,18 @@ class Chef
node_map.set(short_name, true, opts, &block)
end
- # provides a node on the resource (early binding)
+ # Check whether this resource provides the resource_name DSL for the given
+ # node
def provides?(node, resource_name)
resource_name = resource_name.resource_name if resource_name.is_a?(Chef::Resource)
node_map.get(node, resource_name)
end
+
+ # Get the list of recipe DSL this resource is responsible for on the given
+ # node.
+ def provided_as(node)
+ node_map.list(node)
+ end
end
end
end
diff --git a/lib/chef/platform/provider_mapping.rb b/lib/chef/platform/provider_mapping.rb
index 0d7285729f..00458b6485 100644
--- a/lib/chef/platform/provider_mapping.rb
+++ b/lib/chef/platform/provider_mapping.rb
@@ -460,16 +460,20 @@ class Chef
pmap.has_key?(rtkey) ? pmap[rtkey] : nil
end
+ include Chef::Mixin::ConvertToClassName
+
def resource_matching_provider(platform, version, resource_type)
if resource_type.kind_of?(Chef::Resource)
+ class_name = resource_type.class.to_s.split('::').last
+
begin
- Chef::Provider.const_get(resource_type.class.to_s.split('::').last)
+ result = Chef::Provider.const_get(class_name)
+ Chef::Log.warn("Class Chef::Provider::#{class_name} does not declare 'provides #{convert_to_snake_case(class_name).to_sym.inspect}'.")
+ Chef::Log.warn("This will no longer work in Chef 13: you must use 'provides' to provide DSL.")
rescue NameError
- nil
end
- else
- nil
end
+ result
end
end
diff --git a/lib/chef/provider.rb b/lib/chef/provider.rb
index 65a56cf726..3ab4d4d2b1 100644
--- a/lib/chef/provider.rb
+++ b/lib/chef/provider.rb
@@ -191,5 +191,33 @@ class Chef
end
end
+ module DeprecatedLWRPClass
+ def const_missing(class_name)
+ if deprecated_constants[class_name.to_sym]
+ Chef::Log.deprecation("Using an LWRP provider by its name (#{class_name}) directly is no longer supported in Chef 12 and will be removed. Use Chef::ProviderResolver.new(node, resource, action) instead.")
+ deprecated_constants[class_name.to_sym]
+ else
+ raise NameError, "uninitialized constant Chef::Provider::#{class_name}"
+ end
+ end
+
+ # @api private
+ def register_deprecated_lwrp_class(provider_class, class_name)
+ # Register Chef::Provider::MyProvider with deprecation warnings if you
+ # try to access it
+ if Chef::Provider.const_defined?(class_name, false)
+ Chef::Log.warn "Chef::Provider::#{class_name} already exists! Cannot create deprecation class for #{provider_class}"
+ else
+ deprecated_constants[class_name.to_sym] = provider_class
+ end
+ end
+
+ private
+
+ def deprecated_constants
+ @deprecated_constants ||= {}
+ end
+ end
+ extend DeprecatedLWRPClass
end
end
diff --git a/lib/chef/provider/cron/unix.rb b/lib/chef/provider/cron/unix.rb
index 0750c0420b..01c61e4253 100644
--- a/lib/chef/provider/cron/unix.rb
+++ b/lib/chef/provider/cron/unix.rb
@@ -20,6 +20,7 @@
require 'chef/log'
require 'chef/provider'
+require 'chef/provider/cron'
class Chef
class Provider
diff --git a/lib/chef/provider/lwrp_base.rb b/lib/chef/provider/lwrp_base.rb
index 492ddda6da..38430ced6d 100644
--- a/lib/chef/provider/lwrp_base.rb
+++ b/lib/chef/provider/lwrp_base.rb
@@ -69,9 +69,6 @@ class Chef
end
- extend Chef::Mixin::ConvertToClassName
- extend Chef::Mixin::FromFile
-
include Chef::DSL::Recipe
# These were previously provided by Chef::Mixin::RecipeDefinitionDSLCore.
@@ -80,71 +77,92 @@ class Chef
include Chef::DSL::PlatformIntrospection
include Chef::DSL::DataQuery
- def self.build_from_file(cookbook_name, filename, run_context)
- provider_class = nil
- provider_name = filename_to_qualified_string(cookbook_name, filename)
+ # no-op `load_current_resource`. Allows simple LWRP providers to work
+ # without defining this method explicitly (silences
+ # Chef::Exceptions::Override exception)
+ def load_current_resource
+ end
+
+ # class methods
+ class <<self
+ include Chef::Mixin::ConvertToClassName
+ include Chef::Mixin::FromFile
+
+ def build_from_file(cookbook_name, filename, run_context)
+ if LWRPBase.loaded_lwrps[filename]
+ Chef::Log.info("LWRP provider #{filename} from cookbook #{cookbook_name} has already been loaded! Skipping the reload.")
+ return loaded_lwrps[filename]
+ end
- class_name = convert_to_class_name(provider_name)
+ resource_name = filename_to_qualified_string(cookbook_name, filename)
- if Chef::Provider.const_defined?(class_name, false)
- Chef::Log.info("#{class_name} light-weight provider is already initialized -- Skipping loading #{filename}!")
- Chef::Log.debug("Overriding already defined LWRPs is not supported anymore starting with Chef 12.")
- provider_class = Chef::Provider.const_get(class_name)
- else
+ # We load the class first to give it a chance to set its own name
provider_class = Class.new(self)
- Chef::Provider.const_set(class_name, provider_class)
+ provider_class.provides resource_name.to_sym
provider_class.class_from_file(filename)
- Chef::Log.debug("Loaded contents of #{filename} into a provider named #{provider_name} defined in Chef::Provider::#{class_name}")
- end
- provider_class
- end
+ # Respect resource_name set inside the LWRP
+ provider_class.instance_eval do
+ define_method(:to_s) do
+ "LWRP provider #{resource_name} from cookbook #{cookbook_name}"
+ end
+ define_method(:inspect) { to_s }
+ end
- # Enables inline evaluation of resources in provider actions.
- #
- # Without this option, any resources declared inside the LWRP are added
- # to the resource collection after the current position at the time the
- # action is executed. Because they are added to the primary resource
- # collection for the chef run, they can notify other resources outside
- # the LWRP, and potentially be notified by resources outside the LWRP
- # (but this is complicated by the fact that they don't exist until the
- # provider executes). In this mode, it is impossible to correctly set the
- # updated_by_last_action flag on the parent LWRP resource, since it
- # executes and returns before its component resources are run.
- #
- # With this option enabled, each action creates a temporary run_context
- # with its own resource collection, evaluates the action's code in that
- # context, and then converges the resources created. If any resources
- # were updated, then this provider's new_resource will be marked updated.
- #
- # In this mode, resources created within the LWRP cannot interact with
- # external resources via notifies, though notifications to other
- # resources within the LWRP will work. Delayed notifications are executed
- # at the conclusion of the provider's action, *not* at the end of the
- # main chef run.
- #
- # This mode of evaluation is experimental, but is believed to be a better
- # set of tradeoffs than the append-after mode, so it will likely become
- # the default in a future major release of Chef.
- #
- def self.use_inline_resources
- extend InlineResources::ClassMethods
- include InlineResources
- end
+ Chef::Log.debug("Loaded contents of #{filename} into provider #{resource_name} (#{provider_class})")
+
+ LWRPBase.loaded_lwrps[filename] = true
- # DSL for defining a provider's actions.
- def self.action(name, &block)
- define_method("action_#{name}") do
- instance_eval(&block)
+ Chef::Provider.register_deprecated_lwrp_class(provider_class, convert_to_class_name(resource_name))
+
+ provider_class
end
- end
- # no-op `load_current_resource`. Allows simple LWRP providers to work
- # without defining this method explicitly (silences
- # Chef::Exceptions::Override exception)
- def load_current_resource
- end
+ # Enables inline evaluation of resources in provider actions.
+ #
+ # Without this option, any resources declared inside the LWRP are added
+ # to the resource collection after the current position at the time the
+ # action is executed. Because they are added to the primary resource
+ # collection for the chef run, they can notify other resources outside
+ # the LWRP, and potentially be notified by resources outside the LWRP
+ # (but this is complicated by the fact that they don't exist until the
+ # provider executes). In this mode, it is impossible to correctly set the
+ # updated_by_last_action flag on the parent LWRP resource, since it
+ # executes and returns before its component resources are run.
+ #
+ # With this option enabled, each action creates a temporary run_context
+ # with its own resource collection, evaluates the action's code in that
+ # context, and then converges the resources created. If any resources
+ # were updated, then this provider's new_resource will be marked updated.
+ #
+ # In this mode, resources created within the LWRP cannot interact with
+ # external resources via notifies, though notifications to other
+ # resources within the LWRP will work. Delayed notifications are executed
+ # at the conclusion of the provider's action, *not* at the end of the
+ # main chef run.
+ #
+ # This mode of evaluation is experimental, but is believed to be a better
+ # set of tradeoffs than the append-after mode, so it will likely become
+ # the default in a future major release of Chef.
+ #
+ def use_inline_resources
+ extend InlineResources::ClassMethods
+ include InlineResources
+ end
+
+ # DSL for defining a provider's actions.
+ def action(name, &block)
+ define_method("action_#{name}") do
+ instance_eval(&block)
+ end
+ end
+
+ protected
+ def loaded_lwrps
+ @loaded_lwrps ||= {}
+ end
+ end
end
end
end
diff --git a/lib/chef/provider/ohai.rb b/lib/chef/provider/ohai.rb
index a6b5ab5daa..b7f4aa704b 100644
--- a/lib/chef/provider/ohai.rb
+++ b/lib/chef/provider/ohai.rb
@@ -21,6 +21,7 @@ require 'ohai'
class Chef
class Provider
class Ohai < Chef::Provider
+ provides :ohai
def whyrun_supported?
true
diff --git a/lib/chef/provider/reboot.rb b/lib/chef/provider/reboot.rb
index 8dde4653ec..22e77dcc13 100644
--- a/lib/chef/provider/reboot.rb
+++ b/lib/chef/provider/reboot.rb
@@ -22,6 +22,7 @@ require 'chef/provider'
class Chef
class Provider
class Reboot < Chef::Provider
+ provides :reboot
def whyrun_supported?
true
diff --git a/lib/chef/provider/registry_key.rb b/lib/chef/provider/registry_key.rb
index 94f4e2655b..cd62f7c56f 100644
--- a/lib/chef/provider/registry_key.rb
+++ b/lib/chef/provider/registry_key.rb
@@ -31,6 +31,8 @@ class Chef
class Provider
class RegistryKey < Chef::Provider
+ provides :registry_key
+
include Chef::Mixin::Checksum
def whyrun_supported?
diff --git a/lib/chef/provider/remote_file.rb b/lib/chef/provider/remote_file.rb
index da2573dacb..c4643edc0b 100644
--- a/lib/chef/provider/remote_file.rb
+++ b/lib/chef/provider/remote_file.rb
@@ -24,6 +24,7 @@ require 'chef/deprecation/warnings'
class Chef
class Provider
class RemoteFile < Chef::Provider::File
+ provides :remote_file
extend Chef::Deprecation::Warnings
include Chef::Deprecation::Provider::RemoteFile
diff --git a/lib/chef/provider_resolver.rb b/lib/chef/provider_resolver.rb
index 5e887225e4..7c7bac0443 100644
--- a/lib/chef/provider_resolver.rb
+++ b/lib/chef/provider_resolver.rb
@@ -20,6 +20,93 @@ require 'chef/exceptions'
require 'chef/platform/provider_priority_map'
class Chef
+ #
+ # Provider Resolution
+ # ===================
+ #
+ # When you type `service 'myservice' { action :restart }` in a recipe, a whole
+ # string of events happens eventually leading to convergence. The overview of
+ # that process is described in `Chef::DSL::Recipe`. Provider resolution is
+ # the process of taking a Resource object and an action, and determining the
+ # Provider class that should be instantiated to handle the action.
+ #
+ # The process happens in three steps:
+ #
+ # Explicit Provider on the Resource
+ # ---------------------------------
+ # If the resource has its `provider` set, that is used.
+ #
+ # Dynamic Provider Matches
+ # ------------------------
+ # In this stage, we call `provides?` to see if the Provider supports the
+ # resource on this platform, and then we call `supports?` to determine if it
+ # can handle the action. It's a little more complicated than that, though:
+ #
+ # ### Provider.provides?
+ #
+ # First, we go through all known provider classes (all descendants of
+ # `Chef::Provider`), and call `provides?(node, resource)` to determine if it
+ # supports this action for this resource on this OS. We get a list of all
+ # matches.
+ #
+ # #### Defining provides
+ #
+ # The typical way of getting `provides?` is for the Provider class to call
+ # `provides :name`.
+ #
+ # The Provider may pass the OS, platform family, platform, and platform version
+ # to `provides`, and they will be matched against the values in the `node`
+ # object. The Provider may also pass a block, which allows for custom logic
+ # to decide whether it provides the resource or not.
+ #
+ # Some Providers also override `provides?` with custom logic.
+ #
+ # ### Provider.supports?
+ #
+ # Once we have the list of willing providers, we filter it by calling their
+ # `supports?(resource, action)` method to see if they support the specific
+ # action (`:create`, `:delete`) or not.
+ #
+ # If no provider supports the specific action, we fall back to the full list
+ # of matches from step 1. (TODO The comment says it's for why run. I'm not
+ # sure what that means specifically yet.)
+ #
+ # ### Priority lists: Chef.get_provider_priority_array
+ #
+ # Once we have the list of matches, we look at `Chef.get_provider_priority_array(node, resource)`
+ # to see if anyone has set a *priority list*. This method takes
+ # the the first matching priority list for this OS (which is the last list
+ # that was registered).
+ #
+ # If any of our matches are on the priority list, we take the first one.
+ #
+ # If there is no priority list or no matches on it, we take the first result
+ # alphabetically by class name.
+ #
+ # Chef::Platform Provider Map
+ # ---------------------------
+ # If we still have no matches, we try `Chef::Platform.find_provider_for_node(node, resource)`.
+ # This does two new things:
+ #
+ # ### System Provider Map
+ #
+ # The system provider map is a large Hash loaded during `require` time,
+ # which shows system-specific providers by os/platform, and platform_version.
+ # It keys off of `node[:platform] || node[:os]`, and `node[:platform_version]
+ # || node[:os_version] || node[:os_release]`. The version uses typical gem
+ # constraints like > and <=.
+ #
+ # The first platform+version match wins over the first platform-only match,
+ # which wins over the default.
+ #
+ # ### Chef::Provider::FooBar
+ #
+ # As a last resort, if there are *still* no classes, the system transforms the
+ # DSL name `foo_bar` into `Chef::Provider::FooBar`, and returns the class if
+ # it is there and descends from `Chef::Provider`.
+ #
+ # NOTE: this behavior is now deprecated.
+ #
class ProviderResolver
attr_reader :node
diff --git a/lib/chef/resource.rb b/lib/chef/resource.rb
index 3d319617d1..2f5c2b7798 100644
--- a/lib/chef/resource.rb
+++ b/lib/chef/resource.rb
@@ -22,6 +22,7 @@ require 'chef/dsl/platform_introspection'
require 'chef/dsl/data_query'
require 'chef/dsl/registry_helper'
require 'chef/dsl/reboot_pending'
+require 'chef/dsl/resources'
require 'chef/mixin/convert_to_class_name'
require 'chef/guard_interpreter/resource_guard_interpreter'
require 'chef/resource/conditional'
@@ -31,6 +32,8 @@ require 'chef/node_map'
require 'chef/node'
require 'chef/platform'
require 'chef/resource/resource_notification'
+require 'chef/provider_resolver'
+require 'chef/resource_resolver'
require 'chef/mixin/deprecation'
require 'chef/mixin/provides'
@@ -604,7 +607,7 @@ class Chef
return "suppressed sensitive resource output" if sensitive
ivars = instance_variables.map { |ivar| ivar.to_sym } - HIDDEN_IVARS
text = "# Declared in #{@source_line}\n\n"
- text << self.class.dsl_name + "(\"#{name}\") do\n"
+ text << "#{resource_name}(\"#{name}\") do\n"
ivars.each do |ivar|
if (value = instance_variable_get(ivar)) && !(value.respond_to?(:empty?) && value.empty?)
value_string = value.respond_to?(:to_text) ? value.to_text : value.inspect
@@ -820,7 +823,11 @@ class Chef
#
# @return [String] The DSL name of this resource.
def self.dsl_name
- convert_to_snake_case(name, 'Chef::Resource')
+ Chef::Log.deprecation "Resource.dsl_name is deprecated and will be removed in Chef 11. Use resource.resource_name instead."
+ if name
+ name = self.name.split('::')[-1]
+ convert_to_snake_case(name)
+ end
end
#
@@ -969,6 +976,12 @@ class Chef
end
end
+ def self.provides(name, *args, &block)
+ result = super
+ Chef::DSL::Resources.add_resource_dsl(name)
+ result
+ end
+
# Helper for #notifies
def validate_resource_spec!(resource_spec)
run_context.resource_collection.validate_lookup_spec!(resource_spec)
@@ -1025,7 +1038,6 @@ class Chef
end
def provider_for_action(action)
- require 'chef/provider_resolver'
provider = Chef::ProviderResolver.new(node, self, action).resolve.new(self, run_context)
provider.action = action
provider
@@ -1099,30 +1111,68 @@ class Chef
# === Returns
# <Chef::Resource>:: returns the proper Chef::Resource class
def self.resource_for_node(short_name, node)
- require 'chef/resource_resolver'
klass = Chef::ResourceResolver.new(node, short_name).resolve
raise Chef::Exceptions::NoSuchResourceType.new(short_name, node) if klass.nil?
klass
end
+ #
# Returns the class of a Chef::Resource based on the short name
+ # Only returns the *canonical* class with the given name, not the one that
+ # would be picked by the ResourceResolver.
+ #
# ==== Parameters
# short_name<Symbol>:: short_name of the resource (ie :directory)
#
# === Returns
# <Chef::Resource>:: returns the proper Chef::Resource class
+ #
+ # @deprecated Chef::Resource::FooBar will no longer mean anything special in
+ # Chef 13. Use `resource_for_node` instead.
def self.resource_matching_short_name(short_name)
begin
rname = convert_to_class_name(short_name.to_s)
- Chef::Resource.const_get(rname)
+ result = Chef::Resource.const_get(rname)
+ if result <= Chef::Resource
+ Chef::Log.deprecation("Class Chef::Resource::#{rname} does not declare 'provides #{short_name.inspect}'.")
+ Chef::Log.deprecation("This will no longer work in Chef 13: you must use 'provides' to provide DSL.")
+ result
+ end
rescue NameError
nil
end
end
- private
+ # Implement deprecated LWRP class
+ module DeprecatedLWRPClass
+ # @api private
+ def register_deprecated_lwrp_class(resource_class, class_name)
+ if Chef::Resource.const_defined?(class_name, false)
+ Chef::Log.warn "#{class_name} already exists! Cannot create deprecation class for #{resource_class}"
+ else
+ deprecated_constants[class_name.to_sym] = resource_class
+ end
+ end
+
+ def const_missing(class_name)
+ if deprecated_constants[class_name.to_sym]
+ Chef::Log.deprecation("Using an LWRP by its name (#{class_name}) directly is no longer supported in Chef 12 and will be removed. Use Chef::Resource.resource_for_node(node, name) instead.")
+ deprecated_constants[class_name.to_sym]
+ else
+ raise NameError, "uninitialized constant Chef::Resource::#{class_name}"
+ end
+ end
+
+ private
+
+ def deprecated_constants
+ @deprecated_constants ||= {}
+ end
+ end
+ extend DeprecatedLWRPClass
- def lookup_provider_constant(name)
+ # @api private
+ def lookup_provider_constant(name, action=:nothing)
begin
self.class.provider_base.const_get(convert_to_class_name(name.to_s))
rescue NameError => e
diff --git a/lib/chef/resource/bash.rb b/lib/chef/resource/bash.rb
index 0add0ce501..366d8c7bd6 100644
--- a/lib/chef/resource/bash.rb
+++ b/lib/chef/resource/bash.rb
@@ -22,6 +22,7 @@ require 'chef/provider/script'
class Chef
class Resource
class Bash < Chef::Resource::Script
+ provides :bash
def initialize(name, run_context=nil)
super
diff --git a/lib/chef/resource/bff_package.rb b/lib/chef/resource/bff_package.rb
index 917f0d1d50..d4139e7ffe 100644
--- a/lib/chef/resource/bff_package.rb
+++ b/lib/chef/resource/bff_package.rb
@@ -22,6 +22,7 @@ require 'chef/provider/package/aix'
class Chef
class Resource
class BffPackage < Chef::Resource::Package
+ provides :bff_package
def initialize(name, run_context=nil)
super
@@ -31,5 +32,3 @@ class Chef
end
end
end
-
-
diff --git a/lib/chef/resource/breakpoint.rb b/lib/chef/resource/breakpoint.rb
index b2210262d2..5a55858f71 100644
--- a/lib/chef/resource/breakpoint.rb
+++ b/lib/chef/resource/breakpoint.rb
@@ -22,6 +22,7 @@ require 'chef/resource'
class Chef
class Resource
class Breakpoint < Chef::Resource
+ provides :breakpoint
def initialize(action="break", *args)
@name = caller.first
diff --git a/lib/chef/resource/csh.rb b/lib/chef/resource/csh.rb
index 36659c349b..d37f1a8e0c 100644
--- a/lib/chef/resource/csh.rb
+++ b/lib/chef/resource/csh.rb
@@ -22,6 +22,7 @@ require 'chef/provider/script'
class Chef
class Resource
class Csh < Chef::Resource::Script
+ provides :csh
def initialize(name, run_context=nil)
super
diff --git a/lib/chef/resource/deploy.rb b/lib/chef/resource/deploy.rb
index 4252aa230f..55e3547b25 100644
--- a/lib/chef/resource/deploy.rb
+++ b/lib/chef/resource/deploy.rb
@@ -50,6 +50,7 @@ class Chef
# release directory. Callback files can contain chef code (resources, etc.)
#
class Deploy < Chef::Resource
+ provides :deploy
provider_base Chef::Provider::Deploy
diff --git a/lib/chef/resource/erl_call.rb b/lib/chef/resource/erl_call.rb
index 24009d51c7..75422c55a1 100644
--- a/lib/chef/resource/erl_call.rb
+++ b/lib/chef/resource/erl_call.rb
@@ -23,6 +23,7 @@ require 'chef/provider/erl_call'
class Chef
class Resource
class ErlCall < Chef::Resource
+ provides :erl_call
# erl_call : http://erlang.org/doc/man/erl_call.html
diff --git a/lib/chef/resource/execute.rb b/lib/chef/resource/execute.rb
index 9f8b629fb8..8fc97d748f 100644
--- a/lib/chef/resource/execute.rb
+++ b/lib/chef/resource/execute.rb
@@ -23,6 +23,7 @@ require 'chef/provider/execute'
class Chef
class Resource
class Execute < Chef::Resource
+ provides :execute
identity_attr :command
diff --git a/lib/chef/resource/http_request.rb b/lib/chef/resource/http_request.rb
index ccb0a26629..5986ebd4a0 100644
--- a/lib/chef/resource/http_request.rb
+++ b/lib/chef/resource/http_request.rb
@@ -23,6 +23,7 @@ require 'chef/provider/http_request'
class Chef
class Resource
class HttpRequest < Chef::Resource
+ provides :http_request
identity_attr :url
diff --git a/lib/chef/resource/ifconfig.rb b/lib/chef/resource/ifconfig.rb
index c289ddadbe..60feba1704 100644
--- a/lib/chef/resource/ifconfig.rb
+++ b/lib/chef/resource/ifconfig.rb
@@ -22,6 +22,7 @@ require 'chef/resource'
class Chef
class Resource
class Ifconfig < Chef::Resource
+ provides :ifconfig
identity_attr :device
@@ -145,5 +146,3 @@ class Chef
end
end
-
-
diff --git a/lib/chef/resource/log.rb b/lib/chef/resource/log.rb
index 7f970a87a4..87be01aaa9 100644
--- a/lib/chef/resource/log.rb
+++ b/lib/chef/resource/log.rb
@@ -23,6 +23,7 @@ require 'chef/provider/log'
class Chef
class Resource
class Log < Chef::Resource
+ provides :log
identity_attr :message
@@ -75,5 +76,3 @@ class Chef
end
end
end
-
-
diff --git a/lib/chef/resource/lwrp_base.rb b/lib/chef/resource/lwrp_base.rb
index ce72e98028..c79c205285 100644
--- a/lib/chef/resource/lwrp_base.rb
+++ b/lib/chef/resource/lwrp_base.rb
@@ -19,6 +19,13 @@
#
require 'chef/resource'
+require 'chef/resource_resolver'
+require 'chef/node'
+require 'chef/log'
+require 'chef/exceptions'
+require 'chef/mixin/convert_to_class_name'
+require 'chef/mixin/from_file'
+require 'chef/mixin/params_validate' # for DelayedEvaluator
class Chef
class Resource
@@ -30,121 +37,134 @@ class Chef
NULL_ARG = Object.new
- extend Chef::Mixin::ConvertToClassName
- extend Chef::Mixin::FromFile
+ # Class methods
+ class <<self
- # Evaluates the LWRP resource file and instantiates a new Resource class.
- def self.build_from_file(cookbook_name, filename, run_context)
- resource_class = nil
- rname = filename_to_qualified_string(cookbook_name, filename)
+ include Chef::Mixin::ConvertToClassName
+ include Chef::Mixin::FromFile
- class_name = convert_to_class_name(rname)
- if Resource.const_defined?(class_name, false)
- Chef::Log.info("#{class_name} light-weight resource is already initialized -- Skipping loading #{filename}!")
- Chef::Log.debug("Overriding already defined LWRPs is not supported anymore starting with Chef 12.")
- resource_class = Resource.const_get(class_name)
- else
- resource_class = Class.new(self)
+ attr_accessor :loaded_lwrps
+
+ def build_from_file(cookbook_name, filename, run_context)
+ if LWRPBase.loaded_lwrps[filename]
+ Chef::Log.info("LWRP resource #{filename} from cookbook #{cookbook_name} has already been loaded! Skipping the reload.")
+ return loaded_lwrps[filename]
+ end
- Chef::Resource.const_set(class_name, resource_class)
- resource_class.resource_name = rname
+ resource_name = filename_to_qualified_string(cookbook_name, filename)
+
+ # We load the class first to give it a chance to set its own name
+ resource_class = Class.new(self)
+ resource_class.resource_name = resource_name
resource_class.run_context = run_context
+ resource_class.provides resource_name.to_sym
resource_class.class_from_file(filename)
- Chef::Log.debug("Loaded contents of #{filename} into a resource named #{rname} defined in Chef::Resource::#{class_name}")
- end
+ # Respect resource_name set inside the LWRP
+ resource_class.instance_eval do
+ define_method(:to_s) do
+ "LWRP resource #{resource_name} from cookbook #{cookbook_name}"
+ end
+ define_method(:inspect) { to_s }
+ end
- resource_class
- end
+ Chef::Log.debug("Loaded contents of #{filename} into resource #{resource_name} (#{resource_class})")
- # Set the resource name for this LWRP
- def self.resource_name(arg = NULL_ARG)
- if arg.equal?(NULL_ARG)
- @resource_name
- else
- @resource_name = arg
- end
- end
+ LWRPBase.loaded_lwrps[filename] = true
- class << self
- alias_method :resource_name=, :resource_name
- end
+ Chef::Resource.register_deprecated_lwrp_class(resource_class, convert_to_class_name(resource_name))
- # Define an attribute on this resource, including optional validation
- # parameters.
- def self.attribute(attr_name, validation_opts={})
- define_method(attr_name) do |arg=nil|
- set_or_return(attr_name.to_sym, arg, validation_opts)
+ resource_class
end
- end
- # Sets the default action
- def self.default_action(action_name=NULL_ARG)
- unless action_name.equal?(NULL_ARG)
- @actions ||= []
- if action_name.is_a?(Array)
- action = action_name.map { |arg| arg.to_sym }
- @actions = actions | action
- @default_action = action
+ def resource_name(arg = NULL_ARG)
+ if arg.equal?(NULL_ARG)
+ @resource_name
else
- action = action_name.to_sym
- @actions.push(action) unless @actions.include?(action)
- @default_action = action
+ @resource_name = arg
end
end
- @default_action ||= from_superclass(:default_action)
- end
+ alias_method :resource_name=, :resource_name
+
+ # Define an attribute on this resource, including optional validation
+ # parameters.
+ def attribute(attr_name, validation_opts={})
+ define_method(attr_name) do |arg=nil|
+ set_or_return(attr_name.to_sym, arg, validation_opts)
+ end
+ end
+
+ # Sets the default action
+ def default_action(action_name=NULL_ARG)
+ unless action_name.equal?(NULL_ARG)
+ @actions ||= []
+ if action_name.is_a?(Array)
+ action = action_name.map { |arg| arg.to_sym }
+ @actions = actions | action
+ @default_action = action
+ else
+ action = action_name.to_sym
+ @actions.push(action) unless @actions.include?(action)
+ @default_action = action
+ end
+ end
+
+ @default_action ||= from_superclass(:default_action)
+ end
- # Adds +action_names+ to the list of valid actions for this resource.
- def self.actions(*action_names)
- if action_names.empty?
- defined?(@actions) ? @actions : from_superclass(:actions, []).dup
- else
- # BC-compat way for checking if actions have already been defined
- if defined?(@actions)
- @actions.push(*action_names)
+ # Adds +action_names+ to the list of valid actions for this resource.
+ def actions(*action_names)
+ if action_names.empty?
+ defined?(@actions) ? @actions : from_superclass(:actions, []).dup
else
- @actions = action_names
+ # BC-compat way for checking if actions have already been defined
+ if defined?(@actions)
+ @actions.push(*action_names)
+ else
+ @actions = action_names
+ end
end
end
- end
- # @deprecated
- def self.valid_actions(*args)
- Chef::Log.warn("`valid_actions' is deprecated, please use actions `instead'!")
- actions(*args)
- end
+ # @deprecated
+ def valid_actions(*args)
+ Chef::Log.warn("`valid_actions' is deprecated, please use actions `instead'!")
+ actions(*args)
+ end
- # Set the run context on the class. Used to provide access to the node
- # during class definition.
- def self.run_context=(run_context)
- @run_context = run_context
- end
+ # Set the run context on the class. Used to provide access to the node
+ # during class definition.
+ attr_accessor :run_context
- def self.run_context
- @run_context
- end
+ def node
+ run_context ? run_context.node : nil
+ end
- def self.node
- run_context.node
- end
+ def lazy(&block)
+ DelayedEvaluator.new(&block)
+ end
- def self.lazy(&block)
- DelayedEvaluator.new(&block)
- end
+ protected
- private
+ def loaded_lwrps
+ @loaded_lwrps ||= {}
+ end
+
+ private
- # Get the value from the superclass, if it responds, otherwise return
- # +nil+. Since class instance variables are **not** inherited upon
- # subclassing, this is a required check to ensure Chef pulls the
- # +default_action+ and other DSL-y methods when extending LWRP::Base.
- def self.from_superclass(m, default = nil)
- return default if superclass == Chef::Resource::LWRPBase
- superclass.respond_to?(m) ? superclass.send(m) : default
+ # Get the value from the superclass, if it responds, otherwise return
+ # +nil+. Since class instance variables are **not** inherited upon
+ # subclassing, this is a required check to ensure Chef pulls the
+ # +default_action+ and other DSL-y methods when extending LWRP::Base.
+ def from_superclass(m, default = nil)
+ return default if superclass == Chef::Resource::LWRPBase
+ superclass.respond_to?(m) ? superclass.send(m) : default
+ end
end
+ private
+
# Default initializer. Sets the default action and allowed actions.
def initialize(name, run_context=nil)
super(name, run_context)
@@ -159,7 +179,6 @@ class Chef
@action = self.class.default_action
allowed_actions.push(self.class.actions).flatten!
end
-
end
end
end
diff --git a/lib/chef/resource/ohai.rb b/lib/chef/resource/ohai.rb
index b567db40f9..e2d12ce395 100644
--- a/lib/chef/resource/ohai.rb
+++ b/lib/chef/resource/ohai.rb
@@ -20,6 +20,7 @@
class Chef
class Resource
class Ohai < Chef::Resource
+ provides :ohai
identity_attr :name
diff --git a/lib/chef/resource/package.rb b/lib/chef/resource/package.rb
index f4f49b543b..5bea894a02 100644
--- a/lib/chef/resource/package.rb
+++ b/lib/chef/resource/package.rb
@@ -22,6 +22,7 @@ require 'chef/resource'
class Chef
class Resource
class Package < Chef::Resource
+ provides :package
identity_attr :package_name
diff --git a/lib/chef/resource/perl.rb b/lib/chef/resource/perl.rb
index c4bdb6e130..cb741d145a 100644
--- a/lib/chef/resource/perl.rb
+++ b/lib/chef/resource/perl.rb
@@ -22,6 +22,7 @@ require 'chef/provider/script'
class Chef
class Resource
class Perl < Chef::Resource::Script
+ provides :perl
def initialize(name, run_context=nil)
super
diff --git a/lib/chef/resource/portage_package.rb b/lib/chef/resource/portage_package.rb
index 42c03560b6..b03b69796a 100644
--- a/lib/chef/resource/portage_package.rb
+++ b/lib/chef/resource/portage_package.rb
@@ -21,6 +21,7 @@ require 'chef/resource/package'
class Chef
class Resource
class PortagePackage < Chef::Resource::Package
+ provides :portage_package
def initialize(name, run_context=nil)
super
diff --git a/lib/chef/resource/python.rb b/lib/chef/resource/python.rb
index b1f23d13ce..fffd4d75f6 100644
--- a/lib/chef/resource/python.rb
+++ b/lib/chef/resource/python.rb
@@ -21,6 +21,7 @@ require 'chef/provider/script'
class Chef
class Resource
class Python < Chef::Resource::Script
+ provides :python
def initialize(name, run_context=nil)
super
diff --git a/lib/chef/resource/reboot.rb b/lib/chef/resource/reboot.rb
index c111b23d2e..7cd53450ed 100644
--- a/lib/chef/resource/reboot.rb
+++ b/lib/chef/resource/reboot.rb
@@ -24,6 +24,8 @@ require 'chef/resource'
class Chef
class Resource
class Reboot < Chef::Resource
+ provides :reboot
+
def initialize(name, run_context=nil)
super
@resource_name = :reboot
diff --git a/lib/chef/resource/registry_key.rb b/lib/chef/resource/registry_key.rb
index 8126ccf126..cc8d05dd53 100644
--- a/lib/chef/resource/registry_key.rb
+++ b/lib/chef/resource/registry_key.rb
@@ -22,6 +22,7 @@ require 'chef/digester'
class Chef
class Resource
class RegistryKey < Chef::Resource
+ provides :registry_key
identity_attr :key
state_attrs :values
diff --git a/lib/chef/resource/route.rb b/lib/chef/resource/route.rb
index 942905d138..8f9172060b 100644
--- a/lib/chef/resource/route.rb
+++ b/lib/chef/resource/route.rb
@@ -22,6 +22,7 @@ require 'chef/resource'
class Chef
class Resource
class Route < Chef::Resource
+ provides :route
identity_attr :target
@@ -136,5 +137,3 @@ class Chef
end
end
end
-
-
diff --git a/lib/chef/resource/ruby.rb b/lib/chef/resource/ruby.rb
index 2b2aa0249d..2b7644562a 100644
--- a/lib/chef/resource/ruby.rb
+++ b/lib/chef/resource/ruby.rb
@@ -22,6 +22,7 @@ require 'chef/provider/script'
class Chef
class Resource
class Ruby < Chef::Resource::Script
+ provides :ruby
def initialize(name, run_context=nil)
super
diff --git a/lib/chef/resource/ruby_block.rb b/lib/chef/resource/ruby_block.rb
index a9cbf234cf..07eec5599d 100644
--- a/lib/chef/resource/ruby_block.rb
+++ b/lib/chef/resource/ruby_block.rb
@@ -23,6 +23,7 @@ require 'chef/provider/ruby_block'
class Chef
class Resource
class RubyBlock < Chef::Resource
+ provides :ruby_block
identity_attr :block_name
diff --git a/lib/chef/resource/scm.rb b/lib/chef/resource/scm.rb
index 87c217b4cc..d41764d595 100644
--- a/lib/chef/resource/scm.rb
+++ b/lib/chef/resource/scm.rb
@@ -22,6 +22,7 @@ require 'chef/resource'
class Chef
class Resource
class Scm < Chef::Resource
+ provides :scm
identity_attr :destination
diff --git a/lib/chef/resource/script.rb b/lib/chef/resource/script.rb
index fd0fd5a7fd..e2fbb29d0f 100644
--- a/lib/chef/resource/script.rb
+++ b/lib/chef/resource/script.rb
@@ -23,6 +23,7 @@ require 'chef/provider/script'
class Chef
class Resource
class Script < Chef::Resource::Execute
+ provides :script
# Chef-13: go back to using :name as the identity attr
identity_attr :command
diff --git a/lib/chef/resource/service.rb b/lib/chef/resource/service.rb
index 36df7c859a..47d2ab9e12 100644
--- a/lib/chef/resource/service.rb
+++ b/lib/chef/resource/service.rb
@@ -22,6 +22,7 @@ require 'chef/resource'
class Chef
class Resource
class Service < Chef::Resource
+ provides :service
identity_attr :service_name
diff --git a/lib/chef/resource/subversion.rb b/lib/chef/resource/subversion.rb
index 3afbe0baaf..e5a2e9d1a5 100644
--- a/lib/chef/resource/subversion.rb
+++ b/lib/chef/resource/subversion.rb
@@ -22,6 +22,7 @@ require "chef/resource/scm"
class Chef
class Resource
class Subversion < Chef::Resource::Scm
+ provides :subversion
def initialize(name, run_context=nil)
super
diff --git a/lib/chef/resource/whyrun_safe_ruby_block.rb b/lib/chef/resource/whyrun_safe_ruby_block.rb
index 6fa5383f5d..f512dc67fc 100644
--- a/lib/chef/resource/whyrun_safe_ruby_block.rb
+++ b/lib/chef/resource/whyrun_safe_ruby_block.rb
@@ -19,6 +19,7 @@
class Chef
class Resource
class WhyrunSafeRubyBlock < Chef::Resource::RubyBlock
+ provides :whyrun_safe_ruby_block
def initialize(name, run_context=nil)
super
diff --git a/lib/chef/resource/windows_script.rb b/lib/chef/resource/windows_script.rb
index 6b0827b77c..1af7a48fe4 100644
--- a/lib/chef/resource/windows_script.rb
+++ b/lib/chef/resource/windows_script.rb
@@ -22,6 +22,7 @@ require 'chef/mixin/windows_architecture_helper'
class Chef
class Resource
class WindowsScript < Chef::Resource::Script
+ # This is an abstract resource meant to be subclasses; thus no 'provides'
set_guard_inherited_attributes(:architecture)
diff --git a/lib/chef/resource_definition.rb b/lib/chef/resource_definition.rb
index 9d6844129c..cffabb6786 100644
--- a/lib/chef/resource_definition.rb
+++ b/lib/chef/resource_definition.rb
@@ -50,6 +50,7 @@ class Chef
else
raise ArgumentError, "You must pass a block to a definition."
end
+ Chef::DSL::Definitions.add_definition(name)
true
end
diff --git a/lib/chef/resource_reporter.rb b/lib/chef/resource_reporter.rb
index 96cc01d814..7829bb4d70 100644
--- a/lib/chef/resource_reporter.rb
+++ b/lib/chef/resource_reporter.rb
@@ -59,7 +59,7 @@ class Chef
# attrs.
def for_json
as_hash = {}
- as_hash["type"] = new_resource.class.dsl_name
+ as_hash["type"] = new_resource.resource_name.to_sym
as_hash["name"] = new_resource.name.to_s
as_hash["id"] = new_resource.identity.to_s
as_hash["after"] = new_resource.state_for_resource_reporter
diff --git a/lib/chef/resource_resolver.rb b/lib/chef/resource_resolver.rb
index ff9d7aeb9f..a987b236c2 100644
--- a/lib/chef/resource_resolver.rb
+++ b/lib/chef/resource_resolver.rb
@@ -18,9 +18,11 @@
require 'chef/exceptions'
require 'chef/platform/resource_priority_map'
+require 'chef/mixin/convert_to_class_name'
class Chef
class ResourceResolver
+ include Chef::Mixin::ConvertToClassName
attr_reader :node
attr_reader :resource
@@ -28,7 +30,7 @@ class Chef
def initialize(node, resource)
@node = node
- @resource = resource
+ @resource = resource.to_sym
end
# return a deterministically sorted list of Chef::Resource subclasses
@@ -37,8 +39,8 @@ class Chef
end
def resolve
- maybe_dynamic_resource_resolution(resource) ||
- maybe_chef_platform_lookup(resource)
+ maybe_dynamic_resource_resolution ||
+ maybe_chef_platform_lookup
end
# this cut looks at if the resource can handle the resource type on the node
@@ -47,21 +49,29 @@ class Chef
resources.select do |klass|
klass.provides?(node, resource)
end.sort {|a,b| a.to_s <=> b.to_s }
- @enabled_handlers
+ end
+
+ #
+ # Resolve a resource by name.
+ #
+ # @param resource_name [Symbol] The resource DSL name (e.g. `:file`)
+ # @param node [Chef::Node] The node on which the resource will run.
+ #
+ def self.resolve(resource_name, node: Chef.node)
+ new(node, resource_name).resolve
end
private
# try dynamically finding a resource based on querying the resources to see what they support
- def maybe_dynamic_resource_resolution(resource)
- # log this so we know what resources will work for the generic resource on the node (early cut)
+ def maybe_dynamic_resource_resolution # log this so we know what resources will work for the generic resource on the node (early cut)
Chef::Log.debug "resources for generic #{resource} resource enabled on node include: #{enabled_handlers}"
# if none of the resources specifically support the resource, we still need to pick one of the resources that are
# enabled on the node to handle the why-run use case.
handlers = enabled_handlers
- if handlers.count >= 2
+ if handlers.size >= 2
# this magic stack ranks the resources by where they appear in the resource_priority_map
priority_list = [ get_priority_array(node, resource) ].flatten.compact
handlers = handlers.sort_by { |x| i = priority_list.index x; i.nil? ? Float::INFINITY : i }
@@ -70,14 +80,14 @@ class Chef
# entry for this resource is missing -- we should probably raise here and force resolution of the ambiguity.
Chef::Log.warn "Ambiguous resource precedence: #{handlers}, please use Chef.set_resource_priority_array to provide determinism"
end
- handlers = [ handlers.first ]
+ handlers = handlers[0..0]
end
Chef::Log.debug "resources that survived replacement include: #{handlers}"
raise Chef::Exceptions::AmbiguousResourceResolution.new(resource, handlers) if handlers.count >= 2
- Chef::Log.debug "dynamic resource resolver FAILED to resolve a resouce" if handlers.empty?
+ Chef::Log.debug "dynamic resource resolver FAILED to resolve a resource" if handlers.empty?
return nil if handlers.empty?
@@ -85,7 +95,7 @@ class Chef
end
# try the old static lookup of resources by mangling name to resource klass
- def maybe_chef_platform_lookup(resource)
+ def maybe_chef_platform_lookup
Chef::Resource.resource_matching_short_name(resource)
end
diff --git a/spec/data/lwrp/providers/buck_passer.rb b/spec/data/lwrp/providers/buck_passer.rb
index 9792e2c824..2bbca07bf7 100644
--- a/spec/data/lwrp/providers/buck_passer.rb
+++ b/spec/data/lwrp/providers/buck_passer.rb
@@ -1,12 +1,28 @@
provides :buck_passer
+def without_deprecation_warnings(&block)
+ old_treat_deprecation_warnings_as_errors = Chef::Config[:treat_deprecation_warnings_as_errors]
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ begin
+ block.call
+ ensure
+ Chef::Config[:treat_deprecation_warnings_as_errors] = old_treat_deprecation_warnings_as_errors
+ end
+end
+
action :pass_buck do
lwrp_foo :prepared_thumbs do
action :prepare_thumbs
- provider :lwrp_thumb_twiddler
+ # We know there will be a deprecation error here; head it off
+ without_deprecation_warnings do
+ provider :lwrp_thumb_twiddler
+ end
end
lwrp_foo :twiddled_thumbs do
action :twiddle_thumbs
- provider :lwrp_thumb_twiddler
+ # We know there will be a deprecation error here; head it off
+ without_deprecation_warnings do
+ provider :lwrp_thumb_twiddler
+ end
end
end
diff --git a/spec/data/lwrp/providers/buck_passer_2.rb b/spec/data/lwrp/providers/buck_passer_2.rb
index d34da3c378..c3bab7266f 100644
--- a/spec/data/lwrp/providers/buck_passer_2.rb
+++ b/spec/data/lwrp/providers/buck_passer_2.rb
@@ -1,10 +1,26 @@
+def without_deprecation_warnings(&block)
+ old_treat_deprecation_warnings_as_errors = Chef::Config[:treat_deprecation_warnings_as_errors]
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ begin
+ block.call
+ ensure
+ Chef::Config[:treat_deprecation_warnings_as_errors] = old_treat_deprecation_warnings_as_errors
+ end
+end
+
action :pass_buck do
lwrp_bar :prepared_eyes do
action :prepare_eyes
- provider :lwrp_paint_drying_watcher
+ # We know there will be a deprecation error here; head it off
+ without_deprecation_warnings do
+ provider :lwrp_paint_drying_watcher
+ end
end
lwrp_bar :dried_paint_watched do
action :watch_paint_dry
- provider :lwrp_paint_drying_watcher
+ # We know there will be a deprecation error here; head it off
+ without_deprecation_warnings do
+ provider :lwrp_paint_drying_watcher
+ end
end
end
diff --git a/spec/data/lwrp/providers/embedded_resource_accesses_providers_scope.rb b/spec/data/lwrp/providers/embedded_resource_accesses_providers_scope.rb
index f5841fb01c..77c1111ff5 100644
--- a/spec/data/lwrp/providers/embedded_resource_accesses_providers_scope.rb
+++ b/spec/data/lwrp/providers/embedded_resource_accesses_providers_scope.rb
@@ -3,11 +3,23 @@
# are passed properly (as demonstrated by the call to generate_new_name).
attr_reader :enclosed_resource
+def without_deprecation_warnings(&block)
+ old_treat_deprecation_warnings_as_errors = Chef::Config[:treat_deprecation_warnings_as_errors]
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ begin
+ block.call
+ ensure
+ Chef::Config[:treat_deprecation_warnings_as_errors] = old_treat_deprecation_warnings_as_errors
+ end
+end
+
action :twiddle_thumbs do
@enclosed_resource = lwrp_foo :foo do
monkey generate_new_name(new_resource.monkey){ 'the monkey' }
- action :twiddle_thumbs
- provider :lwrp_monkey_name_printer
+ # We know there will be a deprecation error here; head it off
+ without_deprecation_warnings do
+ provider :lwrp_monkey_name_printer
+ end
end
end
diff --git a/spec/data/lwrp_override/resources/foo.rb b/spec/data/lwrp_override/resources/foo.rb
index 14decb9634..2fc13d32fd 100644
--- a/spec/data/lwrp_override/resources/foo.rb
+++ b/spec/data/lwrp_override/resources/foo.rb
@@ -3,3 +3,8 @@
actions :never_execute
attribute :ever, :kind_of => String
+
+class ::Chef
+ def method_created_by_override_lwrp_foo
+ end
+end
diff --git a/spec/integration/recipes/lwrp_inline_resources_spec.rb b/spec/integration/recipes/lwrp_inline_resources_spec.rb
index b4c4e6ca11..e70605d3d3 100644
--- a/spec/integration/recipes/lwrp_inline_resources_spec.rb
+++ b/spec/integration/recipes/lwrp_inline_resources_spec.rb
@@ -5,7 +5,7 @@ describe "LWRPs with inline resources" do
include IntegrationSupport
include Chef::Mixin::ShellOut
- let(:chef_dir) { File.join(File.dirname(__FILE__), "..", "..", "..", "bin") }
+ let(:chef_dir) { File.expand_path("../../../../bin", __FILE__) }
# Invoke `chef-client` as `ruby PATH/TO/chef-client`. This ensures the
# following constraints are satisfied:
diff --git a/spec/integration/recipes/provider_choice.rb b/spec/integration/recipes/provider_choice.rb
new file mode 100644
index 0000000000..999765a1de
--- /dev/null
+++ b/spec/integration/recipes/provider_choice.rb
@@ -0,0 +1,41 @@
+require 'support/shared/integration/integration_helper'
+
+describe "Recipe DSL methods" do
+ include IntegrationSupport
+
+ context "With resource class providing 'provider_thingy'" do
+ before :context do
+ class Chef::Resource::ProviderThingy < Chef::Resource
+ def initialize(*args, &block)
+ super
+ @action = :create
+ @allowed_actions = [ :create ]
+ @resource_name = 'provider_thingy'
+ end
+ provides :provider_thingy
+ def to_s
+ "provider_thingy resource class"
+ end
+ end
+ end
+ context "And class Chef::Provider::ProviderThingy with no provides" do
+ before :context do
+ class Chef::Provider::ProviderThingy < Chef::Provider
+ def load_current_resource
+ end
+ def action_create
+ Chef::Log.warn("hello from #{self.class.name}")
+ end
+ end
+ end
+
+ it "provider_thingy 'blah' runs the provider and warns" do
+ recipe = converge {
+ provider_thingy 'blah' do; end
+ }
+ expect(recipe.logged_warnings).to match /hello from Chef::Provider::ProviderThingy/
+ expect(recipe.logged_warnings).to match /you must use 'provides' to provide DSL/i
+ end
+ end
+ end
+end
diff --git a/spec/integration/recipes/recipe_dsl_spec.rb b/spec/integration/recipes/recipe_dsl_spec.rb
new file mode 100644
index 0000000000..66eeb62ae8
--- /dev/null
+++ b/spec/integration/recipes/recipe_dsl_spec.rb
@@ -0,0 +1,259 @@
+require 'support/shared/integration/integration_helper'
+
+describe "Recipe DSL methods" do
+ include IntegrationSupport
+
+ context "With resource 'base_thingy' declared as BaseThingy" do
+ before(:context) {
+
+ class BaseThingy < Chef::Resource
+ def initialize(*args, &block)
+ super
+ @resource_name = 'base_thingy'
+ @allowed_actions = [ :create ]
+ @action = :create
+ end
+
+ class<<self
+ attr_accessor :created_resource
+ attr_accessor :created_provider
+ end
+
+ def provider
+ Provider
+ end
+ class Provider < Chef::Provider
+ def load_current_resource
+ end
+ def action_create
+ BaseThingy.created_resource = new_resource.class
+ BaseThingy.created_provider = self.class
+ end
+ end
+ end
+
+ # Modules to put stuff in
+ module Foo; end
+ module Foo::Bar; end
+
+ }
+
+ before :each do
+ BaseThingy.created_resource = nil
+ BaseThingy.created_provider = nil
+ end
+
+ context "Deprecated automatic resource DSL" do
+ before do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ end
+
+ context "With a resource 'backcompat_thingy' declared in Chef::Resource and Chef::Provider" do
+ before(:context) {
+
+ class Chef::Resource::BackcompatThingy < Chef::Resource
+ def initialize(*args, &block)
+ super
+ @resource_name = 'backcompat_thingy'
+ @allowed_actions = [ :create ]
+ @action = :create
+ end
+ end
+ class Chef::Provider::BackcompatThingy < Chef::Provider
+ def load_current_resource
+ end
+ def action_create
+ BaseThingy.created_resource = new_resource.class
+ BaseThingy.created_provider = self.class
+ end
+ end
+
+ }
+
+ it "backcompat_thingy creates a Chef::Resource::BackcompatThingy" do
+ recipe = converge {
+ backcompat_thingy 'blah' do; end
+ }
+ expect(recipe.logged_warnings).to match /Class Chef::Resource::BackcompatThingy does not declare 'provides :backcompat_thingy'/i
+ expect(BaseThingy.created_resource).to eq Chef::Resource::BackcompatThingy
+ expect(BaseThingy.created_provider).to eq Chef::Provider::BackcompatThingy
+ end
+
+ context "And another resource 'backcompat_thingy' in BackcompatThingy with 'provides'" do
+ before(:context) {
+
+ class Foo::BackcompatThingy < BaseThingy
+ provides :backcompat_thingy
+ end
+
+ }
+
+ it "backcompat_thingy creates a BackcompatThingy" do
+ recipe = converge {
+ backcompat_thingy 'blah' do; end
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).not_to be_nil
+ end
+ end
+ end
+
+ context "With a resource named Foo::Bar::Thingy" do
+ before(:context) {
+
+ class Foo::Bar::Thingy < BaseThingy; end
+
+ }
+
+ it "thingy does not work" do
+ expect_converge {
+ thingy 'blah' do; end
+ }.to raise_error(NoMethodError)
+ end
+ end
+ end
+
+ context "provides" do
+ context "When MySupplier provides :hemlock" do
+ before(:context) {
+
+ class Foo::MySupplier < BaseThingy
+ provides :hemlock
+ end
+
+ }
+
+ it "my_supplier does not work in a recipe" do
+ expect_converge {
+ my_supplier 'blah' do; end
+ }.to raise_error(NoMethodError)
+ end
+
+ it "hemlock works in a recipe" do
+ expect_recipe {
+ hemlock 'blah' do; end
+ }.to emit_no_warnings_or_errors
+ expect(BaseThingy.created_resource).to eq Foo::MySupplier
+ end
+ end
+
+ context "When Thingy3 provides :thingy3" do
+ before(:context) {
+
+ class Foo::Thingy3 < BaseThingy
+ provides :thingy3
+ end
+
+ }
+
+ it "thingy3 works in a recipe" do
+ expect_recipe {
+ thingy3 'blah' do; end
+ }.to emit_no_warnings_or_errors
+ expect(BaseThingy.created_resource).to eq Foo::Thingy3
+ end
+
+ context "And Thingy4 provides :thingy3" do
+ before(:context) {
+
+ class Foo::Thingy4 < Chef::Resource
+ provides :thingy3
+ end
+
+ }
+
+ it "thingy3 works in a recipe and yields " do
+ recipe = converge {
+ thingy3 'blah' do; end
+ }
+ expect(recipe.logged_warnings).to match /ambiguous resource precedence/i
+ expect(BaseThingy.created_resource).not_to be_nil
+ end
+
+ it "thingy4 does not work in a recipe" do
+ expect_converge {
+ thingy4 'blah' do; end
+ }.to raise_error(NoMethodError)
+ end
+ end
+ end
+
+ context "When Thingy5 provides :thingy5, :twizzle and :twizzle2" do
+ before(:context) {
+
+ class Foo::Thingy5 < BaseThingy
+ provides :thingy5
+ provides :twizzle
+ provides :twizzle2
+ end
+
+ }
+
+ it "thingy5 works in a recipe and yields Thingy5" do
+ expect_recipe {
+ thingy5 'blah' do; end
+ }.to emit_no_warnings_or_errors
+ expect(BaseThingy.created_resource).to eq Foo::Thingy5
+ end
+
+ it "twizzle works in a recipe and yields Thingy5" do
+ expect_recipe {
+ twizzle 'blah' do; end
+ }.to emit_no_warnings_or_errors
+ expect(BaseThingy.created_resource).to eq Foo::Thingy5
+ end
+
+ it "twizzle2 works in a recipe and yields Thingy5" do
+ expect_recipe {
+ twizzle2 'blah' do; end
+ }.to emit_no_warnings_or_errors
+ expect(BaseThingy.created_resource).to eq Foo::Thingy5
+ end
+ end
+
+ context "With platform-specific resources 'my_super_thingy_foo' and 'my_super_thingy_bar'" do
+ before(:context) {
+ class MySuperThingyFoo < BaseThingy
+ provides :my_super_thingy, platform: 'foo'
+ end
+
+ class MySuperThingyBar < BaseThingy
+ provides :my_super_thingy, platform: 'bar'
+ end
+ }
+
+ it "A run with platform 'foo' uses MySuperThingyFoo" do
+ r = Cheffish::ChefRun.new(chef_config)
+ r.client.run_context.node.automatic['platform'] = 'foo'
+ r.compile_recipe {
+ my_super_thingy 'blah' do; end
+ }
+ r.converge
+ expect(r).to emit_no_warnings_or_errors
+ expect(BaseThingy.created_resource).to eq MySuperThingyFoo
+ end
+
+ it "A run with platform 'bar' uses MySuperThingyBar" do
+ r = Cheffish::ChefRun.new(chef_config)
+ r.client.run_context.node.automatic['platform'] = 'bar'
+ r.compile_recipe {
+ my_super_thingy 'blah' do; end
+ }
+ r.converge
+ expect(r).to emit_no_warnings_or_errors
+ expect(BaseThingy.created_resource).to eq MySuperThingyBar
+ end
+
+ it "A run with platform 'x' reports that my_super_thingy is not supported" do
+ r = Cheffish::ChefRun.new(chef_config)
+ r.client.run_context.node.automatic['platform'] = 'x'
+ expect {
+ r.compile_recipe {
+ my_super_thingy 'blah' do; end
+ }
+ }.to raise_error(Chef::Exceptions::NoSuchResourceType)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index fb284c721b..7cc9b8f7d6 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -54,6 +54,9 @@ Dir['lib/chef/knife/**/*.rb'].
map {|f| f.gsub(%r[\.rb$], '') }.
each {|f| require f }
+require 'chef/resource_resolver'
+require 'chef/provider_resolver'
+
require 'chef/mixins'
require 'chef/dsl'
require 'chef/application'
diff --git a/spec/support/lib/chef/resource/cat.rb b/spec/support/lib/chef/resource/cat.rb
index ecca50cb53..641ce28795 100644
--- a/spec/support/lib/chef/resource/cat.rb
+++ b/spec/support/lib/chef/resource/cat.rb
@@ -19,6 +19,7 @@
class Chef
class Resource
class Cat < Chef::Resource
+ provides :cat
attr_accessor :action
diff --git a/spec/support/lib/chef/resource/one_two_three_four.rb b/spec/support/lib/chef/resource/one_two_three_four.rb
index 296d2cd970..d7e5ea095e 100644
--- a/spec/support/lib/chef/resource/one_two_three_four.rb
+++ b/spec/support/lib/chef/resource/one_two_three_four.rb
@@ -19,6 +19,8 @@
class Chef
class Resource
class OneTwoThreeFour < Chef::Resource
+ provides :one_two_three_four
+
attr_reader :i_can_count
def initialize(name, run_context)
diff --git a/spec/support/lib/chef/resource/zen_follower.rb b/spec/support/lib/chef/resource/zen_follower.rb
index ddc289e48d..590aa0827b 100644
--- a/spec/support/lib/chef/resource/zen_follower.rb
+++ b/spec/support/lib/chef/resource/zen_follower.rb
@@ -21,6 +21,7 @@ require 'chef/json_compat'
class Chef
class Resource
class ZenFollower < Chef::Resource
+ provides :zen_follower
provides :follower, platform: "zen"
diff --git a/spec/support/lib/chef/resource/zen_master.rb b/spec/support/lib/chef/resource/zen_master.rb
index d47d174e28..145dd70e64 100644
--- a/spec/support/lib/chef/resource/zen_master.rb
+++ b/spec/support/lib/chef/resource/zen_master.rb
@@ -22,6 +22,8 @@ require 'chef/json_compat'
class Chef
class Resource
class ZenMaster < Chef::Resource
+ provides :zen_master
+
attr_reader :peace
def initialize(name, run_context=nil)
diff --git a/spec/support/shared/integration/integration_helper.rb b/spec/support/shared/integration/integration_helper.rb
index e6942c62af..927ff2f42b 100644
--- a/spec/support/shared/integration/integration_helper.rb
+++ b/spec/support/shared/integration/integration_helper.rb
@@ -22,14 +22,19 @@ require 'fileutils'
require 'chef/config'
require 'chef/json_compat'
require 'chef/server_api'
-require 'chef_zero/rspec'
require 'support/shared/integration/knife_support'
require 'support/shared/integration/app_server_support'
+require 'cheffish/rspec/chef_run_support'
require 'spec_helper'
module IntegrationSupport
include ChefZero::RSpec
+ def self.included(includer_class)
+ includer_class.extend(Cheffish::RSpec::ChefRunSupport)
+ includer_class.extend(ClassMethods)
+ end
+
module ClassMethods
include ChefZero::RSpec
@@ -49,10 +54,6 @@ module IntegrationSupport
end
end
- def self.included(includer_class)
- includer_class.extend(ClassMethods)
- end
-
def api
Chef::ServerAPI.new
end
diff --git a/spec/unit/guard_interpreter/resource_guard_interpreter_spec.rb b/spec/unit/guard_interpreter/resource_guard_interpreter_spec.rb
index 4cf3ba827a..02e4eb2fae 100644
--- a/spec/unit/guard_interpreter/resource_guard_interpreter_spec.rb
+++ b/spec/unit/guard_interpreter/resource_guard_interpreter_spec.rb
@@ -24,6 +24,7 @@ describe Chef::GuardInterpreter::ResourceGuardInterpreter do
node.default["kernel"] = Hash.new
node.default["kernel"][:machine] = :x86_64.to_s
+ node.automatic[:os] = 'windows'
node
end
@@ -144,4 +145,3 @@ describe Chef::GuardInterpreter::ResourceGuardInterpreter do
end
end
end
-
diff --git a/spec/unit/lwrp_spec.rb b/spec/unit/lwrp_spec.rb
index ec39174da6..ff7f8e7d7b 100644
--- a/spec/unit/lwrp_spec.rb
+++ b/spec/unit/lwrp_spec.rb
@@ -17,20 +17,40 @@
#
require 'spec_helper'
+require 'tmpdir'
+require 'fileutils'
+require 'chef/mixin/convert_to_class_name'
module LwrpConstScopingConflict
end
describe "LWRP" do
+ include Chef::Mixin::ConvertToClassName
+
before do
@original_VERBOSE = $VERBOSE
$VERBOSE = nil
+ Chef::Resource::LWRPBase.class_eval { @loaded_lwrps = {} }
end
after do
$VERBOSE = @original_VERBOSE
end
+ def get_lwrp(name)
+ Chef::Resource.resource_for_node(name, Chef::Node.new)
+ end
+
+ def get_lwrp_provider(name)
+ old_treat_deprecation_warnings_as_errors = Chef::Config[:treat_deprecation_warnings_as_errors]
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ begin
+ Chef::Provider.const_get(convert_to_class_name(name.to_s))
+ ensure
+ Chef::Config[:treat_deprecation_warnings_as_errors] = old_treat_deprecation_warnings_as_errors
+ end
+ end
+
describe "when overriding an existing class" do
before :each do
allow($stderr).to receive(:write)
@@ -43,7 +63,6 @@ describe "LWRP" do
expect(Chef::Log).not_to receive(:debug).with(/anymore/)
Chef::Resource::LWRPBase.build_from_file("lwrp", file, nil)
Object.send(:remove_const, 'LwrpFoo')
- Chef::Resource.send(:remove_const, 'LwrpFoo')
end
it "should not skip loading a provider when there's a top level symbol of the same name" do
@@ -53,7 +72,6 @@ describe "LWRP" do
expect(Chef::Log).not_to receive(:debug).with(/anymore/)
Chef::Provider::LWRPBase.build_from_file("lwrp", file, nil)
Object.send(:remove_const, 'LwrpBuckPasser')
- Chef::Provider.send(:remove_const, 'LwrpBuckPasser')
end
# @todo: we need a before block to manually remove_const all of the LWRPs that we
@@ -67,7 +85,6 @@ describe "LWRP" do
Dir[File.expand_path( "lwrp/resources/*", CHEF_SPEC_DATA)].each do |file|
expect(Chef::Log).to receive(:info).with(/Skipping/)
- expect(Chef::Log).to receive(:debug).with(/anymore/)
Chef::Resource::LWRPBase.build_from_file("lwrp", file, nil)
end
end
@@ -79,7 +96,6 @@ describe "LWRP" do
Dir[File.expand_path( "lwrp/providers/*", CHEF_SPEC_DATA)].each do |file|
expect(Chef::Log).to receive(:info).with(/Skipping/)
- expect(Chef::Log).to receive(:debug).with(/anymore/)
Chef::Provider::LWRPBase.build_from_file("lwrp", file, nil)
end
end
@@ -90,7 +106,7 @@ describe "LWRP" do
Dir[File.expand_path( "lwrp/resources/*", CHEF_SPEC_DATA)].each do |file|
Chef::Resource::LWRPBase.build_from_file("lwrp", file, nil)
end
- first_lwr_foo_class = Chef::Resource::LwrpFoo
+ first_lwr_foo_class = get_lwrp(:lwrp_foo)
expect(Chef::Resource.resource_classes).to include(first_lwr_foo_class)
Dir[File.expand_path( "lwrp/resources/*", CHEF_SPEC_DATA)].each do |file|
Chef::Resource::LWRPBase.build_from_file("lwrp", file, nil)
@@ -106,40 +122,70 @@ describe "LWRP" do
end
+ context "When an LWRP resource lwrp_foo is loaded" do
+ before do
+ @tmpdir = Dir.mktmpdir("lwrp_test")
+ @lwrp_path = File.join(@tmpdir, "foo.rb")
+ content = IO.read(File.expand_path("../../data/lwrp/resources/foo.rb", __FILE__))
+ IO.write(@lwrp_path, content)
+ Chef::Resource::LWRPBase.build_from_file("lwrp", @lwrp_path, nil)
+ @original_resource = Chef::Resource.resource_for_node(:lwrp_foo, Chef::Node.new)
+ end
+
+ after do
+ FileUtils.remove_entry @tmpdir
+ end
+
+ context "And the LWRP is asked to load again, this time with different code" do
+ before do
+ content = IO.read(File.expand_path("../../data/lwrp_override/resources/foo.rb", __FILE__))
+ IO.write(@lwrp_path, content)
+ Chef::Resource::LWRPBase.build_from_file("lwrp", @lwrp_path, nil)
+ end
+
+ it "Should load the old content, and not the new" do
+ resource = Chef::Resource.resource_for_node(:lwrp_foo, Chef::Node.new)
+ expect(resource).to eq @original_resource
+ expect(resource.default_action).to eq :pass_buck
+ expect(Chef.method_defined?(:method_created_by_override_lwrp_foo)).to be_falsey
+ end
+ end
+ end
+
describe "Lightweight Chef::Resource" do
before do
Dir[File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "lwrp", "resources", "*"))].each do |file|
Chef::Resource::LWRPBase.build_from_file("lwrp", file, nil)
end
+ end
- Dir[File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "lwrp_override", "resources", "*"))].each do |file|
- Chef::Resource::LWRPBase.build_from_file("lwrp", file, nil)
- end
+ it "should load the resource into a properly-named class and emit a warning about deprecation when accessing it" do
+ expect { Chef::Resource::LwrpFoo }.to raise_error(Chef::Exceptions::DeprecatedFeatureError)
end
- it "should load the resource into a properly-named class" do
- expect(Chef::Resource.const_get("LwrpFoo")).to be_kind_of(Class)
+ it "should be resolvable with Chef::ResourceResolver.resolve(:lwrp_foo)" do
+ expect(Chef::ResourceResolver.resolve(:lwrp_foo, node: Chef::Node.new)).to eq(get_lwrp(:lwrp_foo))
end
it "should set resource_name" do
- expect(Chef::Resource::LwrpFoo.new("blah").resource_name).to eql(:lwrp_foo)
+ expect(get_lwrp(:lwrp_foo).new("blah").resource_name).to eql(:lwrp_foo)
end
it "should add the specified actions to the allowed_actions array" do
- expect(Chef::Resource::LwrpFoo.new("blah").allowed_actions).to include(:pass_buck, :twiddle_thumbs)
+ expect(get_lwrp(:lwrp_foo).new("blah").allowed_actions).to include(:pass_buck, :twiddle_thumbs)
end
it "should set the specified action as the default action" do
- expect(Chef::Resource::LwrpFoo.new("blah").action).to eq(:pass_buck)
+ expect(get_lwrp(:lwrp_foo).new("blah").action).to eq(:pass_buck)
end
it "should create a method for each attribute" do
- expect(Chef::Resource::LwrpFoo.new("blah").methods.map{ |m| m.to_sym}).to include(:monkey)
+ expect(get_lwrp(:lwrp_foo).new("blah").methods.map{ |m| m.to_sym}).to include(:monkey)
end
it "should build attribute methods that respect validation rules" do
- expect { Chef::Resource::LwrpFoo.new("blah").monkey(42) }.to raise_error(ArgumentError)
+ expect { get_lwrp(:lwrp_foo).new("blah").monkey(42) }.to raise_error(ArgumentError)
end
it "should have access to the run context and node during class definition" do
@@ -151,7 +197,7 @@ describe "LWRP" do
Chef::Resource::LWRPBase.build_from_file("lwrp", file, run_context)
end
- cls = Chef::Resource.const_get("LwrpNodeattr")
+ cls = get_lwrp(:lwrp_nodeattr)
expect(cls.node).to be_kind_of(Chef::Node)
expect(cls.run_context).to be_kind_of(Chef::RunContext)
expect(cls.node[:penguin_name]).to eql("jackass")
@@ -291,48 +337,48 @@ describe "LWRP" do
end
before(:each) do
- Dir[File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "lwrp", "resources", "*"))].each do |file|
+ Dir[File.expand_path(File.expand_path("../../data/lwrp/resources/*", __FILE__))].each do |file|
Chef::Resource::LWRPBase.build_from_file("lwrp", file, @run_context)
end
- Dir[File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "lwrp_override", "resources", "*"))].each do |file|
- Chef::Resource::LWRPBase.build_from_file("lwrp", file, @run_context)
- end
-
- Dir[File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "lwrp", "providers", "*"))].each do |file|
- Chef::Provider::LWRPBase.build_from_file("lwrp", file, @run_context)
- end
-
- Dir[File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "lwrp_override", "providers", "*"))].each do |file|
+ Dir[File.expand_path(File.expand_path("../../data/lwrp/providers/*", __FILE__))].each do |file|
Chef::Provider::LWRPBase.build_from_file("lwrp", file, @run_context)
end
-
end
it "should properly handle a new_resource reference" do
- resource = Chef::Resource::LwrpFoo.new("morpheus")
+ resource = get_lwrp(:lwrp_foo).new("morpheus", @run_context)
resource.monkey("bob")
- resource.provider(:lwrp_monkey_name_printer)
- resource.run_context = @run_context
+ resource.provider(get_lwrp_provider(:lwrp_monkey_name_printer))
provider = Chef::Platform.provider_for_resource(resource, :twiddle_thumbs)
provider.action_twiddle_thumbs
end
- it "should load the provider into a properly-named class" do
- expect(Chef::Provider.const_get("LwrpBuckPasser")).to be_kind_of(Class)
- end
+ context "resource class created" do
+ before do
+ @old_treat_deprecation_warnings_as_errors = Chef::Config[:treat_deprecation_warnings_as_errors]
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ end
+ after do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = @old_treat_deprecation_warnings_as_errors
+ end
- it "should create a method for each attribute" do
- new_resource = double("new resource").as_null_object
- expect(Chef::Provider::LwrpBuckPasser.new(nil, new_resource).methods.map{|m|m.to_sym}).to include(:action_pass_buck)
- expect(Chef::Provider::LwrpThumbTwiddler.new(nil, new_resource).methods.map{|m|m.to_sym}).to include(:action_twiddle_thumbs)
+ it "should load the provider into a properly-named class" do
+ expect(Chef::Provider.const_get("LwrpBuckPasser")).to be_kind_of(Class)
+ expect(Chef::Provider::LwrpBuckPasser <= Chef::Provider::LWRPBase).to be_truthy
+ end
+
+ it "should create a method for each action" do
+ expect(get_lwrp_provider(:lwrp_buck_passer).instance_methods).to include(:action_pass_buck)
+ expect(get_lwrp_provider(:lwrp_thumb_twiddler).instance_methods).to include(:action_twiddle_thumbs)
+ end
end
it "should insert resources embedded in the provider into the middle of the resource collection" do
- injector = Chef::Resource::LwrpFoo.new("morpheus", @run_context)
+ injector = get_lwrp(:lwrp_foo).new("morpheus", @run_context)
injector.action(:pass_buck)
- injector.provider(:lwrp_buck_passer)
+ injector.provider(get_lwrp_provider(:lwrp_buck_passer))
dummy = Chef::Resource::ZenMaster.new("keanu reeves", @run_context)
dummy.provider(Chef::Provider::Easy)
@run_context.resource_collection.insert(injector)
@@ -347,13 +393,13 @@ describe "LWRP" do
end
it "should insert embedded resources from multiple providers, including from the last position, properly into the resource collection" do
- injector = Chef::Resource::LwrpFoo.new("morpheus", @run_context)
+ injector = get_lwrp(:lwrp_foo).new("morpheus", @run_context)
injector.action(:pass_buck)
- injector.provider(:lwrp_buck_passer)
+ injector.provider(get_lwrp_provider(:lwrp_buck_passer))
- injector2 = Chef::Resource::LwrpBar.new("tank", @run_context)
+ injector2 = get_lwrp(:lwrp_bar).new("tank", @run_context)
injector2.action(:pass_buck)
- injector2.provider(:lwrp_buck_passer_2)
+ injector2.provider(get_lwrp_provider(:lwrp_buck_passer_2))
dummy = Chef::Resource::ZenMaster.new("keanu reeves", @run_context)
dummy.provider(Chef::Provider::Easy)
@@ -374,9 +420,9 @@ describe "LWRP" do
end
it "should properly handle a new_resource reference" do
- resource = Chef::Resource::LwrpFoo.new("morpheus", @run_context)
+ resource = get_lwrp(:lwrp_foo).new("morpheus", @run_context)
resource.monkey("bob")
- resource.provider(:lwrp_monkey_name_printer)
+ resource.provider(get_lwrp_provider(:lwrp_monkey_name_printer))
provider = Chef::Platform.provider_for_resource(resource, :twiddle_thumbs)
provider.action_twiddle_thumbs
@@ -385,9 +431,9 @@ describe "LWRP" do
end
it "should properly handle an embedded Resource accessing the enclosing Provider's scope" do
- resource = Chef::Resource::LwrpFoo.new("morpheus", @run_context)
+ resource = get_lwrp(:lwrp_foo).new("morpheus", @run_context)
resource.monkey("bob")
- resource.provider(:lwrp_embedded_resource_accesses_providers_scope)
+ resource.provider(get_lwrp_provider(:lwrp_embedded_resource_accesses_providers_scope))
provider = Chef::Platform.provider_for_resource(resource, :twiddle_thumbs)
#provider = @runner.build_provider(resource)
@@ -404,10 +450,10 @@ describe "LWRP" do
# Side effect of lwrp_inline_compiler provider for testing notifications.
$interior_ruby_block_2 = nil
# resource type doesn't matter, so make an existing resource type work with provider.
- @resource = Chef::Resource::LwrpFoo.new("morpheus", @run_context)
+ @resource = get_lwrp(:lwrp_foo).new("morpheus", @run_context)
@resource.allowed_actions << :test
@resource.action(:test)
- @resource.provider(:lwrp_inline_compiler)
+ @resource.provider(get_lwrp_provider(:lwrp_inline_compiler))
end
it "does not add interior resources to the exterior resource collection" do
diff --git a/spec/unit/recipe_spec.rb b/spec/unit/recipe_spec.rb
index 7442f4477e..9c4fb49497 100644
--- a/spec/unit/recipe_spec.rb
+++ b/spec/unit/recipe_spec.rb
@@ -83,7 +83,7 @@ describe Chef::Recipe do
it "should require a name argument" do
expect {
recipe.cat
- }.to raise_error(ArgumentError, "You must supply a name when declaring a cat resource")
+ }.to raise_error(ArgumentError)
end
it "should allow regular errors (not NameErrors) to pass unchanged" do
diff --git a/spec/unit/resource/batch_spec.rb b/spec/unit/resource/batch_spec.rb
index 4a056b8735..b8c2897f42 100644
--- a/spec/unit/resource/batch_spec.rb
+++ b/spec/unit/resource/batch_spec.rb
@@ -25,6 +25,7 @@ describe Chef::Resource::Batch do
node.default["kernel"] = Hash.new
node.default["kernel"][:machine] = :x86_64.to_s
+ node.automatic[:os] = 'windows'
run_context = Chef::RunContext.new(node, nil, nil)
diff --git a/spec/unit/resource/powershell_spec.rb b/spec/unit/resource/powershell_spec.rb
index c263172ae6..2505c4a3d7 100644
--- a/spec/unit/resource/powershell_spec.rb
+++ b/spec/unit/resource/powershell_spec.rb
@@ -25,6 +25,7 @@ describe Chef::Resource::PowershellScript do
node.default["kernel"] = Hash.new
node.default["kernel"][:machine] = :x86_64.to_s
+ node.automatic[:os] = 'windows'
run_context = Chef::RunContext.new(node, nil, nil)
diff --git a/spec/unit/resource_spec.rb b/spec/unit/resource_spec.rb
index 6b2d6c89d3..0479778f55 100644
--- a/spec/unit/resource_spec.rb
+++ b/spec/unit/resource_spec.rb
@@ -731,35 +731,51 @@ describe Chef::Resource do
end
- describe "lookups from the platform map" do
- let(:klz1) { Class.new(Chef::Resource) }
- let(:klz2) { Class.new(Chef::Resource) }
+ describe "resource_for_node" do
+ describe "lookups from the platform map" do
+ let(:klz1) { Class.new(Chef::Resource) }
+
+ before(:each) do
+ Chef::Resource::Klz1 = klz1
+ @node = Chef::Node.new
+ @node.name("bumblebee")
+ @node.automatic[:platform] = "autobots"
+ @node.automatic[:platform_version] = "6.1"
+ Object.const_set('Soundwave', klz1)
+ klz1.provides :soundwave
+ end
- 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', klz1)
- klz2.provides :dinobot, :on_platforms => ['autobots']
- Object.const_set('Grimlock', klz2)
- end
+ after(:each) do
+ Object.send(:remove_const, :Soundwave)
+ Chef::Resource.send(:remove_const, :Klz1)
+ end
- after(:each) do
- Object.send(:remove_const, :Soundwave)
- Object.send(:remove_const, :Grimlock)
- Chef::Resource.send(:remove_const, :Klz1)
- Chef::Resource.send(:remove_const, :Klz2)
+ it "returns a resource by short_name if nothing else matches" do
+ expect(Chef::Resource.resource_for_node(:soundwave, @node)).to eql(klz1)
+ end
end
- describe "resource_for_node" do
- it "returns a resource by short_name and node" do
- expect(Chef::Resource.resource_for_node(:dinobot, @node)).to eql(Grimlock)
+ describe "lookups from the platform map" do
+ let(:klz2) { Class.new(Chef::Resource) }
+
+ before(:each) do
+ Chef::Resource::Klz2 = klz2
+ @node = Chef::Node.new
+ @node.name("bumblebee")
+ @node.automatic[:platform] = "autobots"
+ @node.automatic[:platform_version] = "6.1"
+ klz2.provides :dinobot, :on_platforms => ['autobots']
+ Object.const_set('Grimlock', klz2)
+ klz2.provides :grimlock
end
- it "returns a resource by short_name if nothing else matches" do
- expect(Chef::Resource.resource_for_node(:soundwave, @node)).to eql(Soundwave)
+
+ after(:each) do
+ Object.send(:remove_const, :Grimlock)
+ Chef::Resource.send(:remove_const, :Klz2)
+ end
+
+ it "returns a resource by short_name and node" do
+ expect(Chef::Resource.resource_for_node(:dinobot, @node)).to eql(klz2)
end
end
diff --git a/tasks/external_tests.rb b/tasks/external_tests.rb
new file mode 100644
index 0000000000..2ff991ddf7
--- /dev/null
+++ b/tasks/external_tests.rb
@@ -0,0 +1,29 @@
+task :chef_sugar_spec do
+ gem_path = Bundler.environment.specs['chef-sugar'].first.full_gem_path
+ system("cd #{gem_path} && rake")
+end
+
+task :foodcritic_spec do
+ gem_path = Bundler.environment.specs['foodcritic'].first.full_gem_path
+ system("cd #{gem_path} && rake test")
+end
+
+task :chefspec_spec do
+ gem_path = Bundler.environment.specs['chefspec'].first.full_gem_path
+ system("cd #{gem_path} && rake")
+end
+
+task :chef_rewind_spec do
+ gem_path = Bundler.environment.specs['chef-rewind'].first.full_gem_path
+ system("cd #{gem_path} && rake spec")
+end
+
+task :poise_spec do
+ gem_path = Bundler.environment.specs['poise'].first.full_gem_path
+ system("cd #{gem_path} && rake spec")
+end
+
+task :halite_spec do
+ gem_path = Bundler.environment.specs['halite'].first.full_gem_path
+ system("cd #{gem_path} && rake spec")
+end