lib/bolt/bolt_option_parser.rb in bolt-1.23.0 vs lib/bolt/bolt_option_parser.rb in bolt-1.24.0

- old
+ new

@@ -4,10 +4,83 @@ require 'optparse' module Bolt class BoltOptionParser < OptionParser + OPTIONS = { inventory: %w[nodes targets query rerun description], + authentication: %w[user password private-key host-key-check ssl ssl-verify], + escalation: %w[run-as sudo-password], + run_context: %w[concurrency inventoryfile save-rerun], + global_config_setters: %w[modulepath boltdir configfile], + transports: %w[transport connect-timeout tty], + display: %w[format color verbose trace], + global: %w[help version debug] }.freeze + + ACTION_OPTS = OPTIONS.values.flatten.freeze + + def get_help_text(subcommand, action = nil) + case subcommand + when 'apply' + { flags: ACTION_OPTS + %w[noop execute compile-concurrency], + banner: APPLY_HELP } + when 'command' + { flags: ACTION_OPTS, + banner: COMMAND_HELP } + when 'file' + { flags: ACTION_OPTS + %w[tmpdir], + banner: FILE_HELP } + when 'plan' + case action + when 'convert' + { flags: OPTIONS[:global] + OPTIONS[:global_config_setters], + banner: PLAN_CONVERT_HELP } + when 'show' + { flags: OPTIONS[:global] + OPTIONS[:global_config_setters], + banner: PLAN_SHOW_HELP } + when 'run' + { flags: ACTION_OPTS + %w[params compile-concurrency tmpdir], + banner: PLAN_RUN_HELP } + else + { flags: ACTION_OPTS + %w[params compile-concurrency tmpdir], + banner: PLAN_HELP } + end + when 'puppetfile' + case action + when 'install' + { flags: OPTIONS[:global] + OPTIONS[:global_config_setters], + banner: PUPPETFILE_INSTALL_HELP } + when 'show-modules' + { flags: OPTIONS[:global] + OPTIONS[:global_config_setters], + banner: PUPPETFILE_SHOWMODULES_HELP } + else + { flags: OPTIONS[:global] + OPTIONS[:global_config_setters], + banner: PUPPETFILE_HELP } + end + when 'script' + { flags: ACTION_OPTS + %w[tmpdir], + banner: SCRIPT_HELP } + when 'secret' + { flags: OPTIONS[:global] + OPTIONS[:global_config_setters], + banner: SECRET_HELP } + when 'task' + case action + when 'show' + { flags: OPTIONS[:global] + OPTIONS[:global_config_setters], + banner: TASK_SHOW_HELP } + when 'run' + { flags: ACTION_OPTS + %w[params tmpdir], + banner: TASK_RUN_HELP } + else + { flags: ACTION_OPTS + %w[params tmpdir], + banner: TASK_HELP } + end + else + { flags: OPTIONS[:global], + banner: BANNER } + end + end + def self.examples(cmd, desc) <<-EXAMP #{desc} a Windows host via WinRM, providing for the password bolt #{cmd} -n winrm://winhost -u Administrator -p #{desc} the local machine, a Linux host via SSH, and hosts from a group specified in an inventory file @@ -16,11 +89,11 @@ bolt #{cmd} -q 'inventory[certname] { facts.os.family = "windows" }' --transport winrm -u 'domain\\Administrator' -p EXAMP end BANNER = <<-HELP -Usage: bolt <subcommand> <action> [options] +Usage: bolt <subcommand> <action> Available subcommands: bolt command run <command> Run a command remotely bolt file upload <src> <dest> Upload a local file or directory bolt script run <script> Upload a local script and run it remotely @@ -32,52 +105,74 @@ bolt plan show <plan> Show details for plan bolt plan run <plan> [params] Run a Puppet task plan bolt apply <manifest> Apply Puppet manifest code bolt puppetfile install Install modules from a Puppetfile into a Boltdir bolt puppetfile show-modules List modules available to Bolt + bolt secret createkeys Create new encryption keys + bolt secret encrypt <plaintext> Encrypt a value + bolt secret decrypt <encrypted> Decrypt a value Run `bolt <subcommand> --help` to view specific examples. -where [options] are: +Available options are: HELP TASK_HELP = <<-HELP -Usage: bolt task <action> <task> [options] [parameters] +Usage: bolt task <action> <task> [parameters] Available actions are: show Show list of available tasks show <task> Show documentation for task - run Run a Puppet task + run <task> Run a Puppet task Parameters are of the form <parameter>=<value>. #{examples('task run facts', 'run facter on')} Available options are: HELP + TASK_SHOW_HELP = <<-HELP +Usage: bolt task show <task> + +Available actions are: + show Show list of available tasks + show <task> Show documentation for task + +Available options are: + HELP + + TASK_RUN_HELP = <<-HELP +Usage: bolt task run <task> [parameters] + +Parameters are of the form <parameter>=<value>. + +#{examples('task run facts', 'run facter on')} +Available options are: + HELP + COMMAND_HELP = <<-HELP -Usage: bolt command <action> <command> [options] +Usage: bolt command <action> <command> Available actions are: run Run a command remotely #{examples('command run hostname', 'run hostname on')} Available options are: HELP SCRIPT_HELP = <<-HELP -Usage: bolt script <action> <script> [[arg1] ... [argN]] [options] +Usage: bolt script <action> <script> [[arg1] ... [argN]] Available actions are: run Upload a local script and run it remotely #{examples('script run my_script.ps1 some args', 'run a script on')} Available options are: HELP PLAN_HELP = <<-HELP -Usage: bolt plan <action> <plan> [options] [parameters] +Usage: bolt plan <action> <plan> [parameters] Available actions are: convert <plan_path> Convert a YAML plan to a Puppet plan show Show list of available plans show <plan> Show details for plan @@ -87,22 +182,47 @@ #{examples('plan run canary command=hostname', 'run the canary plan on')} Available options are: HELP + PLAN_CONVERT_HELP = <<-HELP +Usage: bolt plan convert <plan_path> + +Available options are: + HELP + + PLAN_SHOW_HELP = <<-HELP +Usage: bolt plan show <plan> + +Available actions are: + show Show list of available plans + show <plan> Show details for plan + +Available options are: + HELP + + PLAN_RUN_HELP = <<-HELP +Usage: bolt plan run <plan> [parameters] + +Parameters are of the form <parameter>=<value>. + +#{examples('plan run canary command=hostname', 'run the canary plan on')} +Available options are: + HELP + FILE_HELP = <<-HELP -Usage: bolt file <action> [options] +Usage: bolt file <action> Available actions are: upload <src> <dest> Upload local file or directory <src> to <dest> on each node #{examples('file upload /tmp/source /etc/profile.d/login.sh', 'upload a file to')} Available options are: HELP PUPPETFILE_HELP = <<-HELP -Usage: bolt puppetfile <action> [options] +Usage: bolt puppetfile <action> Available actions are: install Install modules from a Puppetfile into a Boltdir show-modules List modules available to Bolt @@ -110,61 +230,76 @@ bolt puppetfile install Available options are: HELP + PUPPETFILE_INSTALL_HELP = <<-HELP +Usage: bolt puppetfile install + +Install modules into the local Boltdir + bolt puppetfile install + +Available options are: + HELP + + PUPPETFILE_SHOWMODULES_HELP = <<-HELP +Usage: bolt puppetfile show-modules + +Available options are: + HELP + APPLY_HELP = <<-HELP -Usage: bolt apply <manifest.pp> [options] +Usage: bolt apply <manifest.pp> #{examples('apply site.pp', 'apply a manifest on')} bolt apply site.pp --nodes foo.example.com,bar.example.com + +Available options are: HELP - # A helper mixin for OptionParser::Switch instances which will allow - # us to show/hide particular switch in the help message produced by - # the OptionParser#help method on demand. - module SwitchHider - attr_accessor :hide + SECRET_HELP = <<~SECRET_HELP + Manage secrets for inventory and hiera data. - def summarize(*args) - return self if hide - super - end - end + Available actions are: + createkeys Create new encryption keys + encrypt Encrypt a value + decrypt Decrypt a value + Available options are: + SECRET_HELP def initialize(options) super() @options = options - @nodes = define('-n', '--nodes NODES', - 'Alias for --targets') do |nodes| + define('-n', '--nodes NODES', + 'Alias for --targets') do |nodes| @options [:nodes] ||= [] @options[:nodes] << get_arg_input(nodes) - end.extend(SwitchHider) - @targets = define('-t', '--targets TARGETS', - 'Identifies the targets of command.', - 'Enter a comma-separated list of target URIs or group names.', - "Or read a target list from an input file '@<file>' or stdin '-'.", - 'Example: --targets localhost,node_group,ssh://nix.com:23,winrm://windows.puppet.com', - 'URI format is [protocol://]host[:port]', - "SSH is the default protocol; may be #{TRANSPORTS.keys.join(', ')}", - 'For Windows targets, specify the winrm:// protocol if it has not be configured', - 'For SSH, port defaults to `22`', - 'For WinRM, port defaults to `5985` or `5986` based on the --[no-]ssl setting') do |targets| + end + define('-t', '--targets TARGETS', + 'Identifies the targets of command.', + 'Enter a comma-separated list of target URIs or group names.', + "Or read a target list from an input file '@<file>' or stdin '-'.", + 'Example: --targets localhost,node_group,ssh://nix.com:23,winrm://windows.puppet.com', + 'URI format is [protocol://]host[:port]', + "SSH is the default protocol; may be #{TRANSPORTS.keys.join(', ')}", + 'For Windows targets, specify the winrm:// protocol if it has not be configured', + 'For SSH, port defaults to `22`', + 'For WinRM, port defaults to `5985` or `5986` based on the --[no-]ssl setting') do |targets| @options[:targets] ||= [] @options[:targets] << get_arg_input(targets) - end.extend(SwitchHider) - @query = define('-q', '--query QUERY', 'Query PuppetDB to determine the targets') do |query| + end + define('-q', '--query QUERY', 'Query PuppetDB to determine the targets') do |query| @options[:query] = query - end.extend(SwitchHider) - @rerun = define('--rerun FILTER', 'Retry on nodes from the last run', - "'all' all nodes that were part of the last run.", - "'failure' nodes that failed in the last run.", - "'success' nodes that succeeded in the last run.") do |rerun| + end + define('--rerun FILTER', 'Retry on nodes from the last run', + "'all' all nodes that were part of the last run.", + "'failure' nodes that failed in the last run.", + "'success' nodes that succeeded in the last run.") do |rerun| @options[:rerun] = rerun - end.extend(SwitchHider) + end define('--noop', 'Execute a task that supports it in noop mode') do |_| @options[:noop] = true end define('--description DESCRIPTION', 'Description to use for the job') do |description| @@ -172,16 +307,16 @@ end define('--params PARAMETERS', "Parameters to a task or plan as json, a json file '@<file>', or on stdin '-'") do |params| @options[:task_options] = parse_params(params) end - @execute = define('-e', '--execute CODE', - "Puppet manifest code to apply to the targets") do |code| + define('-e', '--execute CODE', + "Puppet manifest code to apply to the targets") do |code| @options[:code] = code - end.extend(SwitchHider) + end - separator 'Authentication:' + separator "\nAuthentication:" define('-u', '--user USER', 'User to authenticate as') do |user| @options[:user] = user end define('-p', '--password [PASSWORD]', 'Password to authenticate with. Omit the value to prompt for the password.') do |password| @@ -204,11 +339,11 @@ end define('--[no-]ssl-verify', 'Verify remote host SSL certificate with WinRM') do |ssl_verify| @options[:'ssl-verify'] = ssl_verify end - separator 'Escalation:' + separator "\nEscalation:" define('--run-as USER', 'User to run as using privilege escalation') do |user| @options[:'run-as'] = user end define('--sudo-password [PASSWORD]', 'Password for privilege escalation. Omit the value to prompt for the password.') do |password| @@ -219,11 +354,11 @@ else @options[:'sudo-password'] = password end end - separator 'Run context:' + separator "\nRun context:" define('-c', '--concurrency CONCURRENCY', Integer, 'Maximum number of simultaneous connections (default: 100)') do |concurrency| @options[:concurrency] = concurrency end define('--compile-concurrency CONCURRENCY', Integer, @@ -254,11 +389,11 @@ end define('--[no-]save-rerun', 'Whether to update the rerun file after this command.') do |save| @options[:'save-rerun'] = save end - separator 'Transports:' + separator "\nTransports:" define('--transport TRANSPORT', TRANSPORTS.keys.map(&:to_s), "Specify a default transport: #{TRANSPORTS.keys.join(', ')}") do |t| @options[:transport] = t end define('--connect-timeout TIMEOUT', Integer, 'Connection timeout (defaults vary)') do |timeout| @@ -269,68 +404,55 @@ end define('--tmpdir DIR', 'The directory to upload and execute temporary files on the target') do |tmpdir| @options[:tmpdir] = tmpdir end - separator 'Display:' + separator "\nDisplay:" define('--format FORMAT', 'Output format to use: human or json') do |format| @options[:format] = format end define('--[no-]color', 'Whether to show output in color') do |color| @options[:color] = color end - define('-h', '--help', 'Display help') do |_| - @options[:help] = true - end define('-v', '--[no-]verbose', 'Display verbose logging') do |value| @options[:verbose] = value end - define('--debug', 'Display debug logging') do |_| - @options[:debug] = true - end define('--trace', 'Display error stack traces') do |_| @options[:trace] = true end + + separator "\nGlobal:" + define('-h', '--help', 'Display help') do |_| + @options[:help] = true + end define('--version', 'Display the version') do |_| puts Bolt::VERSION raise Bolt::CLIExit end - - update + define('--debug', 'Display debug logging') do |_| + @options[:debug] = true + end end - def hide_target_opts(toggle = true) - @nodes.hide = @query.hide = @rerun.hide = @targets.hide = toggle + def remove_excluded_opts(option_list) + # Remove any options that are not available for the specified subcommand + top.list.delete_if do |opt| + opt.respond_to?(:switch_name) && !option_list.include?(opt.switch_name) + end + # Remove any separators if all options of that type have been removed + top.list.delete_if do |opt| + i = top.list.index(opt) + opt.is_a?(String) && top.list[i + 1].is_a?(String) + end end def update - # show the --nodes, --query, and --rerun switches by default - hide_target_opts(false) - # Don't show the --execute switch except for `apply` - @execute.hide = true - + help_text = get_help_text(@options[:subcommand], @options[:action]) # Update the banner according to the subcommand - self.banner = case @options[:subcommand] - when 'plan' - PLAN_HELP - when 'command' - COMMAND_HELP - when 'script' - SCRIPT_HELP - when 'task' - TASK_HELP - when 'file' - FILE_HELP - when 'puppetfile' - # Don't show targeting options for puppetfile - hide_target_opts - PUPPETFILE_HELP - when 'apply' - @execute.hide = false - APPLY_HELP - else - BANNER - end + self.banner = help_text[:banner] + # Builds the option list for the specified subcommand and removes all excluded + # options from the help text + remove_excluded_opts(help_text[:flags]) end def parse_params(params) json = get_arg_input(params) JSON.parse(json)