diff options
author | Brice Figureau <brice-puppet@daysofwonder.com> | 2008-12-21 20:38:48 +0100 |
---|---|---|
committer | Brice Figureau <brice-puppet@daysofwonder.com> | 2009-02-16 20:12:02 +0100 |
commit | 97e716a97de81bb31b665c70489ee1657ddc5424 (patch) | |
tree | cf98fa5199cfd1654995c7d8c0d2bd496af7bb9e /lib | |
parent | 495ad6654643ee7cfc500a174d36fe67eb8970b1 (diff) | |
download | puppet-97e716a97de81bb31b665c70489ee1657ddc5424.tar.gz puppet-97e716a97de81bb31b665c70489ee1657ddc5424.tar.xz puppet-97e716a97de81bb31b665c70489ee1657ddc5424.zip |
Introducing the Application Controller
Signed-off-by: Brice Figureau <brice-puppet@daysofwonder.com>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/puppet/application.rb | 292 | ||||
-rw-r--r-- | lib/puppet/util/settings.rb | 28 |
2 files changed, 320 insertions, 0 deletions
diff --git a/lib/puppet/application.rb b/lib/puppet/application.rb new file mode 100644 index 000000000..3cd7598bb --- /dev/null +++ b/lib/puppet/application.rb @@ -0,0 +1,292 @@ +require 'puppet' +require 'optparse' + +# This class handles all the aspects of a Puppet application/executable +# * setting up options +# * setting up logs +# * choosing what to run +# +# === Usage +# The application is a Puppet::Application object that register itself in the list +# of available application. Each application needs a +name+ and a getopt +options+ +# description array. +# +# The executable uses the application object like this: +# Puppet::Application[:example].run +# +# +# Puppet::Application.new(:example) do +# +# preinit do +# # perform some pre initialization +# @all = false +# end +# +# # dispatch is called to know to what command to call +# dispatch do +# ARGV.shift +# end +# +# option("--arg ARGUMENT") do |v| +# @args << v +# end +# +# option("--debug", "-d") do |v| +# @debug = v +# end +# +# option("--all", "-a:) do |v| +# @all = v +# end +# +# unknown do |opt,arg| +# # last chance to manage an option +# ... +# # let's say to the framework we finally handle this option +# true +# end +# +# command(:read) do +# # read action +# end +# +# command(:write) do +# # writeaction +# end +# +# end +# +# === Preinit +# The preinit block is the first code to be called in your application, before option parsing, +# setup or command execution. +# +# === Options +# Puppet::Application uses +OptionParser+ to manage the application options. +# Options are defined with the +option+ method to which are passed various +# arguments, including the long option, the short option, a description... +# Refer to +OptionParser+ documentation for the exact format. +# * If the option method is given a block, this one will be called whenever +# the option is encountered in the command-line argument. +# * If the option method has no block, a default functionnality will be used, that +# stores the argument (or true/false if the option doesn't require an argument) in +# the global (to the application) options array. +# * If a given option was not defined by a the +option+ method, but it exists as a Puppet settings: +# * if +unknown+ was used with a block, it will be called with the option name and argument +# * if +unknown+ wasn't used, then the option/argument is handed to Puppet.settings.handlearg for +# a default behavior +# +# --help is managed directly by the Puppet::Application class, but can be overriden. +# +# === Setup +# Applications can use the setup block to perform any initialization. +# The defaul +setup+ behaviour is to: read Puppet configuration and manage log level and destination +# +# === What and how to run +# If the +dispatch+ block is defined it is called. This block should return the name of the registered command +# to be run. +# If it doesn't exist, it defaults to execute the +main+ command if defined. +# +class Puppet::Application + include Puppet::Util + + @@applications = {} + class << self + include Puppet::Util + end + + attr_reader :options, :opt_parser + + def self.[](name) + name = symbolize(name) + @@applications[name] + end + + def should_parse_config + @parse_config = true + end + + def should_not_parse_config + @parse_config = false + end + + def should_parse_config? + unless @parse_config.nil? + return @parse_config + end + @parse_config = true + end + + # used to declare a new command + def command(name, &block) + meta_def(symbolize(name), &block) + end + + # used as a catch-all for unknown option + def unknown(&block) + meta_def(:handle_unknown, &block) + end + + # used to declare code that handle an option + def option(*options, &block) + long = options.find { |opt| opt =~ /^--/ }.gsub(/^--(?:\[no-\])?([^ =]+).*$/, '\1' ).gsub('-','_') + fname = "handle_#{long}" + if (block_given?) + meta_def(symbolize(fname), &block) + else + meta_def(symbolize(fname)) do |value| + self.options["#{long}".to_sym] = value + end + end + @opt_parser.on(*options) do |value| + self.send(symbolize(fname), value) + end + end + + # used to declare accessor in a more natural way in the + # various applications + def attr_accessor(*args) + args.each do |arg| + meta_def(arg) do + instance_variable_get("@#{arg}".to_sym) + end + meta_def("#{arg}=") do |value| + instance_variable_set("@#{arg}".to_sym, value) + end + end + end + + # used to declare code run instead the default setup + def setup(&block) + meta_def(:run_setup, &block) + end + + # used to declare code to choose which command to run + def dispatch(&block) + meta_def(:get_command, &block) + end + + # used to execute code before running anything else + def preinit(&block) + meta_def(:run_preinit, &block) + end + + def initialize(name, banner = nil, &block) + @opt_parser = OptionParser.new(banner) + + name = symbolize(name) + + setup do + default_setup + end + + dispatch do + :main + end + + # empty by default + preinit do + end + + option("--help", "-h") do |v| + help + end + + @options = {} + + instance_eval(&block) if block_given? + + @@applications[name] = self + end + + # This is the main application entry point + def run + run_preinit + parse_options + Puppet.parse_config if should_parse_config? + run_setup + run_command + end + + def main + raise NotImplementedError, "No valid command or main" + end + + def run_command + if command = get_command() and respond_to?(command) + send(command) + else + main + end + end + + def default_setup + # Handle the logging settings + if options[:debug] or options[:verbose] + Puppet::Util::Log.newdestination(:console) + if options[:debug] + Puppet::Util::Log.level = :debug + else + Puppet::Util::Log.level = :info + end + end + + unless options[:setdest] + Puppet::Util::Log.newdestination(:syslog) + end + end + + def parse_options + # get all puppet options + optparse_opt = [] + optparse_opt = Puppet.settings.optparse_addargs(optparse_opt) + + # convert them to OptionParser format + optparse_opt.each do |option| + @opt_parser.on(*option) do |arg| + handlearg(option[0], arg) + end + end + + # scan command line argument + begin + @opt_parser.parse! + rescue OptionParser::ParseError => detail + $stderr.puts detail + $stderr.puts "Try '#{$0} --help'" + exit(1) + end + end + + def handlearg(opt, arg) + # rewrite --[no-]option to --no-option if that's what was given + if opt =~ /\[no-\]/ and !arg + opt = opt.gsub(/\[no-\]/,'no-') + end + # otherwise remove the [no-] prefix to not confuse everybody + opt = opt.gsub(/\[no-\]/, '') + unless respond_to?(:handle_unknown) and send(:handle_unknown, opt, arg) + # Puppet.settings.handlearg doesn't handle direct true/false :-) + if arg.is_a?(FalseClass) + arg = "false" + elsif arg.is_a?(TrueClass) + arg = "true" + end + Puppet.settings.handlearg(opt, arg) + end + end + + # this is used for testing + def self.exit(code) + exit(code) + end + + def help + if Puppet.features.usage? + ::RDoc::usage && exit + else + puts "No help available unless you have RDoc::usage installed" + exit + end + end + +end
\ No newline at end of file diff --git a/lib/puppet/util/settings.rb b/lib/puppet/util/settings.rb index 0af842c8d..27cee8c9f 100644 --- a/lib/puppet/util/settings.rb +++ b/lib/puppet/util/settings.rb @@ -55,6 +55,17 @@ class Puppet::Util::Settings return options end + # Generate the list of valid arguments, in a format that OptionParser can + # understand, and add them to the passed option list. + def optparse_addargs(options) + # Add all of the config parameters as valid options. + self.each { |name, element| + options << element.optparse_args + } + + return options + end + # Is our parameter a boolean parameter? def boolean?(param) param = param.to_sym @@ -912,6 +923,15 @@ Generated on #{Time.now}. end end + # get the arguments in OptionParser format + def optparse_args + if short + ["--#{name}", "-#{short}", desc, :REQUIRED] + else + ["--#{name}", desc, :REQUIRED] + end + end + def hook=(block) meta_def :handle, &block end @@ -1108,6 +1128,14 @@ Generated on #{Time.now}. end end + def optparse_args + if short + ["--[no-]#{name}", "-#{short}", desc, :NONE ] + else + ["--[no-]#{name}", desc, :NONE] + end + end + def munge(value) case value when true, "true": return true |