Class: Puppet::Application

Inherits:
Object show all
Extended by:
Util
Includes:
Util
Defined in:
lib/puppet/application.rb

Overview

This class handles all the aspects of a Puppet application/executable

  • setting up options

  • setting up logs

  • choosing what to run

  • representing execution status

Usage

An application is a subclass of Puppet::Application.

For legacy compatibility,

Puppet::Application[:example].run

is equivalent to

Puppet::Application::Example.new.run

class Puppet::Application::Example < Puppet::Application

def preinit
    # perform some pre initialization
    @all = false
end

# run_command is called to actually run the specified command
def run_command
    send Puppet::Util::CommandLine.new.args.shift
end

# option uses metaprogramming to create a method
# and also tells the option parser how to invoke that method
option("--arg ARGUMENT") do |v|
    @args << v
end

option("--debug", "-d") do |v|
    @debug = v
end

option("--all", "-a:) do |v|
    @all = v
end

def handle_unknown(opt,arg)
    # last chance to manage an option
    ...
    # let's say to the framework we finally handle this option
    true
end

def read
    # read action
end

def write
    # 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 functionality 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 overridden.

Setup

Applications can use the setup block to perform any initialization. The default 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.

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

Defined Under Namespace

Classes: Agent, Apply, Catalog, Cert, CommandLineArgs, Config, Describe, Device, Doc, Epp, FaceBase, Facts, Filebucket, Generate, Help, IndirectionBase, Key, Lookup, Man, Module, Node, Parser, Plugin, Report, Resource, Script, Ssl, Status

Constant Summary collapse

DOCPATTERN =
::File.expand_path(::File.dirname(__FILE__) + "/util/command_line/*" )

Constants included from Util

Util::AbsolutePathPosix, Util::AbsolutePathWindows, Util::DEFAULT_POSIX_MODE, Util::DEFAULT_WINDOWS_MODE, Util::RFC_3986_URI_REGEX

Constants included from Util::SymbolicFileMode

Util::SymbolicFileMode::SetGIDBit, Util::SymbolicFileMode::SetUIDBit, Util::SymbolicFileMode::StickyBit, Util::SymbolicFileMode::SymbolicMode, Util::SymbolicFileMode::SymbolicSpecialToBit

Constants included from Util::POSIX

Util::POSIX::LOCALE_ENV_VARS, Util::POSIX::USER_ENV_VARS

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Util

absolute_path?, benchmark, chuser, clear_environment, default_env, deterministic_rand, deterministic_rand_int, exit_on_fail, get_env, get_environment, logmethods, merge_environment, path_to_uri, pretty_backtrace, replace_file, safe_posix_fork, set_env, symbolizehash, thinmark, uri_encode, uri_query_encode, uri_to_path, which, withenv, withumask

Methods included from Util::SymbolicFileMode

#normalize_symbolic_mode, #symbolic_mode_to_int, #valid_symbolic_mode?

Methods included from Util::POSIX

#get_posix_field, #gid, groups_of, #idfield, #methodbyid, #methodbyname, #search_posix_field, #uid

Constructor Details

#initialize(command_line = Puppet::Util::CommandLine.new) ⇒ Application


344
345
346
347
# File 'lib/puppet/application.rb', line 344

def initialize(command_line = Puppet::Util::CommandLine.new)
  @command_line = CommandLineArgs.new(command_line.subcommand_name, command_line.args.dup)
  @options = {}
end

Class Attribute Details

.run_statusObject

Returns the value of attribute run_status


134
135
136
# File 'lib/puppet/application.rb', line 134

def run_status
  @run_status
end

Instance Attribute Details

#command_lineObject (readonly)

Returns the value of attribute command_line


314
315
316
# File 'lib/puppet/application.rb', line 314

def command_line
  @command_line
end

#optionsObject (readonly)

Returns the value of attribute options


314
315
316
# File 'lib/puppet/application.rb', line 314

def options
  @options
end

Class Method Details

.[](name) ⇒ Object


273
274
275
# File 'lib/puppet/application.rb', line 273

def [](name)
  find(name).new
end

.available_application_namesArray<String>

Returns the names of available applications


211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/puppet/application.rb', line 211

def available_application_names
  # Use our configured environment to load the application, as it may
  # be in a module we installed locally, otherwise fallback to our
  # current environment (*root*). Once we load the application the
  # current environment will change from *root* to the application
  # specific environment.
  environment = Puppet.lookup(:environments).get(Puppet[:environment]) ||
                Puppet.lookup(:current_environment)
  @loader.files_to_load(environment).map do |fn|
    ::File.basename(fn, '.rb')
  end.uniq
end

198
199
200
# File 'lib/puppet/application.rb', line 198

def banner(banner = nil)
  @banner ||= banner
end

.clear!Object


136
137
138
# File 'lib/puppet/application.rb', line 136

def clear!
  self.run_status = nil
end

.clear?Boolean

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


168
169
170
# File 'lib/puppet/application.rb', line 168

def clear?
  run_status.nil?
end

.clear_everything_for_testsObject

This is for testing only


309
310
311
# File 'lib/puppet/application.rb', line 309

def clear_everything_for_tests
  @run_mode = @banner = @run_status = @option_parser_commands = nil
end

.controlled_run(&block) ⇒ Object

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.


177
178
179
180
181
182
# File 'lib/puppet/application.rb', line 177

def controlled_run(&block)
  return unless clear?
  result = block.call
  Process.kill(:HUP, $PID) if restart_requested?
  result
end

.environment_mode(mode_name) ⇒ Object

Sets environment_mode name

Raises:


297
298
299
300
# File 'lib/puppet/application.rb', line 297

def environment_mode(mode_name)
  raise Puppet::Error, _("Invalid environment mode '%{mode_name}'") % { mode_name: mode_name } unless [:local, :remote, :not_required].include?(mode_name)
  @environment_mode = mode_name
end

.exit(code) ⇒ Object

this is used for testing


495
496
497
# File 'lib/puppet/application.rb', line 495

def self.exit(code)
  exit(code)
end

.find(application_name) ⇒ Class

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

Raises:

  • (Puppet::Error)

    if the application class was not found.

  • (LoadError)

    if there was a problem loading the application file.


234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/puppet/application.rb', line 234

def find(application_name)
  begin
    require @loader.expand(application_name.to_s.downcase)
  rescue LoadError => e
    Puppet.log_and_raise(e, _("Unable to find application '%{application_name}'. %{error}") % { application_name: application_name, error: e })
  end

  class_name = Puppet::Util::ConstantInflector.file2constant(application_name.to_s)

  clazz = try_load_class(class_name)

  ################################################################
  #### Begin 2.7.x backward compatibility hack;
  ####  eventually we need to issue a deprecation warning here,
  ####  and then get rid of this stanza in a subsequent release.
  ################################################################
  if (clazz.nil?)
    class_name = application_name.capitalize
    clazz = try_load_class(class_name)
  end
  ################################################################
  #### End 2.7.x backward compatibility hack
  ################################################################

  if clazz.nil?
    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 })
  end

  return clazz
end

.get_environment_modeObject

Gets environment_mode name. If none is set with `environment_mode=`, default to :local.


304
305
306
# File 'lib/puppet/application.rb', line 304

def get_environment_mode
  @environment_mode || :local
end

.interrupted?Boolean

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


162
163
164
# File 'lib/puppet/application.rb', line 162

def interrupted?
  [:restart_requested, :stop_requested].include? run_status
end

.option(*options, &block) ⇒ Object

used to declare code that handle an option


185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/puppet/application.rb', line 185

def option(*options, &block)
  long = options.find { |opt| opt =~ /^--/ }.gsub(/^--(?:\[no-\])?([^ =]+).*$/, '\1' ).tr('-','_')
  fname = "handle_#{long}".intern
  if (block_given?)
    define_method(fname, &block)
  else
    define_method(fname) do |value|
      self.options["#{long}".to_sym] = value
    end
  end
  self.option_parser_commands << [options, fname]
end

.option_parser_commandsObject


202
203
204
205
206
207
# File 'lib/puppet/application.rb', line 202

def option_parser_commands
  @option_parser_commands ||= (
    superclass.respond_to?(:option_parser_commands) ? superclass.option_parser_commands.dup : []
  )
  @option_parser_commands
end

.restart!Object


144
145
146
# File 'lib/puppet/application.rb', line 144

def restart!
  self.run_status = :restart_requested
end

.restart_requested?Boolean

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


150
151
152
# File 'lib/puppet/application.rb', line 150

def restart_requested?
  :restart_requested == run_status
end

.run_mode(mode_name = nil) ⇒ Object

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


280
281
282
283
284
285
286
287
288
289
# File 'lib/puppet/application.rb', line 280

def run_mode( mode_name = nil)
  if mode_name
    Puppet.settings.preferred_run_mode = mode_name
  end

  return @run_mode if @run_mode and not mode_name

  require 'puppet/util/run_mode'
  @run_mode = Puppet::Util::RunMode[ mode_name || Puppet.settings.preferred_run_mode ]
end

.stop!Object


140
141
142
# File 'lib/puppet/application.rb', line 140

def stop!
  self.run_status = :stop_requested
end

.stop_requested?Boolean

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


156
157
158
# File 'lib/puppet/application.rb', line 156

def stop_requested?
  :stop_requested == run_status
end

.try_load_class(class_name) ⇒ Class

Given the fully qualified name of a class, attempt to get the class instance.


268
269
270
# File 'lib/puppet/application.rb', line 268

def try_load_class(class_name)
    return self.const_defined?(class_name) ? const_get(class_name) : nil
end

Instance Method Details

#app_defaultsObject


330
331
332
333
334
# File 'lib/puppet/application.rb', line 330

def app_defaults()
  Puppet::Settings.app_defaults_for_run_mode(self.class.run_mode).merge(
      :name => name
  )
end

#configure_indirector_routesObject


432
433
434
# File 'lib/puppet/application.rb', line 432

def configure_indirector_routes
  Puppet::ApplicationSupport.configure_indirector_routes(name.to_s)
end

#deprecateObject

call in setup of subclass to deprecate an application


350
351
352
# File 'lib/puppet/application.rb', line 350

def deprecate
  @deprecated = true
end

#deprecated?Boolean


354
355
356
# File 'lib/puppet/application.rb', line 354

def deprecated?
  @deprecated
end

#handle_logdest_arg(arg) ⇒ Object


420
421
422
423
424
425
426
427
428
429
430
# File 'lib/puppet/application.rb', line 420

def handle_logdest_arg(arg)
  return if arg.nil?

  begin
    Puppet[:logdest] = arg
    Puppet::Util::Log.newdestination(arg)
    options[:setdest] = true
  rescue => detail
    Puppet.log_and_raise(detail, _("Could not set logdest to %{dest}.") % { dest: arg })
  end
end

#handlearg(opt, val) ⇒ Object


489
490
491
492
# File 'lib/puppet/application.rb', line 489

def handlearg(opt, val)
  opt, val = Puppet::Settings.clean_opt(opt, val)
  send(:handle_unknown, opt, val) if respond_to?(:handle_unknown)
end

#helpObject


503
504
505
# File 'lib/puppet/application.rb', line 503

def help
  _("No help available for puppet %{app_name}") % { app_name: name }
end

#initialize_app_defaultsObject


336
337
338
# File 'lib/puppet/application.rb', line 336

def initialize_app_defaults()
  Puppet.settings.initialize_app_defaults(app_defaults)
end

#log_runtime_environment(extra_info = nil) ⇒ void

This method returns an undefined value.

Output basic information about the runtime environment for debugging purposes.


444
445
446
447
448
449
450
451
452
453
454
# File 'lib/puppet/application.rb', line 444

def log_runtime_environment(extra_info=nil)
  runtime_info = {
    'puppet_version' => Puppet.version,
    'ruby_version'   => RUBY_VERSION,
    'run_mode'       => self.class.run_mode.name,
  }
  runtime_info['default_encoding'] = Encoding.default_external
  runtime_info.merge!(extra_info) unless extra_info.nil?

  Puppet.debug 'Runtime environment: ' + runtime_info.map{|k,v| k + '=' + v.to_s}.join(', ')
end

#mainObject

Raises:

  • (NotImplementedError)

385
386
387
# File 'lib/puppet/application.rb', line 385

def main
  raise NotImplementedError, _("No valid command or main")
end

#nameObject


499
500
501
# File 'lib/puppet/application.rb', line 499

def name
  self.class.to_s.sub(/.*::/,"").downcase.to_sym
end

#parse_optionsObject


456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
# File 'lib/puppet/application.rb', line 456

def parse_options
  # Create an option parser
  option_parser = OptionParser.new(self.class.banner)

  # Here we're building up all of the options that the application may need to handle.  The main
  # puppet settings defined in "defaults.rb" have already been parsed once (in command_line.rb) by
  # the time we get here; however, our app may wish to handle some of them specially, so we need to
  # make the parser aware of them again.  We might be able to make this a bit more efficient by
  # re-using the parser object that gets built up in command_line.rb.  --cprice 2012-03-16

  # Add all global options to it.
  Puppet.settings.optparse_addargs([]).each do |option|
    option_parser.on(*option) do |arg|
      handlearg(option[0], arg)
    end
  end

  # Add options that are local to this application, which were
  # created using the "option()" metaprogramming method.  If there
  # are any conflicts, this application's options will be favored.
  self.class.option_parser_commands.each do |options, fname|
    option_parser.on(*options) do |value|
      # Call the method that "option()" created.
      self.send(fname, value)
    end
  end

  # Scan command line.  We just hand any exceptions to our upper levels,
  # rather than printing help and exiting, so that we can meaningfully
  # respond with context-sensitive help if we want to. --daniel 2011-04-12
  option_parser.parse!(self.command_line.args)
end

#preinitObject

override to execute code before running anything else


341
342
# File 'lib/puppet/application.rb', line 341

def preinit
end

#runvoid

This method returns an undefined value.

Execute the application.


361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
# File 'lib/puppet/application.rb', line 361

def run

  # I don't really like the names of these lifecycle phases.  It would be nice to change them to some more meaningful
  # names, and make deprecated aliases.  --cprice 2012-03-16

  exit_on_fail(_("Could not get application-specific default settings")) do
    initialize_app_defaults
  end

  Puppet::ApplicationSupport.push_application_context(self.class.run_mode, self.class.get_environment_mode)

  exit_on_fail(_("Could not initialize"))                { preinit }
  exit_on_fail(_("Could not parse application options")) { parse_options }
  exit_on_fail(_("Could not prepare for execution"))     { setup }

  if deprecated?
    Puppet.deprecation_warning(_("`puppet %{name}` is deprecated and will be removed in a future release.") % { name: name })
  end

  exit_on_fail(_("Could not configure routes from %{route_file}") % { route_file: Puppet[:route_file] }) { configure_indirector_routes }
  exit_on_fail(_("Could not log runtime debug info"))                       { log_runtime_environment }
  exit_on_fail(_("Could not run"))                                          { run_command }
end

#run_commandObject


389
390
391
# File 'lib/puppet/application.rb', line 389

def run_command
  main
end

#set_log_level(opts = nil) ⇒ Object


411
412
413
414
415
416
417
418
# File 'lib/puppet/application.rb', line 411

def set_log_level(opts = nil)
  opts ||= options
  if opts[:debug]
    Puppet::Util::Log.level = :debug
  elsif opts[:verbose] && !Puppet::Util::Log.sendlevel?(:info)
    Puppet::Util::Log.level = :info
  end
end

#setupObject


393
394
395
# File 'lib/puppet/application.rb', line 393

def setup
  setup_logs
end

#setup_logsObject


397
398
399
400
401
402
403
404
405
406
407
408
409
# File 'lib/puppet/application.rb', line 397

def setup_logs
  handle_logdest_arg(Puppet[:logdest]) if !options[:setdest]

  unless options[:setdest]
    if options[:debug] || options[:verbose]
      Puppet::Util::Log.newdestination(:console)
    end
  end

  set_log_level

  Puppet::Util::Log.setup_default unless options[:setdest]
end

#summaryObject

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.


510
511
512
# File 'lib/puppet/application.rb', line 510

def summary
  ""
end