lib/qb/cli/run.rb in qb-0.3.4 vs lib/qb/cli/run.rb in qb-0.3.5

- old
+ new

@@ -1,290 +1,581 @@ \ No newline at end of file +# Requirements +# ===================================================================== + +# package +require 'qb/cli/help' + + +# Declarations +# ======================================================================= + +module QB; end + + +# Definitions +# ======================================================================= + +module QB::CLI + + # Run a QB role. + # + # @param [Array<String>] args + # CLI args to work with. + # + # @return [Fixnum] + # Exit status code from `ansible-playbook` command, unless we invoked + # help or error'd out in another way before the run (in which case `1` + # is returned). + # + def self.run args + role_arg = args.shift + QB.debug "role arg", role_arg + + begin + role = QB::Role.require role_arg + rescue QB::Role::NoMatchesError => e + puts "ERROR - #{ e.message }\n\n" + # exits with status code 1 + return help + rescue QB::Role::MultipleMatchesError => e + puts "ERROR - #{ e.message }\n\n" + return 1 + end + + QB.check_qb_version role + + options = QB::Options.new role, args + + QB.debug "role options set on cli", options.role_options.select {|k, o| + !o.value.nil? + } + + QB.debug "qb options", options.qb + + cwd = Dir.getwd + + dir = nil + + if role.uses_default_dir? + # get the target dir + dir = case args.length + when 0 + # in this case, a dir has not been provided + # + # in some cases (like projects) the dir can be figured out in other ways: + # + + if options.ask? + default = begin + role.default_dir cwd, options.role_options + rescue QB::UserInputError => e + NRSER::NO_ARG + end + + QB::CLI.ask name: "target directory (`qb_dir`)", + type: t.non_empty_str, + default: default + + else + role.default_dir cwd, options.role_options + end + + when 1 + # there is a single positional arg, which is used as dir + args[0] + + else + # there are multiple positional args, which is not allowed + raise "can't supply more than one argument: #{ args.inspect }" + + end + + QB.debug "input_dir", dir + + # normalize to expanded path (has no trailing slash) + dir = File.expand_path dir + + QB.debug "normalized_dir", dir + + # create the dir if it doesn't exist (so don't have to cover this in + # every role) + if role.mkdir + FileUtils.mkdir_p dir unless File.exists? dir + end + + saved_options_path = Pathname.new(dir) + '.qb-options.yml' + + saved_options = if saved_options_path.exist? + # convert old _ separated names to - separated + YAML.load(saved_options_path.read).map {|role_options_key, role_options| + [ + role_options_key, + role_options.map {|name, value| + [QB::Options.cli_ize_name(name), value] + }.to_h + ] + }.to_h.tap {|saved_options| + QB.debug "found saved options", saved_options + } + else + QB.debug "no saved options" + {} + end + + if saved_options.key? role.options_key + role_saved_options = saved_options[role.options_key] + + QB.debug "found saved options for role", role_saved_options + + role_saved_options.each do |option_cli_name, value| + option = options.role_options[option_cli_name] + + if option.value.nil? + QB.debug "setting from saved options", option: option, value: value + + option.value = value + end + end + end + end # unless default_dir == false + + + # Interactive Input + # ===================================================================== + + if options.ask? + # Incomplete + raise "COMING SOON!!!...?" + QB::CLI.ask_for_options role: role, options: options + end + + + # Validation + # ===================================================================== + # + # Should have already been taken care of if we used interactive input. + # + + # check that required options are present + missing = options.role_options.values.select {|option| + option.required? && option.value.nil? + } + + unless missing.empty? + puts "ERROR: options #{ missing.map {|o| o.cli_name } } are required." + return 1 + end + + set_options = options.role_options.select {|k, o| !o.value.nil?} + + QB.debug "set options", set_options + + playbook_role = {'role' => role.name} + + playbook_vars = { + 'qb_dir' => dir, + # depreciated due to mass potential for conflict + 'dir' => dir, + 'qb_cwd' => cwd, + 'qb_user_roles_dir' => QB::USER_ROLES_DIR.to_s, + } + + set_options.values.each do |option| + playbook_role[option.var_name] = option.value + end + + play = + { + 'hosts' => options.qb['hosts'], + 'vars' => playbook_vars, + # 'gather_subset' => ['!all'], + 'gather_facts' => options.qb['facts'], + 'pre_tasks' => [ + { + 'qb_facts' => { + 'qb_dir' => dir, + } + }, + ], + 'roles' => [ + 'nrser.blockinfile', + playbook_role + ], + } + + if options.qb['user'] + play['become'] = true + play['become_user'] = options.qb['user'] + end + + playbook = [play] + + QB.debug "playbook", playbook + + env = QB::Ansible::Env.new + + # stick the role path in front to make sure we get **that** role + env.roles_path.unshift role.path.expand_path.dirname + + cmd = QB::Ansible::Cmds::Playbook.new \ + env: env, + playbook: playbook, + role_options: options, + chdir: (File.exists?('./ansible/ansible.cfg') ? './ansible' : nil) + + # print + # ===== + # + # print useful stuff for debugging / running outside of qb + # + + if options.qb['print'].include? 'options' + puts "SET OPTIONS:\n\n#{ YAML.dump set_options }\n\n" + end + + if options.qb['print'].include? 'env' + puts "ENV:\n\n#{ YAML.dump cmd.env.to_h }\n\n" + end + + if options.qb['print'].include? 'cmd' + puts "COMMAND:\n\n#{ cmd.prepare }\n\n" + end + + if options.qb['print'].include? 'playbook' + puts "PLAYBOOK:\n\n#{ YAML.dump playbook }\n\n" + end + + # stop here if we're not supposed to run + exit 0 if !options.qb['run'] + + # run + # === + # + # stuff below here does stuff + # + + # save the options back + if ( + dir && + # we set some options that we can save + set_options.values.select {|o| o.save? }.length > 0 && + # the role says to save options + role.save_options + ) + saved_options[role.options_key] = set_options.select{|key, option| + option.save? + }.map {|key, option| + [key, option.value] + }.to_h + + unless saved_options_path.dirname.exist? + FileUtils.mkdir_p saved_options_path.dirname + end + + saved_options_path.open('w') do |f| + f.write YAML.dump(saved_options) + end + end + + status = cmd.stream + + if status != 0 + $stderr.puts "ERROR ansible-playbook failed." + end + + # exit status + status + end # .run + +end # module QB::CLI + +