summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorluke <luke@980ebf18-57e1-0310-9a29-db15c13687c0>2006-12-23 04:49:56 +0000
committerluke <luke@980ebf18-57e1-0310-9a29-db15c13687c0>2006-12-23 04:49:56 +0000
commit9bb5c50d0b30b4dfb82b6b705dfcbf0e126a9d61 (patch)
treee41fbeb90e4050ea2af6d37e7b443cf9f84be162
parentbe711d357857f5e6d4a28a22bd60dd89e9e136c0 (diff)
downloadpuppet-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.rb2
-rw-r--r--lib/puppet/event.rb16
-rw-r--r--lib/puppet/log.rb13
-rw-r--r--lib/puppet/parser/ast.rb14
-rw-r--r--lib/puppet/parser/ast/casestatement.rb11
-rw-r--r--lib/puppet/parser/ast/selector.rb10
-rw-r--r--lib/puppet/parser/interpreter.rb12
-rw-r--r--lib/puppet/pgraph.rb13
-rw-r--r--lib/puppet/statechange.rb95
-rw-r--r--lib/puppet/transaction.rb45
-rwxr-xr-xtest/client/master.rb17
-rwxr-xr-xtest/language/ast.rb21
-rwxr-xr-xtest/language/ast/casestatement.rb67
-rwxr-xr-xtest/language/ast/selector.rb53
-rwxr-xr-xtest/language/interpreter.rb30
-rwxr-xr-xtest/language/node.rb4
-rwxr-xr-xtest/lib/puppettest.rb9
-rw-r--r--test/lib/puppettest/parsertesting.rb28
-rwxr-xr-xtest/other/statechange.rb111
-rwxr-xr-xtest/other/transactions.rb117
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$