#!/usr/bin/env ruby
$:.unshift("../lib").unshift("../../lib") if __FILE__ =~ /\.rb$/
require 'puppet'
require 'puppettest'
require 'mocha'
require 'puppettest/support/resources'
# $Id: transactions.rb 2372 2007-03-30 00:47:03Z luke $
class TestTransactions < Test::Unit::TestCase
include PuppetTest::FileTesting
include PuppetTest::Support::Resources
class Fakeprop <Puppet::Type::Property
attr_accessor :path, :is, :should, :name
def should_to_s
@should.to_s
end
def insync?
true
end
def info(*args)
false
end
end
def mkgenerator(&block)
$finished = []
cleanup { $finished = nil }
# Create a bogus type that generates new instances with shorter
type = Puppet::Type.newtype(:generator) do
newparam(:name, :namevar => true)
def finish
$finished << self.name
end
end
if block
type.class_eval(&block)
end
cleanup do
Puppet::Type.rmtype(:generator)
end
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()
path2 = tempfile()
objects = []
objects << Puppet::Type.newfile(
:path => path1,
:content => "yayness"
)
objects << Puppet::Type.newfile(
:path => path2,
:content => "booness"
)
trans = assert_events([:file_created, :file_created], *objects)
report = nil
assert_nothing_raised {
report = trans.generate_report
}
# First test the report logs
assert(report.logs.length > 0, "Did not get any report logs")
report.logs.each do |obj|
assert_instance_of(Puppet::Util::Log, obj)
end
# Then test the metrics
metrics = report.metrics
assert(metrics, "Did not get any metrics")
assert(metrics.length > 0, "Did not get any metrics")
assert(metrics.has_key?("resources"), "Did not get object metrics")
assert(metrics.has_key?("changes"), "Did not get change metrics")
metrics.each do |name, metric|
assert_instance_of(Puppet::Util::Metric, metric)
end
end
def test_prefetch
# Create a type just for testing prefetch
name = :prefetchtesting
$prefetched = false
type = Puppet::Type.newtype(name) do
newparam(:name) {}
end
cleanup do
Puppet::Type.rmtype(name)
end
# Now create a provider
type.provide(:prefetch) do
def self.prefetch
$prefetched = true
end
end
# Now create an instance
inst = type.create :name => "yay"
# Create a transaction
trans = Puppet::Transaction.new(newcomp(inst))
# Make sure prefetch works
assert_nothing_raised do
trans.prefetch
end
assert_equal(true, $prefetched, "type prefetch was not called")
# Now make sure it gets called from within evaluate()
$prefetched = false
assert_nothing_raised do
trans.evaluate
end
assert_equal(true, $prefetched, "evaluate did not call prefetch")
end
def test_refreshes_generate_events
path = tempfile()
firstpath = tempfile()
secondpath = tempfile()
file = Puppet::Type.newfile(:title => "file", :path => path, :content => "yayness")
first = Puppet::Type.newexec(:title => "first",
:command => "/bin/echo first > #{firstpath}",
:subscribe => [:file, path],
:refreshonly => true
)
second = Puppet::Type.newexec(:title => "second",
:command => "/bin/echo second > #{secondpath}",
:subscribe => [:exec, "first"],
:refreshonly => true
)
assert_apply(file, first, second)
assert(FileTest.exists?(secondpath), "Refresh did not generate an event")
end
unless %x{groups}.chomp.split(/ /).length > 1
$stderr.puts "You must be a member of more than one group to test transactions"
else
def ingroup(gid)
require 'etc'
begin
group = Etc.getgrgid(gid)
rescue => detail
puts "Could not retrieve info for group %s: %s" % [gid, detail]
return nil
end
return @groups.include?(group.name)
end
def setup
super
@groups = %x{groups}.chomp.split(/ /)
unless @groups.length > 1
p @groups
raise "You must be a member of more than one group to test this"
end
end
def newfile(hash = {})
tmpfile = tempfile()
File.open(tmpfile, "w") { |f| f.puts rand(100) }
# XXX now, because os x apparently somehow allows me to make a file
# owned by a group i'm not a member of, i have to verify that
# the file i just created is owned by one of my groups
# grrr
unless ingroup(File.stat(tmpfile).gid)
Puppet.info "Somehow created file in non-member group %s; fixing" %
File.stat(tmpfile).gid
require 'etc'
firstgr = @groups[0]
unless firstgr.is_a?(Integer)
str = Etc.getgrnam(firstgr)
firstgr = str.gid
end
File.chown(nil, firstgr, tmpfile)
end
hash[:name] = tmpfile
assert_nothing_raised() {
return Puppet.type(:file).create(hash)
}
end
def newexec(file)
assert_nothing_raised() {
return Puppet.type(:exec).create(
:name => "touch %s" % file,
:path => "/bin:/usr/bin:/sbin:/usr/sbin",
:returns => 0
)
}
end
# modify a file and then roll the modifications back
def test_filerollback
transaction = nil
file = newfile()
properties = {}
check = [:group,:mode]
file[:check] = check
assert_nothing_raised() {
file.retrieve
}
assert_nothing_raised() {
check.each { |property|
assert(file[property])
properties[property] = file[property]
}
}
component = newcomp("file",file)
require 'etc'
groupname = Etc.getgrgid(File.stat(file.name).gid).name
assert_nothing_raised() {
# Find a group that it's not set to
group = @groups.find { |group| group != groupname }
unless group
raise "Could not find suitable group"
end
file[:group] = group
file[:mode] = "755"
}
trans = assert_events([:file_changed, :file_changed], component)
file.retrieve
assert_rollback_events(trans, [:file_changed, :file_changed], "file")
assert_nothing_raised() {
file.retrieve
}
properties.each { |property,value|
assert_equal(
value,file.is(property), "File %s remained %s" % [property, file.is(property)]
)
}
end
# test that services are correctly restarted and that work is done
# in the right order
def test_refreshing
transaction = nil
file = newfile()
execfile = File.join(tmpdir(), "exectestingness")
exec = newexec(execfile)
properties = {}
check = [:group,:mode]
file[:check] = check
file[:group] = @groups[0]
assert_apply(file)
@@tmpfiles << execfile
component = newcomp("both",file,exec)
# 'subscribe' expects an array of arrays
exec[:subscribe] = [[file.class.name,file.name]]
exec[:refreshonly] = true
assert_nothing_raised() {
file.retrieve
exec.retrieve
}
check.each { |property|
properties[property] = file[property]
}
assert_nothing_raised() {
file[:mode] = "755"
}
trans = assert_events([:file_changed, :triggered], component)
assert(FileTest.exists?(execfile), "Execfile does not exist")
File.unlink(execfile)
assert_nothing_raised() {
file[:group] = @groups[1]
}
trans = assert_events([:file_changed, :triggered], component)
assert(FileTest.exists?(execfile), "Execfile does not exist")
end
# Verify that one component requiring another causes the contained
# resources in the requiring component to get refreshed.
def test_refresh_across_two_components
transaction = nil
file = newfile()
execfile = File.join(tmpdir(), "exectestingness2")
@@tmpfiles << execfile
exec = newexec(execfile)
properties = {}
check = [:group,:mode]
file[:check] = check
file[:group] = @groups[0]
assert_apply(file)
fcomp = newcomp("file",file)
ecomp = newcomp("exec",exec)
component = newcomp("both",fcomp,ecomp)
# 'subscribe' expects an array of arrays
#component[:require] = [[file.class.name,file.name]]
ecomp[:subscribe] = fcomp
exec[:refreshonly] = true
trans = assert_events([], component)
assert_nothing_raised() {
file[:group] = @groups[1]
file[:mode] = "755"
}
trans = assert_events([:file_changed, :file_changed, :triggered], component)
end
# Make sure that multiple subscriptions get triggered.
def test_multisubs
path = tempfile()
file1 = tempfile()
file2 = tempfile()
file = Puppet.type(:file).create(
:path => path,
:ensure => "file"
)
exec1 = Puppet.type(:exec).create(
:path => ENV["PATH"],
:command => "touch %s" % file1,
:refreshonly => true,
:subscribe => [:file, path]
)
exec2 = Puppet.type(:exec).create(
:path => ENV["PATH"],
:command => "touch %s" % file2,
:refreshonly => true,
:subscribe => [:file, path]
)
assert_apply(file, exec1, exec2)
assert(FileTest.exists?(file1), "File 1 did not get created")
assert(FileTest.exists?(file2), "File 2 did not get created")
end
# Make sure that a failed trigger doesn't result in other events not
# getting triggered.
def test_failedrefreshes
path = tempfile()
newfile = tempfile()
file = Puppet.type(:file).create(
:path => path,
:ensure => "file"
)
exec1 = Puppet.type(:exec).create(
:path => ENV["PATH"],
:command => "touch /this/cannot/possibly/exist",
:logoutput => true,
:refreshonly => true,
:subscribe => file,
:title => "one"
)
exec2 = Puppet.type(:exec).create(
:path => ENV["PATH"],
:command => "touch %s" % newfile,
:logoutput => true,
:refreshonly => true,
:subscribe => [file, exec1],
:title => "two"
)
assert_apply(file, exec1, exec2)
assert(FileTest.exists?(newfile), "Refresh file did not get created")
end
# Make sure that unscheduled and untagged objects still respond to events
def test_unscheduled_and_untagged_response
Puppet::Type.type(:schedule).mkdefaultschedules
Puppet[:ignoreschedules] = false
file = Puppet.type(:file).create(
:name => tempfile(),
:ensure => "file"
)
fname = tempfile()
exec = Puppet.type(:exec).create(
:name => "touch %s" % fname,
:path => "/usr/bin:/bin",
:schedule => "monthly",
:subscribe => ["file", file.name]
)
comp = newcomp(file,exec)
comp.finalize
# Run it once
assert_apply(comp)
assert(FileTest.exists?(fname), "File did not get created")
assert(!exec.scheduled?, "Exec is somehow scheduled")
# Now remove it, so it can get created again
File.unlink(fname)
file[:content] = "some content"
assert_events([:file_changed, :triggered], comp)
assert(FileTest.exists?(fname), "File did not get recreated")
# Now remove it, so it can get created again
File.unlink(fname)
# And tag our exec
exec.tag("testrun")
# And our file, so it runs
file.tag("norun")
Puppet[:tags] = "norun"
file[:content] = "totally different content"
assert(! file.insync?, "Uh, file is in sync?")
assert_events([:file_changed, :triggered], comp)
assert(FileTest.exists?(fname), "File did not get recreated")
end
def test_failed_reqs_mean_no_run
exec = Puppet::Type.type(:exec).create(
:command => "/bin/mkdir /this/path/cannot/possibly/exit",
:title => "mkdir"
)
file1 = Puppet::Type.type(:file).create(
:title => "file1",
:path => tempfile(),
:require => exec,
:ensure => :file
)
file2 = Puppet::Type.type(:file).create(
:title => "file2",
:path => tempfile(),
:require => file1,
:ensure => :file
)
comp = newcomp(exec, file1, file2)
comp.finalize
assert_apply(comp)
assert(! FileTest.exists?(file1[:path]),
"File got created even tho its dependency failed")
assert(! FileTest.exists?(file2[:path]),
"File got created even tho its deep dependency failed")
end
end
def f(n)
Puppet::Type.type(:file)["/tmp/#{n.to_s}"]
end
def test_relationship_graph
one, two, middle, top = mktree
{one => two, "f" => "c", "h" => middle}.each do |source, target|
if source.is_a?(String)
source = f(source)
end
if target.is_a?(String)
target = f(target)
end
target[:require] = source
end
trans = Puppet::Transaction.new(top)
graph = nil
assert_nothing_raised do
graph = trans.relationship_graph
end
assert_instance_of(Puppet::PGraph, graph,
"Did not get relationship graph")
# Make sure all of the components are gone
comps = graph.vertices.find_all { |v| v.is_a?(Puppet::Type::Component)}
assert(comps.empty?, "Deps graph still contains components %s" %
comps.collect { |c| c.ref }.join(","))
assert_equal([], comps, "Deps graph still contains components")
# It must be reversed because of how topsort works
sorted = graph.topsort.reverse
# Now make sure the appropriate edges are there and are in the right order
assert(graph.dependents(f(:f)).include?(f(:c)),
"c not marked a dep of f")
assert(sorted.index(f(:c)) < sorted.index(f(:f)),
"c is not before f")
one.each do |o|
two.each do |t|
assert(graph.dependents(o).include?(t),
"%s not marked a dep of %s" % [t.ref, o.ref])
assert(sorted.index(t) < sorted.index(o),
"%s is not before %s" % [t.ref, o.ref])
end
end
trans.resources.leaves(middle).each do |child|
assert(graph.dependents(f(:h)).include?(child),
"%s not marked a dep of h" % [child.ref])
assert(sorted.index(child) < sorted.index(f(:h)),
"%s is not before h" % child.ref)
end
# Lastly, make sure our 'g' vertex made it into the relationship
# graph, since it's not involved in any relationships.
assert(graph.vertex?(f(:g)),
"Lost vertexes with no relations")
# Now make the reversal graph and make sure all of the vertices made it into that
reverse = graph.reversal
%w{a b c d e f g h}.each do |letter|
file = f(letter)
assert(reverse.vertex?(file), "%s did not make it into reversal" % letter)
end
end
# Test pre-evaluation generation
def test_generate
mkgenerator() do
def generate
ret = []
if title.length > 1
ret << self.class.create(:title => title[0..-2])
else
return nil
end
ret
end
end
yay = Puppet::Type.newgenerator :title => "yay"
rah = Puppet::Type.newgenerator :title => "rah"
comp = newcomp(yay, rah)
trans = comp.evaluate
assert_nothing_raised do
trans.generate
end
%w{ya ra y r}.each do |name|
assert(trans.resources.vertex?(Puppet::Type.type(:generator)[name]),
"Generated %s was not a vertex" % name)
assert($finished.include?(name), "%s was not finished" % name)
end
# Now make sure that cleanup gets rid of those generated types.
assert_nothing_raised do
trans.cleanup
end
%w{ya ra y r}.each do |name|
assert(!trans.resources.vertex?(Puppet::Type.type(:generator)[name]),
"Generated vertex %s was not removed from graph" % name)
assert_nil(Puppet::Type.type(:generator)[name],
"Generated vertex %s was not removed from class" % name)
end
end
# Test mid-evaluation generation.
def test_eval_generate
$evaluated = []
cleanup { $evaluated = nil }
type = mkreducer() do
def evaluate
$evaluated << self.title
return []
end
end
yay = Puppet::Type.newgenerator :title => "yay"
rah = Puppet::Type.newgenerator :title => "rah", :subscribe => yay
comp = newcomp(yay, rah)
trans = comp.evaluate
trans.prepare
# Now apply the resources, and make sure they appropriately generate
# things.
assert_nothing_raised("failed to apply yay") do
trans.eval_resource(yay)
end
ya = type["ya"]
assert(ya, "Did not generate ya")
assert(trans.relgraph.vertex?(ya),
"Did not add ya to rel_graph")
# 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),
"generated child ya inherited depencency on rah")
# Now make sure it in turn eval_generates appropriately
assert_nothing_raised("failed to apply yay") do
trans.eval_resource(type["ya"])
end
%w{y}.each do |name|
res = type[name]
assert(res, "Did not generate %s" % name)
assert(trans.relgraph.vertex?(res),
"Did not add %s to rel_graph" % name)
assert($finished.include?("y"), "y was not finished")
end
assert_nothing_raised("failed to eval_generate with nil response") do
trans.eval_resource(type["y"])
end
assert(trans.relgraph.edge?(yay, ya), "no edge was created for ya => yay")
assert_nothing_raised("failed to apply rah") do
trans.eval_resource(rah)
end
ra = type["ra"]
assert(ra, "Did not generate ra")
assert(trans.relgraph.vertex?(ra),
"Did not add ra to rel_graph" % name)
assert($finished.include?("ra"), "y was not finished")
# Now make sure this generated resource has the same relationships as
# the generating resource
assert(! trans.relgraph.edge?(yay, ra),
"rah passed its dependencies on to its children")
assert(! trans.relgraph.edge?(ya, ra),
"children have a direct relationship")
# Now make sure that cleanup gets rid of those generated types.
assert_nothing_raised do
trans.cleanup
end
%w{ya ra y r}.each do |name|
assert(!trans.relgraph.vertex?(type[name]),
"Generated vertex %s was not removed from graph" % name)
assert_nil(type[name],
"Generated vertex %s was not removed from class" % name)
end
# Now, start over and make sure that everything gets evaluated.
trans = comp.evaluate
$evaluated.clear
assert_nothing_raised do
trans.evaluate
end
assert_equal(%w{yay ya y rah ra r}, $evaluated,
"Not all resources were evaluated or not in the right order")
end
def test_tags
res = Puppet::Type.newfile :path => tempfile()
comp = newcomp(res)
# Make sure they default to none
assert_equal([], comp.evaluate.tags)
# Make sure we get the main tags
Puppet[:tags] = %w{this is some tags}
assert_equal(%w{this is some tags}, comp.evaluate.tags)
# And make sure they get processed correctly
Puppet[:tags] = ["one", "two,three", "four"]
assert_equal(%w{one two three four}, comp.evaluate.tags)
# lastly, make sure we can override them
trans = comp.evaluate
trans.tags = ["one", "two,three", "four"]
assert_equal(%w{one two three four}, comp.evaluate.tags)
end
def test_tagged?
res = Puppet::Type.newfile :path => tempfile()
comp = newcomp(res)
trans = comp.evaluate
assert(trans.tagged?(res), "tagged? defaulted to false")
# Now set some tags
trans.tags = %w{some tags}
# And make sure it's false
assert(! trans.tagged?(res), "matched invalid tags")
# Set ignoretags and make sure it sticks
trans.ignoretags = true
assert(trans.tagged?(res), "tags were not ignored")
# Now make sure we actually correctly match tags
res[:tag] = "mytag"
trans.ignoretags = false
trans.tags = %w{notag}
assert(! trans.tagged?(res), "tags incorrectly matched")
trans.tags = %w{mytag yaytag}
assert(trans.tagged?(res), "tags should have matched")
end
# Make sure changes generated by eval_generated resources have proxies
# set to the top-level resource.
def test_proxy_resources
type = mkreducer do
def evaluate
return Puppet::PropertyChange.new(Fakeprop.new(
:path => :path, :is => :is, :should => :should, :name => self.name, :parent => "a parent"))
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
# Yay, this out to be fun.
def test_trigger
$triggered = []
cleanup { $triggered = nil }
trigger = Class.new do
attr_accessor :name
include Puppet::Util::Logging
def initialize(name)
@name = name
end
def ref
self.name
end
def refresh
$triggered << self.name
end
def to_s
self.name
end
end
# Make a graph with some stuff in it.
graph = Puppet::PGraph.new
# Add a non-triggering edge.
a = trigger.new(:a)
b = trigger.new(:b)
c = trigger.new(:c)
nope = Puppet::Relationship.new(a, b)
yep = Puppet::Relationship.new(a, c, {:callback => :refresh})
graph.add_edge!(nope)
# And a triggering one.
graph.add_edge!(yep)
# Create our transaction
trans = Puppet::Transaction.new(graph)
# Set the non-triggering on
assert_nothing_raised do
trans.set_trigger(nope)
end
assert(! trans.targeted?(b), "b is incorrectly targeted")
# Now set the other
assert_nothing_raised do
trans.set_trigger(yep)
end
assert(trans.targeted?(c), "c is not targeted")
# Now trigger our three resources
assert_nothing_raised do
assert_nil(trans.trigger(a), "a somehow triggered something")
end
assert_nothing_raised do
assert_nil(trans.trigger(b), "b somehow triggered something")
end
assert_equal([], $triggered,"got something in triggered")
result = nil
assert_nothing_raised do
result = trans.trigger(c)
end
assert(result, "c did not trigger anything")
assert_instance_of(Array, result)
event = result.shift
assert_instance_of(Puppet::Event, event)
assert_equal(:triggered, event.event, "event was not set correctly")
assert_equal(c, event.source, "source was not set correctly")
assert_equal(trans, event.transaction, "transaction was not set correctly")
assert(trans.triggered?(c, :refresh),
"Transaction did not store the trigger")
end
def test_graph
Puppet.config.use(:puppet)
# Make a graph
graph = Puppet::PGraph.new
graph.add_edge!("a", "b")
# Create our transaction
trans = Puppet::Transaction.new(graph)
assert_nothing_raised do
trans.graph(graph, :testing)
end
dotfile = File.join(Puppet[:graphdir], "testing.dot")
assert(! FileTest.exists?(dotfile), "Enabled graphing even tho disabled")
# Now enable graphing
Puppet[:graph] = true
assert_nothing_raised do
trans.graph(graph, :testing)
end
assert(FileTest.exists?(dotfile), "Did not create graph.")
end
def test_created_graphs
FileUtils.mkdir_p(Puppet[:graphdir])
file = Puppet::Type.newfile(:path => tempfile, :content => "yay")
exec = Puppet::Type.type(:exec).create(:command => "echo yay", :path => ENV['PATH'],
:require => file)
Puppet[:graph] = true
assert_apply(file, exec)
%w{resources relationships expanded_relationships}.each do |name|
file = File.join(Puppet[:graphdir], "%s.dot" % name)
assert(FileTest.exists?(file), "graph for %s was not created" % name)
end
end
def test_set_target
file = Puppet::Type.newfile(:path => tempfile(), :content => "yay")
exec1 = Puppet::Type.type(:exec).create :command => "/bin/echo exec1"
exec2 = Puppet::Type.type(:exec).create :command => "/bin/echo exec2"
trans = Puppet::Transaction.new(newcomp(file, exec1, exec2))
# First try it with an edge that has no callback
edge = Puppet::Relationship.new(file, exec1)
assert_nothing_raised { trans.set_trigger(edge) }
assert(! trans.targeted?(exec1), "edge with no callback resulted in a target")
# Now with an edge that has an unsupported callback
edge = Puppet::Relationship.new(file, exec1, :callback => :nosuchmethod, :event => :ALL_EVENTS)
assert_nothing_raised { trans.set_trigger(edge) }
assert(! trans.targeted?(exec1), "edge with invalid callback resulted in a target")
# Lastly, with an edge with a supported callback
edge = Puppet::Relationship.new(file, exec1, :callback => :refresh, :event => :ALL_EVENTS)
assert_nothing_raised { trans.set_trigger(edge) }
assert(trans.targeted?(exec1), "edge with valid callback did not result in a target")
end
# Testing #401 -- transactions are calling refresh() on classes that don't support it.
def test_callback_availability
$called = []
klass = Puppet::Type.newtype(:norefresh) do
newparam(:name, :namevar => true) {}
def method_missing(method, *args)
$called << method
end
end
cleanup do
$called = nil
Puppet::Type.rmtype(:norefresh)
end
file = Puppet::Type.newfile :path => tempfile(), :content => "yay"
one = klass.create :name => "one", :subscribe => file
assert_apply(file, one)
assert(! $called.include?(:refresh), "Called refresh when it wasn't set as a method")
end
# Testing #437 - cyclic graphs should throw failures.
def test_fail_on_cycle
one = Puppet::Type.type(:exec).create(:name => "/bin/echo one")
two = Puppet::Type.type(:exec).create(:name => "/bin/echo two")
one[:require] = two
two[:require] = one
trans = newcomp(one, two).evaluate
assert_raise(Puppet::Error) do
trans.prepare
end
end
def test_errors_during_generation
type = Puppet::Type.newtype(:failer) do
newparam(:name) {}
def eval_generate
raise ArgumentError, "Invalid value"
end
def generate
raise ArgumentError, "Invalid value"
end
end
cleanup { Puppet::Type.rmtype(:failer) }
obj = type.create(:name => "testing")
assert_apply(obj)
end
def test_self_refresh_causes_triggering
type = Puppet::Type.newtype(:refresher, :self_refresh => true) do
attr_accessor :refreshed, :testing
newparam(:name) {}
newproperty(:testing) do
def sync
self.is = self.should
:ran_testing
end
end
def refresh
@refreshed = true
end
end
cleanup { Puppet::Type.rmtype(:refresher)}
obj = type.create(:name => "yay", :testing => "cool")
assert(! obj.insync?, "fake object is already in sync")
# Now make sure it gets refreshed when the change happens
assert_apply(obj)
assert(obj.refreshed, "object was not refreshed during transaction")
end
# Testing #433
def test_explicit_dependencies_beat_automatic
# Create a couple of different resource sets that have automatic relationships and make sure the manual relationships win
rels = {}
# First users and groups
group = Puppet::Type.type(:group).create(:name => nonrootgroup.name, :ensure => :present)
user = Puppet::Type.type(:user).create(:name => nonrootuser.name, :ensure => :present, :gid => group.title)
# Now add the explicit relationship
group[:require] = user
rels[group] = user
# Now files
d = tempfile()
f = File.join(d, "file")
file = Puppet::Type.newfile(:path => f, :content => "yay")
dir = Puppet::Type.newfile(:path => d, :ensure => :directory, :require => file)
rels[dir] = file
rels.each do |after, before|
comp = newcomp(before, after)
trans = comp.evaluate
str = "from %s to %s" % [before, after]
assert_nothing_raised("Failed to create graph %s" % str) do
trans.prepare
end
graph = trans.relgraph
assert(graph.edge?(before, after), "did not create manual relationship %s" % str)
assert(! graph.edge?(after, before), "created automatic relationship %s" % str)
end
end
def test_labeled_deps_beat_unlabeled
one = Puppet::Type.type(:exec).create :command => "/bin/echo one"
two = Puppet::Type.type(:exec).create :command => "/bin/echo two"
one[:require] = two
one[:subscribe] = two
comp = newcomp(one, two)
trans = Puppet::Transaction.new(comp)
graph = trans.relationship_graph
label = graph.edge_label(two, one)
assert(label, "require beat subscribe")
assert_equal(:refresh, label[:callback],
"did not get correct callback from subscribe")
one.delete(:require)
one.delete(:subscribe)
two[:before] = one
two[:notify] = one
trans = Puppet::Transaction.new(comp)
graph = trans.relationship_graph
label = graph.edge_label(two, one)
assert(label, "before beat notify")
assert_equal(:refresh, label[:callback],
"did not get correct callback from notify")
end
# #542 - make sure resources in noop mode still notify their resources,
# so that users know if a service will get restarted.
def test_noop_with_notify
path = tempfile
epath = tempfile
spath = tempfile
file = Puppet::Type.newfile(:path => path, :ensure => :file,
:title => "file")
exec = Puppet::Type.type(:exec).create(:command => "touch %s" % epath,
:path => ENV["PATH"], :subscribe => file, :refreshonly => true,
:title => 'exec1')
exec2 = Puppet::Type.type(:exec).create(:command => "touch %s" % spath,
:path => ENV["PATH"], :subscribe => exec, :refreshonly => true,
:title => 'exec2')
Puppet[:noop] = true
assert(file.noop, "file not in noop")
assert(exec.noop, "exec not in noop")
@logs.clear
assert_apply(file, exec, exec2)
assert(! FileTest.exists?(path), "Created file in noop")
assert(! FileTest.exists?(epath), "Executed exec in noop")
assert(! FileTest.exists?(spath), "Executed second exec in noop")
assert(@logs.detect { |l|
l.message =~ /should be/ and l.source == file.property(:ensure).path},
"did not log file change")
assert(@logs.detect { |l|
l.message =~ /Would have/ and l.source == exec.path },
"did not log first exec trigger")
assert(@logs.detect { |l|
l.message =~ /Would have/ and l.source == exec2.path },
"did not log second exec trigger")
end
def test_only_stop_purging_with_relations
files = []
paths = []
3.times do |i|
path = tempfile
paths << path
file = Puppet::Type.newfile(:path => path, :ensure => :absent,
:backup => false, :title => "file%s" % i)
File.open(path, "w") { |f| f.puts "" }
files << file
end
files[0][:ensure] = :file
files[0][:require] = files[1..2]
# Mark the second as purging
files[1].purging
assert_apply(*files)
assert(FileTest.exists?(paths[1]), "Deleted required purging file")
assert(! FileTest.exists?(paths[2]), "Did not delete non-purged file")
end
end
# $Id: transactions.rb 2372 2007-03-30 00:47:03Z luke $
syntax highlighted by Code2HTML, v. 0.9.1