From d48fff6658535ca1781443aba9ab21893c13e55c Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Tue, 9 Dec 2008 15:33:28 -0600 Subject: Renaming Puppet::Node::Catalog to Puppet::Resource::Catalog Signed-off-by: Luke Kanies --- bin/puppet | 2 +- bin/ralsh | 2 +- ext/module_puppet | 2 +- ext/puppet-test | 2 +- lib/puppet/indirector/catalog/compiler.rb | 4 +- lib/puppet/indirector/catalog/yaml.rb | 4 +- lib/puppet/network/client/master.rb | 2 +- lib/puppet/network/handler/master.rb | 2 +- lib/puppet/node/catalog.rb | 508 ---------------- lib/puppet/parser/compiler.rb | 4 +- lib/puppet/resource/catalog.rb | 508 ++++++++++++++++ lib/puppet/transaction.rb | 2 +- lib/puppet/transportable.rb | 2 +- spec/integration/network/formats.rb | 2 +- spec/integration/node/catalog.rb | 54 -- spec/integration/resource/catalog.rb | 54 ++ spec/integration/type/file.rb | 26 +- spec/integration/type/tidy.rb | 2 +- spec/unit/indirector/catalog/compiler.rb | 24 +- spec/unit/indirector/catalog/yaml.rb | 12 +- spec/unit/node/catalog.rb | 932 ------------------------------ spec/unit/other/transaction.rb | 2 +- spec/unit/other/transbucket.rb | 4 +- spec/unit/parser/compiler.rb | 2 +- spec/unit/resource/catalog.rb | 932 ++++++++++++++++++++++++++++++ spec/unit/transaction.rb | 2 +- spec/unit/type.rb | 4 +- spec/unit/type/file.rb | 4 +- spec/unit/type/mount.rb | 2 +- spec/unit/type/package.rb | 2 +- spec/unit/type/ssh_authorized_key.rb | 2 +- spec/unit/type/user.rb | 2 +- spec/unit/type/zfs.rb | 2 +- test/language/snippets.rb | 2 +- test/lib/puppettest/support/resources.rb | 2 +- test/lib/puppettest/support/utils.rb | 6 +- test/network/handler/master.rb | 6 +- test/other/transactions.rb | 8 +- 38 files changed, 1567 insertions(+), 1567 deletions(-) delete mode 100644 lib/puppet/node/catalog.rb create mode 100644 lib/puppet/resource/catalog.rb delete mode 100755 spec/integration/node/catalog.rb create mode 100755 spec/integration/resource/catalog.rb delete mode 100755 spec/unit/node/catalog.rb create mode 100755 spec/unit/resource/catalog.rb diff --git a/bin/puppet b/bin/puppet index f3d8c252f..c01d7392c 100755 --- a/bin/puppet +++ b/bin/puppet @@ -218,7 +218,7 @@ end begin # Compile our catalog starttime = Time.now - catalog = Puppet::Node::Catalog.find(node.name, :use_node => node) + catalog = Puppet::Resource::Catalog.find(node.name, :use_node => node) # Translate it to a RAL catalog catalog = catalog.to_ral diff --git a/bin/ralsh b/bin/ralsh index 5dae8f130..5ff43c39b 100755 --- a/bin/ralsh +++ b/bin/ralsh @@ -231,7 +231,7 @@ else params.each do |param, value| obj[param] = value end - catalog = Puppet::Node::Catalog.new + catalog = Puppet::Resource::Catalog.new catalog.add_resource obj begin catalog.apply diff --git a/ext/module_puppet b/ext/module_puppet index 8609e4411..4ee273451 100755 --- a/ext/module_puppet +++ b/ext/module_puppet @@ -180,7 +180,7 @@ node.classes = classes begin # Compile our configuration - catalog = Puppet::Node::Catalog.find(node.name, :use_node => node) + catalog = Puppet::Resource::Catalog.find(node.name, :use_node => node) rescue => detail if Puppet[:trace] puts detail.backtrace diff --git a/ext/puppet-test b/ext/puppet-test index 41759d489..84ea483db 100755 --- a/ext/puppet-test +++ b/ext/puppet-test @@ -220,7 +220,7 @@ Suite.new :local_catalog, "Local catalog handling" do end newtest :compile, "Compiled catalog" do - Puppet::Node::Catalog.find(@node) + Puppet::Resource::Catalog.find(@node) end end diff --git a/lib/puppet/indirector/catalog/compiler.rb b/lib/puppet/indirector/catalog/compiler.rb index a6a812817..df1808d73 100644 --- a/lib/puppet/indirector/catalog/compiler.rb +++ b/lib/puppet/indirector/catalog/compiler.rb @@ -1,10 +1,10 @@ require 'puppet/node' -require 'puppet/node/catalog' +require 'puppet/resource/catalog' require 'puppet/indirector/code' require 'puppet/parser/interpreter' require 'yaml' -class Puppet::Node::Catalog::Compiler < Puppet::Indirector::Code +class Puppet::Resource::Catalog::Compiler < Puppet::Indirector::Code desc "Puppet's catalog compilation interface, and its back-end is Puppet's compiler" diff --git a/lib/puppet/indirector/catalog/yaml.rb b/lib/puppet/indirector/catalog/yaml.rb index 6a8d1765c..d6fc9b81d 100644 --- a/lib/puppet/indirector/catalog/yaml.rb +++ b/lib/puppet/indirector/catalog/yaml.rb @@ -1,7 +1,7 @@ -require 'puppet/node/catalog' +require 'puppet/resource/catalog' require 'puppet/indirector/yaml' -class Puppet::Node::Catalog::Yaml < Puppet::Indirector::Yaml +class Puppet::Resource::Catalog::Yaml < Puppet::Indirector::Yaml desc "Store catalogs as flat files, serialized using YAML." private diff --git a/lib/puppet/network/client/master.rb b/lib/puppet/network/client/master.rb index d24fd7df7..f86c9bb01 100644 --- a/lib/puppet/network/client/master.rb +++ b/lib/puppet/network/client/master.rb @@ -319,7 +319,7 @@ class Puppet::Network::Client::Master < Puppet::Network::Client if args[:ignore] hash[:ignore] = args[:ignore].split(/\s+/) end - downconfig = Puppet::Node::Catalog.new("downloading") + downconfig = Puppet::Resource::Catalog.new("downloading") downconfig.add_resource Puppet::Type.type(:file).create(hash) Puppet.info "Retrieving #{args[:name]}s" diff --git a/lib/puppet/network/handler/master.rb b/lib/puppet/network/handler/master.rb index 71b633a09..7bde0af73 100644 --- a/lib/puppet/network/handler/master.rb +++ b/lib/puppet/network/handler/master.rb @@ -62,7 +62,7 @@ class Puppet::Network::Handler # Pass the facts to the fact handler Puppet::Node::Facts.new(client, facts).save unless local? - catalog = Puppet::Node::Catalog.find(client) + catalog = Puppet::Resource::Catalog.find(client) case format when "yaml": diff --git a/lib/puppet/node/catalog.rb b/lib/puppet/node/catalog.rb deleted file mode 100644 index bc522cc1e..000000000 --- a/lib/puppet/node/catalog.rb +++ /dev/null @@ -1,508 +0,0 @@ -require 'puppet/indirector' -require 'puppet/simple_graph' -require 'puppet/transaction' - -require 'puppet/util/cacher' - -require 'puppet/util/tagging' - -# This class models a node catalog. It is the thing -# meant to be passed from server to client, and it contains all -# of the information in the catalog, including the resources -# and the relationships between them. -class Puppet::Node::Catalog < Puppet::SimpleGraph - class DuplicateResourceError < Puppet::Error; end - - extend Puppet::Indirector - indirects :catalog, :terminus_class => :compiler - - include Puppet::Util::Tagging - include Puppet::Util::Cacher::Expirer - - # The host name this is a catalog for. - attr_accessor :name - - # The catalog version. Used for testing whether a catalog - # is up to date. - attr_accessor :version - - # How long this catalog took to retrieve. Used for reporting stats. - attr_accessor :retrieval_duration - - # How we should extract the catalog for sending to the client. - attr_reader :extraction_format - - # Whether this is a host catalog, which behaves very differently. - # In particular, reports are sent, graphs are made, and state is - # stored in the state database. If this is set incorrectly, then you often - # end up in infinite loops, because catalogs are used to make things - # that the host catalog needs. - attr_accessor :host_config - - # Whether this catalog was retrieved from the cache, which affects - # whether it is written back out again. - attr_accessor :from_cache - - # Add classes to our class list. - def add_class(*classes) - classes.each do |klass| - @classes << klass - end - - # Add the class names as tags, too. - tag(*classes) - end - - # Add one or more resources to our graph and to our resource table. - # This is actually a relatively complicated method, because it handles multiple - # aspects of Catalog behaviour: - # * Add the resource to the resource table - # * Add the resource to the resource graph - # * Add the resource to the relationship graph - # * Add any aliases that make sense for the resource (e.g., name != title) - def add_resource(*resources) - resources.each do |resource| - unless resource.respond_to?(:ref) - raise ArgumentError, "Can only add objects that respond to :ref, not instances of %s" % resource.class - end - end.find_all { |resource| fail_or_skip_unless_unique(resource) }.each do |resource| - ref = resource.ref - - @transient_resources << resource if applying? - @resource_table[ref] = resource - - # If the name and title differ, set up an alias - #self.alias(resource, resource.name) if resource.respond_to?(:name) and resource.respond_to?(:title) and resource.name != resource.title - if resource.respond_to?(:name) and resource.respond_to?(:title) and resource.name != resource.title - self.alias(resource, resource.name) if resource.isomorphic? - end - - resource.catalog = self if resource.respond_to?(:catalog=) - - add_vertex(resource) - - if @relationship_graph - @relationship_graph.add_vertex(resource) - end - - yield(resource) if block_given? - end - end - - # Create an alias for a resource. - def alias(resource, name) - #set $1 - resource.ref =~ /^(.+)\[/ - - newref = "%s[%s]" % [$1 || resource.class.name, name] - - # LAK:NOTE It's important that we directly compare the references, - # because sometimes an alias is created before the resource is - # added to the catalog, so comparing inside the below if block - # isn't sufficient. - return if newref == resource.ref - if existing = @resource_table[newref] - return if existing == resource - raise(ArgumentError, "Cannot alias %s to %s; resource %s already exists" % [resource.ref, name, newref]) - end - @resource_table[newref] = resource - @aliases[resource.ref] << newref - end - - # Apply our catalog to the local host. Valid options - # are: - # :tags - set the tags that restrict what resources run - # during the transaction - # :ignoreschedules - tell the transaction to ignore schedules - # when determining the resources to run - def apply(options = {}) - @applying = true - - # Expire all of the resource data -- this ensures that all - # data we're operating against is entirely current. - expire() - - Puppet::Util::Storage.load if host_config? - transaction = Puppet::Transaction.new(self) - - transaction.tags = options[:tags] if options[:tags] - transaction.ignoreschedules = true if options[:ignoreschedules] - - transaction.addtimes :config_retrieval => @retrieval_duration - - - begin - transaction.evaluate - rescue Puppet::Error => detail - Puppet.err "Could not apply complete catalog: %s" % detail - rescue => detail - puts detail.backtrace if Puppet[:trace] - Puppet.err "Got an uncaught exception of type %s: %s" % [detail.class, detail] - ensure - # Don't try to store state unless we're a host config - # too recursive. - Puppet::Util::Storage.store if host_config? - end - - yield transaction if block_given? - - transaction.send_report if host_config and (Puppet[:report] or Puppet[:summarize]) - - return transaction - ensure - @applying = false - cleanup() - transaction.cleanup if defined? transaction and transaction - end - - # Are we in the middle of applying the catalog? - def applying? - @applying - end - - def clear(remove_resources = true) - super() - # We have to do this so that the resources clean themselves up. - @resource_table.values.each { |resource| resource.remove } if remove_resources - @resource_table.clear - - if defined?(@relationship_graph) and @relationship_graph - @relationship_graph.clear - @relationship_graph = nil - end - end - - def classes - @classes.dup - end - - # Create a new resource and register it in the catalog. - def create_resource(type, options) - unless klass = Puppet::Type.type(type) - raise ArgumentError, "Unknown resource type %s" % type - end - return unless resource = klass.create(options) - - add_resource(resource) - resource - end - - def expired?(ts) - if applying? - return super - else - return true - end - end - - # Make sure we support the requested extraction format. - def extraction_format=(value) - unless respond_to?("extract_to_%s" % value) - raise ArgumentError, "Invalid extraction format %s" % value - end - @extraction_format = value - end - - # Turn our catalog graph into whatever the client is expecting. - def extract - send("extract_to_%s" % extraction_format) - end - - # Create the traditional TransBuckets and TransObjects from our catalog - # graph. This will hopefully be deprecated soon. - def extract_to_transportable - top = nil - current = nil - buckets = {} - - unless main = vertices.find { |res| res.type == "Class" and res.title == :main } - raise Puppet::DevError, "Could not find 'main' class; cannot generate catalog" - end - - # Create a proc for examining edges, which we'll use to build our tree - # of TransBuckets and TransObjects. - bucket = nil - walk(main, :out) do |source, target| - # The sources are always non-builtins. - unless tmp = buckets[source.to_s] - if tmp = buckets[source.to_s] = source.to_trans - bucket = tmp - else - # This is because virtual resources return nil. If a virtual - # container resource contains realized resources, we still need to get - # to them. So, we keep a reference to the last valid bucket - # we returned and use that if the container resource is virtual. - end - end - bucket = tmp || bucket - if child = target.to_trans - unless bucket - raise "No bucket created for %s" % source - end - bucket.push child - - # It's important that we keep a reference to any TransBuckets we've created, so - # we don't create multiple buckets for children. - unless target.builtin? - buckets[target.to_s] = child - end - end - end - - # Retrieve the bucket for the top-level scope and set the appropriate metadata. - unless result = buckets[main.to_s] - # This only happens when the catalog is entirely empty. - result = buckets[main.to_s] = main.to_trans - end - - result.classes = classes - - # Clear the cache to encourage the GC - buckets.clear - return result - end - - # Make sure all of our resources are "finished". - def finalize - make_default_resources - - @resource_table.values.each { |resource| resource.finish } - - write_graph(:resources) - end - - def host_config? - host_config || false - end - - def initialize(name = nil) - super() - @name = name if name - @extraction_format ||= :transportable - @classes = [] - @resource_table = {} - @transient_resources = [] - @applying = false - @relationship_graph = nil - - @aliases = Hash.new { |hash, key| hash[key] = [] } - - if block_given? - yield(self) - finalize() - end - end - - # Make the default objects necessary for function. - def make_default_resources - # We have to add the resources to the catalog, or else they won't get cleaned up after - # the transaction. - - # First create the default scheduling objects - Puppet::Type.type(:schedule).mkdefaultschedules.each { |res| add_resource(res) unless resource(res.ref) } - - # And filebuckets - if bucket = Puppet::Type.type(:filebucket).mkdefaultbucket - add_resource(bucket) unless resource(bucket.ref) - end - end - - # Create a graph of all of the relationships in our catalog. - def relationship_graph - unless defined? @relationship_graph and @relationship_graph - # It's important that we assign the graph immediately, because - # the debug messages below use the relationships in the - # relationship graph to determine the path to the resources - # spitting out the messages. If this is not set, - # then we get into an infinite loop. - @relationship_graph = Puppet::SimpleGraph.new - - # First create the dependency graph - self.vertices.each do |vertex| - @relationship_graph.add_vertex vertex - vertex.builddepends.each do |edge| - @relationship_graph.add_edge(edge) - end - end - - # Lastly, add in any autorequires - @relationship_graph.vertices.each do |vertex| - vertex.autorequire(self).each do |edge| - unless @relationship_graph.edge?(edge.source, edge.target) # don't let automatic relationships conflict with manual ones. - unless @relationship_graph.edge?(edge.target, edge.source) - vertex.debug "Autorequiring %s" % [edge.source] - @relationship_graph.add_edge(edge) - else - vertex.debug "Skipping automatic relationship with %s" % (edge.source == vertex ? edge.target : edge.source) - end - end - end - end - - @relationship_graph.write_graph(:relationships) if host_config? - - # Then splice in the container information - @relationship_graph.splice!(self, Puppet::Type::Component) - - @relationship_graph.write_graph(:expanded_relationships) if host_config? - end - @relationship_graph - end - - # Remove the resource from our catalog. Notice that we also call - # 'remove' on the resource, at least until resource classes no longer maintain - # references to the resource instances. - def remove_resource(*resources) - resources.each do |resource| - @resource_table.delete(resource.ref) - @aliases[resource.ref].each { |res_alias| @resource_table.delete(res_alias) } - @aliases[resource.ref].clear - remove_vertex!(resource) if vertex?(resource) - @relationship_graph.remove_vertex!(resource) if @relationship_graph and @relationship_graph.vertex?(resource) - resource.remove - end - end - - # Look a resource up by its reference (e.g., File[/etc/passwd]). - def resource(type, title = nil) - # Always create a resource reference, so that it always canonizes how we - # are referring to them. - if title - ref = Puppet::Resource::Reference.new(type, title).to_s - else - # If they didn't provide a title, then we expect the first - # argument to be of the form 'Class[name]', which our - # Reference class canonizes for us. - ref = Puppet::Resource::Reference.new(nil, type).to_s - end - @resource_table[ref] - end - - # Return an array of all resources. - def resources - @resource_table.keys - end - - # Convert our catalog into a RAL catalog. - def to_ral - to_catalog :to_type - end - - # Turn our parser catalog into a transportable catalog. - def to_transportable - to_catalog :to_transobject - end - - # Produce the graph files if requested. - def write_graph(name) - # We only want to graph the main host catalog. - return unless host_config? - - super - end - - private - - def cleanup - unless @transient_resources.empty? - remove_resource(*@transient_resources) - @transient_resources.clear - @relationship_graph = nil - end - - # Expire any cached data the resources are keeping. - expire() - end - - # Verify that the given resource isn't defined elsewhere. - def fail_or_skip_unless_unique(resource) - # Short-curcuit the common case, - return resource unless existing_resource = @resource_table[resource.ref] - - if resource.implicit? - resource.debug "Generated resource conflicts with explicit resource; ignoring generated resource" - return nil - elsif old = resource(resource.ref) and old.implicit? - # The existing resource is implicit; remove it and replace it with - # the new one. - old.debug "Replacing with new resource" - remove_resource(old) - return resource - end - - # If we've gotten this far, it's a real conflict - - # Either it's a defined type, which are never - # isomorphic, or it's a non-isomorphic type, so - # we should throw an exception. - msg = "Duplicate definition: %s is already defined" % resource.ref - - if existing_resource.file and existing_resource.line - msg << " in file %s at line %s" % - [existing_resource.file, existing_resource.line] - end - - if resource.line or resource.file - msg << "; cannot redefine" - end - - raise DuplicateResourceError.new(msg) - end - - # An abstracted method for converting one catalog into another type of catalog. - # This pretty much just converts all of the resources from one class to another, using - # a conversion method. - def to_catalog(convert) - result = self.class.new(self.name) - - map = {} - vertices.each do |resource| - next if resource.respond_to?(:virtual?) and resource.virtual? - - #This is hackity hack for 1094 - #Aliases aren't working in the ral catalog because the current instance of the resource - #has a reference to the catalog being converted. . . So, give it a reference to the new one - #problem solved. . . - if resource.is_a?(Puppet::TransObject) - resource = resource.dup - resource.catalog = result - elsif resource.is_a?(Puppet::Parser::Resource) - resource = resource.to_transobject - resource.catalog = result - end - - newres = resource.send(convert) - - # We can't guarantee that resources don't munge their names - # (like files do with trailing slashes), so we have to keep track - # of what a resource got converted to. - map[resource.ref] = newres - - result.add_resource newres - end - - message = convert.to_s.gsub "_", " " - edges.each do |edge| - # Skip edges between virtual resources. - next if edge.source.respond_to?(:virtual?) and edge.source.virtual? - next if edge.target.respond_to?(:virtual?) and edge.target.virtual? - - unless source = map[edge.source.ref] - raise Puppet::DevError, "Could not find resource %s when converting %s resources" % [edge.source.ref, message] - end - - unless target = map[edge.target.ref] - raise Puppet::DevError, "Could not find resource %s when converting %s resources" % [edge.target.ref, message] - end - - result.add_edge(source, target, edge.label) - end - - map.clear - - result.add_class(*self.classes) - result.tag(*self.tags) - - return result - end -end diff --git a/lib/puppet/parser/compiler.rb b/lib/puppet/parser/compiler.rb index d67b3d275..9f5548d8b 100644 --- a/lib/puppet/parser/compiler.rb +++ b/lib/puppet/parser/compiler.rb @@ -2,7 +2,7 @@ # Copyright (c) 2007. All rights reserved. require 'puppet/node' -require 'puppet/node/catalog' +require 'puppet/resource/catalog' require 'puppet/util/errors' # Maintain a graph of scopes, along with a bunch of data @@ -412,7 +412,7 @@ class Puppet::Parser::Compiler @scope_graph = Puppet::SimpleGraph.new # For maintaining the relationship between scopes and their resources. - @catalog = Puppet::Node::Catalog.new(@node.name) + @catalog = Puppet::Resource::Catalog.new(@node.name) @catalog.version = @parser.version end diff --git a/lib/puppet/resource/catalog.rb b/lib/puppet/resource/catalog.rb new file mode 100644 index 000000000..5b1022799 --- /dev/null +++ b/lib/puppet/resource/catalog.rb @@ -0,0 +1,508 @@ +require 'puppet/indirector' +require 'puppet/simple_graph' +require 'puppet/transaction' + +require 'puppet/util/cacher' + +require 'puppet/util/tagging' + +# This class models a node catalog. It is the thing +# meant to be passed from server to client, and it contains all +# of the information in the catalog, including the resources +# and the relationships between them. +class Puppet::Resource::Catalog < Puppet::SimpleGraph + class DuplicateResourceError < Puppet::Error; end + + extend Puppet::Indirector + indirects :catalog, :terminus_class => :compiler + + include Puppet::Util::Tagging + include Puppet::Util::Cacher::Expirer + + # The host name this is a catalog for. + attr_accessor :name + + # The catalog version. Used for testing whether a catalog + # is up to date. + attr_accessor :version + + # How long this catalog took to retrieve. Used for reporting stats. + attr_accessor :retrieval_duration + + # How we should extract the catalog for sending to the client. + attr_reader :extraction_format + + # Whether this is a host catalog, which behaves very differently. + # In particular, reports are sent, graphs are made, and state is + # stored in the state database. If this is set incorrectly, then you often + # end up in infinite loops, because catalogs are used to make things + # that the host catalog needs. + attr_accessor :host_config + + # Whether this catalog was retrieved from the cache, which affects + # whether it is written back out again. + attr_accessor :from_cache + + # Add classes to our class list. + def add_class(*classes) + classes.each do |klass| + @classes << klass + end + + # Add the class names as tags, too. + tag(*classes) + end + + # Add one or more resources to our graph and to our resource table. + # This is actually a relatively complicated method, because it handles multiple + # aspects of Catalog behaviour: + # * Add the resource to the resource table + # * Add the resource to the resource graph + # * Add the resource to the relationship graph + # * Add any aliases that make sense for the resource (e.g., name != title) + def add_resource(*resources) + resources.each do |resource| + unless resource.respond_to?(:ref) + raise ArgumentError, "Can only add objects that respond to :ref, not instances of %s" % resource.class + end + end.find_all { |resource| fail_or_skip_unless_unique(resource) }.each do |resource| + ref = resource.ref + + @transient_resources << resource if applying? + @resource_table[ref] = resource + + # If the name and title differ, set up an alias + #self.alias(resource, resource.name) if resource.respond_to?(:name) and resource.respond_to?(:title) and resource.name != resource.title + if resource.respond_to?(:name) and resource.respond_to?(:title) and resource.name != resource.title + self.alias(resource, resource.name) if resource.isomorphic? + end + + resource.catalog = self if resource.respond_to?(:catalog=) + + add_vertex(resource) + + if @relationship_graph + @relationship_graph.add_vertex(resource) + end + + yield(resource) if block_given? + end + end + + # Create an alias for a resource. + def alias(resource, name) + #set $1 + resource.ref =~ /^(.+)\[/ + + newref = "%s[%s]" % [$1 || resource.class.name, name] + + # LAK:NOTE It's important that we directly compare the references, + # because sometimes an alias is created before the resource is + # added to the catalog, so comparing inside the below if block + # isn't sufficient. + return if newref == resource.ref + if existing = @resource_table[newref] + return if existing == resource + raise(ArgumentError, "Cannot alias %s to %s; resource %s already exists" % [resource.ref, name, newref]) + end + @resource_table[newref] = resource + @aliases[resource.ref] << newref + end + + # Apply our catalog to the local host. Valid options + # are: + # :tags - set the tags that restrict what resources run + # during the transaction + # :ignoreschedules - tell the transaction to ignore schedules + # when determining the resources to run + def apply(options = {}) + @applying = true + + # Expire all of the resource data -- this ensures that all + # data we're operating against is entirely current. + expire() + + Puppet::Util::Storage.load if host_config? + transaction = Puppet::Transaction.new(self) + + transaction.tags = options[:tags] if options[:tags] + transaction.ignoreschedules = true if options[:ignoreschedules] + + transaction.addtimes :config_retrieval => @retrieval_duration + + + begin + transaction.evaluate + rescue Puppet::Error => detail + Puppet.err "Could not apply complete catalog: %s" % detail + rescue => detail + puts detail.backtrace if Puppet[:trace] + Puppet.err "Got an uncaught exception of type %s: %s" % [detail.class, detail] + ensure + # Don't try to store state unless we're a host config + # too recursive. + Puppet::Util::Storage.store if host_config? + end + + yield transaction if block_given? + + transaction.send_report if host_config and (Puppet[:report] or Puppet[:summarize]) + + return transaction + ensure + @applying = false + cleanup() + transaction.cleanup if defined? transaction and transaction + end + + # Are we in the middle of applying the catalog? + def applying? + @applying + end + + def clear(remove_resources = true) + super() + # We have to do this so that the resources clean themselves up. + @resource_table.values.each { |resource| resource.remove } if remove_resources + @resource_table.clear + + if defined?(@relationship_graph) and @relationship_graph + @relationship_graph.clear + @relationship_graph = nil + end + end + + def classes + @classes.dup + end + + # Create a new resource and register it in the catalog. + def create_resource(type, options) + unless klass = Puppet::Type.type(type) + raise ArgumentError, "Unknown resource type %s" % type + end + return unless resource = klass.create(options) + + add_resource(resource) + resource + end + + def expired?(ts) + if applying? + return super + else + return true + end + end + + # Make sure we support the requested extraction format. + def extraction_format=(value) + unless respond_to?("extract_to_%s" % value) + raise ArgumentError, "Invalid extraction format %s" % value + end + @extraction_format = value + end + + # Turn our catalog graph into whatever the client is expecting. + def extract + send("extract_to_%s" % extraction_format) + end + + # Create the traditional TransBuckets and TransObjects from our catalog + # graph. This will hopefully be deprecated soon. + def extract_to_transportable + top = nil + current = nil + buckets = {} + + unless main = vertices.find { |res| res.type == "Class" and res.title == :main } + raise Puppet::DevError, "Could not find 'main' class; cannot generate catalog" + end + + # Create a proc for examining edges, which we'll use to build our tree + # of TransBuckets and TransObjects. + bucket = nil + walk(main, :out) do |source, target| + # The sources are always non-builtins. + unless tmp = buckets[source.to_s] + if tmp = buckets[source.to_s] = source.to_trans + bucket = tmp + else + # This is because virtual resources return nil. If a virtual + # container resource contains realized resources, we still need to get + # to them. So, we keep a reference to the last valid bucket + # we returned and use that if the container resource is virtual. + end + end + bucket = tmp || bucket + if child = target.to_trans + unless bucket + raise "No bucket created for %s" % source + end + bucket.push child + + # It's important that we keep a reference to any TransBuckets we've created, so + # we don't create multiple buckets for children. + unless target.builtin? + buckets[target.to_s] = child + end + end + end + + # Retrieve the bucket for the top-level scope and set the appropriate metadata. + unless result = buckets[main.to_s] + # This only happens when the catalog is entirely empty. + result = buckets[main.to_s] = main.to_trans + end + + result.classes = classes + + # Clear the cache to encourage the GC + buckets.clear + return result + end + + # Make sure all of our resources are "finished". + def finalize + make_default_resources + + @resource_table.values.each { |resource| resource.finish } + + write_graph(:resources) + end + + def host_config? + host_config || false + end + + def initialize(name = nil) + super() + @name = name if name + @extraction_format ||= :transportable + @classes = [] + @resource_table = {} + @transient_resources = [] + @applying = false + @relationship_graph = nil + + @aliases = Hash.new { |hash, key| hash[key] = [] } + + if block_given? + yield(self) + finalize() + end + end + + # Make the default objects necessary for function. + def make_default_resources + # We have to add the resources to the catalog, or else they won't get cleaned up after + # the transaction. + + # First create the default scheduling objects + Puppet::Type.type(:schedule).mkdefaultschedules.each { |res| add_resource(res) unless resource(res.ref) } + + # And filebuckets + if bucket = Puppet::Type.type(:filebucket).mkdefaultbucket + add_resource(bucket) unless resource(bucket.ref) + end + end + + # Create a graph of all of the relationships in our catalog. + def relationship_graph + unless defined? @relationship_graph and @relationship_graph + # It's important that we assign the graph immediately, because + # the debug messages below use the relationships in the + # relationship graph to determine the path to the resources + # spitting out the messages. If this is not set, + # then we get into an infinite loop. + @relationship_graph = Puppet::SimpleGraph.new + + # First create the dependency graph + self.vertices.each do |vertex| + @relationship_graph.add_vertex vertex + vertex.builddepends.each do |edge| + @relationship_graph.add_edge(edge) + end + end + + # Lastly, add in any autorequires + @relationship_graph.vertices.each do |vertex| + vertex.autorequire(self).each do |edge| + unless @relationship_graph.edge?(edge.source, edge.target) # don't let automatic relationships conflict with manual ones. + unless @relationship_graph.edge?(edge.target, edge.source) + vertex.debug "Autorequiring %s" % [edge.source] + @relationship_graph.add_edge(edge) + else + vertex.debug "Skipping automatic relationship with %s" % (edge.source == vertex ? edge.target : edge.source) + end + end + end + end + + @relationship_graph.write_graph(:relationships) if host_config? + + # Then splice in the container information + @relationship_graph.splice!(self, Puppet::Type::Component) + + @relationship_graph.write_graph(:expanded_relationships) if host_config? + end + @relationship_graph + end + + # Remove the resource from our catalog. Notice that we also call + # 'remove' on the resource, at least until resource classes no longer maintain + # references to the resource instances. + def remove_resource(*resources) + resources.each do |resource| + @resource_table.delete(resource.ref) + @aliases[resource.ref].each { |res_alias| @resource_table.delete(res_alias) } + @aliases[resource.ref].clear + remove_vertex!(resource) if vertex?(resource) + @relationship_graph.remove_vertex!(resource) if @relationship_graph and @relationship_graph.vertex?(resource) + resource.remove + end + end + + # Look a resource up by its reference (e.g., File[/etc/passwd]). + def resource(type, title = nil) + # Always create a resource reference, so that it always canonizes how we + # are referring to them. + if title + ref = Puppet::Resource::Reference.new(type, title).to_s + else + # If they didn't provide a title, then we expect the first + # argument to be of the form 'Class[name]', which our + # Reference class canonizes for us. + ref = Puppet::Resource::Reference.new(nil, type).to_s + end + @resource_table[ref] + end + + # Return an array of all resources. + def resources + @resource_table.keys + end + + # Convert our catalog into a RAL catalog. + def to_ral + to_catalog :to_type + end + + # Turn our parser catalog into a transportable catalog. + def to_transportable + to_catalog :to_transobject + end + + # Produce the graph files if requested. + def write_graph(name) + # We only want to graph the main host catalog. + return unless host_config? + + super + end + + private + + def cleanup + unless @transient_resources.empty? + remove_resource(*@transient_resources) + @transient_resources.clear + @relationship_graph = nil + end + + # Expire any cached data the resources are keeping. + expire() + end + + # Verify that the given resource isn't defined elsewhere. + def fail_or_skip_unless_unique(resource) + # Short-curcuit the common case, + return resource unless existing_resource = @resource_table[resource.ref] + + if resource.implicit? + resource.debug "Generated resource conflicts with explicit resource; ignoring generated resource" + return nil + elsif old = resource(resource.ref) and old.implicit? + # The existing resource is implicit; remove it and replace it with + # the new one. + old.debug "Replacing with new resource" + remove_resource(old) + return resource + end + + # If we've gotten this far, it's a real conflict + + # Either it's a defined type, which are never + # isomorphic, or it's a non-isomorphic type, so + # we should throw an exception. + msg = "Duplicate definition: %s is already defined" % resource.ref + + if existing_resource.file and existing_resource.line + msg << " in file %s at line %s" % + [existing_resource.file, existing_resource.line] + end + + if resource.line or resource.file + msg << "; cannot redefine" + end + + raise DuplicateResourceError.new(msg) + end + + # An abstracted method for converting one catalog into another type of catalog. + # This pretty much just converts all of the resources from one class to another, using + # a conversion method. + def to_catalog(convert) + result = self.class.new(self.name) + + map = {} + vertices.each do |resource| + next if resource.respond_to?(:virtual?) and resource.virtual? + + #This is hackity hack for 1094 + #Aliases aren't working in the ral catalog because the current instance of the resource + #has a reference to the catalog being converted. . . So, give it a reference to the new one + #problem solved. . . + if resource.is_a?(Puppet::TransObject) + resource = resource.dup + resource.catalog = result + elsif resource.is_a?(Puppet::Parser::Resource) + resource = resource.to_transobject + resource.catalog = result + end + + newres = resource.send(convert) + + # We can't guarantee that resources don't munge their names + # (like files do with trailing slashes), so we have to keep track + # of what a resource got converted to. + map[resource.ref] = newres + + result.add_resource newres + end + + message = convert.to_s.gsub "_", " " + edges.each do |edge| + # Skip edges between virtual resources. + next if edge.source.respond_to?(:virtual?) and edge.source.virtual? + next if edge.target.respond_to?(:virtual?) and edge.target.virtual? + + unless source = map[edge.source.ref] + raise Puppet::DevError, "Could not find resource %s when converting %s resources" % [edge.source.ref, message] + end + + unless target = map[edge.target.ref] + raise Puppet::DevError, "Could not find resource %s when converting %s resources" % [edge.target.ref, message] + end + + result.add_edge(source, target, edge.label) + end + + map.clear + + result.add_class(*self.classes) + result.tag(*self.tags) + + return result + end +end diff --git a/lib/puppet/transaction.rb b/lib/puppet/transaction.rb index 83017222f..30f86a9d7 100644 --- a/lib/puppet/transaction.rb +++ b/lib/puppet/transaction.rb @@ -400,7 +400,7 @@ class Transaction # this should only be called by a Puppet::Type::Component resource now # and it should only receive an array def initialize(resources) - if resources.is_a?(Puppet::Node::Catalog) + if resources.is_a?(Puppet::Resource::Catalog) @catalog = resources elsif resources.is_a?(Puppet::SimpleGraph) raise "Transactions should get catalogs now, not SimpleGraph" diff --git a/lib/puppet/transportable.rb b/lib/puppet/transportable.rb index b809d5b94..9e85b1fe7 100644 --- a/lib/puppet/transportable.rb +++ b/lib/puppet/transportable.rb @@ -181,7 +181,7 @@ module Puppet # Create a resource graph from our structure. def to_catalog(clear_on_failure = true) - catalog = Puppet::Node::Catalog.new(Facter.value("hostname")) + catalog = Puppet::Resource::Catalog.new(Facter.value("hostname")) # This should really use the 'delve' method, but this # whole class is going away relatively soon, hopefully, diff --git a/spec/integration/network/formats.rb b/spec/integration/network/formats.rb index 0cfbadd38..2ec4cb9e8 100755 --- a/spec/integration/network/formats.rb +++ b/spec/integration/network/formats.rb @@ -14,6 +14,6 @@ describe Puppet::Network::FormatHandler.format(:s) do end it "should not support catalogs" do - @format.should_not be_supported(Puppet::Node::Catalog) + @format.should_not be_supported(Puppet::Resource::Catalog) end end diff --git a/spec/integration/node/catalog.rb b/spec/integration/node/catalog.rb deleted file mode 100755 index 438f92834..000000000 --- a/spec/integration/node/catalog.rb +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2007-4-8. -# Copyright (c) 2008. All rights reserved. - -require File.dirname(__FILE__) + '/../../spec_helper' - -describe Puppet::Node::Catalog do - describe "when using the indirector" do - after { Puppet::Util::Cacher.expire } - before do - # This is so the tests work w/out networking. - Facter.stubs(:to_hash).returns({"hostname" => "foo.domain.com"}) - Facter.stubs(:value).returns("eh") - end - - - it "should be able to delegate to the :yaml terminus" do - Puppet::Node::Catalog.indirection.stubs(:terminus_class).returns :yaml - - # Load now, before we stub the exists? method. - terminus = Puppet::Node::Catalog.indirection.terminus(:yaml) - terminus.expects(:path).with("me").returns "/my/yaml/file" - - FileTest.expects(:exist?).with("/my/yaml/file").returns false - Puppet::Node::Catalog.find("me").should be_nil - end - - it "should be able to delegate to the :compiler terminus" do - Puppet::Node::Catalog.indirection.stubs(:terminus_class).returns :compiler - - # Load now, before we stub the exists? method. - compiler = Puppet::Node::Catalog.indirection.terminus(:compiler) - - node = mock 'node' - node.stub_everything - - Puppet::Node.expects(:find).returns(node) - compiler.expects(:compile).with(node).returns nil - - Puppet::Node::Catalog.find("me").should be_nil - end - - it "should pass provided node information directly to the terminus" do - terminus = mock 'terminus' - - Puppet::Node::Catalog.indirection.stubs(:terminus).returns terminus - - node = mock 'node' - terminus.expects(:find).with { |request| request.options[:use_node] == node } - Puppet::Node::Catalog.find("me", :use_node => node) - end - end -end diff --git a/spec/integration/resource/catalog.rb b/spec/integration/resource/catalog.rb new file mode 100755 index 000000000..db25dab67 --- /dev/null +++ b/spec/integration/resource/catalog.rb @@ -0,0 +1,54 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2007-4-8. +# Copyright (c) 2008. All rights reserved. + +require File.dirname(__FILE__) + '/../../spec_helper' + +describe Puppet::Resource::Catalog do + describe "when using the indirector" do + after { Puppet::Util::Cacher.expire } + before do + # This is so the tests work w/out networking. + Facter.stubs(:to_hash).returns({"hostname" => "foo.domain.com"}) + Facter.stubs(:value).returns("eh") + end + + + it "should be able to delegate to the :yaml terminus" do + Puppet::Resource::Catalog.indirection.stubs(:terminus_class).returns :yaml + + # Load now, before we stub the exists? method. + terminus = Puppet::Resource::Catalog.indirection.terminus(:yaml) + terminus.expects(:path).with("me").returns "/my/yaml/file" + + FileTest.expects(:exist?).with("/my/yaml/file").returns false + Puppet::Resource::Catalog.find("me").should be_nil + end + + it "should be able to delegate to the :compiler terminus" do + Puppet::Resource::Catalog.indirection.stubs(:terminus_class).returns :compiler + + # Load now, before we stub the exists? method. + compiler = Puppet::Resource::Catalog.indirection.terminus(:compiler) + + node = mock 'node' + node.stub_everything + + Puppet::Node.expects(:find).returns(node) + compiler.expects(:compile).with(node).returns nil + + Puppet::Resource::Catalog.find("me").should be_nil + end + + it "should pass provided node information directly to the terminus" do + terminus = mock 'terminus' + + Puppet::Resource::Catalog.indirection.stubs(:terminus).returns terminus + + node = mock 'node' + terminus.expects(:find).with { |request| request.options[:use_node] == node } + Puppet::Resource::Catalog.find("me", :use_node => node) + end + end +end diff --git a/spec/integration/type/file.rb b/spec/integration/type/file.rb index 9d1b77788..5f15aa55c 100755 --- a/spec/integration/type/file.rb +++ b/spec/integration/type/file.rb @@ -37,7 +37,7 @@ describe Puppet::Type.type(:file) do @file = Puppet::Type::File.create(:name => @path, :mode => 0644, :recurse => true) - @catalog = Puppet::Node::Catalog.new + @catalog = Puppet::Resource::Catalog.new @catalog.add_resource @file lambda { @file.eval_generate }.should_not raise_error @@ -50,7 +50,7 @@ describe Puppet::Type.type(:file) do @file = Puppet::Type::File.create(:name => @path, :mode => 0644, :recurse => true) - @catalog = Puppet::Node::Catalog.new + @catalog = Puppet::Resource::Catalog.new @catalog.add_resource @file @catalog.apply @@ -73,7 +73,7 @@ describe Puppet::Type.type(:file) do @file = Puppet::Type::File.create(:name => dest, :target => source, :recurse => true, :ensure => :link) - @catalog = Puppet::Node::Catalog.new + @catalog = Puppet::Resource::Catalog.new @catalog.add_resource @file @catalog.apply @@ -100,7 +100,7 @@ describe Puppet::Type.type(:file) do @file = Puppet::Type::File.create(:name => dest, :source => source, :recurse => true) - @catalog = Puppet::Node::Catalog.new + @catalog = Puppet::Resource::Catalog.new @catalog.add_resource @file @catalog.apply @@ -135,7 +135,7 @@ describe Puppet::Type.type(:file) do @file = Puppet::Type::File.create(:name => @dest, :source => @source, :recurse => true) - @catalog = Puppet::Node::Catalog.new + @catalog = Puppet::Resource::Catalog.new @catalog.add_resource @file end @@ -168,7 +168,7 @@ describe Puppet::Type.type(:file) do file = Puppet::Type::File.create(:name => dest, :source => source) - catalog = Puppet::Node::Catalog.new + catalog = Puppet::Resource::Catalog.new catalog.add_resource file catalog.apply @@ -185,7 +185,7 @@ describe Puppet::Type.type(:file) do file = Puppet::Type::File.create(:name => dest, :source => source) - catalog = Puppet::Node::Catalog.new + catalog = Puppet::Resource::Catalog.new catalog.add_resource file catalog.apply @@ -201,7 +201,7 @@ describe Puppet::Type.type(:file) do file = Puppet::Type::File.create(:name => dest, :source => source) - catalog = Puppet::Node::Catalog.new + catalog = Puppet::Resource::Catalog.new catalog.add_resource file catalog.apply @@ -223,7 +223,7 @@ describe Puppet::Type.type(:file) do file = Puppet::Type::File.create(:name => dest, :source => source, :recurse => true) - catalog = Puppet::Node::Catalog.new + catalog = Puppet::Resource::Catalog.new catalog.add_resource file catalog.apply @@ -239,7 +239,7 @@ describe Puppet::Type.type(:file) do :content => "this is some content, yo" ) - catalog = Puppet::Node::Catalog.new + catalog = Puppet::Resource::Catalog.new catalog.add_resource file catalog.apply @@ -255,7 +255,7 @@ describe Puppet::Type.type(:file) do :content => "this is some content, yo" ) - catalog = Puppet::Node::Catalog.new + catalog = Puppet::Resource::Catalog.new catalog.add_resource file catalog.apply @@ -274,7 +274,7 @@ describe Puppet::Type.type(:file) do :source => source ) - catalog = Puppet::Node::Catalog.new + catalog = Puppet::Resource::Catalog.new catalog.add_resource file catalog.apply @@ -310,7 +310,7 @@ describe Puppet::Type.type(:file) do :purge => true, :recurse => true) - @catalog = Puppet::Node::Catalog.new + @catalog = Puppet::Resource::Catalog.new @catalog.add_resource @lfobj, @destobj end diff --git a/spec/integration/type/tidy.rb b/spec/integration/type/tidy.rb index 2c83e7a8a..3ad7de9ec 100755 --- a/spec/integration/type/tidy.rb +++ b/spec/integration/type/tidy.rb @@ -19,7 +19,7 @@ describe Puppet::Type.type(:tidy) do tidy = Puppet::Type.type(:tidy).create :path => dir, :recurse => true - catalog = Puppet::Node::Catalog.new + catalog = Puppet::Resource::Catalog.new catalog.add_resource(tidy) catalog.apply diff --git a/spec/unit/indirector/catalog/compiler.rb b/spec/unit/indirector/catalog/compiler.rb index 8cd3c72f4..29b76d8e5 100755 --- a/spec/unit/indirector/catalog/compiler.rb +++ b/spec/unit/indirector/catalog/compiler.rb @@ -7,7 +7,7 @@ require File.dirname(__FILE__) + '/../../../spec_helper' require 'puppet/indirector/catalog/compiler' -describe Puppet::Node::Catalog::Compiler do +describe Puppet::Resource::Catalog::Compiler do before do Puppet.expects(:version).returns(1) Facter.expects(:value).with('fqdn').returns("my.server.com") @@ -15,11 +15,11 @@ describe Puppet::Node::Catalog::Compiler do end it "should gather data about itself" do - Puppet::Node::Catalog::Compiler.new + Puppet::Resource::Catalog::Compiler.new end it "should cache the server metadata and reuse it" do - compiler = Puppet::Node::Catalog::Compiler.new + compiler = Puppet::Resource::Catalog::Compiler.new node1 = stub 'node1', :merge => nil node2 = stub 'node2', :merge => nil compiler.stubs(:compile) @@ -31,16 +31,16 @@ describe Puppet::Node::Catalog::Compiler do end it "should provide a method for determining if the catalog is networked" do - compiler = Puppet::Node::Catalog::Compiler.new + compiler = Puppet::Resource::Catalog::Compiler.new compiler.should respond_to(:networked?) end end -describe Puppet::Node::Catalog::Compiler, " when creating the interpreter" do +describe Puppet::Resource::Catalog::Compiler, " when creating the interpreter" do before do # This gets pretty annoying on a plane where we have no IP address Facter.stubs(:value).returns("whatever") - @compiler = Puppet::Node::Catalog::Compiler.new + @compiler = Puppet::Resource::Catalog::Compiler.new end it "should not create the interpreter until it is asked for the first time" do @@ -57,10 +57,10 @@ describe Puppet::Node::Catalog::Compiler, " when creating the interpreter" do end end -describe Puppet::Node::Catalog::Compiler, " when finding nodes" do +describe Puppet::Resource::Catalog::Compiler, " when finding nodes" do before do Facter.stubs(:value).returns("whatever") - @compiler = Puppet::Node::Catalog::Compiler.new + @compiler = Puppet::Resource::Catalog::Compiler.new @name = "me" @node = mock 'node' @request = stub 'request', :key => @name, :options => {} @@ -74,12 +74,12 @@ describe Puppet::Node::Catalog::Compiler, " when finding nodes" do end end -describe Puppet::Node::Catalog::Compiler, " after finding nodes" do +describe Puppet::Resource::Catalog::Compiler, " after finding nodes" do before do Puppet.expects(:version).returns(1) Facter.expects(:value).with('fqdn').returns("my.server.com") Facter.expects(:value).with('ipaddress').returns("my.ip.address") - @compiler = Puppet::Node::Catalog::Compiler.new + @compiler = Puppet::Resource::Catalog::Compiler.new @name = "me" @node = mock 'node' @request = stub 'request', :key => @name, :options => {} @@ -103,13 +103,13 @@ describe Puppet::Node::Catalog::Compiler, " after finding nodes" do end end -describe Puppet::Node::Catalog::Compiler, " when creating catalogs" do +describe Puppet::Resource::Catalog::Compiler, " when creating catalogs" do before do Facter.stubs(:value).returns("whatever") env = stub 'environment', :name => "yay" Puppet::Node::Environment.stubs(:new).returns(env) - @compiler = Puppet::Node::Catalog::Compiler.new + @compiler = Puppet::Resource::Catalog::Compiler.new @name = "me" @node = Puppet::Node.new @name @node.stubs(:merge) diff --git a/spec/unit/indirector/catalog/yaml.rb b/spec/unit/indirector/catalog/yaml.rb index 717fb816c..5f233c91c 100755 --- a/spec/unit/indirector/catalog/yaml.rb +++ b/spec/unit/indirector/catalog/yaml.rb @@ -2,24 +2,24 @@ require File.dirname(__FILE__) + '/../../../spec_helper' -require 'puppet/node/catalog' +require 'puppet/resource/catalog' require 'puppet/indirector/catalog/yaml' -describe Puppet::Node::Catalog::Yaml do +describe Puppet::Resource::Catalog::Yaml do it "should be a subclass of the Yaml terminus" do - Puppet::Node::Catalog::Yaml.superclass.should equal(Puppet::Indirector::Yaml) + Puppet::Resource::Catalog::Yaml.superclass.should equal(Puppet::Indirector::Yaml) end it "should have documentation" do - Puppet::Node::Catalog::Yaml.doc.should_not be_nil + Puppet::Resource::Catalog::Yaml.doc.should_not be_nil end it "should be registered with the catalog store indirection" do indirection = Puppet::Indirector::Indirection.instance(:catalog) - Puppet::Node::Catalog::Yaml.indirection.should equal(indirection) + Puppet::Resource::Catalog::Yaml.indirection.should equal(indirection) end it "should have its name set to :yaml" do - Puppet::Node::Catalog::Yaml.name.should == :yaml + Puppet::Resource::Catalog::Yaml.name.should == :yaml end end diff --git a/spec/unit/node/catalog.rb b/spec/unit/node/catalog.rb deleted file mode 100755 index 97674dfc2..000000000 --- a/spec/unit/node/catalog.rb +++ /dev/null @@ -1,932 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -describe Puppet::Node::Catalog, " when compiling" do - it "should accept tags" do - config = Puppet::Node::Catalog.new("mynode") - config.tag("one") - config.tags.should == %w{one} - end - - it "should accept multiple tags at once" do - config = Puppet::Node::Catalog.new("mynode") - config.tag("one", "two") - config.tags.should == %w{one two} - end - - it "should convert all tags to strings" do - config = Puppet::Node::Catalog.new("mynode") - config.tag("one", :two) - config.tags.should == %w{one two} - end - - it "should tag with both the qualified name and the split name" do - config = Puppet::Node::Catalog.new("mynode") - config.tag("one::two") - config.tags.include?("one").should be_true - config.tags.include?("one::two").should be_true - end - - it "should accept classes" do - config = Puppet::Node::Catalog.new("mynode") - config.add_class("one") - config.classes.should == %w{one} - config.add_class("two", "three") - config.classes.should == %w{one two three} - end - - it "should tag itself with passed class names" do - config = Puppet::Node::Catalog.new("mynode") - config.add_class("one") - config.tags.should == %w{one} - end -end - -describe Puppet::Node::Catalog, " when extracting" do - it "should return extraction result as the method result" do - config = Puppet::Node::Catalog.new("mynode") - config.expects(:extraction_format).returns(:whatever) - config.expects(:extract_to_whatever).returns(:result) - config.extract.should == :result - end -end - -describe Puppet::Node::Catalog, " when extracting transobjects" do - - def mkscope - @parser = Puppet::Parser::Parser.new :Code => "" - @node = Puppet::Node.new("mynode") - @compiler = Puppet::Parser::Compiler.new(@node, @parser) - - # XXX This is ridiculous. - @compiler.send(:evaluate_main) - @scope = @compiler.topscope - end - - def mkresource(type, name) - Puppet::Parser::Resource.new(:type => type, :title => name, :source => @source, :scope => @scope) - end - - it "should always create a TransBucket for the 'main' class" do - config = Puppet::Node::Catalog.new("mynode") - - @scope = mkscope - @source = mock 'source' - - main = mkresource("class", :main) - config.add_vertex(main) - - bucket = mock 'bucket' - bucket.expects(:classes=).with(config.classes) - main.stubs(:builtin?).returns(false) - main.expects(:to_transbucket).returns(bucket) - - config.extract_to_transportable.should equal(bucket) - end - - # This isn't really a spec-style test, but I don't know how better to do it. - it "should transform the resource graph into a tree of TransBuckets and TransObjects" do - config = Puppet::Node::Catalog.new("mynode") - - @scope = mkscope - @source = mock 'source' - - defined = mkresource("class", :main) - builtin = mkresource("file", "/yay") - - config.add_edge(defined, builtin) - - bucket = [] - bucket.expects(:classes=).with(config.classes) - defined.stubs(:builtin?).returns(false) - defined.expects(:to_transbucket).returns(bucket) - builtin.expects(:to_transobject).returns(:builtin) - - config.extract_to_transportable.should == [:builtin] - end - - # Now try it with a more complicated graph -- a three tier graph, each tier - it "should transform arbitrarily deep graphs into isomorphic trees" do - config = Puppet::Node::Catalog.new("mynode") - - @scope = mkscope - @scope.stubs(:tags).returns([]) - @source = mock 'source' - - # Create our scopes. - top = mkresource "class", :main - topbucket = [] - topbucket.expects(:classes=).with([]) - top.expects(:to_trans).returns(topbucket) - topres = mkresource "file", "/top" - topres.expects(:to_trans).returns(:topres) - config.add_edge top, topres - - middle = mkresource "class", "middle" - middle.expects(:to_trans).returns([]) - config.add_edge top, middle - midres = mkresource "file", "/mid" - midres.expects(:to_trans).returns(:midres) - config.add_edge middle, midres - - bottom = mkresource "class", "bottom" - bottom.expects(:to_trans).returns([]) - config.add_edge middle, bottom - botres = mkresource "file", "/bot" - botres.expects(:to_trans).returns(:botres) - config.add_edge bottom, botres - - toparray = config.extract_to_transportable - - # This is annoying; it should look like: - # [[[:botres], :midres], :topres] - # but we can't guarantee sort order. - toparray.include?(:topres).should be_true - - midarray = toparray.find { |t| t.is_a?(Array) } - midarray.include?(:midres).should be_true - botarray = midarray.find { |t| t.is_a?(Array) } - botarray.include?(:botres).should be_true - end -end - -describe Puppet::Node::Catalog, " when converting to a transobject catalog" do - class CatalogTestResource - attr_accessor :name, :virtual, :builtin - def initialize(name, options = {}) - @name = name - options.each { |p,v| send(p.to_s + "=", v) } - end - - def ref - if builtin? - "File[%s]" % name - else - "Class[%s]" % name - end - end - - def virtual? - virtual - end - - def builtin? - builtin - end - - def to_transobject - Puppet::TransObject.new(name, builtin? ? "file" : "class") - end - end - - before do - @original = Puppet::Node::Catalog.new("mynode") - @original.tag(*%w{one two three}) - @original.add_class *%w{four five six} - - @top = CatalogTestResource.new 'top' - @topobject = CatalogTestResource.new 'topobject', :builtin => true - @virtual = CatalogTestResource.new 'virtual', :virtual => true - @virtualobject = CatalogTestResource.new 'virtualobject', :builtin => true, :virtual => true - @middle = CatalogTestResource.new 'middle' - @middleobject = CatalogTestResource.new 'middleobject', :builtin => true - @bottom = CatalogTestResource.new 'bottom' - @bottomobject = CatalogTestResource.new 'bottomobject', :builtin => true - - @resources = [@top, @topobject, @middle, @middleobject, @bottom, @bottomobject] - - @original.add_edge(@top, @topobject) - @original.add_edge(@top, @virtual) - @original.add_edge(@virtual, @virtualobject) - @original.add_edge(@top, @middle) - @original.add_edge(@middle, @middleobject) - @original.add_edge(@middle, @bottom) - @original.add_edge(@bottom, @bottomobject) - - @catalog = @original.to_transportable - end - - it "should add all resources as TransObjects" do - @resources.each { |resource| @catalog.resource(resource.ref).should be_instance_of(Puppet::TransObject) } - end - - it "should not extract defined virtual resources" do - @catalog.vertices.find { |v| v.name == "virtual" }.should be_nil - end - - it "should not extract builtin virtual resources" do - @catalog.vertices.find { |v| v.name == "virtualobject" }.should be_nil - end - - it "should copy the tag list to the new catalog" do - @catalog.tags.sort.should == @original.tags.sort - end - - it "should copy the class list to the new catalog" do - @catalog.classes.should == @original.classes - end - - it "should duplicate the original edges" do - @original.edges.each do |edge| - next if edge.source.virtual? or edge.target.virtual? - source = @catalog.resource(edge.source.ref) - target = @catalog.resource(edge.target.ref) - - source.should_not be_nil - target.should_not be_nil - @catalog.edge?(source, target).should be_true - end - end - - it "should set itself as the catalog for each converted resource" do - @catalog.vertices.each { |v| v.catalog.object_id.should equal(@catalog.object_id) } - end -end - -describe Puppet::Node::Catalog, " when converting to a RAL catalog" do - before do - @original = Puppet::Node::Catalog.new("mynode") - @original.tag(*%w{one two three}) - @original.add_class *%w{four five six} - - @top = Puppet::TransObject.new 'top', "class" - @topobject = Puppet::TransObject.new '/topobject', "file" - @middle = Puppet::TransObject.new 'middle', "class" - @middleobject = Puppet::TransObject.new '/middleobject', "file" - @bottom = Puppet::TransObject.new 'bottom', "class" - @bottomobject = Puppet::TransObject.new '/bottomobject', "file" - - @resources = [@top, @topobject, @middle, @middleobject, @bottom, @bottomobject] - - @original.add_resource(*@resources) - - @original.add_edge(@top, @topobject) - @original.add_edge(@top, @middle) - @original.add_edge(@middle, @middleobject) - @original.add_edge(@middle, @bottom) - @original.add_edge(@bottom, @bottomobject) - - @catalog = @original.to_ral - end - - it "should add all resources as RAL instances" do - @resources.each { |resource| @catalog.resource(resource.ref).should be_instance_of(Puppet::Type) } - end - - it "should copy the tag list to the new catalog" do - @catalog.tags.sort.should == @original.tags.sort - end - - it "should copy the class list to the new catalog" do - @catalog.classes.should == @original.classes - end - - it "should duplicate the original edges" do - @original.edges.each do |edge| - @catalog.edge?(@catalog.resource(edge.source.ref), @catalog.resource(edge.target.ref)).should be_true - end - end - - it "should set itself as the catalog for each converted resource" do - @catalog.vertices.each { |v| v.catalog.object_id.should equal(@catalog.object_id) } - end - - it "should convert parser resources to transobjects and set the catalog" do - catalog = Puppet::Node::Catalog.new("mynode") - - result = mock 'catalog' - result.stub_everything - - Puppet::Node::Catalog.expects(:new).returns result - - trans = mock 'trans' - resource = Puppet::Parser::Resource.new(:scope => mock("scope"), :source => mock("source"), :type => :file, :title => "/eh") - resource.expects(:to_transobject).returns trans - trans.expects(:catalog=).with result - - trans.stub_everything - - catalog.add_resource(resource) - - catalog.to_ral - end - - # This tests #931. - it "should not lose track of resources whose names vary" do - changer = Puppet::TransObject.new 'changer', 'test' - - config = Puppet::Node::Catalog.new('test') - config.add_resource(changer) - config.add_resource(@top) - - config.add_edge(@top, changer) - - resource = stub 'resource', :name => "changer2", :title => "changer2", :ref => "Test[changer2]", :catalog= => nil, :remove => nil - - #changer is going to get duplicated as part of a fix for aliases 1094 - changer.expects(:dup).returns(changer) - changer.expects(:to_type).returns(resource) - - newconfig = nil - - proc { @catalog = config.to_ral }.should_not raise_error - @catalog.resource("Test[changer2]").should equal(resource) - end - - after do - # Remove all resource instances. - @catalog.clear(true) - end -end - -describe Puppet::Node::Catalog, " when functioning as a resource container" do - before do - @catalog = Puppet::Node::Catalog.new("host") - @one = Puppet::Type.type(:notify).create :name => "one" - @two = Puppet::Type.type(:notify).create :name => "two" - @dupe = Puppet::Type.type(:notify).create :name => "one" - end - - it "should provide a method to add one or more resources" do - @catalog.add_resource @one, @two - @catalog.resource(@one.ref).should equal(@one) - @catalog.resource(@two.ref).should equal(@two) - end - - it "should add resources to the relationship graph if it exists" do - relgraph = @catalog.relationship_graph - @catalog.add_resource @one - relgraph.should be_vertex(@one) - end - - it "should yield added resources if a block is provided" do - yielded = [] - @catalog.add_resource(@one, @two) { |r| yielded << r } - yielded.length.should == 2 - end - - it "should set itself as the resource's catalog if it is not a relationship graph" do - @one.expects(:catalog=).with(@catalog) - @catalog.add_resource @one - end - - it "should make all vertices available by resource reference" do - @catalog.add_resource(@one) - @catalog.resource(@one.ref).should equal(@one) - @catalog.vertices.find { |r| r.ref == @one.ref }.should equal(@one) - end - - it "should canonize how resources are referred to during retrieval when both type and title are provided" do - @catalog.add_resource(@one) - - @catalog.resource("notify", "one").should equal(@one) - end - - it "should canonize how resources are referred to during retrieval when just the title is provided" do - @catalog.add_resource(@one) - - @catalog.resource("notify[one]", nil).should equal(@one) - end - - it "should not allow two resources with the same resource reference" do - @catalog.add_resource(@one) - - proc { @catalog.add_resource(@dupe) }.should raise_error(Puppet::Node::Catalog::DuplicateResourceError) - end - - it "should ignore implicit resources that conflict with existing resources" do - @catalog.add_resource(@one) - - @dupe.implicit = true - - yielded = [] - @catalog.add_resource(@dupe) { |r| yielded << r } - yielded.should be_empty - end - - it "should not set the catalog for ignored implicit resources" do - @catalog.add_resource(@one) - - @dupe.implicit = true - - @catalog.add_resource(@dupe) - @dupe.catalog.should be_nil - end - - it "should log when implicit resources are ignored" do - @catalog.add_resource(@one) - - @dupe.implicit = true - - @dupe.expects(:debug) - @catalog.add_resource(@dupe) - end - - it "should replace implicit resources if a conflicting explicit resource is added" do - @catalog.add_resource(@one) - @one.implicit = true - - proc { @catalog.add_resource(@dupe) }.should_not raise_error - @catalog.resource(:notify, "one").should equal(@dupe) - end - - it "should log when implicit resources are removed from the catalog" do - @catalog.add_resource(@one) - @one.implicit = true - - @one.expects(:debug) - @catalog.add_resource(@dupe) - end - - it "should not store objects that do not respond to :ref" do - proc { @catalog.add_resource("thing") }.should raise_error(ArgumentError) - end - - it "should remove all resources when asked" do - @catalog.add_resource @one - @catalog.add_resource @two - @one.expects :remove - @two.expects :remove - @catalog.clear(true) - end - - it "should support a mechanism for finishing resources" do - @one.expects :finish - @two.expects :finish - @catalog.add_resource @one - @catalog.add_resource @two - - @catalog.finalize - end - - it "should make default resources when finalizing" do - @catalog.expects(:make_default_resources) - @catalog.finalize - end - - it "should add default resources to the catalog upon creation" do - @catalog.make_default_resources - @catalog.resource(:schedule, "daily").should_not be_nil - end - - it "should optionally support an initialization block and should finalize after such blocks" do - @one.expects :finish - @two.expects :finish - config = Puppet::Node::Catalog.new("host") do |conf| - conf.add_resource @one - conf.add_resource @two - end - end - - it "should inform the resource that it is the resource's catalog" do - @one.expects(:catalog=).with(@catalog) - @catalog.add_resource @one - end - - it "should be able to find resources by reference" do - @catalog.add_resource @one - @catalog.resource(@one.ref).should equal(@one) - end - - it "should be able to find resources by reference or by type/title tuple" do - @catalog.add_resource @one - @catalog.resource("notify", "one").should equal(@one) - end - - it "should have a mechanism for removing resources" do - @catalog.add_resource @one - @one.expects :remove - @catalog.remove_resource(@one) - @catalog.resource(@one.ref).should be_nil - @catalog.vertex?(@one).should be_false - end - - it "should have a method for creating aliases for resources" do - @catalog.add_resource @one - @catalog.alias(@one, "other") - @catalog.resource("notify", "other").should equal(@one) - end - - it "should ignore conflicting aliases that point to the aliased resource" do - @catalog.alias(@one, "other") - lambda { @catalog.alias(@one, "other") }.should_not raise_error - end - - it "should create aliases for resources isomorphic resources whose names do not match their titles" do - resource = Puppet::Type::File.create(:title => "testing", :path => "/something") - - @catalog.add_resource(resource) - - @catalog.resource(:file, "/something").should equal(resource) - end - - it "should not create aliases for resources non-isomorphic resources whose names do not match their titles" do - resource = Puppet::Type.type(:exec).create(:title => "testing", :command => "echo", :path => %w{/bin /usr/bin /usr/local/bin}) - - @catalog.add_resource(resource) - - # Yay, I've already got a 'should' method - @catalog.resource(:exec, "echo").object_id.should == nil.object_id - end - - # This test is the same as the previous, but the behaviour should be explicit. - it "should alias using the class name from the resource reference, not the resource class name" do - @catalog.add_resource @one - @catalog.alias(@one, "other") - @catalog.resource("notify", "other").should equal(@one) - end - - it "should ignore conflicting aliases that point to the aliased resource" do - @catalog.alias(@one, "other") - lambda { @catalog.alias(@one, "other") }.should_not raise_error - end - - it "should fail to add an alias if the aliased name already exists" do - @catalog.add_resource @one - proc { @catalog.alias @two, "one" }.should raise_error(ArgumentError) - end - - it "should not fail when a resource has duplicate aliases created" do - @catalog.add_resource @one - proc { @catalog.alias @one, "one" }.should_not raise_error - end - - it "should not create aliases that point back to the resource" do - @catalog.alias(@one, "one") - @catalog.resource(:notify, "one").should be_nil - end - - it "should be able to look resources up by their aliases" do - @catalog.add_resource @one - @catalog.alias @one, "two" - @catalog.resource(:notify, "two").should equal(@one) - end - - it "should remove resource aliases when the target resource is removed" do - @catalog.add_resource @one - @catalog.alias(@one, "other") - @one.expects :remove - @catalog.remove_resource(@one) - @catalog.resource("notify", "other").should be_nil - end - - it "should add an alias for the namevar when the title and name differ on isomorphic resource types" do - resource = Puppet::Type.type(:file).create :path => "/something", :title => "other", :content => "blah" - resource.expects(:isomorphic?).returns(true) - @catalog.add_resource(resource) - @catalog.resource(:file, "other").should equal(resource) - @catalog.resource(:file, "/something").ref.should == resource.ref - end - - it "should not add an alias for the namevar when the title and name differ on non-isomorphic resource types" do - resource = Puppet::Type.type(:file).create :path => "/something", :title => "other", :content => "blah" - resource.expects(:isomorphic?).returns(false) - @catalog.add_resource(resource) - @catalog.resource(:file, resource.title).should equal(resource) - # We can't use .should here, because the resources respond to that method. - if @catalog.resource(:file, resource.name) - raise "Aliased non-isomorphic resource" - end - end - - it "should provide a method to create additional resources that also registers the resource" do - args = {:name => "/yay", :ensure => :file} - resource = stub 'file', :ref => "File[/yay]", :catalog= => @catalog, :title => "/yay", :[] => "/yay" - Puppet::Type.type(:file).expects(:create).with(args).returns(resource) - @catalog.create_resource :file, args - @catalog.resource("File[/yay]").should equal(resource) - end -end - -describe Puppet::Node::Catalog do - before :each do - @catalog = Puppet::Node::Catalog.new("host") - - @catalog.retrieval_duration = Time.now - @transaction = mock 'transaction' - Puppet::Transaction.stubs(:new).returns(@transaction) - @transaction.stubs(:evaluate) - @transaction.stubs(:cleanup) - @transaction.stubs(:addtimes) - end - - it "should be an Expirer" do - Puppet::Node::Catalog.ancestors.should be_include(Puppet::Util::Cacher::Expirer) - end - - it "should always be expired if it's not applying" do - @catalog.expects(:applying?).returns false - @catalog.should be_expired(Time.now) - end - - it "should not be expired if it's applying and the timestamp is late enough" do - @catalog.expire - @catalog.expects(:applying?).returns true - @catalog.should_not be_expired(Time.now) - end - - describe "when applying" do - it "should create and evaluate a transaction" do - @transaction.expects(:evaluate) - @catalog.apply - end - - it "should provide the catalog time to the transaction" do - @transaction.expects(:addtimes).with do |arg| - arg[:config_retrieval].should be_instance_of(Time) - true - end - @catalog.apply - end - - it "should clean up the transaction" do - @transaction.expects :cleanup - @catalog.apply - end - - it "should return the transaction" do - @catalog.apply.should equal(@transaction) - end - - it "should yield the transaction if a block is provided" do - @catalog.apply do |trans| - trans.should equal(@transaction) - end - end - - it "should default to not being a host catalog" do - @catalog.host_config.should be_nil - end - - it "should pass supplied tags on to the transaction" do - @transaction.expects(:tags=).with(%w{one two}) - @catalog.apply(:tags => %w{one two}) - end - - it "should set ignoreschedules on the transaction if specified in apply()" do - @transaction.expects(:ignoreschedules=).with(true) - @catalog.apply(:ignoreschedules => true) - end - - it "should remove resources created mid-transaction" do - args = {:name => "/yay", :ensure => :file} - resource = stub 'file', :ref => "File[/yay]", :catalog= => @catalog, :title => "/yay", :[] => "/yay" - @transaction = mock 'transaction' - Puppet::Transaction.stubs(:new).returns(@transaction) - @transaction.stubs(:evaluate) - @transaction.stubs(:cleanup) - @transaction.stubs(:addtimes) - Puppet::Type.type(:file).expects(:create).with(args).returns(resource) - resource.expects :remove - @catalog.apply do |trans| - @catalog.create_resource :file, args - @catalog.resource("File[/yay]").should equal(resource) - end - @catalog.resource("File[/yay]").should be_nil - end - - it "should remove resources added mid-transaction" do - @transaction = mock 'transaction' - Puppet::Transaction.stubs(:new).returns(@transaction) - @transaction.stubs(:evaluate) - @transaction.stubs(:cleanup) - @transaction.stubs(:addtimes) - file = Puppet::Type.type(:file).create(:name => "/yay", :ensure => :file) - @catalog.apply do |trans| - @catalog.add_resource file - @catalog.resource("File[/yay]").should_not be_nil - end - @catalog.resource("File[/yay]").should be_nil - end - - it "should expire cached data in the resources both before and after the transaction" do - @catalog.expects(:expire).times(2) - @catalog.apply - end - end - - describe "when applying host catalogs" do - - # super() doesn't work in the setup method for some reason - before do - @catalog.host_config = true - Puppet::Util::Storage.stubs(:store) - end - - it "should send a report if reporting is enabled" do - Puppet[:report] = true - @transaction.expects :send_report - @transaction.stubs :any_failed? => false - @catalog.apply - end - - it "should send a report if report summaries are enabled" do - Puppet[:summarize] = true - @transaction.expects :send_report - @transaction.stubs :any_failed? => false - @catalog.apply - end - - it "should initialize the state database before applying a catalog" do - Puppet::Util::Storage.expects(:load) - - # Short-circuit the apply, so we know we're loading before the transaction - Puppet::Transaction.expects(:new).raises ArgumentError - proc { @catalog.apply }.should raise_error(ArgumentError) - end - - it "should sync the state database after applying" do - Puppet::Util::Storage.expects(:store) - @transaction.stubs :any_failed? => false - @catalog.apply - end - - after { Puppet.settings.clear } - end - - describe Puppet::Node::Catalog, " when applying non-host catalogs" do - - before do - @catalog.host_config = false - end - - it "should never send reports" do - Puppet[:report] = true - Puppet[:summarize] = true - @transaction.expects(:send_report).never - @catalog.apply - end - - it "should never modify the state database" do - Puppet::Util::Storage.expects(:load).never - Puppet::Util::Storage.expects(:store).never - @catalog.apply - end - - after { Puppet.settings.clear } - end -end - -describe Puppet::Node::Catalog, " when creating a relationship graph" do - before do - Puppet::Type.type(:component) - @catalog = Puppet::Node::Catalog.new("host") - @compone = Puppet::Type::Component.create :name => "one" - @comptwo = Puppet::Type::Component.create :name => "two", :require => ["class", "one"] - @file = Puppet::Type.type(:file) - @one = @file.create :path => "/one" - @two = @file.create :path => "/two" - @sub = @file.create :path => "/two/subdir" - @catalog.add_edge @compone, @one - @catalog.add_edge @comptwo, @two - - @three = @file.create :path => "/three" - @four = @file.create :path => "/four", :require => ["file", "/three"] - @five = @file.create :path => "/five" - @catalog.add_resource @compone, @comptwo, @one, @two, @three, @four, @five, @sub - @relationships = @catalog.relationship_graph - end - - it "should be able to create a relationship graph" do - @relationships.should be_instance_of(Puppet::SimpleGraph) - end - - it "should not have any components" do - @relationships.vertices.find { |r| r.instance_of?(Puppet::Type::Component) }.should be_nil - end - - it "should have all non-component resources from the catalog" do - # The failures print out too much info, so i just do a class comparison - @relationships.vertex?(@five).should be_true - end - - it "should have all resource relationships set as edges" do - @relationships.edge?(@three, @four).should be_true - end - - it "should copy component relationships to all contained resources" do - @relationships.edge?(@one, @two).should be_true - end - - it "should add automatic relationships to the relationship graph" do - @relationships.edge?(@two, @sub).should be_true - end - - it "should get removed when the catalog is cleaned up" do - @relationships.expects(:clear) - @catalog.clear - @catalog.instance_variable_get("@relationship_graph").should be_nil - end - - it "should write :relationships and :expanded_relationships graph files if the catalog is a host catalog" do - @catalog.clear - graph = Puppet::SimpleGraph.new - Puppet::SimpleGraph.expects(:new).returns graph - - graph.expects(:write_graph).with(:relationships) - graph.expects(:write_graph).with(:expanded_relationships) - - @catalog.host_config = true - - @catalog.relationship_graph - end - - it "should not write graph files if the catalog is not a host catalog" do - @catalog.clear - graph = Puppet::SimpleGraph.new - Puppet::SimpleGraph.expects(:new).returns graph - - graph.expects(:write_graph).never - - @catalog.host_config = false - - @catalog.relationship_graph - end - - it "should create a new relationship graph after clearing the old one" do - @relationships.expects(:clear) - @catalog.clear - @catalog.relationship_graph.should be_instance_of(Puppet::SimpleGraph) - end - - it "should remove removed resources from the relationship graph if it exists" do - @catalog.remove_resource(@one) - @catalog.relationship_graph.vertex?(@one).should be_false - end -end - -describe Puppet::Node::Catalog, " when writing dot files" do - before do - @catalog = Puppet::Node::Catalog.new("host") - @name = :test - @file = File.join(Puppet[:graphdir], @name.to_s + ".dot") - end - - it "should only write when it is a host catalog" do - File.expects(:open).with(@file).never - @catalog.host_config = false - Puppet[:graph] = true - @catalog.write_graph(@name) - end - - after do - Puppet.settings.clear - end -end - -describe Puppet::Node::Catalog, " when indirecting" do - before do - @indirection = stub 'indirection', :name => :catalog - - Puppet::Util::Cacher.expire - end - - it "should redirect to the indirection for retrieval" do - Puppet::Node::Catalog.stubs(:indirection).returns(@indirection) - @indirection.expects(:find) - Puppet::Node::Catalog.find(:myconfig) - end - - it "should default to the 'compiler' terminus" do - Puppet::Node::Catalog.indirection.terminus_class.should == :compiler - end - - after do - Puppet::Util::Cacher.expire - end -end - -describe Puppet::Node::Catalog, " when converting to yaml" do - before do - @catalog = Puppet::Node::Catalog.new("me") - @catalog.add_edge("one", "two") - end - - it "should be able to be dumped to yaml" do - YAML.dump(@catalog).should be_instance_of(String) - end -end - -describe Puppet::Node::Catalog, " when converting from yaml" do - before do - @catalog = Puppet::Node::Catalog.new("me") - @catalog.add_edge("one", "two") - - text = YAML.dump(@catalog) - @newcatalog = YAML.load(text) - end - - it "should get converted back to a catalog" do - @newcatalog.should be_instance_of(Puppet::Node::Catalog) - end - - it "should have all vertices" do - @newcatalog.vertex?("one").should be_true - @newcatalog.vertex?("two").should be_true - end - - it "should have all edges" do - @newcatalog.edge?("one", "two").should be_true - end -end diff --git a/spec/unit/other/transaction.rb b/spec/unit/other/transaction.rb index 0db470d26..d2ac26869 100755 --- a/spec/unit/other/transaction.rb +++ b/spec/unit/other/transaction.rb @@ -6,7 +6,7 @@ require 'puppet/transaction' describe Puppet::Transaction, " when determining tags" do before do - @config = Puppet::Node::Catalog.new + @config = Puppet::Resource::Catalog.new @transaction = Puppet::Transaction.new(@config) end diff --git a/spec/unit/other/transbucket.rb b/spec/unit/other/transbucket.rb index e447c78a2..60efcdb87 100755 --- a/spec/unit/other/transbucket.rb +++ b/spec/unit/other/transbucket.rb @@ -130,14 +130,14 @@ describe Puppet::TransBucket, " when generating a catalog" do end it "should set each TransObject's catalog before converting to a RAL resource" do - @middleobj.expects(:catalog=).with { |c| c.is_a?(Puppet::Node::Catalog) } + @middleobj.expects(:catalog=).with { |c| c.is_a?(Puppet::Resource::Catalog) } @top.to_catalog end it "should set each TransBucket's catalog before converting to a RAL resource" do # each bucket is seen twice in the loop, so we have to handle the case where the config # is set twice - @bottom.expects(:catalog=).with { |c| c.is_a?(Puppet::Node::Catalog) }.at_least_once + @bottom.expects(:catalog=).with { |c| c.is_a?(Puppet::Resource::Catalog) }.at_least_once @top.to_catalog end end diff --git a/spec/unit/parser/compiler.rb b/spec/unit/parser/compiler.rb index bf50d8790..025836229 100755 --- a/spec/unit/parser/compiler.rb +++ b/spec/unit/parser/compiler.rb @@ -251,7 +251,7 @@ describe Puppet::Parser::Compiler do file2 = Puppet::Type.type(:file).create :path => "/foo" @compiler.add_resource(@scope, file1) - lambda { @compiler.add_resource(@scope, file2) }.should raise_error(Puppet::Node::Catalog::DuplicateResourceError) + lambda { @compiler.add_resource(@scope, file2) }.should raise_error(Puppet::Resource::Catalog::DuplicateResourceError) end it "should have a method for looking up resources" do diff --git a/spec/unit/resource/catalog.rb b/spec/unit/resource/catalog.rb new file mode 100755 index 000000000..27e6296c6 --- /dev/null +++ b/spec/unit/resource/catalog.rb @@ -0,0 +1,932 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +describe Puppet::Resource::Catalog, " when compiling" do + it "should accept tags" do + config = Puppet::Resource::Catalog.new("mynode") + config.tag("one") + config.tags.should == %w{one} + end + + it "should accept multiple tags at once" do + config = Puppet::Resource::Catalog.new("mynode") + config.tag("one", "two") + config.tags.should == %w{one two} + end + + it "should convert all tags to strings" do + config = Puppet::Resource::Catalog.new("mynode") + config.tag("one", :two) + config.tags.should == %w{one two} + end + + it "should tag with both the qualified name and the split name" do + config = Puppet::Resource::Catalog.new("mynode") + config.tag("one::two") + config.tags.include?("one").should be_true + config.tags.include?("one::two").should be_true + end + + it "should accept classes" do + config = Puppet::Resource::Catalog.new("mynode") + config.add_class("one") + config.classes.should == %w{one} + config.add_class("two", "three") + config.classes.should == %w{one two three} + end + + it "should tag itself with passed class names" do + config = Puppet::Resource::Catalog.new("mynode") + config.add_class("one") + config.tags.should == %w{one} + end +end + +describe Puppet::Resource::Catalog, " when extracting" do + it "should return extraction result as the method result" do + config = Puppet::Resource::Catalog.new("mynode") + config.expects(:extraction_format).returns(:whatever) + config.expects(:extract_to_whatever).returns(:result) + config.extract.should == :result + end +end + +describe Puppet::Resource::Catalog, " when extracting transobjects" do + + def mkscope + @parser = Puppet::Parser::Parser.new :Code => "" + @node = Puppet::Node.new("mynode") + @compiler = Puppet::Parser::Compiler.new(@node, @parser) + + # XXX This is ridiculous. + @compiler.send(:evaluate_main) + @scope = @compiler.topscope + end + + def mkresource(type, name) + Puppet::Parser::Resource.new(:type => type, :title => name, :source => @source, :scope => @scope) + end + + it "should always create a TransBucket for the 'main' class" do + config = Puppet::Resource::Catalog.new("mynode") + + @scope = mkscope + @source = mock 'source' + + main = mkresource("class", :main) + config.add_vertex(main) + + bucket = mock 'bucket' + bucket.expects(:classes=).with(config.classes) + main.stubs(:builtin?).returns(false) + main.expects(:to_transbucket).returns(bucket) + + config.extract_to_transportable.should equal(bucket) + end + + # This isn't really a spec-style test, but I don't know how better to do it. + it "should transform the resource graph into a tree of TransBuckets and TransObjects" do + config = Puppet::Resource::Catalog.new("mynode") + + @scope = mkscope + @source = mock 'source' + + defined = mkresource("class", :main) + builtin = mkresource("file", "/yay") + + config.add_edge(defined, builtin) + + bucket = [] + bucket.expects(:classes=).with(config.classes) + defined.stubs(:builtin?).returns(false) + defined.expects(:to_transbucket).returns(bucket) + builtin.expects(:to_transobject).returns(:builtin) + + config.extract_to_transportable.should == [:builtin] + end + + # Now try it with a more complicated graph -- a three tier graph, each tier + it "should transform arbitrarily deep graphs into isomorphic trees" do + config = Puppet::Resource::Catalog.new("mynode") + + @scope = mkscope + @scope.stubs(:tags).returns([]) + @source = mock 'source' + + # Create our scopes. + top = mkresource "class", :main + topbucket = [] + topbucket.expects(:classes=).with([]) + top.expects(:to_trans).returns(topbucket) + topres = mkresource "file", "/top" + topres.expects(:to_trans).returns(:topres) + config.add_edge top, topres + + middle = mkresource "class", "middle" + middle.expects(:to_trans).returns([]) + config.add_edge top, middle + midres = mkresource "file", "/mid" + midres.expects(:to_trans).returns(:midres) + config.add_edge middle, midres + + bottom = mkresource "class", "bottom" + bottom.expects(:to_trans).returns([]) + config.add_edge middle, bottom + botres = mkresource "file", "/bot" + botres.expects(:to_trans).returns(:botres) + config.add_edge bottom, botres + + toparray = config.extract_to_transportable + + # This is annoying; it should look like: + # [[[:botres], :midres], :topres] + # but we can't guarantee sort order. + toparray.include?(:topres).should be_true + + midarray = toparray.find { |t| t.is_a?(Array) } + midarray.include?(:midres).should be_true + botarray = midarray.find { |t| t.is_a?(Array) } + botarray.include?(:botres).should be_true + end +end + +describe Puppet::Resource::Catalog, " when converting to a transobject catalog" do + class CatalogTestResource + attr_accessor :name, :virtual, :builtin + def initialize(name, options = {}) + @name = name + options.each { |p,v| send(p.to_s + "=", v) } + end + + def ref + if builtin? + "File[%s]" % name + else + "Class[%s]" % name + end + end + + def virtual? + virtual + end + + def builtin? + builtin + end + + def to_transobject + Puppet::TransObject.new(name, builtin? ? "file" : "class") + end + end + + before do + @original = Puppet::Resource::Catalog.new("mynode") + @original.tag(*%w{one two three}) + @original.add_class *%w{four five six} + + @top = CatalogTestResource.new 'top' + @topobject = CatalogTestResource.new 'topobject', :builtin => true + @virtual = CatalogTestResource.new 'virtual', :virtual => true + @virtualobject = CatalogTestResource.new 'virtualobject', :builtin => true, :virtual => true + @middle = CatalogTestResource.new 'middle' + @middleobject = CatalogTestResource.new 'middleobject', :builtin => true + @bottom = CatalogTestResource.new 'bottom' + @bottomobject = CatalogTestResource.new 'bottomobject', :builtin => true + + @resources = [@top, @topobject, @middle, @middleobject, @bottom, @bottomobject] + + @original.add_edge(@top, @topobject) + @original.add_edge(@top, @virtual) + @original.add_edge(@virtual, @virtualobject) + @original.add_edge(@top, @middle) + @original.add_edge(@middle, @middleobject) + @original.add_edge(@middle, @bottom) + @original.add_edge(@bottom, @bottomobject) + + @catalog = @original.to_transportable + end + + it "should add all resources as TransObjects" do + @resources.each { |resource| @catalog.resource(resource.ref).should be_instance_of(Puppet::TransObject) } + end + + it "should not extract defined virtual resources" do + @catalog.vertices.find { |v| v.name == "virtual" }.should be_nil + end + + it "should not extract builtin virtual resources" do + @catalog.vertices.find { |v| v.name == "virtualobject" }.should be_nil + end + + it "should copy the tag list to the new catalog" do + @catalog.tags.sort.should == @original.tags.sort + end + + it "should copy the class list to the new catalog" do + @catalog.classes.should == @original.classes + end + + it "should duplicate the original edges" do + @original.edges.each do |edge| + next if edge.source.virtual? or edge.target.virtual? + source = @catalog.resource(edge.source.ref) + target = @catalog.resource(edge.target.ref) + + source.should_not be_nil + target.should_not be_nil + @catalog.edge?(source, target).should be_true + end + end + + it "should set itself as the catalog for each converted resource" do + @catalog.vertices.each { |v| v.catalog.object_id.should equal(@catalog.object_id) } + end +end + +describe Puppet::Resource::Catalog, " when converting to a RAL catalog" do + before do + @original = Puppet::Resource::Catalog.new("mynode") + @original.tag(*%w{one two three}) + @original.add_class *%w{four five six} + + @top = Puppet::TransObject.new 'top', "class" + @topobject = Puppet::TransObject.new '/topobject', "file" + @middle = Puppet::TransObject.new 'middle', "class" + @middleobject = Puppet::TransObject.new '/middleobject', "file" + @bottom = Puppet::TransObject.new 'bottom', "class" + @bottomobject = Puppet::TransObject.new '/bottomobject', "file" + + @resources = [@top, @topobject, @middle, @middleobject, @bottom, @bottomobject] + + @original.add_resource(*@resources) + + @original.add_edge(@top, @topobject) + @original.add_edge(@top, @middle) + @original.add_edge(@middle, @middleobject) + @original.add_edge(@middle, @bottom) + @original.add_edge(@bottom, @bottomobject) + + @catalog = @original.to_ral + end + + it "should add all resources as RAL instances" do + @resources.each { |resource| @catalog.resource(resource.ref).should be_instance_of(Puppet::Type) } + end + + it "should copy the tag list to the new catalog" do + @catalog.tags.sort.should == @original.tags.sort + end + + it "should copy the class list to the new catalog" do + @catalog.classes.should == @original.classes + end + + it "should duplicate the original edges" do + @original.edges.each do |edge| + @catalog.edge?(@catalog.resource(edge.source.ref), @catalog.resource(edge.target.ref)).should be_true + end + end + + it "should set itself as the catalog for each converted resource" do + @catalog.vertices.each { |v| v.catalog.object_id.should equal(@catalog.object_id) } + end + + it "should convert parser resources to transobjects and set the catalog" do + catalog = Puppet::Resource::Catalog.new("mynode") + + result = mock 'catalog' + result.stub_everything + + Puppet::Resource::Catalog.expects(:new).returns result + + trans = mock 'trans' + resource = Puppet::Parser::Resource.new(:scope => mock("scope"), :source => mock("source"), :type => :file, :title => "/eh") + resource.expects(:to_transobject).returns trans + trans.expects(:catalog=).with result + + trans.stub_everything + + catalog.add_resource(resource) + + catalog.to_ral + end + + # This tests #931. + it "should not lose track of resources whose names vary" do + changer = Puppet::TransObject.new 'changer', 'test' + + config = Puppet::Resource::Catalog.new('test') + config.add_resource(changer) + config.add_resource(@top) + + config.add_edge(@top, changer) + + resource = stub 'resource', :name => "changer2", :title => "changer2", :ref => "Test[changer2]", :catalog= => nil, :remove => nil + + #changer is going to get duplicated as part of a fix for aliases 1094 + changer.expects(:dup).returns(changer) + changer.expects(:to_type).returns(resource) + + newconfig = nil + + proc { @catalog = config.to_ral }.should_not raise_error + @catalog.resource("Test[changer2]").should equal(resource) + end + + after do + # Remove all resource instances. + @catalog.clear(true) + end +end + +describe Puppet::Resource::Catalog, " when functioning as a resource container" do + before do + @catalog = Puppet::Resource::Catalog.new("host") + @one = Puppet::Type.type(:notify).create :name => "one" + @two = Puppet::Type.type(:notify).create :name => "two" + @dupe = Puppet::Type.type(:notify).create :name => "one" + end + + it "should provide a method to add one or more resources" do + @catalog.add_resource @one, @two + @catalog.resource(@one.ref).should equal(@one) + @catalog.resource(@two.ref).should equal(@two) + end + + it "should add resources to the relationship graph if it exists" do + relgraph = @catalog.relationship_graph + @catalog.add_resource @one + relgraph.should be_vertex(@one) + end + + it "should yield added resources if a block is provided" do + yielded = [] + @catalog.add_resource(@one, @two) { |r| yielded << r } + yielded.length.should == 2 + end + + it "should set itself as the resource's catalog if it is not a relationship graph" do + @one.expects(:catalog=).with(@catalog) + @catalog.add_resource @one + end + + it "should make all vertices available by resource reference" do + @catalog.add_resource(@one) + @catalog.resource(@one.ref).should equal(@one) + @catalog.vertices.find { |r| r.ref == @one.ref }.should equal(@one) + end + + it "should canonize how resources are referred to during retrieval when both type and title are provided" do + @catalog.add_resource(@one) + + @catalog.resource("notify", "one").should equal(@one) + end + + it "should canonize how resources are referred to during retrieval when just the title is provided" do + @catalog.add_resource(@one) + + @catalog.resource("notify[one]", nil).should equal(@one) + end + + it "should not allow two resources with the same resource reference" do + @catalog.add_resource(@one) + + proc { @catalog.add_resource(@dupe) }.should raise_error(Puppet::Resource::Catalog::DuplicateResourceError) + end + + it "should ignore implicit resources that conflict with existing resources" do + @catalog.add_resource(@one) + + @dupe.implicit = true + + yielded = [] + @catalog.add_resource(@dupe) { |r| yielded << r } + yielded.should be_empty + end + + it "should not set the catalog for ignored implicit resources" do + @catalog.add_resource(@one) + + @dupe.implicit = true + + @catalog.add_resource(@dupe) + @dupe.catalog.should be_nil + end + + it "should log when implicit resources are ignored" do + @catalog.add_resource(@one) + + @dupe.implicit = true + + @dupe.expects(:debug) + @catalog.add_resource(@dupe) + end + + it "should replace implicit resources if a conflicting explicit resource is added" do + @catalog.add_resource(@one) + @one.implicit = true + + proc { @catalog.add_resource(@dupe) }.should_not raise_error + @catalog.resource(:notify, "one").should equal(@dupe) + end + + it "should log when implicit resources are removed from the catalog" do + @catalog.add_resource(@one) + @one.implicit = true + + @one.expects(:debug) + @catalog.add_resource(@dupe) + end + + it "should not store objects that do not respond to :ref" do + proc { @catalog.add_resource("thing") }.should raise_error(ArgumentError) + end + + it "should remove all resources when asked" do + @catalog.add_resource @one + @catalog.add_resource @two + @one.expects :remove + @two.expects :remove + @catalog.clear(true) + end + + it "should support a mechanism for finishing resources" do + @one.expects :finish + @two.expects :finish + @catalog.add_resource @one + @catalog.add_resource @two + + @catalog.finalize + end + + it "should make default resources when finalizing" do + @catalog.expects(:make_default_resources) + @catalog.finalize + end + + it "should add default resources to the catalog upon creation" do + @catalog.make_default_resources + @catalog.resource(:schedule, "daily").should_not be_nil + end + + it "should optionally support an initialization block and should finalize after such blocks" do + @one.expects :finish + @two.expects :finish + config = Puppet::Resource::Catalog.new("host") do |conf| + conf.add_resource @one + conf.add_resource @two + end + end + + it "should inform the resource that it is the resource's catalog" do + @one.expects(:catalog=).with(@catalog) + @catalog.add_resource @one + end + + it "should be able to find resources by reference" do + @catalog.add_resource @one + @catalog.resource(@one.ref).should equal(@one) + end + + it "should be able to find resources by reference or by type/title tuple" do + @catalog.add_resource @one + @catalog.resource("notify", "one").should equal(@one) + end + + it "should have a mechanism for removing resources" do + @catalog.add_resource @one + @one.expects :remove + @catalog.remove_resource(@one) + @catalog.resource(@one.ref).should be_nil + @catalog.vertex?(@one).should be_false + end + + it "should have a method for creating aliases for resources" do + @catalog.add_resource @one + @catalog.alias(@one, "other") + @catalog.resource("notify", "other").should equal(@one) + end + + it "should ignore conflicting aliases that point to the aliased resource" do + @catalog.alias(@one, "other") + lambda { @catalog.alias(@one, "other") }.should_not raise_error + end + + it "should create aliases for resources isomorphic resources whose names do not match their titles" do + resource = Puppet::Type::File.create(:title => "testing", :path => "/something") + + @catalog.add_resource(resource) + + @catalog.resource(:file, "/something").should equal(resource) + end + + it "should not create aliases for resources non-isomorphic resources whose names do not match their titles" do + resource = Puppet::Type.type(:exec).create(:title => "testing", :command => "echo", :path => %w{/bin /usr/bin /usr/local/bin}) + + @catalog.add_resource(resource) + + # Yay, I've already got a 'should' method + @catalog.resource(:exec, "echo").object_id.should == nil.object_id + end + + # This test is the same as the previous, but the behaviour should be explicit. + it "should alias using the class name from the resource reference, not the resource class name" do + @catalog.add_resource @one + @catalog.alias(@one, "other") + @catalog.resource("notify", "other").should equal(@one) + end + + it "should ignore conflicting aliases that point to the aliased resource" do + @catalog.alias(@one, "other") + lambda { @catalog.alias(@one, "other") }.should_not raise_error + end + + it "should fail to add an alias if the aliased name already exists" do + @catalog.add_resource @one + proc { @catalog.alias @two, "one" }.should raise_error(ArgumentError) + end + + it "should not fail when a resource has duplicate aliases created" do + @catalog.add_resource @one + proc { @catalog.alias @one, "one" }.should_not raise_error + end + + it "should not create aliases that point back to the resource" do + @catalog.alias(@one, "one") + @catalog.resource(:notify, "one").should be_nil + end + + it "should be able to look resources up by their aliases" do + @catalog.add_resource @one + @catalog.alias @one, "two" + @catalog.resource(:notify, "two").should equal(@one) + end + + it "should remove resource aliases when the target resource is removed" do + @catalog.add_resource @one + @catalog.alias(@one, "other") + @one.expects :remove + @catalog.remove_resource(@one) + @catalog.resource("notify", "other").should be_nil + end + + it "should add an alias for the namevar when the title and name differ on isomorphic resource types" do + resource = Puppet::Type.type(:file).create :path => "/something", :title => "other", :content => "blah" + resource.expects(:isomorphic?).returns(true) + @catalog.add_resource(resource) + @catalog.resource(:file, "other").should equal(resource) + @catalog.resource(:file, "/something").ref.should == resource.ref + end + + it "should not add an alias for the namevar when the title and name differ on non-isomorphic resource types" do + resource = Puppet::Type.type(:file).create :path => "/something", :title => "other", :content => "blah" + resource.expects(:isomorphic?).returns(false) + @catalog.add_resource(resource) + @catalog.resource(:file, resource.title).should equal(resource) + # We can't use .should here, because the resources respond to that method. + if @catalog.resource(:file, resource.name) + raise "Aliased non-isomorphic resource" + end + end + + it "should provide a method to create additional resources that also registers the resource" do + args = {:name => "/yay", :ensure => :file} + resource = stub 'file', :ref => "File[/yay]", :catalog= => @catalog, :title => "/yay", :[] => "/yay" + Puppet::Type.type(:file).expects(:create).with(args).returns(resource) + @catalog.create_resource :file, args + @catalog.resource("File[/yay]").should equal(resource) + end +end + +describe Puppet::Resource::Catalog do + before :each do + @catalog = Puppet::Resource::Catalog.new("host") + + @catalog.retrieval_duration = Time.now + @transaction = mock 'transaction' + Puppet::Transaction.stubs(:new).returns(@transaction) + @transaction.stubs(:evaluate) + @transaction.stubs(:cleanup) + @transaction.stubs(:addtimes) + end + + it "should be an Expirer" do + Puppet::Resource::Catalog.ancestors.should be_include(Puppet::Util::Cacher::Expirer) + end + + it "should always be expired if it's not applying" do + @catalog.expects(:applying?).returns false + @catalog.should be_expired(Time.now) + end + + it "should not be expired if it's applying and the timestamp is late enough" do + @catalog.expire + @catalog.expects(:applying?).returns true + @catalog.should_not be_expired(Time.now) + end + + describe "when applying" do + it "should create and evaluate a transaction" do + @transaction.expects(:evaluate) + @catalog.apply + end + + it "should provide the catalog time to the transaction" do + @transaction.expects(:addtimes).with do |arg| + arg[:config_retrieval].should be_instance_of(Time) + true + end + @catalog.apply + end + + it "should clean up the transaction" do + @transaction.expects :cleanup + @catalog.apply + end + + it "should return the transaction" do + @catalog.apply.should equal(@transaction) + end + + it "should yield the transaction if a block is provided" do + @catalog.apply do |trans| + trans.should equal(@transaction) + end + end + + it "should default to not being a host catalog" do + @catalog.host_config.should be_nil + end + + it "should pass supplied tags on to the transaction" do + @transaction.expects(:tags=).with(%w{one two}) + @catalog.apply(:tags => %w{one two}) + end + + it "should set ignoreschedules on the transaction if specified in apply()" do + @transaction.expects(:ignoreschedules=).with(true) + @catalog.apply(:ignoreschedules => true) + end + + it "should remove resources created mid-transaction" do + args = {:name => "/yay", :ensure => :file} + resource = stub 'file', :ref => "File[/yay]", :catalog= => @catalog, :title => "/yay", :[] => "/yay" + @transaction = mock 'transaction' + Puppet::Transaction.stubs(:new).returns(@transaction) + @transaction.stubs(:evaluate) + @transaction.stubs(:cleanup) + @transaction.stubs(:addtimes) + Puppet::Type.type(:file).expects(:create).with(args).returns(resource) + resource.expects :remove + @catalog.apply do |trans| + @catalog.create_resource :file, args + @catalog.resource("File[/yay]").should equal(resource) + end + @catalog.resource("File[/yay]").should be_nil + end + + it "should remove resources added mid-transaction" do + @transaction = mock 'transaction' + Puppet::Transaction.stubs(:new).returns(@transaction) + @transaction.stubs(:evaluate) + @transaction.stubs(:cleanup) + @transaction.stubs(:addtimes) + file = Puppet::Type.type(:file).create(:name => "/yay", :ensure => :file) + @catalog.apply do |trans| + @catalog.add_resource file + @catalog.resource("File[/yay]").should_not be_nil + end + @catalog.resource("File[/yay]").should be_nil + end + + it "should expire cached data in the resources both before and after the transaction" do + @catalog.expects(:expire).times(2) + @catalog.apply + end + end + + describe "when applying host catalogs" do + + # super() doesn't work in the setup method for some reason + before do + @catalog.host_config = true + Puppet::Util::Storage.stubs(:store) + end + + it "should send a report if reporting is enabled" do + Puppet[:report] = true + @transaction.expects :send_report + @transaction.stubs :any_failed? => false + @catalog.apply + end + + it "should send a report if report summaries are enabled" do + Puppet[:summarize] = true + @transaction.expects :send_report + @transaction.stubs :any_failed? => false + @catalog.apply + end + + it "should initialize the state database before applying a catalog" do + Puppet::Util::Storage.expects(:load) + + # Short-circuit the apply, so we know we're loading before the transaction + Puppet::Transaction.expects(:new).raises ArgumentError + proc { @catalog.apply }.should raise_error(ArgumentError) + end + + it "should sync the state database after applying" do + Puppet::Util::Storage.expects(:store) + @transaction.stubs :any_failed? => false + @catalog.apply + end + + after { Puppet.settings.clear } + end + + describe Puppet::Resource::Catalog, " when applying non-host catalogs" do + + before do + @catalog.host_config = false + end + + it "should never send reports" do + Puppet[:report] = true + Puppet[:summarize] = true + @transaction.expects(:send_report).never + @catalog.apply + end + + it "should never modify the state database" do + Puppet::Util::Storage.expects(:load).never + Puppet::Util::Storage.expects(:store).never + @catalog.apply + end + + after { Puppet.settings.clear } + end +end + +describe Puppet::Resource::Catalog, " when creating a relationship graph" do + before do + Puppet::Type.type(:component) + @catalog = Puppet::Resource::Catalog.new("host") + @compone = Puppet::Type::Component.create :name => "one" + @comptwo = Puppet::Type::Component.create :name => "two", :require => ["class", "one"] + @file = Puppet::Type.type(:file) + @one = @file.create :path => "/one" + @two = @file.create :path => "/two" + @sub = @file.create :path => "/two/subdir" + @catalog.add_edge @compone, @one + @catalog.add_edge @comptwo, @two + + @three = @file.create :path => "/three" + @four = @file.create :path => "/four", :require => ["file", "/three"] + @five = @file.create :path => "/five" + @catalog.add_resource @compone, @comptwo, @one, @two, @three, @four, @five, @sub + @relationships = @catalog.relationship_graph + end + + it "should be able to create a relationship graph" do + @relationships.should be_instance_of(Puppet::SimpleGraph) + end + + it "should not have any components" do + @relationships.vertices.find { |r| r.instance_of?(Puppet::Type::Component) }.should be_nil + end + + it "should have all non-component resources from the catalog" do + # The failures print out too much info, so i just do a class comparison + @relationships.vertex?(@five).should be_true + end + + it "should have all resource relationships set as edges" do + @relationships.edge?(@three, @four).should be_true + end + + it "should copy component relationships to all contained resources" do + @relationships.edge?(@one, @two).should be_true + end + + it "should add automatic relationships to the relationship graph" do + @relationships.edge?(@two, @sub).should be_true + end + + it "should get removed when the catalog is cleaned up" do + @relationships.expects(:clear) + @catalog.clear + @catalog.instance_variable_get("@relationship_graph").should be_nil + end + + it "should write :relationships and :expanded_relationships graph files if the catalog is a host catalog" do + @catalog.clear + graph = Puppet::SimpleGraph.new + Puppet::SimpleGraph.expects(:new).returns graph + + graph.expects(:write_graph).with(:relationships) + graph.expects(:write_graph).with(:expanded_relationships) + + @catalog.host_config = true + + @catalog.relationship_graph + end + + it "should not write graph files if the catalog is not a host catalog" do + @catalog.clear + graph = Puppet::SimpleGraph.new + Puppet::SimpleGraph.expects(:new).returns graph + + graph.expects(:write_graph).never + + @catalog.host_config = false + + @catalog.relationship_graph + end + + it "should create a new relationship graph after clearing the old one" do + @relationships.expects(:clear) + @catalog.clear + @catalog.relationship_graph.should be_instance_of(Puppet::SimpleGraph) + end + + it "should remove removed resources from the relationship graph if it exists" do + @catalog.remove_resource(@one) + @catalog.relationship_graph.vertex?(@one).should be_false + end +end + +describe Puppet::Resource::Catalog, " when writing dot files" do + before do + @catalog = Puppet::Resource::Catalog.new("host") + @name = :test + @file = File.join(Puppet[:graphdir], @name.to_s + ".dot") + end + + it "should only write when it is a host catalog" do + File.expects(:open).with(@file).never + @catalog.host_config = false + Puppet[:graph] = true + @catalog.write_graph(@name) + end + + after do + Puppet.settings.clear + end +end + +describe Puppet::Resource::Catalog, " when indirecting" do + before do + @indirection = stub 'indirection', :name => :catalog + + Puppet::Util::Cacher.expire + end + + it "should redirect to the indirection for retrieval" do + Puppet::Resource::Catalog.stubs(:indirection).returns(@indirection) + @indirection.expects(:find) + Puppet::Resource::Catalog.find(:myconfig) + end + + it "should default to the 'compiler' terminus" do + Puppet::Resource::Catalog.indirection.terminus_class.should == :compiler + end + + after do + Puppet::Util::Cacher.expire + end +end + +describe Puppet::Resource::Catalog, " when converting to yaml" do + before do + @catalog = Puppet::Resource::Catalog.new("me") + @catalog.add_edge("one", "two") + end + + it "should be able to be dumped to yaml" do + YAML.dump(@catalog).should be_instance_of(String) + end +end + +describe Puppet::Resource::Catalog, " when converting from yaml" do + before do + @catalog = Puppet::Resource::Catalog.new("me") + @catalog.add_edge("one", "two") + + text = YAML.dump(@catalog) + @newcatalog = YAML.load(text) + end + + it "should get converted back to a catalog" do + @newcatalog.should be_instance_of(Puppet::Resource::Catalog) + end + + it "should have all vertices" do + @newcatalog.vertex?("one").should be_true + @newcatalog.vertex?("two").should be_true + end + + it "should have all edges" do + @newcatalog.edge?("one", "two").should be_true + end +end diff --git a/spec/unit/transaction.rb b/spec/unit/transaction.rb index 2c1998587..0758e6c1e 100755 --- a/spec/unit/transaction.rb +++ b/spec/unit/transaction.rb @@ -9,7 +9,7 @@ describe Puppet::Transaction do @generator_class = mkgenerator @generator = mkgenerator.create(:name => "foo") - @catalog = Puppet::Node::Catalog.new + @catalog = Puppet::Resource::Catalog.new @catalog.add_resource @generator @report = stub_everything 'report' diff --git a/spec/unit/type.rb b/spec/unit/type.rb index 89a8e5e9c..a279e1139 100755 --- a/spec/unit/type.rb +++ b/spec/unit/type.rb @@ -8,7 +8,7 @@ describe Puppet::Type do end it "should use its catalog as its expirer" do - catalog = Puppet::Node::Catalog.new + catalog = Puppet::Resource::Catalog.new resource = Puppet::Type.type(:mount).create(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) resource.catalog = catalog resource.expirer.should equal(catalog) @@ -67,7 +67,7 @@ describe Puppet::Type do describe "when in a catalog" do before do - @catalog = Puppet::Node::Catalog.new + @catalog = Puppet::Resource::Catalog.new @container = Puppet::Type.type(:component).create(:name => "container") @one = Puppet::Type.type(:file).create(:path => "/file/one") @two = Puppet::Type.type(:file).create(:path => "/file/two") diff --git a/spec/unit/type/file.rb b/spec/unit/type/file.rb index 0ab4f40bb..399181e6c 100755 --- a/spec/unit/type/file.rb +++ b/spec/unit/type/file.rb @@ -105,7 +105,7 @@ describe Puppet::Type.type(:file) do :path => @link, :mode => "755" ) - @catalog = Puppet::Node::Catalog.new + @catalog = Puppet::Resource::Catalog.new @catalog.add_resource @resource end @@ -186,7 +186,7 @@ describe Puppet::Type.type(:file) do stat = mock 'stat' File.expects(:lstat).returns stat - catalog = Puppet::Node::Catalog.new + catalog = Puppet::Resource::Catalog.new @resource.catalog = catalog catalog.stubs(:applying?).returns true diff --git a/spec/unit/type/mount.rb b/spec/unit/type/mount.rb index 7ddb7ea26..72028f312 100755 --- a/spec/unit/type/mount.rb +++ b/spec/unit/type/mount.rb @@ -196,7 +196,7 @@ describe Puppet::Type.type(:mount), "when modifying an existing mount entry" do @mount.provider.stubs(:mounted?).returns true - @catalog = Puppet::Node::Catalog.new + @catalog = Puppet::Resource::Catalog.new @catalog.add_resource @mount end diff --git a/spec/unit/type/package.rb b/spec/unit/type/package.rb index 13bf4d3d3..10d74d0b6 100755 --- a/spec/unit/type/package.rb +++ b/spec/unit/type/package.rb @@ -108,7 +108,7 @@ describe Puppet::Type.type(:package) do Puppet::Type.type(:package).defaultprovider.stubs(:instances).returns([]) @package = Puppet::Type.type(:package).create(:name => "yay") - @catalog = Puppet::Node::Catalog.new + @catalog = Puppet::Resource::Catalog.new @catalog.add_resource(@package) end diff --git a/spec/unit/type/ssh_authorized_key.rb b/spec/unit/type/ssh_authorized_key.rb index 3240b7a94..0e32ef77a 100755 --- a/spec/unit/type/ssh_authorized_key.rb +++ b/spec/unit/type/ssh_authorized_key.rb @@ -14,7 +14,7 @@ describe ssh_authorized_key do @provider = stub 'provider', :class => @provider_class, :file_path => "/tmp/whatever", :clear => nil @provider_class.stubs(:new).returns(@provider) - @catalog = Puppet::Node::Catalog.new + @catalog = Puppet::Resource::Catalog.new end it "should have a name parameter" do diff --git a/spec/unit/type/user.rb b/spec/unit/type/user.rb index 78bfa2e82..51b38186a 100755 --- a/spec/unit/type/user.rb +++ b/spec/unit/type/user.rb @@ -246,7 +246,7 @@ describe user do testrole = Puppet.type(:user).create(:name => "testrole") - config = Puppet::Node::Catalog.new :testing do |conf| + config = Puppet::Resource::Catalog.new :testing do |conf| [testuser, testrole].each { |resource| conf.add_resource resource } end diff --git a/spec/unit/type/zfs.rb b/spec/unit/type/zfs.rb index 08d6e0747..f36c715d2 100755 --- a/spec/unit/type/zfs.rb +++ b/spec/unit/type/zfs.rb @@ -34,7 +34,7 @@ describe zfs do foo_bar_baz_zfs = Puppet.type(:zfs).create(:name => "foo/bar/baz") foo_bar_baz_buz_zfs = Puppet.type(:zfs).create(:name => "foo/bar/baz/buz") - config = Puppet::Node::Catalog.new :testing do |conf| + config = Puppet::Resource::Catalog.new :testing do |conf| [foo_pool, foo_bar_zfs, foo_bar_baz_zfs, foo_bar_baz_buz_zfs].each { |resource| conf.add_resource resource } end diff --git a/test/language/snippets.rb b/test/language/snippets.rb index dfb914f72..a1d0f5d4f 100755 --- a/test/language/snippets.rb +++ b/test/language/snippets.rb @@ -498,7 +498,7 @@ class TestSnippets < Test::Unit::TestCase catalog = nil assert_nothing_raised("Could not compile catalog") { - catalog = Puppet::Node::Catalog.find(node) + catalog = Puppet::Resource::Catalog.find(node) } assert_nothing_raised("Could not convert catalog") { diff --git a/test/lib/puppettest/support/resources.rb b/test/lib/puppettest/support/resources.rb index 255c55569..045579302 100755 --- a/test/lib/puppettest/support/resources.rb +++ b/test/lib/puppettest/support/resources.rb @@ -25,7 +25,7 @@ module PuppetTest::Support::Resources end def mktree - catalog = Puppet::Node::Catalog.new do |config| + catalog = Puppet::Resource::Catalog.new do |config| one = treenode(config, "one", "a", "b") two = treenode(config, "two", "c", "d") middle = treenode(config, "middle", "e", "f", two) diff --git a/test/lib/puppettest/support/utils.rb b/test/lib/puppettest/support/utils.rb index 0d775d184..aa3ad8b8d 100644 --- a/test/lib/puppettest/support/utils.rb +++ b/test/lib/puppettest/support/utils.rb @@ -24,7 +24,7 @@ module PuppetTest::Support::Utils # Turn a list of resources, or possibly a catalog and some resources, # into a catalog object. def resources2catalog(*resources) - if resources[0].is_a?(Puppet::Node::Catalog) + if resources[0].is_a?(Puppet::Resource::Catalog) config = resources.shift unless resources.empty? resources.each { |r| config.add_resource r } @@ -34,7 +34,7 @@ module PuppetTest::Support::Utils comp = resources.shift comp.delve else - config = Puppet::Node::Catalog.new + config = Puppet::Resource::Catalog.new resources.each { |res| config.add_resource res } end return config @@ -159,7 +159,7 @@ module PuppetTest::Support::Utils else name = :testing end - config = Puppet::Node::Catalog.new :testing do |conf| + config = Puppet::Resource::Catalog.new :testing do |conf| resources.each { |resource| conf.add_resource resource } end diff --git a/test/network/handler/master.rb b/test/network/handler/master.rb index 8e55f104c..a802b0a0a 100755 --- a/test/network/handler/master.rb +++ b/test/network/handler/master.rb @@ -13,7 +13,7 @@ class TestMaster < Test::Unit::TestCase @master = Puppet::Network::Handler.master.new(:Manifest => tempfile) @catalog = stub 'catalog', :extract => "" - Puppet::Node::Catalog.stubs(:find).returns(@catalog) + Puppet::Resource::Catalog.stubs(:find).returns(@catalog) end def teardown @@ -53,7 +53,7 @@ class TestMaster < Test::Unit::TestCase @master.stubs(:decode_facts) - Puppet::Node::Catalog.expects(:find).with("foo.com").returns(@catalog) + Puppet::Resource::Catalog.expects(:find).with("foo.com").returns(@catalog) @master.getconfig("facts", "yaml", "foo.com") end @@ -68,7 +68,7 @@ class TestMasterFormats < Test::Unit::TestCase @master.stubs(:decode_facts) @catalog = stub 'catalog', :extract => "" - Puppet::Node::Catalog.stubs(:find).returns(@catalog) + Puppet::Resource::Catalog.stubs(:find).returns(@catalog) end def test_marshal_can_be_used diff --git a/test/other/transactions.rb b/test/other/transactions.rb index 04480c24c..931a4d4d1 100755 --- a/test/other/transactions.rb +++ b/test/other/transactions.rb @@ -346,7 +346,7 @@ class TestTransactions < Test::Unit::TestCase file[:group] = @groups[0] assert_apply(file) - config = Puppet::Node::Catalog.new + config = Puppet::Resource::Catalog.new fcomp = Puppet::Type.type(:component).create(:name => "file") config.add_resource fcomp config.add_resource file @@ -554,7 +554,7 @@ class TestTransactions < Test::Unit::TestCase end def test_ignore_tags? - config = Puppet::Node::Catalog.new + config = Puppet::Resource::Catalog.new config.host_config = true transaction = Puppet::Transaction.new(config) assert(! transaction.ignore_tags?, "Ignoring tags when applying a host catalog") @@ -566,7 +566,7 @@ class TestTransactions < Test::Unit::TestCase def test_missing_tags? resource = stub 'resource', :tagged? => true - config = Puppet::Node::Catalog.new + config = Puppet::Resource::Catalog.new # Mark it as a host config so we don't care which test is first config.host_config = true @@ -659,7 +659,7 @@ class TestTransactions < Test::Unit::TestCase end # Make a graph with some stuff in it. - graph = Puppet::Node::Catalog.new + graph = Puppet::Resource::Catalog.new # Add a non-triggering edge. a = trigger.new(:a) -- cgit