summaryrefslogtreecommitdiff
path: root/lib/chef/resource_collection.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/chef/resource_collection.rb')
-rw-r--r--lib/chef/resource_collection.rb217
1 files changed, 217 insertions, 0 deletions
diff --git a/lib/chef/resource_collection.rb b/lib/chef/resource_collection.rb
new file mode 100644
index 0000000000..51858c53d8
--- /dev/null
+++ b/lib/chef/resource_collection.rb
@@ -0,0 +1,217 @@
+#
+# 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/resource'
+require 'chef/resource_collection/stepable_iterator'
+
+class Chef
+ class ResourceCollection
+ include Enumerable
+
+ attr_reader :iterator
+
+ def initialize
+ @resources = Array.new
+ @resources_by_name = Hash.new
+ @insert_after_idx = nil
+ end
+
+ def all_resources
+ @resources
+ end
+
+ def [](index)
+ @resources[index]
+ end
+
+ def []=(index, arg)
+ is_chef_resource(arg)
+ @resources[index] = arg
+ @resources_by_name[arg.to_s] = index
+ end
+
+ def <<(*args)
+ args.flatten.each do |a|
+ is_chef_resource(a)
+ @resources << a
+ @resources_by_name[a.to_s] = @resources.length - 1
+ end
+ end
+
+ def insert(resource)
+ is_chef_resource(resource)
+ if @insert_after_idx
+ # in the middle of executing a run, so any resources inserted now should
+ # be placed after the most recent addition done by the currently executing
+ # resource
+ @resources.insert(@insert_after_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] > @insert_after_idx
+ end
+ @resources_by_name[resource.to_s] = @insert_after_idx + 1
+ @insert_after_idx += 1
+ else
+ @resources << resource
+ @resources_by_name[resource.to_s] = @resources.length - 1
+ end
+ end
+
+ def push(*args)
+ args.flatten.each do |arg|
+ is_chef_resource(arg)
+ @resources.push(arg)
+ @resources_by_name[arg.to_s] = @resources.length - 1
+ end
+ end
+
+ def each
+ @resources.each do |resource|
+ yield resource
+ end
+ end
+
+ def execute_each_resource(&resource_exec_block)
+ @iterator = StepableIterator.for_collection(@resources)
+ @iterator.each_with_index do |resource, idx|
+ @insert_after_idx = idx
+ yield resource
+ end
+ end
+
+ def each_index
+ @resources.each_index do |i|
+ yield i
+ end
+ end
+
+ def lookup(resource)
+ lookup_by = nil
+ if resource.kind_of?(Chef::Resource)
+ lookup_by = resource.to_s
+ elsif resource.kind_of?(String)
+ lookup_by = resource
+ else
+ raise ArgumentError, "Must pass a Chef::Resource or String to lookup"
+ end
+ res = @resources_by_name[lookup_by]
+ unless res
+ raise Chef::Exceptions::ResourceNotFound, "Cannot find a resource matching #{lookup_by} (did you define it first?)"
+ end
+ @resources[res]
+ end
+
+ # Find existing resources by searching the list of existing resources. Possible
+ # forms are:
+ #
+ # find(:file => "foobar")
+ # find(:file => [ "foobar", "baz" ])
+ # find("file[foobar]", "file[baz]")
+ # find("file[foobar,baz]")
+ #
+ # Returns the matching resource, or an Array of matching resources.
+ #
+ # Raises an ArgumentError if you feed it bad lookup information
+ # Raises a Runtime Error if it can't find the resources you are looking for.
+ def find(*args)
+ results = Array.new
+ args.each do |arg|
+ case arg
+ when Hash
+ results << find_resource_by_hash(arg)
+ when String
+ results << find_resource_by_string(arg)
+ else
+ msg = "arguments to #{self.class.name}#find should be of the form :resource => 'name' or resource[name]"
+ raise Chef::Exceptions::InvalidResourceSpecification, msg
+ end
+ end
+ flat_results = results.flatten
+ flat_results.length == 1 ? flat_results[0] : flat_results
+ end
+
+ # resources is a poorly named, but we have to maintain it for back
+ # compat.
+ alias_method :resources, :find
+
+ # Serialize this object as a hash
+ def to_json(*a)
+ instance_vars = Hash.new
+ self.instance_variables.each do |iv|
+ instance_vars[iv] = self.instance_variable_get(iv)
+ end
+ results = {
+ 'json_class' => self.class.name,
+ 'instance_vars' => instance_vars
+ }
+ results.to_json(*a)
+ end
+
+ def self.json_create(o)
+ collection = self.new()
+ o["instance_vars"].each do |k,v|
+ collection.instance_variable_set(k.to_sym, v)
+ end
+ collection
+ end
+
+ private
+
+ def find_resource_by_hash(arg)
+ results = Array.new
+ arg.each do |resource_name, name_list|
+ names = name_list.kind_of?(Array) ? name_list : [ name_list ]
+ names.each do |name|
+ res_name = "#{resource_name.to_s}[#{name}]"
+ results << lookup(res_name)
+ end
+ end
+ return results
+ end
+
+ def find_resource_by_string(arg)
+ results = Array.new
+ case arg
+ when /^(.+)\[(.+?),(.+)\]$/
+ resource_type = $1
+ arg =~ /^.+\[(.+)\]$/
+ resource_list = $1
+ resource_list.split(",").each do |name|
+ resource_name = "#{resource_type}[#{name}]"
+ results << lookup(resource_name)
+ end
+ when /^(.+)\[(.+)\]$/
+ resource_type = $1
+ name = $2
+ resource_name = "#{resource_type}[#{name}]"
+ results << lookup(resource_name)
+ else
+ raise ArgumentError, "You must have a string like resource_type[name]!"
+ end
+ return results
+ end
+
+ def is_chef_resource(arg)
+ unless arg.kind_of?(Chef::Resource)
+ raise ArgumentError, "Members must be Chef::Resource's"
+ end
+ true
+ end
+ end
+end