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
273 def [](name)
274   find(name).new
275 end
available_application_names() click to toggle source

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

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

This is for testing only @api public

    # File lib/puppet/application.rb
319 def clear_everything_for_tests
320   @run_mode = @banner = @run_status = @option_parser_commands = nil
321 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
170 def controlled_run(&block)
171   return unless clear?
172   result = block.call
173   Process.kill(:HUP, $PID) if restart_requested?
174   result
175 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
304 def environment_mode(mode_name)
305   raise Puppet::Error, _("Invalid environment mode '%{mode_name}'") % { mode_name: mode_name } unless [:local, :remote, :not_required].include?(mode_name)
306   @environment_mode = mode_name
307 end
exit(code) click to toggle source

this is used for testing

    # File lib/puppet/application.rb
568 def self.exit(code)
569   exit(code)
570 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
227 def find(application_name)
228   begin
229     require @loader.expand(application_name.to_s.downcase)
230   rescue LoadError => e
231     Puppet.log_and_raise(e, _("Unable to find application '%{application_name}'. %{error}") % { application_name: application_name, error: e })
232   end
233 
234   class_name = Puppet::Util::ConstantInflector.file2constant(application_name.to_s)
235 
236   clazz = try_load_class(class_name)
237 
238   ################################################################
239   #### Begin 2.7.x backward compatibility hack;
240   ####  eventually we need to issue a deprecation warning here,
241   ####  and then get rid of this stanza in a subsequent release.
242   ################################################################
243   if (clazz.nil?)
244     class_name = application_name.capitalize
245     clazz = try_load_class(class_name)
246   end
247   ################################################################
248   #### End 2.7.x backward compatibility hack
249   ################################################################
250 
251   if clazz.nil?
252     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 })
253   end
254 
255   return clazz
256 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
313 def get_environment_mode
314   @environment_mode || :local
315 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
154 def interrupted?
155   [:restart_requested, :stop_requested].include? run_status
156 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
345 def initialize(command_line = Puppet::Util::CommandLine.new)
346   @command_line = CommandLineArgs.new(command_line.subcommand_name, command_line.args.dup)
347   @options = {}
348 end
option(*options, &block) click to toggle source

used to declare code that handle an option

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

Signal that the application should restart. @api public

    # File lib/puppet/application.rb
133 def restart!
134   self.run_status = :restart_requested
135 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
140 def restart_requested?
141   :restart_requested == run_status
142 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
280 def run_mode(mode_name = nil)
281   if mode_name
282     Puppet.settings.preferred_run_mode = mode_name
283   end
284 
285   return @run_mode if @run_mode and not mode_name
286 
287   require_relative '../puppet/util/run_mode'
288   @run_mode = Puppet::Util::RunMode[ mode_name || Puppet.settings.preferred_run_mode ]
289 end
stop!() click to toggle source

Signal that the application should stop. @api public

    # File lib/puppet/application.rb
127 def stop!
128   self.run_status = :stop_requested
129 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
147 def stop_requested?
148   :stop_requested == run_status
149 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
261 def try_load_class(class_name)
262     return self.const_defined?(class_name) ? const_get(class_name) : nil
263 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
363 def app_defaults
364   Puppet::Settings.app_defaults_for_run_mode(self.class.run_mode).merge(
365       :name => name
366   )
367 end
configure_indirector_routes() click to toggle source
    # File lib/puppet/application.rb
491 def configure_indirector_routes
492   Puppet::ApplicationSupport.configure_indirector_routes(name.to_s)
493 end
deprecate() click to toggle source

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

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

Return true if this application is deprecated. @api public

    # File lib/puppet/application.rb
393 def deprecated?
394   @deprecated
395 end
handle_logdest_arg(arg) click to toggle source
    # File lib/puppet/application.rb
475 def handle_logdest_arg(arg)
476   return if arg.nil?
477 
478   logdest = arg.split(',').map!(&:strip)
479   Puppet[:logdest] = arg
480 
481   logdest.each do |dest|
482     begin
483       Puppet::Util::Log.newdestination(dest)
484       options[:setdest] = true
485     rescue => detail
486       Puppet.log_and_raise(detail, _("Could not set logdest to %{dest}.") % { dest: arg })
487     end
488   end
489 end
handlearg(opt, val) click to toggle source
    # File lib/puppet/application.rb
562 def handlearg(opt, val)
563   opt, val = Puppet::Settings.clean_opt(opt, val)
564   send(:handle_unknown, opt, val) if respond_to?(:handle_unknown)
565 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
579 def help
580   _("No help available for puppet %{app_name}") % { app_name: name }
581 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
372 def initialize_app_defaults()
373   Puppet.settings.initialize_app_defaults(app_defaults)
374 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
502 def log_runtime_environment(extra_info=nil)
503   runtime_info = {
504     'puppet_version' => Puppet.version,
505     'ruby_version'   => RUBY_VERSION,
506     'run_mode'       => self.class.run_mode.name,
507   }
508   runtime_info['default_encoding'] = Encoding.default_external
509   runtime_info.merge!(extra_info) unless extra_info.nil?
510 
511   Puppet.debug 'Runtime environment: ' + runtime_info.map{|k,v| k + '=' + v.to_s}.join(', ')
512 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
429 def main
430   raise NotImplementedError, _("No valid command or main")
431 end
name() click to toggle source
    # File lib/puppet/application.rb
572 def name
573   self.class.to_s.sub(/.*::/,"").downcase.to_sym
574 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
529 def parse_options
530   # Create an option parser
531   option_parser = OptionParser.new(self.class.banner)
532 
533   # Here we're building up all of the options that the application may need to handle.  The main
534   # puppet settings defined in "defaults.rb" have already been parsed once (in command_line.rb) by
535   # the time we get here; however, our app may wish to handle some of them specially, so we need to
536   # make the parser aware of them again.  We might be able to make this a bit more efficient by
537   # re-using the parser object that gets built up in command_line.rb.  --cprice 2012-03-16
538 
539   # Add all global options to it.
540   Puppet.settings.optparse_addargs([]).each do |option|
541     option_parser.on(*option) do |arg|
542       handlearg(option[0], arg)
543     end
544   end
545 
546   # Add options that are local to this application, which were
547   # created using the "option()" metaprogramming method.  If there
548   # are any conflicts, this application's options will be favored.
549   self.class.option_parser_commands.each do |options, fname|
550     option_parser.on(*options) do |value|
551       # Call the method that "option()" created.
552       self.send(fname, value)
553     end
554   end
555 
556   # Scan command line.  We just hand any exceptions to our upper levels,
557   # rather than printing help and exiting, so that we can meaningfully
558   # respond with context-sensitive help if we want to. --daniel 2011-04-12
559   option_parser.parse!(self.command_line.args)
560 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
381 def preinit
382 end
run() click to toggle source

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

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

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

    # File lib/puppet/application.rb
436 def run_command
437   main
438 end
set_log_level(opts = nil) click to toggle source
    # File lib/puppet/application.rb
466 def set_log_level(opts = nil)
467   opts ||= options
468   if opts[:debug]
469     Puppet::Util::Log.level = :debug
470   elsif opts[:verbose] && !Puppet::Util::Log.sendlevel?(:info)
471     Puppet::Util::Log.level = :info
472   end
473 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
443 def setup
444   setup_logs
445 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
452 def setup_logs
453   handle_logdest_arg(Puppet[:logdest]) if !options[:setdest]
454 
455   unless options[:setdest]
456     if options[:debug] || options[:verbose]
457       Puppet::Util::Log.newdestination(:console)
458     end
459   end
460 
461   set_log_level
462 
463   Puppet::Util::Log.setup_default unless options[:setdest]
464 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
588 def summary
589   ""
590 end