class Puppet::Application

Defines an abstract Puppet application.

# Usage

To create a new application extend `Puppet::Application`. Derived applications must implement the `main` method and should implement the `summary` and `help` methods in order to be included in `puppet help`, and should define application-specific options. For example:

“` class Puppet::Application::Example < Puppet::Application

def summary
  "My puppet example application"
end

def help
  <<~HELP
  puppet-example(8) -- #{summary}
  ...
  HELP
end

# define arg with a required option
option("--arg ARGUMENT") do |v|
  options[:arg] = v
end

# define arg with an optional option
option("--maybe [ARGUMENT]") do |v|
  options[:maybe] = v
end

# define long and short arg
option("--all", "-a")

def initialize(command_line = Puppet::Util::CommandLine.new)
  super
  @data = {}
end

def main
  # call action
  send(@command_line.args.shift)
end

def read
  # read action
end

def write
  # write action
end

end “`

Puppet defines the following application lifecycle methods that are called in the following order:

## Execution state The class attributes/methods of Puppet::Application serve as a global place to set and query the execution status of the application: stopping, restarting, etc. The setting of the application status does not directly affect its running status; it's assumed that the various components within the application will consult these settings appropriately and affect their own processing accordingly. Control operations (signal handlers and the like) should set the status appropriately to indicate to the overall system that it's the process of stopping or restarting (or just running as usual).

So, if something in your application needs to stop the process, for some reason, you might consider:

“`

def stop_me!
  # indicate that we're stopping
  Puppet::Application.stop!
  # ...do stuff...
end

“`

And, if you have some component that involves a long-running process, you might want to consider:

“`

def my_long_process(giant_list_to_munge)
  giant_list_to_munge.collect do |member|
    # bail if we're stopping
    return if Puppet::Application.stop_requested?
    process_member(member)
  end
end

“` @abstract @api public

Constants

CommandLineArgs
DOCPATTERN

Attributes

run_status[RW]
command_line[R]
options[R]

Public Class Methods

[](name) click to toggle source

Return an instance of the specified application.

@param [Symbol] name the lowercase name of the application @return [Puppet::Application] an instance of the specified name @raise [Puppet::Error] if the application class was not found. @raise [LoadError] if there was a problem loading the application file. @api public

    # File lib/puppet/application.rb
274 def [](name)
275   find(name).new
276 end
available_application_names() click to toggle source

@return [Array<String>] the names of available applications @api public

    # File lib/puppet/application.rb
205 def available_application_names
206   # Use our configured environment to load the application, as it may
207   # be in a module we installed locally, otherwise fallback to our
208   # current environment (*root*). Once we load the application the
209   # current environment will change from *root* to the application
210   # specific environment.
211   environment = Puppet.lookup(:environments).get(Puppet[:environment]) ||
212                 Puppet.lookup(:current_environment)
213   @loader.files_to_load(environment).map do |fn|
214     ::File.basename(fn, '.rb')
215   end.uniq
216 end
banner(banner = nil) click to toggle source
clear!() click to toggle source
    # File lib/puppet/application.rb
122 def clear!
123   self.run_status = nil
124 end
clear?() click to toggle source

Indicates that Puppet::Application believes that it's in usual running run_mode (no stop/restart request currently active). @api public

    # File lib/puppet/application.rb
162 def clear?
163   run_status.nil?
164 end
clear_everything_for_tests() click to toggle source

This is for testing only @api public

    # File lib/puppet/application.rb
320 def clear_everything_for_tests
321   @run_mode = @banner = @run_status = @option_parser_commands = nil
322 end
controlled_run(&block) click to toggle source

Only executes the given block if the run status of Puppet::Application is clear (no restarts, stops, etc. requested). Upon block execution, checks the run status again; if a restart has been requested during the block's execution, then controlled_run will send a new HUP signal to the current process. Thus, long-running background processes can potentially finish their work before a restart.

    # File lib/puppet/application.rb
171 def controlled_run(&block)
172   return unless clear?
173   result = block.call
174   Process.kill(:HUP, $PID) if restart_requested?
175   result
176 end
environment_mode(mode_name) click to toggle source

Sets environment_mode name. When acting as a compiler, the environment mode should be `:local` since the directory must exist to compile the catalog. When acting as an agent, the environment mode should be `:remote` since the Puppet setting refers to an environment directoy on a remote system. The `:not_required` mode is for cases where the application does not need an environment to run.

@param mode_name [Symbol] The name of the environment mode to run in. May

be one of `:local`, `:remote`, or `:not_required`. This impacts where the
application looks for its specified environment. If `:not_required` or
`:remote` are set, the application will not fail if the environment does
not exist on the local filesystem.

@api public

    # File lib/puppet/application.rb
305 def environment_mode(mode_name)
306   raise Puppet::Error, _("Invalid environment mode '%{mode_name}'") % { mode_name: mode_name } unless [:local, :remote, :not_required].include?(mode_name)
307   @environment_mode = mode_name
308 end
exit(code) click to toggle source

this is used for testing

    # File lib/puppet/application.rb
569 def self.exit(code)
570   exit(code)
571 end
find(application_name) click to toggle source

Finds the class for a given application and loads the class. This does not create an instance of the application, it only gets a handle to the class. The code for the application is expected to live in a ruby file `puppet/application/#{name}.rb` that is available on the `$LOAD_PATH`.

@param application_name [String] the name of the application to find (eg. “apply”). @return [Class] the Class instance of the application that was found. @raise [Puppet::Error] if the application class was not found. @raise [LoadError] if there was a problem loading the application file. @api public

    # File lib/puppet/application.rb
228 def find(application_name)
229   begin
230     require @loader.expand(application_name.to_s.downcase)
231   rescue LoadError => e
232     Puppet.log_and_raise(e, _("Unable to find application '%{application_name}'. %{error}") % { application_name: application_name, error: e })
233   end
234 
235   class_name = Puppet::Util::ConstantInflector.file2constant(application_name.to_s)
236 
237   clazz = try_load_class(class_name)
238 
239   ################################################################
240   #### Begin 2.7.x backward compatibility hack;
241   ####  eventually we need to issue a deprecation warning here,
242   ####  and then get rid of this stanza in a subsequent release.
243   ################################################################
244   if (clazz.nil?)
245     class_name = application_name.capitalize
246     clazz = try_load_class(class_name)
247   end
248   ################################################################
249   #### End 2.7.x backward compatibility hack
250   ################################################################
251 
252   if clazz.nil?
253     raise Puppet::Error.new(_("Unable to load application class '%{class_name}' from file 'puppet/application/%{application_name}.rb'") % { class_name: class_name, application_name: application_name })
254   end
255 
256   return clazz
257 end
get_environment_mode() click to toggle source

Gets environment_mode name. If none is set with `environment_mode=`, default to :local. @return [Symbol] The current environment mode @api public

    # File lib/puppet/application.rb
314 def get_environment_mode
315   @environment_mode || :local
316 end
interrupted?() click to toggle source

Indicates that one of stop! or start! was invoked on Puppet::Application, and some kind of process shutdown/short-circuit may be necessary. @api public

    # File lib/puppet/application.rb
155 def interrupted?
156   [:restart_requested, :stop_requested].include? run_status
157 end
new(command_line = Puppet::Util::CommandLine.new) click to toggle source

Initialize the application receiving the {Puppet::Util::CommandLine} object containing the application name and arguments.

@param command_line [Puppet::Util::CommandLine] An instance of the command line to create the application with @api public

    # File lib/puppet/application.rb
346 def initialize(command_line = Puppet::Util::CommandLine.new)
347   @command_line = CommandLineArgs.new(command_line.subcommand_name, command_line.args.dup)
348   @options = {}
349 end
option(*options, &block) click to toggle source

used to declare code that handle an option

    # File lib/puppet/application.rb
179 def option(*options, &block)
180   long = options.find { |opt| opt =~ /^--/ }.gsub(/^--(?:\[no-\])?([^ =]+).*$/, '\1' ).tr('-','_')
181   fname = "handle_#{long}".intern
182   if (block_given?)
183     define_method(fname, &block)
184   else
185     define_method(fname) do |value|
186       self.options["#{long}".to_sym] = value
187     end
188   end
189   self.option_parser_commands << [options, fname]
190 end
option_parser_commands() click to toggle source
    # File lib/puppet/application.rb
196 def option_parser_commands
197   @option_parser_commands ||= (
198     superclass.respond_to?(:option_parser_commands) ? superclass.option_parser_commands.dup : []
199   )
200   @option_parser_commands
201 end
restart!() click to toggle source

Signal that the application should restart. @api public

    # File lib/puppet/application.rb
134 def restart!
135   self.run_status = :restart_requested
136 end
restart_requested?() click to toggle source

Indicates that Puppet::Application.restart! has been invoked and components should do what is necessary to facilitate a restart. @api public

    # File lib/puppet/application.rb
141 def restart_requested?
142   :restart_requested == run_status
143 end
run_mode(mode_name = nil) click to toggle source

Sets or gets the run_mode name. Sets the run_mode name if a mode_name is passed. Otherwise, gets the run_mode or a default run_mode @api public

    # File lib/puppet/application.rb
281 def run_mode(mode_name = nil)
282   if mode_name
283     Puppet.settings.preferred_run_mode = mode_name
284   end
285 
286   return @run_mode if @run_mode and not mode_name
287 
288   require_relative '../puppet/util/run_mode'
289   @run_mode = Puppet::Util::RunMode[ mode_name || Puppet.settings.preferred_run_mode ]
290 end
stop!() click to toggle source

Signal that the application should stop. @api public

    # File lib/puppet/application.rb
128 def stop!
129   self.run_status = :stop_requested
130 end
stop_requested?() click to toggle source

Indicates that Puppet::Application.stop! has been invoked and components should do what is necessary for a clean stop. @api public

    # File lib/puppet/application.rb
148 def stop_requested?
149   :stop_requested == run_status
150 end

Private Class Methods

try_load_class(class_name) click to toggle source

Given the fully qualified name of a class, attempt to get the class instance. @param [String] class_name the fully qualified name of the class to try to load @return [Class] the Class instance, or nil? if it could not be loaded.

    # File lib/puppet/application.rb
262 def try_load_class(class_name)
263     return self.const_defined?(class_name) ? const_get(class_name) : nil
264 end

Public Instance Methods

app_defaults() click to toggle source

Now that the `run_mode` has been resolved, return default settings for the application. Note these values may be overridden when puppet's configuration is loaded later.

@example To override the facts terminus:

def app_defaults
  super.merge({
    :facts_terminus => 'yaml'
  })
end

@return [Hash<String, String>] default application settings @api public

    # File lib/puppet/application.rb
364 def app_defaults
365   Puppet::Settings.app_defaults_for_run_mode(self.class.run_mode).merge(
366       :name => name
367   )
368 end
configure_indirector_routes() click to toggle source
    # File lib/puppet/application.rb
492 def configure_indirector_routes
493   Puppet::ApplicationSupport.configure_indirector_routes(name.to_s)
494 end
deprecate() click to toggle source

Call in setup of subclass to deprecate an application. @return [void] @api public

    # File lib/puppet/application.rb
388 def deprecate
389   @deprecated = true
390 end
deprecated?() click to toggle source

Return true if this application is deprecated. @api public

    # File lib/puppet/application.rb
394 def deprecated?
395   @deprecated
396 end
handle_logdest_arg(arg) click to toggle source
    # File lib/puppet/application.rb
476 def handle_logdest_arg(arg)
477   return if arg.nil?
478 
479   logdest = arg.split(',').map!(&:strip)
480   Puppet[:logdest] = arg
481 
482   logdest.each do |dest|
483     begin
484       Puppet::Util::Log.newdestination(dest)
485       options[:setdest] = true
486     rescue => detail
487       Puppet.log_and_raise(detail, _("Could not set logdest to %{dest}.") % { dest: arg })
488     end
489   end
490 end
handlearg(opt, val) click to toggle source
    # File lib/puppet/application.rb
563 def handlearg(opt, val)
564   opt, val = Puppet::Settings.clean_opt(opt, val)
565   send(:handle_unknown, opt, val) if respond_to?(:handle_unknown)
566 end
help() click to toggle source

Return the text to display when running `puppet help`. @return [String] The help to display @api public

    # File lib/puppet/application.rb
580 def help
581   _("No help available for puppet %{app_name}") % { app_name: name }
582 end
initialize_app_defaults() click to toggle source

Initialize application defaults. It's usually not necessary to override this method. @return [void] @api public

    # File lib/puppet/application.rb
373 def initialize_app_defaults()
374   Puppet.settings.initialize_app_defaults(app_defaults)
375 end
log_runtime_environment(extra_info=nil) click to toggle source

Output basic information about the runtime environment for debugging purposes.

@param extra_info [Hash{String => to_s}] a flat hash of extra information

to log. Intended to be passed to super by subclasses.

@return [void] @api public

    # File lib/puppet/application.rb
503 def log_runtime_environment(extra_info=nil)
504   runtime_info = {
505     'puppet_version' => Puppet.version,
506     'ruby_version'   => RUBY_VERSION,
507     'run_mode'       => self.class.run_mode.name,
508   }
509   runtime_info['default_encoding'] = Encoding.default_external
510   runtime_info.merge!(extra_info) unless extra_info.nil?
511 
512   Puppet.debug 'Runtime environment: ' + runtime_info.map{|k,v| k + '=' + v.to_s}.join(', ')
513 end
main() click to toggle source

This method must be overridden and perform whatever action is required for the application. The `command_line` reader contains the actions and arguments. @return [void] @api public

    # File lib/puppet/application.rb
430 def main
431   raise NotImplementedError, _("No valid command or main")
432 end
name() click to toggle source
    # File lib/puppet/application.rb
573 def name
574   self.class.to_s.sub(/.*::/,"").downcase.to_sym
575 end
parse_options() click to toggle source

Options defined with the `option` method are parsed from settings and the command line. Refer to {OptionParser} documentation for the exact format. Options are parsed as follows:

  • If the option method is given a block, then it will be called whenever the option is encountered in the command-line argument.

  • If the option method has no block, then the default option handler will store the argument in the `options` instance variable.

  • If a given option was not defined by an `option` method, but it exists as a Puppet setting:

    • 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.

  • The `-h` and `–help` options are automatically handled by the command line before creating the application.

Options specified on the command line override settings. It is usually not necessary to override this method. @return [void] @api public

    # File lib/puppet/application.rb
530 def parse_options
531   # Create an option parser
532   option_parser = OptionParser.new(self.class.banner)
533 
534   # Here we're building up all of the options that the application may need to handle.  The main
535   # puppet settings defined in "defaults.rb" have already been parsed once (in command_line.rb) by
536   # the time we get here; however, our app may wish to handle some of them specially, so we need to
537   # make the parser aware of them again.  We might be able to make this a bit more efficient by
538   # re-using the parser object that gets built up in command_line.rb.  --cprice 2012-03-16
539 
540   # Add all global options to it.
541   Puppet.settings.optparse_addargs([]).each do |option|
542     option_parser.on(*option) do |arg|
543       handlearg(option[0], arg)
544     end
545   end
546 
547   # Add options that are local to this application, which were
548   # created using the "option()" metaprogramming method.  If there
549   # are any conflicts, this application's options will be favored.
550   self.class.option_parser_commands.each do |options, fname|
551     option_parser.on(*options) do |value|
552       # Call the method that "option()" created.
553       self.send(fname, value)
554     end
555   end
556 
557   # Scan command line.  We just hand any exceptions to our upper levels,
558   # rather than printing help and exiting, so that we can meaningfully
559   # respond with context-sensitive help if we want to. --daniel 2011-04-12
560   option_parser.parse!(self.command_line.args)
561 end
preinit() click to toggle source

The preinit block is the first code to be called in your application, after `initialize`, but before option parsing, setup or command execution. It is usually not necessary to override this method. @return [void] @api public

    # File lib/puppet/application.rb
382 def preinit
383 end
run() click to toggle source

Execute the application. This method should not be overridden. @return [void] @api public

    # File lib/puppet/application.rb
401 def run
402 
403   # I don't really like the names of these lifecycle phases.  It would be nice to change them to some more meaningful
404   # names, and make deprecated aliases.  --cprice 2012-03-16
405 
406   exit_on_fail(_("Could not get application-specific default settings")) do
407     initialize_app_defaults
408   end
409 
410   Puppet::ApplicationSupport.push_application_context(self.class.run_mode, self.class.get_environment_mode)
411 
412   exit_on_fail(_("Could not initialize"))                { preinit }
413   exit_on_fail(_("Could not parse application options")) { parse_options }
414   exit_on_fail(_("Could not prepare for execution"))     { setup }
415 
416   if deprecated?
417     Puppet.deprecation_warning(_("`puppet %{name}` is deprecated and will be removed in a future release.") % { name: name })
418   end
419 
420   exit_on_fail(_("Could not configure routes from %{route_file}") % { route_file: Puppet[:route_file] }) { configure_indirector_routes }
421   exit_on_fail(_("Could not log runtime debug info"))                       { log_runtime_environment }
422   exit_on_fail(_("Could not run"))                                          { run_command }
423 end
run_command() click to toggle source

Run the application. By default, it calls {#main}. @return [void] @api public

    # File lib/puppet/application.rb
437 def run_command
438   main
439 end
set_log_level(opts = nil) click to toggle source
    # File lib/puppet/application.rb
467 def set_log_level(opts = nil)
468   opts ||= options
469   if opts[:debug]
470     Puppet::Util::Log.level = :debug
471   elsif opts[:verbose] && !Puppet::Util::Log.sendlevel?(:info)
472     Puppet::Util::Log.level = :info
473   end
474 end
setup() click to toggle source

Setup the application. It is usually not necessary to override this method. @return [void] @api public

    # File lib/puppet/application.rb
444 def setup
445   setup_logs
446 end
setup_logs() click to toggle source

Setup logging. By default the `console` log destination will only be created if `debug` or `verbose` is specified on the command line. Override to customize the logging behavior. @return [void] @api public

    # File lib/puppet/application.rb
453 def setup_logs
454   handle_logdest_arg(Puppet[:logdest]) if !options[:setdest]
455 
456   unless options[:setdest]
457     if options[:debug] || options[:verbose]
458       Puppet::Util::Log.newdestination(:console)
459     end
460   end
461 
462   set_log_level
463 
464   Puppet::Util::Log.setup_default unless options[:setdest]
465 end
summary() click to toggle source

The description used in top level `puppet help` output If left empty in implementations, we will attempt to extract the summary from the help text itself. @return [String] @api public

    # File lib/puppet/application.rb
589 def summary
590   ""
591 end