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 273 def [](name) 274 find(name).new 275 end
@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
# File lib/puppet/application.rb 121 def clear! 122 self.run_status = nil 123 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 161 def clear? 162 run_status.nil? 163 end
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
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
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
this is used for testing
# File lib/puppet/application.rb 568 def self.exit(code) 569 exit(code) 570 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 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
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
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
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
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
# 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
Signal that the application should restart. @api public
# File lib/puppet/application.rb 133 def restart! 134 self.run_status = :restart_requested 135 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 140 def restart_requested? 141 :restart_requested == run_status 142 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 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
Signal that the application should stop. @api public
# File lib/puppet/application.rb 127 def stop! 128 self.run_status = :stop_requested 129 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 147 def stop_requested? 148 :stop_requested == run_status 149 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 261 def try_load_class(class_name) 262 return self.const_defined?(class_name) ? const_get(class_name) : nil 263 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 363 def app_defaults 364 Puppet::Settings.app_defaults_for_run_mode(self.class.run_mode).merge( 365 :name => name 366 ) 367 end
# File lib/puppet/application.rb 491 def configure_indirector_routes 492 Puppet::ApplicationSupport.configure_indirector_routes(name.to_s) 493 end
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
Return true if this application is deprecated. @api public
# File lib/puppet/application.rb 393 def deprecated? 394 @deprecated 395 end
# 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
# 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
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 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
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
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
# File lib/puppet/application.rb 572 def name 573 self.class.to_s.sub(/.*::/,"").downcase.to_sym 574 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
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
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
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 the application. By default, it calls {#main}. @return [void] @api public
# File lib/puppet/application.rb 436 def run_command 437 main 438 end
# 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 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 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
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