diff options
| author | luke <luke@980ebf18-57e1-0310-9a29-db15c13687c0> | 2006-12-23 04:49:56 +0000 |
|---|---|---|
| committer | luke <luke@980ebf18-57e1-0310-9a29-db15c13687c0> | 2006-12-23 04:49:56 +0000 |
| commit | 9bb5c50d0b30b4dfb82b6b705dfcbf0e126a9d61 (patch) | |
| tree | e41fbeb90e4050ea2af6d37e7b443cf9f84be162 | |
| parent | be711d357857f5e6d4a28a22bd60dd89e9e136c0 (diff) | |
| download | puppet-9bb5c50d0b30b4dfb82b6b705dfcbf0e126a9d61.tar.gz puppet-9bb5c50d0b30b4dfb82b6b705dfcbf0e126a9d61.tar.xz puppet-9bb5c50d0b30b4dfb82b6b705dfcbf0e126a9d61.zip | |
Not downcasing facts any longer, closing #210 (although not using the patch from mpalmer, since I had not noticed the patch was there). Also, making all nodes, classes, and definitions case insensitive, closing #344. Finally, I added case insensitivity to the language in general, which should preserve backwards compatibility and probably makes the most sense in the long run anyway.
git-svn-id: https://reductivelabs.com/svn/puppet/trunk@1964 980ebf18-57e1-0310-9a29-db15c13687c0
| -rw-r--r-- | lib/puppet/client/master.rb | 2 | ||||
| -rw-r--r-- | lib/puppet/event.rb | 16 | ||||
| -rw-r--r-- | lib/puppet/log.rb | 13 | ||||
| -rw-r--r-- | lib/puppet/parser/ast.rb | 14 | ||||
| -rw-r--r-- | lib/puppet/parser/ast/casestatement.rb | 11 | ||||
| -rw-r--r-- | lib/puppet/parser/ast/selector.rb | 10 | ||||
| -rw-r--r-- | lib/puppet/parser/interpreter.rb | 12 | ||||
| -rw-r--r-- | lib/puppet/pgraph.rb | 13 | ||||
| -rw-r--r-- | lib/puppet/statechange.rb | 95 | ||||
| -rw-r--r-- | lib/puppet/transaction.rb | 45 | ||||
| -rwxr-xr-x | test/client/master.rb | 17 | ||||
| -rwxr-xr-x | test/language/ast.rb | 21 | ||||
| -rwxr-xr-x | test/language/ast/casestatement.rb | 67 | ||||
| -rwxr-xr-x | test/language/ast/selector.rb | 53 | ||||
| -rwxr-xr-x | test/language/interpreter.rb | 30 | ||||
| -rwxr-xr-x | test/language/node.rb | 4 | ||||
| -rwxr-xr-x | test/lib/puppettest.rb | 9 | ||||
| -rw-r--r-- | test/lib/puppettest/parsertesting.rb | 28 | ||||
| -rwxr-xr-x | test/other/statechange.rb | 111 | ||||
| -rwxr-xr-x | test/other/transactions.rb | 117 |
20 files changed, 525 insertions, 163 deletions
diff --git a/lib/puppet/client/master.rb b/lib/puppet/client/master.rb index d6997f631..e6e7c7835 100644 --- a/lib/puppet/client/master.rb +++ b/lib/puppet/client/master.rb @@ -83,7 +83,7 @@ class Puppet::Client::MasterClient < Puppet::Client facts = {} Facter.each { |name,fact| - facts[name] = fact.to_s.downcase + facts[name] = fact.to_s } # Add our client version to the list of facts, so people can use it diff --git a/lib/puppet/event.rb b/lib/puppet/event.rb index 98ea92c9d..16e76bf50 100644 --- a/lib/puppet/event.rb +++ b/lib/puppet/event.rb @@ -1,5 +1,6 @@ require 'puppet' -require 'puppet/type' +require 'puppet/util/methodhelper' +require 'puppet/util/errors' module Puppet # events are transient packets of information; they result in one or more (or none) @@ -7,19 +8,16 @@ module Puppet # eventually, these will be passed on to some central event system class Event include Puppet + include Puppet::Util::MethodHelper + include Puppet::Util::Errors + attr_accessor :event, :source, :transaction @@events = [] def initialize(args) - unless args.include?(:event) and args.include?(:source) - raise Puppet::DevError, "Event.new called incorrectly" - end - - @change = args[:change] - @event = args[:event] - @source = args[:source] - @transaction = args[:transaction] + set_options symbolize_options(args) + requiredopts(:event, :source) end def to_s diff --git a/lib/puppet/log.rb b/lib/puppet/log.rb index 8362e8d4d..53535e90c 100644 --- a/lib/puppet/log.rb +++ b/lib/puppet/log.rb @@ -368,6 +368,19 @@ module Puppet end end + # Log to an array, just for testing. + newdesttype :array do + match "Array" + + def initialize(array) + @array = array + end + + def handle(msg) + @array << msg + end + end + # Create a new log destination. def Log.newdestination(dest) # Each destination can only occur once. diff --git a/lib/puppet/parser/ast.rb b/lib/puppet/parser/ast.rb index ccd6d216a..e8cfcc4dd 100644 --- a/lib/puppet/parser/ast.rb +++ b/lib/puppet/parser/ast.rb @@ -80,24 +80,18 @@ class Puppet::Parser::AST # correctly handles errors. It is critical to use this method because # it can enable you to catch the error where it happens, rather than # much higher up the stack. - def safeevaluate(*args) + def safeevaluate(options) # We duplicate code here, rather than using exceptwrap, because this # is called so many times during parsing. - #exceptwrap do - # self.evaluate(*args) - #end begin - return self.evaluate(*args) + return self.evaluate(options) rescue Puppet::Error => detail raise adderrorcontext(detail) rescue => detail - message = options[:message] || "%s failed with error %s: %s" % - [self.class, detail.class, detail.to_s] - - error = options[:type].new(message) + error = Puppet::Error.new(detail.to_s) # We can't use self.fail here because it always expects strings, # not exceptions. - raise adderrorcontext(error, detail) + raise adderrorcontext(error) end end diff --git a/lib/puppet/parser/ast/casestatement.rb b/lib/puppet/parser/ast/casestatement.rb index 6543293b4..b4197d9b4 100644 --- a/lib/puppet/parser/ast/casestatement.rb +++ b/lib/puppet/parser/ast/casestatement.rb @@ -11,13 +11,21 @@ class Puppet::Parser::AST def evaluate(hash) scope = hash[:scope] value = @test.safeevaluate(:scope => scope) + sensitive = Puppet[:casesensitive] + value = value.downcase if ! sensitive and value.respond_to?(:downcase) retvalue = nil found = false # Iterate across the options looking for a match. @options.each { |option| - if option.eachvalue(scope) { |opval| break true if opval == value } + if option.eachvalue(scope) { |opval| + opval = opval.downcase if ! sensitive and opval.respond_to?(:downcase) + # break true if opval == value + if opval == value + break true + end + } # we found a matching option retvalue = option.safeevaluate(:scope => scope) found = true @@ -31,6 +39,7 @@ class Puppet::Parser::AST retvalue = @default.safeevaluate(:scope => scope) else Puppet.debug "No true answers and no default" + retvalue = nil end end return retvalue diff --git a/lib/puppet/parser/ast/selector.rb b/lib/puppet/parser/ast/selector.rb index 94b7a5583..ce421e491 100644 --- a/lib/puppet/parser/ast/selector.rb +++ b/lib/puppet/parser/ast/selector.rb @@ -18,10 +18,15 @@ class Puppet::Parser::AST # Get our parameter. paramvalue = @param.safeevaluate(:scope => scope) + + sensitive = Puppet[:casesensitive] + + if ! sensitive and paramvalue.respond_to?(:downcase) + paramvalue = paramvalue.downcase + end default = nil - #@values = [@values] unless @values.instance_of? AST::ASTArray unless @values.instance_of? AST::ASTArray or @values.instance_of? Array @values = [@values] end @@ -29,6 +34,9 @@ class Puppet::Parser::AST # Then look for a match in the options. @values.each { |obj| param = obj.param.safeevaluate(:scope => scope) + if ! sensitive && param.respond_to?(:downcase) + param = param.downcase + end if param == paramvalue # we found a matching option retvalue = obj.value.safeevaluate(:scope => scope) diff --git a/lib/puppet/parser/interpreter.rb b/lib/puppet/parser/interpreter.rb index 5144c4970..a81b7939f 100644 --- a/lib/puppet/parser/interpreter.rb +++ b/lib/puppet/parser/interpreter.rb @@ -10,6 +10,12 @@ require 'puppet/parser/scope' class Puppet::Parser::Interpreter include Puppet::Util + + Puppet.setdefaults(:puppet, + :casesensitive => [false, + "Whether matching in case statements and selectors + should be case-sensitive. Case insensitivity is + handled by downcasing all values before comparison."]) Puppet.setdefaults("ldap", :ldapnodes => [false, @@ -239,6 +245,8 @@ class Puppet::Parser::Interpreter # The recursive method used to actually look these objects up. def fqfind(namespace, name, table) + namespace = namespace.downcase + name = name.downcase if name =~ /^::/ or namespace == "" return table[name.sub(/^::/, '')] end @@ -469,6 +477,7 @@ class Puppet::Parser::Interpreter # Create a new class, or merge with an existing class. def newclass(fqname, options = {}) + fqname = fqname.downcase if @definetable.include?(fqname) raise Puppet::ParseError, "Cannot redefine class %s as a definition" % fqname @@ -521,6 +530,7 @@ class Puppet::Parser::Interpreter # Create a new definition. def newdefine(fqname, options = {}) + fqname = fqname.downcase if @classtable.include?(fqname) raise Puppet::ParseError, "Cannot redefine class %s as a definition" % fqname @@ -551,6 +561,7 @@ class Puppet::Parser::Interpreter def newnode(names, options = {}) names = [names] unless names.instance_of?(Array) names.collect do |name| + name = name.to_s.downcase if other = @nodetable[name] @parser.error("Node %s is already defined at %s:%s; cannot redefine" % [other.name, other.file, other.line]) end @@ -585,6 +596,7 @@ class Puppet::Parser::Interpreter # Search for our node in the various locations. def nodesearch(*nodes) + nodes = nodes.collect { |n| n.to_s.downcase } # At this point, stop at the first source that defines # the node @nodesources.each do |source| diff --git a/lib/puppet/pgraph.rb b/lib/puppet/pgraph.rb index 7be785477..36df0cc96 100644 --- a/lib/puppet/pgraph.rb +++ b/lib/puppet/pgraph.rb @@ -66,13 +66,14 @@ class Puppet::PGraph < GRATR::Digraph # Collect all of the edges that the passed events match. Returns # an array of edges. - def matching_edges(source, events) - unless vertex?(source) - Puppet.warning "Got an event from invalid vertex %s" % source.ref - return [] - end - + def matching_edges(events, base = nil) events.collect do |event| + source = base || event.source + + unless vertex?(source) + Puppet.warning "Got an event from invalid vertex %s" % source.ref + next + end # Get all of the edges that this vertex should forward events # to, which is the same thing as saying all edges directly below # This vertex in the graph. diff --git a/lib/puppet/statechange.rb b/lib/puppet/statechange.rb index 4884026fe..de160d584 100644 --- a/lib/puppet/statechange.rb +++ b/lib/puppet/statechange.rb @@ -6,21 +6,40 @@ module Puppet # Handle all of the work around performing an actual change, # including calling 'sync' on the states and producing events. class StateChange - attr_accessor :is, :should, :type, :path, :state, :transaction, :changed + attr_accessor :is, :should, :type, :path, :state, :transaction, :changed, :proxy # The log file generated when this object was changed. attr_reader :report + + # Switch the goals of the state, thus running the change in reverse. + def backward + @state.should = @is + @state.retrieve + + unless defined? @transaction + raise Puppet::Error, + "StateChange '%s' tried to be executed outside of transaction" % + self + end + unless @state.insync? + @state.info "Backing %s" % self + return self.go + else + @state.debug "rollback is already in sync: %s vs. %s" % + [@state.is.inspect, @state.should.inspect] + return nil + end + end + + def changed? + self.changed + end def initialize(state) @state = state @path = [state.path,"change"].flatten @is = state.is - if state.insync? - raise Puppet::Error.new( - "Tried to create a change for in-sync state %s" % state.name - ) - end @should = state.should @changed = false @@ -29,20 +48,7 @@ module Puppet # Perform the actual change. This method can go either forward or # backward, and produces an event. def go - if @state.insync? - @state.info "Already in sync" - return nil - end - - if @state.noop - @state.log "is %s, should be %s (noop)" % - [state.is_to_s, state.should_to_s] - #@state.debug "%s is noop" % @state - return nil - end - - #@state.info "Is: %s, Should: %s" % - # [@state.is.inspect, @state.should.inspect] + return nil if skip? # The transaction catches any exceptions here. events = @state.sync @@ -60,24 +66,18 @@ module Puppet return events.collect { |event| # default to a simple event type - if ! event.is_a?(Symbol) + unless event.is_a?(Symbol) @state.warning("State '%s' returned invalid event '%s'; resetting to default" % [@state.class,event]) event = @state.parent.class.name.id2name + "_changed" end - - # i should maybe include object type, but the event type - # should basically point to that, right? - #:state => @state, - #:object => @state.parent, + @report = @state.log(@state.change_to_s) Puppet::Event.new( :event => event, - :change => self, :transaction => @transaction, - :source => @state.parent, - :message => self.to_s + :source => self.source ) } end @@ -93,29 +93,28 @@ module Puppet return self.go end - - # Switch the goals of the state, thus running the change in reverse. - def backward - @state.should = @is - @state.retrieve - - unless defined? @transaction - raise Puppet::Error, - "StateChange '%s' tried to be executed outside of transaction" % - self + + def noop + return @state.noop + end + + def skip? + if @state.insync? + @state.info "Already in sync" + return true end - unless @state.insync? - @state.info "Backing %s" % self - return self.go - else - @state.debug "rollback is already in sync: %s vs. %s" % - [@state.is.inspect, @state.should.inspect] - return nil + + if @state.noop + @state.log "is %s, should be %s (noop)" % + [state.is_to_s, state.should_to_s] + #@state.debug "%s is noop" % @state + return true end + return false end - def noop - return @state.noop + def source + self.proxy || @state.parent end def to_s diff --git a/lib/puppet/transaction.rb b/lib/puppet/transaction.rb index 2feb3e27d..a7862aea2 100644 --- a/lib/puppet/transaction.rb +++ b/lib/puppet/transaction.rb @@ -47,6 +47,7 @@ class Transaction # And then return return [] end + changes = [changes] unless changes.is_a?(Array) # If a resource is going to be deleted but it still has dependencies, then # don't delete it unless it's implicit. @@ -79,6 +80,7 @@ class Transaction if Puppet[:trace] puts detail.backtrace end + puts detail change.state.err "change from %s to %s failed: %s" % [change.state.is_to_s, change.state.should_to_s, detail] @failures[resource] += 1 @@ -134,27 +136,13 @@ class Transaction # child resource. def copy_relationships(resource, children) depthfirst = resource.depthfirst? - - # We only have to worry about dependents, because any dependencies - # already missed their chance to register an event for us. - #dependents = @relgraph.adjacent(resource, - # :direction => :out, :type => :edges).reject { |e| ! e.callback } - - #sources = @relgraph.adjacent(resource, - # :direction => :in, :type => :edges).reject { |e| ! e.callback } - + children.each do |gen_child| if depthfirst @relgraph.add_edge!(gen_child, resource) else @relgraph.add_edge!(resource, gen_child) end - #dependents.each do |edge| - # @relgraph.add_edge!(gen_child, edge.target, edge.label) - #end - #sources.each do |edge| - # @relgraph.add_edge!(edge.source, gen_child, edge.label) - #end end end @@ -189,11 +177,13 @@ class Transaction else @resourcemetrics[:scheduled] += 1 + changecount = @changes.length + # We need to generate first regardless, because the recursive # actions sometimes change how the top resource is applied. children = eval_generate(resource) - if resource.depthfirst? and children + if children and resource.depthfirst? children.each do |child| # The child will never be skipped when the parent isn't events += eval_resource(child, false) @@ -205,11 +195,19 @@ class Transaction events += apply(resource) end - if ! resource.depthfirst? and children + if children and ! resource.depthfirst? children.each do |child| - events += eval_resource(child) + events += eval_resource(child, false) end end + + # A bit of hackery here -- if skipcheck is true, then we're the top-level + # resource. If that's the case, then make sure all of the changes list this resource + # as a proxy. This is really only necessary for rollback, since we know the generating + # resource during forward changes. + if children and checkskip + @changes[changecount..-1].each { |change| change.proxy = resource } + end # Keep track of how long we spend in each type of resource @timemetrics[resource.class.name] += seconds @@ -220,8 +218,10 @@ class Transaction events += triggedevents end - # Collect the targets of any subscriptions to those events - @relgraph.matching_edges(resource, events).each do |edge| + # Collect the targets of any subscriptions to those events. We pass the parent resource + # in so it will override the source in the events, since eval_generated children can't + # have direct relationships. + @relgraph.matching_edges(events, resource).each do |edge| @targets[edge.target] << edge end @@ -360,6 +360,7 @@ class Transaction end @report = Report.new + @count = 0 end # Prefetch any providers that support it. We don't support prefetching @@ -507,7 +508,6 @@ class Transaction def skip?(resource) skip = false if ! tagged?(resource) - p resource.tags resource.debug "Not tagged with %s" % tags.join(", ") elsif ! scheduled?(resource) resource.debug "Not scheduled" @@ -604,8 +604,7 @@ class Transaction trigged << Puppet::Event.new( :event => :triggered, :transaction => self, - :source => obj, - :message => message + :source => obj ) triggered(obj, callback) diff --git a/test/client/master.rb b/test/client/master.rb index 987649a74..5b34e6a7e 100755 --- a/test/client/master.rb +++ b/test/client/master.rb @@ -427,6 +427,23 @@ end assert_equal(Process.uid, File.stat(destfile).uid) end end + + # Test retrieving all of the facts. + def test_facts + facts = nil + assert_nothing_raised do + facts = Puppet::Client::MasterClient.facts + end + Facter.to_hash.each do |fact, value| + assert_equal(facts[fact.downcase], value, "%s is not equal" % fact.inspect) + end + + # Make sure the puppet version got added + assert_equal(Puppet::PUPPETVERSION, facts["clientversion"], "client version did not get added") + + # And make sure the ruby version is in there + assert_equal(RUBY_VERSION, facts["rubyversion"], "ruby version did not get added") + end end # $Id$ diff --git a/test/language/ast.rb b/test/language/ast.rb index 3fb6f5c0d..ffec7e66a 100755 --- a/test/language/ast.rb +++ b/test/language/ast.rb @@ -16,26 +16,7 @@ class TestAST < Test::Unit::TestCase include PuppetTest::RailsTesting include PuppetTest::ParserTesting include PuppetTest::ResourceTesting - - # A fake class that we can use for testing evaluation. - class FakeAST - attr_writer :evaluate - - def evaluate(*args) - return @evaluate - end - - def initialize(val = nil) - if val - @evaluate = val - end - end - - def safeevaluate(*args) - evaluate() - end - end - + if defined? ActiveRecord # Verify that our collection stuff works. def test_collection diff --git a/test/language/ast/casestatement.rb b/test/language/ast/casestatement.rb new file mode 100755 index 000000000..493474909 --- /dev/null +++ b/test/language/ast/casestatement.rb @@ -0,0 +1,67 @@ +#!/usr/bin/env ruby +# +# Created by Luke A. Kanies on 2006-12-22. +# Copyright (c) 2006. All rights reserved. + +$:.unshift("../lib").unshift("../../lib") if __FILE__ =~ /\.rb$/ + +require 'puppettest' +require 'puppettest/parsertesting' + +class TestCaseStatement < Test::Unit::TestCase + include PuppetTest + include PuppetTest::ParserTesting + AST = Puppet::Parser::AST + + class ActiveAST < FakeAST + def self.clear + $evaluated = [] + end + def evaluate + $evaluated ||= [] + $evaluated << @evaluate + end + end + + def test_evaluate + ast = nil + scope = mkscope + param = nameobj("MyParam") + + hash = { + "myparam" => ActiveAST.new("lower"), + "MyParam" => ActiveAST.new("upper"), + true => ActiveAST.new(true) + } + options = ["myparam", "MyParam"].collect do |p| + AST::CaseOpt.new(:value => FakeAST.new(p), :statements => hash[p]) + end + assert_nothing_raised do + ast = AST::CaseStatement.new(:test => param, :options => options) + end + + # Start out case-sensitive + Puppet[:casesensitive] = true + + result = nil + assert_nothing_raised do + result = ast.evaluate :scope => scope + end + assert(result, "did not get valid result") + assert_equal(["upper"], $evaluated, "Did not match case-sensitively") + assert(! hash["myparam"].evaluated?, "lower value was evaluated even though it did not match") + + # Now try it case-insensitive + Puppet[:casesensitive] = false + $evaluated.clear + hash["MyParam"].reset + assert_nothing_raised do + result = ast.evaluate :scope => scope + end + assert(result, "did not get valid result") + assert_equal(["lower"], result, "Did not match case-insensitively") + assert(! hash["MyParam"].evaluated?, "upper value was evaluated even though it did not match") + end +end + +# $Id$
\ No newline at end of file diff --git a/test/language/ast/selector.rb b/test/language/ast/selector.rb new file mode 100755 index 000000000..343134348 --- /dev/null +++ b/test/language/ast/selector.rb @@ -0,0 +1,53 @@ +#!/usr/bin/env ruby +# +# Created by Luke A. Kanies on 2006-12-22. +# Copyright (c) 2006. All rights reserved. + +$:.unshift("../lib").unshift("../../lib") if __FILE__ =~ /\.rb$/ + +require 'puppettest' +require 'puppettest/parsertesting' + +class TestSelector < Test::Unit::TestCase + include PuppetTest + include PuppetTest::ParserTesting + AST = Puppet::Parser::AST + + def test_evaluate + sel = nil + scope = mkscope + param = nameobj("MyParam") + + hash = { + "myparam" => FakeAST.new("lower"), + "MyParam" => FakeAST.new("upper") + } + values = ["myparam", "MyParam"].collect do |p| + AST::ResourceParam.new(:param => FakeAST.new(p), :value => hash[p]) + end + assert_nothing_raised do + sel = AST::Selector.new(:param => param, :values => values) + end + + # Start out case-sensitive + Puppet[:casesensitive] = true + + result = nil + assert_nothing_raised do + result = sel.evaluate :scope => scope + end + assert_equal("upper", result, "Did not match case-sensitively") + assert(! hash["myparam"].evaluated?, "lower value was evaluated even though it did not match") + + # Now try it case-insensitive + Puppet[:casesensitive] = false + hash["MyParam"].reset + assert_nothing_raised do + result = sel.evaluate :scope => scope + end + assert_equal("lower", result, "Did not match case-insensitively") + assert(! hash["MyParam"].evaluated?, "upper value was evaluated even though it did not match") + end +end + +# $Id$
\ No newline at end of file diff --git a/test/language/interpreter.rb b/test/language/interpreter.rb index a19bec288..e82461d1b 100755 --- a/test/language/interpreter.rb +++ b/test/language/interpreter.rb @@ -610,7 +610,35 @@ class TestInterpreter < Test::Unit::TestCase end assert(klass, "Did not return class with no code") assert_nil(interp.findclass("", "nocode3").code) - + end + + # Make sure class, node, and define methods are case-insensitive + def test_structure_case_insensitivity + interp = mkinterp + + result = nil + assert_nothing_raised do + result = interp.newclass "Yayness" + end + assert_equal(result, interp.findclass("", "yayNess")) + + assert_nothing_raised do + result = interp.newdefine "FunTest" + end + assert_equal(result, interp.finddefine("", "fUntEst"), + "%s was not matched" % "fUntEst") + + assert_nothing_raised do + result = interp.newnode("MyNode").shift + end + assert_equal(result, interp.nodesearch("mYnOde"), + "mYnOde was not matched") + + assert_nothing_raised do + result = interp.newnode("YayTest.Domain.Com").shift + end + assert_equal(result, interp.nodesearch("yaYtEst.domAin.cOm"), + "yaYtEst.domAin.cOm was not matched") end # Now make sure we get appropriate behaviour with parent class conflicts. diff --git a/test/language/node.rb b/test/language/node.rb index 3f5ff5561..ccdc77fda 100755 --- a/test/language/node.rb +++ b/test/language/node.rb @@ -58,8 +58,8 @@ class TestParser < Test::Unit::TestCase # Now make sure we can look up each of the names hostnames.each do |name| - assert(interp.nodesearch_code(name), - "Could not find node %s" % name) + assert(interp.nodesearch(name), + "Could not find node %s" % name.inspect) end end diff --git a/test/lib/puppettest.rb b/test/lib/puppettest.rb index e83cc9a04..67d1a29c9 100755 --- a/test/lib/puppettest.rb +++ b/test/lib/puppettest.rb @@ -55,6 +55,15 @@ module PuppetTest ENV["RUBYLIB"] = curlibs.join(":") end + + def logcollector + collector = [] + Puppet::Log.newdestination(collector) + cleanup do + Puppet::Log.close(collector) + end + collector + end def rake? $0 =~ /rake_test_loader/ diff --git a/test/lib/puppettest/parsertesting.rb b/test/lib/puppettest/parsertesting.rb index c7d4ec961..f71ba6b82 100644 --- a/test/lib/puppettest/parsertesting.rb +++ b/test/lib/puppettest/parsertesting.rb @@ -5,6 +5,34 @@ module PuppetTest::ParserTesting include PuppetTest AST = Puppet::Parser::AST + # A fake class that we can use for testing evaluation. + class FakeAST + attr_writer :evaluate + + def evaluated? + defined? @evaluated and @evaluated + end + + def evaluate(*args) + @evaluated = true + return @evaluate + end + + def initialize(val = nil) + if val + @evaluate = val + end + end + + def reset + @evaluated = nil + end + + def safeevaluate(*args) + evaluate() + end + end + def astarray(*args) AST::ASTArray.new( :children => args diff --git a/test/other/statechange.rb b/test/other/statechange.rb new file mode 100755 index 000000000..09295a5f0 --- /dev/null +++ b/test/other/statechange.rb @@ -0,0 +1,111 @@ +#!/usr/bin/env ruby +# +# Created by Luke A. Kanies on 2006-12-21. +# Copyright (c) 2006. All rights reserved. + +$:.unshift("../lib").unshift("../../lib") if __FILE__ =~ /\.rb$/ + +require 'puppettest' + +class TestStateChange < Test::Unit::TestCase + include PuppetTest + class FakeState + attr_accessor :is, :should, :parent + def change_to_s + "fake change" + end + def insync? + @is == @should + end + def log(msg) + Puppet::Log.create( + :level => :info, + :source => self, + :message => msg + ) + end + def noop + false + end + def path + "fakechange" + end + def sync + if insync? + return nil + else + @is = @should + return :fake_change + end + end + def to_s + path + end + end + + def mkchange + state = FakeState.new + state.is = :start + state.should = :finish + state.parent = :parent + change = nil + assert_nothing_raised do + change = Puppet::StateChange.new(state) + end + change.transaction = :trans + + assert_equal(:start, change.is, "@is did not get copied") + assert_equal(:finish, change.should, "@should did not get copied") + assert_equal(%w{fakechange change}, change.path, "path did not get set correctly") + + assert(! change.changed?, "change defaulted to already changed") + + return change + end + + def test_go + change = mkchange + + coll = logcollector() + + events = nil + # First make sure we get an immediate return + assert_nothing_raised do + events = change.go + end + assert_instance_of(Array, events, "events were not returned in an array") + assert_instance_of(Puppet::Event, events[0], "event array did not contain events") + + event = events.shift + {:event => :fake_change, :transaction => :trans, :source => :parent}.each do |method, val| + assert_equal(val, event.send(method), "Event did not set %s correctly" % method) + end + + assert(coll.detect { |l| l.message == "fake change" }, "Did not log change") + assert_equal(change.state.is, change.state.should, "did not call sync method") + + # Now make sure that proxy sources can be set. + assert_nothing_raised do + change.proxy = :other + end + # Reset, so we change again + change.state.is = :start + change.is = :start + assert_nothing_raised do + events = change.go + end + + assert_instance_of(Array, events, "events were not returned in an array") + assert_instance_of(Puppet::Event, events[0], "event array did not contain events") + + event = events.shift + {:event => :fake_change, :transaction => :trans, :source => :other}.each do |method, val| + assert_equal(val, event.send(method), "Event did not set %s correctly" % method) + end + + assert(coll.detect { |l| l.message == "fake change" }, "Did not log change") + assert_equal(change.state.is, change.state.should, "did not call sync method") + end +end + +# $Id$
\ No newline at end of file diff --git a/test/other/transactions.rb b/test/other/transactions.rb index 32a6b4acd..1b9257dec 100755 --- a/test/other/transactions.rb +++ b/test/other/transactions.rb @@ -26,6 +26,27 @@ class TestTransactions < Test::Unit::TestCase return type end + + # Create a new type that generates instances with shorter names. + def mkreducer(&block) + type = mkgenerator() do + def eval_generate + ret = [] + if title.length > 1 + ret << self.class.create(:title => title[0..-2]) + else + return nil + end + ret + end + end + + if block + type.class_eval(&block) + end + + return type + end def test_reports path1 = tempfile() @@ -253,26 +274,6 @@ class TestTransactions < Test::Unit::TestCase } end - # start a service, and then roll the modification back - # Disabled, because it wasn't really worth the effort. - def disabled_test_servicetrans - transaction = nil - service = newservice() - - component = newcomp("service",service) - - assert_nothing_raised() { - service[:ensure] = 1 - } - service.retrieve - assert(service.insync?, "Service did not start") - system("ps -ef | grep ruby") - trans = assert_events([:service_started], component) - service.retrieve - - assert_rollback_events(trans, [:service_stopped], "service") - end - # test that services are correctly restarted and that work is done # in the right order def test_refreshing @@ -607,17 +608,8 @@ class TestTransactions < Test::Unit::TestCase # Test mid-evaluation generation. def test_eval_generate $evaluated = [] - type = mkgenerator() do - def eval_generate - ret = [] - if title.length > 1 - ret << self.class.create(:title => title[0..-2]) - else - return nil - end - ret - end - + cleanup { $evaluated = nil } + type = mkreducer() do def evaluate $evaluated << self.title return [] @@ -644,14 +636,8 @@ class TestTransactions < Test::Unit::TestCase # Now make sure the appropriate relationships were added assert(trans.relgraph.edge?(yay, ya), "parent was not required by child") - assert(trans.relgraph.edge?(ya, rah), - "rah was not subscribed to ya") - - # And make sure the relationship is a subscription with a callback, - # not just a require. - assert_equal({:callback => :refresh, :event => :ALL_EVENTS}, - trans.relgraph[Puppet::Relationship.new(ya, rah)], - "The label was not retained") + assert(! trans.relgraph.edge?(ya, rah), + "generated child ya inherited depencency on rah") # Now make sure it in turn eval_generates appropriately assert_nothing_raised("failed to apply yay") do @@ -683,8 +669,6 @@ class TestTransactions < Test::Unit::TestCase # the generating resource assert(! trans.relgraph.edge?(yay, ra), "rah passed its dependencies on to its children") - assert(trans.relgraph.edge?(ya, rah), - "rah is not subscribed to ya") assert(! trans.relgraph.edge?(ya, ra), "children have a direct relationship") @@ -779,6 +763,57 @@ class TestTransactions < Test::Unit::TestCase assert(FileTest.exists?(path1), "required file was deleted") end + + # Make sure changes generated by eval_generated resources have proxies + # set to the top-level resource. + def test_proxy_resources + Struct.new("FakeEvalState", :path, :is, :should, :name) + Struct::FakeEvalState.send(:define_method, :insync?) { true } + Struct::FakeEvalState.send(:define_method, :info) { |*args| false } + + + type = mkreducer do + def evaluate + return Puppet::StateChange.new(Struct::FakeEvalState.new(:path, :is, :should, self.name)) + end + end + + resource = type.create :name => "test" + comp = newcomp(resource) + trans = comp.evaluate + trans.prepare + + assert_nothing_raised do + trans.eval_resource(resource) + end + + changes = trans.instance_variable_get("@changes") + + assert(changes.length > 0, "did not get any changes") + + changes.each do |change| + assert_equal(resource, change.source, "change did not get proxy set correctly") + end + end + + # Make sure changes in contained files still generate callback events. + def test_generated_callbacks + dir = tempfile() + maker = tempfile() + Dir.mkdir(dir) + file = File.join(dir, "file") + File.open(file, "w") { |f| f.puts "" } + File.chmod(0644, file) + File.chmod(0755, dir) # So only the child file causes a change + + dirobj = Puppet::Type.type(:file).create :mode => "755", :recurse => true, :path => dir + exec = Puppet::Type.type(:exec).create :title => "make", + :command => "touch #{maker}", :path => ENV['PATH'], :refreshonly => true, + :subscribe => dirobj + + assert_apply(dirobj, exec) + assert(FileTest.exists?(maker), "Did not make callback file") + end end # $Id$ |
