diff --git a/lib/puppet/file_bucket/dipper.rb b/lib/puppet/file_bucket/dipper.rb index f48fe63b8..dda16018d 100644 --- a/lib/puppet/file_bucket/dipper.rb +++ b/lib/puppet/file_bucket/dipper.rb @@ -1,122 +1,122 @@ require 'pathname' require 'puppet/file_bucket' require 'puppet/file_bucket/file' require 'puppet/indirector/request' class Puppet::FileBucket::Dipper include Puppet::Util::Checksums # This is a transitional implementation that uses REST # to access remote filebucket files. attr_accessor :name # Creates a bucket client def initialize(hash = {}) # Emulate the XMLRPC client server = hash[:Server] port = hash[:Port] || Puppet[:masterport] environment = Puppet[:environment] if hash.include?(:Path) @local_path = hash[:Path] @rest_path = nil else @local_path = nil @rest_path = "https://#{server}:#{port}/#{environment}/file_bucket_file/" end @checksum_type = Puppet[:digest_algorithm].to_sym @digest = method(@checksum_type) end def local? !! @local_path end # Backs up a file to the file bucket def backup(file) file_handle = Puppet::FileSystem.pathname(file) raise(ArgumentError, "File #{file} does not exist") unless Puppet::FileSystem.exist?(file_handle) begin file_bucket_file = Puppet::FileBucket::File.new(file_handle, :bucket_path => @local_path) files_original_path = absolutize_path(file) dest_path = "#{@rest_path}#{file_bucket_file.name}/#{files_original_path}" file_bucket_path = "#{@rest_path}#{file_bucket_file.checksum_type}/#{file_bucket_file.checksum_data}/#{files_original_path}" # Make a HEAD request for the file so that we don't waste time # uploading it if it already exists in the bucket. unless Puppet::FileBucket::File.indirection.head(file_bucket_path) Puppet::FileBucket::File.indirection.save(file_bucket_file, dest_path) end return file_bucket_file.checksum_data rescue => detail message = "Could not back up #{file}: #{detail}" Puppet.log_exception(detail, message) raise Puppet::Error, message, detail.backtrace end end # Retrieves a file by sum. def getfile(sum) get_bucket_file(sum).to_s end # Retrieves a FileBucket::File by sum. def get_bucket_file(sum) source_path = "#{@rest_path}#{@checksum_type}/#{sum}" file_bucket_file = Puppet::FileBucket::File.indirection.find(source_path, :bucket_path => @local_path) raise Puppet::Error, "File not found" unless file_bucket_file file_bucket_file end # Restores the file def restore(file, sum) restore = true file_handle = Puppet::FileSystem.pathname(file) if Puppet::FileSystem.exist?(file_handle) cursum = Puppet::FileBucket::File.new(file_handle).checksum_data() # if the checksum has changed... # this might be extra effort if cursum == sum restore = false end end if restore if newcontents = get_bucket_file(sum) - newsum = newcontents.checksum_data() + newsum = newcontents.checksum_data changed = nil if Puppet::FileSystem.exist?(file_handle) and ! Puppet::FileSystem.writable?(file_handle) changed = Puppet::FileSystem.stat(file_handle).mode ::File.chmod(changed | 0200, file) end ::File.open(file, ::File::WRONLY|::File::TRUNC|::File::CREAT) { |of| of.binmode begin - source_stream = newcontents.stream(); + source_stream = newcontents.stream FileUtils.copy_stream(source_stream, of) ensure source_stream.close() end #of.print(newcontents) } ::File.chmod(changed, file) if changed else Puppet.err "Could not find file with checksum #{sum}" return nil end return newsum else return nil end end private def absolutize_path( path ) Pathname.new(path).realpath end end diff --git a/lib/puppet/file_bucket/file.rb b/lib/puppet/file_bucket/file.rb index b9e48e9f5..3c8731da0 100644 --- a/lib/puppet/file_bucket/file.rb +++ b/lib/puppet/file_bucket/file.rb @@ -1,153 +1,153 @@ require 'puppet/file_bucket' require 'puppet/indirector' require 'puppet/util/checksums' require 'digest/md5' require 'stringio' class Puppet::FileBucket::File # This class handles the abstract notion of a file in a filebucket. # There are mechanisms to save and load this file locally and remotely in puppet/indirector/filebucketfile/* # There is a compatibility class that emulates pre-indirector filebuckets in Puppet::FileBucket::Dipper extend Puppet::Indirector indirects :file_bucket_file, :terminus_class => :selector attr :bucket_path def self.supported_formats [:s, :pson] end def self.default_format # This should really be :raw, like is done for Puppet::FileServing::Content # but this class hasn't historically supported `from_raw`, so switching # would break compatibility between newer 3.x agents talking to older 3.x # masters. However, to/from_s has been supported and achieves the desired # result without breaking compatibility. :s end def initialize(contents, options = {}) case contents when String - @contents = StringContents.new(contents); + @contents = StringContents.new(contents) when Pathname - @contents = FileContents.new(contents); + @contents = FileContents.new(contents) else raise ArgumentError.new("contents must be a String or Pathname, got a #{contents.class}") end @bucket_path = options.delete(:bucket_path) @checksum_type = Puppet[:digest_algorithm].to_sym raise ArgumentError.new("Unknown option(s): #{options.keys.join(', ')}") unless options.empty? end # @return [Num] The size of the contents def size @contents.size() end # @return [IO] A stream that reads the contents def stream @contents.stream() end def checksum_type @checksum_type.to_s end def checksum "{#{checksum_type}}#{checksum_data}" end def checksum_data @checksum_data ||= @contents.checksum_data(@checksum_type) end def to_s @contents.to_s end def contents to_s end def name "#{checksum_type}/#{checksum_data}" end def self.from_s(contents) self.new(contents) end def to_data_hash # Note that this serializes the entire data to a string and places it in a hash. { "contents" => contents.to_s } end def self.from_data_hash(data) self.new(data["contents"]) end def to_pson Puppet.deprecation_warning("Serializing Puppet::FileBucket::File objects to pson is deprecated.") to_data_hash.to_pson end # This method is deprecated, but cannot be removed for awhile, otherwise # older agents sending pson couldn't backup to filebuckets on newer masters def self.from_pson(pson) Puppet.deprecation_warning("Deserializing Puppet::FileBucket::File objects from pson is deprecated. Upgrade to a newer version.") self.from_data_hash(pson) end private class StringContents def initialize(content) @contents = content; end def stream StringIO.new(@contents) end def size @contents.size end def checksum_data(base_method) Puppet.info("Computing checksum on string") Puppet::Util::Checksums.method(base_method).call(@contents) end def to_s # This is not so horrible as for FileContent, but still possible to mutate the content that the # checksum is based on... so semi horrible... return @contents; end end class FileContents def initialize(path) @path = path end # Caller *must* close the stream def stream() - Puppet::FileSystem.open(@path, nil, 'r') + Puppet::FileSystem.open(@path, nil, 'rb') end def size Puppet::FileSystem.size(@path) end def checksum_data(base_method) Puppet.info("Computing checksum on file #{@path}") Puppet::Util::Checksums.method(:"#{base_method}_file").call(@path) end def to_s Puppet::FileSystem::binread(@path) end end end