summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorluke <luke@980ebf18-57e1-0310-9a29-db15c13687c0>2006-12-09 16:40:50 +0000
committerluke <luke@980ebf18-57e1-0310-9a29-db15c13687c0>2006-12-09 16:40:50 +0000
commitcdd1e6e19e7b8fc340ebcf543a30564c76e71eb9 (patch)
tree65f7511a9f6386db44c92c502de1d56c7d2f0b6d
parent01e5b692f0ba209956f4b84c7e597bd867154cbf (diff)
downloadpuppet-cdd1e6e19e7b8fc340ebcf543a30564c76e71eb9.tar.gz
puppet-cdd1e6e19e7b8fc340ebcf543a30564c76e71eb9.tar.xz
puppet-cdd1e6e19e7b8fc340ebcf543a30564c76e71eb9.zip
Another intermediate commit. Most of the graphing work itself is now done, but I am in the middle of converting files to use the graphs and at the same time am writing some actually decent tests for the file recursion stuff.
git-svn-id: https://reductivelabs.com/svn/puppet/trunk@1899 980ebf18-57e1-0310-9a29-db15c13687c0
-rw-r--r--lib/puppet/metatype/container.rb5
-rw-r--r--lib/puppet/pgraph.rb3
-rwxr-xr-xlib/puppet/server/authstore.rb3
-rwxr-xr-xlib/puppet/server/fileserver.rb17
-rw-r--r--lib/puppet/transaction.rb224
-rw-r--r--lib/puppet/type/pfile.rb204
-rwxr-xr-xlib/puppet/type/pfile/ensure.rb1
-rwxr-xr-xlib/puppet/type/pfile/source.rb201
-rw-r--r--lib/puppet/type/pfile/target.rb2
-rw-r--r--lib/puppet/type/state.rb2
-rwxr-xr-xtest/other/transactions.rb204
-rwxr-xr-xtest/server/fileserver.rb5
-rwxr-xr-xtest/types/file.rb305
-rwxr-xr-xtest/types/filesources.rb308
14 files changed, 1078 insertions, 406 deletions
diff --git a/lib/puppet/metatype/container.rb b/lib/puppet/metatype/container.rb
index 9ed587a4c..d7c509699 100644
--- a/lib/puppet/metatype/container.rb
+++ b/lib/puppet/metatype/container.rb
@@ -80,10 +80,7 @@ class Puppet::Type
end
self.class.delete(self)
- if defined? @parent and @parent
- @parent.delete(self)
- @parent = nil
- end
+ @parent = nil
# Remove the reference to the provider.
if self.provider
diff --git a/lib/puppet/pgraph.rb b/lib/puppet/pgraph.rb
index 292e25073..58bee8605 100644
--- a/lib/puppet/pgraph.rb
+++ b/lib/puppet/pgraph.rb
@@ -11,6 +11,9 @@ require 'puppet/relationship'
# This class subclasses a graph class in order to handle relationships
# among resources.
class Puppet::PGraph < GRATR::Digraph
+ # This is the type used for splicing.
+ attr_accessor :container_type
+
# The dependencies for a given resource.
def dependencies(resource)
tree_from_vertex(resource, :dfs).keys
diff --git a/lib/puppet/server/authstore.rb b/lib/puppet/server/authstore.rb
index 3e7881162..b0f63b68a 100755
--- a/lib/puppet/server/authstore.rb
+++ b/lib/puppet/server/authstore.rb
@@ -28,6 +28,9 @@ class Server
def allowed?(name, ip)
if name or ip
+ # This is probably unnecessary, and can cause some weirdnesses in
+ # cases where we're operating over localhost but don't have a real
+ # IP defined.
unless name and ip
raise Puppet::DevError, "Name and IP must be passed to 'allowed?'"
end
diff --git a/lib/puppet/server/fileserver.rb b/lib/puppet/server/fileserver.rb
index 8033fac5b..53c60cdbe 100755
--- a/lib/puppet/server/fileserver.rb
+++ b/lib/puppet/server/fileserver.rb
@@ -126,6 +126,10 @@ class Server
sub.join("\t")
}.join("\n")
end
+
+ def local?
+ self.local
+ end
# Mount a new directory with a name.
def mount(path, name)
@@ -188,6 +192,11 @@ class Server
private
def authcheck(file, mount, client, clientip)
+ # If we're local, don't bother passing in information.
+ if local?
+ client = nil
+ clientip = nil
+ end
unless mount.allowed?(client, clientip)
mount.warning "%s cannot access %s" %
[client, file]
@@ -482,12 +491,6 @@ class Server
@path = nil
end
- @comp = Puppet.type(:component).create(
- :name => "mount[#{name}]"
- )
- #@comp.type = "mount"
- #@comp.name = name
-
super()
end
@@ -504,8 +507,6 @@ class Server
:name => path,
:check => CHECKPARAMS
)
-
- @comp.push(obj)
end
if links == :manage
diff --git a/lib/puppet/transaction.rb b/lib/puppet/transaction.rb
index 6835b1b19..5bd5afd2f 100644
--- a/lib/puppet/transaction.rb
+++ b/lib/puppet/transaction.rb
@@ -6,8 +6,10 @@ require 'puppet/statechange'
module Puppet
class Transaction
- attr_accessor :component, :resources, :tags, :ignoreschedules, :ignoretags
- attr_accessor :relgraph
+ attr_accessor :component, :resources, :ignoreschedules, :ignoretags
+ attr_accessor :relgraph, :sorted_resources
+
+ attr_writer :tags
include Puppet::Util
@@ -24,41 +26,42 @@ class Transaction
end
end
- # Apply all changes for a child, returning a list of the events
+ # Apply all changes for a resource, returning a list of the events
# generated.
- def apply(child)
- child.info "applying"
+ def apply(resource)
# First make sure there are no failed dependencies. To do this,
# we check for failures in any of the vertexes above us. It's not
# enough to check the immediate dependencies, which is why we use
# a tree from the reversed graph.
- p @relgraph.vertices.collect { |v| v.ref }
- @relgraph.reversal.tree_from_vertex(child, :dfs).keys.each do |dep|
+ @relgraph.reversal.tree_from_vertex(resource, :dfs).keys.each do |dep|
skip = false
if fails = failed?(dep)
- child.notice "Dependency %s[%s] has %s failures" %
+ resource.notice "Dependency %s[%s] has %s failures" %
[dep.class.name, dep.name, @failures[dep]]
skip = true
end
if skip
- child.warning "Skipping because of failed dependencies"
+ resource.warning "Skipping because of failed dependencies"
@resourcemetrics[:skipped] += 1
return []
end
end
+
+ # If the resource needs to generate new objects at eval time, do it now.
+ eval_generate(resource)
begin
- changes = child.evaluate
+ changes = resource.evaluate
rescue => detail
if Puppet[:trace]
puts detail.backtrace
end
- child.err "Failed to retrieve current state: %s" % detail
+ resource.err "Failed to retrieve current state: %s" % detail
# Mark that it failed
- @failures[child] += 1
+ @failures[resource] += 1
# And then return
return []
@@ -72,7 +75,7 @@ class Transaction
@resourcemetrics[:out_of_sync] += 1
end
- childevents = changes.collect { |change|
+ resourceevents = changes.collect { |change|
@changes << change
@count += 1
change.transaction = self
@@ -87,7 +90,7 @@ class Transaction
end
change.state.err "change from %s to %s failed: %s" %
[change.state.is_to_s, change.state.should_to_s, detail]
- @failures[child] += 1
+ @failures[resource] += 1
next
# FIXME this should support using onerror to determine
# behaviour; or more likely, the client calling us
@@ -106,15 +109,15 @@ class Transaction
unless changes.empty?
# Record when we last synced
- child.cache(:synced, Time.now)
+ resource.cache(:synced, Time.now)
# Flush, if appropriate
- if child.respond_to?(:flush)
- child.flush
+ if resource.respond_to?(:flush)
+ resource.flush
end
end
- childevents
+ resourceevents
end
# Find all of the changed resources.
@@ -123,75 +126,96 @@ class Transaction
change.state.parent
}.uniq
end
+
+ # Do any necessary cleanup. Basically just removes any generated
+ # resources.
+ def cleanup
+ @generated.each do |resource|
+ resource.remove
+ end
+ end
+
+ # See if the resource generates new resources at evaluation time.
+ def eval_generate(resource)
+ if resource.respond_to?(:eval_generate)
+ if children = resource.eval_generate
+ dependents = @relgraph.adjacent(resource, :direction => :out, :type => :edges)
+ targets = @relgraph.adjacent(resource, :direction => :in, :type => :edges)
+ children.each do |gen_child|
+ gen_child.info "generated"
+ @relgraph.add_edge!(resource, gen_child)
+ dependents.each do |edge|
+ @relgraph.add_edge!(gen_child, edge.target, edge.label)
+ end
+ targets.each do |edge|
+ @relgraph.add_edge!(edge.source, gen_child, edge.label)
+ end
+ @sorted_resources.insert(@sorted_resources.index(resource) + 1, gen_child)
+ @generated << gen_child
+ end
+ end
+ end
+ end
+
+ # Evaluate a single resource.
+ def eval_resource(resource)
+ events = []
+
+ unless tagged?(resource)
+ resource.debug "Not tagged with %s" % tags.join(", ")
+ return events
+ end
+
+ unless scheduled?(resource)
+ resource.debug "Not scheduled"
+ return events
+ end
+
+ @resourcemetrics[:scheduled] += 1
+
+ # Perform the actual changes
+ seconds = thinmark do
+ events = apply(resource)
+ end
+
+ # Keep track of how long we spend in each type of resource
+ @timemetrics[resource.class.name] += seconds
+
+ # Check to see if there are any events for this resource
+ if triggedevents = trigger(resource)
+ events += triggedevents
+ end
+
+ # Collect the targets of any subscriptions to those events
+ @relgraph.matching_edges(events).each do |edge|
+ @targets[edge.target] << edge
+ end
+
+ # And return the events for collection
+ events
+ end
# This method does all the actual work of running a transaction. It
# collects all of the changes, executes them, and responds to any
# necessary events.
def evaluate
@count = 0
-
- # Allow the tags to be overriden
- tags = self.tags || Puppet[:tags]
- if tags.nil? or tags == ""
- tags = nil
- else
- tags = [tags] unless tags.is_a? Array
- tags = tags.collect do |tag|
- tag.split(/\s*,\s*/)
- end.flatten
- end
-
+
# Start logging.
Puppet::Log.newdestination(@report)
-
- prefetch()
-
- # Now add any dynamically generated resources
- generate()
-
- # Create a relationship graph from our resource graph
- @relgraph = relationship_graph
- @relgraph.to_jpg("relations")
+ prepare()
begin
- allevents = @relgraph.topsort.collect { |child|
- events = []
- if (self.ignoretags or tags.nil? or child.tagged?(tags))
- if self.ignoreschedules or child.scheduled?
- @resourcemetrics[:scheduled] += 1
- # Perform the actual changes
-
- seconds = thinmark do
- events = apply(child)
- end
-
- # Keep track of how long we spend in each type of resource
- @timemetrics[child.class.name] += seconds
- else
- child.debug "Not scheduled"
- end
- else
- child.debug "Not tagged with %s" % tags.join(", ")
- end
-
- # Check to see if there are any events for this child
- if triggedevents = trigger(child)
- events += triggedevents
- end
-
- # Collect the targets of any subscriptions to those events
- @relgraph.matching_edges(events).each do |edge|
- @targets[edge.target] << edge
- end
-
- # And return the events for collection
- events
+ allevents = @sorted_resources.collect { |resource|
+ eval_resource(resource)
}.flatten.reject { |e| e.nil? }
ensure
# And then close the transaction log.
Puppet::Log.close(@report)
end
+
+ cleanup()
Puppet.debug "Finishing transaction %s with %s changes" %
[self.object_id, @count]
@@ -211,17 +235,25 @@ class Transaction
# Collect any dynamically generated resources.
def generate
list = @resources.vertices
+
+ # Store a list of all generated resources, so that we can clean them up
+ # after the transaction closes.
+ @generated = []
+
newlist = []
while ! list.empty?
list.each do |resource|
if resource.respond_to?(:generate)
made = resource.generate
+ next unless made
unless made.is_a?(Array)
made = [made]
end
+ made.uniq!
made.each do |res|
@resources.add_vertex!(res)
newlist << res
+ @generated << res
end
end
end
@@ -288,6 +320,19 @@ class Transaction
end
end
+ # Prepare to evaluate the elements in a transaction.
+ def prepare
+ prefetch()
+
+ # Now add any dynamically generated resources
+ generate()
+
+ # Create a relationship graph from our resource graph
+ @relgraph = relationship_graph
+
+ @sorted_resources = @relgraph.topsort
+ end
+
# Create a graph of all of the relationships in our resource graph.
def relationship_graph
graph = Puppet::PGraph.new
@@ -392,6 +437,43 @@ class Transaction
events
}.flatten.reject { |e| e.nil? }
end
+
+ # Is the resource currently scheduled?
+ def scheduled?(resource)
+ self.ignoreschedules or resource.scheduled?
+ end
+
+ # The tags we should be checking.
+ def tags
+ # Allow the tags to be overridden
+ unless defined? @tags
+ @tags = Puppet[:tags]
+ end
+
+ unless defined? @processed_tags
+ if @tags.nil? or @tags == ""
+ @tags = []
+ else
+ @tags = [@tags] unless @tags.is_a? Array
+ @tags = @tags.collect do |tag|
+ tag.split(/\s*,\s*/)
+ end.flatten
+ end
+ @processed_tags = true
+ end
+
+ @tags
+ end
+
+ # Is this resource tagged appropriately?
+ def tagged?(resource)
+ self.ignoretags or tags.empty? or resource.tagged?(tags)
+ end
+
+ # Are there any edges that target this resource?
+ def targeted?(resource)
+ @targets[resource]
+ end
# Trigger any subscriptions to a child. This does an upwardly recursive
# search -- it triggers the passed resource, but also the resource's parent
diff --git a/lib/puppet/type/pfile.rb b/lib/puppet/type/pfile.rb
index 15367a0c2..b3bbba3b9 100644
--- a/lib/puppet/type/pfile.rb
+++ b/lib/puppet/type/pfile.rb
@@ -174,18 +174,7 @@ module Puppet
# Autorequire any parent directories.
autorequire(:file) do
- cur = []
- pary = self[:path].split(File::SEPARATOR)
- pary.shift # remove the initial nil
- pary.pop # remove us
-
- pary.inject([""]) do |ary, dir|
- ary << dir
- cur << ary.join(File::SEPARATOR)
- ary
- end
-
- cur
+ File.dirname(self[:path])
end
# Autorequire the owner and group of the file.
@@ -285,6 +274,11 @@ module Puppet
end
super
end
+
+ # Create any children via recursion or whatever.
+ def eval_generate
+ recurse()
+ end
# Deal with backups.
def handlebackup(file = nil)
@@ -413,12 +407,14 @@ module Puppet
@arghash = tmphash
@arghash.delete(self.class.namevar)
- if @arghash.include?(:source)
- @arghash.delete(:source)
+ [:source, :parent].each do |param|
+ if @arghash.include?(param)
+ @arghash.delete(param)
+ end
end
- if @arghash.include?(:parent)
- @arghash.delete(:parent)
+ if @arghash[:target]
+ warning "%s vs %s" % [@arghash[:ensure], @arghash[:target]]
end
@stat = nil
@@ -435,15 +431,11 @@ module Puppet
targetstat = nil
unless FileTest.exist?(target)
- #self.info "%s does not exist; not recursing" %
- # target
return
end
# Now stat our target
targetstat = File.send(method, target)
unless targetstat.ftype == "directory"
- #self.info "%s is not a directory; not recursing" %
- # target
return
end
@@ -458,10 +450,10 @@ module Puppet
children = Dir.entries(target).reject { |d| d =~ /^\.+$/ }
- #Get rid of ignored children
+ # Get rid of ignored children
if @parameters.include?(:ignore)
children = handleignore(children)
- end
+ end
added = []
children.each do |file|
@@ -476,13 +468,12 @@ module Puppet
}
if child = self.newchild(file, true, args)
- unless @children.include?(child)
- self.push child
- added.push file
- end
+ added << child
end
end
end
+
+ added
end
# Build up a recursive map of what's around right now
@@ -503,7 +494,7 @@ module Puppet
#Get rid of ignored children
if @parameters.include?(:ignore)
children = handleignore(children)
- end
+ end
added = []
children.each { |file|
@@ -514,16 +505,17 @@ module Puppet
if child = self.newchild(file, true, options)
# Mark any unmanaged files for removal if purge is set.
# Use the array rather than [] because tidy uses this method, too.
- if @parameters.include?(:purge) and self[:purge] == :true and child.implicit?
+ if @parameters.include?(:purge) and self.purge?
+ info "purging %s" % child.ref
child[:ensure] = :absent
+ else
+ child[:require] = self
end
-
- unless @children.include?(child)
- self.push child
- added.push file
- end
+ added << child
end
}
+
+ added
end
# Create a new file or directory object as a child to the current
@@ -574,13 +566,13 @@ module Puppet
else
klass = self.class
end
-
+
# The child might already exist because 'localrecurse' runs
# before 'sourcerecurse'. I could push the override stuff into
# a separate method or something, but the work is the same other
# than this last bit, so it doesn't really make sense.
if child = klass[path]
- unless @children.include?(child)
+ unless child.parent.object_id == self.object_id
self.debug "Not managing more explicit file %s" %
path
return nil
@@ -599,6 +591,7 @@ module Puppet
end
}
end
+ return nil
else # create it anew
#notice "Creating new file with args %s" % args.inspect
args[:parent] = self
@@ -609,7 +602,6 @@ module Puppet
if child.nil?
return nil
end
- @children << child
rescue Puppet::Error => detail
self.notice(
"Cannot manage: %s" %
@@ -629,6 +621,9 @@ module Puppet
return child
end
+ # Files handle paths specially, because they just lengthen their
+ # path names, rather than including the full parent's title each
+ # time.
def pathbuilder
if defined? @parent
# We only need to behave specially when our parent is also
@@ -658,40 +653,53 @@ module Puppet
end
end
end
+
+ # Should we be purging?
+ def purge?
+ @parameters.include?(:purge) and (self[:purge] == :true or self[:purge] == "true")
+ end
# Recurse into the directory. This basically just calls 'localrecurse'
- # and maybe 'sourcerecurse'.
+ # and maybe 'sourcerecurse', returning the collection of generated
+ # files.
def recurse
+ # are we at the end of the recursion?
+ unless self.recurse?
+ return
+ end
+
recurse = self[:recurse]
# we might have a string, rather than a number
if recurse.is_a?(String)
if recurse =~ /^[0-9]+$/
recurse = Integer(recurse)
- #elsif recurse =~ /^inf/ # infinite recursion
else # anything else is infinite recursion
recurse = true
end
end
- # are we at the end of the recursion?
- #if recurse == 0
- unless self.recurse?
- return
- end
-
if recurse.is_a?(Integer)
recurse -= 1
end
-
- self.localrecurse(recurse)
- if @states.include? :target
- self.linkrecurse(recurse)
+
+ children = []
+
+ # We want to do link-recursing before normal recursion so that all
+ # of the target stuff gets copied over correctly.
+ if @states.include? :target and ret = self.linkrecurse(recurse)
+ children += ret
end
- if @states.include?(:source)
- self.sourcerecurse(recurse)
+ if ret = self.localrecurse(recurse)
+ children += ret
end
+ if @states.include?(:source) and ret = self.sourcerecurse(recurse)
+ children += ret
+ end
+
+ children
end
+ # A simple method for determining whether we should be recursing.
def recurse?
return false unless @parameters.include?(:recurse)
@@ -767,96 +775,78 @@ module Puppet
# a wrapper method to make sure the file exists before doing anything
def retrieve
- if @states.include?(:source)
- # This probably isn't the best place for it, but we need
- # to make sure that we have a corresponding checksum state.
- unless @states.include?(:checksum)
- self[:checksum] = "md5"
- end
-
- # We have to retrieve the source info before the recursion happens,
- # although I'm not exactly clear on why.
- @states[:source].retrieve
- end
-
- if @parameters.include?(:recurse)
- self.recurse
- end
-
unless stat = self.stat(true)
self.debug "File does not exist"
@states.each { |name,state|
# We've already retrieved the source, and we don't
# want to overwrite whatever it did. This is a bit
# of a hack, but oh well, source is definitely special.
- next if name == :source
+ # next if name == :source
state.is = :absent
}
+
+ # If the file doesn't exist but we have a source, then call
+ # retrieve on that state
+ if @states.include?(:source)
+ @states[:source].retrieve
+ end
return
end
states().each { |state|
- # We don't want to call 'describe()' twice, so only do a local
- # retrieve on the source.
- if state.name == :source
- state.retrieve(false)
- else
- state.retrieve
- end
+ state.retrieve
}
end
# This recurses against the remote source and makes sure the local
- # and remote structures match. It's run after 'localrecurse'.
+ # and remote structures match. It's run after 'localrecurse'. This
+ # method only does anything when its corresponding remote entry is
+ # a directory; in that case, this method creates file objects that
+ # correspond to any contained remote files.
def sourcerecurse(recurse)
- # FIXME sourcerecurse should support purging non-remote files
- source = @states[:source].source
-
- unless ! source.nil? and source !~ /^\s*$/
- self.notice "source %s does not exist" % @states[:source].should
- return nil
- end
-
- sourceobj, path = uri2obj(source)
-
# we'll set this manually as necessary
if @arghash.include?(:ensure)
@arghash.delete(:ensure)
end
-
- # okay, we've got our source object; now we need to
- # build up a local file structure to match the remote
- # one
-
- server = sourceobj.server
- sum = "md5"
- if state = self.state(:checksum)
- sum = state.should
- end
+
r = false
if recurse
unless recurse == 0
r = 1
end
end
-
+
ignore = self[:ignore]
- desc = server.list(path, self[:links], r, ignore)
+ @states[:source].should.each do |source|
+ sourceobj, path = uri2obj(source)
+
+ # okay, we've got our source object; now we need to
+ # build up a local file structure to match the remote
+ # one
- # Now create a new child for every file returned in the list.
- desc.split("\n").each { |line|
- file, type = line.split("\t")
- next if file == "/" # skip the listing object
- name = file.sub(/^\//, '')
- args = {:source => source + file}
- if type == file
- args[:recurse] = nil
+ server = sourceobj.server
+
+ desc = server.list(path, self[:links], r, ignore)
+ if desc == ""
+ next
end
+
+ # Now create a new child for every file returned in the list.
+ return desc.split("\n").collect { |line|
+ file, type = line.split("\t")
+ next if file == "/" # skip the listing object
+ name = file.sub(/^\//, '')
+ args = {:source => source + file}
+ if type == file
+ args[:recurse] = nil
+ end
- self.newchild(name, false, args)
- }
+ self.newchild(name, false, args)
+ }.reject {|c| c.nil? }.each do |f| f.info "sourced" end
+ end
+ return []
end
# Set the checksum, from another state. There are multiple states that
diff --git a/lib/puppet/type/pfile/ensure.rb b/lib/puppet/type/pfile/ensure.rb
index 6f7b15d49..c998e0f7f 100755
--- a/lib/puppet/type/pfile/ensure.rb
+++ b/lib/puppet/type/pfile/ensure.rb
@@ -60,6 +60,7 @@ module Puppet
end
newvalue(:directory) do
+ p @is
mode = @parent.should(:mode)
parent = File.dirname(@parent[:path])
unless FileTest.exists? parent
diff --git a/lib/puppet/type/pfile/source.rb b/lib/puppet/type/pfile/source.rb
index 62d3dbbc4..8ac60422c 100755
--- a/lib/puppet/type/pfile/source.rb
+++ b/lib/puppet/type/pfile/source.rb
@@ -1,7 +1,11 @@
require 'puppet/server/fileserver'
module Puppet
- # Copy files from a local or remote source.
+ # Copy files from a local or remote source. This state *only* does any work
+ # when the remote file is an actual file; in that case, this state copies
+ # the file down. If the remote file is a dir or a link or whatever, then
+ # this state, during retrieval, modifies the appropriate other states
+ # so that things get taken care of appropriately.
Puppet.type(:file).newstate(:source) do
PINPARAMS = Puppet::Server::FileServer::CHECKPARAMS
@@ -38,12 +42,38 @@ module Puppet
}
This will use the first found file as the source.
+
+ You cannot currently copy links using this mechanism; set ``links``
+ to ``follow`` if any remote sources are links.
[fileserver docs]: ../installing/fsconfigref.html
"
uncheckable
+
+ validate do |source|
+ unless @parent.uri2obj(source)
+ raise Puppet::Error, "Invalid source %s" % source
+ end
+ end
+
+ munge do |source|
+ # if source.is_a? Symbol
+ # return source
+ # end
+
+ # Remove any trailing slashes
+ source.sub(/\/$/, '')
+ end
+
+ def checksum
+ if defined?(@stats)
+ @stats[:checksum]
+ else
+ nil
+ end
+ end
# Ask the file server to describe our file.
def describe(source)
@@ -75,34 +105,52 @@ module Puppet
args.delete(:owner)
end
- if args.empty?
+ if args.empty? or (args[:type] == "link" and @parent[:links] == :ignore)
return nil
else
return args
end
end
-
+
+ # Have we successfully described the remote source?
+ def described?
+ ! @stats.nil? and ! @stats[:type].nil? and @is != :notdescribed
+ end
+
+ # Use the info we get from describe() to check if we're in sync.
+ def insync?
+ unless described?
+ info "No specified sources exist"
+ return true
+ end
+
+ if @is == :nocopy
+ return true
+ end
+
+ # the only thing this actual state can do is copy files around. Therefore,
+ # only pay attention if the remote is a file.
+ unless @stats[:type] == "file"
+ return true
+ end
+ # Now, we just check to see if the checksums are the same
+ return @parent.is(:checksum) == @stats[:checksum]
+ end
+
# This basically calls describe() on our file, and then sets all
# of the local states appropriately. If the remote file is a normal
# file then we set it to copy; if it's a directory, then we just mark
# that the local directory should be created.
def retrieve(remote = true)
sum = nil
-
- unless defined? @shouldorig
- raise Puppet::DevError, "No sources defined for %s" %
- @parent.title
- end
-
- @source = nil unless defined? @source
+ @source = nil
# This is set to false by the File#retrieve function on the second
# retrieve, so that we do not do two describes.
if remote
- @source = nil
# Find the first source that exists. @shouldorig contains
# the sources as specified by the user.
- @shouldorig.each { |source|
+ @should.each { |source|
if @stats = self.describe(source)
@source = source
break
@@ -112,70 +160,18 @@ module Puppet
if @stats.nil? or @stats[:type].nil?
@is = :notdescribed
- @source = nil
return nil
end
-
- # If we're a normal file, then set things up to copy the file down.
+
case @stats[:type]
- when "file":
- if sum = @parent.state(:checksum)
- if sum.is
- if sum.is == :absent
- sum.retrieve(true)
- end
- @is = sum.is
- else
- @is = :absent
- end
- else
- self.info "File does not have checksum"
- @is = :absent
- end
- # if replace => false then fake the checksum so that the file
- # is not overwritten.
- unless @is == :absent
- if @parent[:replace] == :false
- info "Not replacing existing file"
- @is = @stats[:checksum]
- end
- end
- @should = [@stats[:checksum]]
- # If we're a directory, then do not copy anything, and instead just
- # create the directory using the 'create' state.
- when "directory":
- if state = @parent.state(:ensure)
- unless state.should == "directory"
- state.should = "directory"
- end
- else
- @parent[:ensure] = "directory"
- @parent.state(:ensure).retrieve
- end
- # we'll let the :ensure state do our work
- @should.clear
- @is = true
- when "link":
- case @parent[:links]
- when :ignore
- @is = :nocopy
- @should = [:nocopy]
- self.info "Ignoring link %s" % @source
- return
- when :follow
- @stats = self.describe(source, :follow)
- if @stats.empty?
- raise Puppet::Error, "Could not follow link %s" % @source
- end
- when :copy
- raise Puppet::Error, "Cannot copy links yet"
- end
+ when "directory", "file":
+ @parent[:ensure] = @stats[:type]
else
self.info @stats.inspect
self.err "Cannot use files of type %s as sources" %
@stats[:type]
- @should = [:nocopy]
@is = :nocopy
+ return
end
# Take each of the stats and set them as states on the local file
@@ -187,61 +183,32 @@ module Puppet
# was the stat already specified, or should the value
# be inherited from the source?
unless @parent.argument?(stat)
- if state = @parent.state(stat)
- state.should = value
- else
- @parent[stat] = value
- end
- #else
- # @parent.info "Already specified %s" % stat
+ @parent[stat] = value
end
}
+
+ @is = @stats[:checksum]
end
-
- # The special thing here is that we need to make sure that 'should'
- # is only set for files, not directories. The processing we're doing
- # here doesn't really matter, because the @should values will be
- # overridden when we 'retrieve'.
- munge do |source|
- if source.is_a? Symbol
- return source
- end
-
- # Remove any trailing slashes
- source.sub!(/\/$/, '')
- unless @parent.uri2obj(source)
- raise Puppet::Error, "Invalid source %s" % source
+
+ def should
+ @should
+ end
+
+ # Make sure we're also checking the checksum
+ def should=(value)
+ super
+
+ # @parent[:check] = [:checksum, :ensure]
+ unless @parent.state(:checksum)
+ @parent[:checksum] = :md5
end
-
- if ! defined? @stats or @stats.nil?
- # stupid hack for now; it'll get overriden
- return source
- else
- if @stats[:type] == "directory"
- @is = true
- return nil
- else
- return source
- end
+
+ unless @parent.state(:ensure)
+ @parent[:check] = :ensure
end
end
def sync
- if @is == :notdescribed
- self.retrieve # try again
- if @is == :notdescribed
- @parent.log "Could not retrieve information on %s" %
- @parent.title
- return nil
- end
- if @is == @should
- return nil
- end
- end
-
- case @stats[:type]
- when "link":
- end
unless @stats[:type] == "file"
#if @stats[:type] == "directory"
#[@parent.name, @is.inspect, @should.inspect]
@@ -250,10 +217,6 @@ module Puppet
@parent[:path]
end
- unless defined? @source
- raise Puppet::DevError, "Somehow source is still undefined"
- end
-
sourceobj, path = @parent.uri2obj(@source)
begin
diff --git a/lib/puppet/type/pfile/target.rb b/lib/puppet/type/pfile/target.rb
index 8c22b10b9..17d9bebbc 100644
--- a/lib/puppet/type/pfile/target.rb
+++ b/lib/puppet/type/pfile/target.rb
@@ -59,8 +59,6 @@ module Puppet
(tstat = File.lstat(should)) and
(tstat.ftype == "directory") and
@parent.recurse?
- warning "Changing ensure to directory; recurse is %s but %s" %
- [@parent[:recurse].inspect, @parent.recurse?]
@parent[:ensure] = :directory
@is = should
@linkmaker = true
diff --git a/lib/puppet/type/state.rb b/lib/puppet/type/state.rb
index 9b96b909f..6dd5f1567 100644
--- a/lib/puppet/type/state.rb
+++ b/lib/puppet/type/state.rb
@@ -491,7 +491,7 @@ class State < Puppet::Parameter
def change_to_s
begin
- if @is == :absent
+ if @is == :absent or @is.nil?
return "created"
elsif self.should == :absent
return "removed"
diff --git a/test/other/transactions.rb b/test/other/transactions.rb
index c143b3a0c..7342b57ec 100755
--- a/test/other/transactions.rb
+++ b/test/other/transactions.rb
@@ -11,6 +11,21 @@ require 'puppettest/support/resources'
class TestTransactions < Test::Unit::TestCase
include PuppetTest::FileTesting
include PuppetTest::Support::Resources
+
+ def mkgenerator(&block)
+ # Create a bogus type that generates new instances with shorter
+ type = Puppet::Type.newtype(:generator) do
+ newparam(:name, :namevar => true)
+ end
+ if block
+ type.class_eval(&block)
+ end
+ cleanup do
+ Puppet::Type.rmtype(:generator)
+ end
+
+ return type
+ end
def test_reports
path1 = tempfile()
@@ -541,22 +556,19 @@ class TestTransactions < Test::Unit::TestCase
graph.to_jpg("normal_relations")
end
+ # Test pre-evaluation generation
def test_generate
- # Create a bogus type that generates new instances with shorter
- Puppet::Type.newtype(:generator) do
- newparam(:name, :namevar => true)
-
+ mkgenerator() do
def generate
ret = []
if title.length > 1
ret << self.class.create(:title => title[0..-2])
+ else
+ return nil
end
ret
end
end
- cleanup do
- Puppet::Type.rmtype(:generator)
- end
yay = Puppet::Type.newgenerator :title => "yay"
rah = Puppet::Type.newgenerator :title => "rah"
@@ -571,6 +583,184 @@ class TestTransactions < Test::Unit::TestCase
assert(trans.resources.vertex?(Puppet::Type.type(:generator)[name]),
"Generated %s was not a vertex" % 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 = {}
+ 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
+
+ def evaluate
+ $evaluated[self.title] = true
+ 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.apply(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),
+ "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")
+
+ # Now make sure it in turn eval_generates appropriately
+ assert_nothing_raised("failed to apply yay") do
+ trans.apply(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)
+ end
+
+ assert_nothing_raised("failed to eval_generate with nil response") do
+ trans.apply(type["y"])
+ end
+
+ assert_equal(%w{yay ya y rah}, trans.sorted_resources.collect { |r| r.title },
+ "Did not eval_generate correctly")
+
+ assert_nothing_raised("failed to apply rah") do
+ trans.apply(rah)
+ end
+
+ ra = type["ra"]
+ assert(ra, "Did not generate ra")
+ assert(trans.relgraph.vertex?(ra),
+ "Did not add ra to rel_graph" % name)
+
+ # Now make sure this generated resource has the same relationships as the generating
+ # resource
+ assert(trans.relgraph.edge?(yay, ra),
+ "yay is not required by ra")
+ assert(trans.relgraph.edge?(ya, ra),
+ "ra is 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, ra)],
+ "The label was not retained")
+
+ # 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
+ assert_nothing_raised do
+ trans.evaluate
+ end
+
+ assert_equal(%w{yay ya y rah ra r}.sort, $evaluated.keys.sort,
+ "Not all resources were evaluated")
+ 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 events propagate down the relationship graph appropriately.
+ def test_trigger
end
end
diff --git a/test/server/fileserver.rb b/test/server/fileserver.rb
index 00d235cb2..5e9c60ddc 100755
--- a/test/server/fileserver.rb
+++ b/test/server/fileserver.rb
@@ -493,7 +493,7 @@ class TestFileServer < Test::Unit::TestCase
# create a server with the file
assert_nothing_raised {
server = Puppet::Server::FileServer.new(
- :Local => true,
+ :Local => false,
:Config => conffile
)
}
@@ -658,7 +658,7 @@ class TestFileServer < Test::Unit::TestCase
# start our server with a fast timeout
assert_nothing_raised {
server = Puppet::Server::FileServer.new(
- :Local => true,
+ :Local => false,
:Config => conffile
)
}
@@ -968,6 +968,7 @@ allow *
end
+ # Test that the fileserver expands the %h and %d things.
def test_fileserver_expansion
server = nil
assert_nothing_raised {
diff --git a/test/types/file.rb b/test/types/file.rb
index 28cb6c754..d5b493788 100755
--- a/test/types/file.rb
+++ b/test/types/file.rb
@@ -30,6 +30,7 @@ class TestFile < Test::Unit::TestCase
def setup
super
+ @file = Puppet::Type.type(:file)
begin
initstorage
rescue
@@ -489,11 +490,173 @@ class TestFile < Test::Unit::TestCase
Puppet::Type.allclear
}
end
-
+
+ def test_localrecurse
+ # Create a test directory
+ path = tempfile()
+ dir = @file.create :path => path, :mode => 0755, :recurse => true
+
+ Dir.mkdir(path)
+
+ # Make sure we return nothing when there are no children
+ ret = nil
+ assert_nothing_raised() { ret = dir.localrecurse(true) }
+ assert_equal([], ret, "empty dir returned children")
+
+ # Now make a file and make sure we get it
+ test = File.join(path, "file")
+ File.open(test, "w") { |f| f.puts "yay" }
+ assert_nothing_raised() { ret = dir.localrecurse(true) }
+ fileobj = @file[test]
+ assert(fileobj, "child object was not created")
+ assert_equal([fileobj], ret, "child object was not returned")
+
+ # check that the file lists us as a dependency
+ assert_equal([[:file, dir.title]], fileobj[:require], "dependency was not set up")
+
+ # And that it inherited our recurse setting
+ assert_equal(true, fileobj[:recurse], "file did not inherit recurse")
+
+ # Make sure it's not returned again
+ assert_nothing_raised() { ret = dir.localrecurse(true) }
+ assert_equal([], ret, "child object was returned twice")
+
+ # Now just for completion, make sure we will return many files
+ files = []
+ 10.times do |i|
+ f = File.join(path, i.to_s)
+ files << f
+ File.open(f, "w") do |o| o.puts "" end
+ end
+ assert_nothing_raised() { ret = dir.localrecurse(true) }
+ assert_equal(files.sort, ret.collect { |f| f.title }, "child object was returned twice")
+
+ # Clean everything up and start over
+ files << test
+ files.each do |f| File.unlink(f) end
+
+ # Now make sure we correctly ignore things
+ dir[:ignore] = "*.out"
+ bad = File.join(path, "test.out")
+ good = File.join(path, "yayness")
+ [good, bad].each do |f|
+ File.open(f, "w") { |o| o.puts "" }
+ end
+
+ assert_nothing_raised() { ret = dir.localrecurse(true) }
+ assert_equal([good], ret.collect { |f| f.title }, "ignore failed")
+
+ # Now make sure purging works
+ dir[:purge] = true
+ dir[:ignore] = "svn"
+
+ assert_nothing_raised() { ret = dir.localrecurse(true) }
+ assert_equal([bad], ret.collect { |f| f.title }, "purge failed")
+
+ badobj = @file[bad]
+ assert(badobj, "did not create bad object")
+ assert_equal(:absent, badobj.should(:ensure), "ensure was not set to absent on bad object")
+ end
+
+ def test_recurse
+ basedir = tempfile()
+ FileUtils.mkdir_p(basedir)
+
+ # Create our file
+ dir = nil
+ assert_nothing_raised {
+ dir = Puppet.type(:file).create(
+ :path => basedir,
+ :check => %w{owner mode group}
+ )
+ }
+
+ return_nil = false
+
+ # and monkey-patch it
+ [:localrecurse, :sourcerecurse, :linkrecurse].each do |m|
+ dir.meta_def(m) do |recurse|
+ if return_nil # for testing nil return, of course
+ return nil
+ else
+ return [recurse]
+ end
+ end
+ end
+
+ # First try it with recurse set to false
+ dir[:recurse] = false
+ assert_nothing_raised do
+ assert_nil(dir.recurse)
+ end
+
+ # Now try it with the different valid positive values
+ [true, "true", "inf", 50].each do |value|
+ assert_nothing_raised { dir[:recurse] = value}
+
+ # Now make sure the methods are called appropriately
+ ret = nil
+ assert_nothing_raised do
+ ret = dir.recurse
+ end
+
+ # We should only call the localrecurse method, so make sure
+ # that's the case
+ if value == 50
+ # Make sure our counter got decremented
+ assert_equal([49], ret, "did not call localrecurse")
+ else
+ assert_equal([true], ret, "did not call localrecurse")
+ end
+ end
+
+ # Make sure it doesn't recurse when we've set recurse to false
+ [false, "false"].each do |value|
+ assert_nothing_raised { dir[:recurse] = value }
+
+ ret = nil
+ assert_nothing_raised() { ret = dir.recurse }
+ assert_nil(ret)
+ end
+ dir[:recurse] = true
+
+ # Now add a target, so we do the linking thing
+ dir[:target] = tempfile()
+ ret = nil
+ assert_nothing_raised { ret = dir.recurse }
+ assert_equal([true, true], ret, "did not call linkrecurse")
+
+ # And add a source, and make sure we call that
+ dir[:source] = tempfile()
+ assert_nothing_raised { ret = dir.recurse }
+ assert_equal([true, true, true], ret, "did not call linkrecurse")
+
+ # Lastly, make sure we correctly handle returning nil
+ return_nil = true
+ assert_nothing_raised { ret = dir.recurse }
+ end
+
+ def test_recurse?
+ file = Puppet::Type.type(:file).create :path => tempfile
+
+ # Make sure we default to false
+ assert(! file.recurse?, "Recurse defaulted to true")
+
+ [true, "true", 10, "inf"].each do |value|
+ file[:recurse] = value
+ assert(file.recurse?, "%s did not cause recursion" % value)
+ end
+
+ [false, "false", 0].each do |value|
+ file[:recurse] = value
+ assert(! file.recurse?, "%s caused recursion" % value)
+ end
+ end
+
def test_recursion
basedir = tempfile()
- subdir = File.join(basedir, "this", "is", "sub", "dir")
- tmpfile = File.join(subdir,"testing")
+ subdir = File.join(basedir, "subdir")
+ tmpfile = File.join(basedir,"testing")
FileUtils.mkdir_p(subdir)
dir = nil
@@ -505,67 +668,33 @@ class TestFile < Test::Unit::TestCase
:check => %w{owner mode group}
)
}
+
+ children = nil
assert_nothing_raised {
- dir.evaluate
- }
-
- subobj = nil
- assert_nothing_raised {
- subobj = Puppet.type(:file)[subdir]
+ children = dir.eval_generate
}
-
- assert(subobj, "Could not retrieve %s object" % subdir)
+
+ assert_equal([subdir], children.collect {|c| c.title },
+ "Incorrect generated children")
+
+ dir.class[subdir].remove
File.open(tmpfile, "w") { |f| f.puts "yayness" }
-
- dir.evaluate
-
- file = nil
+
assert_nothing_raised {
- file = Puppet.type(:file)[tmpfile]
+ children = dir.eval_generate
}
- assert(file, "Could not retrieve %s object" % tmpfile)
+ assert_equal([subdir, tmpfile].sort, children.collect {|c| c.title }.sort,
+ "Incorrect generated children")
+
+ File.unlink(tmpfile)
#system("rm -rf %s" % basedir)
Puppet.type(:file).clear
end
end
-=begin
- def test_ignore
-
- end
-=end
-
- # XXX disabled until i change how dependencies work
- def disabled_test_recursionwithcreation
- path = "/tmp/this/directory/structure/does/not/exist"
- @@tmpfiles.push "/tmp/this"
-
- file = nil
- assert_nothing_raised {
- file = mkfile(
- :name => path,
- :recurse => true,
- :ensure => "file"
- )
- }
-
- trans = nil
- comp = newcomp("recursewithfiles", file)
- assert_nothing_raised {
- trans = comp.evaluate
- }
-
- events = nil
- assert_nothing_raised {
- events = trans.evaluate.collect { |e| e.event.to_s }
- }
-
- puts "events are %s" % events.join(", ")
- end
-
def test_filetype_retrieval
file = nil
@@ -621,7 +750,7 @@ class TestFile < Test::Unit::TestCase
}
assert_nothing_raised {
- dir.retrieve
+ dir.eval_generate
}
obj = nil
@@ -645,7 +774,7 @@ class TestFile < Test::Unit::TestCase
def test_path
dir = tempfile()
- path = File.join(dir, "and", "a", "sub", "dir")
+ path = File.join(dir, "subdir")
assert_nothing_raised("Could not make file") {
FileUtils.mkdir_p(File.dirname(path))
@@ -663,7 +792,7 @@ class TestFile < Test::Unit::TestCase
}
assert_nothing_raised {
- dirobj.evaluate
+ dirobj.generate
}
assert_nothing_raised {
@@ -968,10 +1097,57 @@ class TestFile < Test::Unit::TestCase
assert_events([], file)
assert_events([], file)
end
+
+ def test_linkrecurse
+ dest = tempfile()
+ link = @file.create :path => tempfile(), :recurse => true, :ensure => dest
+
+ ret = nil
+
+ # Start with nothing, just to make sure we get nothing back
+ assert_nothing_raised { ret = link.linkrecurse(true) }
+ assert_nil(ret, "got a return when the dest doesn't exist")
+
+ # then with a directory with only one file
+ Dir.mkdir(dest)
+ one = File.join(dest, "one")
+ File.open(one, "w") { |f| f.puts "" }
+ link[:ensure] = dest
+ assert_nothing_raised { ret = link.linkrecurse(true) }
+
+ assert_equal(:directory, link.should(:ensure), "ensure was not set to directory")
+ assert_equal([File.join(link.title, "one")], ret.collect { |f| f.title },
+ "Did not get linked file")
+ oneobj = @file[File.join(link.title, "one")]
+ assert_equal(one, oneobj.should(:target), "target was not set correctly")
+
+ oneobj.remove
+ File.unlink(one)
+
+ # Then make sure we get multiple files
+ returns = []
+ 5.times do |i|
+ path = File.join(dest, i.to_s)
+ returns << File.join(link.title, i.to_s)
+ File.open(path, "w") { |f| f.puts "" }
+ end
+ assert_nothing_raised { ret = link.linkrecurse(true) }
+
+ assert_equal(returns.sort, ret.collect { |f| f.title },
+ "Did not get links back")
+
+ returns.each do |path|
+ obj = @file[path]
+ assert(path, "did not get obj for %s" % path)
+ sdest = File.join(dest, File.basename(path))
+ assert_equal(sdest, obj.should(:target),
+ "target was not set correctly for %s" % path)
+ end
+ end
def test_simplerecursivelinking
source = tempfile()
- dest = tempfile()
+ path = tempfile()
subdir = File.join(source, "subdir")
file = File.join(subdir, "file")
@@ -982,20 +1158,21 @@ class TestFile < Test::Unit::TestCase
assert_nothing_raised {
link = Puppet.type(:file).create(
:ensure => source,
- :path => dest,
+ :path => path,
:recurse => true
)
}
assert_apply(link)
- subdest = File.join(dest, "subdir")
- linkpath = File.join(subdest, "file")
- assert(File.directory?(dest), "dest is not a dir")
- assert(File.directory?(subdest), "subdest is not a dir")
+ sublink = File.join(path, "subdir")
+ linkpath = File.join(sublink, "file")
+ assert(File.directory?(path), "dest is not a dir")
+ assert(File.directory?(sublink), "subdest is not a dir")
assert(File.symlink?(linkpath), "path is not a link")
assert_equal(file, File.readlink(linkpath))
+ assert_nil(@file[sublink], "objects were not removed")
assert_events([], link)
end
@@ -1077,12 +1254,14 @@ class TestFile < Test::Unit::TestCase
objects = []
objects << Puppet.type(:exec).create(
:command => "mkdir %s; touch %s/file" % [source, source],
+ :title => "yay",
:path => ENV["PATH"]
)
objects << Puppet.type(:file).create(
:ensure => source,
:path => dest,
- :recurse => true
+ :recurse => true,
+ :require => objects[0]
)
assert_apply(*objects)
@@ -1371,7 +1550,7 @@ class TestFile < Test::Unit::TestCase
assert_equal("yayness", File.read(path), "Content did not get set correctly")
end
- # Make sure unmanaged files can be purged.
+ # Make sure unmanaged files are be purged.
def test_purge
sourcedir = tempfile()
destdir = tempfile()
@@ -1386,12 +1565,12 @@ class TestFile < Test::Unit::TestCase
File.open(randfile, "w") { |f| f.puts "footest" }
lfobj = Puppet::Type.newfile(:path => localfile, :content => "rahtest")
+
destobj = Puppet::Type.newfile(:path => destdir,
:source => sourcedir,
:recurse => true)
-
assert_apply(lfobj, destobj)
assert(FileTest.exists?(dsourcefile), "File did not get copied")
diff --git a/test/types/filesources.rb b/test/types/filesources.rb
index 4c1139a0b..bae4c7d5f 100755
--- a/test/types/filesources.rb
+++ b/test/types/filesources.rb
@@ -11,26 +11,37 @@ class TestFileSources < Test::Unit::TestCase
include PuppetTest::FileTesting
def setup
super
- begin
- initstorage
- rescue
- system("rm -rf %s" % Puppet[:statefile])
- end
if defined? @port
@port += 1
else
@port = 8800
end
+ @file = Puppet::Type.type(:file)
+ end
+
+ def use_storage
+ begin
+ initstorage
+ rescue
+ system("rm -rf %s" % Puppet[:statefile])
+ end
end
def initstorage
Puppet::Storage.init
Puppet::Storage.load
end
-
- def clearstorage
- Puppet::Storage.store
- Puppet::Storage.clear
+
+ # Make a simple recursive tree.
+ def mk_sourcetree
+ source = tempfile()
+ sourcefile = File.join(source, "file")
+ Dir.mkdir source
+ File.open(sourcefile, "w") { |f| f.puts "yay" }
+
+ dest = tempfile()
+ destfile = File.join(dest, "file")
+ return source, dest, sourcefile, destfile
end
def test_newchild
@@ -58,10 +69,244 @@ class TestFileSources < Test::Unit::TestCase
file.newchild(File.join(path,"childtest"), true)
}
end
+
+ def test_describe
+ source = tempfile()
+ dest = tempfile()
+
+ file = Puppet::Type.newfile :path => dest, :source => source,
+ :title => "copier"
+
+ state = file.state(:source)
+
+ # First try describing with a normal source
+ result = nil
+ assert_nothing_raised do
+ result = state.describe(source)
+ end
+ assert_nil(result, "Got a result back when source is missing")
+
+ # Now make a remote directory
+ Dir.mkdir(source)
+ assert_nothing_raised do
+ result = state.describe(source)
+ end
+ assert_equal("directory", result[:type])
+
+ # And as a file
+ Dir.rmdir(source)
+ File.open(source, "w") { |f| f.puts "yay" }
+ assert_nothing_raised do
+ result = state.describe(source)
+ end
+ assert_equal("file", result[:type])
+ assert(result[:checksum], "did not get value for checksum")
+ if Puppet::SUIDManager.uid == 0
+ assert(result.has_key?("owner"), "Lost owner in describe")
+ else
+ assert(! result.has_key?("owner"),
+ "Kept owner in describe even tho not root")
+ end
+
+ # Now let's do the various link things
+ File.unlink(source)
+ target = tempfile()
+ File.open(target, "w") { |f| f.puts "yay" }
+ File.symlink(target, source)
+
+ file[:links] = :ignore
+ assert_nil(state.describe(source),
+ "Links were not ignored")
+
+ file[:links] = :manage
+ # We can't manage links at this point
+ assert_raise(Puppet::FileServerError) do
+ state.describe(source)
+ end
+
+ # And then make sure links get followed, otherwise
+ file[:links] = :follow
+ assert_equal("file", state.describe(source)[:type])
+ end
+
+ def test_source_retrieve
+ source = tempfile()
+ dest = tempfile()
+
+ file = Puppet::Type.newfile :path => dest, :source => source,
+ :title => "copier"
+
+ assert(file.state(:checksum), "source state did not create checksum state")
+ state = file.state(:source)
+ assert(state, "did not get source state")
+
+ # Make sure the munge didn't actually change the source
+ assert_equal(source, state.should, "munging changed the source")
+
+ # First try it with a missing source
+ assert_nothing_raised do
+ state.retrieve
+ end
+
+ # And make sure the state considers itself in sync, since there's nothing
+ # to do
+ assert(state.insync?, "source thinks there's work to do with no file or dest")
+
+ # Now make the dest a directory, and make sure the object sets :ensure up to
+ # create a directory
+ Dir.mkdir(source)
+ assert_nothing_raised do
+ state.retrieve
+ end
+ assert_equal(:directory, file.should(:ensure),
+ "Did not set to create directory")
+
+ # And make sure the source state won't try to do anything with a remote dir
+ assert(state.insync?, "Source was out of sync even tho remote is dir")
+
+ # Now remove the source, and make sure :ensure was not modified
+ Dir.rmdir(source)
+ assert_nothing_raised do
+ state.retrieve
+ end
+ assert_equal(:directory, file.should(:ensure),
+ "Did not keep :ensure setting")
+
+ # Now have a remote file and make sure things work correctly
+ File.open(source, "w") { |f| f.puts "yay" }
+ File.chmod(0755, source)
+
+ assert_nothing_raised do
+ state.retrieve
+ end
+ assert_equal(:file, file.should(:ensure),
+ "Did not make correct :ensure setting")
+ assert_equal(0755, file.should(:mode),
+ "Mode was not copied over")
+
+ # Now let's make sure that we get the first found source
+ fake = tempfile()
+ state.should = [fake, source]
+ assert_nothing_raised do
+ state.retrieve
+ end
+ assert_equal(Digest::MD5.hexdigest(File.read(source)), state.checksum.sub(/^\{\w+\}/, ''),
+ "Did not catch later source")
+ end
+
+ def test_insync
+ source = tempfile()
+ dest = tempfile()
+
+ file = Puppet::Type.newfile :path => dest, :source => source,
+ :title => "copier"
+
+ state = file.state(:source)
+ assert(state, "did not get source state")
+
+ # Try it with no source at all
+ file.retrieve
+ assert(state.insync?, "source state not in sync with missing source")
+
+ # with a directory
+ Dir.mkdir(source)
+ file.retrieve
+ assert(state.insync?, "source state not in sync with directory as source")
+ Dir.rmdir(source)
+
+ # with a file
+ File.open(source, "w") { |f| f.puts "yay" }
+ file.retrieve
+ assert(!state.insync?, "source state was in sync when file was missing")
+
+ # With a different file
+ File.open(dest, "w") { |f| f.puts "foo" }
+ file.retrieve
+ assert(!state.insync?, "source state was in sync with different file")
+
+ # with matching files
+ File.open(dest, "w") { |f| f.puts "yay" }
+ file.retrieve
+ assert(state.insync?, "source state was not in sync with matching file")
+ end
+
+ def test_source_sync
+ source = tempfile()
+ dest = tempfile()
+
+ file = Puppet::Type.newfile :path => dest, :source => source,
+ :title => "copier"
+ state = file.state(:source)
+
+ File.open(source, "w") { |f| f.puts "yay" }
+
+ file.retrieve
+ assert(! state.insync?, "source thinks it's in sync")
+
+ event = nil
+ assert_nothing_raised do
+ event = state.sync
+ end
+ assert_equal(:file_created, event)
+ assert_equal(File.read(source), File.read(dest),
+ "File was not copied correctly")
+
+ # Now write something different
+ File.open(source, "w") { |f| f.puts "rah" }
+ file.retrieve
+ assert(! state.insync?, "source should be out of sync")
+ assert_nothing_raised do
+ event = state.sync
+ end
+ assert_equal(:file_changed, event)
+ assert_equal(File.read(source), File.read(dest),
+ "File was not copied correctly")
+ end
+
+ # XXX This test doesn't cover everything. Specifically,
+ # it doesn't handle 'ignore' and 'links'.
+ def test_sourcerecurse
+ source, dest, sourcefile, destfile = mk_sourcetree
+
+ # The sourcerecurse method will only ever get called when we're
+ # recursing, so we go ahead and set it.
+ obj = Puppet::Type.newfile :source => source, :path => dest, :recurse => true
+
+ result = nil
+ assert_nothing_raised do
+ result = obj.sourcerecurse(true)
+ end
+ dfileobj = @file[destfile]
+ assert(dfileobj, "Did not create destfile object")
+ assert_equal([dfileobj], result)
+
+ # Clean this up so it can be recreated
+ dfileobj.remove
+
+ # Make sure we correctly iterate over the sources
+ nosource = tempfile()
+ obj[:source] = [nosource, source]
+
+ result = nil
+ assert_nothing_raised do
+ result = obj.sourcerecurse(true)
+ end
+ dfileobj = @file[destfile]
+ assert(dfileobj, "Did not create destfile object with a missing source")
+ assert_equal([dfileobj], result)
+ dfileobj.remove
+
+ # Lastly, make sure we return an empty array when no sources are there
+ obj[:source] = [nosource, tempfile()]
+
+ assert_nothing_raised do
+ result = obj.sourcerecurse(true)
+ end
+ assert_equal([], result, "Sourcerecurse failed when all sources are missing")
+ end
def test_simplelocalsource
path = tempfile()
- @@tmpfiles.push path
FileUtils.mkdir_p path
frompath = File.join(path,"source")
topath = File.join(path,"dest")
@@ -85,7 +330,19 @@ class TestFileSources < Test::Unit::TestCase
from = File.open(frompath) { |o| o.read }
to = File.open(topath) { |o| o.read }
assert_equal(from,to)
- @@tmpfiles.push path
+ end
+
+ # Make sure a simple recursive copy works
+ def test_simple_recursive_source
+ source, dest, sourcefile, destfile = mk_sourcetree
+
+ file = Puppet::Type.newfile :path => dest, :source => source, :recurse => true
+
+ assert_events([:directory_created, :file_created], file)
+
+ assert(FileTest.directory?(dest), "Dest dir was not created")
+ assert(FileTest.file?(destfile), "dest file was not created")
+ assert_equal("yay\n", File.read(destfile), "dest file was not copied correctly")
end
def recursive_source_test(fromdir, todir)
@@ -110,17 +367,16 @@ class TestFileSources < Test::Unit::TestCase
def run_complex_sources(networked = false)
path = tempfile()
- @@tmpfiles.push path
# first create the source directory
FileUtils.mkdir_p path
-
# okay, let's create a directory structure
fromdir = File.join(path,"fromdir")
Dir.mkdir(fromdir)
FileUtils.cd(fromdir) {
- mkranddirsandfiles()
+ File.open("one", "w") { |f| f.puts "onefile"}
+ File.open("two", "w") { |f| f.puts "twofile"}
}
todir = File.join(path, "todir")
@@ -130,28 +386,36 @@ class TestFileSources < Test::Unit::TestCase
end
recursive_source_test(source, todir)
- return [fromdir,todir]
+ return [fromdir,todir, File.join(todir, "one"), File.join(todir, "two")]
end
def test_complex_sources_twice
- fromdir, todir = run_complex_sources
+ fromdir, todir, one, two = run_complex_sources
+ assert_trees_equal(fromdir,todir)
+ recursive_source_test(fromdir, todir)
assert_trees_equal(fromdir,todir)
+ # Now remove the whole tree and try it again.
+ [one, two].each do |f| File.unlink(f) end
+ Dir.rmdir(todir)
recursive_source_test(fromdir, todir)
assert_trees_equal(fromdir,todir)
end
def test_sources_with_deleted_destfiles
- fromdir, todir = run_complex_sources
- # then delete some files
+ fromdir, todir, one, two = run_complex_sources
assert(FileTest.exists?(todir))
- missing_files = delete_random_files(todir)
+
+ # We shouldn't have a 'two' file object in memory
+ assert_nil(@file[two], "object for 'two' is still in memory")
+ # then delete a file
+ File.unlink(two)
+
+ puts "yay"
# and run
recursive_source_test(fromdir, todir)
- missing_files.each { |file|
- assert(FileTest.exists?(file), "Deleted file %s is still missing" % file)
- }
+ assert(FileTest.exists?(two), "Deleted file was not recopied")
# and make sure they're still equal
assert_trees_equal(fromdir,todir)