diff options
author | Markus Roberts <Markus@reality.com> | 2011-03-25 12:00:39 -0700 |
---|---|---|
committer | Markus Roberts <Markus@reality.com> | 2011-04-06 20:50:36 -0700 |
commit | 99d78f2c15b6ccf543466d3fa2cdeda7eb3f9987 (patch) | |
tree | e7b3aae19a9d94715694512fe0d50c5c45c4fdbd /lib/puppet/util | |
parent | 9d17809e5b240aa80d2c30f4d9625d4812802b8f (diff) | |
download | puppet-99d78f2c15b6ccf543466d3fa2cdeda7eb3f9987.tar.gz puppet-99d78f2c15b6ccf543466d3fa2cdeda7eb3f9987.tar.xz puppet-99d78f2c15b6ccf543466d3fa2cdeda7eb3f9987.zip |
(#6490) Add plugin initialization callback system to core
The recurring pattern "drop-in code needs to have something done at startup"
is presently being solved with a variety of ad-hock mechanism. This commit
adds a general, extensible, centralized system for adding such hooks and
manages an extensible set of metadata about plugins which it collects by
searching for files named "plugin_init.rb" in a series of directories.
Initially, these are simply the $LOAD_PATH. Applications can add more places
to look for plugins without risk of adding duplicates or changing the order of
ones that have already been found with:
Puppet::Plugins.look_in(*paths)
The contents of each file found is executed in the context of a Puppet::Plugins
object (and thus scoped). An example file might contain:
-------------------------------------------------------
@name = "Greet the CA"
@description = %q{
This plugin causes a friendly greeting to print out on a master
that is operating as the CA, after it has been set up but before
it does anything.
}
def after_application_setup(options)
if options[:application_object].is_a?(Puppet::Application::Master) && Puppet::SSL::CertificateAuthority.ca?
puts "Hey, this is the CA! Hi everyone!!!"
end
end
-------------------------------------------------------
Note that the instance variables are local to this Puppet::Plugin (and so may be
used for maintaining state, etc.) but the plugin system does not provide any
thread safety assurances, so they may not be adequate for some complex use cases.
Presently supported hooks:
before_application_preinit( :application_object => ... )
after_application_preinit( :application_object => ... )
before_application_parse_options( :application_object => ... )
after_application_parse_options( :application_object => ... )
before_application_setup( :application_object => ... )
after_application_setup( :application_object => ... )
before_application_run_command( :application_object => ... )
after_application_run_command( :application_object => ... )
on_commandline_initialization(:command_line_object => ... )
on_application_initialization(:appliation_object => ... )
Paired-with: Daniel Pitman
Diffstat (limited to 'lib/puppet/util')
-rw-r--r-- | lib/puppet/util/command_line.rb | 7 | ||||
-rw-r--r-- | lib/puppet/util/plugins.rb | 82 |
2 files changed, 88 insertions, 1 deletions
diff --git a/lib/puppet/util/command_line.rb b/lib/puppet/util/command_line.rb index 7f74d266a..fb56b2898 100644 --- a/lib/puppet/util/command_line.rb +++ b/lib/puppet/util/command_line.rb @@ -1,3 +1,5 @@ +require "puppet/util/plugins" + module Puppet module Util class CommandLine @@ -23,6 +25,7 @@ module Puppet @stdin = stdin @subcommand_name, @args = subcommand_and_args( @zero, @argv, @stdin ) + Puppet::Plugins.on_commandline_initialization(:command_line_object => self) end attr :subcommand_name @@ -56,7 +59,9 @@ module Puppet puts usage_message elsif available_subcommands.include?(subcommand_name) #subcommand require_application subcommand_name - Puppet::Application.find(subcommand_name).new(self).run + app = Puppet::Application.find(subcommand_name).new(self) + Puppet::Plugins.on_application_initialization(:appliation_object => self) + app.run else abort "Error: Unknown command #{subcommand_name}.\n#{usage_message}" unless execute_external_subcommand end diff --git a/lib/puppet/util/plugins.rb b/lib/puppet/util/plugins.rb new file mode 100644 index 000000000..105fdcd75 --- /dev/null +++ b/lib/puppet/util/plugins.rb @@ -0,0 +1,82 @@ +# +# This system manages an extensible set of metadata about plugins which it +# collects by searching for files named "plugin_init.rb" in a series of +# directories. Initially, these are simply the $LOAD_PATH. +# +# The contents of each file found is executed in the context of a Puppet::Plugins +# object (and thus scoped). An example file might contain: +# +# ------------------------------------------------------- +# @name = "Greet the CA" +# +# @description = %q{ +# This plugin causes a friendly greeting to print out on a master +# that is operating as the CA, after it has been set up but before +# it does anything. +# } +# +# def after_application_setup(options) +# if options[:application_object].is_a?(Puppet::Application::Master) && Puppet::SSL::CertificateAuthority.ca? +# puts "Hey, this is the CA!" +# end +# end +# ------------------------------------------------------- +# +# Note that the instance variables are local to this Puppet::Plugin (and so may be used +# for maintaining state, etc.) but the plugin system does not provide any thread safety +# assurances, so they may not be adequate for some complex use cases. +# +# +module Puppet + class Plugins + Paths = [] # Where we might find plugin initialization code + Loaded = [] # Code we have found (one-to-one with paths once searched) + # + # Return all the Puppet::Plugins we know about, searching any new paths + # + def self.known + Paths[Loaded.length...Paths.length].each { |path| + file = File.join(path,'plugin_init.rb') + Loaded << (File.exist?(file) && new(file)) + } + Loaded.compact + end + # + # Add more places to look for plugins without adding duplicates or changing the + # order of ones we've already found. + # + def self.look_in(*paths) + Paths.replace Paths | paths.flatten.collect { |path| File.expand_path(path) } + end + # + # Initially just look in $LOAD_PATH + # + look_in $LOAD_PATH + # + # Calling methods (hooks) on the class calls the method of the same name on + # all plugins that use that hook, passing in the same arguments to each + # and returning an array containing the results returned by each plugin as + # an array of [plugin_name,result] pairs. + # + def self.method_missing(hook,*args,&block) + known. + select { |p| p.respond_to? hook }. + collect { |p| [p.name,p.send(hook,*args,&block)] } + end + # + # + # + attr_reader :path,:name + def initialize(path) + @name = @path = path + class << self + private + def define_hooks + eval File.read(path),nil,path,1 + end + end + define_hooks + end + end +end + |