summaryrefslogtreecommitdiffstats
path: root/lib/puppet/server/filebucket.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/puppet/server/filebucket.rb')
-rwxr-xr-xlib/puppet/server/filebucket.rb277
1 files changed, 277 insertions, 0 deletions
diff --git a/lib/puppet/server/filebucket.rb b/lib/puppet/server/filebucket.rb
new file mode 100755
index 000000000..f0e717146
--- /dev/null
+++ b/lib/puppet/server/filebucket.rb
@@ -0,0 +1,277 @@
+#!/usr/bin/ruby -w
+
+#--------------------
+# accept and serve files
+#
+# $Id$
+
+
+require 'webrick'
+#require 'webrick/https'
+require 'xmlrpc/server'
+require 'xmlrpc/client'
+#require 'webrick/httpstatus'
+require 'facter'
+require 'digest/md5'
+require 'base64'
+
+class BucketError < RuntimeError; end
+
+module FileBucket
+ DEFAULTPORT = 8139
+ # this doesn't work for relative paths
+ def FileBucket.mkdir(dir)
+ if FileTest.exist?(dir)
+ return false
+ else
+ tmp = dir.sub(/^\//,'')
+ path = [File::SEPARATOR]
+ tmp.split(File::SEPARATOR).each { |dir|
+ path.push dir
+ unless FileTest.exist?(File.join(path))
+ Dir.mkdir(File.join(path))
+ end
+ }
+ return true
+ end
+ end
+
+ def FileBucket.paths(base,md5)
+ return [
+ File.join(base, md5),
+ File.join(base, md5, "contents"),
+ File.join(base, md5, "paths")
+ ]
+ end
+
+ #---------------------------------------------------------------
+ class Bucket
+ def initialize(hash)
+ # build our AST
+
+ if hash.include?(:ConflictCheck)
+ @conflictchk = hash[:ConflictCheck]
+ hash.delete(:ConflictCheck)
+ else
+ @conflictchk = true
+ end
+
+ if hash.include?(:Path)
+ @bucket = hash[:Path]
+ hash.delete(:Path)
+ else
+ if defined? Puppet
+ @bucket = Puppet[:bucketdir]
+ else
+ @bucket = File.expand_path("~/.filebucket")
+ end
+ end
+
+ # XXX this should really be done using Puppet::Type instances...
+ FileBucket.mkdir(@bucket)
+ end
+
+ # accept a file from a client
+ def addfile(string,path)
+ #puts "entering addfile"
+ contents = Base64.decode64(string)
+ #puts "string is decoded"
+
+ md5 = Digest::MD5.hexdigest(contents)
+ #puts "md5 is made"
+
+ bpath, bfile, pathpath = FileBucket.paths(@bucket,md5)
+
+ # if it's a new directory...
+ if FileBucket.mkdir(bpath)
+ # ...then just create the file
+ #puts "creating file"
+ File.open(bfile, File::WRONLY|File::CREAT) { |of|
+ of.print contents
+ }
+ #puts "File is created"
+ else # if the dir already existed...
+ # ...we need to verify that the contents match the existing file
+ if @conflictchk
+ unless FileTest.exists?(bfile)
+ raise(BucketError,
+ "No file at %s for sum %s" % [bfile,md5], caller)
+ end
+
+ curfile = ""
+ File.open(bfile) { |of|
+ curfile = of.read
+ }
+
+ # if the contents don't match, then we've found a conflict
+ # unlikely, but quite bad
+ if curfile != contents
+ raise(BucketError,
+ "Got passed new contents for sum %s" % md5, caller)
+ end
+ end
+ #puts "Conflict check is done"
+ end
+
+ # in either case, add the passed path to the list of paths
+ paths = nil
+ addpath = false
+ if FileTest.exists?(pathpath)
+ File.open(pathpath) { |of|
+ paths = of.readlines
+ }
+
+ # unless our path is already there...
+ unless paths.include?(path)
+ addpath = true
+ end
+ else
+ addpath = true
+ end
+ #puts "Path is checked"
+
+ # if it's a new file, or if our path isn't in the file yet, add it
+ if addpath
+ File.open(pathpath, File::WRONLY|File::CREAT|File::APPEND) { |of|
+ of.puts path
+ }
+ #puts "Path is added"
+ end
+
+ return md5
+ end
+
+ def getfile(md5)
+ bpath, bfile, bpaths = FileBucket.paths(@bucket,md5)
+
+ unless FileTest.exists?(bfile)
+ return false
+ end
+
+ contents = nil
+ File.open(bfile) { |of|
+ contents = of.read
+ }
+
+ return Base64.encode64(contents)
+ end
+
+ private
+
+ def on_init
+ @default_namespace = 'urn:filebucket-server'
+ add_method(self, 'addfile', 'string', 'path')
+ add_method(self, 'getfile', 'md5')
+ end
+
+ def cert(filename)
+ OpenSSL::X509::Certificate.new(File.open(File.join(@dir, filename)) { |f|
+ f.read
+ })
+ end
+
+ def key(filename)
+ OpenSSL::PKey::RSA.new(File.open(File.join(@dir, filename)) { |f|
+ f.read
+ })
+ end
+
+ end
+ #---------------------------------------------------------------
+
+ class BucketWebserver < WEBrick::HTTPServer
+ def initialize(hash)
+ unless hash.include?(:Port)
+ hash[:Port] = FileBucket::DEFAULTPORT
+ end
+ servlet = XMLRPC::WEBrickServlet.new
+ @bucket = FileBucket::Bucket.new(hash)
+ #puts @bucket
+ servlet.add_handler("bucket",@bucket)
+ super
+
+ self.mount("/RPC2", servlet)
+ end
+ end
+
+ class BucketClient < XMLRPC::Client
+ @@methods = [ :addfile, :getfile ]
+
+ @@methods.each { |method|
+ self.send(:define_method,method) { |*args|
+ begin
+ call("bucket.%s" % method.to_s,*args)
+ rescue => detail
+ #puts detail
+ end
+ }
+ }
+
+ def initialize(hash)
+ hash[:Path] ||= "/RPC2"
+ hash[:Server] ||= "localhost"
+ hash[:Port] ||= FileBucket::DEFAULTPORT
+ super(hash[:Server],hash[:Path],hash[:Port])
+ end
+ end
+
+ class Dipper
+ def initialize(hash)
+ if hash.include?(:Server)
+ @bucket = FileBucket::BucketClient.new(
+ :Server => hash[:Server]
+ )
+ elsif hash.include?(:Bucket)
+ @bucket = hash[:Bucket]
+ elsif hash.include?(:Path)
+ @bucket = FileBucket::Bucket.new(
+ :Path => hash[:Path]
+ )
+ end
+ end
+
+ def backup(file)
+ unless FileTest.exists?(file)
+ raise(BucketError, "File %s does not exist" % file, caller)
+ end
+ contents = File.open(file) { |of| of.read }
+
+ string = Base64.encode64(contents)
+ #puts "string is created"
+
+ sum = @bucket.addfile(string,file)
+ #puts "file %s is added" % file
+ return sum
+ end
+
+ def restore(file,sum)
+ restore = true
+ if FileTest.exists?(file)
+ contents = File.open(file) { |of| of.read }
+
+ cursum = Digest::MD5.hexdigest(contents)
+
+ # if the checksum has changed...
+ # this might be extra effort
+ if cursum == sum
+ restore = false
+ end
+ end
+
+ if restore
+ #puts "Restoring %s" % file
+ newcontents = Base64.decode64(@bucket.getfile(sum))
+ newsum = Digest::MD5.hexdigest(newcontents)
+ File.open(file,File::WRONLY|File::TRUNC) { |of|
+ of.print(newcontents)
+ }
+ #puts "Done"
+ return newsum
+ else
+ return nil
+ end
+
+ end
+ end
+ #---------------------------------------------------------------
+end