Updated mcollective.init according to OSCI-658
[packages/precise/mcollective.git] / lib / mcollective / applications.rb
diff --git a/lib/mcollective/applications.rb b/lib/mcollective/applications.rb
new file mode 100644 (file)
index 0000000..3be93f5
--- /dev/null
@@ -0,0 +1,134 @@
+module MCollective
+  class Applications
+    def self.[](appname)
+      load_application(appname)
+      PluginManager["#{appname}_application"]
+    end
+
+    def self.run(appname)
+      load_config
+
+      begin
+        load_application(appname)
+      rescue Exception => e
+        e.backtrace.first << Util.colorize(:red, "  <----")
+        STDERR.puts "Application '#{appname}' failed to load:"
+        STDERR.puts
+        STDERR.puts Util.colorize(:red, "   #{e} (#{e.class})")
+        STDERR.puts
+        STDERR.puts "       %s" % [e.backtrace.join("\n       ")]
+        exit 1
+      end
+
+      PluginManager["#{appname}_application"].run
+    end
+
+    def self.load_application(appname)
+      return if PluginManager.include?("#{appname}_application")
+
+      load_config
+
+      PluginManager.loadclass "MCollective::Application::#{appname.capitalize}"
+      PluginManager << {:type => "#{appname}_application", :class => "MCollective::Application::#{appname.capitalize}"}
+    end
+
+    # Returns an array of applications found in the lib dirs
+    def self.list
+      load_config
+
+      PluginManager.find("application")
+    rescue SystemExit
+      exit 1
+    rescue Exception => e
+      STDERR.puts "Failed to generate application list: #{e.class}: #{e}"
+      exit 1
+    end
+
+    # Filters a string of opts out using Shellwords
+    # keeping only things related to --config and -c
+    def self.filter_extra_options(opts)
+      res = ""
+      words = Shellwords.shellwords(opts)
+      words.each_with_index do |word,idx|
+        if word == "-c"
+          return "--config=#{words[idx + 1]}"
+        elsif word == "--config"
+          return "--config=#{words[idx + 1]}"
+        elsif word =~ /\-c=/
+          return word
+        elsif word =~ /\-\-config=/
+          return word
+        end
+      end
+
+      return ""
+    end
+
+    # We need to know the config file in order to know the libdir
+    # so that we can find applications.
+    #
+    # The problem is the CLI might be stuffed with options only the
+    # app in the libdir might understand so we have a chicken and
+    # egg situation.
+    #
+    # We're parsing and filtering MCOLLECTIVE_EXTRA_OPTS removing
+    # all but config related options and parsing the options looking
+    # just for the config file.
+    #
+    # We're handling failures gracefully and finally restoring the
+    # ARG and MCOLLECTIVE_EXTRA_OPTS to the state they were before
+    # we started parsing.
+    #
+    # This is mostly a hack, when we're redoing how config works
+    # this stuff should be made less sucky
+    def self.load_config
+      return if Config.instance.configured
+
+      original_argv = ARGV.clone
+      original_extra_opts = ENV["MCOLLECTIVE_EXTRA_OPTS"].clone rescue nil
+      configfile = nil
+
+      parser = OptionParser.new
+      parser.on("--config CONFIG", "-c", "Config file") do |f|
+        configfile = f
+      end
+
+      parser.program_name = $0
+
+      parser.on("--help")
+
+      # avoid option parsers own internal version handling that sux
+      parser.on("-v", "--verbose")
+
+      if original_extra_opts
+        begin
+          # optparse will parse the whole ENV in one go and refuse
+          # to play along with the retry trick I do below so in
+          # order to handle unknown options properly I parse out
+          # only -c and --config deleting everything else and
+          # then restore the environment variable later when I
+          # am done with it
+          ENV["MCOLLECTIVE_EXTRA_OPTS"] = filter_extra_options(ENV["MCOLLECTIVE_EXTRA_OPTS"].clone)
+          parser.environment("MCOLLECTIVE_EXTRA_OPTS")
+        rescue Exception => e
+          Log.error("Failed to parse MCOLLECTIVE_EXTRA_OPTS: #{e}")
+        end
+
+        ENV["MCOLLECTIVE_EXTRA_OPTS"] = original_extra_opts.clone
+      end
+
+      begin
+        parser.parse!
+      rescue OptionParser::InvalidOption => e
+        retry
+      end
+
+      ARGV.clear
+      original_argv.each {|a| ARGV << a}
+
+      configfile = Util.config_file_for_user unless configfile
+
+      Config.instance.loadconfig(configfile)
+    end
+  end
+end