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 functionnality 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 overriden.

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, Ca, Catalog, Cert, Certificate, Certificate_request, Certificate_revocation_list, CommandLineArgs, Config, Describe, Device, Doc, FaceBase, Facts, File, Filebucket, Help, IndirectionBase, Inspect, Instrumentation_data, Instrumentation_listener, Instrumentation_probe, Key, Kick, Man, Master, Module, Node, Parser, Plugin, Queue, Report, Resource, Resource_type, Secret_agent, Status, UploadFacts

Constant Summary collapse

DOCPATTERN =
::File.expand_path(::File.dirname(__FILE__) + "/util/command_line/*" )
SHOULD_PARSE_CONFIG_DEPRECATION_MSG =
"is no longer supported; config file parsing " +
"is now controlled by the puppet engine, rather than by individual applications.  This " +
"method will be removed in a future version of puppet."

Constants included from Util

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

Constants included from Util::POSIX

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

Constants included from Util::SymbolicFileMode

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

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Util

absolute_path?, activerecord_version, benchmark, binread, chuser, classproxy, deterministic_rand, execfail, execpipe, execute, exit_on_fail, logmethods, memory, path_to_uri, pretty_backtrace, proxy, replace_file, safe_posix_fork, symbolizehash, thinmark, uri_to_path, which, withenv, withumask

Methods included from Util::POSIX

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

Methods included from Util::SymbolicFileMode

#normalize_symbolic_mode, #symbolic_mode_to_int, #valid_symbolic_mode?

Constructor Details

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

Returns a new instance of Application.



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

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



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

def run_status
  @run_status
end

Instance Attribute Details

#command_lineObject (readonly)



307
308
309
# File 'lib/puppet/application.rb', line 307

def command_line
  @command_line
end

#optionsObject (readonly)



307
308
309
# File 'lib/puppet/application.rb', line 307

def options
  @options
end

Class Method Details

.[](name) ⇒ Object



283
284
285
# File 'lib/puppet/application.rb', line 283

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

.available_application_namesArray<String>

Returns the names of available applications.

Returns:

  • (Array<String>)

    the names of available applications



228
229
230
231
232
# File 'lib/puppet/application.rb', line 228

def available_application_names
  @loader.files_to_load.map do |fn|
    ::File.basename(fn, '.rb')
  end.uniq
end


215
216
217
# File 'lib/puppet/application.rb', line 215

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

Returns:

  • (Boolean)


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



302
303
304
# File 'lib/puppet/application.rb', line 302

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

.exit(code) ⇒ Object

this is used for testing



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

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

Parameters:

  • application_name (String)

    the name of the application to find (eg. “apply”).

Returns:

  • (Class)

    the Class instance of the application that was found.

Raises:

  • (Puppet::Error)

    if the application class was not found.

  • (LoadError)

    if there was a problem loading the application file.



244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# File 'lib/puppet/application.rb', line 244

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}'. #{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'")
  end

  return clazz
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.

Returns:

  • (Boolean)


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



202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/puppet/application.rb', line 202

def option(*options, &block)
  long = options.find { |opt| opt =~ /^--/ }.gsub(/^--(?:\[no-\])?([^ =]+).*$/, '\1' ).gsub('-','_')
  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



219
220
221
222
223
224
# File 'lib/puppet/application.rb', line 219

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.

Returns:

  • (Boolean)


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



290
291
292
293
294
295
296
297
298
299
# File 'lib/puppet/application.rb', line 290

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

.should_not_parse_configObject



192
193
194
# File 'lib/puppet/application.rb', line 192

def should_not_parse_config
  Puppet.deprecation_warning("should_not_parse_config " + SHOULD_PARSE_CONFIG_DEPRECATION_MSG)
end

.should_parse_configObject



188
189
190
# File 'lib/puppet/application.rb', line 188

def should_parse_config
  Puppet.deprecation_warning("should_parse_config " + SHOULD_PARSE_CONFIG_DEPRECATION_MSG)
end

.should_parse_config?Boolean

Returns:

  • (Boolean)


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

def should_parse_config?
  Puppet.deprecation_warning("should_parse_config? " + SHOULD_PARSE_CONFIG_DEPRECATION_MSG)
  true
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.

Returns:

  • (Boolean)


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.

Parameters:

  • class_name (String)

    the fully qualified name of the class to try to load

Returns:

  • (Class)

    the Class instance, or nil? if it could not be loaded.



278
279
280
# File 'lib/puppet/application.rb', line 278

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

Instance Method Details

#app_defaultsObject



323
324
325
326
327
# File 'lib/puppet/application.rb', line 323

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

#configure_indirector_routesObject



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

def configure_indirector_routes
  route_file = Puppet[:route_file]
  if Puppet::FileSystem.exist?(route_file)
    routes = YAML.load_file(route_file)
    application_routes = routes[name.to_s]
    Puppet::Indirector.configure_routes(application_routes) if application_routes
  end
end

#handle_logdest_arg(arg) ⇒ Object



414
415
416
417
418
419
420
421
# File 'lib/puppet/application.rb', line 414

def handle_logdest_arg(arg)
  begin
    Puppet::Util::Log.newdestination(arg)
    options[:setdest] = true
  rescue => detail
    Puppet.log_exception(detail)
  end
end

#handlearg(opt, val) ⇒ Object



485
486
487
488
# File 'lib/puppet/application.rb', line 485

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

#helpObject



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

def help
  "No help available for puppet #{name}"
end

#initialize_app_defaultsObject



329
330
331
# File 'lib/puppet/application.rb', line 329

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.

Parameters:

  • extra_info (Hash{String => #to_s}) (defaults to: nil)

    a flat hash of extra information to log. Intended to be passed to super by subclasses.



440
441
442
443
444
445
446
447
448
449
450
# File 'lib/puppet/application.rb', line 440

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 if RUBY_VERSION >= '1.9.3'
  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)


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

def main
  raise NotImplementedError, "No valid command or main"
end

#nameObject



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

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

#parse_optionsObject



452
453
454
455
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
# File 'lib/puppet/application.rb', line 452

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



334
335
# File 'lib/puppet/application.rb', line 334

def preinit
end

#runvoid

This method returns an undefined value.

Execute the application.



345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
# File 'lib/puppet/application.rb', line 345

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.  Also, Daniel suggests that we can probably get rid of this "plugin_hook"
  # pattern, but we need to check with PE and the community first.  --cprice 2012-03-16
  #

  exit_on_fail("get application-specific default settings") do
    plugin_hook('initialize_app_defaults') { initialize_app_defaults }
  end

  Puppet.push_context(Puppet.base_context(Puppet.settings), "Update for application settings (#{self.class.run_mode})")
  # This use of configured environment is correct, this is used to establish
  # the defaults for an application that does not override, or where an override
  # has not been made from the command line.
  #
  configured_environment_name = Puppet[:environment]
  if self.class.run_mode.name == :agent
    configured_environment = Puppet::Node::Environment.remote(configured_environment_name)
  else
    configured_environment = Puppet.lookup(:environments).get!(configured_environment_name)
  end
  configured_environment = configured_environment.override_from_commandline(Puppet.settings)

  # Setup a new context using the app's configuration
  Puppet.push_context({ :current_environment => configured_environment },
                  "Update current environment from application's configuration")

  require 'puppet/util/instrumentation'
  Puppet::Util::Instrumentation.init

  exit_on_fail("initialize")                                   { plugin_hook('preinit')       { preinit } }
  exit_on_fail("parse application options")                    { plugin_hook('parse_options') { parse_options } }
  exit_on_fail("prepare for execution")                        { plugin_hook('setup')         { setup } }
  exit_on_fail("configure routes from #{Puppet[:route_file]}") { configure_indirector_routes }
  exit_on_fail("log runtime debug info")                       { log_runtime_environment }
  exit_on_fail("run")                                          { plugin_hook('run_command')   { run_command } }
end

#run_commandObject



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

def run_command
  main
end

#set_log_levelObject



406
407
408
409
410
411
412
# File 'lib/puppet/application.rb', line 406

def set_log_level
  if options[:debug]
    Puppet::Util::Log.level = :debug
  elsif options[:verbose]
    Puppet::Util::Log.level = :info
  end
end

#setupObject



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

def setup
  setup_logs
end

#setup_logsObject



396
397
398
399
400
401
402
403
404
# File 'lib/puppet/application.rb', line 396

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

  set_log_level

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