summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLamont Granquist <lamont@scriptkiddie.org>2016-04-15 11:49:18 -0700
committerLamont Granquist <lamont@scriptkiddie.org>2016-04-15 11:50:01 -0700
commit544a9127cf3ed738d642b2490ed3063f1623c36c (patch)
tree9faf191b88bab71583c257d51298cdc7360b5107
parentf99b3362694d9cd9f70aa808b96a3fe3420650e1 (diff)
downloadchef-544a9127cf3ed738d642b2490ed3063f1623c36c.tar.gz
add better resource manipulation API
deprecates chef_rewind functionality completely and adds a few more features
-rw-r--r--lib/chef/dsl/declare_resource.rb189
-rw-r--r--lib/chef/provider.rb15
-rw-r--r--lib/chef/recipe.rb3
-rw-r--r--lib/chef/resource_collection.rb5
-rw-r--r--lib/chef/resource_collection/resource_list.rb10
-rw-r--r--lib/chef/resource_collection/resource_set.rb25
-rw-r--r--spec/unit/dsl/declare_resource_spec.rb335
-rw-r--r--spec/unit/resource_collection_spec.rb30
8 files changed, 585 insertions, 27 deletions
diff --git a/lib/chef/dsl/declare_resource.rb b/lib/chef/dsl/declare_resource.rb
index e57bc0f4c4..454afdc2b8 100644
--- a/lib/chef/dsl/declare_resource.rb
+++ b/lib/chef/dsl/declare_resource.rb
@@ -1,6 +1,6 @@
#--
# Author:: Adam Jacob (<adam@chef.io>)
-# Author:: Christopher Walters (<cw@chef.io>)
+# Author:: Christopher Walters
# Copyright:: Copyright 2008-2016, 2009-2015 Chef Software, Inc.
# License:: Apache License, Version 2.0
#
@@ -23,7 +23,180 @@ class Chef
module DSL
module DeclareResource
+ # Helper for switching run_contexts. Allows for using :parent or :root in place of
+ # passing the run_context. Executes the block in the run_context. Returns the return
+ # value of the passed block.
#
+ # @param rc [Chef::RunContext,Symbol] Either :root, :parent or a Chef::RunContext
+ #
+ # @return return value of the block
+ #
+ # @example
+ # # creates/returns a 'service[foo]' resource in the root run_context
+ # resource = with_run_context(:root)
+ # edit_resource(:service, "foo") do
+ # action :nothing
+ # end
+ # end
+ #
+ def with_run_context(rc, &block)
+ raise ArgumentError, "with_run_context is useless without a block" unless block_given?
+ @old_run_context = @run_context
+ @run_context =
+ case rc
+ when Chef::RunContext
+ rc
+ when :root
+ Chef.run_context
+ when :parent
+ run_context.parent_run_context
+ else
+ raise "bad argument to run_context helper, must be :root, :parent, or a Chef::RunContext"
+ end
+ ret = yield
+ @run_context = @old_run_context
+ ret
+ end
+
+ # Lookup a resource in the resource collection by name and delete it. This
+ # will raise Chef::Exceptions::ResourceNotFound if the resource is not found.
+ #
+ # @param type [Symbol] The type of resource (e.g. `:file` or `:package`)
+ # @param name [String] The name of the resource (e.g. '/x/y.txt' or 'apache2')
+ # @param run_context [Chef::RunContext] the run_context of the resource collection to operate on
+ #
+ # @return [Chef::Resource] The resource
+ #
+ # @example
+ # delete_resource(:template, '/x/y.txy')
+ #
+ def delete_resource!(type, name, run_context: self.run_context)
+ run_context.resource_collection.delete("#{type}[#{name}]")
+ end
+
+ # Lookup a resource in the resource collection by name and delete it. Returns
+ # nil if the resource is not found and should not fail.
+ #
+ # @param type [Symbol] The type of resource (e.g. `:file` or `:package`)
+ # @param name [String] The name of the resource (e.g. '/x/y.txt' or 'apache2')
+ # @param run_context [Chef::RunContext] the run_context of the resource collection to operate on
+ #
+ # @return [Chef::Resource] The resource
+ #
+ # @example
+ # delete_resource(:template, '/x/y.txy')
+ #
+ def delete_resource(type, name, run_context: self.run_context)
+ delete_resource!(type, name, run_context: run_context)
+ rescue Chef::Exceptions::ResourceNotFound
+ nil
+ end
+
+ # Lookup a resource in the resource collection by name and edit the resource. If the resource is not
+ # found this will raise Chef::Exceptions::ResourceNotFound. This is the correct API to use for
+ # "chef_rewind" functionality.
+ #
+ # @param type [Symbol] The type of resource (e.g. `:file` or `:package`)
+ # @param name [String] The name of the resource (e.g. '/x/y.txt' or 'apache2')
+ # @param run_context [Chef::RunContext] the run_context of the resource collection to operate on
+ # @param resource_attrs_block A block that lets you set attributes of the
+ # resource (it is instance_eval'd on the resource instance).
+ #
+ # @return [Chef::Resource] The updated resource
+ #
+ # @example
+ # edit_resource!(:template, '/x/y.txy') do
+ # cookbook_name: cookbook_name
+ # end
+ #
+ def edit_resource!(type, name, created_at = nil, run_context: self.run_context, &resource_attrs_block)
+ resource = find_resource!(type, name, run_context: run_context)
+ resource.instance_eval(&resource_attrs_block) if block_given?
+ resource
+ end
+
+ # Lookup a resource in the resource collection by name. If it exists,
+ # return it. If it does not exist, create it. This is a useful function
+ # for accumulator patterns. In CRUD terminology this is an "upsert" operation and is
+ # used to assert that the resource must exist with the specified properties.
+ #
+ # @param type [Symbol] The type of resource (e.g. `:file` or `:package`)
+ # @param name [String] The name of the resource (e.g. '/x/y.txt' or 'apache2')
+ # @param created_at [String] The caller of the resource. Use `caller[0]`
+ # to get the caller of your function. Defaults to the caller of this
+ # function.
+ # @param run_context [Chef::RunContext] the run_context of the resource collection to operate on
+ # @param resource_attrs_block A block that lets you set attributes of the
+ # resource (it is instance_eval'd on the resource instance).
+ #
+ # @return [Chef::Resource] The updated or created resource
+ #
+ # @example
+ # resource = edit_resource(:template, '/x/y.txy') do
+ # source "y.txy.erb"
+ # variables {}
+ # end
+ # resource.variables.merge!({ home: "/home/klowns" })
+ #
+ def edit_resource(type, name, created_at = nil, run_context: self.run_context, &resource_attrs_block)
+ edit_resource!(type, name, created_at, run_context: run_context, &resource_attrs_block)
+ rescue Chef::Exceptions::ResourceNotFound
+ declare_resource(type, name, created_at, run_context: run_context, &resource_attrs_block)
+ end
+
+ # Lookup a resource in the resource collection by name. If the resource is not
+ # found this will raise Chef::Exceptions::ResourceNotFound. This API is identical to the
+ # resources() call and while it is a synonym it is not intended to deprecate that call.
+ #
+ # @param type [Symbol] The type of resource (e.g. `:file` or `:package`)
+ # @param name [String] The name of the resource (e.g. '/x/y.txt' or 'apache2')
+ # @param run_context [Chef::RunContext] the run_context of the resource collection to operate on
+ #
+ # @return [Chef::Resource] The updated resource
+ #
+ # @example
+ # resource = find_resource!(:template, '/x/y.txy')
+ #
+ def find_resource!(type, name, run_context: self.run_context)
+ raise ArgumentError, "find_resource! does not take a block" if block_given?
+ run_context.resource_collection.find(type => name)
+ end
+
+ # Lookup a resource in the resource collection by name. If the resource is not found
+ # the will be no exception raised and the call will return nil. If a block is given and
+ # no resource is found it will create the resource using the block, if the resource is
+ # found then the block will not be applied. The block version is similar to create_if_missing
+ #
+ # @param type [Symbol] The type of resource (e.g. `:file` or `:package`)
+ # @param name [String] The name of the resource (e.g. '/x/y.txt' or 'apache2')
+ # @param run_context [Chef::RunContext] the run_context of the resource collection to operate on
+ #
+ # @return [Chef::Resource] The updated resource
+ #
+ # @example
+ # if ( find_resource(:template, '/x/y.txy') )
+ # # do something
+ # else
+ # # don't worry about the error
+ # end
+ #
+ # @example
+ # # this API can be used to return a resource from an outer run context, and will only create
+ # # an action :nothing service if one does not already exist.
+ # resource = with_run_context(:root) do
+ # find_resource(:service, 'whatever') do
+ # action :nothing
+ # end
+ # end
+ #
+ def find_resource(type, name, created_at: nil, run_context: self.run_context, &resource_attrs_block)
+ find_resource!(type, name, run_context: run_context)
+ rescue Chef::Exceptions::ResourceNotFound
+ if block_given?
+ declare_resource(type, name, created_at, run_context: run_context, &resource_attrs_block)
+ end # returns nil otherwise
+ end
+
# Instantiates a resource (via #build_resource), then adds it to the
# resource collection. Note that resource classes are looked up directly,
# so this will create the resource you intended even if the method name
@@ -34,6 +207,7 @@ class Chef
# @param created_at [String] The caller of the resource. Use `caller[0]`
# to get the caller of your function. Defaults to the caller of this
# function.
+ # @param run_context [Chef::RunContext] the run_context of the resource collection to operate on
# @param resource_attrs_block A block that lets you set attributes of the
# resource (it is instance_eval'd on the resource instance).
#
@@ -52,11 +226,9 @@ class Chef
created_at ||= caller[0]
if create_if_missing
- begin
- resource = run_context.resource_collection.find(type => name)
- return resource
- rescue Chef::Exceptions::ResourceNotFound
- end
+ Chef::Log.deprecation "build_resource with a create_if_missing flag is deprecated, use edit_resource instead"
+ # midly goofy since we call edit_resoruce only to re-call ourselves, but that's why its deprecated...
+ return edit_resource(type, name, created_at, run_context: run_context, &resource_attrs_block)
end
resource = build_resource(type, name, created_at, &resource_attrs_block)
@@ -65,7 +237,6 @@ class Chef
resource
end
- #
# Instantiate a resource of the given +type+ with the given +name+ and
# attributes as given in the +resource_attrs_block+.
#
@@ -76,6 +247,7 @@ class Chef
# @param created_at [String] The caller of the resource. Use `caller[0]`
# to get the caller of your function. Defaults to the caller of this
# function.
+ # @param run_context [Chef::RunContext] the run_context of the resource collection to operate on
# @param resource_attrs_block A block that lets you set attributes of the
# resource (it is instance_eval'd on the resource instance).
#
@@ -88,6 +260,9 @@ class Chef
#
def build_resource(type, name, created_at = nil, run_context: self.run_context, &resource_attrs_block)
created_at ||= caller[0]
+
+ # this needs to be lazy in order to avoid circular dependencies since ResourceBuilder
+ # will requires the entire provider+resolver universe
require "chef/resource_builder" unless defined?(Chef::ResourceBuilder)
Chef::ResourceBuilder.new(
diff --git a/lib/chef/provider.rb b/lib/chef/provider.rb
index ebabb7b9eb..03b546c09d 100644
--- a/lib/chef/provider.rb
+++ b/lib/chef/provider.rb
@@ -32,6 +32,14 @@ class Chef
class Provider
require "chef/mixin/why_run"
require "chef/mixin/provides"
+
+ attr_accessor :new_resource
+ attr_accessor :current_resource
+ attr_accessor :run_context
+
+ attr_reader :recipe_name
+ attr_reader :cookbook_name
+
include Chef::Mixin::WhyRun
extend Chef::Mixin::Provides
@@ -43,13 +51,6 @@ class Chef
true
end
- attr_accessor :new_resource
- attr_accessor :current_resource
- attr_accessor :run_context
-
- attr_reader :recipe_name
- attr_reader :cookbook_name
-
#--
# TODO: this should be a reader, and the action should be passed in the
# constructor; however, many/most subclasses override the constructor so
diff --git a/lib/chef/recipe.rb b/lib/chef/recipe.rb
index 403d393fcd..3cc5634dc8 100644
--- a/lib/chef/recipe.rb
+++ b/lib/chef/recipe.rb
@@ -34,14 +34,13 @@ class Chef
# == Chef::Recipe
# A Recipe object is the context in which Chef recipes are evaluated.
class Recipe
+ attr_accessor :cookbook_name, :recipe_name, :recipe, :params, :run_context
include Chef::DSL::Recipe
include Chef::Mixin::FromFile
include Chef::Mixin::Deprecation
- attr_accessor :cookbook_name, :recipe_name, :recipe, :params, :run_context
-
# Parses a potentially fully-qualified recipe name into its
# cookbook name and recipe short name.
#
diff --git a/lib/chef/resource_collection.rb b/lib/chef/resource_collection.rb
index 1429c25d7f..8eaa2961c4 100644
--- a/lib/chef/resource_collection.rb
+++ b/lib/chef/resource_collection.rb
@@ -59,6 +59,11 @@ class Chef
end
end
+ def delete(key)
+ resource_list.delete(key)
+ resource_set.delete(key)
+ end
+
# @deprecated
def []=(index, resource)
Chef::Log.warn("`[]=` is deprecated, use `insert` (which only inserts at the end)")
diff --git a/lib/chef/resource_collection/resource_list.rb b/lib/chef/resource_collection/resource_list.rb
index 37eb12a107..9fe012d4c3 100644
--- a/lib/chef/resource_collection/resource_list.rb
+++ b/lib/chef/resource_collection/resource_list.rb
@@ -67,6 +67,16 @@ class Chef
end
end
+ def delete(key)
+ raise ArgumentError, "Must pass a Chef::Resource or String to delete" unless key.is_a?(String) || key.is_a?(Chef::Resource)
+ key = key.to_s
+ ret = @resources.reject! { |r| r.to_s == key }
+ if ret.nil?
+ raise Chef::Exceptions::ResourceNotFound, "Cannot find a resource matching #{key} (did you define it first?)"
+ end
+ ret
+ end
+
# @deprecated - can be removed when it is removed from resource_collection.rb
def []=(index, resource)
@resources[index] = resource
diff --git a/lib/chef/resource_collection/resource_set.rb b/lib/chef/resource_collection/resource_set.rb
index 88354de31d..99be025cd5 100644
--- a/lib/chef/resource_collection/resource_set.rb
+++ b/lib/chef/resource_collection/resource_set.rb
@@ -49,18 +49,22 @@ class Chef
end
def lookup(key)
- case
- when key.kind_of?(String)
- lookup_by = key
- when key.kind_of?(Chef::Resource)
- lookup_by = create_key(key.resource_name, key.name)
- else
- raise ArgumentError, "Must pass a Chef::Resource or String to lookup"
+ raise ArgumentError, "Must pass a Chef::Resource or String to lookup" unless key.is_a?(String) || key.is_a?(Chef::Resource)
+ key = key.to_s
+ res = @resources_by_key[key]
+ unless res
+ raise Chef::Exceptions::ResourceNotFound, "Cannot find a resource matching #{key} (did you define it first?)"
end
+ res
+ end
- res = @resources_by_key[lookup_by]
- unless res
- raise Chef::Exceptions::ResourceNotFound, "Cannot find a resource matching #{lookup_by} (did you define it first?)"
+ def delete(key)
+ raise ArgumentError, "Must pass a Chef::Resource or String to delete" unless key.is_a?(String) || key.is_a?(Chef::Resource)
+ key = key.to_s
+ res = @resources_by_key.delete(key)
+
+ if res == @resources_by_key.default
+ raise Chef::Exceptions::ResourceNotFound, "Cannot find a resource matching #{key} (did you define it first?)"
end
res
end
@@ -164,7 +168,6 @@ class Chef
end
return results
end
-
end
end
end
diff --git a/spec/unit/dsl/declare_resource_spec.rb b/spec/unit/dsl/declare_resource_spec.rb
new file mode 100644
index 0000000000..6dd9317c21
--- /dev/null
+++ b/spec/unit/dsl/declare_resource_spec.rb
@@ -0,0 +1,335 @@
+#
+# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::ResourceCollection do
+ let(:run_context) do
+ cookbook_repo = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "data", "cookbooks"))
+ cookbook_loader = Chef::CookbookLoader.new(cookbook_repo)
+ cookbook_loader.load_cookbooks
+ node = Chef::Node.new
+ cookbook_collection = Chef::CookbookCollection.new(cookbook_loader)
+ events = Chef::EventDispatch::Dispatcher.new
+ Chef::RunContext.new(node, cookbook_collection, events)
+ end
+
+ let(:recipe) do
+ Chef::Recipe.new("hjk", "test", run_context)
+ end
+
+ describe "#declare_resource" do
+ before do
+ recipe.declare_resource(:zen_master, "monkey") do
+ something true
+ end
+ end
+
+ it "inserts into the resource collection" do
+ expect(run_context.resource_collection.first.to_s).to eql("zen_master[monkey]")
+ end
+
+ it "sets the property from the block" do
+ expect(run_context.resource_collection.first.something).to be true
+ end
+ end
+
+ describe "#edit_resource!" do
+ it "raises if nothing is found" do
+ expect {
+ recipe.edit_resource!(:zen_master, "monkey") do
+ something true
+ end
+ }.to raise_error(Chef::Exceptions::ResourceNotFound)
+ end
+
+ it "raises if nothing is found and no block is given" do
+ expect {
+ recipe.edit_resource!(:zen_master, "monkey")
+ }.to raise_error(Chef::Exceptions::ResourceNotFound)
+ end
+
+ it "edits the resource if it finds one" do
+ resource = recipe.declare_resource(:zen_master, "monkey") do
+ something false
+ end
+ expect(
+ recipe.edit_resource!(:zen_master, "monkey") do
+ something true
+ end
+ ).to eql(resource)
+ expect(run_context.resource_collection.all_resources.size).to eql(1)
+ expect(run_context.resource_collection.first.something).to be true
+ end
+
+ it "acts like find_resource! if not given a block and the resource exists" do
+ resource = recipe.declare_resource(:zen_master, "monkey") do
+ something false
+ end
+ expect(
+ recipe.edit_resource!(:zen_master, "monkey")
+ ).to eql(resource)
+ expect(run_context.resource_collection.all_resources.size).to eql(1)
+ expect(run_context.resource_collection.first.something).to be false
+ end
+ end
+
+ describe "#edit_resource" do
+ it "inserts a resource if nothing is found" do
+ resource = recipe.edit_resource(:zen_master, "monkey") do
+ something true
+ end
+ expect(run_context.resource_collection.all_resources.size).to eql(1)
+ expect(run_context.resource_collection.first).to eql(resource)
+ expect(run_context.resource_collection.first.something).to be true
+ end
+
+ it "inserts a resource even if not given a block" do
+ resource = recipe.edit_resource(:zen_master, "monkey")
+ expect(run_context.resource_collection.all_resources.size).to eql(1)
+ expect(run_context.resource_collection.first).to eql(resource)
+ end
+
+ it "edits the resource if it finds one" do
+ resource = recipe.declare_resource(:zen_master, "monkey") do
+ something false
+ end
+ expect(
+ recipe.edit_resource(:zen_master, "monkey") do
+ something true
+ end
+ ).to eql(resource)
+ expect(run_context.resource_collection.all_resources.size).to eql(1)
+ expect(run_context.resource_collection.first.something).to be true
+ end
+
+ it "acts like find_resource if not given a block and the resource exists" do
+ resource = recipe.declare_resource(:zen_master, "monkey") do
+ something false
+ end
+ expect(
+ recipe.edit_resource(:zen_master, "monkey")
+ ).to eql(resource)
+ expect(run_context.resource_collection.all_resources.size).to eql(1)
+ expect(run_context.resource_collection.first.something).to be false
+ end
+ end
+
+ describe "#find_resource!" do
+ it "raises if nothing is found" do
+ expect {
+ recipe.find_resource!(:zen_master, "monkey")
+ }.to raise_error(Chef::Exceptions::ResourceNotFound)
+ end
+
+ it "raises if given a block" do
+ resource = recipe.declare_resource(:zen_master, "monkey") do
+ something false
+ end
+ expect {
+ recipe.find_resource!(:zen_master, "monkey") do
+ something false
+ end
+ }.to raise_error(ArgumentError)
+ end
+
+ it "returns the resource if it finds one" do
+ resource = recipe.declare_resource(:zen_master, "monkey") do
+ something false
+ end
+ expect(
+ recipe.find_resource!(:zen_master, "monkey")
+ ).to eql(resource)
+ expect(run_context.resource_collection.all_resources.size).to eql(1)
+ expect(run_context.resource_collection.first.something).to be false
+ end
+ end
+
+ describe "#find_resource without block" do
+ it "returns nil if nothing is found" do
+ expect(recipe.find_resource(:zen_master, "monkey")).to be nil
+ expect(run_context.resource_collection.all_resources.size).to eql(0)
+ end
+
+ it "returns the resource if it finds one" do
+ resource = recipe.declare_resource(:zen_master, "monkey") do
+ something false
+ end
+ expect(
+ recipe.find_resource(:zen_master, "monkey")
+ ).to eql(resource)
+ expect(run_context.resource_collection.all_resources.size).to eql(1)
+ expect(run_context.resource_collection.first.something).to be false
+ end
+ end
+
+ describe "#find_resource with block" do
+ it "inserts a resource if nothing is found" do
+ resource = recipe.find_resource(:zen_master, "monkey") do
+ something true
+ end
+ expect(run_context.resource_collection.all_resources.size).to eql(1)
+ expect(run_context.resource_collection.first).to eql(resource)
+ expect(run_context.resource_collection.first.something).to be true
+ end
+
+ it "returns the resource if it finds one" do
+ resource = recipe.declare_resource(:zen_master, "monkey") do
+ something false
+ end
+ expect(
+ recipe.find_resource(:zen_master, "monkey") do
+ something true
+ end
+ ).to eql(resource)
+ expect(run_context.resource_collection.all_resources.size).to eql(1)
+ expect(run_context.resource_collection.first.something).to be false
+ end
+ end
+
+ describe "#delete_resource" do
+ it "returns nil if nothing is found" do
+ expect(
+ recipe.delete_resource(:zen_master, "monkey")
+ ).to be nil
+ end
+
+ it "deletes and returns the resource if it finds one" do
+ resource = recipe.declare_resource(:zen_master, "monkey") do
+ something false
+ end
+ expect(
+ recipe.delete_resource(:zen_master, "monkey")
+ ).to eql(resource)
+ expect(run_context.resource_collection.all_resources.size).to eql(0)
+ end
+ end
+
+ describe "#delete_resource!" do
+ it "raises if nothing is found" do
+ expect {
+ recipe.delete_resource!(:zen_master, "monkey")
+ }.to raise_error(Chef::Exceptions::ResourceNotFound)
+ end
+
+ it "deletes and returns the resource if it finds one" do
+ resource = recipe.declare_resource(:zen_master, "monkey") do
+ something false
+ end
+ expect(
+ recipe.delete_resource!(:zen_master, "monkey")
+ ).to eql(resource)
+ expect(run_context.resource_collection.all_resources.size).to eql(0)
+ end
+ end
+
+ describe "run_context helpers" do
+
+ let(:parent_run_context) do
+ run_context.create_child
+ end
+
+ let(:child_run_context) do
+ parent_run_context.create_child
+ end
+
+ let(:parent_recipe) do
+ Chef::Recipe.new("hjk", "parent", parent_run_context)
+ end
+
+ let(:child_recipe) do
+ Chef::Recipe.new("hjk", "child", child_run_context)
+ end
+
+ before do
+ # wire up our outer run context to the root Chef.run_context
+ allow(Chef).to receive(:run_context).and_return(run_context)
+ end
+
+ it "our tests have correct separation" do
+ child_resource = child_recipe.declare_resource(:zen_master, "child") do
+ something false
+ end
+ parent_resource = parent_recipe.declare_resource(:zen_master, "parent") do
+ something false
+ end
+ root_resource = recipe.declare_resource(:zen_master, "root") do
+ something false
+ end
+ expect(run_context.resource_collection.first).to eql(root_resource)
+ expect(run_context.resource_collection.first.to_s).to eql("zen_master[root]")
+ expect(run_context.resource_collection.all_resources.size).to eql(1)
+ expect(parent_run_context.resource_collection.first).to eql(parent_resource)
+ expect(parent_run_context.resource_collection.first.to_s).to eql("zen_master[parent]")
+ expect(parent_run_context.resource_collection.all_resources.size).to eql(1)
+ expect(child_run_context.resource_collection.first).to eql(child_resource)
+ expect(child_run_context.resource_collection.first.to_s).to eql("zen_master[child]")
+ expect(child_run_context.resource_collection.all_resources.size).to eql(1)
+ end
+
+ it "with_run_context with :parent lets us build resources in the parent run_context from the child" do
+ child_recipe.instance_eval do
+ with_run_context(:parent) do
+ declare_resource(:zen_master, "parent") do
+ something false
+ end
+ end
+ end
+ expect(run_context.resource_collection.all_resources.size).to eql(0)
+ expect(parent_run_context.resource_collection.all_resources.size).to eql(1)
+ expect(parent_run_context.resource_collection.first.to_s).to eql("zen_master[parent]")
+ expect(child_run_context.resource_collection.all_resources.size).to eql(0)
+ end
+
+ it "with_run_context with :root lets us build resources in the root run_context from the child" do
+ child_recipe.instance_eval do
+ with_run_context(:root) do
+ declare_resource(:zen_master, "root") do
+ something false
+ end
+ end
+ end
+ expect(run_context.resource_collection.first.to_s).to eql("zen_master[root]")
+ expect(run_context.resource_collection.all_resources.size).to eql(1)
+ expect(parent_run_context.resource_collection.all_resources.size).to eql(0)
+ expect(child_run_context.resource_collection.all_resources.size).to eql(0)
+ end
+
+ it "with_run_context also takes a RunContext object as an argument" do
+ child_recipe.instance_exec(parent_run_context) do |parent_run_context|
+ with_run_context(parent_run_context) do
+ declare_resource(:zen_master, "parent") do
+ something false
+ end
+ end
+ end
+ expect(run_context.resource_collection.all_resources.size).to eql(0)
+ expect(parent_run_context.resource_collection.all_resources.size).to eql(1)
+ expect(parent_run_context.resource_collection.first.to_s).to eql("zen_master[parent]")
+ expect(child_run_context.resource_collection.all_resources.size).to eql(0)
+ end
+
+ it "with_run_context returns the return value of the block" do
+ child_recipe.instance_eval do
+ ret = with_run_context(:root) do
+ "return value"
+ end
+ raise "failed" unless ret == "return value"
+ end
+ end
+ end
+end
diff --git a/spec/unit/resource_collection_spec.rb b/spec/unit/resource_collection_spec.rb
index 256ae090e0..3fec2d9477 100644
--- a/spec/unit/resource_collection_spec.rb
+++ b/spec/unit/resource_collection_spec.rb
@@ -162,6 +162,36 @@ describe Chef::ResourceCollection do
end
end
+ describe "delete" do
+ it "should allow you to delete resources by name via delete" do
+ zmr = Chef::Resource::ZenMaster.new("dog")
+ rc << zmr
+ expect(rc).not_to be_empty
+ expect(rc.delete(zmr.to_s)).to eql(zmr)
+ expect(rc).to be_empty
+
+ zmr = Chef::Resource::ZenMaster.new("cat")
+ rc[0] = zmr
+ expect(rc).not_to be_empty
+ expect(rc.delete(zmr)).to eql(zmr)
+ expect(rc).to be_empty
+
+ zmr = Chef::Resource::ZenMaster.new("monkey")
+ rc.push(zmr)
+ expect(rc).not_to be_empty
+ expect(rc.delete(zmr)).to eql(zmr)
+ expect(rc).to be_empty
+ end
+
+ it "should raise an exception if you send something strange to delete" do
+ expect { rc.delete(:symbol) }.to raise_error(ArgumentError)
+ end
+
+ it "should raise an exception if it cannot find a resource with delete" do
+ expect { rc.delete("zen_master[dog]") }.to raise_error(Chef::Exceptions::ResourceNotFound)
+ end
+ end
+
describe "resources" do
it "should find a resource by symbol and name (:zen_master => monkey)" do