Class: Bolt::BoltOptionParser

Inherits:
OptionParser
  • Object
show all
Defined in:
lib/bolt/bolt_option_parser.rb

Constant Summary collapse

OPTIONS =
{ inventory: %w[nodes targets query rerun description],
authentication: %w[user password password-prompt private-key host-key-check ssl ssl-verify],
escalation: %w[run-as sudo-password sudo-password-prompt],
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
"Usage: bolt <subcommand> <action>\n\nAvailable subcommands:\n  bolt command run <command>       Run a command remotely\n  bolt file upload <src> <dest>    Upload a local file or directory\n  bolt script run <script>         Upload a local script and run it remotely\n  bolt task show                   Show list of available tasks\n  bolt task show <task>            Show documentation for task\n  bolt task run <task> [params]    Run a Puppet task\n  bolt plan convert <plan_path>    Convert a YAML plan to a Puppet plan\n  bolt plan show                   Show list of available plans\n  bolt plan show <plan>            Show details for plan\n  bolt plan run <plan> [params]    Run a Puppet task plan\n  bolt apply <manifest>            Apply Puppet manifest code\n  bolt puppetfile install          Install modules from a Puppetfile into a Boltdir\n  bolt puppetfile show-modules     List modules available to Bolt\n  bolt secret createkeys           Create new encryption keys\n  bolt secret encrypt <plaintext>  Encrypt a value\n  bolt secret decrypt <encrypted>  Decrypt a value\n  bolt inventory show              Show the list of targets an action would run on\n  bolt group show                  Show the list of groups in the inventory\n  bolt project init                Create a new Bolt project\n  bolt project migrate             Migrate a Bolt project to the latest version\n\nRun `bolt <subcommand> --help` to view specific examples.\n\nAvailable options are:\n"
TASK_HELP =
"Usage: bolt task <action> <task> [parameters]\n\nAvailable actions are:\n  show                             Show list of available tasks\n  show <task>                      Show documentation for task\n  run <task>                       Run a Puppet task\n\nParameters are of the form <parameter>=<value>.\n\n\#{examples('task run facts', 'run facter on')}\nAvailable options are:\n"
TASK_SHOW_HELP =
"Usage: bolt task show <task>\n\nAvailable actions are:\n  show                             Show list of available tasks\n  show <task>                      Show documentation for task\n\nAvailable options are:\n"
TASK_RUN_HELP =
"Usage: bolt task run <task> [parameters]\n\nParameters are of the form <parameter>=<value>.\n\n\#{examples('task run facts', 'run facter on')}\nAvailable options are:\n"
COMMAND_HELP =
"Usage: bolt command <action> <command>\n\nAvailable actions are:\n  run                              Run a command remotely\n\n\#{examples('command run hostname', 'run hostname on')}\nAvailable options are:\n"
SCRIPT_HELP =
"Usage: bolt script <action> <script> [[arg1] ... [argN]]\n\nAvailable actions are:\n  run                              Upload a local script and run it remotely\n\n\#{examples('script run my_script.ps1 some args', 'run a script on')}\nAvailable options are:\n"
PLAN_HELP =
"Usage: bolt plan <action> <plan> [parameters]\n\nAvailable actions are:\n  convert <plan_path>              Convert a YAML plan to a Puppet plan\n  show                             Show list of available plans\n  show <plan>                      Show details for plan\n  run                              Run a Puppet task plan\n\nParameters are of the form <parameter>=<value>.\n\n\#{examples('plan run canary command=hostname', 'run the canary plan on')}\nAvailable options are:\n"
PLAN_CONVERT_HELP =
"Usage: bolt plan convert <plan_path>\n\nAvailable options are:\n"
PLAN_SHOW_HELP =
"Usage: bolt plan show <plan>\n\nAvailable actions are:\n  show                             Show list of available plans\n  show <plan>                      Show details for plan\n\nAvailable options are:\n"
PLAN_RUN_HELP =
"Usage: bolt plan run <plan> [parameters]\n\nParameters are of the form <parameter>=<value>.\n\n\#{examples('plan run canary command=hostname', 'run the canary plan on')}\nAvailable options are:\n"
FILE_HELP =
"Usage: bolt file <action>\n\nAvailable actions are:\n  upload <src> <dest>              Upload local file or directory <src> to <dest> on each node\n\n\#{examples('file upload /tmp/source /etc/profile.d/login.sh', 'upload a file to')}\nAvailable options are:\n"
PUPPETFILE_HELP =
"Usage: bolt puppetfile <action>\n\nAvailable actions are:\n  install                          Install modules from a Puppetfile into a Boltdir\n  show-modules                     List modules available to Bolt\n  generate-types                   Generate type references to register in Plans\n\nInstall modules into the local Boltdir\n  bolt puppetfile install\n\nAvailable options are:\n"
PUPPETFILE_INSTALL_HELP =
"Usage: bolt puppetfile install\n\nInstall modules into the local Boltdir\n  bolt puppetfile install\n\nAvailable options are:\n"
PUPPETFILE_SHOWMODULES_HELP =
"Usage: bolt puppetfile show-modules\n\nList modules available to Bolt\n  bolt puppetfile show-modules\n\nAvailable options are:\n"
PUPPETFILE_GENERATETYPES_HELP =
"Usage: bolt puppetfile generate-types\n\nGenerate type references to register in Plans\n  bolt puppetfile generate-types\n\nAvailable options are:\n"
APPLY_HELP =
"Usage: bolt apply <manifest.pp>\n\n\#{examples('apply site.pp', 'apply a manifest on')}\n  bolt apply site.pp --nodes foo.example.com,bar.example.com\n\nAvailable options are:\n"
SECRET_HELP =
"Usage: bolt secret <action> <value>\nManage secrets for inventory and hiera data.\n\nAvailable actions are:\n  createkeys               Create new encryption keys\n  encrypt                  Encrypt a value\n  decrypt                  Decrypt a value\n\nAvailable options are:\n"
INVENTORY_HELP =
"Usage: bolt inventory <action>\n\nAvailable actions are:\n  show                     Show the list of targets an action would run on\n\nAvailable options are:\n"
GROUP_HELP =
"Usage: bolt group <action>\n\nAvailable actions are:\n  show                     Show the list of groups in the inventory\n\nAvailable options are:\n"
PROJECT_HELP =
"Usage: bolt project <action>\n\nAvailable actions are:\n  init                     Create a new Bolt project\n  migrate                  Migrate a Bolt project to the latest version\n\nAvailable options are:\n"
PROJECT_INIT_HELP =
"Usage: bolt project init [directory]\n\nCreate a new Bolt project.\nSpecify a directory to create the Bolt project in. Defaults to the current working directory.\n\nAvailable options are:\n"
PROJECT_MIGRATE_HELP =
"Usage: bolt project migrate\n\nMigrate a Bolt project to the latest version.\nLoads a Bolt project's inventory file and migrates it to the latest version. The\ninventory file is modified in place and will not preserve comments or formatting.\n\nAvailable options are:\n"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options) ⇒ BoltOptionParser

Returns a new instance of BoltOptionParser.



357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
# File 'lib/bolt/bolt_option_parser.rb', line 357

def initialize(options)
  super()

  @options = options
  @warnings = []

  define('-n', '--nodes NODES',
         'Alias for --targets',
         'Deprecated in favor of --targets') do |nodes|
    @options [:nodes] ||= []
    @options[:nodes] << get_arg_input(nodes)
  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
  define('-q', '--query QUERY', 'Query PuppetDB to determine the targets') do |query|
    @options[:query] = query
  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
  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|
    @options[:description] = description
  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
  define('-e', '--execute CODE',
         "Puppet manifest code to apply to the targets") do |code|
    @options[:code] = code
  end
  define('--detail', 'Show resolved configuration for the targets') do |detail|
    @options[:detail] = detail
  end

  separator "\nAuthentication:"
  define('-u', '--user USER', 'User to authenticate as') do |user|
    @options[:user] = user
  end
  define('-p', '--password [PASSWORD]',
         'Password to authenticate with') do |password|
    if password.nil?
      msg = "Optional parameter for --password is deprecated and will no longer prompt for password. " \
            "Use the prompt plugin or --password-prompt instead to prompt for passwords."
      @warnings << { option: 'password', msg: msg }
      STDOUT.print "Please enter your password: "
      @options[:password] = STDIN.noecho(&:gets).chomp
      STDOUT.puts
    else
      @options[:password] = password
    end
  end
  define('--password-prompt', 'Prompt for user to input password') do |_password|
    STDERR.print "Please enter your password: "
    @options[:password] = STDIN.noecho(&:gets).chomp
    STDERR.puts
  end
  define('--private-key KEY', 'Private ssh key to authenticate with') do |key|
    @options[:'private-key'] = key
  end
  define('--[no-]host-key-check', 'Check host keys with SSH') do |host_key_check|
    @options[:'host-key-check'] = host_key_check
  end
  define('--[no-]ssl', 'Use SSL with WinRM') do |ssl|
    @options[:ssl] = ssl
  end
  define('--[no-]ssl-verify', 'Verify remote host SSL certificate with WinRM') do |ssl_verify|
    @options[:'ssl-verify'] = ssl_verify
  end

  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') do |password|
    if password.nil?
      msg = "Optional parameter for --sudo-password is deprecated and will no longer prompt for password. " \
            "Use the prompt plugin or --sudo-password-prompt instead to prompt for passwords."
      @warnings << { option: 'sudo-password', msg: msg }
      STDOUT.print "Please enter your privilege escalation password: "
      @options[:'sudo-password'] = STDIN.noecho(&:gets).chomp
      STDOUT.puts
    else
      @options[:'sudo-password'] = password
    end
  end
  define('--sudo-password-prompt', 'Prompt for user to input escalation password') do |_password|
    STDERR.print "Please enter your privilege escalation password: "
    @options[:'sudo-password'] = STDIN.noecho(&:gets).chomp
    STDERR.puts
  end

  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,
         'Maximum number of simultaneous manifest block compiles (default: number of cores)') do |concurrency|
    @options[:'compile-concurrency'] = concurrency
  end
  define('-m', '--modulepath MODULES',
         "List of directories containing modules, separated by '#{File::PATH_SEPARATOR}'",
         'Directories are case-sensitive') do |modulepath|
    # When specified from the CLI, modulepath entries are relative to pwd
    @options[:modulepath] = modulepath.split(File::PATH_SEPARATOR).map do |moduledir|
      File.expand_path(moduledir)
    end
  end
  define('--boltdir FILEPATH',
         'Specify what Boltdir to load config from (default: autodiscovered from current working dir)') do |path|
    @options[:boltdir] = path
  end
  define('--configfile FILEPATH',
         'Specify where to load config from (default: ~/.puppetlabs/bolt/bolt.yaml). ' \
         'Directory containing bolt.yaml will be used as the Boltdir.') do |path|
    @options[:configfile] = path
  end
  define('-i', '--inventoryfile FILEPATH',
         'Specify where to load inventory from (default: ~/.puppetlabs/bolt/inventory.yaml)') do |path|
    if ENV.include?(Bolt::Inventory::ENVIRONMENT_VAR)
      raise Bolt::CLIError, "Cannot pass inventory file when #{Bolt::Inventory::ENVIRONMENT_VAR} is set"
    end
    @options[:inventoryfile] = File.expand_path(path)
  end
  define('--[no-]save-rerun', 'Whether to update the rerun file after this command.') do |save|
    @options[:'save-rerun'] = save
  end

  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|
    @options[:'connect-timeout'] = timeout
  end
  define('--[no-]tty', 'Request a pseudo TTY on nodes that support it') do |tty|
    @options[:tty] = tty
  end
  define('--tmpdir DIR', 'The directory to upload and execute temporary files on the target') do |tmpdir|
    @options[:tmpdir] = tmpdir
  end

  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('-v', '--[no-]verbose', 'Display verbose logging') do |value|
    @options[:verbose] = value
  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
  define('--debug', 'Display debug logging') do |_|
    @options[:debug] = true
  end

  define('--plugin PLUGIN', 'Select the plugin to use') do |plug|
    @options[:plugin] = plug
  end
end

Instance Attribute Details

#warningsObject (readonly)

Returns the value of attribute warnings.



356
357
358
# File 'lib/bolt/bolt_option_parser.rb', line 356

def warnings
  @warnings
end

Class Method Details

.examples(cmd, desc) ⇒ Object



103
104
105
106
107
108
109
110
111
112
# File 'lib/bolt/bolt_option_parser.rb', line 103

def self.examples(cmd, desc)
  "  \#{desc} a Windows host via WinRM, providing for the password\n    bolt \#{cmd} -n winrm://winhost -u Administrator -p\n  \#{desc} the local machine, a Linux host via SSH, and hosts from a group specified in an inventory file\n    bolt \#{cmd} -n localhost,nixhost,node_group\n  \#{desc} Windows hosts queried from PuppetDB via WinRM as a domain user, prompting for the password\n    bolt \#{cmd} -q 'inventory[certname] { facts.os.family = \"windows\" }' --transport winrm -u 'domain\\\\Administrator' -p\n  EXAMP\nend\n"

Instance Method Details

#get_arg_input(value) ⇒ Object



579
580
581
582
583
584
585
586
587
588
# File 'lib/bolt/bolt_option_parser.rb', line 579

def get_arg_input(value)
  if value.start_with?('@')
    file = value.sub(/^@/, '')
    read_arg_file(file)
  elsif value == '-'
    STDIN.read
  else
    value
  end
end

#get_help_text(subcommand, action = nil) ⇒ Object



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/bolt/bolt_option_parser.rb', line 20

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 'inventory'
    { flags: OPTIONS[:inventory] + OPTIONS[:global] + %w[format inventoryfile boltdir configfile detail],
      banner: INVENTORY_HELP }
  when 'group'
    { flags: OPTIONS[:global] + %w[format inventoryfile boltdir configfile],
      banner: GROUP_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 'project'
    case action
    when 'init'
      { flags: OPTIONS[:global],
        banner: PROJECT_INIT_HELP }
    when 'migrate'
      { flags: OPTIONS[:global] + %w[inventoryfile boltdir configfile],
        banner: PROJECT_MIGRATE_HELP }
    else
      { flags: OPTIONS[:global],
        banner: PROJECT_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 }
    when 'generate-types'
      { flags: OPTIONS[:global] + OPTIONS[:global_config_setters],
        banner: PUPPETFILE_GENERATETYPES_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] + %w[plugin],
      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

#parse_params(params) ⇒ Object



572
573
574
575
576
577
# File 'lib/bolt/bolt_option_parser.rb', line 572

def parse_params(params)
  json = get_arg_input(params)
  JSON.parse(json)
rescue JSON::ParserError => e
  raise Bolt::CLIError, "Unable to parse --params value as JSON: #{e}"
end

#read_arg_file(file) ⇒ Object



590
591
592
593
594
# File 'lib/bolt/bolt_option_parser.rb', line 590

def read_arg_file(file)
  File.read(File.expand_path(file))
rescue StandardError => e
  raise Bolt::FileError.new("Error attempting to read #{file}: #{e}", file)
end

#remove_excluded_opts(option_list) ⇒ Object



551
552
553
554
555
556
557
558
559
560
561
# File 'lib/bolt/bolt_option_parser.rb', line 551

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

#updateObject



563
564
565
566
567
568
569
570
# File 'lib/bolt/bolt_option_parser.rb', line 563

def update
  help_text = get_help_text(@options[:subcommand], @options[:action])
  # Update the banner according to the subcommand
  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