diff options
author | danielsdeleo <dan@opscode.com> | 2014-01-02 11:30:43 -0800 |
---|---|---|
committer | danielsdeleo <dan@opscode.com> | 2014-01-02 13:05:46 -0800 |
commit | 5911833efb4feabe1f7bfc126fdcd36f15a3b1e9 (patch) | |
tree | 8b08257ac745f740c5878cd75d53ce96bed84642 | |
parent | ab6f04ecfe8d19efcd6f44039cf3d1e1c8edfd18 (diff) | |
download | ohai-5911833efb4feabe1f7bfc126fdcd36f15a3b1e9.tar.gz |
Evaluate all plugin files before instatiating plugin objects
The previous logic instantiated plugins as the files were read. This
caused multiple instances of plugins to be created when a plugin is
"reopened" in another file.
Additionally, loading-specific logic is moved from System to Loader.
-rw-r--r-- | lib/ohai/loader.rb | 172 | ||||
-rw-r--r-- | lib/ohai/system.rb | 20 |
2 files changed, 137 insertions, 55 deletions
diff --git a/lib/ohai/loader.rb b/lib/ohai/loader.rb index 410a58e1..b02b65ed 100644 --- a/lib/ohai/loader.rb +++ b/lib/ohai/loader.rb @@ -19,39 +19,101 @@ require 'ohai/log' require 'ohai/mash' require 'ohai/dsl' +require 'pathname' module Ohai + + # Ohai plugin loader. Finds all the plugins in your + # `Ohai::Config[:plugin_path]` (supports a single or multiple path setting + # here), evaluates them and returns plugin objects. class Loader + # Simple struct like objects to track the path of a plugin and the root + # directory of plugins in which we found it. We don't care about the + # relative paths of v7 plugins, but in v6 plugins, dependencies are + # specified by calling `require_plugin` with a relative path. To manage + # this, we track the path and root of each file as we discover them so we + # can feed this into the v6 "dependency solver" as we load them. + class PluginFile < Struct.new(:path, :plugin_root) + + # Finds all the *.rb files under the configured paths in :plugin_path + def self.find_all_in(plugin_dir) + Dir[File.join(plugin_dir, "**", "*.rb")].map do |file| + new(file, plugin_dir) + end + end + end + + # Simple struct to track a v6 plugin's class, file path, and the root of + # the plugin dir from which it was loaded. + V6PluginClass = Struct.new(:plugin_class, :plugin_path, :plugin_dir_path) + def initialize(controller) @controller = controller + @v6_plugin_classes = [] + @v7_plugin_classes = [] + end + + # Searches all plugin paths and returns an Array of PluginFile objects + # representing each plugin file. + def plugin_files_by_dir + Array(Ohai::Config[:plugin_path]).inject([]) do |plugin_files, plugin_path| + plugin_files + PluginFile.find_all_in(plugin_path) + end end + def load_all + plugin_files_by_dir.each do |plugin_file| + load_plugin_class(plugin_file.path, plugin_file.plugin_root) + end + + collect_v6_plugins + collect_v7_plugins + end + + # Load a specified file as an ohai plugin and creates an instance of it. + # Not used by ohai itself, but can be used to load a plugin for testing + # purposes. def load_plugin(plugin_path) - plugin = nil + plugin_class = load_plugin_class(plugin_path) + return nil unless plugin_class.kind_of?(Class) + case + when plugin_class < Ohai::DSL::Plugin::VersionVI + load_v6_plugin(plugin_class, plugin_path) + when plugin_class < Ohai::DSL::Plugin::VersionVII + load_v7_plugin(plugin_class) + else + raise Exceptions::IllegalPluginDefinition, "cannot create plugin of type #{plugin_class}" + end + end + # Reads the file specified by `plugin_path` and returns a class object for + # the ohai plugin defined therein. + # + # If `plugin_dir_path` is given, and the file at `plugin_path` is a v6 + # plugin, the 'relative path' of the plugin (used by `require_plugin()`) is + # computed by finding the relative path from `plugin_dir_path` to `plugin_path` + def load_plugin_class(plugin_path, plugin_dir_path=nil) # Read the contents of the plugin to understand if it's a V6 or V7 plugin. contents = "" begin contents << IO.read(plugin_path) rescue IOError, Errno::ENOENT Ohai::Log.warn("Unable to open or read plugin at #{plugin_path}") - return plugin + return nil end # We assume that a plugin is a V7 plugin if it contains Ohai.plugin in its contents. if contents.include?("Ohai.plugin") - plugin = load_v7_plugin(contents, plugin_path) + load_v7_plugin_class(contents, plugin_path) else Ohai::Log.warn("[DEPRECATION] Plugin at #{plugin_path} is a version 6 plugin. \ Version 6 plugins will not be supported in future releases of Ohai. \ Please upgrade your plugin to version 7 plugin syntax. \ For more information visit here: docs.opscode.com/ohai_custom.html") - plugin = load_v6_plugin(contents, plugin_path) + load_v6_plugin_class(contents, plugin_path, plugin_dir_path) end - - plugin end private @@ -61,43 +123,81 @@ For more information visit here: docs.opscode.com/ohai_custom.html") @controller.provides_map.set_providers_for(plugin, plugin_provides) end - def load_v6_plugin(contents, plugin_path) - klass = Class.new(Ohai::DSL::Plugin::VersionVI) { collect_contents(contents) } - klass.new(@controller, plugin_path) + def v6_dependency_solver + @controller.v6_dependency_solver end - def load_v7_plugin(contents, plugin_path) - plugin = nil + def collect_v6_plugins + @v6_plugin_classes.each do |plugin_spec| + plugin = load_v6_plugin(plugin_spec.plugin_class, plugin_spec.plugin_path) + loaded_v6_plugin(plugin, plugin_spec.plugin_path, plugin_spec.plugin_dir_path) + end + end - begin - klass = eval(contents, TOPLEVEL_BINDING) - plugin = klass.new(@controller.data) unless klass.nil? - rescue SystemExit, Interrupt - raise - rescue Ohai::Exceptions::InvalidPluginName => e - Ohai::Log.warn("Plugin Name Error: <#{plugin_path}>: #{e.message}") - rescue Ohai::Exceptions::IllegalPluginDefinition => e - Ohai::Log.warn("Plugin Definition Error: <#{plugin_path}>: #{e.message}") - rescue NoMethodError => e - Ohai::Log.warn("Plugin Method Error: <#{plugin_path}>: unsupported operation \'#{e.name}\'") - rescue SyntaxError => e - # split on occurrences of - # <env>: syntax error, - # <env>:##: syntax error, - # to remove from error message - parts = e.message.split(/<.*>[:[0-9]+]*: syntax error, /) - parts.each do |part| - next if part.length == 0 - Ohai::Log.warn("Plugin Syntax Error: <#{plugin_path}>: #{part}") - end - rescue Exception, Errno::ENOENT => e - Ohai::Log.warn("Plugin Error: <#{plugin_path}>: #{e.message}") - Ohai::Log.debug("Plugin Error: <#{plugin_path}>: #{e.inspect}, #{e.backtrace.join('\n')}") + def collect_v7_plugins + @v7_plugin_classes.each do |plugin_class| + load_v7_plugin(plugin_class) + end + end + + def load_v6_plugin_class(contents, plugin_path, plugin_dir_path) + plugin_class = Class.new(Ohai::DSL::Plugin::VersionVI) { collect_contents(contents) } + @v6_plugin_classes << V6PluginClass.new(plugin_class, plugin_path, plugin_dir_path) + plugin_class + end + + def load_v6_plugin(plugin_class, plugin_path) + plugin_class.new(@controller, plugin_path) + end + + # Capture the plugin in @v6_dependency_solver if it is a V6 plugin + # to be able to resolve V6 dependencies later on. + # We are using the partial path in the dep solver as a key. + def loaded_v6_plugin(plugin, plugin_file_path, plugin_dir_path) + partial_path = Pathname.new(plugin_file_path).relative_path_from(Pathname.new(plugin_dir_path)).to_s + + unless v6_dependency_solver.has_key?(partial_path) + v6_dependency_solver[partial_path] = plugin + else + Ohai::Log.debug("Plugin '#{plugin_file_path}' is already loaded.") end + end - collect_provides(plugin) unless plugin.nil? + def load_v7_plugin_class(contents, plugin_path) + plugin_class = eval(contents, TOPLEVEL_BINDING) + unless plugin_class.kind_of?(Class) and plugin_class < Ohai::DSL::Plugin + raise Ohai::Exceptions::IllegalPluginDefinition, "Plugin file cannot contain any statements after the plugin definition" + end + @v7_plugin_classes << plugin_class unless @v7_plugin_classes.include?(plugin_class) + plugin_class + rescue SystemExit, Interrupt + raise + rescue Ohai::Exceptions::InvalidPluginName => e + Ohai::Log.warn("Plugin Name Error: <#{plugin_path}>: #{e.message}") + rescue Ohai::Exceptions::IllegalPluginDefinition => e + Ohai::Log.warn("Plugin Definition Error: <#{plugin_path}>: #{e.message}") + rescue NoMethodError => e + Ohai::Log.warn("Plugin Method Error: <#{plugin_path}>: unsupported operation \'#{e.name}\'") + rescue SyntaxError => e + # split on occurrences of + # <env>: syntax error, + # <env>:##: syntax error, + # to remove from error message + parts = e.message.split(/<.*>[:[0-9]+]*: syntax error, /) + parts.each do |part| + next if part.length == 0 + Ohai::Log.warn("Plugin Syntax Error: <#{plugin_path}>: #{part}") + end + rescue Exception, Errno::ENOENT => e + Ohai::Log.warn("Plugin Error: <#{plugin_path}>: #{e.message}") + Ohai::Log.debug("Plugin Error: <#{plugin_path}>: #{e.inspect}, #{e.backtrace.join('\n')}") + end + def load_v7_plugin(plugin_class) + plugin = plugin_class.new(@controller.data) + collect_provides(plugin) plugin end + end end diff --git a/lib/ohai/system.rb b/lib/ohai/system.rb index 8e3eca25..609eb888 100644 --- a/lib/ohai/system.rb +++ b/lib/ohai/system.rb @@ -61,25 +61,7 @@ module Ohai end def load_plugins - Ohai::Config[:plugin_path].each do |path| - Dir[File.join(path, '**', '*.rb')].each do |plugin_file_path| - # Load all the *.rb files under the configured paths in :plugin_path - plugin = @loader.load_plugin(plugin_file_path) - - if plugin && plugin.version == :version6 - # Capture the plugin in @v6_dependency_solver if it is a V6 plugin - # to be able to resolve V6 dependencies later on. - # We are using the partial path in the dep solver as a key. - partial_path = Pathname.new(plugin_file_path).relative_path_from(Pathname.new(path)).to_s - - unless @v6_dependency_solver.has_key?(partial_path) - @v6_dependency_solver[partial_path] = plugin - else - Ohai::Log.debug("Plugin '#{plugin_file_path}' is already loaded.") - end - end - end - end + @loader.load_all end def run_plugins(safe = false, force = false, attribute_filter = nil) |