require 'optparse' require 'logger' require 'hybrid_platforms_conductor/config' require 'hybrid_platforms_conductor/platforms_handler' require 'hybrid_platforms_conductor/nodes_handler' require 'hybrid_platforms_conductor/actions_executor' require 'hybrid_platforms_conductor/cmd_runner' require 'hybrid_platforms_conductor/deployer' require 'hybrid_platforms_conductor/json_dumper' require 'hybrid_platforms_conductor/reports_handler' require 'hybrid_platforms_conductor/tests_runner' require 'hybrid_platforms_conductor/topographer' require 'hybrid_platforms_conductor/logger_helpers' require 'hybrid_platforms_conductor/current_dir_monitor' module HybridPlatformsConductor # Give a common executable interface to all our executables class Executable include LoggerHelpers # Give the list of selected nodes, if the option was offered. Check NodesHandler#select_nodes to know which kind of nodes description exist. # Array attr_reader :selected_nodes # Constructor # # Parameters:: # * *check_options* (Boolean): Do we offer check/why-run options? [default: true] # * *nodes_selection_options* (Boolean): Do we offer nodes selection options? [default: true] # * *parallel_options* (Boolean): Do we offer parallel options? [default: true] # * *timeout_options* (Boolean): Do we offer timeout options? [default: true] # * *deploy_options* (Boolean): Do we offer deploy options? [default: true] # * *logger* (Logger): The stdout logger to be used [default: Logger.new(STDOUT, level: :info)] # * *logger_stderr* (Logger): The stderr logger to be used [default: Logger.new(STDERR, level: :info)] # * *opts_block* (Proc): Optional code called to register main options # * Parameters:: # * *opts* (OptionsParser): The options parser to complete def initialize( check_options: true, nodes_selection_options: true, parallel_options: true, timeout_options: true, deploy_options: true, logger: Logger.new(STDOUT, level: :info), logger_stderr: Logger.new(STDERR, level: :info), &opts_block ) init_loggers(logger, logger_stderr) @check_options = check_options @nodes_selection_options = nodes_selection_options @parallel_options = parallel_options @timeout_options = timeout_options @deploy_options = deploy_options @opts_block = opts_block # List of nodes description selected @selected_nodes = [] # Possible Conductor components this executable can use @instantiated_components = {} # Initialize the loggers # We set the debug format right now before calling the options parser, just in case some option parsing needs debugging (like plugins discovery) self.log_level = :debug if ARGV.include?('--debug') || ARGV.include?('-d') end # Define all the dependency injection rules between the various APIs. # Singleton accessors for each one of those components will be generated automatically based on these definitions. { config: [], cmd_runner: [], platforms_handler: %i[config cmd_runner], nodes_handler: %i[config cmd_runner platforms_handler], actions_executor: %i[config cmd_runner nodes_handler], services_handler: %i[config cmd_runner platforms_handler nodes_handler actions_executor], deployer: %i[config cmd_runner nodes_handler actions_executor services_handler], json_dumper: %i[config nodes_handler deployer], reports_handler: %i[config platforms_handler nodes_handler], tests_runner: %i[config cmd_runner platforms_handler nodes_handler actions_executor deployer], topographer: %i[nodes_handler json_dumper] }.each do |component, dependencies| # Has a singleton been instantiated for this component? # # Result:: # * Boolean: Has a singleton been instantiated for this component? define_method("#{component}_instantiated?".to_sym) do @instantiated_components.key?(component) end # Get a singleton for this component # # Result:: # * Object: The corresponding component define_method(component) do @instantiated_components[component] = HybridPlatformsConductor.const_get(component.to_s.split('_').collect(&:capitalize).join.to_sym).new( logger: @logger, logger_stderr: @logger_stderr, **Hash[dependencies.map { |dependency| [dependency, send(dependency)] }] ) unless @instantiated_components.key?(component) @instantiated_components[component] end end # Parse options for this executable. # Use options for any Hybrid Platforms Conductor component that has been accessed through the above methods. # Handle common options (like logging and help). def parse_options! OptionParser.new do |opts| opts.banner = "Usage: #{$0} [options]" opts.separator '' opts.separator 'Main options:' opts.on('-d', '--debug', 'Activate debug mode') do self.log_level = :debug end opts.on('-h', '--help', 'Display help and exit') do out opts exit 0 end @opts_block.call(opts) if @opts_block nodes_handler.options_parse(opts) if nodes_handler_instantiated? nodes_handler.options_parse_nodes_selectors(opts, @selected_nodes) if @nodes_selection_options cmd_runner.options_parse(opts) if cmd_runner_instantiated? actions_executor.options_parse(opts, parallel: @parallel_options) if actions_executor_instantiated? deployer.options_parse( opts, parallel_switch: @parallel_options, timeout_options: @timeout_options, why_run_switch: @check_options ) if deployer_instantiated? && @deploy_options json_dumper.options_parse(opts) if json_dumper_instantiated? reports_handler.options_parse(opts) if reports_handler_instantiated? tests_runner.options_parse(opts) if tests_runner_instantiated? topographer.options_parse(opts) if topographer_instantiated? end.parse! actions_executor.validate_params if actions_executor_instantiated? deployer.validate_params if deployer_instantiated? reports_handler.validate_params if reports_handler_instantiated? topographer.validate_params if topographer_instantiated? raise "Unknown options: #{ARGV.join(' ')}" unless ARGV.empty? end end end