diff options
author | luke <luke@980ebf18-57e1-0310-9a29-db15c13687c0> | 2006-12-09 16:40:50 +0000 |
---|---|---|
committer | luke <luke@980ebf18-57e1-0310-9a29-db15c13687c0> | 2006-12-09 16:40:50 +0000 |
commit | cdd1e6e19e7b8fc340ebcf543a30564c76e71eb9 (patch) | |
tree | 65f7511a9f6386db44c92c502de1d56c7d2f0b6d | |
parent | 01e5b692f0ba209956f4b84c7e597bd867154cbf (diff) | |
download | puppet-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.rb | 5 | ||||
-rw-r--r-- | lib/puppet/pgraph.rb | 3 | ||||
-rwxr-xr-x | lib/puppet/server/authstore.rb | 3 | ||||
-rwxr-xr-x | lib/puppet/server/fileserver.rb | 17 | ||||
-rw-r--r-- | lib/puppet/transaction.rb | 224 | ||||
-rw-r--r-- | lib/puppet/type/pfile.rb | 204 | ||||
-rwxr-xr-x | lib/puppet/type/pfile/ensure.rb | 1 | ||||
-rwxr-xr-x | lib/puppet/type/pfile/source.rb | 201 | ||||
-rw-r--r-- | lib/puppet/type/pfile/target.rb | 2 | ||||
-rw-r--r-- | lib/puppet/type/state.rb | 2 | ||||
-rwxr-xr-x | test/other/transactions.rb | 204 | ||||
-rwxr-xr-x | test/server/fileserver.rb | 5 | ||||
-rwxr-xr-x | test/types/file.rb | 305 | ||||
-rwxr-xr-x | test/types/filesources.rb | 308 |
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) |