require "thor" require "fileutils" require "beaker/subcommands/subcommand_util" module Beaker class Subcommand < Thor SubcommandUtil = Beaker::Subcommands::SubcommandUtil attr_reader :cli def initialize(*args) super FileUtils.mkdir_p(SubcommandUtil::CONFIG_DIR) FileUtils.touch(SubcommandUtil::SUBCOMMAND_OPTIONS) unless SubcommandUtil::SUBCOMMAND_OPTIONS.exist? FileUtils.touch(SubcommandUtil::SUBCOMMAND_STATE) unless SubcommandUtil::SUBCOMMAND_STATE.exist? @cli = Beaker::CLI.new end # Options listed in this group 'Beaker run' are options that can be set on subcommands # but are not processed by the subcommand itself. They are passed through so that when # a Beaker::CLI object executes, it can pick up these options. Notably excluded from this # group are `help` and `version`. Please note that whenever the command_line_parser.rb is # updated, this list should also be updated as well. class_option :'options-file', :aliases => '-o', :type => :string, :group => 'Beaker run' class_option :helper, :type => :string, :group => 'Beaker run' class_option :'load-path', :type => :string, :group => 'Beaker run' class_option :tests, :aliases => '-t', :type => :string, :group => 'Beaker run' class_option :'pre-suite', :type => :string, :group => 'Beaker run' class_option :'post-suite', :type => :string, :group => 'Beaker run' class_option :'pre-cleanup', :type => :string, :group => 'Beaker run' class_option :provision, :type => :boolean, :group => 'Beaker run' class_option :'preserve-hosts', :type => :string, :group => 'Beaker run' class_option :'preserve-state', :type => :boolean, :group => 'Beaker run' class_option :'root-keys', :type => :boolean, :group => 'Beaker run' class_option :keyfile, :type => :string, :group => 'Beaker run' class_option :timeout, :type => :string, :group => 'Beaker run' class_option :install, :aliases => '-i', :type => :string, :group => 'Beaker run' class_option :modules, :aliases => '-m', :type => :string, :group => 'Beaker run' class_option :quiet, :aliases => '-q', :type => :boolean, :group => 'Beaker run' class_option :color, :type => :boolean, :group => 'Beaker run' class_option :'color-host-output', :type => :boolean, :group => 'Beaker run' class_option :'log-level', :type => :string, :group => 'Beaker run' class_option :'log-prefix', :type => :string, :group => 'Beaker run' class_option :'dry-run', :type => :boolean, :group => 'Beaker run' class_option :'fail-mode', :type => :string, :group => 'Beaker run' class_option :'test-results-file', :type => :string, :group => 'Beaker run' class_option :ntp, :type => :boolean, :group => 'Beaker run' class_option :'repo-proxy', :type => :boolean, :group => 'Beaker run' class_option :'add-el-extras', :type => :boolean, :group => 'Beaker run' class_option :'package-proxy', :type => :string, :group => 'Beaker run' class_option :validate, :type => :boolean, :group => 'Beaker run' class_option :'collect-perf-data', :type => :boolean, :group => 'Beaker run' class_option :'parse-only', :type => :boolean, :group => 'Beaker run' class_option :tag, :type => :string, :group => 'Beaker run' class_option :'exclude-tags', :type => :string, :group => 'Beaker run' class_option :'xml-time-order', :type => :boolean, :group => 'Beaker run' class_option :'debug-errors', :type => :boolean, :group => 'Beaker run' class_option :exec_manual_tests, :type => :boolean, :group => 'Beaker run' class_option :'test-tag-exclude', :type => :string, :group => 'Beaker run' class_option :'test-tag-and', :type => :string, :group => 'Beaker run' class_option :'test-tag-or', :type => :string, :group => 'Beaker run' # The following are listed as deprecated in beaker --help, but needed now for # feature parity for beaker 3.x. class_option :xml, :type => :boolean, :group => "Beaker run" class_option :type, :type => :string, :group => "Beaker run" class_option :debug, :type => :boolean, :group => "Beaker run" desc "init BEAKER_RUN_OPTIONS", "Initializes the required configuration for Beaker subcommand execution" long_desc <<-LONGDESC Initializes the required .beaker configuration folder. This folder contains a subcommand_options.yaml file that is user-facing; altering this file will alter the options subcommand execution. Subsequent subcommand execution, such as `provision`, will result in beaker making modifications to this file as necessary. LONGDESC option :help, :type => :boolean, :hide => true method_option :hosts, :aliases => '-h', :type => :string, :required => true def init() if options[:help] invoke :help, [], ["init"] return end @cli.parse_options options_to_write = SubcommandUtil.sanitize_options_for_save(@cli.configured_options) @cli.logger.notify 'Writing configured options to disk' File.open(SubcommandUtil::SUBCOMMAND_OPTIONS, 'w') do |f| f.write(options_to_write.to_yaml) end @cli.logger.notify "Options written to #{SubcommandUtil::SUBCOMMAND_OPTIONS}" state = YAML::Store.new(SubcommandUtil::SUBCOMMAND_STATE) state.transaction do state['provisioned'] = false end end desc "provision", "Provisions the beaker systems under test(SUTs)" long_desc <<-LONGDESC Provisions hosts defined in your subcommand_options file. You can pass the --hosts flag here to override any hosts provided there. Really, you can pass most any beaker flag here to override. LONGDESC option :help, :type => :boolean, :hide => true def provision() if options[:help] invoke :help, [], ["provision"] return end state = YAML::Store.new(SubcommandUtil::SUBCOMMAND_STATE) if state.transaction { state['provisioned']} SubcommandUtil.error_with('Provisioned SUTs detected. Please destroy and reprovision.') end @cli.parse_options @cli.provision # Sanitize the hosts cleaned_hosts = SubcommandUtil.sanitize_options_for_save(@cli.combined_instance_and_options_hosts) # Update each host provisioned with a flag indicating that it no longer needs # provisioning cleaned_hosts.each do |_host, host_hash| host_hash['provision'] = false end # should we only update the options here with the new host? Or update the settings # with whatever new flags may have been provided with provision? options_storage = YAML::Store.new(SubcommandUtil::SUBCOMMAND_OPTIONS) options_storage.transaction do @cli.logger.notify 'updating HOSTS key in subcommand_options' options_storage['HOSTS'] = cleaned_hosts options_storage['hosts_preserved_yaml_file'] = @cli.options[:hosts_preserved_yaml_file] end @cli.preserve_hosts_file state.transaction do state['provisioned'] = true end end desc 'exec FILE(S)/BEAKER_SUITE', 'execute directories, files, or beaker suites' long_desc <<-LONG_DESC Run either files, directories, or beaker suites. If supplied a file or directory, that resource will be run in the context of the `tests` suite; If supplied a beaker suite, then just that suite will run. If no resource is supplied, then this command executes the suites as they are defined in the configuration. Accepts a comma -separated, homogeneous list. E.g. only files, only directories, or only suites, such as: exec pre-suite,tests LONG_DESC option :help, :type => :boolean, :hide => true def exec(resource=nil) if options[:help] invoke :help, [], ["exec"] return end @cli.parse_options @cli.initialize_network_manager if !resource @cli.execute! return end beaker_suites = [:pre_suite, :tests, :post_suite, :pre_cleanup] resources = resource.split(',') paths = resources.map { |r| Pathname(r) } if paths.all?(&:exist?) # If we determine the resource is a valid file resource, then we empty # all the suites and run that file resource in the tests suite. In the # future, when we have the ability to have custom suites, we should change # this to run in a custom suite. You know, in the future. beaker_suites.each do |suite| @cli.options[suite] = [] end @cli.options[:tests] = paths.map do |path| if path.directory? Dir.glob("#{path}/**/*.rb") else path.to_s end end.flatten elsif resources.all? { |r| /^(pre-suite|tests|post-suite|pre-cleanup)$/.match?(r) } # The regex match here is loose so that users can supply multiple suites, # such as `beaker exec pre-suite,tests`. beaker_suites.each do |suite| @cli.options[suite] = [] unless resource.tr('-', '_').match(suite.to_s) end else raise ArgumentError, "Unable to parse #{resource} with beaker exec" end @cli.execute! if options['preserve-state'] @cli.logger.notify 'updating HOSTS key in subcommand_options' hosts = SubcommandUtil.sanitize_options_for_save(@cli.combined_instance_and_options_hosts) options_storage = YAML::Store.new(SubcommandUtil::SUBCOMMAND_OPTIONS) options_storage.transaction do options_storage['HOSTS'] = hosts end end end desc "destroy", "Destroys the provisioned VMs" long_desc <<-LONG_DESC Destroys the currently provisioned VMs LONG_DESC option :help, :type => :boolean, :hide => true def destroy() if options[:help] invoke :help, [], ["destroy"] return end state = YAML::Store.new(SubcommandUtil::SUBCOMMAND_STATE) unless state.transaction { state['provisioned']} SubcommandUtil.error_with('Please provision an environment') end @cli.parse_options @cli.options[:provision] = false @cli.initialize_network_manager @cli.network_manager.cleanup state.transaction { state.delete('provisioned') } end end end