diff options
author | Luke Kanies <luke@madstop.com> | 2005-07-19 21:49:09 +0000 |
---|---|---|
committer | Luke Kanies <luke@madstop.com> | 2005-07-19 21:49:09 +0000 |
commit | 9a5477b8c46841c6cf313ee9769afdeda559fb4b (patch) | |
tree | da2104b5af68ba7df83567477cc20b1017010b07 | |
parent | 0e94644b6e2ccbd0a3d52cb210bcfced39321194 (diff) | |
download | puppet-9a5477b8c46841c6cf313ee9769afdeda559fb4b.tar.gz puppet-9a5477b8c46841c6cf313ee9769afdeda559fb4b.tar.xz puppet-9a5477b8c46841c6cf313ee9769afdeda559fb4b.zip |
checkpoint commit
git-svn-id: https://reductivelabs.com/svn/puppet/library/trunk@424 980ebf18-57e1-0310-9a29-db15c13687c0
-rw-r--r-- | lib/puppet/type.rb | 75 | ||||
-rw-r--r-- | lib/puppet/type/pfile.rb | 438 | ||||
-rw-r--r-- | test/types/tc_file.rb | 3 |
3 files changed, 328 insertions, 188 deletions
diff --git a/lib/puppet/type.rb b/lib/puppet/type.rb index e32754914..82164a25c 100644 --- a/lib/puppet/type.rb +++ b/lib/puppet/type.rb @@ -242,9 +242,11 @@ class Type < Puppet::Element end if @objects.has_key?(newobj.name) - raise "Object '%s' of type '%s' already exists with id '%s' vs. '%s'" % + raise Puppet::Error.new( + "Object '%s' of type '%s' already exists with id '%s' vs. '%s'" % [newobj.name,newobj.class.name, @objects[newobj.name].object_id,newobj.object_id] + ) else #debug("adding %s of type %s to class list" % # [object.name,object.class]) @@ -475,12 +477,19 @@ class Type < Puppet::Element # we want to return the states in the order that each type # specifies it, because it may (as in the case of File#create) # be important - @children.each { |child| - yield child - } + if self.class.depthfirst? + @children.each { |child| + yield child + } + end self.eachstate { |state| yield state } + unless self.class.depthfirst? + @children.each { |child| + yield child + } + end end #--------------------------------------------------------------- @@ -517,19 +526,30 @@ class Type < Puppet::Element #--------------------------------------------------------------- # return the value of a parameter def parameter(name) + unless name.is_a? Symbol + name = name.intern + end return @parameters[name] end #--------------------------------------------------------------- #--------------------------------------------------------------- - def push(*child) - @children.push(*child) + def push(*childs) + unless defined? @children + @children = [] + end + childs.each { |child| + @children.push(child) + } end #--------------------------------------------------------------- #--------------------------------------------------------------- # return an actual type by name; to return the value, use 'inst[name]' def state(name) + unless name.is_a? Symbol + name = name.intern + end return @states[name] end #--------------------------------------------------------------- @@ -608,6 +628,7 @@ class Type < Puppet::Element self.nameclean(hash) + # this should probably do all states and then all parameters, right? hash.each { |param,value| #debug("adding param '%s' with value '%s'" % # [param,value]) @@ -651,6 +672,8 @@ class Type < Puppet::Element raise TypeError.new("A name must be provided to %s at initialization time" % self.class) end + + #return hash end #--------------------------------------------------------------- @@ -708,6 +731,18 @@ class Type < Puppet::Element public #--------------------------------------------------------------- + # this is a retarded hack method to get around the difference between + # component children and file children + def self.depthfirst? + if defined? @depthfirst + return @depthfirst + else + return true + end + end + #--------------------------------------------------------------- + + #--------------------------------------------------------------- # this method is responsible for collecting state changes # we always descend into the children before we evaluate our current # states @@ -725,9 +760,17 @@ class Type < Puppet::Element #end @evalcount += 1 - changes = @children.collect { |child| - child.evaluate - } + #changes = @children.collect { |child| + # child.evaluate + #} + + changes = [] + # collect all of the changes from children and states + if self.class.depthfirst? + changes << self.collect { |child| + child.evaluate + } + end # this only operates on states, not states + children self.retrieve @@ -742,6 +785,12 @@ class Type < Puppet::Element Puppet::StateChange.new(state) } end + + unless self.class.depthfirst? + changes << self.collect { |child| + child.evaluate + } + end # collect changes and return them # these changes could be from child objects or from contained states #self.collect { |child| @@ -791,15 +840,15 @@ class Type < Puppet::Element #--------------------------------------------------------------- #--------------------------------------------------------------- - # this just marks states that we definitely want to retrieve values - # on + # This just marks states that we definitely want to retrieve values + # on. There is currently no way to uncheck a parameter. def metacheck=(args) unless args.is_a?(Array) args = [args] end - # these are states that we might not have values for but we want to retrieve - # values for anyway + # these are states that we might not have 'should' + # values for but we want to retrieve 'is' values for anyway args.each { |state| unless state.is_a?(Symbol) state = state.intern diff --git a/lib/puppet/type/pfile.rb b/lib/puppet/type/pfile.rb index 99d66e47f..56ce2ee91 100644 --- a/lib/puppet/type/pfile.rb +++ b/lib/puppet/type/pfile.rb @@ -528,137 +528,63 @@ module Puppet end end - class PFileSource < Puppet::State + class PFileCopy < Puppet::State attr_accessor :source, :local - @name = :source + @name = :copy - def localshould=(value) - # in order to know if we need to sync, we need to compare our file's - # checksum to the source file's checksum - type = Puppet::Type.type(:file) - file = nil - - @source = value - # if the file is already being managed through some other mechanism... - if file = type[value] - error = Puppet::Error.new( - "Cannot currently use managed files (%s) as sources" % value) - raise error - else - self.parent.pin(@source) + def retrieve + sum = nil + unless sum = self.parent.state(:checksum) + raise Puppet::Error.new( + "Cannot copy without knowing the sum state of %s" % + self.parent.path + ) end + @is = sum.is end - def retrieve + def should=(source) + @local = true # redundant for now + @source = source type = Puppet::Type.type(:file) + sourcesum = nil stat = File.stat(@source) case stat.ftype when "file": - @should = type[@source].state(:checksum).is || -1 - @is = self.parent[:checksum] + unless sourcesum = type[@source].state(:checksum).is + raise Puppet::Error.new( + "Could not retrieve checksum of source %s" % + @source + ) + end when "directory": error = Puppet::Error.new( - "Somehow got told to create dir %s" % self.parent.name) + "Somehow got told to copy dir %s" % self.parent.name) raise error else error = Puppet::Error.new( "Cannot use files of type %s as source" % stat.ftype) raise error end - end - def should=(value) - lreg = Regexp.new("^file://") - oreg = Regexp.new("^(\s+)://") - - # if we're a local file... - if value =~ /^\// or value =~ lreg - @local = true - - # if they passed a uri instead of just a filename... - if value =~ lreg - value.sub(lreg,'') - unless value =~ /\// - error = Puppet::Error.new("Invalid file name: %s" % value) - raise error - end - end - - # XXX for now, only allow files that already exist - unless FileTest.exist?(value) - Puppet.err "Cannot use non-existent file %s as source" % - value - @should = nil - @nil = nil - return nil - end - elsif value =~ oreg - @local = false - - # currently, we only support local sources - error = Puppet::Error.new("No support for proto %s" % $1) - raise error - else - error = Puppet::Error.new("Invalid URI %s" % value) - raise error - end - - # if they haven't already specified a checksum type to us, then - # specify that we need to collect checksums and default to md5 - unless self.parent[:checksum] - self.parent[:checksum] = "md5" - end - - self.localshould = value + @should = sourcesum end def sync - # this method is kind of interesting - # we could choose to do this two ways: either directly - # compare and then copy over, as we currently are, or we could - # just define a 'refresh' method for this state and let the existing - # event mechanisms notify us when there's a change - - unless defined? @source - Puppet.err "No source set for %s" % self.parent.name - return nil - end - - unless FileTest.exists?(@source) - Puppet.err "Source %s does not exist -- cannot copy to %s" % - [@source, self.parent.name] - return nil - end - - if @should == -1 - Puppet.warning "Trying again for source checksum" - type = Puppet::Type.type(:file) - file = nil - - if file = type[@source] - @should = file.state(:checksum).is - if @should.nil? or @should == -1 - error = Puppet::Error.new( - "Could not retrieve checksum state for %s(%s)" % - [file.name,@should]) - raise error - end - else - error = Puppet::Error.new("%s is somehow not managed" % @source) - raise error - end - end - - if FileTest.file?(@source) + @backed = false + # try backing ourself up before we overwrite + if FileTest.file?(self.parent.name) if bucket = self.parent[:filebucket] bucket.backup(self.parent.name) + @backed = true elsif str = self.parent[:backup] # back the file up begin FileUtils.cp(self.parent.name, self.parent.name + self.parent[:backup]) + @backed = true rescue => detail # since they said they want a backup, let's error out # if we couldn't make one @@ -676,7 +602,32 @@ module Puppet case stat.ftype when "file": begin - FileUtils.cp(@source, self.parent.name) + if FileTest.exists?(self.parent.name) + # get the file here + FileUtils.cp(@source, self.parent.name + ".tmp") + if FileTest.exists?(self.parent.name + ".puppet-bak") + Puppet.warning "Deleting backup of %s" % + self.parent.name + File.unlink(self.parent.name + ".puppet-bak") + end + # rename the existing one + File.rename( + self.parent.name, + self.parent.name + ".puppet-bak" + ) + # move the new file into place + File.rename( + self.parent.name + ".tmp", + self.parent.name + ) + # if we've made a backup, then delete the old file + if @backed + File.unlink(self.parent.name + ".puppet-bak") + end + else + # the easy case + FileUtils.cp(@source, self.parent.name) + end rescue => detail # since they said they want a backup, let's error out # if we couldn't make one @@ -685,17 +636,15 @@ module Puppet raise error end when "directory": - error = Puppet::Error.new( - "Somehow got told to sync directory %s" % + raise Puppet::Error.new( + "Somehow got told to copy directory %s" % self.parent.name) - raise error when "link": dest = File.readlink(@source) Puppet::State::PFileLink.create(@dest,self.parent.path) else - error = Puppet::Error.new("Cannot use files of type %s as source" % - stat.ftype) - raise error + raise Puppet::Error.new( + "Cannot use files of type %s as source" % stat.ftype) end else raise Puppet::Error.new("Somehow got a non-local source") @@ -709,105 +658,239 @@ module Puppet attr_reader :params, :source # class instance variable + #Puppet::State::PFileSource, @states = [ Puppet::State::PFileCreate, - Puppet::State::PFileSource, + Puppet::State::PFileChecksum, + Puppet::State::PFileCopy, Puppet::State::PFileUID, Puppet::State::PFileGroup, Puppet::State::PFileMode, - Puppet::State::PFileChecksum, Puppet::State::PFileSetUID, Puppet::State::PFileLink ] + #:source, @parameters = [ :path, :recurse, :filebucket, + :source, :backup ] @name = :file @namevar = :path + @depthfirst = false + def initialize(hash) - arghash = hash.dup + if hash.include?("recurse") + hash[:recurse] = hash["recurse"] + hash.delete("recurse") + end + @arghash = hash.dup super + #self.nameclean(@arghash) + #Puppet.err "arghash is %s" % @arghash.inspect @stat = nil - # if recursion is enabled and we're a directory... - if @parameters[:recurse] - if FileTest.exist?(self.name) and self.stat.directory? - self.recurse(arghash) - elsif @states.include?(:source) # uh, yeah, uh... - Puppet.err "Ugh!" - self.recurse(arghash) - else - Puppet.err "No recursion or source for %s" % self.name - end + # yay, super-hack! + #if @states.include?(:create) + # if @states[:create].should == "directory" + # if @states.include?(:source) + # Puppet.warning "Deleting source for directory %s" % + # self.name + # @states.delete(:source) + # end +# +# if @states.include?(:checksum) +# Puppet.warning "Deleting checksum for directory %s" % +# self.name +# @states.delete(:checksum) +# end +# else +# Puppet.info "Create is %s for %s" % +# [@states[:create].should,self.name] +# end +# end + end + + # pinning is like recursion, except that it's recursion across + # the pinned file's tree, instead of our own + # if recursion is turned off, then this whole thing is pretty easy + def paramsource=(source) + @parameters[:source] = source + @source = source + + pinparams = [:mode, :owner, :group, :checksum] + + # verify we support the proto + if @source =~ /^file:\/\/(\/.+)/ + @source = $1 + elsif @source =~ /(\w+):\/\/(\/.+)/ + raise Puppet::Error("Protocol %s not supported" % $1) end - # yay, super-hack! - if @states.include?(:create) - if @states[:create].should == "directory" - if @states.include?(:source) - Puppet.warning "Deleting source for directory %s" % - self.name - @states.delete(:source) - end + # verify that the source exists + unless FileTest.exists?(@source) + raise Puppet::Error( + "Files must exist to be sources; %s does not" % @source + ) + end - if @states.include?(:checksum) - Puppet.warning "Deleting checksum for directory %s" % - self.name - @states.delete(:checksum) - end + # Check whether we'll be creating the file or whether it already + # exists. The root of the destination tree will cause the + # recursive creation of all of the objects, and then all the + # children of the tree will just pull existing objects + if obj = self.class[@source] + Puppet.info "%s is already in memory" % @source + if obj.managed? + raise Puppet::Error( + "You cannot currently use managed files as sources;" + + "%s is managed" % @source + ) else - Puppet.info "Create is %s for %s" % - [@states[:create].should,self.name] + @sourceobj = obj + + # verify they're looking up the correct info + check = [] + pinparams.each { |param| + unless @sourceobj.state(param) + check.push param + end + } + + @sourceobj[:check] = check + end + else # the obj does not exist yet... + Puppet.info "%s is not in memory" % @source + args = {} + + args[:check] = pinparams + args[:name] = @source + + if @arghash.include?(:recurse) + args[:recurse] = @parameters[:recurse] + end + + # if the checksum got specified... + if @states.include?(:checksum) + args[:checksum] = @states[:checksum].checktype + else # default to md5 + args[:checksum] = "md5" end - end - end - # this is kind of equivalent to copying the actual file - def pin(path) - pinparams = [:owner, :group, :mode, :checksum] + # now create the tree of objects + # if recursion is turned on, this will create the whole tree + # and we'll just pick it up as our own recursive stuff + @sourceobj = self.class.new(args) + end - obj = Puppet::Type::PFile.new( - :name => path, - :check => pinparams - ) - obj.evaluate # XXX *shudder* + # okay, now we've got the object; retrieve its values, so we + # can make them our 'should' values + @sourceobj.retrieve - # only copy the inode and content states, not all of the metastates + # if the pin states, these can be done easily [:owner, :group, :mode].each { |state| unless @states.include?(state) # this copies the source's 'is' value to our 'should' - self[state] = obj[state] + # but doesn't override existing settings + self[state] = @sourceobj[state] end } - if FileTest.directory?(path) + if FileTest.directory?(@source) self[:create] = "directory" # see amazingly crappy hack in initialize() #self.delete(:source) - Puppet.info "Not sourcing checksum of directory %s" % path + Puppet.info "Not sourcing checksum of directory %s" % @source + + # now, make sure that if they've got children we model those, too + curchildren = {} + if defined? @children + Puppet.info "Collecting info about existing children" + @children.each { |child| + name = File.basename(child.name) + curchildren[name] = child + } + end + @sourceobj.each { |child| + Puppet.info "Looking at %s => %s" % + [@sourceobj.name, child.name] + if child.is_a?(Puppet::Type::PFile) + name = File.basename(child.name) + + if curchildren.include?(name) # the file's in both places + # set the source accordingly + Puppet.info "Adding %s as an existing child" % name + curchildren[name][:source] = child.name + else # they have it but we don't + # XXX we have serious probability of repeating + # bugs in paramrecurse + Puppet.info "Adding %s as a new child" % child.name + fullname = File.join(self.name, name) + newchild = nil + + if newchild = self.class[fullname] + Puppet.info "%s is already being managed" % + fullname + # first pin the file + newchild[:source] = child.name + + # then make sure they're collecting the same + # info we are + @arghash.each { |var,value| + next if var == :path + next if var == :name + # unless they've already got it set... + unless newchild.state(var) or newchild.parameter(var) + newchild[var] = value + end + } + else # create it anew + Puppet.info "Managing %s" % fullname + + # told you we'd repeat the name bugs from recurse + args = {:name => fullname} + @arghash.each { |var,value| + next if var == :path + next if var == :name + next if var == :source + args[var] = value + } + args[:source] = child.name + Puppet.info "Creating child with %s" % args.inspect + newchild = self.class.new(args) + end + self.push newchild + end + end + } + else # checksums are, like, special if @states.include?(:checksum) + # this is weird, because normally setting a 'should' state + # on checksums just manipulates the contents of the state + # database if @states[:checksum].checktype == - obj.state(:checksum).checktype - @states[:checksum].should = obj[:checksum] + @sourceobj.state(:checksum).checktype + @states[:checksum].should = @sourceobj[:checksum] else - Puppet.warning "Source file '%s' checksum type '%s' is incompatible with destination file '%s' checksum type '%s'; defaulting to md5 for both" % - [obj.name, obj.state(:checksum).checktype, + Puppet.warning("Source file '%s' checksum type '%s' is " + + "incompatible with destination file '%s' checksum " + + "type '%s'; defaulting to md5 for both" % + [@sourceobj.name, @sourceobj.state(:checksum).checktype, self.name, self[:checksum].checktype] + ) # and then, um, default to md5 for everyone? - unless @source.state[:checksum].checktype == "md5" + unless @sourceobj.state[:checksum].checktype == "md5" Puppet.warning "Changing checktype on %s to md5" % file.name - @source.state[:checksum].should = "md5" + @sourceobj.state[:checksum].should = "md5" end unless @states[:ckecksum].checktype == "md5" @@ -817,59 +900,66 @@ module Puppet end end else - self[:checksum] = obj.state(:checksum).checktype - @states[:checksum].should = obj[:checksum] + self[:check] = [:checksum] + #self[:checksum] = @sourceobj.state(:checksum).checktype + #@states[:checksum].should = @sourceobj[:checksum] end + + self[:copy] = @sourceobj.name end end - def recurse(arghash) + def paramrecurse=(value) + @parameters[:recurse] = value + unless FileTest.exist?(self.name) and self.stat.directory? + Puppet.info "%s is not a directory; not recursing" % + self.name + return + end + Puppet.err "Recursing!" - recurse = self[:recurse] + recurse = value # 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 + #elsif recurse =~ /^inf/ # infinite recursion + else # anything else is infinite recursion recurse = true end end # unless we're at the end of the recursion if recurse != 0 - arghash.delete("recurse") + @arghash.delete("recurse") if recurse.is_a?(Integer) recurse -= 1 # reduce the level of recursion end - arghash[:recurse] = recurse + @arghash[:recurse] = recurse - # now make each contained file/dir a child - unless defined? @children - @children = [] - end # make sure we don't have any remaining ':name' params - self.nameclean(arghash) + self.nameclean(@arghash) Dir.foreach(self.name) { |file| next if file =~ /^\.\.?/ # skip . and .. - arghash[:path] = File.join(self.name,file) + @arghash[:path] = File.join(self.name,file) child = nil # if the file already exists... - if child = self.class[arghash[:path]] - arghash.each { |var,value| + if child = self.class[@arghash[:path]] + @arghash.each { |var,value| next if var == :path child[var] = value } else # create it anew #notice "Creating new file with args %s" % - # arghash.inspect - child = self.class.new(arghash) + # @arghash.inspect + child = self.class.new(@arghash) end - @children.push child + self.push child } end end diff --git a/test/types/tc_file.rb b/test/types/tc_file.rb index d7df72311..677cc6ae5 100644 --- a/test/types/tc_file.rb +++ b/test/types/tc_file.rb @@ -353,6 +353,7 @@ class TestFile < Test::Unit::TestCase assert_nothing_raised { comp.sync } + assert(FileTest.exists?(topath)) from = File.open(frompath) { |o| o.read } to = File.open(topath) { |o| o.read } assert_equal(from,to) @@ -362,7 +363,7 @@ class TestFile < Test::Unit::TestCase end def test_xcomplicatedlocalsource - path = "/tmp/filesourcetest" + path = "/tmp/complsourcetest" @@tmpfiles.push path system("mkdir -p #{path}") fromdir = File.join(path,"fromdir") |