diff --git a/CHANGELOG b/CHANGELOG index af206cdef..83b6710e2 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,9 @@ -0.23.2 (misspiggy) + The --use-nodes and --no-nodes options are now obsolete. + Puppet automatically detects when nodes are defined, and if + they are defined it will require that a node be found, + else it will not look for a node nor will it fail if it + fails to find one. + Fixed #832. Added the '--no-daemonize' option to puppetd and puppetmasterd. NOTE: The default behavior of 'verbose' and 'debug' no longer cause puppetd and puppetmasterd to not @@ -10,6 +15,7 @@ Added shortname support to config.rb and refactored addargs +0.23.2 Fixed the problem in cron jobs where environment settings tended to multiple. (#749) diff --git a/bin/puppet b/bin/puppet index a87a07619..e4e3dd309 100755 --- a/bin/puppet +++ b/bin/puppet @@ -80,14 +80,16 @@ Puppet.settings.addargs(options) result = GetoptLong.new(*options) -debug = false -verbose = false -noop = false -logfile = false -loadclasses = false -logset = false +options = { + :debug => false, + :verbose => false, + :noop => false, + :logfile => false, + :loadclasses => false, + :logset => false, + :code => nil +} -code = nil master = { :Local => true @@ -107,19 +109,19 @@ begin exit end when "--use-nodes" - master[:UseNodes] = true + options[:UseNodes] = true when "--verbose" - verbose = true + options[:verbose] = true when "--debug" - debug = true + options[:debug] = true when "--execute" - code = arg + options[:code] = arg when "--loadclasses" - loadclasses = true + options[:loadclasses] = true when "--logdest" begin Puppet::Util::Log.newdestination(arg) - logset = true + options[:logset] = true rescue => detail $stderr.puts detail.to_s end @@ -139,7 +141,7 @@ if Puppet[:noop] Puppet[:show_diff] = true end -unless logset +unless options[:logset] Puppet::Util::Log.newdestination(:console) end @@ -148,9 +150,9 @@ server = nil Puppet.settraps -if debug +if options[:debug] Puppet::Util::Log.level = :debug -elsif verbose +elsif options[:verbose] Puppet::Util::Log.level = :info end @@ -162,18 +164,26 @@ end Puppet.genconfig Puppet.genmanifest -if code - master[:Code] = code +# Set our code or file to use. +if options[:code] or ARGV.length == 0 + Puppet[:code] = options[:code] || STDIN.read else - if ARGV.length > 0 - master[:Manifest] = ARGV.shift - else - master[:Code] = STDIN.read - end + Puppet[:manifest] = ARGV.shift end +# Collect our facts. +Puppet::Node::Facts.terminus_class = :code +facts = Puppet::Node::Facts.find("me") +facts.name = facts.values["hostname"] + +# Create our Node +node = Puppet::Node.new(facts.name) + +# Merge in the facts. +node.merge(facts.values) + # Allow users to load the classes that puppetd creates. -if loadclasses +if options[:loadclasses] file = Puppet[:classfile] if FileTest.exists?(file) unless FileTest.readable?(file) @@ -181,20 +191,21 @@ if loadclasses exit(63) end - master[:Classes] = File.read(file).split(/[\s\n]+/) + node.classes = File.read(file).split(/[\s\n]+/) end end +# Compile and apply the configuration +Puppet::Node::Configuration.terminus_class = :code + begin - server = Puppet::Network::Handler.master.new(master) - client = Puppet::Network::Client.master.new( - :Master => server, - :Cache => false - ) - if Puppet[:parseonly] - exit(0) - end - config = client.getconfig + # Compile our configuration + config = Puppet::Node::Configuration.find(node) + + # Translate it to a RAL configuration + config = config.to_ral + + # And apply it config.apply rescue => detail if Puppet[:trace] diff --git a/bin/puppetmasterd b/bin/puppetmasterd index 39802f229..b782969a8 100755 --- a/bin/puppetmasterd +++ b/bin/puppetmasterd @@ -186,6 +186,9 @@ end Puppet.genconfig Puppet.genmanifest +# A temporary solution, to at least make the master work for now. +Puppet::Node::Facts.terminus_class = :yaml + require 'etc' handlers = { diff --git a/ext/module_puppet b/ext/module_puppet index 52f65b094..36efb4f0e 100755 --- a/ext/module_puppet +++ b/ext/module_puppet @@ -150,14 +150,23 @@ unless setdest Puppet::Util::Log.newdestination(:syslog) end -master[:Manifest] = ARGV.shift +Puppet[:manifest] = ARGV.shift unless ENV.include?("CFALLCLASSES") $stderr.puts "Cfengine classes must be passed to the module" exit(15) end -master[:UseNodes] = false +# Collect our facts. +Puppet::Node::Facts.terminus_class = :code +facts = Puppet::Node::Facts.find("me") +facts.name = facts.values["hostname"] + +# Create our Node +node = Puppet::Node.new(facts.name) + +# Merge in the facts. +node.merge(facts.values) classes = ENV["CFALLCLASSES"].split(":") @@ -166,32 +175,32 @@ if classes.empty? exit(16) end -master[:Classes] = classes +node.classes = classes begin - server = Puppet::Network::Handler.master.new(master) + # Compile our configuration + config = Puppet::Node::Configuration.find(node) rescue => detail - $stderr.puts detail + if Puppet[:trace] + puts detail.backtrace + end + if detail.is_a?(XMLRPC::FaultException) + $stderr.puts detail.message + else + $stderr.puts detail + end exit(1) end -begin - client = Puppet::Network::Client.master.new( - :Master => server, - :Cache => false - ) -rescue => detail - $stderr.puts detail - exit(1) -end - - if parseonly exit(0) end begin - config = client.getconfig + # Translate it to a RAL configuration + config = config.to_ral + + # And apply it config.apply rescue => detail Puppet.err detail diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index 6ea4eef4c..ce1411b62 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -292,6 +292,9 @@ module Puppet "Where puppetmasterd looks for its manifests."], :manifest => ["$manifestdir/site.pp", "The entry-point manifest for puppetmasterd."], + :code => ["", "Code to parse directly. This is essentially only used + by ``puppet`, and should only be set if you're writing your own Puppet + executable"], :masterlog => { :default => "$logdir/puppetmaster.log", :owner => "$user", :group => "$group", diff --git a/lib/puppet/dsl.rb b/lib/puppet/dsl.rb index 75bc81b2b..97d06a4ec 100644 --- a/lib/puppet/dsl.rb +++ b/lib/puppet/dsl.rb @@ -251,12 +251,17 @@ module Puppet def scope unless defined?(@scope) - @interp = Puppet::Parser::Interpreter.new :Code => "" + # Set the code to something innocuous; we just need the + # scopes, not the interpreter. Hackish, but true. + Puppet[:code] = " " + @interp = Puppet::Parser::Interpreter.new require 'puppet/node' @node = Puppet::Node.new(Facter.value(:hostname)) + if env = Puppet[:environment] and env == "" + env = nil + end @node.parameters = Facter.to_hash - @interp = Puppet::Parser::Interpreter.new :Code => "" - @compile = Puppet::Parser::Compile.new(@node, @interp.send(:parser, Puppet[:environment])) + @compile = Puppet::Parser::Compile.new(@node, @interp.send(:parser, env)) @scope = @compile.topscope end @scope diff --git a/lib/puppet/indirector.rb b/lib/puppet/indirector.rb index 6ff2de1b4..a2eb41763 100644 --- a/lib/puppet/indirector.rb +++ b/lib/puppet/indirector.rb @@ -15,7 +15,7 @@ module Puppet::Indirector # default, not the value -- if it's the value, then it gets # evaluated at parse time, which is before the user has had a chance # to override it. - def indirects(indirection) + def indirects(indirection, options = {}) raise(ArgumentError, "Already handling indirection for %s; cannot also handle %s" % [@indirection.name, indirection]) if defined?(@indirection) and @indirection # populate this class with the various new methods extend ClassMethods @@ -24,10 +24,29 @@ module Puppet::Indirector # instantiate the actual Terminus for that type and this name (:ldap, w/ args :node) # & hook the instantiated Terminus into this class (Node: @indirection = terminus) @indirection = Puppet::Indirector::Indirection.new(self, indirection) + + unless options.empty? + options.each do |param, value| + case param + when :terminus_class: @indirection.terminus_class = value + else + raise ArgumenError, "Invalid option '%s' to 'indirects'" % param + end + end + end + @indirection end module ClassMethods attr_reader :indirection + + def cache_class=(klass) + indirection.cache_class = klass + end + + def terminus_class=(klass) + indirection.terminus_class = klass + end def find(*args) indirection.find(*args) @@ -43,6 +62,10 @@ module Puppet::Indirector end module InstanceMethods + # Make it easy for the model to set versions, + # which are used for caching and such. + attr_accessor :version + # these become instance methods def save(*args) self.class.indirection.save(self, *args) diff --git a/lib/puppet/indirector/code/configuration.rb b/lib/puppet/indirector/code/configuration.rb index 6d0317204..949926a3c 100644 --- a/lib/puppet/indirector/code/configuration.rb +++ b/lib/puppet/indirector/code/configuration.rb @@ -15,24 +15,19 @@ class Puppet::Indirector::Code::Configuration < Puppet::Indirector::Code # Compile a node's configuration. def find(key, client = nil, clientip = nil) - # If we want to use the cert name as our key - if Puppet[:node_name] == 'cert' and client - key = client + if key.is_a?(Puppet::Node) + node = key + else + node = find_node(key) end - # Note that this is reasonable, because either their node source should actually - # know about the node, or they should be using the ``none`` node source, which - # will always return data. - unless node = Puppet::Node.search(key) - raise Puppet::Error, "Could not find node '%s'" % key + if configuration = compile(node) + return configuration.to_transportable + else + # This shouldn't actually happen; we should either return + # a config or raise an exception. + return nil end - - # Add any external data to the node. - add_node_data(node) - - configuration = compile(node) - - return configuration end def initialize @@ -47,6 +42,11 @@ class Puppet::Indirector::Code::Configuration < Puppet::Indirector::Code @interpreter end + # Is our compiler part of a network, or are we just local? + def networked? + $0 =~ /puppetmasterd/ + end + # Return the configuration version. def version(client = nil, clientip = nil) if client and node = Puppet::Node.search(client) @@ -76,22 +76,14 @@ class Puppet::Indirector::Code::Configuration < Puppet::Indirector::Code end config = nil - # LAK:FIXME This should log at :none when our client is - # local, since we don't want 'puppet' (vs. puppetmasterd) to - # log compile times. - benchmark(:notice, "Compiled configuration for %s" % node.name) do + loglevel = networked? ? :notice : :none + + benchmark(loglevel, "Compiled configuration for %s" % node.name) do begin config = interpreter.compile(node) rescue Puppet::Error => detail - if Puppet[:trace] - puts detail.backtrace - end - unless local? - Puppet.err detail.to_s - end - raise XMLRPC::FaultException.new( - 1, detail.to_s - ) + Puppet.err(detail.to_s) if networked? + raise end end @@ -100,21 +92,28 @@ class Puppet::Indirector::Code::Configuration < Puppet::Indirector::Code # Create our interpreter object. def create_interpreter - args = {} + return Puppet::Parser::Interpreter.new + end - # Allow specification of a code snippet or of a file - if self.code - args[:Code] = self.code - end - - # LAK:FIXME This needs to be handled somehow. - #if options.include?(:UseNodes) - # args[:UseNodes] = options[:UseNodes] - #elsif @local - # args[:UseNodes] = false + # Turn our host name into a node object. + def find_node(key) + # If we want to use the cert name as our key + # LAK:FIXME This needs to be figured out somehow, but it requires the routing. + #if Puppet[:node_name] == 'cert' and client + # key = client #end - return Puppet::Parser::Interpreter.new(args) + # Note that this is reasonable, because either their node source should actually + # know about the node, or they should be using the ``none`` node source, which + # will always return data. + unless node = Puppet::Node.search(key) + raise Puppet::Error, "Could not find node '%s'" % key + end + + # Add any external data to the node. + add_node_data(node) + + node end # Initialize our server fact hash; we add these to each client, and they @@ -150,7 +149,7 @@ class Puppet::Indirector::Code::Configuration < Puppet::Indirector::Code # LAK:FIXME This method should probably be part of the protocol, but it # shouldn't be here. def translate(config) - if local? + unless networked? config else CGI.escape(config.to_yaml(:UseBlock => true)) diff --git a/lib/puppet/indirector/indirection.rb b/lib/puppet/indirector/indirection.rb index 3a6284877..5f7baa3da 100644 --- a/lib/puppet/indirector/indirection.rb +++ b/lib/puppet/indirector/indirection.rb @@ -90,12 +90,21 @@ class Puppet::Indirector::Indirection end end - def find(*args) - terminus.find(*args) + def find(key, *args) + if cache? and cache.has_most_recent?(key, terminus.version(key)) + return cache.find(key, *args) + end + if result = terminus.find(key, *args) + result.version ||= Time.now.utc + if cache? + Puppet.info "Caching %s %s" % [self.name, key] + cache.save(result, *args) + end + return result + end end def destroy(*args) - cache.destroy(*args) if cache? terminus.destroy(*args) end @@ -104,9 +113,12 @@ class Puppet::Indirector::Indirection end # these become instance methods - def save(*args) - cache.save(*args) if cache? - terminus.save(*args) + def save(instance, *args) + instance.version ||= Time.now.utc + dest = cache? ? cache : terminus + return if dest.has_most_recent?(instance.name, instance.version) + cache.save(instance, *args) if cache? + terminus.save(instance, *args) end private diff --git a/lib/puppet/indirector/terminus.rb b/lib/puppet/indirector/terminus.rb index 3e0ea447c..37cfdc499 100644 --- a/lib/puppet/indirector/terminus.rb +++ b/lib/puppet/indirector/terminus.rb @@ -95,25 +95,52 @@ class Puppet::Indirector::Terminus end end + # Do we have an update for this object? This compares the provided version + # to our version, and returns true if our version is at least as high + # as the asked-about version. + def has_most_recent?(key, vers) + raise Puppet::DevError.new("Cannot check update status when no 'version' method is defined") unless respond_to?(:version) + + if existing_version = version(key) + #puts "%s fresh: %s (%s vs %s)" % [self.name, (existing_version.to_f >= vers.to_f).inspect, existing_version.to_f, vers.to_f] + existing_version.to_f >= vers.to_f + else + false + end + end + + def indirection + self.class.indirection + end + def initialize if self.class.abstract_terminus? raise Puppet::DevError, "Cannot create instances of abstract terminus types" end end - def terminus_type - self.class.terminus_type + def model + self.class.model end def name self.class.name end - def model - self.class.model + def terminus_type + self.class.terminus_type end - def indirection - self.class.indirection + # Provide a default method for retrieving an instance's version. + # By default, just find the resource and get its version. Individual + # terminus types can override this method to provide custom definitions of + # 'versions'. + def version(name) + raise Puppet::DevError.new("Cannot retrieve an instance's version without a :find method") unless respond_to?(:find) + if instance = find(name) + instance.version + else + nil + end end end diff --git a/lib/puppet/indirector/yaml.rb b/lib/puppet/indirector/yaml.rb index b9ea54f39..e11c88474 100644 --- a/lib/puppet/indirector/yaml.rb +++ b/lib/puppet/indirector/yaml.rb @@ -14,9 +14,9 @@ class Puppet::Indirector::Yaml < Puppet::Indirector::Terminus return nil unless FileTest.exist?(file) begin - return YAML.load(File.read(file)) + return from_yaml(File.read(file)) rescue => detail - raise Puppet::Error, "Could not read YAML data for %s(%s): %s" % [indirection.name, name, detail] + raise Puppet::Error, "Could not read YAML data for %s %s: %s" % [indirection.name, name, detail] end end @@ -33,11 +33,23 @@ class Puppet::Indirector::Yaml < Puppet::Indirector::Terminus Dir.mkdir(basedir) end - File.open(file, "w", 0660) { |f| f.print YAML.dump(object) } + begin + File.open(file, "w", 0660) { |f| f.print to_yaml(object) } + rescue TypeError => detail + Puppet.err "Could not save %s %s: %s" % [self.name, object.name, detail] + end end private + def from_yaml(text) + YAML.load(text) + end + + def to_yaml(object) + YAML.dump(object) + end + # Return the path to a given node's file. def path(name) File.join(Puppet[:yamldir], self.name.to_s, name.to_s + ".yaml") diff --git a/lib/puppet/indirector/yaml/configuration.rb b/lib/puppet/indirector/yaml/configuration.rb index 691f0e343..8e40ff9bf 100644 --- a/lib/puppet/indirector/yaml/configuration.rb +++ b/lib/puppet/indirector/yaml/configuration.rb @@ -2,4 +2,22 @@ require 'puppet/indirector/yaml' class Puppet::Indirector::Yaml::Configuration < Puppet::Indirector::Yaml desc "Store configurations as flat files, serialized using YAML." + + private + + # Override these, because yaml doesn't want to convert our self-referential + # objects. This is hackish, but eh. + def from_yaml(text) + if config = YAML.load(text) + # We can't yaml-dump classes. + #config.edgelist_class = Puppet::Relationship + return config + end + end + + def to_yaml(config) + # We can't yaml-dump classes. + #config.edgelist_class = nil + YAML.dump(config) + end end diff --git a/lib/puppet/network/client/master.rb b/lib/puppet/network/client/master.rb index f950a6059..5408cabe4 100644 --- a/lib/puppet/network/client/master.rb +++ b/lib/puppet/network/client/master.rb @@ -266,6 +266,7 @@ class Puppet::Network::Client::Master < Puppet::Network::Client self.getconfig end rescue => detail + puts detail.backtrace if Puppet[:trace] Puppet.err "Could not retrieve configuration: %s" % detail end @@ -544,6 +545,7 @@ class Puppet::Network::Client::Master < Puppet::Network::Client end rescue => detail + puts detail.backtrace Puppet.err "Could not retrieve configuration: %s" % detail unless Puppet[:usecacheonfailure] diff --git a/lib/puppet/network/handler/configuration.rb b/lib/puppet/network/handler/configuration.rb index 353693bdc..680304e2a 100644 --- a/lib/puppet/network/handler/configuration.rb +++ b/lib/puppet/network/handler/configuration.rb @@ -13,7 +13,7 @@ class Puppet::Network::Handler include Puppet::Util - attr_accessor :local + attr_accessor :local, :classes @interface = XMLRPC::Service::Interface.new("configuration") { |iface| iface.add_method("string configuration(string)") @@ -43,17 +43,15 @@ class Puppet::Network::Handler end def initialize(options = {}) - if options[:Local] - @local = options[:Local] - else - @local = false + options.each do |param, value| + case param + when :Classes: @classes = value + when :Local: self.local = value + else + raise ArgumentError, "Configuration handler does not accept %s" % param + end end - # Just store the options, rather than creating the interpreter - # immediately. Mostly, this is so we can create the interpreter - # on-demand, which is easier for testing. - @options = options - set_server_facts end @@ -82,8 +80,8 @@ class Puppet::Network::Handler node.merge(@server_facts) # Add any specified classes to the node's class list. - if classes = @options[:Classes] - classes.each do |klass| + if @classes + @classes.each do |klass| node.classes << klass end end @@ -121,37 +119,14 @@ class Puppet::Network::Handler end # Create our interpreter object. - def create_interpreter(options) - args = {} - - # Allow specification of a code snippet or of a file - if code = options[:Code] - args[:Code] = code - elsif options[:Manifest] - args[:Manifest] = options[:Manifest] - end - - args[:Local] = local? - - if options.include?(:UseNodes) - args[:UseNodes] = options[:UseNodes] - elsif @local - args[:UseNodes] = false - end - - # This is only used by the cfengine module, or if --loadclasses was - # specified in +puppet+. - if options.include?(:Classes) - args[:Classes] = options[:Classes] - end - - return Puppet::Parser::Interpreter.new(args) + def create_interpreter + return Puppet::Parser::Interpreter.new end # Create/return our interpreter. def interpreter unless defined?(@interpreter) and @interpreter - @interpreter = create_interpreter(@options) + @interpreter = create_interpreter end @interpreter end diff --git a/lib/puppet/network/handler/master.rb b/lib/puppet/network/handler/master.rb index 25c4318b8..1c7f7310e 100644 --- a/lib/puppet/network/handler/master.rb +++ b/lib/puppet/network/handler/master.rb @@ -30,13 +30,6 @@ class Puppet::Network::Handler def initialize(hash = {}) args = {} - # Allow specification of a code snippet or of a file - if code = hash[:Code] - args[:Code] = code - elsif man = hash[:Manifest] - args[:Manifest] = man - end - if hash[:Local] @local = hash[:Local] else @@ -53,12 +46,6 @@ class Puppet::Network::Handler Puppet.debug("Creating interpreter") - if hash.include?(:UseNodes) - args[:UseNodes] = hash[:UseNodes] - elsif @local - args[:UseNodes] = false - end - # This is only used by the cfengine module, or if --loadclasses was # specified in +puppet+. if hash.include?(:Classes) @@ -74,7 +61,7 @@ class Puppet::Network::Handler client, clientip = clientname(client, clientip, facts) # Pass the facts to the fact handler - Puppet::Node::Facts.new(client, facts).save + Puppet::Node::Facts.new(client, facts).save unless local? # And get the configuration from the config handler config = config_handler.configuration(client) diff --git a/lib/puppet/node.rb b/lib/puppet/node.rb index d71bd507e..9758c895c 100644 --- a/lib/puppet/node.rb +++ b/lib/puppet/node.rb @@ -9,7 +9,7 @@ class Puppet::Node extend Puppet::Indirector # Use the node source as the indirection terminus. - indirects :node + indirects :node, :terminus_class => :null # Add the node-searching methods. This is what people will actually # interact with that will find the node with the list of names or diff --git a/lib/puppet/node/configuration.rb b/lib/puppet/node/configuration.rb index 3571eecda..da8dc3a9a 100644 --- a/lib/puppet/node/configuration.rb +++ b/lib/puppet/node/configuration.rb @@ -7,7 +7,7 @@ require 'puppet/external/gratr/digraph' # and the relationships between them. class Puppet::Node::Configuration < Puppet::PGraph extend Puppet::Indirector - indirects :configuration + indirects :configuration, :terminus_class => :code # The host name this is a configuration for. attr_accessor :name @@ -22,6 +22,10 @@ class Puppet::Node::Configuration < Puppet::PGraph # How we should extract the configuration for sending to the client. attr_reader :extraction_format + # We need the ability to set this externally, so we can yaml-dump the + # configuration. + attr_accessor :edgelist_class + # Whether this is a host configuration, 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 @@ -355,6 +359,16 @@ class Puppet::Node::Configuration < Puppet::PGraph @tags.dup end + # Convert our configuration into a RAL configuration. + def to_ral + to_configuration :to_type + end + + # Turn our parser configuration into a transportable configuration. + def to_transportable + to_configuration :to_transobject + end + # Produce the graph files if requested. def write_graph(name) # We only want to graph the main host configuration. @@ -370,6 +384,14 @@ class Puppet::Node::Configuration < Puppet::PGraph } end + # LAK:NOTE We cannot yaml-dump the class in the edgelist_class, because classes cannot be + # dumped by default, nor does yaml-dumping # the edge-labels work at this point (I don't + # know why). + # Neither of these matters right now, but I suppose it could at some point. + def to_yaml_properties + instance_variables.reject { |v| %w{@edgelist_class @edge_labels}.include?(v) } + end + private def cleanup @@ -379,4 +401,39 @@ class Puppet::Node::Configuration < Puppet::PGraph @relationship_graph = nil end end + + # An abstracted method for converting one configuration into another type of configuration. + # This pretty much just converts all of the resources from one class to another, using + # a conversion method. + def to_configuration(convert) + result = self.class.new(self.name) + + vertices.each do |resource| + next if resource.respond_to?(:virtual?) and resource.virtual? + + result.add_resource resource.send(convert) + 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 = result.resource(edge.source.ref) + raise Puppet::DevError, "Could not find vertex for %s when converting %s" % [edge.source.ref, message] + end + + unless target = result.resource(edge.target.ref) + raise Puppet::DevError, "Could not find vertex for %s when converting %s" % [edge.target.ref, message] + end + + result.add_edge!(source, target, edge.label) + end + + result.add_class *self.classes + result.tag(*self.tags) + + return result + end end diff --git a/lib/puppet/node/facts.rb b/lib/puppet/node/facts.rb index a2e6d9c04..2da6c488e 100755 --- a/lib/puppet/node/facts.rb +++ b/lib/puppet/node/facts.rb @@ -9,7 +9,7 @@ class Puppet::Node::Facts extend Puppet::Indirector # Use the node source as the indirection terminus. - indirects :facts + indirects :facts, :terminus_class => :code attr_accessor :name, :values diff --git a/lib/puppet/parser/compile.rb b/lib/puppet/parser/compile.rb index 6aeebeaae..992b165e5 100644 --- a/lib/puppet/parser/compile.rb +++ b/lib/puppet/parser/compile.rb @@ -16,8 +16,6 @@ class Puppet::Parser::Compile include Puppet::Util::Errors attr_reader :topscope, :parser, :node, :facts, :collections, :configuration - attr_writer :ast_nodes - # Add a collection to the global list. def add_collection(coll) @collections << coll @@ -25,7 +23,7 @@ class Puppet::Parser::Compile # Do we use nodes found in the code, vs. the external node sources? def ast_nodes? - defined?(@ast_nodes) and @ast_nodes + parser.nodes.length > 0 end # Store the fact that we've evaluated a class, and store a reference to diff --git a/lib/puppet/parser/interpreter.rb b/lib/puppet/parser/interpreter.rb index 0b8c035ba..87513cb18 100644 --- a/lib/puppet/parser/interpreter.rb +++ b/lib/puppet/parser/interpreter.rb @@ -14,7 +14,6 @@ class Puppet::Parser::Interpreter include Puppet::Util attr_accessor :usenodes - attr_accessor :code, :file include Puppet::Util::Errors @@ -26,21 +25,11 @@ class Puppet::Parser::Interpreter # evaluate our whole tree def compile(node) raise Puppet::ParseError, "Could not parse configuration; cannot compile" unless env_parser = parser(node.environment) - return Puppet::Parser::Compile.new(node, env_parser, :ast_nodes => usenodes?).compile + return Puppet::Parser::Compile.new(node, env_parser).compile end # create our interpreter - def initialize(options = {}) - if @code = options[:Code] - elsif @file = options[:Manifest] - end - - if options.include?(:UseNodes) - @usenodes = options[:UseNodes] - else - @usenodes = true - end - + def initialize # The class won't always be defined during testing. if Puppet[:storeconfigs] if Puppet.features.rails? @@ -53,21 +42,14 @@ class Puppet::Parser::Interpreter @parsers = {} end - # Should we parse ast nodes? - def usenodes? - defined?(@usenodes) and @usenodes - end - private # Create a new parser object and pre-parse the configuration. def create_parser(environment) begin parser = Puppet::Parser::Parser.new(:environment => environment) - if self.code - parser.string = self.code - elsif self.file - parser.file = self.file + if code = Puppet.settings.value(:code, environment) and code != "" + parser.string = code else file = Puppet.settings.value(:manifest, environment) parser.file = file diff --git a/lib/puppet/parser/parser_support.rb b/lib/puppet/parser/parser_support.rb index 8cf0dcfe8..acf3c9f7c 100644 --- a/lib/puppet/parser/parser_support.rb +++ b/lib/puppet/parser/parser_support.rb @@ -366,10 +366,12 @@ class Puppet::Parser::Parser end def on_error(token,value,stack) - #on '%s' at '%s' in\n'%s'" % [token,value,stack] - #error = "line %s: parse error after '%s'" % - # [@lexer.line,@lexer.last] - error = "Syntax error at '%s'" % [value] + if token == 0 # denotes end of file + value = 'end of file' + else + value = "'%s'" % value + end + error = "Syntax error at %s" % [value] if brace = @lexer.expected error += "; expected '%s'" % brace diff --git a/lib/puppet/pgraph.rb b/lib/puppet/pgraph.rb index 2399d1651..ca45aa2b3 100644 --- a/lib/puppet/pgraph.rb +++ b/lib/puppet/pgraph.rb @@ -190,6 +190,10 @@ class Puppet::PGraph < GRATR::Digraph return result end + def to_yaml_properties + instance_variables + end + # A different way of walking a tree, and a much faster way than the # one that comes with GRATR. def tree_from_vertex2(start, direction = :out) diff --git a/lib/puppet/provider/package/portage.rb b/lib/puppet/provider/package/portage.rb index 866ec8730..6a42444ef 100644 --- a/lib/puppet/provider/package/portage.rb +++ b/lib/puppet/provider/package/portage.rb @@ -55,7 +55,7 @@ Puppet::Type.type(:package).provide :portage, :parent => Puppet::Provider::Packa # The common package name format. def package_name - "%s/%s" % [@resource[:category], @resource[:name]] + @resource[:category] ? "%s/%s" % [@resource[:category], @resource[:name]] : @resource[:name] end def uninstall @@ -71,7 +71,7 @@ Puppet::Type.type(:package).provide :portage, :parent => Puppet::Provider::Packa result_fields = [:category, :name, :ensure, :version_available, :slot, :vendor, :description] search_field = @resource[:category] ? "--category-name" : "--name" - search_value = @resource[:category] ? package_name : @resource[:name] + search_value = package_name search_format = " [] [] " begin diff --git a/lib/puppet/provider/service/debian.rb b/lib/puppet/provider/service/debian.rb index d810eac1b..0ba7e1a79 100755 --- a/lib/puppet/provider/service/debian.rb +++ b/lib/puppet/provider/service/debian.rb @@ -17,7 +17,7 @@ Puppet::Type.type(:service).provide :debian, :parent => :init do # If it's enabled, then it will print output showing removal of # links. - if output =~ /etc\/rc[\dS].d|Nothing to do\./ + if output =~ /etc\/rc[\dS].d|not installed/ return :true else return :false diff --git a/lib/puppet/transportable.rb b/lib/puppet/transportable.rb index ecd179ed7..2a600b50e 100644 --- a/lib/puppet/transportable.rb +++ b/lib/puppet/transportable.rb @@ -8,7 +8,7 @@ module Puppet # YAML. class TransObject include Enumerable - attr_accessor :type, :name, :file, :line, :collectable, :collected + attr_accessor :type, :name, :file, :line, :configuration attr_writer :tags @@ -25,7 +25,6 @@ module Puppet def initialize(name,type) @type = type @name = name - @collectable = false @params = {} @tags = [] end @@ -34,10 +33,44 @@ module Puppet return [@type,@name].join('--') end + def ref + unless defined? @ref + if @type == :component + @ref = @name + else + @ref = "%s[%s]" % [type_capitalized, name] + end + end + @ref + end + def tags return @tags end + # Convert a defined type into a component. + def to_component + tmpname = nil + + # Nodes have the same name and type + if self.name + tmpname = "%s[%s]" % [type_capitalized, self.name] + else + tmpname = @type + end + trans = TransObject.new(tmpname, :component) + if defined? @parameters + @parameters.each { |param,value| + Puppet.debug "Defining %s on %s of type %s" % + [param,@name,@type] + trans[param] = value + } + else + #Puppet.debug "%s[%s] has no parameters" % [@type, @name] + end + Puppet::Type::Component.create(trans) + end + def to_hash @params.dup end @@ -57,18 +90,18 @@ module Puppet end def to_yaml_properties - instance_variables + instance_variables.reject { |v| %w{@ref}.include?(v) } end def to_ref - unless defined? @ref + unless defined? @res_ref if self.type and self.name - @ref = "%s[%s]" % [self.type, self.name] + @res_ref = "%s[%s]" % [type_capitalized, self.name] else - @ref = nil + @res_ref = nil end end - @ref + @res_ref end def to_type @@ -85,11 +118,16 @@ module Puppet end end else - raise Puppet::Error.new("Could not find object type %s" % self.type) + return to_component end return retobj end + + # Return the type fully capitalized correctly. + def type_capitalized + type.split("::").collect { |s| s.capitalize }.join("::") + end end # Just a linear container for objects. Behaves mostly like an array, except @@ -107,20 +145,6 @@ module Puppet end } - # Remove all collectable objects from our tree, since the client - # should not see them. - def collectstrip! - @children.dup.each do |child| - if child.is_a? self.class - child.collectstrip! - else - if child.collectable and ! child.collected - @children.delete(child) - end - end - end - end - # Recursively yield everything. def delve(&block) @children.each do |obj| diff --git a/lib/puppet/type/component.rb b/lib/puppet/type/component.rb index c5842e0e7..4dc542a65 100644 --- a/lib/puppet/type/component.rb +++ b/lib/puppet/type/component.rb @@ -109,14 +109,6 @@ Puppet::Type.newtype(:component) do @children = [] end - def parent=(parent) - if self.parentof?(parent) - devfail "%s[%s] is already the parent of %s[%s]" % - [self.class.name, self.title, parent.class.name, parent.title] - end - @parent = parent - end - # Add a hook for testing for recursion. def parentof?(child) if super(child) @@ -133,7 +125,7 @@ Puppet::Type.newtype(:component) do def pathbuilder tmp = [] myname = "" - if self.title =~ /^class\[(.+)\]$/ + if self.title =~ /^class\[(.+)\]$/i # 'main' is the top class, so we want to see '//' instead of # its name. unless $1 == "main" diff --git a/spec/Rakefile b/spec/Rakefile index bb2a75de5..8b45eff89 100644 --- a/spec/Rakefile +++ b/spec/Rakefile @@ -9,7 +9,7 @@ speclibdir = File.join(basedir, "lib") desc "Run all spec unit tests" Spec::Rake::SpecTask.new('unit') do |t| - t.spec_files = FileList['unit/**/*.rb'] + t.spec_files = FileList['unit/**/*.rb', 'integration/**/*.rb'] t.libs = [puppetlibdir, puppettestlibdir, speclibdir] end diff --git a/spec/integration/checksum.rb b/spec/integration/checksum.rb index f112f7502..cb187c656 100755 --- a/spec/integration/checksum.rb +++ b/spec/integration/checksum.rb @@ -9,8 +9,7 @@ require 'puppet/checksum' describe Puppet::Checksum, " when using the file terminus" do before do - Puppet[:checksum_terminus] = "file" - + Puppet::Checksum.terminus_class = :file @content = "this is some content" @sum = Puppet::Checksum.new(@content) diff --git a/spec/integration/node.rb b/spec/integration/node.rb index 8bc641bae..e4a311998 100755 --- a/spec/integration/node.rb +++ b/spec/integration/node.rb @@ -10,8 +10,8 @@ require 'puppet/node' describe Puppet::Node, " when using the memory terminus" do before do @name = "me" + Puppet::Node.terminus_class = :memory @node = Puppet::Node.new(@name) - Puppet[:node_terminus] = "memory" end it "should find no nodes by default" do diff --git a/spec/unit/indirector/code/configuration.rb b/spec/unit/indirector/code/configuration.rb index 8652f342d..0038a038e 100755 --- a/spec/unit/indirector/code/configuration.rb +++ b/spec/unit/indirector/code/configuration.rb @@ -39,6 +39,11 @@ describe Puppet::Indirector::Code::Configuration do compiler.find('node1') compiler.find('node2') end + + it "should provide a method for determining if the configuration is networked" do + compiler = Puppet::Indirector::Code::Configuration.new + compiler.should respond_to(:networked?) + end end describe Puppet::Indirector::Code::Configuration, " when creating the interpreter" do @@ -48,29 +53,16 @@ describe Puppet::Indirector::Code::Configuration, " when creating the interprete it "should not create the interpreter until it is asked for the first time" do interp = mock 'interp' - Puppet::Parser::Interpreter.expects(:new).with({}).returns(interp) + Puppet::Parser::Interpreter.expects(:new).with().returns(interp) @compiler.interpreter.should equal(interp) end it "should use the same interpreter for all compiles" do interp = mock 'interp' - Puppet::Parser::Interpreter.expects(:new).with({}).returns(interp) + Puppet::Parser::Interpreter.expects(:new).with().returns(interp) @compiler.interpreter.should equal(interp) @compiler.interpreter.should equal(interp) end - - it "should provide a mechanism for setting the code to pass to the interpreter" do - @compiler.should respond_to(:code=) - end - - it "should pass any specified code on to the interpreter when it is being initialized" do - code = "some code" - @compiler.code = code - interp = mock 'interp' - Puppet::Parser::Interpreter.expects(:new).with(:Code => code).returns(interp) - @compiler.send(:interpreter).should equal(interp) - end - end describe Puppet::Indirector::Code::Configuration, " when finding nodes" do @@ -132,10 +124,17 @@ describe Puppet::Indirector::Code::Configuration, " when creating configurations before do @compiler = Puppet::Indirector::Code::Configuration.new @name = "me" - @node = stub 'node', :merge => nil, :name => @name, :environment => "yay" + @node = Puppet::Node.new @name, :environment => "yay" + @node.stubs(:merge) Puppet::Node.stubs(:search).with(@name).returns(@node) end + it "should directly use provided nodes" do + Puppet::Node.expects(:search).never + @compiler.interpreter.expects(:compile).with(@node) + @compiler.find(@node) + end + it "should pass the found node to the interpreter for compiling" do config = mock 'config' @compiler.interpreter.expects(:compile).with(@node) @@ -144,11 +143,14 @@ describe Puppet::Indirector::Code::Configuration, " when creating configurations it "should return the results of compiling as the configuration" do config = mock 'config' - @compiler.interpreter.expects(:compile).with(@node).returns(:configuration) + result = mock 'result', :to_transportable => :configuration + + @compiler.interpreter.expects(:compile).with(@node).returns(result) @compiler.find(@name).should == :configuration end it "should benchmark the compile process" do + @compiler.stubs(:networked?).returns(true) @compiler.expects(:benchmark).with do |level, message| level == :notice and message =~ /^Compiled configuration/ end diff --git a/spec/unit/indirector/indirection.rb b/spec/unit/indirector/indirection.rb index 4311c88bf..7a1c4531c 100755 --- a/spec/unit/indirector/indirection.rb +++ b/spec/unit/indirector/indirection.rb @@ -7,28 +7,62 @@ require 'puppet/indirector' describe Puppet::Indirector::Indirection do before do @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) - @terminus = mock 'terminus' + @terminus = stub 'terminus', :has_most_recent? => false @indirection.stubs(:terminus).returns(@terminus) + @instance = stub 'instance', :version => nil, :version= => nil, :name => "whatever" + @name = :mything + end + + it "should not attempt to set a timestamp if the terminus cannot find the instance" do + @terminus.expects(:find).with(@name).returns(nil) + proc { @indirection.find(@name) }.should_not raise_error end it "should handle lookups of a model instance by letting the appropriate terminus perform the lookup" do - @terminus.expects(:find).with(:mything).returns(:whev) - @indirection.find(:mything).should == :whev + @terminus.expects(:find).with(@name).returns(@instance) + @indirection.find(@name).should == @instance end it "should handle removing model instances from a terminus letting the appropriate terminus remove the instance" do - @terminus.expects(:destroy).with(:mything).returns(:whev) - @indirection.destroy(:mything).should == :whev + @terminus.expects(:destroy).with(@name).returns(@instance) + @indirection.destroy(@name).should == @instance end it "should handle searching for model instances by letting the appropriate terminus find the matching instances" do - @terminus.expects(:search).with(:mything).returns(:whev) - @indirection.search(:mything).should == :whev + @terminus.expects(:search).with(@name).returns(@instance) + @indirection.search(@name).should == @instance end it "should handle storing a model instance by letting the appropriate terminus store the instance" do - @terminus.expects(:save).with(:mything).returns(:whev) - @indirection.save(:mything).should == :whev + @terminus.expects(:save).with(@instance).returns(@instance) + @indirection.save(@instance).should == @instance + end + + it "should add versions to found instances that do not already have them" do + @terminus.expects(:find).with(@name).returns(@instance) + time = mock 'time' + time.expects(:utc).returns(:mystamp) + Time.expects(:now).returns(time) + @instance.expects(:version=).with(:mystamp) + @indirection.find(@name) + end + + it "should add versions to saved instances that do not already have them" do + time = mock 'time' + time.expects(:utc).returns(:mystamp) + Time.expects(:now).returns(time) + @instance.expects(:version=).with(:mystamp) + @terminus.stubs(:save) + @indirection.save(@instance) + end + + # We've already tested this, basically, but... + it "should use the current time in UTC for versions" do + @instance.expects(:version=).with do |time| + time.utc? + end + @terminus.stubs(:save) + @indirection.save(@instance) end after do @@ -189,33 +223,14 @@ describe Puppet::Indirector::Indirection, " when deciding whether to cache" do proc { @indirection.cache_class = :foo }.should raise_error(ArgumentError) end - it "should not use a cache if there no cache setting" do - @indirection.expects(:cache).never - @terminus.stubs(:save) - @indirection.save(:whev) - end - - it "should use a cache if a cache was configured" do - cache = mock 'cache' - cache.expects(:save).with(:whev) - - cache_class = mock 'cache class' - cache_class.expects(:new).returns(cache) - Puppet::Indirector::Terminus.stubs(:terminus_class).with(:mycache, :test).returns(cache_class) - - @indirection.cache_class = :mycache - @terminus.stubs(:save) - @indirection.save(:whev) - end - after do @indirection.delete Puppet::Indirector::Indirection.clear_cache end end -describe Puppet::Indirector::Indirection, " when using a cache" do - before do +module IndirectionCaching + def setup Puppet.settings.stubs(:value).with("test_terminus").returns("test_terminus") @terminus_class = mock 'terminus_class' @terminus = mock 'terminus' @@ -228,13 +243,14 @@ describe Puppet::Indirector::Indirection, " when using a cache" do @indirection.terminus_class = :test_terminus end - it "should copy all writing indirection calls to the cache terminus" do - @cache_class.expects(:new).returns(@cache) - @indirection.cache_class = :cache_terminus - @cache.expects(:save).with(:whev) - @terminus.stubs(:save) - @indirection.save(:whev) + def teardown + @indirection.delete + Puppet::Indirector::Indirection.clear_cache end +end + +describe Puppet::Indirector::Indirection, " when managing the cache terminus" do + include IndirectionCaching it "should not create a cache terminus at initialization" do # This is weird, because all of the code is in the setup. If we got @@ -257,9 +273,77 @@ describe Puppet::Indirector::Indirection, " when using a cache" do @indirection.clear_cache @indirection.cache.should equal(cache2) end +end - after do - @indirection.delete - Puppet::Indirector::Indirection.clear_cache +describe Puppet::Indirector::Indirection, " when saving and using a cache" do + include IndirectionCaching + + before do + @indirection.cache_class = :cache_terminus + @cache_class.expects(:new).returns(@cache) + @name = "testing" + @instance = stub 'instance', :version => 5, :name => @name + end + + it "should not update the cache or terminus if the new object is not different" do + @cache.expects(:has_most_recent?).with(@name, 5).returns(true) + @indirection.save(@instance) + end + + it "should update the original and the cache if the cached object is different" do + @cache.expects(:has_most_recent?).with(@name, 5).returns(false) + @terminus.expects(:save).with(@instance) + @cache.expects(:save).with(@instance) + @indirection.save(@instance) + end +end + +describe Puppet::Indirector::Indirection, " when finding and using a cache" do + include IndirectionCaching + + before do + @indirection.cache_class = :cache_terminus + @cache_class.expects(:new).returns(@cache) + end + + it "should return the cached object if the cache is up to date" do + cached = mock 'cached object' + + name = "myobject" + + @terminus.expects(:version).with(name).returns(1) + @cache.expects(:has_most_recent?).with(name, 1).returns(true) + + @cache.expects(:find).with(name).returns(cached) + + @indirection.find(name).should equal(cached) + end + + it "should return the original object if the cache is not up to date" do + real = stub 'real object', :version => 1 + + name = "myobject" + + @cache.stubs(:save) + @cache.expects(:has_most_recent?).with(name, 1).returns(false) + @terminus.expects(:version).with(name).returns(1) + + @terminus.expects(:find).with(name).returns(real) + + @indirection.find(name).should equal(real) + end + + it "should cache any newly returned objects" do + real = stub 'real object', :version => 1 + + name = "myobject" + + @terminus.expects(:version).with(name).returns(1) + @cache.expects(:has_most_recent?).with(name, 1).returns(false) + + @terminus.expects(:find).with(name).returns(real) + @cache.expects(:save).with(real) + + @indirection.find(name).should equal(real) end end diff --git a/spec/unit/indirector/indirector.rb b/spec/unit/indirector/indirector.rb index 1702bf51f..78c8c614a 100755 --- a/spec/unit/indirector/indirector.rb +++ b/spec/unit/indirector/indirector.rb @@ -44,43 +44,68 @@ describe Puppet::Indirector, "when registering an indirection" do @thingie.indirection.should equal(@indirection) end + it "should allow specification of a default terminus" do + klass = mock 'terminus class' + Puppet::Indirector::Terminus.stubs(:terminus_class).with(:foo, :first).returns(klass) + @indirection = @thingie.indirects :first, :terminus_class => :foo + @indirection.terminus_class.should == :foo + end + after do @indirection.delete if @indirection end end describe Puppet::Indirector, " when redirecting a model" do - before do - @thingie = Class.new do - extend Puppet::Indirector + before do + @thingie = Class.new do + extend Puppet::Indirector + end + @indirection = @thingie.send(:indirects, :test) end - @mock_terminus = mock('Terminus') - @indirection = @thingie.send(:indirects, :test) - @thingie.expects(:indirection).returns(@mock_terminus) - end - - it "should give the model the ability to lookup a model instance by letting the indirection perform the lookup" do - @mock_terminus.expects(:find) - @thingie.find - end - it "should give the model the ability to remove model instances from a terminus by letting the indirection remove the instance" do - @mock_terminus.expects(:destroy) - @thingie.destroy - end - - it "should give the model the ability to search for model instances by letting the indirection find the matching instances" do - @mock_terminus.expects(:search) - @thingie.search - end - - it "should give the model the ability to store a model instance by letting the indirection store the instance" do - thing = @thingie.new - @mock_terminus.expects(:save).with(thing) - thing.save - end + it "should give the model the ability set a version" do + thing = @thingie.new + thing.should respond_to(:version=) + end - after do - @indirection.delete - end + it "should give the model the ability retrieve a version" do + thing = @thingie.new + thing.should respond_to(:version) + end + + it "should give the model the ability to lookup a model instance by letting the indirection perform the lookup" do + @indirection.expects(:find) + @thingie.find + end + + it "should give the model the ability to remove model instances from a terminus by letting the indirection remove the instance" do + @indirection.expects(:destroy) + @thingie.destroy + end + + it "should give the model the ability to search for model instances by letting the indirection find the matching instances" do + @indirection.expects(:search) + @thingie.search + end + + it "should give the model the ability to store a model instance by letting the indirection store the instance" do + thing = @thingie.new + @indirection.expects(:save).with(thing) + thing.save + end + + it "should give the model the ability to set the indirection terminus class" do + @indirection.expects(:terminus_class=).with(:myterm) + @thingie.terminus_class = :myterm + end + + it "should give the model the ability to set the indirection cache class" do + @indirection.expects(:cache_class=).with(:mycache) + @thingie.cache_class = :mycache + end + + after do + @indirection.delete + end end diff --git a/spec/unit/indirector/terminus.rb b/spec/unit/indirector/terminus.rb index 44180cf4b..3361bfeeb 100755 --- a/spec/unit/indirector/terminus.rb +++ b/spec/unit/indirector/terminus.rb @@ -1,3 +1,5 @@ +#!/usr/bin/env ruby + require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/defaults' require 'puppet/indirector' @@ -200,8 +202,8 @@ describe Puppet::Indirector::Terminus, " when creating terminus classes" do end end -describe Puppet::Indirector::Terminus, " when a terminus instance" do - before do +module TerminusInstanceTesting + def setup Puppet::Indirector::Terminus.stubs(:register_terminus_class) @indirection = stub 'indirection', :name => :myyaml, :register_terminus_type => nil Puppet::Indirector::Indirection.stubs(:instance).with(:my_stuff).returns(@indirection) @@ -218,6 +220,10 @@ describe Puppet::Indirector::Terminus, " when a terminus instance" do @terminus_class.name = :test @terminus = @terminus_class.new end +end + +describe Puppet::Indirector::Terminus, " when a terminus instance" do + include TerminusInstanceTesting it "should return the class's name as its name" do @terminus.name.should == :test @@ -236,3 +242,58 @@ describe Puppet::Indirector::Terminus, " when a terminus instance" do @terminus.model.should == :yay end end + +describe Puppet::Indirector::Terminus, " when managing indirected instances" do + include TerminusInstanceTesting + + it "should support comparing an instance's version with the terminus's version using just the instance's key" do + @terminus.should respond_to(:has_most_recent?) + end + + it "should fail if the :version method has not been overridden and no :find method is available" do + proc { @terminus.version('yay') }.should raise_error(Puppet::DevError) + end + + it "should use a found instance's version by default" do + name = 'instance' + instance = stub name, :version => 2 + @terminus.expects(:find).with(name).returns(instance) + @terminus.version(name).should == 2 + end + + it "should return nil as the version if no instance can be found" do + name = 'instance' + @terminus.expects(:find).with(name).returns(nil) + @terminus.version(name).should be_nil + end + + it "should consider an instance fresh if its version is more recent than the version provided" do + name = "yay" + @terminus.expects(:version).with(name).returns(5) + @terminus.has_most_recent?(name, 4).should be_true + end + + it "should consider an instance fresh if its version is equal to the version provided" do + name = "yay" + @terminus.expects(:version).with(name).returns(5) + @terminus.has_most_recent?(name, 5).should be_true + end + + it "should consider an instance not fresh if the provided version is more recent than its version" do + name = "yay" + @terminus.expects(:version).with(name).returns(4) + @terminus.has_most_recent?(name, 5).should be_false + end + + # Times annoyingly can't be compared directly to numbers, and our + # default version is 0. + it "should convert versions to floats when checking for freshness" do + existing = mock 'existing version' + new = mock 'new version' + existing.expects(:to_f).returns(1.0) + new.expects(:to_f).returns(1.0) + name = "yay" + @terminus.expects(:version).with(name).returns(existing) + @terminus.has_most_recent?(name, new) + end +end diff --git a/spec/unit/node/configuration.rb b/spec/unit/node/configuration.rb index 6b0677973..ee3834ef3 100755 --- a/spec/unit/node/configuration.rb +++ b/spec/unit/node/configuration.rb @@ -151,6 +151,153 @@ describe Puppet::Node::Configuration, " when extracting transobjects" do end end +describe Puppet::Node::Configuration, " when converting to a transobject configuration" do + class TestResource + 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::Configuration.new("mynode") + @original.tag(*%w{one two three}) + @original.add_class *%w{four five six} + + @top = TestResource.new 'top' + @topobject = TestResource.new 'topobject', :builtin => true + @virtual = TestResource.new 'virtual', :virtual => true + @virtualobject = TestResource.new 'virtualobject', :builtin => true, :virtual => true + @middle = TestResource.new 'middle' + @middleobject = TestResource.new 'middleobject', :builtin => true + @bottom = TestResource.new 'bottom' + @bottomobject = TestResource.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) + + @config = @original.to_transportable + end + + it "should add all resources as TransObjects" do + @resources.each { |resource| @config.resource(resource.ref).should be_instance_of(Puppet::TransObject) } + end + + it "should not extract defined virtual resources" do + @config.vertices.find { |v| v.name == "virtual" }.should be_nil + end + + it "should not extract builtin virtual resources" do + @config.vertices.find { |v| v.name == "virtualobject" }.should be_nil + end + + it "should copy the tag list to the new configuration" do + @config.tags.sort.should == @original.tags.sort + end + + it "should copy the class list to the new configuration" do + @config.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 = @config.resource(edge.source.ref) + target = @config.resource(edge.target.ref) + + source.should_not be_nil + target.should_not be_nil + @config.edge?(source, target).should be_true + end + end + + it "should set itself as the configuration for each converted resource" do + @config.vertices.each { |v| v.configuration.object_id.should equal(@config.object_id) } + end +end + +describe Puppet::Node::Configuration, " when converting to a RAL configuration" do + before do + @original = Puppet::Node::Configuration.new("mynode") + @original.tag(*%w{one two three}) + @original.add_class *%w{four five six} + + @top = Puppet::TransObject.new 'Class[top]', "component" + @topobject = Puppet::TransObject.new '/topobject', "file" + @middle = Puppet::TransObject.new 'Class[middle]', "component" + @middleobject = Puppet::TransObject.new '/middleobject', "file" + @bottom = Puppet::TransObject.new 'Class[bottom]', "component" + @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) + + @config = @original.to_ral + end + + it "should add all resources as RAL instances" do + @resources.each { |resource| @config.resource(resource.ref).should be_instance_of(Puppet::Type) } + end + + it "should copy the tag list to the new configuration" do + @config.tags.sort.should == @original.tags.sort + end + + it "should copy the class list to the new configuration" do + @config.classes.should == @original.classes + end + + it "should duplicate the original edges" do + @original.edges.each do |edge| + @config.edge?(@config.resource(edge.source.ref), @config.resource(edge.target.ref)).should be_true + end + end + + it "should set itself as the configuration for each converted resource" do + @config.vertices.each { |v| v.configuration.object_id.should equal(@config.object_id) } + end + + after do + # Remove all resource instances. + @config.clear(true) + end +end + describe Puppet::Node::Configuration, " when functioning as a resource container" do before do @config = Puppet::Node::Configuration.new("host") @@ -490,3 +637,60 @@ describe Puppet::Node::Configuration, " when writing dot files" do Puppet.settings.clear end end + +describe Puppet::Node::Configuration, " when indirecting" do + before do + @indirection = mock 'indirection' + + Puppet::Indirector::Indirection.clear_cache + end + + it "should redirect to the indirection for retrieval" do + Puppet::Node::Configuration.stubs(:indirection).returns(@indirection) + @indirection.expects(:find).with(:myconfig) + Puppet::Node::Configuration.find(:myconfig) + end + + it "should default to the code terminus" do + Puppet::Node::Configuration.indirection.terminus_class.should == :code + end + + after do + mocha_verify + Puppet::Indirector::Indirection.clear_cache + end +end + +describe Puppet::Node::Configuration, " when converting to yaml" do + before do + @configuration = Puppet::Node::Configuration.new("me") + @configuration.add_edge!("one", "two") + end + + it "should be able to be dumped to yaml" do + YAML.dump(@configuration).should be_instance_of(String) + end +end + +describe Puppet::Node::Configuration, " when converting from yaml" do + before do + @configuration = Puppet::Node::Configuration.new("me") + @configuration.add_edge!("one", "two") + + text = YAML.dump(@configuration) + @newconfig = YAML.load(text) + end + + it "should get converted back to a configuration" do + @newconfig.should be_instance_of(Puppet::Node::Configuration) + end + + it "should have all vertices" do + @newconfig.vertex?("one").should be_true + @newconfig.vertex?("two").should be_true + end + + it "should have all edges" do + @newconfig.edge?("one", "two").should be_true + end +end diff --git a/spec/unit/node/facts.rb b/spec/unit/node/facts.rb index 61f05a2b2..ef5c91071 100755 --- a/spec/unit/node/facts.rb +++ b/spec/unit/node/facts.rb @@ -6,25 +6,30 @@ require 'puppet/node/facts' describe Puppet::Node::Facts, " when indirecting" do before do - @terminus = mock 'terminus' - Puppet::Node::Facts.stubs(:indirection).returns(@terminus) + @indirection = mock 'indirection' - # We have to clear the cache so that the facts ask for our terminus stub, + # We have to clear the cache so that the facts ask for our indirection stub, # instead of anything that might be cached. Puppet::Indirector::Indirection.clear_cache @facts = Puppet::Node::Facts.new("me", "one" => "two") end it "should redirect to the specified fact store for retrieval" do - @terminus.expects(:find).with(:my_facts) + Puppet::Node::Facts.stubs(:indirection).returns(@indirection) + @indirection.expects(:find).with(:my_facts) Puppet::Node::Facts.find(:my_facts) end it "should redirect to the specified fact store for storage" do - @terminus.expects(:save).with(@facts) + Puppet::Node::Facts.stubs(:indirection).returns(@indirection) + @indirection.expects(:save).with(@facts) @facts.save end + it "should default to the code terminus" do + Puppet::Node::Facts.indirection.terminus_class.should == :code + end + after do mocha_verify Puppet::Indirector::Indirection.clear_cache diff --git a/spec/unit/node/node.rb b/spec/unit/node/node.rb index fe5d2be8b..06719c4cd 100755 --- a/spec/unit/node/node.rb +++ b/spec/unit/node/node.rb @@ -112,14 +112,15 @@ describe Puppet::Node, " when merging facts" do end describe Puppet::Node, " when indirecting" do - before do - @terminus = mock 'terminus' - Puppet::Node.stubs(:indirection).returns(@terminus) + it "should redirect to the indirection" do + @indirection = mock 'indirection' + Puppet::Node.stubs(:indirection).returns(@indirection) + @indirection.expects(:find).with(:my_node.to_s) + Puppet::Node.find(:my_node.to_s) end - it "should redirect to the specified node source" do - @terminus.expects(:find).with(:my_node.to_s) - Puppet::Node.find(:my_node.to_s) + it "should default to the 'null' node terminus" do + Puppet::Node.indirection.terminus_class.should == :null end after do diff --git a/spec/unit/parser/compile.rb b/spec/unit/parser/compile.rb index f6b3c9b3f..8bfcdd495 100755 --- a/spec/unit/parser/compile.rb +++ b/spec/unit/parser/compile.rb @@ -49,4 +49,14 @@ describe Puppet::Parser::Compile, " when compiling" do @compile.resources.find { |r| r.to_s == "Class[two]" }.should be_nil @compile.resources.find { |r| r.to_s == "Class[four]" }.should be_nil end + + it "should enable ast_nodes if the parser has any nodes" do + @parser.expects(:nodes).returns(:one => :yay) + @compile.ast_nodes?.should be_true + end + + it "should disable ast_nodes if the parser has no nodes" do + @parser.expects(:nodes).returns({}) + @compile.ast_nodes?.should be_false + end end diff --git a/spec/unit/parser/interpreter.rb b/spec/unit/parser/interpreter.rb index b4aa2a2d1..782b30cc7 100755 --- a/spec/unit/parser/interpreter.rb +++ b/spec/unit/parser/interpreter.rb @@ -2,65 +2,25 @@ require File.dirname(__FILE__) + '/../../spec_helper' -describe Puppet::Parser::Interpreter, " when initializing" do - it "should default to neither code nor file" do - interp = Puppet::Parser::Interpreter.new - interp.code.should be_nil - interp.file.should be_nil - end - - it "should set the code to parse" do - interp = Puppet::Parser::Interpreter.new :Code => :code - interp.code.should equal(:code) - end - - it "should set the file to parse" do - interp = Puppet::Parser::Interpreter.new :Manifest => :file - interp.file.should equal(:file) - end - - it "should set code and ignore manifest when both are present" do - interp = Puppet::Parser::Interpreter.new :Code => :code, :Manifest => :file - interp.code.should equal(:code) - interp.file.should be_nil - end - - it "should default to usenodes" do - interp = Puppet::Parser::Interpreter.new - interp.usenodes?.should be_true - end - - it "should allow disabling of usenodes" do - interp = Puppet::Parser::Interpreter.new :UseNodes => false - interp.usenodes?.should be_false - end -end - describe Puppet::Parser::Interpreter, " when creating parser instances" do before do @interp = Puppet::Parser::Interpreter.new @parser = mock('parser') end - it "should create a parser with code passed in at initialization time" do - @interp.code = :some_code - @parser.expects(:string=).with(:some_code) + it "should create a parser with code if there is code defined in the :code setting" do + Puppet.settings.stubs(:value).with(:code, :myenv).returns("mycode") + @parser.expects(:string=).with("mycode") @parser.expects(:parse) Puppet::Parser::Parser.expects(:new).with(:environment => :myenv).returns(@parser) @interp.send(:create_parser, :myenv).object_id.should equal(@parser.object_id) end - it "should create a parser with a file passed in at initialization time" do - @interp.file = :a_file - @parser.expects(:file=).with(:a_file) + it "should create a parser with the main manifest when the code setting is an empty string" do + Puppet.settings.stubs(:value).with(:code, :myenv).returns("") + Puppet.settings.stubs(:value).with(:manifest, :myenv).returns("/my/file") @parser.expects(:parse) - Puppet::Parser::Parser.expects(:new).with(:environment => :myenv).returns(@parser) - @interp.send(:create_parser, :myenv).should equal(@parser) - end - - it "should create a parser with the main manifest when passed neither code nor file" do - @parser.expects(:parse) - @parser.expects(:file=).with(Puppet[:manifest]) + @parser.expects(:file=).with("/my/file") Puppet::Parser::Parser.expects(:new).with(:environment => :myenv).returns(@parser) @interp.send(:create_parser, :myenv).should equal(@parser) end @@ -159,22 +119,13 @@ describe Puppet::Parser::Interpreter, " when compiling configurations" do @parser = mock 'parser' end - it "should create a compile with the node, parser, and whether to use ast nodes when ast nodes is true" do + it "should create a compile with the node and parser" do @compile.expects(:compile).returns(:config) @interp.expects(:parser).with(:myenv).returns(@parser) - @interp.expects(:usenodes?).returns(true) - Puppet::Parser::Compile.expects(:new).with(@node, @parser, :ast_nodes => true).returns(@compile) + Puppet::Parser::Compile.expects(:new).with(@node, @parser).returns(@compile) @interp.compile(@node) end - it "should create a compile with the node, parser, and whether to use ast nodes when ast nodes is false" do - @compile.expects(:compile).returns(:config) - @interp.expects(:parser).with(:myenv).returns(@parser) - @interp.expects(:usenodes?).returns(false) - Puppet::Parser::Compile.expects(:new).with(@node, @parser, :ast_nodes => false).returns(@compile) - @interp.compile(@node).should equal(:config) - end - it "should fail intelligently when no parser can be found" do @interp.expects(:parser).with(:myenv).returns(nil) proc { @interp.compile(@node) }.should raise_error(Puppet::ParseError) diff --git a/test/data/snippets/classpathtest b/test/data/snippets/classpathtest index 68610958b..580333369 100644 --- a/test/data/snippets/classpathtest +++ b/test/data/snippets/classpathtest @@ -1,11 +1,11 @@ # $Id$ -define component { +define mytype { file { "/tmp/classtest": ensure => file, mode => 755 } } class testing { - component { "componentname": } + mytype { "componentname": } } include testing diff --git a/test/language/compile.rb b/test/language/compile.rb index 5732acba3..7a8a613af 100755 --- a/test/language/compile.rb +++ b/test/language/compile.rb @@ -23,7 +23,7 @@ class TestCompile < Test::Unit::TestCase def mkparser # This should mock an interpreter - @parser = stub 'parser', :version => "1.0" + @parser = stub 'parser', :version => "1.0", :nodes => {} end def mkcompile(options = {}) @@ -38,7 +38,7 @@ class TestCompile < Test::Unit::TestCase def test_initialize compile = nil node = stub 'node', :name => "foo" - parser = stub 'parser', :version => "1.0" + parser = stub 'parser', :version => "1.0", :nodes => {} assert_nothing_raised("Could not init compile with all required options") do compile = Compile.new(node, parser) end @@ -51,7 +51,7 @@ class TestCompile < Test::Unit::TestCase # Now try it with some options assert_nothing_raised("Could not init compile with extra options") do - compile = Compile.new(node, parser, :ast_nodes => false) + compile = Compile.new(node, parser) end assert_equal(false, compile.ast_nodes?, "Did not set ast_nodes? correctly") @@ -188,7 +188,7 @@ class TestCompile < Test::Unit::TestCase # Make sure we either don't look for nodes, or that we find and evaluate the right object. def test_evaluate_ast_node # First try it with ast_nodes disabled - compile = mkcompile :ast_nodes => false + compile = mkcompile name = compile.node.name compile.expects(:ast_nodes?).returns(false) compile.parser.expects(:nodes).never @@ -201,7 +201,7 @@ class TestCompile < Test::Unit::TestCase # Now try it with them enabled, but no node found. nodes = mock 'node_hash' - compile = mkcompile :ast_nodes => true + compile = mkcompile name = compile.node.name compile.expects(:ast_nodes?).returns(true) compile.parser.stubs(:nodes).returns(nodes) @@ -221,7 +221,7 @@ class TestCompile < Test::Unit::TestCase end # Finally, make sure it works dandily when we have a node - compile = mkcompile :ast_nodes => true + compile = mkcompile compile.expects(:ast_nodes?).returns(true) node = stub 'node', :classname => "c" @@ -240,7 +240,7 @@ class TestCompile < Test::Unit::TestCase "Did not create node resource") # Lastly, check when we actually find the default. - compile = mkcompile :ast_nodes => true + compile = mkcompile compile.expects(:ast_nodes?).returns(true) node = stub 'node', :classname => "default" diff --git a/test/language/functions.rb b/test/language/functions.rb index 2a392e01a..db107fd36 100755 --- a/test/language/functions.rb +++ b/test/language/functions.rb @@ -202,16 +202,10 @@ class TestLangFunctions < Test::Unit::TestCase f.puts "original text" end - manifest = tempfile() file = tempfile() - File.open(manifest, "w") do |f| - f.puts %{file { "#{file}": content => template("#{template}") }} - end - interp = Puppet::Parser::Interpreter.new( - :Manifest => manifest, - :UseNodes => false - ) + Puppet[:code] = %{file { "#{file}": content => template("#{template}") }} + interp = Puppet::Parser::Interpreter.new node = mknode node.stubs(:environment).returns("yay") diff --git a/test/language/parser.rb b/test/language/parser.rb index 9109686cb..1e7adb45e 100755 --- a/test/language/parser.rb +++ b/test/language/parser.rb @@ -618,7 +618,8 @@ file { "/tmp/yayness": f.puts "file { '#{file}': ensure => present }" end - interp = mkinterp :Manifest => top, :UseNodes => false + Puppet[:manifest] = top + interp = Puppet::Parser::Interpreter.new code = nil assert_nothing_raised do diff --git a/test/language/scope.rb b/test/language/scope.rb index b4db5ef40..22734a5fb 100755 --- a/test/language/scope.rb +++ b/test/language/scope.rb @@ -399,24 +399,17 @@ class TestScope < Test::Unit::TestCase Puppet::Rails.init sleep 1 children = [] - file = tempfile() - File.open(file, "w") { |f| - f.puts " + Puppet[:code] = " class yay { @@host { myhost: ip => \"192.168.0.2\" } } include yay @@host { puppet: ip => \"192.168.0.3\" } Host <<||>>" - } interp = nil assert_nothing_raised { - interp = Puppet::Parser::Interpreter.new( - :Manifest => file, - :UseNodes => false, - :ForkSave => false - ) + interp = Puppet::Parser::Interpreter.new } config = nil diff --git a/test/language/snippets.rb b/test/language/snippets.rb index 7168a81d8..58a6e7f89 100755 --- a/test/language/snippets.rb +++ b/test/language/snippets.rb @@ -202,7 +202,7 @@ class TestSnippets < Test::Unit::TestCase assert_nothing_raised { assert_equal( - "//testing/component[componentname]/File[/tmp/classtest]", + "//testing/Mytype[componentname]/File[/tmp/classtest]", file.path) } end @@ -449,45 +449,24 @@ class TestSnippets < Test::Unit::TestCase #eval("alias %s %s" % [testname, mname]) testname = ("test_" + mname).intern self.send(:define_method, testname) { + Puppet[:manifest] = snippet(file) facts = { "hostname" => "testhost", "domain" => "domain.com", "ipaddress" => "127.0.0.1", "fqdn" => "testhost.domain.com" } - Facter.stubs(:each) - facts.each do |name, value| - Facter.stubs(:value).with(name).returns(value) - end - # first parse the file - server = Puppet::Network::Handler.master.new( - :Manifest => snippet(file), - :Local => true - ) - facts = Puppet::Node::Facts.new("testhost", facts) - Puppet::Node::Facts.stubs(:save) - Puppet::Node::Facts.stubs(:find).returns(facts) - client = Puppet::Network::Client.master.new( - :Master => server, - :Cache => false - ) - client.class.stubs(:facts).returns(facts.values) - assert(client.local) - assert_nothing_raised { - client.getconfig() + node = Puppet::Node.new("testhost") + node.merge(facts) + + config = nil + assert_nothing_raised("Could not compile configuration") { + config = Puppet::Node::Configuration.find(node) } - client = Puppet::Network::Client.master.new( - :Master => server, - :Cache => false - ) - - assert(client.local) - # Now do it again - Puppet::Type.allclear - assert_nothing_raised { - client.getconfig() + assert_nothing_raised("Could not convert configuration") { + config = config.to_ral } Puppet::Type.eachtype { |type| @@ -500,12 +479,10 @@ class TestSnippets < Test::Unit::TestCase assert(obj.name) } } - @configuration = client.configuration + @configuration = config assert_nothing_raised { self.send(mname) } - - client.clear } mname = mname.intern end diff --git a/test/language/transportable.rb b/test/language/transportable.rb index 31931c937..4e4573e0b 100755 --- a/test/language/transportable.rb +++ b/test/language/transportable.rb @@ -47,37 +47,17 @@ class TestTransportable < Test::Unit::TestCase assert(newobj.type, "Bucket has no type") end - # Verify that we correctly strip out collectable objects, since they should - # not be sent to the client. - def test_collectstrip - top = mk_transtree do |object, depth, width| - if width % 2 == 1 - object.collectable = true - end - end - - assert(top.flatten.find_all { |o| o.collectable }.length > 0, - "Could not find any collectable objects") - - # Now strip out the collectable objects - top.collectstrip! - - # And make sure they're actually gone - assert_equal(0, top.flatten.find_all { |o| o.collectable }.length, - "Still found collectable objects") - end - # Make sure our 'delve' command is working def test_delve top = mk_transtree do |object, depth, width| if width % 2 == 1 - object.collectable = true + object.file = :funtest end end objects = [] buckets = [] - collectable = [] + found = [] count = 0 assert_nothing_raised { @@ -87,8 +67,8 @@ class TestTransportable < Test::Unit::TestCase buckets << object else objects << object - if object.collectable - collectable << object + if object.file == :funtest + found << object end end end @@ -98,9 +78,9 @@ class TestTransportable < Test::Unit::TestCase assert(objects.include?(obj), "Missing obj %s[%s]" % [obj.type, obj.name]) end - assert_equal(collectable.length, - top.flatten.find_all { |o| o.collectable }.length, - "Found incorrect number of collectable objects") + assert_equal(found.length, + top.flatten.find_all { |o| o.file == :funtest }.length, + "Found incorrect number of objects") end end diff --git a/test/lib/puppettest/parsertesting.rb b/test/lib/puppettest/parsertesting.rb index 62fa7213e..3e3ce6cb9 100644 --- a/test/lib/puppettest/parsertesting.rb +++ b/test/lib/puppettest/parsertesting.rb @@ -54,10 +54,8 @@ module PuppetTest::ParserTesting Puppet::Node.new(name) end - def mkinterp(args = {}) - args[:Code] ||= "" unless args.include?(:Manifest) - args[:Local] ||= true - Puppet::Parser::Interpreter.new(args) + def mkinterp + Puppet::Parser::Interpreter.new end def mkparser @@ -301,11 +299,10 @@ module PuppetTest::ParserTesting # This assumes no nodes def assert_creates(manifest, *files) interp = nil + oldmanifest = Puppet[:manifest] + Puppet[:manifest] = manifest assert_nothing_raised { - interp = Puppet::Parser::Interpreter.new( - :Manifest => manifest, - :UseNodes => false - ) + interp = Puppet::Parser::Interpreter.new } trans = nil @@ -323,6 +320,8 @@ module PuppetTest::ParserTesting files.each do |file| assert(FileTest.exists?(file), "Did not create %s" % file) end + ensure + Puppet[:manifest] = oldmanifest end def mk_transobject(file = "/etc/passwd") diff --git a/test/network/client/client.rb b/test/network/client/client.rb index 484c794e4..a297a87e1 100755 --- a/test/network/client/client.rb +++ b/test/network/client/client.rb @@ -141,17 +141,13 @@ class TestClient < Test::Unit::TestCase end def test_classfile - manifest = tempfile() + Puppet[:code] = "class yaytest {}\n class bootest {}\n include yaytest, bootest" - File.open(manifest, "w") do |file| - file.puts "class yaytest {}\n class bootest {}\n include yaytest, bootest" - end + Puppet::Node::Facts.indirection.stubs(:save) master = client = nil assert_nothing_raised() { master = Puppet::Network::Handler.master.new( - :Manifest => manifest, - :UseNodes => false, :Local => false ) } diff --git a/test/network/client/master.rb b/test/network/client/master.rb index 0a3b75b91..4ae77abc2 100755 --- a/test/network/client/master.rb +++ b/test/network/client/master.rb @@ -48,8 +48,10 @@ class TestMasterClient < Test::Unit::TestCase def mkmaster(options = {}) options[:UseNodes] = false options[:Local] = true - unless options[:Code] - options[:Manifest] ||= mktestmanifest + if code = options[:Code] + Puppet[:code] = code + else + Puppet[:manifest] = options[:Manifest] || mktestmanifest end # create our master # this is the default server setup @@ -395,6 +397,8 @@ end manifest = tempfile() File.open(manifest, "w") { |f| f.puts "file { '#{file}': content => yay }" } + Puppet::Node::Facts.indirection.stubs(:save) + driver = mkmaster(:Manifest => manifest) driver.local = false master = mkclient(driver) @@ -404,7 +408,7 @@ end assert(! master.fresh?(master.class.facts), "Considered fresh with no compile at all") - + assert_nothing_raised { master.run } assert(master.fresh?(master.class.facts), "not considered fresh after compile") @@ -479,7 +483,9 @@ end master.local = false driver = master.send(:instance_variable_get, "@driver") driver.local = false + Puppet::Node::Facts.indirection.stubs(:save) # Retrieve the configuration + master.getconfig # Now the config is up to date, so get rid of the @objects var and @@ -507,6 +513,8 @@ end driver = master.send(:instance_variable_get, "@driver") driver.local = false + Puppet::Node::Facts.indirection.stubs(:save) + assert_nothing_raised("Could not compile config") do master.getconfig end diff --git a/test/network/handler/configuration.rb b/test/network/handler/configuration.rb index 29a393769..1c08fd196 100755 --- a/test/network/handler/configuration.rb +++ b/test/network/handler/configuration.rb @@ -25,9 +25,7 @@ class TestHandlerConfiguration < Test::Unit::TestCase config = Config.new # First test the defaults - args = {} - config.instance_variable_set("@options", args) - config.expects(:create_interpreter).with(args).returns(:interp) + config.expects(:create_interpreter).returns(:interp) assert_equal(:interp, config.send(:interpreter), "Did not return the interpreter") # Now run it again and make sure we get the same thing @@ -39,20 +37,8 @@ class TestHandlerConfiguration < Test::Unit::TestCase args = {} # Try it first with defaults. - Puppet::Parser::Interpreter.expects(:new).with(:Local => config.local?).returns(:interp) - assert_equal(:interp, config.send(:create_interpreter, args), "Did not return the interpreter") - - # Now reset it and make sure a specified manifest passes through - file = tempfile - args[:Manifest] = file - Puppet::Parser::Interpreter.expects(:new).with(:Local => config.local?, :Manifest => file).returns(:interp) - assert_equal(:interp, config.send(:create_interpreter, args), "Did not return the interpreter") - - # And make sure the code does, too - args.delete(:Manifest) - args[:Code] = "yay" - Puppet::Parser::Interpreter.expects(:new).with(:Local => config.local?, :Code => "yay").returns(:interp) - assert_equal(:interp, config.send(:create_interpreter, args), "Did not return the interpreter") + Puppet::Parser::Interpreter.expects(:new).returns(:interp) + assert_equal(:interp, config.send(:create_interpreter), "Did not return the interpreter") end # Make sure node objects get appropriate data added to them. @@ -67,7 +53,7 @@ class TestHandlerConfiguration < Test::Unit::TestCase config.send(:add_node_data, fakenode) # Now try it with classes. - config.instance_variable_set("@options", {:Classes => %w{a b}}) + config.classes = %w{a b} list = [] fakenode = Object.new fakenode.expects(:merge).with(:facts) @@ -126,8 +112,9 @@ class TestHandlerConfiguration < Test::Unit::TestCase # Now a non-local config = Config.new(:Local => false) - obj = Object.new - yamld = Object.new + assert(! config.local?, "Config wrongly thinks it's local") + obj = mock 'dumpee' + yamld = mock 'yaml' obj.expects(:to_yaml).with(:UseBlock => true).returns(yamld) CGI.expects(:escape).with(yamld).returns(:translated) assert_equal(:translated, config.send(:translate, obj), "Did not return translated config") diff --git a/test/network/handler/master.rb b/test/network/handler/master.rb index 42c4d22c9..6c4451d06 100755 --- a/test/network/handler/master.rb +++ b/test/network/handler/master.rb @@ -56,11 +56,10 @@ class TestMaster < Test::Unit::TestCase @@tmpfiles << file2 client = master = nil + Puppet[:manifest] = manifest assert_nothing_raised() { # this is the default server setup master = Puppet::Network::Handler.master.new( - :Manifest => manifest, - :UseNodes => false, :Local => true ) } diff --git a/test/network/handler/runner.rb b/test/network/handler/runner.rb index 23883a17c..50a00862a 100755 --- a/test/network/handler/runner.rb +++ b/test/network/handler/runner.rb @@ -7,15 +7,14 @@ require 'puppettest' class TestHandlerRunner < Test::Unit::TestCase include PuppetTest - def mkclient(file) + def mkclient(code) master = nil client = nil + Puppet[:code] = code # create our master assert_nothing_raised() { # this is the default server setup master = Puppet::Network::Handler.master.new( - :Manifest => file, - :UseNodes => false, :Local => true ) } @@ -38,8 +37,7 @@ class TestHandlerRunner < Test::Unit::TestCase created = tempfile() # We specify the schedule here, because I was having problems with # using default schedules. - File.open(file, "w") do |f| - f.puts %{ + code = %{ class yayness { schedule { "yayness": period => weekly } file { "#{created}": ensure => file, schedule => yayness } @@ -47,9 +45,8 @@ class TestHandlerRunner < Test::Unit::TestCase include yayness } - end - client = mkclient(file) + client = mkclient(code) runner = nil assert_nothing_raised { diff --git a/test/rails/configuration.rb b/test/rails/configuration.rb index 752ea5375..277753945 100755 --- a/test/rails/configuration.rb +++ b/test/rails/configuration.rb @@ -25,7 +25,6 @@ class ConfigurationRailsTests < PuppetTest::TestCase def test_finish_before_store railsinit compile = mkcompile - compile.ast_nodes = true parser = compile.parser node = parser.newnode [compile.node.name], :code => AST::ASTArray.new(:children => [ @@ -52,19 +51,9 @@ class ConfigurationRailsTests < PuppetTest::TestCase Puppet[:storeconfigs] = true } - file = tempfile() - File.open(file, "w") { |f| - f.puts "file { \"/etc\": owner => root }" - } + Puppet[:code] = "file { \"/etc\": owner => root }" - interp = nil - assert_nothing_raised { - interp = Puppet::Parser::Interpreter.new( - :Manifest => file, - :UseNodes => false, - :ForkSave => false - ) - } + interp = Puppet::Parser::Interpreter.new facts = {} Facter.each { |fact, val| facts[fact] = val } diff --git a/test/ral/providers/service/debian.rb b/test/ral/providers/service/debian.rb new file mode 100755 index 000000000..f74141f9e --- /dev/null +++ b/test/ral/providers/service/debian.rb @@ -0,0 +1,71 @@ +#!/usr/bin/env ruby +# +# Created by David Schmitt on 2007-09-13 +# Copyright (c) 2007. All rights reserved. + +$:.unshift("../../../lib") if __FILE__ =~ /\.rb$/ + +require 'puppettest' + +class TestDebianServiceProvider < Test::Unit::TestCase + include PuppetTest + include Puppet::Util + + def prepare_provider(servicename, output) + service = Puppet::Type.type(:service).create( + :name => servicename, :provider => :debian + ) + + provider = service.provider + assert(provider, "did not get debian provider") + + metaclass = class << provider + self + end + + metaclass.instance_eval do + define_method :update do |*args| + return output + end + end + + provider + end + + def assert_enabled( servicename, output) + provider = prepare_provider( servicename, output ) + assert_equal(:true, provider.enabled?, + "Service provider=debian thinks service is disabled, when it isn't") + end + + def assert_disabled( servicename, output ) + provider = prepare_provider( servicename, output ) + assert_equal(:false, provider.enabled?, + "Service provider=debian thinks service is enabled, when it isn't") + end + + # Testing #822 + def test_file_rc + # These messages are from file-rc's + # update-rc.d -n -f $service remove + assert_enabled("test1", "/etc/runlevel.tmp not installed as /etc/runlevel.conf\n") + assert_disabled("test2", "Nothing to do.\n") + end + + def test_sysv_rc + # These messages are from file-rc's + # update-rc.d -n -f $service remove + assert_enabled("test3", """ Removing any system startup links for /etc/init.d/test3 ... + /etc/rc0.d/K11test3 + /etc/rc1.d/K11test3 + /etc/rc2.d/S89test3 + /etc/rc3.d/S89test3 + /etc/rc4.d/S89test3 + /etc/rc5.d/S89test3 + /etc/rc6.d/K11test3 +""") + assert_disabled("test4", " Removing any system startup links for /etc/init.d/test4 ...\n") + end +end + +# $Id$