diff options
author | Luke Kanies <luke@madstop.com> | 2008-02-25 18:28:26 -0500 |
---|---|---|
committer | Luke Kanies <luke@madstop.com> | 2008-02-25 18:28:26 -0500 |
commit | 9d2f45df3d951ef59a44a7fb81a38586f04884b4 (patch) | |
tree | 0c97adcca32ba9ad2e20b2457f3db0c4b91f8376 /lib/puppet | |
parent | 0afea69c06742eff1e8d8bd7df13c9c0e4c397c0 (diff) | |
parent | 04892ee723d1a687c83eb6c99b5c6a6c76bbcbc9 (diff) | |
download | puppet-9d2f45df3d951ef59a44a7fb81a38586f04884b4.tar.gz puppet-9d2f45df3d951ef59a44a7fb81a38586f04884b4.tar.xz puppet-9d2f45df3d951ef59a44a7fb81a38586f04884b4.zip |
Merge branch '0.24.x'
Diffstat (limited to 'lib/puppet')
27 files changed, 459 insertions, 531 deletions
diff --git a/lib/puppet/dsl.rb b/lib/puppet/dsl.rb index 4fbce556c..966feaf9b 100644 --- a/lib/puppet/dsl.rb +++ b/lib/puppet/dsl.rb @@ -46,58 +46,56 @@ # # apply -module Puppet - # Provide the actual commands for acting like a language. - module DSL - def aspect(name, options = {}, &block) - Puppet::Aspect.new(name, options, &block) - end +require 'puppet' - def acquire(*names) - names.each do |name| - if aspect = Puppet::Aspect[name] - unless aspect.evaluated? - aspect.evaluate - end - else - raise "Could not find aspect %s" % name +# Provide the actual commands for acting like a language. +module Puppet::DSL + def aspect(name, options = {}, &block) + Puppet::DSL::Aspect.new(name, options, &block) + end + + def acquire(*names) + names.each do |name| + if aspect = Puppet::DSL::Aspect[name] + unless aspect.evaluated? + aspect.evaluate end + else + raise "Could not find aspect %s" % name end end + end - def apply - bucket = export() - catalog = bucket.to_catalog - catalog.apply - end + def apply + bucket = export() + catalog = bucket.to_catalog + catalog.apply + end - def export - objects = Puppet::Aspect.collect do |name, aspect| - if aspect.evaluated? - aspect.export - end - end.reject { |a| a.nil? }.flatten.collect do |obj| - obj.to_trans + def export + objects = Puppet::DSL::Aspect.collect do |name, aspect| + if aspect.evaluated? + aspect.export end - bucket = Puppet::TransBucket.new(objects) - bucket.name = "top" - bucket.type = "class" - - return bucket + end.reject { |a| a.nil? }.flatten.collect do |obj| + obj.to_trans end + bucket = Puppet::TransBucket.new(objects) + bucket.name = "top" + bucket.type = "class" - def init - unless Process.uid == 0 - Puppet[:confdir] = File.expand_path("~/.puppet") - Puppet[:vardir] = File.expand_path("~/.puppet/var") - end - Puppet[:user] = Process.uid - Puppet[:group] = Process.gid - Puppet::Util::Log.newdestination(:console) - Puppet::Util::Log.level = :info - end + return bucket + end - private + def init + unless Process.uid == 0 + Puppet[:confdir] = File.expand_path("~/.puppet") + Puppet[:vardir] = File.expand_path("~/.puppet/var") + end + Puppet[:user] = Process.uid + Puppet[:group] = Process.gid + Puppet::Util::Log.newdestination(:console) + Puppet::Util::Log.level = :info end class Aspect @@ -224,10 +222,10 @@ module Puppet end def newresource(type, name, params = {}) - if self.is_a?(Puppet::Aspect) + if self.is_a?(Puppet::DSL::Aspect) source = self else - source = Puppet::Aspect[:main] + source = Puppet::DSL::Aspect[:main] end unless obj = @@objects[type][name] obj = Resource.new :title => name, :type => type.name, @@ -262,7 +260,7 @@ module Puppet env = nil end @node.parameters = Facter.to_hash - @compile = Puppet::Parser::Compile.new(@node, @interp.send(:parser, env)) + @compile = Puppet::Parser::Compiler.new(@node, @interp.send(:parser, env)) @scope = @compile.topscope end @scope diff --git a/lib/puppet/file_serving/file_base.rb b/lib/puppet/file_serving/file_base.rb index 7f169d1ea..06b3ad9ef 100644 --- a/lib/puppet/file_serving/file_base.rb +++ b/lib/puppet/file_serving/file_base.rb @@ -9,16 +9,28 @@ require 'puppet/file_serving' class Puppet::FileServing::FileBase attr_accessor :key + # Does our file exist? + def exist? + begin + stat + return true + rescue => detail + return false + end + end + # Return the full path to our file. Fails if there's no path set. def full_path raise(ArgumentError, "You must set a path to get a file's path") unless self.path - relative_path ? File.join(path, relative_path) : path + if relative_path.nil? or relative_path == "" + path + else + File.join(path, relative_path) + end end def initialize(key, options = {}) - raise ArgumentError.new("Files must not be fully qualified") if path =~ /^#{::File::SEPARATOR}/ - @key = key @links = :manage diff --git a/lib/puppet/file_serving/metadata.rb b/lib/puppet/file_serving/metadata.rb index e26e75844..56712122c 100644 --- a/lib/puppet/file_serving/metadata.rb +++ b/lib/puppet/file_serving/metadata.rb @@ -28,6 +28,25 @@ class Puppet::FileServing::Metadata < Puppet::FileServing::FileBase attr_reader :path, :owner, :group, :mode, :checksum_type, :checksum, :ftype, :destination + PARAM_ORDER = [:mode, :ftype, :owner, :group] + + def attributes_with_tabs + desc = [] + PARAM_ORDER.each { |check| + check = :ftype if check == :type + desc << send(check) + } + + case ftype + when "file", "directory": desc << checksum + when "link": desc << @destination + else + raise ArgumentError, "Cannot manage files of type %s" % ftype + end + + return desc.join("\t") + end + def checksum_type=(type) raise(ArgumentError, "Unsupported checksum type %s" % type) unless respond_to?("%s_file" % type) @@ -45,13 +64,19 @@ class Puppet::FileServing::Metadata < Puppet::FileServing::FileBase @ftype = stat.ftype - # Set the octal mode, but as a string. - @mode = "%o" % (stat.mode & 007777) + # We have to mask the mode, yay. + @mode = stat.mode & 007777 - if stat.ftype == "symlink" + case stat.ftype + when "file": + @checksum = ("{%s}" % @checksum_type) + send("%s_file" % @checksum_type, real_path) + when "directory": # Always just timestamp the directory. + sumtype = @checksum_type.to_s =~ /time/ ? @checksum_type : "ctime" + @checksum = ("{%s}" % sumtype) + send("%s_file" % sumtype, path).to_s + when "link": @destination = File.readlink(real_path) else - @checksum = get_checksum(real_path) + raise ArgumentError, "Cannot manage files of type %s" % stat.ftype end end @@ -59,11 +84,4 @@ class Puppet::FileServing::Metadata < Puppet::FileServing::FileBase @checksum_type = "md5" super end - - private - - # Retrieve our checksum. - def get_checksum(path) - ("{%s}" % @checksum_type) + send("%s_file" % @checksum_type, path) - end end diff --git a/lib/puppet/metatype/evaluation.rb b/lib/puppet/metatype/evaluation.rb index b3b6570b2..08756e988 100644 --- a/lib/puppet/metatype/evaluation.rb +++ b/lib/puppet/metatype/evaluation.rb @@ -125,26 +125,14 @@ class Puppet::Type raise Puppet::DevError, "Parameter ensure defined but missing from current values" end if @parameters.include?(:ensure) and ! ensureparam.insync?(currentvalues[ensureparam]) -# self.info "ensuring %s from %s" % -# [@parameters[:ensure].should, @parameters[:ensure].is] changes << Puppet::PropertyChange.new(ensureparam, currentvalues[ensureparam]) # Else, if the 'ensure' property is correctly absent, then do # nothing elsif @parameters.include?(:ensure) and currentvalues[ensureparam] == :absent - # self.info "Object is correctly absent" return [] else -# if @parameters.include?(:ensure) -# self.info "ensure: Is: %s, Should: %s" % -# [@parameters[:ensure].is, @parameters[:ensure].should] -# else -# self.info "no ensure property" -# end changes = properties().find_all { |property| - unless currentvalues.include?(property) - raise Puppet::DevError, "Property %s does not have a current value", - [property.name] - end + currentvalues[property] ||= :absent ! property.insync?(currentvalues[property]) }.collect { |property| Puppet::PropertyChange.new(property, currentvalues[property]) @@ -152,10 +140,7 @@ class Puppet::Type end if Puppet[:debug] and changes.length > 0 - self.debug("Changing " + changes.collect { |ch| - ch.property.name - }.join(",") - ) + self.debug("Changing " + changes.collect { |ch| ch.property.name }.join(",")) end changes diff --git a/lib/puppet/network/client.rb b/lib/puppet/network/client.rb index 283436e95..0a0a72345 100644 --- a/lib/puppet/network/client.rb +++ b/lib/puppet/network/client.rb @@ -95,9 +95,7 @@ class Puppet::Network::Client # We have to start the HTTP connection manually before we start # sending it requests or keep-alive won't work. - if @driver.respond_to? :start - @driver.start - end + @driver.start if @driver.respond_to? :start @local = false elsif hash.include?(driverparam) @@ -107,8 +105,7 @@ class Puppet::Network::Client end @local = true else - raise Puppet::Network::ClientError, "%s must be passed a Server or %s" % - [self.class, driverparam] + raise Puppet::Network::ClientError, "%s must be passed a Server or %s" % [self.class, driverparam] end end diff --git a/lib/puppet/network/client/master.rb b/lib/puppet/network/client/master.rb index 6d1a0235f..390f3d4e2 100644 --- a/lib/puppet/network/client/master.rb +++ b/lib/puppet/network/client/master.rb @@ -40,8 +40,10 @@ class Puppet::Network::Client::Master < Puppet::Network::Client facts["clientversion"] = Puppet.version.to_s # And add our environment as a fact. - facts["environment"] = Puppet[:environment] - + unless facts.include?("environment") + facts["environment"] = Puppet[:environment] + end + facts end diff --git a/lib/puppet/network/handler/fileserver.rb b/lib/puppet/network/handler/fileserver.rb index e6378bf01..a9a95bcfe 100755 --- a/lib/puppet/network/handler/fileserver.rb +++ b/lib/puppet/network/handler/fileserver.rb @@ -5,6 +5,9 @@ require 'cgi' require 'delegate' require 'sync' +require 'puppet/file_serving' +require 'puppet/file_serving/metadata' + class Puppet::Network::Handler AuthStoreError = Puppet::AuthStoreError class FileServerError < Puppet::Error; end @@ -59,40 +62,27 @@ class Puppet::Network::Handler # Describe a given file. This returns all of the manageable aspects # of that file. - def describe(url, links = :ignore, client = nil, clientip = nil) + def describe(url, links = :follow, client = nil, clientip = nil) links = links.intern if links.is_a? String - if links == :manage - raise Puppet::Network::Handler::FileServerError, "Cannot currently copy links" - end - mount, path = convert(url, client, clientip) - if client - mount.debug "Describing %s for %s" % [url, client] - end + mount.debug("Describing %s for %s" % [url, client]) if client + + # Remove any leading slashes, since Metadata doesn't like them, yo. + metadata = Puppet::FileServing::Metadata.new(url, :path => mount.path(client), :relative_path => path.sub(/^\//, ''), :links => links) - obj = nil - unless obj = mount.getfileobject(path, links, client) + return "" unless metadata.exist? + + begin + metadata.collect_attributes + rescue => detail + puts detail.backtrace if Puppet[:trace] + Puppet.err detail return "" end - currentvalues = mount.check(obj) - - desc = [] - CHECKPARAMS.each { |check| - if value = currentvalues[check] - desc << value - else - if check == "checksum" and currentvalues[:type] == "file" - mount.notice "File %s does not have data for %s" % - [obj.name, check] - end - desc << nil - end - } - - return desc.join("\t") + return metadata.attributes_with_tabs end # Create a new fileserving module. @@ -140,26 +130,18 @@ class Puppet::Network::Handler def list(url, links = :ignore, recurse = false, ignore = false, client = nil, clientip = nil) mount, path = convert(url, client, clientip) - if client - mount.debug "Listing %s for %s" % [url, client] - end + mount.debug "Listing %s for %s" % [url, client] if client - obj = nil - unless mount.path_exists?(path, client) - return "" - end + return "" unless mount.path_exists?(path, client) desc = mount.list(path, recurse, ignore, client) if desc.length == 0 - mount.notice "Got no information on //%s/%s" % - [mount, path] + mount.notice "Got no information on //%s/%s" % [mount, path] return "" end - - desc.collect { |sub| - sub.join("\t") - }.join("\n") + + desc.collect { |sub| sub.join("\t") }.join("\n") end def local? @@ -213,12 +195,7 @@ class Puppet::Network::Handler return "" end - str = nil - if links == :manage - raise Puppet::Error, "Cannot copy links yet." - else - str = mount.read_file(path, client) - end + str = mount.read_file(path, client) if @local return str diff --git a/lib/puppet/network/http_pool.rb b/lib/puppet/network/http_pool.rb index 99f09a90c..69574d8fd 100644 --- a/lib/puppet/network/http_pool.rb +++ b/lib/puppet/network/http_pool.rb @@ -1,6 +1,9 @@ require 'puppet/sslcertificates/support' require 'net/https' +module Puppet::Network +end + # Manage Net::HTTP instances for keep-alive. module Puppet::Network::HttpPool # This handles reading in the key and such-like. diff --git a/lib/puppet/network/http_server/mongrel.rb b/lib/puppet/network/http_server/mongrel.rb index d340f3d63..6b2325d29 100644 --- a/lib/puppet/network/http_server/mongrel.rb +++ b/lib/puppet/network/http_server/mongrel.rb @@ -33,6 +33,7 @@ require 'xmlrpc/server' require 'puppet/network/xmlrpc/server' require 'puppet/network/http_server' require 'puppet/network/client_request' +require 'puppet/network/handler' require 'puppet/daemon' require 'resolv' diff --git a/lib/puppet/network/http_server/webrick.rb b/lib/puppet/network/http_server/webrick.rb index e4f00dd73..568b4e798 100644 --- a/lib/puppet/network/http_server/webrick.rb +++ b/lib/puppet/network/http_server/webrick.rb @@ -8,6 +8,7 @@ require 'puppet/sslcertificates/support' require 'puppet/network/xmlrpc/webrick_servlet' require 'puppet/network/http_server' require 'puppet/network/client' +require 'puppet/network/handler' module Puppet class ServerError < RuntimeError; end @@ -133,7 +134,7 @@ module Puppet handlers.collect { |handler, args| hclass = nil - unless hclass = Handler.handler(handler) + unless hclass = Puppet::Network::Handler.handler(handler) raise ServerError, "Invalid handler %s" % handler end hclass.new(args) diff --git a/lib/puppet/node/catalog.rb b/lib/puppet/node/catalog.rb index ee4cedd4b..d680de9a0 100644 --- a/lib/puppet/node/catalog.rb +++ b/lib/puppet/node/catalog.rb @@ -1,4 +1,6 @@ require 'puppet/indirector' +require 'puppet/pgraph' +require 'puppet/transaction' require 'puppet/util/tagging' @@ -69,7 +71,10 @@ class Puppet::Node::Catalog < Puppet::PGraph @resource_table[ref] = resource # If the name and title differ, set up an alias - self.alias(resource, resource.name) if resource.respond_to?(:name) and resource.respond_to?(:title) and resource.name != resource.title + #self.alias(resource, resource.name) if resource.respond_to?(:name) and resource.respond_to?(:title) and resource.name != resource.title + if resource.respond_to?(:name) and resource.respond_to?(:title) and resource.name != resource.title + self.alias(resource, resource.name) if resource.class.isomorphic? + end resource.catalog = self if resource.respond_to?(:catalog=) and ! is_relationship_graph @@ -499,7 +504,7 @@ class Puppet::Node::Catalog < Puppet::PGraph map.clear - result.add_class *self.classes + result.add_class(*self.classes) result.tag(*self.tags) return result diff --git a/lib/puppet/parser/interpreter.rb b/lib/puppet/parser/interpreter.rb index d4b7449fb..d4655c403 100644 --- a/lib/puppet/parser/interpreter.rb +++ b/lib/puppet/parser/interpreter.rb @@ -24,8 +24,13 @@ class Puppet::Parser::Interpreter # evaluate our whole tree def compile(node) - raise Puppet::ParseError, "Could not parse configuration; cannot compile" unless env_parser = parser(node.environment) - return Puppet::Parser::Compiler.new(node, env_parser).compile + raise Puppet::ParseError, "Could not parse configuration; cannot compile on node %s" % node.name unless env_parser = parser(node.environment) + begin + return Puppet::Parser::Compiler.new(node, env_parser).compile + rescue => detail + puts detail.backtrace if Puppet[:trace] + raise Puppet::Error, detail.to_s + " on node %s" % node.name + end end # create our interpreter diff --git a/lib/puppet/provider/package/gem.rb b/lib/puppet/provider/package/gem.rb index f73694779..bb09bc5b0 100755 --- a/lib/puppet/provider/package/gem.rb +++ b/lib/puppet/provider/package/gem.rb @@ -78,7 +78,11 @@ Puppet::Type.type(:package).provide :gem, :parent => Puppet::Provider::Package d command << @resource[:name] end - gemcmd(*command) + output = gemcmd(*command) + # Apparently some stupid gem versions don't exit non-0 on failure + if output.include?("ERROR") + self.fail "Could not install: %s" % output.chomp + end end def latest diff --git a/lib/puppet/provider/package/pkgdmg.rb b/lib/puppet/provider/package/pkgdmg.rb index 2020b6b56..2614d0950 100644 --- a/lib/puppet/provider/package/pkgdmg.rb +++ b/lib/puppet/provider/package/pkgdmg.rb @@ -145,11 +145,8 @@ file system and not via a URL method." begin open(cached_source) do |dmg| xml_str = hdiutil "mount", "-plist", "-nobrowse", "-readonly", "-noidme", "-mountrandom", "/tmp", dmg.path - ptable = Plist::parse_xml xml_str - # JJM Filter out all mount-paths into a single array, discard the rest. - mounts = ptable['system-entities'].collect { |entity| - entity['mount-point'] - }.select { |mountloc|; mountloc } + # JJM THIS IS A HORRIBLE HACK (Well, actually it's not so bad...) + mounts = xml_str.scan(/<string>(\/tmp.*?)<\/string>/)[0] begin mounts.each do |fspath| Dir.entries(fspath).select { |f| diff --git a/lib/puppet/provider/service/init.rb b/lib/puppet/provider/service/init.rb index d9919f58c..274c334a3 100755 --- a/lib/puppet/provider/service/init.rb +++ b/lib/puppet/provider/service/init.rb @@ -110,7 +110,7 @@ Puppet::Type.type(:service).provide :init, :parent => :base do # we just return that; otherwise, we return false, which causes it to # fallback to other mechanisms. def statuscmd - if @resource[:hasstatus] + if @resource[:hasstatus] == :true return [self.initscript, :status] else return false diff --git a/lib/puppet/sslcertificates.rb b/lib/puppet/sslcertificates.rb index bd0ce8c92..0c579d0ad 100755 --- a/lib/puppet/sslcertificates.rb +++ b/lib/puppet/sslcertificates.rb @@ -72,7 +72,7 @@ module Puppet::SSLCertificates subject_alt_name << 'DNS:' + name.sub(/^[^.]+./, "puppet.") # add puppet.domain as an alias end key_usage = %w{digitalSignature keyEncipherment} - ext_key_usage = %w{serverAuth clientAuth} + ext_key_usage = %w{serverAuth clientAuth emailProtection} when :ocsp: basic_constraint = "CA:FALSE" key_usage = %w{nonRepudiation digitalSignature} diff --git a/lib/puppet/type.rb b/lib/puppet/type.rb index c4e154d47..09003e8f5 100644 --- a/lib/puppet/type.rb +++ b/lib/puppet/type.rb @@ -257,10 +257,7 @@ class Type rescue ArgumentError, Puppet::Error, TypeError raise rescue => detail - error = Puppet::DevError.new( - "Could not set %s on %s: %s" % - [attr, self.class.name, detail] - ) + error = Puppet::DevError.new( "Could not set %s on %s: %s" % [attr, self.class.name, detail]) error.set_backtrace(detail.backtrace) raise error end @@ -422,10 +419,6 @@ end require 'puppet/propertychange' require 'puppet/provider' -require 'puppet/type/component' -require 'puppet/type/file' -require 'puppet/type/filebucket' -require 'puppet/type/tidy' - - +# Always load these types. +require 'puppet/type/component' diff --git a/lib/puppet/type/file.rb b/lib/puppet/type/file.rb index 0ec54d907..3518e8bb3 100644 --- a/lib/puppet/type/file.rb +++ b/lib/puppet/type/file.rb @@ -5,10 +5,12 @@ require 'uri' require 'fileutils' require 'puppet/network/handler' require 'puppet/util/diff' +require 'puppet/util/checksums' module Puppet newtype(:file) do include Puppet::Util::MethodHelper + include Puppet::Util::Checksums @doc = "Manages local files, including setting ownership and permissions, creation of both files and directories, and retrieving entire files from remote servers. As Puppet matures, it @@ -173,11 +175,9 @@ module Puppet recursion), and ``follow`` will manage the file to which the link points." - newvalues(:follow, :manage, :ignore) + newvalues(:follow, :manage) - # :ignore and :manage behave equivalently on local files, - # but don't copy remote links - defaultto :ignore + defaultto :manage end newparam(:purge, :boolean => true) do @@ -779,9 +779,7 @@ module Puppet def remove_existing(should) return unless s = stat(true) - unless handlebackup - self.fail "Could not back up; will not replace" - end + self.fail "Could not back up; will not replace" unless handlebackup unless should.to_s == "link" return if s.ftype.to_s == should.to_s @@ -790,8 +788,7 @@ module Puppet case s.ftype when "directory": if self[:force] == :true - debug "Removing existing directory for replacement with %s" % - should + debug "Removing existing directory for replacement with %s" % should FileUtils.rmtree(self[:path]) else notice "Not removing directory; use 'force' to override" @@ -1031,54 +1028,38 @@ module Puppet return [sourceobj, path.sub(/\/\//, '/')] end - # Write out the file. We open the file correctly, with all of the - # uid and mode and such, and then yield the file handle for actual - # writing. - def write(property, usetmp = true) - mode = self.should(:mode) + # Write out the file. Requires the content to be written, + # the property name for logging, and the checksum for validation. + def write(content, property, checksum = nil) + if validate = validate_checksum? + # Use the appropriate checksum type -- md5, md5lite, etc. + sumtype = property(:checksum).checktype + checksum ||= "{#{sumtype}}" + property(:checksum).send(sumtype, content) + end remove_existing(:file) - # The temporary file - path = nil - if usetmp - path = self[:path] + ".puppettmp" - else - path = self[:path] - end - - # As the correct user and group - write_if_writable(File.dirname(path)) do - f = nil - # Open our file with the correct modes - if mode - Puppet::Util.withumask(000) do - f = File.open(path, - File::CREAT|File::WRONLY|File::TRUNC, mode) - end - else - f = File.open(path, File::CREAT|File::WRONLY|File::TRUNC) - end + use_temporary_file = (content.length != 0) + path = self[:path] + path += ".puppettmp" if use_temporary_file - # Yield it - yield f + mode = self.should(:mode) # might be nil + umask = mode ? 000 : 022 - f.flush - f.close + Puppet::Util.withumask(umask) do + File.open(path, File::CREAT|File::WRONLY|File::TRUNC, mode) { |f| f.print content } end # And put our new file in place - if usetmp + if use_temporary_file # This is only not true when our file is empty. begin + fail_if_checksum_is_wrong(path, checksum) if validate File.rename(path, self[:path]) rescue => detail - self.err "Could not rename tmp %s for replacing: %s" % - [self[:path], detail] + self.err "Could not rename tmp %s for replacing: %s" % [self[:path], detail] ensure # Make sure the created file gets removed - if FileTest.exists?(path) - File.unlink(path) - end + File.unlink(path) if FileTest.exists?(path) end end @@ -1086,32 +1067,35 @@ module Puppet property_fix # And then update our checksum, so the next run doesn't find it. - # FIXME This is extra work, because it's going to read the whole - # file back in again. - self.setchecksum - + self.setchecksum(checksum) end - - # Run the block as the specified user if the dir is writeable, else - # run it as root (or the current user). - def write_if_writable(dir) - yield - # We're getting different behaviors from different versions of ruby, so... - # asroot = true - # Puppet::Util::SUIDManager.asuser(asuser(), self.should(:group)) do - # if FileTest.writable?(dir) - # asroot = false - # yield - # end - # end - # - # if asroot - # yield - # end + + # Should we validate the checksum of the file we're writing? + def validate_checksum? + if sumparam = @parameters[:checksum] + return sumparam.checktype.to_s !~ /time/ + else + return false + end end private + # Make sure the file we wrote out is what we think it is. + def fail_if_checksum_is_wrong(path, checksum) + if checksum =~ /^\{(\w+)\}.+/ + sumtype = $1 + else + # This shouldn't happen, but if it happens to, it's nicer + # to just use a default sumtype than fail. + sumtype = "md5" + end + newsum = property(:checksum).getsum(sumtype, path) + return if newsum == checksum + + self.fail "File written to disk did not match checksum; discarding changes (%s vs %s)" % [checksum, newsum] + end + # Override the parent method, because we don't want to generate changes # when the file is missing and there is no 'ensure' state. def propertychanges(currentvalues) diff --git a/lib/puppet/type/file/checksum.rb b/lib/puppet/type/file/checksum.rb index 08f48ea21..debb5a7db 100755 --- a/lib/puppet/type/file/checksum.rb +++ b/lib/puppet/type/file/checksum.rb @@ -1,326 +1,271 @@ -# Keep a copy of the file checksums, and notify when they change. +require 'puppet/util/checksums' -# This state never actually modifies the system, it only notices when the system +# Keep a copy of the file checksums, and notify when they change. This +# property never actually modifies the system, it only notices when the system # changes on its own. -module Puppet - Puppet.type(:file).newproperty(:checksum) do - desc "How to check whether a file has changed. This state is used internally - for file copying, but it can also be used to monitor files somewhat - like Tripwire without managing the file contents in any way. You can - specify that a file's checksum should be monitored and then subscribe to - the file from another object and receive events to signify - checksum changes, for instance." +Puppet::Type.type(:file).newproperty(:checksum) do + include Puppet::Util::Checksums - @event = :file_changed + desc "How to check whether a file has changed. This state is used internally + for file copying, but it can also be used to monitor files somewhat + like Tripwire without managing the file contents in any way. You can + specify that a file's checksum should be monitored and then subscribe to + the file from another object and receive events to signify + checksum changes, for instance." - @unmanaged = true + @event = :file_changed - @validtypes = %w{md5 md5lite timestamp mtime time} + @unmanaged = true - def self.validtype?(type) - @validtypes.include?(type) - end + @validtypes = %w{md5 md5lite timestamp mtime time} - @validtypes.each do |ctype| - newvalue(ctype) do - handlesum() - end - end - - str = @validtypes.join("|") + def self.validtype?(type) + @validtypes.include?(type) + end - # This is here because Puppet sets this internally, using - # {md5}...... - newvalue(/^\{#{str}\}/) do + @validtypes.each do |ctype| + newvalue(ctype) do handlesum() end + end - newvalue(:nosum) do - # nothing - :nochange - end + str = @validtypes.join("|") - # If they pass us a sum type, behave normally, but if they pass - # us a sum type + sum, stick the sum in the cache. - munge do |value| - if value =~ /^\{(\w+)\}(.+)$/ - type = symbolize($1) - sum = $2 - cache(type, sum) - return type - else - if FileTest.directory?(@resource[:path]) - return :time - else - return symbolize(value) - end - end - end + # This is here because Puppet sets this internally, using + # {md5}...... + newvalue(/^\{#{str}\}/) do + handlesum() + end - # Store the checksum in the data cache, or retrieve it if only the - # sum type is provided. - def cache(type, sum = nil) - unless type - raise ArgumentError, "A type must be specified to cache a checksum" - end - type = symbolize(type) - unless state = @resource.cached(:checksums) - self.debug "Initializing checksum hash" - state = {} - @resource.cache(:checksums, state) - end + newvalue(:nosum) do + # nothing + :nochange + end - if sum - unless sum =~ /\{\w+\}/ - sum = "{%s}%s" % [type, sum] - end - state[type] = sum + # If they pass us a sum type, behave normally, but if they pass + # us a sum type + sum, stick the sum in the cache. + munge do |value| + if value =~ /^\{(\w+)\}(.+)$/ + type = symbolize($1) + sum = $2 + cache(type, sum) + return type + else + if FileTest.directory?(@resource[:path]) + return :time else - return state[type] + return symbolize(value) end end + end - # Because source and content and whomever else need to set the checksum - # and do the updating, we provide a simple mechanism for doing so. - def checksum=(value) - munge(@should) - self.updatesum(value) + # Store the checksum in the data cache, or retrieve it if only the + # sum type is provided. + def cache(type, sum = nil) + unless type + raise ArgumentError, "A type must be specified to cache a checksum" + end + type = symbolize(type) + unless state = @resource.cached(:checksums) + self.debug "Initializing checksum hash" + state = {} + @resource.cache(:checksums, state) end - def checktype - self.should || :md5 + if sum + unless sum =~ /\{\w+\}/ + sum = "{%s}%s" % [type, sum] + end + state[type] = sum + else + return state[type] end + end - # Checksums need to invert how changes are printed. - def change_to_s(currentvalue, newvalue) - begin - if currentvalue == :absent - return "defined '%s' as '%s'" % - [self.name, self.currentsum] - elsif newvalue == :absent - return "undefined %s from '%s'" % - [self.name, self.is_to_s(currentvalue)] + # Because source and content and whomever else need to set the checksum + # and do the updating, we provide a simple mechanism for doing so. + def checksum=(value) + munge(@should) + self.updatesum(value) + end + + def checktype + self.should || :md5 + end + + # Checksums need to invert how changes are printed. + def change_to_s(currentvalue, newvalue) + begin + if currentvalue == :absent + return "defined '%s' as '%s'" % + [self.name, self.currentsum] + elsif newvalue == :absent + return "undefined %s from '%s'" % + [self.name, self.is_to_s(currentvalue)] + else + if defined? @cached and @cached + return "%s changed '%s' to '%s'" % + [self.name, @cached, self.is_to_s(currentvalue)] else - if defined? @cached and @cached - return "%s changed '%s' to '%s'" % - [self.name, @cached, self.is_to_s(currentvalue)] - else - return "%s changed '%s' to '%s'" % - [self.name, self.currentsum, self.is_to_s(currentvalue)] - end + return "%s changed '%s' to '%s'" % + [self.name, self.currentsum, self.is_to_s(currentvalue)] end - rescue Puppet::Error, Puppet::DevError - raise - rescue => detail - raise Puppet::DevError, "Could not convert change %s to string: %s" % - [self.name, detail] end + rescue Puppet::Error, Puppet::DevError + raise + rescue => detail + raise Puppet::DevError, "Could not convert change %s to string: %s" % + [self.name, detail] end + end - def currentsum - #"{%s}%s" % [self.should, cache(self.should)] - cache(checktype()) - end + def currentsum + cache(checktype()) + end - # Retrieve the cached sum - def getcachedsum - hash = nil - unless hash = @resource.cached(:checksums) - hash = {} - @resource.cache(:checksums, hash) - end + # Retrieve the cached sum + def getcachedsum + hash = nil + unless hash = @resource.cached(:checksums) + hash = {} + @resource.cache(:checksums, hash) + end - sumtype = self.should + sumtype = self.should - if hash.include?(sumtype) - #self.notice "Found checksum %s for %s" % - # [hash[sumtype] ,@resource[:path]] - sum = hash[sumtype] + if hash.include?(sumtype) + #self.notice "Found checksum %s for %s" % + # [hash[sumtype] ,@resource[:path]] + sum = hash[sumtype] - unless sum =~ /^\{\w+\}/ - sum = "{%s}%s" % [sumtype, sum] - end - return sum - elsif hash.empty? - #self.notice "Could not find sum of type %s" % sumtype - return :nosum - else - #self.notice "Found checksum for %s but not of type %s" % - # [@resource[:path],sumtype] - return :nosum + unless sum =~ /^\{\w+\}/ + sum = "{%s}%s" % [sumtype, sum] end + return sum + elsif hash.empty? + #self.notice "Could not find sum of type %s" % sumtype + return :nosum + else + #self.notice "Found checksum for %s but not of type %s" % + # [@resource[:path],sumtype] + return :nosum end + end - # Calculate the sum from disk. - def getsum(checktype) - sum = "" - - checktype = checktype.intern if checktype.is_a? String - case checktype - when :md5, :md5lite: - if ! FileTest.file?(@resource[:path]) - @resource.debug "Cannot MD5 sum %s; using mtime" % - [@resource.stat.ftype] - sum = @resource.stat.mtime.to_s - else - begin - File.open(@resource[:path]) { |file| - hashfunc = Digest::MD5.new - while (!file.eof) - readBuf = file.read(512) - hashfunc.update(readBuf) - if checktype == :md5lite then - break - end - end - sum = hashfunc.hexdigest - } - rescue Errno::EACCES => detail - self.notice "Cannot checksum %s: permission denied" % - @resource[:path] - @resource.delete(self.class.name) - rescue => detail - self.notice "Cannot checksum: %s" % - detail - @resource.delete(self.class.name) - end - end - when :timestamp, :mtime: - sum = @resource.stat.mtime.to_s - #sum = File.stat(@resource[:path]).mtime.to_s - when :time: - sum = @resource.stat.ctime.to_s - #sum = File.stat(@resource[:path]).ctime.to_s - else - raise Puppet::Error, "Invalid sum type %s" % checktype - end + # Calculate the sum from disk. + def getsum(checktype, file = nil) + sum = "" + + checktype = :mtime if checktype == :timestamp + checktype = :ctime if checktype == :time + + file ||= @resource[:path] + + return nil unless FileTest.exist?(file) - return "{#{checktype}}" + sum.to_s + if ! FileTest.file?(file) + checktype = :mtime end + method = checktype.to_s + "_file" - # At this point, we don't actually modify the system, we modify - # the stored state to reflect the current state, and then kick - # off an event to mark any changes. - def handlesum - currentvalue = self.retrieve - if currentvalue.nil? - raise Puppet::Error, "Checksum state for %s is somehow nil" % - @resource.title - end + self.fail("Invalid checksum type %s" % checktype) unless respond_to?(method) - if self.insync?(currentvalue) - self.debug "Checksum is already in sync" - return nil - end - # @resource.debug "%s(%s): after refresh, is '%s'" % - # [self.class.name,@resource.name,@is] + return "{%s}%s" % [checktype, send(method, file)] + end - # If we still can't retrieve a checksum, it means that - # the file still doesn't exist - if currentvalue == :absent - # if they're copying, then we won't worry about the file - # not existing yet - unless @resource.property(:source) - self.warning("File %s does not exist -- cannot checksum" % - @resource[:path] - ) - end - return nil - end - - # If the sums are different, then return an event. - if self.updatesum(currentvalue) - return :file_changed - else - return nil - end + # At this point, we don't actually modify the system, we modify + # the stored state to reflect the current state, and then kick + # off an event to mark any changes. + def handlesum + currentvalue = self.retrieve + if currentvalue.nil? + raise Puppet::Error, "Checksum state for %s is somehow nil" % + @resource.title end - def insync?(currentvalue) - @should = [checktype()] - if cache(checktype()) - return currentvalue == currentsum() - else - # If there's no cached sum, then we don't want to generate - # an event. - return true + if self.insync?(currentvalue) + self.debug "Checksum is already in sync" + return nil + end + # If we still can't retrieve a checksum, it means that + # the file still doesn't exist + if currentvalue == :absent + # if they're copying, then we won't worry about the file + # not existing yet + unless @resource.property(:source) + self.warning("File %s does not exist -- cannot checksum" % @resource[:path]) end + return nil end - # Even though they can specify multiple checksums, the insync? - # mechanism can really only test against one, so we'll just retrieve - # the first specified sum type. - def retrieve(usecache = false) - # When the 'source' is retrieving, it passes "true" here so - # that we aren't reading the file twice in quick succession, yo. - currentvalue = currentsum() - if usecache and currentvalue - return currentvalue - end - - stat = nil - unless stat = @resource.stat - return :absent - end - - if stat.ftype == "link" and @resource[:links] != :follow - self.debug "Not checksumming symlink" - # @resource.delete(:checksum) - return currentvalue - end - - # Just use the first allowed check type - currentvalue = getsum(checktype()) + # If the sums are different, then return an event. + if self.updatesum(currentvalue) + return :file_changed + else + return nil + end + end - # If there is no sum defined, then store the current value - # into the cache, so that we're not marked as being - # out of sync. We don't want to generate an event the first - # time we get a sum. - unless cache(checktype()) - # FIXME we should support an updatechecksums-like mechanism - self.updatesum(currentvalue) - end - - # @resource.debug "checksum state is %s" % self.is + def insync?(currentvalue) + @should = [checktype()] + if cache(checktype()) + return currentvalue == currentsum() + else + # If there's no cached sum, then we don't want to generate + # an event. + return true + end + end + + # Even though they can specify multiple checksums, the insync? + # mechanism can really only test against one, so we'll just retrieve + # the first specified sum type. + def retrieve(usecache = false) + # When the 'source' is retrieving, it passes "true" here so + # that we aren't reading the file twice in quick succession, yo. + currentvalue = currentsum() + return currentvalue if usecache and currentvalue + + stat = nil + return :absent unless stat = @resource.stat + + if stat.ftype == "link" and @resource[:links] != :follow + self.debug "Not checksumming symlink" + # @resource.delete(:checksum) return currentvalue end - # Store the new sum to the state db. - def updatesum(newvalue) - result = false + # Just use the first allowed check type + currentvalue = getsum(checktype()) - if newvalue.is_a?(Symbol) - raise Puppet::Error, "%s has invalid checksum" % @resource.title - end + # If there is no sum defined, then store the current value + # into the cache, so that we're not marked as being + # out of sync. We don't want to generate an event the first + # time we get a sum. + self.updatesum(currentvalue) unless cache(checktype()) + + # @resource.debug "checksum state is %s" % self.is + return currentvalue + end - # if we're replacing, vs. updating - if sum = cache(checktype()) - # unless defined? @should - # raise Puppet::Error.new( - # ("@should is not initialized for %s, even though we " + - # "found a checksum") % @resource[:path] - # ) - # end - - if newvalue == sum - return false - end + # Store the new sum to the state db. + def updatesum(newvalue) + result = false - self.debug "Replacing %s checksum %s with %s" % - [@resource.title, sum, newvalue] - # @resource.debug "currentvalue: %s; @should: %s" % - # [newvalue,@should] - result = true - else - @resource.debug "Creating checksum %s" % newvalue - result = false - end + # if we're replacing, vs. updating + if sum = cache(checktype()) + return false if newvalue == sum - # Cache the sum so the log message can be right if possible. - @cached = sum - cache(checktype(), newvalue) - return result + self.debug "Replacing %s checksum %s with %s" % [@resource.title, sum, newvalue] + result = true + else + @resource.debug "Creating checksum %s" % newvalue + result = false end + + # Cache the sum so the log message can be right if possible. + @cached = sum + cache(checktype(), newvalue) + return result end end - diff --git a/lib/puppet/type/file/content.rb b/lib/puppet/type/file/content.rb index 6dcda0aa6..1eb1423aa 100755 --- a/lib/puppet/type/file/content.rb +++ b/lib/puppet/type/file/content.rb @@ -1,5 +1,5 @@ module Puppet - Puppet.type(:file).newproperty(:content) do + Puppet::Type.type(:file).newproperty(:content) do include Puppet::Util::Diff desc "Specify the contents of a file as a string. Newlines, tabs, and @@ -47,23 +47,13 @@ module Puppet return result end - # We should probably take advantage of existing md5 sums if they're there, - # but I really don't feel like dealing with the complexity right now. def retrieve - stat = nil - unless stat = @resource.stat - return :absent - end + return :absent unless stat = @resource.stat - if stat.ftype == "link" and @resource[:links] == :ignore - return self.should - end + return self.should if stat.ftype == "link" and @resource[:links] == :ignore # Don't even try to manage the content on directories - if stat.ftype == "directory" and @resource[:links] == :ignore - @resource.delete(:content) - return nil - end + return nil if stat.ftype == "directory" begin currentvalue = File.read(@resource[:path]) @@ -74,12 +64,17 @@ module Puppet end end + # Make sure we're also managing the checksum property. + def should=(value) + super + @resource.newattr(:checksum) unless @resource.property(:checksum) + end # Just write our content out to disk. def sync return_event = @resource.stat ? :file_changed : :file_created - @resource.write(:content) { |f| f.print self.should } + @resource.write(self.should, :content) return return_event end diff --git a/lib/puppet/type/file/ensure.rb b/lib/puppet/type/file/ensure.rb index 3aa918f65..0d2171216 100755 --- a/lib/puppet/type/file/ensure.rb +++ b/lib/puppet/type/file/ensure.rb @@ -46,7 +46,7 @@ module Puppet if property = (@resource.property(:content) || @resource.property(:source)) property.sync else - @resource.write(false) { |f| f.flush } + @resource.write("", :ensure) mode = @resource.should(:mode) end return :file_created @@ -67,14 +67,12 @@ module Puppet "Cannot create %s; parent directory %s does not exist" % [@resource[:path], parent] end - @resource.write_if_writable(parent) do - if mode - Puppet::Util.withumask(000) do - Dir.mkdir(@resource[:path],mode) - end - else - Dir.mkdir(@resource[:path]) + if mode + Puppet::Util.withumask(000) do + Dir.mkdir(@resource[:path],mode) end + else + Dir.mkdir(@resource[:path]) end @resource.send(:property_fix) @resource.setchecksum @@ -101,9 +99,13 @@ module Puppet munge do |value| value = super(value) + # It doesn't make sense to try to manage links unless, well, + # we're managing links. + resource[:links] = :manage if value == :link return value if value.is_a? Symbol @resource[:target] = value + resource[:links] = :manage return :link end diff --git a/lib/puppet/type/file/source.rb b/lib/puppet/type/file/source.rb index a3e533c31..1b0dd3141 100755 --- a/lib/puppet/type/file/source.rb +++ b/lib/puppet/type/file/source.rb @@ -105,10 +105,13 @@ module Puppet return nil end + return nil if desc == "" + + # Collect everything except the checksum + values = desc.split("\t") + other = values.pop args = {} - pinparams.zip( - desc.split("\t") - ).each { |param, value| + pinparams.zip(values).each { |param, value| if value =~ /^[0-9]+$/ value = value.to_i end @@ -117,16 +120,19 @@ module Puppet end } - # we can't manage ownership as root, so don't even try - unless Puppet::Util::SUIDManager.uid == 0 - args.delete(:owner) + # Now decide whether we're doing checksums or symlinks + if args[:type] == "link" + args[:target] = other + else + args[:checksum] = other end - if args.empty? or (args[:type] == "link" and @resource[:links] == :ignore) - return nil - else - return args + # we can't manage ownership unless we're root, so don't even try + unless Puppet::Util::SUIDManager.uid == 0 + args.delete(:owner) end + + return args end # Have we successfully described the remote source? @@ -172,7 +178,7 @@ module Puppet end def pinparams - Puppet::Network::Handler.handler(:fileserver).params + [:mode, :type, :owner, :group] end # This basically calls describe() on our file, and then sets all @@ -201,7 +207,7 @@ module Puppet end case @stats[:type] - when "directory", "file": + when "directory", "file", "link": @resource[:ensure] = @stats[:type] unless @resource.deleting? else self.info @stats.inspect @@ -235,9 +241,7 @@ module Puppet checks.delete(:checksum) @resource[:check] = checks - unless @resource.property(:checksum) - @resource[:checksum] = :md5 - end + @resource[:checksum] = :md5 unless @resource.property(:checksum) end def sync @@ -245,7 +249,7 @@ module Puppet exists = File.exists?(@resource[:path]) - @resource.write(:source) { |f| f.print contents } + @resource.write(contents, :source, @stats[:checksum]) if exists return :file_changed diff --git a/lib/puppet/type/package.rb b/lib/puppet/type/package.rb index 24b187e5f..ee2871ce2 100644 --- a/lib/puppet/type/package.rb +++ b/lib/puppet/type/package.rb @@ -200,7 +200,7 @@ module Puppet package { $ssh: ensure => installed, alias => openssh, - require => package[openssl] + require => Package[openssl] } " diff --git a/lib/puppet/util/checksums.rb b/lib/puppet/util/checksums.rb index 598b3adfa..15d2eadd1 100644 --- a/lib/puppet/util/checksums.rb +++ b/lib/puppet/util/checksums.rb @@ -55,7 +55,7 @@ module Puppet::Util::Checksums end # Return the :ctime of a file. - def timestamp_file(filename) + def ctime_file(filename) File.stat(filename).send(:ctime) end diff --git a/lib/puppet/util/filetype.rb b/lib/puppet/util/filetype.rb index 1c7734cc4..bbc837e75 100755 --- a/lib/puppet/util/filetype.rb +++ b/lib/puppet/util/filetype.rb @@ -165,7 +165,7 @@ class Puppet::Util::FileType # Remove a specific @path's cron tab. def remove - if Facter.value("operatingsystem") == "FreeBSD" + if %w{Darwin FreeBSD}.include?(Facter.value("operatingsystem")) %x{/bin/echo yes | #{cmdbase()} -r 2>/dev/null} else %x{#{cmdbase()} -r 2>/dev/null} diff --git a/lib/puppet/util/settings.rb b/lib/puppet/util/settings.rb index 2c8fc682a..5f4a98558 100644 --- a/lib/puppet/util/settings.rb +++ b/lib/puppet/util/settings.rb @@ -1070,7 +1070,7 @@ Generated on #{Time.now}. return nil unless path.is_a?(String) return nil if path =~ /^\/dev/ - return nil if Puppet::Type::File[path] # skip files that are in our global resource list. + return nil if Puppet::Type.type(:file)[path] # skip files that are in our global resource list. objects = [] diff --git a/lib/puppet/util/tagging.rb b/lib/puppet/util/tagging.rb index 9abb3fb2b..8a50f3458 100644 --- a/lib/puppet/util/tagging.rb +++ b/lib/puppet/util/tagging.rb @@ -34,6 +34,6 @@ module Puppet::Util::Tagging private def valid_tag?(tag) - tag =~ /^\w[-\w:]*$/ + tag =~ /^\w[-\w:.]*$/ end end |