#!/usr/local/bin/ruby

#
# = Synopsis
#
# A stand-alone Puppet filebucket client.
#
# = Usage
#
#   filebucket [-h|--help] [-V|--version] [-d|--debug] [-v|--verbose]
#      [-l|--local] [-r|--remote]
#      [-s|--server <server>] [-b|--bucket <directory>] <file> <file> ...
#
# = Description
#
# This is a stand-alone filebucket client for sending files to a local
# or central filebucket.
#
# = Usage
#
# This client can operate in three modes, with only one mode per call:
#
# backup::
#   Send one or more files to the specified file bucket.  Each sent file
#   is printed with its resulting md5 sum.
#
# get::
#   Return the text associated with an md5 sum.  The text is printed to
#   stdout, and only one file can be retrieved at a time.
#
# restore::
#   Given a file path and an md5 sum, store the content associated with the
#   sum into the specified file path.  You can specify an entirely new path
#   to this argument; you are not restricted to restoring the content to its
#   original location.
#
# Note that +filebucket+ defaults to using a network-based filebucket available on
# the server named +puppet+.  To use this, you'll have to be running as a user
# with valid Puppet certificates.  Alternatively, you can use your local file bucket
# by specifying +--local+.
#
# = Example
#
#   $ filebucket backup /etc/passwd
#   /etc/passwd: 429b225650b912a2ee067b0a4cf1e949
#   $ filebucket restore /tmp/passwd 429b225650b912a2ee067b0a4cf1e949
#   $
# 
# = Options
#
# Note that any configuration parameter that's valid in the configuration file
# is also a valid long argument.  For example, 'ssldir' is a valid configuration
# parameter, so you can specify '--ssldir <directory>' as an argument.
#
# See the configuration file documentation at
# http://reductivelabs.com/projects/puppet/reference/configref.html for
# the full list of acceptable parameters. A commented list of all
# configuration options can also be generated by running puppet with
# '--genconfig'.
#
# debug::
#   Enable full debugging.
#
# help::
#   Print this help message
#
# local::
#   Use the local filebucket.  This will use the default configuration
#   information.
#
# remote::
#   Use a remote filebucket.  This will use the default configuration
#   information.
#
# server::
#   The server to send the file to, instead of locally.
#
# verbose::
#   Print extra information.
#
# version::
#   Print version information.
#
# = Example
#
#   filebucket -b /tmp/filebucket /my/file
#
# = Author
#
# Luke Kanies
#
# = Copyright
#
# Copyright (c) 2005 Reductive Labs, LLC
# Licensed under the GNU Public License

require 'puppet'
require 'puppet/network/client'
require 'getoptlong'

options = [
    [ "--bucket",   "-b",			GetoptLong::REQUIRED_ARGUMENT ],
    [ "--debug",	"-d",			GetoptLong::NO_ARGUMENT ],
    [ "--help",		"-h",			GetoptLong::NO_ARGUMENT ],
    [ "--local",	"-l",			GetoptLong::NO_ARGUMENT ],
    [ "--remote",	"-r",			GetoptLong::NO_ARGUMENT ],
    [ "--verbose",  "-v",			GetoptLong::NO_ARGUMENT ],
    [ "--version",  "-V",           GetoptLong::NO_ARGUMENT ]
]

# Add all of the config parameters as valid options.
Puppet.settings.addargs(options)

result = GetoptLong.new(*options)

options = {}

begin
    result.each { |opt,arg|
        case opt
            when "--version"
                puts "%s" % Puppet.version
                exit
            when "--help"
                if Puppet.features.usage?
                    RDoc::usage && exit
                else
                    puts "No help available unless you have RDoc::usage installed"
                    exit
                end
            when "--bucket"
                options[:bucket] = arg
            when "--verbose"
                options[:verbose] = true
            when "--debug"
                options[:debug] = true
            when "--local"
                options[:local] = true
            when "--remote"
                options[:remote] = true
            else
                Puppet.settings.handlearg(opt, arg)
        end
    }
rescue GetoptLong::InvalidOption => detail
    $stderr.puts "Try '#{$0} --help'"
    exit(1)
end

Puppet::Log.newdestination(:console)

client = nil
server = nil

Puppet.settraps

if options[:debug]
    Puppet::Log.level = :debug
elsif options[:verbose]
    Puppet::Log.level = :info
end

# Now parse the config
Puppet.parse_config

Puppet.genconfig
Puppet.genmanifest

begin
    if options[:local] or options[:bucket]
        path = options[:bucket] || Puppet[:bucketdir]
        client = Puppet::Network::Client.dipper.new(:Path => path)
    else
        require 'puppet/network/handler'
        client = Puppet::Network::Client.dipper.new(:Server => Puppet[:server])
    end
rescue => detail
    $stderr.puts detail
    if Puppet[:trace]
        puts detail.backtrace
    end
    exit(1)
end

mode = ARGV.shift
case mode
when "get":
    md5 = ARGV.shift
    out = client.getfile(md5)
    print out
when "backup":
    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
when "restore":
    file = ARGV.shift
    md5 = ARGV.shift
    client.restore(file, md5)
else
    raise "Invalid mode %s" % mode
end

