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:
-
{#initialize}
-
{#initialize_app_defaults}
-
{#preinit}
-
{#parse_options}
-
{#setup}
-
{#main}
## 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
Public Class Methods
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
@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
# File lib/puppet/application.rb 122 def clear! 123 self.run_status = nil 124 end
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
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
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
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
this is used for testing
# File lib/puppet/application.rb 569 def self.exit(code) 570 exit(code) 571 end
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
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
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
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
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
# 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
Signal that the application should restart. @api public
# File lib/puppet/application.rb 134 def restart! 135 self.run_status = :restart_requested 136 end
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
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
Signal that the application should stop. @api public
# File lib/puppet/application.rb 128 def stop! 129 self.run_status = :stop_requested 130 end
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
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
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
# File lib/puppet/application.rb 492 def configure_indirector_routes 493 Puppet::ApplicationSupport.configure_indirector_routes(name.to_s) 494 end
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
Return true if this application is deprecated. @api public
# File lib/puppet/application.rb 394 def deprecated? 395 @deprecated 396 end
# 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
# 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
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 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
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
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
# File lib/puppet/application.rb 573 def name 574 self.class.to_s.sub(/.*::/,"").downcase.to_sym 575 end
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
Puppetsetting:-
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
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
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 the application. By default, it calls {#main}. @return [void] @api public
# File lib/puppet/application.rb 437 def run_command 438 main 439 end
# 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 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 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
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