#!/usr/bin/env ruby

#
# = Synopsis
#
# Run a +puppet+ script as a cfengine module.
#
# = Usage
#
#   puppet_module [-h|--help] [-V|--version] [-d|--debug] [-v|--verbose]
#               [-l|--logdest <file>]<file>
#
# = Description
#
# This is the standalone puppet execution script; use it to execute
# individual scripts that you write.  If you need to execute site-wide
# scripts, use +puppetd+ and +puppetmasterd+.
#
# = 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 for the full list of acceptable parameters.
#
# debug::
#   Enable full debugging.
#
# help::
#   Print this help message
#
# logdest::
#   Where to send messages.  Choose between syslog, the console, and a log file.
#   Defaults to sending messages to the console.
#
# verbose::
#   Print extra information.
#
# = Author
#
# Luke Kanies
#
# = Copyright
#
# Copyright (c) 2005 Reductive Labs, LLC
# Licensed under the GNU Public License

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

options = [
    [ "--debug",	"-d",			GetoptLong::NO_ARGUMENT ],
    [ "--help",		"-h",			GetoptLong::NO_ARGUMENT ],
    [ "--logdest",	"-l",			GetoptLong::REQUIRED_ARGUMENT ],
    [ "--verbose",  "-v",			GetoptLong::NO_ARGUMENT ],
    [ "--use-nodes",    			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)

debug = false
verbose = false
noop = false
logfile = false
parseonly = false

master = {
    :Local => true
}

setdest = false

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 "--use-nodes"
                master[:UseNodes] = true
            when "--verbose"
                Puppet::Util::Log.level = :info
                Puppet::Util::Log.newdestination(:console)
                verbose = true
            when "--debug"
                Puppet::Util::Log.level = :debug
                Puppet::Util::Log.newdestination(:console)
                debug = true
            when "--logdest"
                begin
                    Puppet::Util::Log.newdestination arg
                    setdest=true
                rescue => detail
                    $stderr.puts detail.to_s
                end
            else
                Puppet.settings.handlearg(opt, arg)
        end
    }
rescue GetoptLong::InvalidOption => detail
    $stderr.puts "Try '#{$0} --help'"
    if Puppet.features.usage?
        RDoc::usage(1,'usage')
    end
    exit(1)
end

# Now parse the config
if Puppet[:config] and File.exists? Puppet[:config]
    Puppet.settings.parse(Puppet[:config])
end

client = nil
server = nil

[:INT, :TERM].each do |signal|
    trap(signal) do
        Puppet.notice "Caught #{signal}; shutting down"
        [client, server].each { |obj|
            if obj
                obj.shutdown
            end
        }
    end
end

Puppet.genconfig
Puppet.genmanifest

unless ARGV.length > 0
    $stderr.puts "You must pass a script to parse"
    exit(14)
end

unless setdest
    Puppet::Util::Log.newdestination(:syslog)
end

Puppet[:manifest] = ARGV.shift

unless ENV.include?("CFALLCLASSES")
    $stderr.puts "Cfengine classes must be passed to the module"
    exit(15)
end

# Collect our facts.
Puppet::Node::Facts.terminus_class = :facter
facts = Puppet::Node::Facts.find("me")
facts.name = facts.values["hostname"]

# Create our Node
node = Puppet::Node.new(facts.name)

# Merge in the facts.
node.merge(facts.values)

classes = ENV["CFALLCLASSES"].split(":")

if classes.empty?
    $stderr.puts "Could not find any cfengine classes"
    exit(16)
end

node.classes = classes

begin
    # Compile our configuration
    catalog = Puppet::Node::Catalog.find(node)
rescue => detail
    if Puppet[:trace]
        puts detail.backtrace
    end
    if detail.is_a?(XMLRPC::FaultException)
        $stderr.puts detail.message
    else
        $stderr.puts detail
    end
    exit(1)
end

if parseonly
    exit(0)
end

begin
    # Translate it to a RAL configuration
    catalog = catalog.to_ral

    # And apply it
    catalog.apply
rescue => detail
    Puppet.err detail
    exit(1)
end
