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 # rubocop:disable Lint/RescueException e.backtrace.first << Util.colorize(:red, " <----") warn "Application '%s' failed to load:" % appname $stderr.puts warn Util.colorize(:red, " %s (%s)" % [$!, $!.class]) $stderr.puts warn " %s" % [$!.backtrace.join("\n ")] exit 1 end PluginManager["%s_application" % appname].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 # rubocop:disable Lint/RescueException warn("Failed to generate application list: %s: %s" % [$!.class, $!]) 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) words = Shellwords.shellwords(opts) words.each_with_index do |word, idx| case word when "-c" return "--config=#{words[idx + 1]}" when "--config" return "--config=#{words[idx + 1]}" when /-c=/ return word when /--config=/ return word end end "" 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") parser.on("--version") 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 # rubocop:disable Lint/RescueException Log.error("Failed to parse MCOLLECTIVE_EXTRA_OPTS: %s" % $!) end ENV["MCOLLECTIVE_EXTRA_OPTS"] = original_extra_opts.clone end begin parser.parse! rescue OptionParser::InvalidOption retry end ARGV.clear original_argv.each {|a| ARGV << a} configfile ||= Util.config_file_for_user Config.instance.loadconfig(configfile) end end end