summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorchris <chris@opscodes-macbook-pro-2.local>2009-09-23 22:08:41 +0000
committerchris <chris@opscodes-macbook-pro-2.local>2009-09-23 22:11:01 +0000
commit480938b0faf62ea264b33d4140ca11561a8235b1 (patch)
tree2c50a349f117f2dbdd3d968ec876b19670331721
parent8ae8a64b8564d19123600434dafc3bf4adfeb9ff (diff)
downloadchef-480938b0faf62ea264b33d4140ca11561a8235b1.tar.gz
Adding embedded resources inside providers and some re-factoring in the core. There are cucumber tests for the new functionality, but unit tests are not included in this commit.
-rw-r--r--chef/lib/chef/client.rb10
-rw-r--r--chef/lib/chef/compile.rb11
-rw-r--r--chef/lib/chef/cookbook.rb19
-rw-r--r--chef/lib/chef/mixin/convert_to_class_name.rb9
-rw-r--r--chef/lib/chef/mixin/recipe_definition_dsl_core.rb75
-rw-r--r--chef/lib/chef/provider.rb38
-rw-r--r--chef/lib/chef/provider/cron.rb4
-rw-r--r--chef/lib/chef/provider/deploy.rb4
-rw-r--r--chef/lib/chef/provider/group.rb4
-rw-r--r--chef/lib/chef/provider/mount.rb4
-rw-r--r--chef/lib/chef/provider/mount/mount.rb4
-rw-r--r--chef/lib/chef/provider/package.rb4
-rw-r--r--chef/lib/chef/provider/package/yum.rb4
-rw-r--r--chef/lib/chef/provider/service.rb4
-rw-r--r--chef/lib/chef/provider/service/init.rb4
-rw-r--r--chef/lib/chef/provider/service/redhat.rb4
-rw-r--r--chef/lib/chef/provider/user.rb4
-rw-r--r--chef/lib/chef/recipe.rb73
-rw-r--r--chef/lib/chef/resource.rb41
-rw-r--r--chef/lib/chef/resource_collection.rb34
-rw-r--r--chef/lib/chef/runner.rb8
-rw-r--r--chef/spec/unit/client_spec.rb12
-rw-r--r--chef/spec/unit/compile_spec.rb10
-rw-r--r--chef/spec/unit/runner_spec.rb4
-rw-r--r--features/cookbooks/lightweight_resources_and_providers.feature18
-rw-r--r--features/data/cookbooks/lwrp/providers/default.rb6
-rw-r--r--features/data/cookbooks/lwrp/recipes/non_default_provider.rb2
-rw-r--r--features/data/cookbooks/lwrp/recipes/provider_invokes_resource.rb6
-rw-r--r--features/data/cookbooks/lwrp/resources/default.rb3
29 files changed, 271 insertions, 152 deletions
diff --git a/chef/lib/chef/client.rb b/chef/lib/chef/client.rb
index da25a1cbcf..9cce426440 100644
--- a/chef/lib/chef/client.rb
+++ b/chef/lib/chef/client.rb
@@ -401,15 +401,9 @@ class Chef
Chef::Config[:cookbook_path] = File.join(Chef::Config[:file_cache_path], "cookbooks")
end
compile = Chef::Compile.new(@node)
- compile.load_libraries
- compile.load_providers
- compile.load_resources
- compile.load_attributes
- compile.load_definitions
- compile.load_recipes
-
+
Chef::Log.debug("Converging node #{@safe_name}")
- cr = Chef::Runner.new(@node, compile.collection)
+ cr = Chef::Runner.new(@node, compile.collection, compile.definitions, compile.cookbook_loader)
cr.converge
true
end
diff --git a/chef/lib/chef/compile.rb b/chef/lib/chef/compile.rb
index 44cc63c92a..b6eaf77e8f 100644
--- a/chef/lib/chef/compile.rb
+++ b/chef/lib/chef/compile.rb
@@ -28,8 +28,8 @@ class Chef
attr_accessor :node, :cookbook_loader, :collection, :definitions
- # Creates a new Chef::Compile object. This object gets used by the Chef Server to generate
- # a fully compiled recipe list for a node.
+ # Creates a new Chef::Compile object and populates its fields. This object gets
+ # used by the Chef Server to generate a fully compiled recipe list for a node.
#
# === Returns
# object<Chef::Compile>:: Duh. :)
@@ -41,6 +41,13 @@ class Chef
@recipes = Array.new
@default_attributes = Array.new
@override_attributes = Array.new
+
+ load_libraries
+ load_providers
+ load_resources
+ load_attributes
+ load_definitions
+ load_recipes
end
# Looks up the node via the "name" argument, first from CouchDB, then by calling
diff --git a/chef/lib/chef/cookbook.rb b/chef/lib/chef/cookbook.rb
index 320da397f3..14f567ef2b 100644
--- a/chef/lib/chef/cookbook.rb
+++ b/chef/lib/chef/cookbook.rb
@@ -101,9 +101,8 @@ class Chef
# true:: Always returns true
def load_resources
@resource_files.each do |file|
- class_name = class_name_from_filename(file)
- Chef::Log.debug("Loading cookbook #{name}'s resources from #{file} into class Chef::Resource::#{class_name}")
- Chef::Resource.const_set(class_name, Chef::Resource.build_from_file(file))
+ Chef::Log.debug("Loading cookbook #{name}'s resources from #{file}")
+ Chef::Resource.build_from_file(name, file)
end
end
@@ -113,9 +112,8 @@ class Chef
# true:: Always returns true
def load_providers
@provider_files.each do |file|
- class_name = class_name_from_filename(file)
- Chef::Log.debug("Loading cookbook #{name}'s providers from #{file} into class Chef::Provider::#{class_name}")
- Chef::Provider.const_set(class_name, Chef::Provider.build_from_file(file))
+ Chef::Log.debug("Loading cookbook #{name}'s providers from #{file}")
+ Chef::Provider.build_from_file(name, file)
end
end
@@ -169,14 +167,5 @@ class Chef
recipe
end
- private
-
- def class_name_from_filename(filename)
- class_name_base = name.to_s
- file_base = File.basename(filename, ".rb")
- class_name_base += "_#{file_base}" unless file_base == 'default'
- convert_to_class_name(class_name_base)
- end
-
end
end
diff --git a/chef/lib/chef/mixin/convert_to_class_name.rb b/chef/lib/chef/mixin/convert_to_class_name.rb
index ce2c75e449..4ed89b527a 100644
--- a/chef/lib/chef/mixin/convert_to_class_name.rb
+++ b/chef/lib/chef/mixin/convert_to_class_name.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@opscode.com>)
# Author:: Christopher Walters (<cw@opscode.com>)
-# Copyright:: Copyright (c) 2009 Opscode, Inc.
+# Copyright:: Copyright (c) 2008, 2009 Opscode, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,7 +27,7 @@ class Chef
mn = str.match(regexp)
if mn
- rname = "#{mod ? "#{mod.to_s}::" : ''}#{mn[1].capitalize}"
+ rname = mn[1].capitalize
while mn && mn[3]
mn = mn[3].match(regexp)
@@ -38,6 +38,11 @@ class Chef
rname
end
+ def filename_to_qualified_string(base, filename)
+ file_base = File.basename(filename, ".rb")
+ base.to_s + (file_base == 'default' ? '' : "_#{file_base}")
+ end
+
end
end
end
diff --git a/chef/lib/chef/mixin/recipe_definition_dsl_core.rb b/chef/lib/chef/mixin/recipe_definition_dsl_core.rb
new file mode 100644
index 0000000000..9f80b41903
--- /dev/null
+++ b/chef/lib/chef/mixin/recipe_definition_dsl_core.rb
@@ -0,0 +1,75 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Author:: Christopher Walters (<cw@opscode.com>)
+# Copyright:: Copyright (c) 2008, 2009 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/recipe'
+require 'chef/resource'
+require 'chef/mixin/convert_to_class_name'
+
+class Chef
+ module Mixin
+ module RecipeDefinitionDSLCore
+
+ include Chef::Mixin::ConvertToClassName
+
+ 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 @definitions.has_key?(method_symbol)
+ # This dupes the high level object, but we still need to dup the params
+ new_def = @definitions[method_symbol].dup
+ new_def.params = new_def.params.dup
+ new_def.node = @node
+ # This sets up the parameter overrides
+ new_def.instance_eval(&block) if block
+ new_recipe = Chef::Recipe.new(@cookbook_name, @recipe_name, @node, @collection, @definitions, @cookbook_loader)
+ new_recipe.params = new_def.params
+ new_recipe.params[:name] = args[0]
+ new_recipe.instance_eval(&new_def.recipe)
+ else
+ # Otherwise, we're rocking the regular resource call route.
+ method_name = method_symbol.to_s
+ rname = convert_to_class_name(method_name)
+
+ resource = nil
+ begin
+ args << @collection
+ args << @node
+ resource = Chef::Resource.const_get(rname).new(*args)
+ # If we have a resource like this one, we want to steal its state
+ resource.load_prior_resource
+ resource.cookbook_name = @cookbook_name
+ resource.recipe_name = @recipe_name
+ resource.params = @params
+ resource.instance_eval(&block) if block
+ rescue NameError => e
+ if e.to_s =~ /Chef::Resource/
+ raise NameError, "Cannot find #{rname} for #{method_name}\nOriginal: #{e.to_s}"
+ else
+ raise e
+ end
+ end
+ @collection.insert(resource)
+ resource
+ end
+ end
+
+ end
+ end
+end
diff --git a/chef/lib/chef/provider.rb b/chef/lib/chef/provider.rb
index 822ab1e6cd..267a0f41d4 100644
--- a/chef/lib/chef/provider.rb
+++ b/chef/lib/chef/provider.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@opscode.com>)
# Author:: Christopher Walters (<cw@opscode.com>)
-# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# Copyright:: Copyright (c) 2008, 2009 Opscode, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,15 +17,24 @@
# limitations under the License.
#
+require 'chef/mixin/from_file'
+require 'chef/mixin/convert_to_class_name'
+require 'chef/mixin/recipe_definition_dsl_core'
+
class Chef
class Provider
+ include Chef::Mixin::RecipeDefinitionDSLCore
+
attr_accessor :node, :new_resource, :current_resource
- def initialize(node, new_resource)
+ def initialize(node, new_resource, collection=nil, definitions=nil, cookbook_loader=nil)
@node = node
@new_resource = new_resource
@current_resource = nil
+ @collection = collection
+ @definitions = definitions
+ @cookbook_loader = cookbook_loader
end
def load_current_resource
@@ -38,26 +47,39 @@ class Chef
end
class << self
- def build_from_file(filename)
- Class.new self do |cls|
-
+ include Chef::Mixin::ConvertToClassName
+
+ def build_from_file(cookbook_name, filename)
+ pname = filename_to_qualified_string(cookbook_name, filename)
+
+ new_provider_class = Class.new self do |cls|
+
def load_current_resource
# silence Chef::Exceptions::Override exception
end
- # setup DSL's shortcut methods
class << cls
include Chef::Mixin::FromFile
+ # setup DSL's shortcut methods
def action(name, &block)
- define_method("action_#{name}", block)
+ define_method("action_#{name.to_s}") do
+ instance_eval(&block)
+ end
end
end
# load provider definition from file
cls.class_from_file(filename)
-
end
+
+ # register new class as a Chef::Provider
+ pname = filename_to_qualified_string(cookbook_name, filename)
+ class_name = convert_to_class_name(pname)
+ Chef::Provider.const_set(class_name, new_provider_class)
+ Chef::Log.debug("Loaded contents of #{filename} into a provider named #{pname} defined in Chef::Provider::#{class_name}")
+
+ new_provider_class
end
end
diff --git a/chef/lib/chef/provider/cron.rb b/chef/lib/chef/provider/cron.rb
index 9e633053ce..a39cff4f25 100644
--- a/chef/lib/chef/provider/cron.rb
+++ b/chef/lib/chef/provider/cron.rb
@@ -25,8 +25,8 @@ class Chef
class Cron < Chef::Provider
include Chef::Mixin::Command
- def initialize(node, new_resource)
- super(node, new_resource)
+ def initialize(node, new_resource, collection=nil, definitions=nil, cookbook_loader=nil)
+ super(node, new_resource, collection, definitions, cookbook_loader)
@cron_exists = false
@cron_empty = false
end
diff --git a/chef/lib/chef/provider/deploy.rb b/chef/lib/chef/provider/deploy.rb
index 569fb14ffd..bd3bb25795 100644
--- a/chef/lib/chef/provider/deploy.rb
+++ b/chef/lib/chef/provider/deploy.rb
@@ -30,8 +30,8 @@ class Chef
attr_reader :scm_provider, :release_path
- def initialize(node, new_resource)
- super(node, new_resource)
+ def initialize(node, new_resource, collection=nil, definitions=nil, cookbook_loader=nil)
+ super(node, new_resource, collection, definitions, cookbook_loader)
@scm_provider = @new_resource.scm_provider.new(@node, @new_resource)
@release_path = @new_resource.deploy_to + "/releases/#{Time.now.utc.strftime("%Y%m%d%H%M%S")}"
diff --git a/chef/lib/chef/provider/group.rb b/chef/lib/chef/provider/group.rb
index 30bfcb487c..27500d1e2d 100644
--- a/chef/lib/chef/provider/group.rb
+++ b/chef/lib/chef/provider/group.rb
@@ -27,8 +27,8 @@ class Chef
include Chef::Mixin::Command
attr_accessor :group_exists
- def initialize(node, new_resource)
- super(node, new_resource)
+ def initialize(node, new_resource, collection=nil, definitions=nil, cookbook_loader=nil)
+ super(node, new_resource, collection, definitions, cookbook_loader)
@group_exists = true
end
diff --git a/chef/lib/chef/provider/mount.rb b/chef/lib/chef/provider/mount.rb
index a12c298034..404677e6fc 100644
--- a/chef/lib/chef/provider/mount.rb
+++ b/chef/lib/chef/provider/mount.rb
@@ -26,10 +26,6 @@ class Chef
include Chef::Mixin::Command
- def initialize(node, new_resource)
- super(node, new_resource)
- end
-
def action_mount
unless @current_resource.mounted
Chef::Log.debug("#{@new_resource}: attempting to mount")
diff --git a/chef/lib/chef/provider/mount/mount.rb b/chef/lib/chef/provider/mount/mount.rb
index 4de2b54fc6..2012401a17 100644
--- a/chef/lib/chef/provider/mount/mount.rb
+++ b/chef/lib/chef/provider/mount/mount.rb
@@ -27,8 +27,8 @@ class Chef
include Chef::Mixin::Command
- def initialize(node, new_resource)
- super(node, new_resource)
+ def initialize(node, new_resource, collection=nil, definitions=nil, cookbook_loader=nil)
+ super(node, new_resource, collection, definitions, cookbook_loader)
@real_device = nil
end
attr_accessor :real_device
diff --git a/chef/lib/chef/provider/package.rb b/chef/lib/chef/provider/package.rb
index 41390832a6..3b40135c61 100644
--- a/chef/lib/chef/provider/package.rb
+++ b/chef/lib/chef/provider/package.rb
@@ -30,8 +30,8 @@ class Chef
attr_accessor :candidate_version
- def initialize(node, new_resource)
- super(node, new_resource)
+ def initialize(node, new_resource, collection=nil, definitions=nil, cookbook_loader=nil)
+ super(node, new_resource, collection, definitions, cookbook_loader)
@candidate_version = nil
end
diff --git a/chef/lib/chef/provider/package/yum.rb b/chef/lib/chef/provider/package/yum.rb
index efd6fee0ad..d4bfac3e87 100644
--- a/chef/lib/chef/provider/package/yum.rb
+++ b/chef/lib/chef/provider/package/yum.rb
@@ -106,9 +106,9 @@ class Chef
end
end
- def initialize(node, new_resource)
+ def initialize(node, new_resource, collection=nil, definitions=nil, cookbook_loader=nil)
@yum = YumCache.instance
- super(node, new_resource)
+ super(node, new_resource, collection, definitions, cookbook_loader)
end
def load_current_resource
diff --git a/chef/lib/chef/provider/service.rb b/chef/lib/chef/provider/service.rb
index f35c424174..d9c3e86401 100644
--- a/chef/lib/chef/provider/service.rb
+++ b/chef/lib/chef/provider/service.rb
@@ -25,8 +25,8 @@ class Chef
include Chef::Mixin::Command
- def initialize(node, new_resource)
- super(node, new_resource)
+ def initialize(node, new_resource, collection=nil, definitions=nil, cookbook_loader=nil)
+ super(node, new_resource, collection, definitions, cookbook_loader)
@enabled = nil
end
diff --git a/chef/lib/chef/provider/service/init.rb b/chef/lib/chef/provider/service/init.rb
index 39f3a63ee7..95709626dd 100644
--- a/chef/lib/chef/provider/service/init.rb
+++ b/chef/lib/chef/provider/service/init.rb
@@ -25,8 +25,8 @@ class Chef
class Service
class Init < Chef::Provider::Service::Simple
- def initialize(node, new_resource)
- super(node, new_resource)
+ def initialize(node, new_resource, collection=nil, definitions=nil, cookbook_loader=nil)
+ super(node, new_resource, collection, definitions, cookbook_loader)
@init_command = "/etc/init.d/#{@new_resource.service_name}"
end
diff --git a/chef/lib/chef/provider/service/redhat.rb b/chef/lib/chef/provider/service/redhat.rb
index 64bfdc4076..ab52762a39 100644
--- a/chef/lib/chef/provider/service/redhat.rb
+++ b/chef/lib/chef/provider/service/redhat.rb
@@ -25,8 +25,8 @@ class Chef
class Service
class Redhat < Chef::Provider::Service::Init
- def initialize(node, new_resource)
- super(node, new_resource)
+ def initialize(node, new_resource, collection=nil, definitions=nil, cookbook_loader=nil)
+ super(node, new_resource, collection, definitions, cookbook_loader)
@init_command = "/sbin/service #{@new_resource.service_name}"
end
diff --git a/chef/lib/chef/provider/user.rb b/chef/lib/chef/provider/user.rb
index 3f76f8ad4b..5bff74d49b 100644
--- a/chef/lib/chef/provider/user.rb
+++ b/chef/lib/chef/provider/user.rb
@@ -29,8 +29,8 @@ class Chef
attr_accessor :user_exists, :locked
- def initialize(node, new_resource)
- super(node, new_resource)
+ def initialize(node, new_resource, collection=nil, definitions=nil, cookbook_loader=nil)
+ super(node, new_resource, collection, definitions, cookbook_loader)
@user_exists = true
@locked = nil
end
diff --git a/chef/lib/chef/recipe.rb b/chef/lib/chef/recipe.rb
index a5c5dcb4a2..fdcdfc9e98 100644
--- a/chef/lib/chef/recipe.rb
+++ b/chef/lib/chef/recipe.rb
@@ -1,6 +1,7 @@
#
# Author:: Adam Jacob (<adam@opscode.com>)
-# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# Author:: Christopher Walters (<cw@opscode.com>)
+# Copyright:: Copyright (c) 2008, 2009 Opscode, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,7 +21,7 @@ require 'chef/resource'
Dir[File.join(File.dirname(__FILE__), 'resource/**/*.rb')].sort.each { |lib| require lib }
require 'chef/mixin/from_file'
require 'chef/mixin/language'
-require 'chef/mixin/convert_to_class_name'
+require 'chef/mixin/recipe_definition_dsl_core'
require 'chef/resource_collection'
require 'chef/cookbook_loader'
require 'chef/rest'
@@ -31,8 +32,8 @@ class Chef
include Chef::Mixin::FromFile
include Chef::Mixin::Language
- include Chef::Mixin::ConvertToClassName
-
+ include Chef::Mixin::RecipeDefinitionDSLCore
+
attr_accessor :cookbook_name, :recipe_name, :recipe, :node, :collection,
:definitions, :params, :cookbook_loader
@@ -40,25 +41,9 @@ class Chef
@cookbook_name = cookbook_name
@recipe_name = recipe_name
@node = node
-
- if collection
- @collection = collection
- else
- @collection = Chef::ResourceCollection.new()
- end
-
- if definitions
- @definitions = definitions
- else
- @definitions = Hash.new
- end
-
- if cookbook_loader
- @cookbook_loader = cookbook_loader
- else
- @cookbook_loader = Chef::CookbookLoader.new()
- end
-
+ @collection = collection || Chef::ResourceCollection.new
+ @definitions = definitions || Hash.new
+ @cookbook_loader = cookbook_loader || Chef::CookbookLoader.new
@params = Hash.new
end
@@ -155,47 +140,5 @@ class Chef
end
end
- def method_missing(method_symbol, *args, &block)
- resource = nil
- # 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 @definitions.has_key?(method_symbol)
- # This dupes the high level object, but we still need to dup the params
- new_def = @definitions[method_symbol].dup
- new_def.params = new_def.params.dup
- new_def.node = @node
- # This sets up the parameter overrides
- new_def.instance_eval(&block) if block
- new_recipe = Chef::Recipe.new(@cookbook_name, @recipe_name, @node, @collection, @definitions, @cookbook_loader)
- new_recipe.params = new_def.params
- new_recipe.params[:name] = args[0]
- new_recipe.instance_eval(&new_def.recipe)
- else
- method_name = method_symbol.to_s
- # Otherwise, we're rocking the regular resource call route.
- rname = convert_to_class_name(method_name, Chef::Resource)
-
- begin
- args << @collection
- args << @node
- resource = eval(rname).new(*args)
- # If we have a resource like this one, we want to steal it's state
- resource.load_prior_resource
- resource.cookbook_name = @cookbook_name
- resource.recipe_name = @recipe_name
- resource.params = @params
- resource.instance_eval(&block) if block
- rescue Exception => e
- if e.kind_of?(NameError) && e.to_s =~ /Chef::Resource/
- raise NameError, "Cannot find #{rname} for #{method_name}\nOriginal: #{e.to_s}"
- else
- raise e
- end
- end
- @collection << resource
- resource
- end
- end
end
end
diff --git a/chef/lib/chef/resource.rb b/chef/lib/chef/resource.rb
index 3e5157304b..77fa606554 100644
--- a/chef/lib/chef/resource.rb
+++ b/chef/lib/chef/resource.rb
@@ -90,7 +90,11 @@ class Chef
begin
Chef::Provider.const_get(convert_to_class_name(arg.to_s))
rescue NameError => e
- raise ArgumentError, "Undefined provider for #{arg}"
+ if e.to_s =~ /Chef::Provider/
+ raise ArgumentError, "No provider found to match '#{arg}'"
+ else
+ raise e
+ end
end
else
arg
@@ -245,8 +249,18 @@ class Chef
resource
end
- def build_from_file(filename)
- Class.new self do |cls|
+ include Chef::Mixin::ConvertToClassName
+
+ def attribute(attr_name, validation_opts={})
+ define_method(attr_name.to_sym) do |arg|
+ set_or_return(attr_name.to_sym, arg, validation_opts)
+ end
+ end
+
+ def build_from_file(cookbook_name, filename)
+ rname = filename_to_qualified_string(cookbook_name, filename)
+
+ new_resource_class = Class.new self do |cls|
# default initialize method that ensures that when initialize is finally
# wrapped (see below), super is called in the event that the resource
@@ -264,19 +278,9 @@ class Chef
@actions_to_create
end
- def actions_to_create=(val)
- @actions_to_create = val
- end
-
define_method(:actions) do |*action_names|
actions_to_create.push(*action_names)
end
-
- def attribute(attr_name, validation_opts={})
- define_method(attr_name.to_sym) do |arg|
- set_or_return(attr_name.to_sym, arg, validation_opts)
- end
- end
end
# load resource definition from file
@@ -285,15 +289,22 @@ class Chef
# create a new constructor that wraps the old one and adds the actions
# specified in the DSL
old_init = instance_method(:initialize)
-
+
define_method(:initialize) do |name, *optional_args|
collection = optional_args.shift
node = optional_args.shift
+ @resource_name = rname.to_sym
old_init.bind(self).call(name, collection, node)
allowed_actions.push(self.class.actions_to_create).flatten!
end
-
end
+
+ # register new class as a Chef::Resource
+ class_name = convert_to_class_name(rname)
+ Chef::Resource.const_set(class_name, new_resource_class)
+ Chef::Log.debug("Loaded contents of #{filename} into a resource named #{rname} defined in Chef::Resource::#{class_name}")
+
+ new_resource_class
end
end
diff --git a/chef/lib/chef/resource_collection.rb b/chef/lib/chef/resource_collection.rb
index 8a6655b704..7e219024c1 100644
--- a/chef/lib/chef/resource_collection.rb
+++ b/chef/lib/chef/resource_collection.rb
@@ -1,6 +1,7 @@
#
# Author:: Adam Jacob (<adam@opscode.com>)
-# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# Author:: Christopher Walters (<cw@opscode.com>)
+# Copyright:: Copyright (c) 2008, 2009 Opscode, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,10 +22,11 @@ require 'chef/resource'
class Chef
class ResourceCollection
include Enumerable
-
+
def initialize
@resources = Array.new
@resources_by_name = Hash.new
+ @currently_executing_resource_idx = nil
end
def [](index)
@@ -36,7 +38,7 @@ class Chef
@resources[index] = arg
@resources_by_name[arg.to_s] = index
end
-
+
def <<(*args)
args.flatten.each do |a|
is_chef_resource(a)
@@ -44,6 +46,23 @@ class Chef
@resources_by_name[a.to_s] = @resources.length - 1
end
end
+
+ def insert(resource)
+ is_chef_resource(resource)
+ if @currently_executing_resource_idx
+ # in the middle of executing a run, so any resources inserted now should
+ # be placed after the currently executing resource
+ @resources.insert(@currently_executing_resource_idx + 1, resource)
+ # update name -> location mappings and register new resource
+ @resources_by_name.each_key do |key|
+ @resources_by_name[key] += 1 if @resources_by_name[key] > @currently_executing_resource_idx
+ end
+ @resources_by_name[resource.to_s] = @currently_executing_resource_idx + 1
+ else
+ @resources << resource
+ @resources_by_name[resource.to_s] = @resources.length - 1
+ end
+ end
def push(*args)
args.flatten.each do |a|
@@ -58,6 +77,13 @@ class Chef
yield r
end
end
+
+ def execute_each_resource
+ @resources.each_with_index do |r, idx|
+ @currently_executing_resource_idx = idx
+ yield r
+ end
+ end
def each_index
@resources.each_index do |i|
@@ -173,4 +199,4 @@ class Chef
true
end
end
-end \ No newline at end of file
+end
diff --git a/chef/lib/chef/runner.rb b/chef/lib/chef/runner.rb
index f1742dd991..2950908f69 100644
--- a/chef/lib/chef/runner.rb
+++ b/chef/lib/chef/runner.rb
@@ -26,7 +26,7 @@ class Chef
include Chef::Mixin::ParamsValidate
- def initialize(node, collection)
+ def initialize(node, collection, definitions=nil, cookbook_loader=nil)
validate(
{
:node => node,
@@ -43,13 +43,15 @@ class Chef
)
@node = node
@collection = collection
+ @definitions = definitions
+ @cookbook_loader = cookbook_loader
end
def build_provider(resource)
provider_klass = resource.provider
provider_klass ||= Chef::Platform.find_provider_for_node(@node, resource)
Chef::Log.debug("#{resource} using #{provider_klass.to_s}")
- provider = provider_klass.new(@node, resource)
+ provider = provider_klass.new(@node, resource, @collection, @definitions, @cookbook_loader)
provider.load_current_resource
provider
end
@@ -58,7 +60,7 @@ class Chef
delayed_actions = Hash.new
- @collection.each do |resource|
+ @collection.execute_each_resource do |resource|
begin
Chef::Log.debug("Processing #{resource}")
diff --git a/chef/spec/unit/client_spec.rb b/chef/spec/unit/client_spec.rb
index 303306a659..8fd1242da8 100644
--- a/chef/spec/unit/client_spec.rb
+++ b/chef/spec/unit/client_spec.rb
@@ -32,6 +32,8 @@ describe Chef::Client, "run" do
:register,
:authenticate,
:sync_library_files,
+ :sync_provider_files,
+ :sync_resource_files,
:sync_attribute_files,
:sync_definitions,
:sync_recipes,
@@ -88,6 +90,16 @@ describe Chef::Client, "run" do
@client.run
end
+ it "should synchronize providers from the server" do
+ @client.should_receive(:sync_provider_files).and_return(true)
+ @client.run
+ end
+
+ it "should synchronize resources from the server" do
+ @client.should_receive(:sync_resource_files).and_return(true)
+ @client.run
+ end
+
it "should save the nodes state on the server (twice!)" do
@client.should_receive(:save_node).exactly(3).times.and_return(true)
@client.run
diff --git a/chef/spec/unit/compile_spec.rb b/chef/spec/unit/compile_spec.rb
index beda2a0e95..79ca3c5d06 100644
--- a/chef/spec/unit/compile_spec.rb
+++ b/chef/spec/unit/compile_spec.rb
@@ -22,7 +22,15 @@ describe Chef::Compile do
before(:each) do
Chef::Config.node_path(File.join(File.dirname(__FILE__), "..", "data", "compile", "nodes"))
Chef::Config.cookbook_path(File.join(File.dirname(__FILE__), "..", "data", "compile", "cookbooks"))
- @compile = Chef::Compile.new
+ node = Chef::Node.new
+ node.stub!(:determine_node_name).and_return(true)
+ node.stub!(:load_libraries).and_return(true)
+ node.stub!(:load_providers).and_return(true)
+ node.stub!(:load_resources).and_return(true)
+ node.stub!(:load_attributes).and_return(true)
+ node.stub!(:load_definitions).and_return(true)
+ node.stub!(:load_recipes).and_return(true)
+ @compile = Chef::Compile.new(node)
end
it "should create a new Chef::Compile" do
diff --git a/chef/spec/unit/runner_spec.rb b/chef/spec/unit/runner_spec.rb
index fca8af4542..5eef9f0e6f 100644
--- a/chef/spec/unit/runner_spec.rb
+++ b/chef/spec/unit/runner_spec.rb
@@ -55,7 +55,7 @@ describe Chef::Runner do
end
it "should pass each resource in the collection to a provider" do
- @collection.should_receive(:each).once
+ @collection.should_receive(:execute_each_resource).once
@runner.converge
end
@@ -153,4 +153,4 @@ describe Chef::Runner do
@runner.converge
end
-end \ No newline at end of file
+end
diff --git a/features/cookbooks/lightweight_resources_and_providers.feature b/features/cookbooks/lightweight_resources_and_providers.feature
index 908d7a129a..1c567906fd 100644
--- a/features/cookbooks/lightweight_resources_and_providers.feature
+++ b/features/cookbooks/lightweight_resources_and_providers.feature
@@ -19,8 +19,15 @@ Feature: Light-weight resources and providers
| provider_is_a_symbol | Provider is a symbol |
| provider_is_a_class | Provider is a class |
+ @solo
+ Scenario: Chef solo properly handles providers that invoke resources in their action definitions
+ Given a local cookbook repository
+ When I run chef-solo with the 'lwrp::provider_invokes_resource' recipe
+ Then the run should exit '0'
+ And a file named 'lwrp_touch_file.txt' should exist
+
@client @api
- Scenario Outline: Chef client handles light-weight resources and providers
+ Scenario Outline: Chef-client handles light-weight resources and providers
Given a validated node
And it includes the recipe 'lwrp::<recipe>'
When I run the chef-client
@@ -37,3 +44,12 @@ Feature: Light-weight resources and providers
| provider_is_a_string | Provider is a string |
| provider_is_a_symbol | Provider is a symbol |
| provider_is_a_class | Provider is a class |
+
+ @client @api
+ Scenario: Chef-client properly handles providers that invoke resources in their action definitions
+ Given a validated node
+ And it includes the recipe 'lwrp::provider_invokes_resource'
+ When I run the chef-client
+ Then the run should exit '0'
+ And a file named 'lwrp_touch_file.txt' should exist
+
diff --git a/features/data/cookbooks/lwrp/providers/default.rb b/features/data/cookbooks/lwrp/providers/default.rb
index f51dcae369..f66e2914f6 100644
--- a/features/data/cookbooks/lwrp/providers/default.rb
+++ b/features/data/cookbooks/lwrp/providers/default.rb
@@ -1,3 +1,9 @@
action :print_message do
puts new_resource.message
end
+
+action :touch_file do
+ file "#{node[:tmpdir]}/#{new_resource.filename}" do
+ action :create
+ end
+end
diff --git a/features/data/cookbooks/lwrp/recipes/non_default_provider.rb b/features/data/cookbooks/lwrp/recipes/non_default_provider.rb
index c37a7b1949..019f3aa9ab 100644
--- a/features/data/cookbooks/lwrp/recipes/non_default_provider.rb
+++ b/features/data/cookbooks/lwrp/recipes/non_default_provider.rb
@@ -2,5 +2,5 @@ lwrp :non_default_provider do
message "Non-default provider"
action :print_message
- provider Chef::Provider::LwrpLwpNonDefault
+ provider :lwrp_lwp_non_default
end
diff --git a/features/data/cookbooks/lwrp/recipes/provider_invokes_resource.rb b/features/data/cookbooks/lwrp/recipes/provider_invokes_resource.rb
new file mode 100644
index 0000000000..c2e94aa064
--- /dev/null
+++ b/features/data/cookbooks/lwrp/recipes/provider_invokes_resource.rb
@@ -0,0 +1,6 @@
+lwrp :lwrp_provider_invokes_resource do
+ filename "lwrp_touch_file.txt"
+ action :touch_file
+
+ provider :lwrp
+end
diff --git a/features/data/cookbooks/lwrp/resources/default.rb b/features/data/cookbooks/lwrp/resources/default.rb
index c29654eba3..00be531184 100644
--- a/features/data/cookbooks/lwrp/resources/default.rb
+++ b/features/data/cookbooks/lwrp/resources/default.rb
@@ -1,3 +1,4 @@
-actions :print_message
+actions :print_message, :touch_file
attribute :message, :kind_of => String
+attribute :filename, :kind_of => String