diff --git a/lib/puppet/application/filebucket.rb b/lib/puppet/application/filebucket.rb index 09aaf2b5d..ed67009aa 100644 --- a/lib/puppet/application/filebucket.rb +++ b/lib/puppet/application/filebucket.rb @@ -1,88 +1,87 @@ require 'puppet' require 'puppet/application' require 'puppet/file_bucket/dipper' Puppet::Application.new(:filebucket) do should_not_parse_config option("--bucket BUCKET","-b") option("--debug","-d") option("--local","-l") option("--remote","-r") option("--verbose","-v") dispatch do ARGV.shift end command(:get) do md5 = ARGV.shift out = @client.getfile(md5) print out end command(:backup) do ARGV.each do |file| unless FileTest.exists?(file) $stderr.puts "%s: no such file" % file next end unless FileTest.readable?(file) $stderr.puts "%s: cannot read file" % file next end md5 = @client.backup(file) puts "%s: %s" % [file, md5] end end command(:restore) do file = ARGV.shift md5 = ARGV.shift @client.restore(file, md5) end setup do Puppet::Log.newdestination(:console) @client = nil @server = nil trap(:INT) do $stderr.puts "Cancelling" exit(1) end if options[:debug] Puppet::Log.level = :debug elsif options[:verbose] Puppet::Log.level = :info end # Now parse the config Puppet.parse_config if Puppet.settings.print_configs? exit(Puppet.settings.print_configs ? 0 : 1) end begin if options[:local] or options[:bucket] path = options[:bucket] || Puppet[:bucketdir] @client = Puppet::FileBucket::Dipper.new(:Path => path) else - require 'puppet/network/handler' @client = Puppet::FileBucket::Dipper.new(:Server => Puppet[:server]) end rescue => detail $stderr.puts detail if Puppet[:trace] puts detail.backtrace end exit(1) end end end diff --git a/lib/puppet/file_bucket/dipper.rb b/lib/puppet/file_bucket/dipper.rb index b13e590a9..b4a9eb83d 100644 --- a/lib/puppet/file_bucket/dipper.rb +++ b/lib/puppet/file_bucket/dipper.rb @@ -1,95 +1,102 @@ require 'puppet/file_bucket' require 'puppet/file_bucket/file' require 'puppet/indirector/request' class Puppet::FileBucket::Dipper # This is a transitional implementation that uses REST # to access remote filebucket files. attr_accessor :name # Create our 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 end def local? !! @local_path end # Back up a file to our bucket def backup(file) unless ::File.exist?(file) raise(ArgumentError, "File %s does not exist" % file) end contents = ::File.read(file) begin - file_bucket_file = Puppet::FileBucket::File.new(contents, :bucket_path => @local_path, :path => file) + file_bucket_file = Puppet::FileBucket::File.new(contents, :bucket_path => @local_path, :path => absolutize_path(file) ) dest_path = "#{@rest_path}#{file_bucket_file.name}" file_bucket_file.save(dest_path) return file_bucket_file.checksum_data rescue => detail puts detail.backtrace if Puppet[:trace] raise Puppet::Error, "Could not back up %s: %s" % [file, detail] end end # Retrieve a file by sum. def getfile(sum) source_path = "#{@rest_path}md5/#{sum}" file_bucket_file = Puppet::FileBucket::File.find(source_path) return file_bucket_file.to_s end # Restore the file def restore(file,sum) restore = true if FileTest.exists?(file) cursum = Digest::MD5.hexdigest(::File.read(file)) # if the checksum has changed... # this might be extra effort if cursum == sum restore = false end end if restore if newcontents = getfile(sum) tmp = "" newsum = Digest::MD5.hexdigest(newcontents) changed = nil if FileTest.exists?(file) and ! FileTest.writable?(file) changed = ::File.stat(file).mode ::File.chmod(changed | 0200, file) end ::File.open(file, ::File::WRONLY|::File::TRUNC|::File::CREAT) { |of| of.print(newcontents) } if changed ::File.chmod(changed, file) end else Puppet.err "Could not find file with checksum %s" % sum return nil end return newsum else return nil end end + + private + def absolutize_path( path ) + require 'pathname' + Pathname.new(path).realpath + end + end diff --git a/spec/unit/file_bucket/dipper.rb b/spec/unit/file_bucket/dipper.rb index 5d6625103..5ada34e9d 100755 --- a/spec/unit/file_bucket/dipper.rb +++ b/spec/unit/file_bucket/dipper.rb @@ -1,104 +1,110 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/file_bucket/dipper' describe Puppet::FileBucket::Dipper do + before do + ['/my/file'].each do |x| + Puppet::FileBucket::Dipper.any_instance.stubs(:absolutize_path).with(x).returns(x) + end + end + it "should fail in an informative way when there are failures backing up to the server" do File.stubs(:exist?).returns true File.stubs(:read).returns "content" @dipper = Puppet::FileBucket::Dipper.new(:Path => "/my/bucket") filemock = stub "bucketfile" Puppet::FileBucket::File.stubs(:new).returns(filemock) filemock.expects(:name).returns "name" filemock.expects(:save).raises ArgumentError lambda { @dipper.backup("/my/file") }.should raise_error(Puppet::Error) end it "should backup files to a local bucket" do @dipper = Puppet::FileBucket::Dipper.new( :Path => "/my/bucket" ) File.stubs(:exist?).returns true File.stubs(:read).with("/my/file").returns "my contents" bucketfile = stub "bucketfile" bucketfile.stubs(:name).returns('md5/DIGEST123') bucketfile.stubs(:checksum_data).returns("DIGEST123") bucketfile.expects(:save).with('md5/DIGEST123') Puppet::FileBucket::File.stubs(:new).with( "my contents", :bucket_path => '/my/bucket', :path => '/my/file' ).returns(bucketfile) @dipper.backup("/my/file").should == "DIGEST123" end it "should retrieve files from a local bucket" do @dipper = Puppet::FileBucket::Dipper.new( :Path => "/my/bucket" ) File.stubs(:exist?).returns true File.stubs(:read).with("/my/file").returns "my contents" bucketfile = stub "bucketfile" bucketfile.stubs(:to_s).returns "Content" Puppet::FileBucket::File.expects(:find).with( 'md5/DIGEST123' ).returns(bucketfile) @dipper.getfile("DIGEST123").should == "Content" end it "should backup files to a remote server" do @dipper = Puppet::FileBucket::Dipper.new( :Server => "puppetmaster", :Port => "31337" ) File.stubs(:exist?).returns true File.stubs(:read).with("/my/file").returns "my contents" bucketfile = stub "bucketfile" bucketfile.stubs(:name).returns('md5/DIGEST123') bucketfile.stubs(:checksum_data).returns("DIGEST123") bucketfile.expects(:save).with('https://puppetmaster:31337/production/file_bucket_file/md5/DIGEST123') Puppet::FileBucket::File.stubs(:new).with( "my contents", :bucket_path => nil, :path => '/my/file' ).returns(bucketfile) @dipper.backup("/my/file").should == "DIGEST123" end it "should retrieve files from a remote server" do @dipper = Puppet::FileBucket::Dipper.new( :Server => "puppetmaster", :Port => "31337" ) File.stubs(:exist?).returns true File.stubs(:read).with("/my/file").returns "my contents" bucketfile = stub "bucketfile" bucketfile.stubs(:to_s).returns "Content" Puppet::FileBucket::File.expects(:find).with( 'https://puppetmaster:31337/production/file_bucket_file/md5/DIGEST123' ).returns(bucketfile) @dipper.getfile("DIGEST123").should == "Content" end end