diff options
-rw-r--r-- | lib/puppet/file_bucket/file.rb | 99 | ||||
-rw-r--r-- | lib/puppet/indirector/file_bucket_file/file.rb | 9 | ||||
-rw-r--r-- | lib/puppet/util/checksums.rb | 9 | ||||
-rw-r--r-- | spec/unit/file_bucket/file.rb | 165 | ||||
-rwxr-xr-x | spec/unit/indirector/file_bucket_file/file.rb | 16 | ||||
-rwxr-xr-x | spec/unit/util/checksums.rb | 4 |
6 files changed, 162 insertions, 140 deletions
diff --git a/lib/puppet/file_bucket/file.rb b/lib/puppet/file_bucket/file.rb index 1e95e5ef6..52ee4f711 100644 --- a/lib/puppet/file_bucket/file.rb +++ b/lib/puppet/file_bucket/file.rb @@ -1,7 +1,10 @@ require 'puppet/file_bucket' require 'puppet/indirector' +require 'puppet/util/checksums' class Puppet::FileBucket::File + include Puppet::Util::Checksums + # 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 @@ -16,16 +19,18 @@ class Puppet::FileBucket::File attr :bucket_path, true def self.default_checksum_type - :md5 + "md5" end def initialize( contents, options = {} ) - @contents = contents @bucket_path = options[:bucket_path] @path = options[:path] @paths = options[:paths] || [] + @checksum = options[:checksum] - @checksum_type = options[:checksum_type] || self.class.default_checksum_type + @checksum_type = options[:checksum_type] + + self.contents = contents yield(self) if block_given? @@ -33,61 +38,45 @@ class Puppet::FileBucket::File end def validate! - digest_class( @checksum_type ) # raises error on bad types - raise ArgumentError, 'contents must be a string' unless @contents.is_a?(String) - validate_checksum(@checksum) if @checksum + validate_checksum_type!(checksum_type) + validate_checksum!(checksum) if checksum end - def contents=(contents) + def contents=(str) raise "You may not change the contents of a FileBucket File" if @contents - @contents = contents - end - - def checksum=(checksum) - validate_checksum(checksum) - self.checksum_type = checksum # this grabs the prefix only - @checksum = checksum - end - - def validate_checksum(new_checksum) - unless new_checksum == checksum_of_type(new_checksum) - raise Puppet::Error, "checksum does not match contents" - end + validate_content!(str) + @contents = str end def checksum - @checksum ||= checksum_of_type(checksum_type) + return @checksum if @checksum + @checksum = calculate_checksum if contents + @checksum end - def checksum_of_type( type ) - type = checksum_type( type ) # strip out data segment if there is one - type.to_s + ":" + digest_class(type).hexdigest(@contents) + def checksum=(checksum) + validate_checksum!(checksum) + @checksum = checksum end def checksum_type=( new_checksum_type ) @checksum = nil - @checksum_type = checksum_type(new_checksum_type) - end - - def checksum_type(checksum = @checksum_type) - checksum.to_s.split(':',2)[0].to_sym + @checksum_type = new_checksum_type end - def checksum_data(new_checksum = self.checksum) - new_checksum.split(':',2)[1] - end - - def checksum_data=(new_data) - self.checksum = "#{checksum_type}:#{new_data}" + def checksum_type + unless @checksum_type + if @checksum + @checksum_type = sumtype(checksum) + else + @checksum_type = self.class.default_checksum_type + end + end + @checksum_type end - def digest_class(type = nil) - case checksum_type(type) - when :md5 : require 'digest/md5' ; Digest::MD5 - when :sha1 : require 'digest/sha1' ; Digest::SHA1 - else - raise ArgumentError, "not a known checksum type: #{checksum_type(type)}" - end + def checksum_data + sumdata(checksum) end def to_s @@ -99,7 +88,10 @@ class Puppet::FileBucket::File end def name=(name) - self.checksum_type, self.checksum_data, self.path = name.split('/',3) + data = name.split('/',3) + self.path = data.pop + @checksum_type = nil + self.checksum = "{#{data[0]}}#{data[1]}" end def conflict_check? @@ -120,4 +112,25 @@ class Puppet::FileBucket::File self.new( pson["contents"], :path => pson["path"] ) end + private + + def calculate_checksum + "{#{checksum_type}}" + send(checksum_type, contents) + end + + def validate_content!(content) + raise ArgumentError, "Contents must be a string" if content and ! content.is_a?(String) + end + + def validate_checksum!(new_checksum) + newtype = sumtype(new_checksum) + + unless sumdata(new_checksum) == (calc_sum = send(newtype, contents)) + raise Puppet::Error, "Checksum #{new_checksum} does not match contents #{calc_sum}" + end + end + + def validate_checksum_type!(type) + raise ArgumentError, "Invalid checksum type #{type}" unless respond_to?(type) + end end diff --git a/lib/puppet/indirector/file_bucket_file/file.rb b/lib/puppet/indirector/file_bucket_file/file.rb index 4c08b8d6b..9fe40f62d 100644 --- a/lib/puppet/indirector/file_bucket_file/file.rb +++ b/lib/puppet/indirector/file_bucket_file/file.rb @@ -1,8 +1,11 @@ require 'puppet/indirector/code' require 'puppet/file_bucket/file' +require 'puppet/util/checksums' module Puppet::FileBucketFile class File < Puppet::Indirector::Code + include Puppet::Util::Checksums + desc "Store files in a directory set based on their checksums." def initialize @@ -80,9 +83,11 @@ module Puppet::FileBucketFile end def request_to_checksum_and_path( request ) - checksum_type, checksum, path = request.key.split(/[:\/]/, 3) + return [request.key, nil] if checksum?(request.key) + + checksum_type, checksum, path = request.key.split(/\//, 3) return nil if checksum_type.to_s == "" - return [ checksum_type + ":" + checksum, path ] + return [ "{#{checksum_type}}#{checksum}", path ] end def path_for(bucket_file, subfile = nil) diff --git a/lib/puppet/util/checksums.rb b/lib/puppet/util/checksums.rb index a09523740..41f878687 100644 --- a/lib/puppet/util/checksums.rb +++ b/lib/puppet/util/checksums.rb @@ -7,6 +7,15 @@ module Puppet::Util::Checksums end # Strip the checksum type from an existing checksum + def sumdata(checksum) + if checksum =~ /^\{(\w+)\}(.+)/ + return $2 + else + return nil + end + end + + # Strip the checksum type from an existing checksum def sumtype(checksum) if checksum =~ /^\{(\w+)\}/ return $1 diff --git a/spec/unit/file_bucket/file.rb b/spec/unit/file_bucket/file.rb index 76f8e2599..73587652c 100644 --- a/spec/unit/file_bucket/file.rb +++ b/spec/unit/file_bucket/file.rb @@ -12,66 +12,18 @@ describe Puppet::FileBucket::File do Puppet[:bucketdir] = "/dev/null/bucket" @digest = "4a8ec4fa5f01b4ab1a0ab8cbccb709f0" - @checksum = "md5:4a8ec4fa5f01b4ab1a0ab8cbccb709f0" + @checksum = "{md5}4a8ec4fa5f01b4ab1a0ab8cbccb709f0" @dir = '/dev/null/bucket/4/a/8/e/c/4/f/a/4a8ec4fa5f01b4ab1a0ab8cbccb709f0' @contents = "file contents" end - it "should save a file" do - ::File.expects(:exist?).with("#{@dir}/contents").returns false - ::File.expects(:directory?).with(@dir).returns false - ::FileUtils.expects(:mkdir_p).with(@dir) - ::File.expects(:open).with("#{@dir}/contents", ::File::WRONLY|::File::CREAT, 0440) - - bucketfile = Puppet::FileBucket::File.new(@contents) - bucketfile.save - - end - - describe "using the indirector's find method" do - it "should return nil if a file doesn't exist" do - ::File.expects(:exist?).with("#{@dir}/contents").returns false - - bucketfile = Puppet::FileBucket::File.find("md5:#{@digest}") - bucketfile.should == nil - end - - it "should find a filebucket if the file exists" do - ::File.expects(:exist?).with("#{@dir}/contents").returns true - ::File.expects(:exist?).with("#{@dir}/paths").returns false - ::File.expects(:read).with("#{@dir}/contents").returns @contents - - bucketfile = Puppet::FileBucket::File.find("md5:#{@digest}") - bucketfile.should_not == nil - end - - describe "using RESTish digest notation" do - it "should return nil if a file doesn't exist" do - ::File.expects(:exist?).with("#{@dir}/contents").returns false - - bucketfile = Puppet::FileBucket::File.find("md5/#{@digest}") - bucketfile.should == nil - end - - it "should find a filebucket if the file exists" do - ::File.expects(:exist?).with("#{@dir}/contents").returns true - ::File.expects(:exist?).with("#{@dir}/paths").returns false - ::File.expects(:read).with("#{@dir}/contents").returns @contents - - bucketfile = Puppet::FileBucket::File.find("md5/#{@digest}") - bucketfile.should_not == nil - end - - end - end - it "should have a to_s method to return the contents" do Puppet::FileBucket::File.new(@contents).to_s.should == @contents end - it "should have a method that returns the digest algorithm" do - Puppet::FileBucket::File.new(@contents, :checksum => @checksum).checksum_type.should == :md5 + it "should calculate the checksum type from the passed in checksum" do + Puppet::FileBucket::File.new(@contents, :checksum => @checksum).checksum_type.should == "md5" end it "should allow contents to be specified in a block" do @@ -102,23 +54,21 @@ describe Puppet::FileBucket::File do Puppet::FileBucket::File.new(@contents).contents.should == @contents end + it "should default to 'md5' as the checksum algorithm if the algorithm is not in the name" do + Puppet::FileBucket::File.new(@contents).checksum_type.should == "md5" + end + it "should calculate the checksum" do - Digest::MD5.expects(:hexdigest).with(@contents).returns('mychecksum') - Puppet::FileBucket::File.new(@contents).checksum.should == 'md5:mychecksum' + Puppet::FileBucket::File.new(@contents).checksum.should == @checksum end it "should remove the old checksum value if the algorithm is changed" do - Digest::MD5.expects(:hexdigest).with(@contents).returns('oldsum') sum = Puppet::FileBucket::File.new(@contents) - oldsum = sum.checksum + sum.checksum.should_not be_nil + newsum = Digest::SHA1.hexdigest(@contents).to_s sum.checksum_type = :sha1 - Digest::SHA1.expects(:hexdigest).with(@contents).returns('newsum') - sum.checksum.should == 'sha1:newsum' - end - - it "should default to 'md5' as the checksum algorithm if the algorithm is not in the name" do - Puppet::FileBucket::File.new(@contents).checksum_type.should == :md5 + sum.checksum.should == "{sha1}#{newsum}" end it "should support specifying the checksum_type during initialization" do @@ -130,21 +80,15 @@ describe Puppet::FileBucket::File do proc { Puppet::FileBucket::File.new(@contents, :checksum_type => :nope) }.should raise_error(ArgumentError) end - it "should fail if given an invalid checksum at initialization" do - proc { Puppet::FileBucket::File.new(@contents, :checksum => "md5:00000000000000000000000000000000") }.should raise_error(RuntimeError) + it "should fail if given an checksum at initialization that does not match the contents" do + proc { Puppet::FileBucket::File.new(@contents, :checksum => "{md5}00000000000000000000000000000000") }.should raise_error(RuntimeError) end - it "should fail if assigned an invalid checksum " do + it "should fail if assigned a checksum that does not match the contents" do bucket = Puppet::FileBucket::File.new(@contents) - proc { bucket.checksum = "md5:00000000000000000000000000000000" }.should raise_error(RuntimeError) + proc { bucket.checksum = "{md5}00000000000000000000000000000000" }.should raise_error(RuntimeError) end - it "should accept checksum_data without a prefix" do - bucket = Puppet::FileBucket::File.new(@contents) - bucket.checksum_data = @digest - end - - describe "when using back-ends" do it "should redirect using Puppet::Indirector" do Puppet::Indirector::Indirection.instance(:file_bucket_file).model.should equal(Puppet::FileBucket::File) @@ -185,26 +129,26 @@ describe Puppet::FileBucket::File do Puppet::FileBucket::File.new(@contents).save end - end - it "should accept a path" do - remote_path = '/path/on/the/remote/box' - Puppet::FileBucket::File.new(@contents, :path => remote_path).path.should == remote_path - end + it "should append the path to the paths file" do + remote_path = '/path/on/the/remote/box' - it "should append the path to the paths file" do - remote_path = '/path/on/the/remote/box' + ::File.expects(:directory?).with(@dir).returns(true) + ::File.expects(:open).with("#{@dir}/contents", ::File::WRONLY|::File::CREAT, 0440) + ::File.expects(:exist?).with("#{@dir}/contents").returns false - ::File.expects(:directory?).with(@dir).returns(true) - ::File.expects(:open).with("#{@dir}/contents", ::File::WRONLY|::File::CREAT, 0440) - ::File.expects(:exist?).with("#{@dir}/contents").returns false + mockfile = mock "file" + mockfile.expects(:puts).with('/path/on/the/remote/box') + ::File.expects(:exist?).with("#{@dir}/paths").returns false + ::File.expects(:open).with("#{@dir}/paths", ::File::WRONLY|::File::CREAT|::File::APPEND).yields mockfile + Puppet::FileBucket::File.new(@contents, :path => remote_path).save - mockfile = mock "file" - mockfile.expects(:puts).with('/path/on/the/remote/box') - ::File.expects(:exist?).with("#{@dir}/paths").returns false - ::File.expects(:open).with("#{@dir}/paths", ::File::WRONLY|::File::CREAT|::File::APPEND).yields mockfile - Puppet::FileBucket::File.new(@contents, :path => remote_path).save + end + end + it "should accept a path" do + remote_path = '/path/on/the/remote/box' + Puppet::FileBucket::File.new(@contents, :path => remote_path).path.should == remote_path end it "should return a url-ish name" do @@ -219,7 +163,7 @@ describe Puppet::FileBucket::File do it "should accept a url-ish name" do bucket = Puppet::FileBucket::File.new(@contents) lambda { bucket.name = "sha1/034fa2ed8e211e4d20f20e792d777f4a30af1a93/new/path" }.should_not raise_error - bucket.checksum_type.should == :sha1 + bucket.checksum_type.should == "sha1" bucket.checksum_data.should == '034fa2ed8e211e4d20f20e792d777f4a30af1a93' bucket.path.should == "new/path" end @@ -236,4 +180,51 @@ describe Puppet::FileBucket::File do Puppet::FileBucket::File.from_pson({"contents"=>"file contents"}).contents.should == "file contents" end + it "should save a file" do + ::File.expects(:exist?).with("#{@dir}/contents").returns false + ::File.expects(:directory?).with(@dir).returns false + ::FileUtils.expects(:mkdir_p).with(@dir) + ::File.expects(:open).with("#{@dir}/contents", ::File::WRONLY|::File::CREAT, 0440) + + bucketfile = Puppet::FileBucket::File.new(@contents) + bucketfile.save + + end + + describe "using the indirector's find method" do + it "should return nil if a file doesn't exist" do + ::File.expects(:exist?).with("#{@dir}/contents").returns false + + bucketfile = Puppet::FileBucket::File.find("{md5}#{@digest}") + bucketfile.should == nil + end + + it "should find a filebucket if the file exists" do + ::File.expects(:exist?).with("#{@dir}/contents").returns true + ::File.expects(:exist?).with("#{@dir}/paths").returns false + ::File.expects(:read).with("#{@dir}/contents").returns @contents + + bucketfile = Puppet::FileBucket::File.find("{md5}#{@digest}") + bucketfile.should_not == nil + end + + describe "using RESTish digest notation" do + it "should return nil if a file doesn't exist" do + ::File.expects(:exist?).with("#{@dir}/contents").returns false + + bucketfile = Puppet::FileBucket::File.find("md5/#{@digest}") + bucketfile.should == nil + end + + it "should find a filebucket if the file exists" do + ::File.expects(:exist?).with("#{@dir}/contents").returns true + ::File.expects(:exist?).with("#{@dir}/paths").returns false + ::File.expects(:read).with("#{@dir}/contents").returns @contents + + bucketfile = Puppet::FileBucket::File.find("md5/#{@digest}") + bucketfile.should_not == nil + end + + end + end end diff --git a/spec/unit/indirector/file_bucket_file/file.rb b/spec/unit/indirector/file_bucket_file/file.rb index 6e0d340f3..bad6de380 100755 --- a/spec/unit/indirector/file_bucket_file/file.rb +++ b/spec/unit/indirector/file_bucket_file/file.rb @@ -27,7 +27,7 @@ describe Puppet::FileBucketFile::File do Puppet[:bucketdir] = "/dev/null/bucket" @digest = "4a8ec4fa5f01b4ab1a0ab8cbccb709f0" - @checksum = "md5:4a8ec4fa5f01b4ab1a0ab8cbccb709f0" + @checksum = "{md5}4a8ec4fa5f01b4ab1a0ab8cbccb709f0" @dir = '/dev/null/bucket/4/a/8/e/c/4/f/a/4a8ec4fa5f01b4ab1a0ab8cbccb709f0' @contents = "file contents" @@ -36,7 +36,7 @@ describe Puppet::FileBucketFile::File do it "should return nil if a file doesn't exist" do ::File.expects(:exist?).with("#{@dir}/contents").returns false - bucketfile = Puppet::FileBucketFile::File.new.send(:find_by_checksum, "md5:#{@digest}", {}) + bucketfile = Puppet::FileBucketFile::File.new.send(:find_by_checksum, "{md5}#{@digest}", {}) bucketfile.should == nil end @@ -45,7 +45,7 @@ describe Puppet::FileBucketFile::File do ::File.expects(:exist?).with("#{@dir}/paths").returns false ::File.expects(:read).with("#{@dir}/contents").returns @contents - bucketfile = Puppet::FileBucketFile::File.new.send(:find_by_checksum, "md5:#{@digest}", {}) + bucketfile = Puppet::FileBucketFile::File.new.send(:find_by_checksum, "{md5}#{@digest}", {}) bucketfile.should_not == nil end @@ -59,7 +59,7 @@ describe Puppet::FileBucketFile::File do mockfile.expects(:readlines).returns( paths ) ::File.expects(:open).with("#{@dir}/paths").yields mockfile - Puppet::FileBucketFile::File.new.send(:find_by_checksum, "md5:#{@digest}", {}).paths.should == paths + Puppet::FileBucketFile::File.new.send(:find_by_checksum, "{md5}#{@digest}", {}).paths.should == paths end end @@ -83,7 +83,7 @@ describe Puppet::FileBucketFile::File do end it "should call find_by_checksum" do - @store.expects(:find_by_checksum).with{|x,opts| x == "md5:#{@digest}"}.returns(false) + @store.expects(:find_by_checksum).with{|x,opts| x == "{md5}#{@digest}"}.returns(false) @store.find(@request) end @@ -101,7 +101,7 @@ describe Puppet::FileBucketFile::File do bucketfile.stubs(:checksum).returns(@checksum) bucketfile.expects(:contents=).with(content) - Puppet::FileBucket::File.expects(:new).with(nil, {:checksum => "md5:#{@digest}"}).yields(bucketfile).returns(bucketfile) + Puppet::FileBucket::File.expects(:new).with(nil, {:checksum => "{md5}#{@digest}"}).yields(bucketfile).returns(bucketfile) ::File.expects(:exist?).with(@contents_path).returns(true) ::File.expects(:exist?).with(@paths_path).returns(false) @@ -164,7 +164,7 @@ describe Puppet::FileBucketFile::File do Puppet[:bucketdir] = "/dev/null/bucket" @digest = "4a8ec4fa5f01b4ab1a0ab8cbccb709f0" - @checksum = "md5:4a8ec4fa5f01b4ab1a0ab8cbccb709f0" + @checksum = "{md5}4a8ec4fa5f01b4ab1a0ab8cbccb709f0" @dir = '/dev/null/bucket/4/a/8/e/c/4/f/a/4a8ec4fa5f01b4ab1a0ab8cbccb709f0' @contents = "file contents" @@ -206,7 +206,7 @@ describe Puppet::FileBucketFile::File do Puppet[:bucketdir] = "/dev/null/bucket" @digest = "4a8ec4fa5f01b4ab1a0ab8cbccb709f0" - @checksum = "md5:4a8ec4fa5f01b4ab1a0ab8cbccb709f0" + @checksum = "{md5}4a8ec4fa5f01b4ab1a0ab8cbccb709f0" @dir = '/dev/null/bucket/4/a/8/e/c/4/f/a/4a8ec4fa5f01b4ab1a0ab8cbccb709f0' @contents = "file contents" diff --git a/spec/unit/util/checksums.rb b/spec/unit/util/checksums.rb index eba564352..35d18633a 100755 --- a/spec/unit/util/checksums.rb +++ b/spec/unit/util/checksums.rb @@ -54,6 +54,10 @@ describe Puppet::Util::Checksums do @summer.sumtype("{md5}asdfasdfa").should == "md5" end + it "should have a method for stripping the data from a checksum" do + @summer.sumdata("{md5}asdfasdfa").should == "asdfasdfa" + end + it "should return a nil sumtype if the checksum does not mention a checksum type" do @summer.sumtype("asdfasdfa").should be_nil end |