Module: MCollective::PluginManager

Defined in:
lib/mcollective/pluginmanager.rb

Overview

A simple plugin manager, it stores one plugin each of a specific type the idea is that we can only have one security provider, one connector etc.

Class Method Summary collapse

Class Method Details

.<<(plugin) ⇒ Object

Adds a plugin to the list of plugins, we expect a hash like:

{:type => "base",
 :class => foo.new}

or like:

{:type => "base",
 :class => "Foo::Bar"}

In the event that we already have a class with the given type an exception will be raised.

If the :class passed is a String then we will delay instantiation till the first time someone asks for the plugin, this is because most likely the registration gets done by inherited() hooks, at which point the plugin class is not final.

If we were to do a .new here the Class initialize method would get called and not the plugins, we there for only initialize the classes when they get requested via []

By default all plugin instances are cached and returned later so there’s always a single instance. You can pass :single_instance => false when calling this to instruct it to always return a new instance when a copy is requested. This only works with sending a String for :class.



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/mcollective/pluginmanager.rb', line 30

def self.<<(plugin)
  plugin[:single_instance] = true unless plugin.include?(:single_instance)

  type = plugin[:type]
  klass = plugin[:class]
  single = plugin[:single_instance]

  raise("Plugin #{type} already loaded") if @plugins.include?(type)

  # If we get a string then store 'nil' as the instance, signalling that we'll
  # create the class later on demand.
  if klass.is_a?(String)
    @plugins[type] = {:loadtime => Time.now, :class => klass, :instance => nil, :single => single}
    Log.debug("Registering plugin #{type} with class #{klass} single_instance: #{single}")
  else
    @plugins[type] = {:loadtime => Time.now, :class => klass.class, :instance => klass, :single => true}
    Log.debug("Registering plugin #{type} with class #{klass.class} single_instance: true")
  end
end

.[](plugin) ⇒ Object

Gets a plugin by type



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/mcollective/pluginmanager.rb', line 71

def self.[](plugin)
  raise("No plugin #{plugin} defined") unless @plugins.include?(plugin)

  klass = @plugins[plugin][:class]

  if @plugins[plugin][:single]
    # Create an instance of the class if one hasn't been done before
    if @plugins[plugin][:instance].nil?
      Log.debug("Returning new plugin #{plugin} with class #{klass}")
      @plugins[plugin][:instance] = create_instance(klass)
    else
      Log.debug("Returning cached plugin #{plugin} with class #{klass}")
    end

    @plugins[plugin][:instance]
  else
    Log.debug("Returning new plugin #{plugin} with class #{klass}")
    create_instance(klass)
  end
end

.clearObject

deletes all registered plugins



66
67
68
# File 'lib/mcollective/pluginmanager.rb', line 66

def self.clear
  @plugins.clear
end

.create_instance(klass) ⇒ Object

use eval to create an instance of a class



93
94
95
96
97
# File 'lib/mcollective/pluginmanager.rb', line 93

def self.create_instance(klass)
  eval("#{klass}.new") # rubocop:disable Security/Eval, Style/EvalWithLocation
rescue Exception => e # rubocop:disable Lint/RescueException
  raise("Could not create instance of plugin #{klass}: #{e}")
end

.delete(plugin) ⇒ Object

Removes a plugim the list



51
52
53
# File 'lib/mcollective/pluginmanager.rb', line 51

def self.delete(plugin)
  @plugins.delete(plugin) if @plugins.include?(plugin)
end

.find(type, extension = "rb") ⇒ Object

Finds plugins in all configured libdirs

find("agent")

will return an array of just agent names, for example:

["puppetd", "package"]

Can also be used to find files of other extensions:

find("agent", "ddl")

Will return the same list but only of files with extension .ddl in the agent subdirectory



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/mcollective/pluginmanager.rb', line 113

def self.find(type, extension="rb")
  extension = ".#{extension}" unless extension =~ /^\./

  plugins = []

  Config.instance.libdir.each do |libdir|
    plugdir = File.join([libdir, "mcollective", type.to_s])
    next unless File.directory?(plugdir)

    Dir.new(plugdir).grep(/#{extension}$/).map do |plugin|
      plugins << File.basename(plugin, extension)
    end
  end

  plugins.sort.uniq
end

.find_and_load(type, extension = "rb") ⇒ Object

Finds and loads from disk all plugins from all libdirs that match certain criteria.

find_and_load("pluginpackager")

Will find all .rb files in the libdir/mcollective/pluginpackager/ directory in all libdirs and load them from disk.

You can influence what plugins get loaded using a block notation:

find_and_load("pluginpackager") do |plugin|
   plugin.match(/puppet/)
end

This will load only plugins matching /puppet/



145
146
147
148
149
150
151
152
153
154
155
# File 'lib/mcollective/pluginmanager.rb', line 145

def self.find_and_load(type, extension="rb")
  extension = ".#{extension}" unless extension =~ /^\./

  klasses = find(type, extension).map do |plugin|
    next if block_given? && !yield(plugin)

    "%s::%s::%s" % ["MCollective", type.capitalize, plugin.capitalize]
  end.compact

  klasses.sort.uniq.each {|klass| loadclass(klass, true)}
end

.grep(regex) ⇒ Object

Grep’s over the plugin list and returns the list found



171
172
173
# File 'lib/mcollective/pluginmanager.rb', line 171

def self.grep(regex)
  @plugins.keys.grep(regex)
end

.include?(plugin) ⇒ Boolean

Finds out if we have a plugin with the given name

Returns:

  • (Boolean)


56
57
58
# File 'lib/mcollective/pluginmanager.rb', line 56

def self.include?(plugin)
  @plugins.include?(plugin)
end

.loadclass(klass, squash_failures = false) ⇒ Object

Loads a class from file by doing some simple search/replace on class names and then doing a require.



159
160
161
162
163
164
165
166
167
168
# File 'lib/mcollective/pluginmanager.rb', line 159

def self.loadclass(klass, squash_failures=false)
  fname = "#{klass.gsub('::', '/').downcase}.rb"

  Log.debug("Loading #{klass} from #{fname}")

  load fname
rescue Exception => e # rubocop:disable Lint/RescueException
  Log.error("Failed to load #{klass}: #{e}")
  raise unless squash_failures
end

.pluginlistObject

Provides a list of plugins we know about



61
62
63
# File 'lib/mcollective/pluginmanager.rb', line 61

def self.pluginlist
  @plugins.keys.sort
end