bolt 3.3.0 → 3.7.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of bolt might be problematic. Click here for more details.

Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/Puppetfile +5 -5
  3. data/bolt-modules/boltlib/lib/puppet/datatypes/containerresult.rb +24 -0
  4. data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_command.rb +66 -0
  5. data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +20 -2
  6. data/bolt-modules/boltlib/lib/puppet/functions/run_container.rb +162 -0
  7. data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +19 -2
  8. data/bolt-modules/boltlib/types/planresult.pp +1 -0
  9. data/bolt-modules/prompt/lib/puppet/functions/prompt.rb +20 -2
  10. data/bolt-modules/prompt/lib/puppet/functions/prompt/menu.rb +103 -0
  11. data/guides/targets.txt +31 -0
  12. data/lib/bolt/analytics.rb +4 -8
  13. data/lib/bolt/bolt_option_parser.rb +35 -17
  14. data/lib/bolt/cli.rb +109 -28
  15. data/lib/bolt/config.rb +11 -7
  16. data/lib/bolt/config/options.rb +41 -9
  17. data/lib/bolt/config/transport/lxd.rb +3 -1
  18. data/lib/bolt/config/transport/options.rb +7 -0
  19. data/lib/bolt/config/transport/podman.rb +33 -0
  20. data/lib/bolt/container_result.rb +105 -0
  21. data/lib/bolt/error.rb +15 -0
  22. data/lib/bolt/executor.rb +27 -15
  23. data/lib/bolt/inventory.rb +5 -4
  24. data/lib/bolt/inventory/inventory.rb +3 -2
  25. data/lib/bolt/inventory/options.rb +9 -0
  26. data/lib/bolt/inventory/target.rb +16 -0
  27. data/lib/bolt/node/output.rb +14 -4
  28. data/lib/bolt/outputter/human.rb +243 -84
  29. data/lib/bolt/outputter/json.rb +6 -4
  30. data/lib/bolt/outputter/logger.rb +17 -0
  31. data/lib/bolt/pal.rb +22 -2
  32. data/lib/bolt/pal/yaml_plan/step.rb +4 -2
  33. data/lib/bolt/pal/yaml_plan/step/command.rb +8 -0
  34. data/lib/bolt/pal/yaml_plan/step/script.rb +4 -0
  35. data/lib/bolt/pal/yaml_plan/transpiler.rb +2 -2
  36. data/lib/bolt/plan_creator.rb +2 -2
  37. data/lib/bolt/plugin.rb +13 -11
  38. data/lib/bolt/puppetdb/client.rb +54 -0
  39. data/lib/bolt/result.rb +5 -14
  40. data/lib/bolt/shell/bash.rb +33 -22
  41. data/lib/bolt/shell/powershell.rb +6 -8
  42. data/lib/bolt/transport/docker.rb +1 -1
  43. data/lib/bolt/transport/docker/connection.rb +21 -32
  44. data/lib/bolt/transport/lxd/connection.rb +5 -5
  45. data/lib/bolt/transport/orch.rb +13 -5
  46. data/lib/bolt/transport/podman.rb +19 -0
  47. data/lib/bolt/transport/podman/connection.rb +98 -0
  48. data/lib/bolt/util.rb +42 -0
  49. data/lib/bolt/version.rb +1 -1
  50. data/lib/bolt_server/transport_app.rb +3 -0
  51. data/lib/bolt_spec/plans/action_stubs/command_stub.rb +8 -1
  52. data/lib/bolt_spec/plans/action_stubs/script_stub.rb +8 -1
  53. data/lib/bolt_spec/plans/mock_executor.rb +91 -11
  54. data/modules/puppet_connect/plans/test_input_data.pp +22 -0
  55. metadata +11 -2
data/lib/bolt/config.rb CHANGED
@@ -169,6 +169,7 @@ module Bolt
169
169
  @config_files = []
170
170
 
171
171
  default_data = {
172
+ 'analytics' => true,
172
173
  'apply-settings' => {},
173
174
  'color' => true,
174
175
  'compile-concurrency' => Etc.nprocessors,
@@ -263,6 +264,8 @@ module Bolt
263
264
  # Disabled warnings are concatenated
264
265
  when 'disable-warnings'
265
266
  val1.concat(val2)
267
+ when 'analytics'
268
+ val1 && val2
266
269
  # All other values are overwritten
267
270
  else
268
271
  val2
@@ -328,13 +331,6 @@ module Bolt
328
331
  end
329
332
 
330
333
  def validate
331
- if @data['future']
332
- Bolt::Logger.warn(
333
- "future_option",
334
- "Configuration option 'future' no longer exposes future behavior."
335
- )
336
- end
337
-
338
334
  if @data['modulepath']&.include?(@project.managed_moduledir.to_s)
339
335
  raise Bolt::ValidationError,
340
336
  "Found invalid path in modulepath: #{@project.managed_moduledir}. This path "\
@@ -395,6 +391,10 @@ module Bolt
395
391
  @data['format'] = value
396
392
  end
397
393
 
394
+ def future
395
+ @data['future']
396
+ end
397
+
398
398
  def trace
399
399
  @data['trace']
400
400
  end
@@ -459,6 +459,10 @@ module Bolt
459
459
  Set.new(@project.disable_warnings + @data['disable-warnings'])
460
460
  end
461
461
 
462
+ def analytics
463
+ @data['analytics']
464
+ end
465
+
462
466
  # Check if there is a case-insensitive match to the path
463
467
  def check_path_case(type, paths)
464
468
  return if paths.nil?
@@ -1,12 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'bolt/config/transport/ssh'
4
- require 'bolt/config/transport/winrm'
5
- require 'bolt/config/transport/orch'
3
+ require 'bolt/config/transport/docker'
6
4
  require 'bolt/config/transport/local'
7
5
  require 'bolt/config/transport/lxd'
8
- require 'bolt/config/transport/docker'
6
+ require 'bolt/config/transport/orch'
7
+ require 'bolt/config/transport/podman'
9
8
  require 'bolt/config/transport/remote'
9
+ require 'bolt/config/transport/ssh'
10
+ require 'bolt/config/transport/winrm'
10
11
 
11
12
  module Bolt
12
13
  class Config
@@ -14,13 +15,14 @@ module Bolt
14
15
  # Transport config classes. Used to load default transport config which
15
16
  # gets passed along to the inventory.
16
17
  TRANSPORT_CONFIG = {
17
- 'ssh' => Bolt::Config::Transport::SSH,
18
- 'winrm' => Bolt::Config::Transport::WinRM,
19
- 'pcp' => Bolt::Config::Transport::Orch,
18
+ 'docker' => Bolt::Config::Transport::Docker,
20
19
  'local' => Bolt::Config::Transport::Local,
21
20
  'lxd' => Bolt::Config::Transport::LXD,
22
- 'docker' => Bolt::Config::Transport::Docker,
23
- 'remote' => Bolt::Config::Transport::Remote
21
+ 'pcp' => Bolt::Config::Transport::Orch,
22
+ 'podman' => Bolt::Config::Transport::Podman,
23
+ 'remote' => Bolt::Config::Transport::Remote,
24
+ 'ssh' => Bolt::Config::Transport::SSH,
25
+ 'winrm' => Bolt::Config::Transport::WinRM
24
26
  }.freeze
25
27
 
26
28
  # Plugin definition. This is used by the JSON schemas to indicate that an option
@@ -55,6 +57,13 @@ module Bolt
55
57
  # Definitions used to validate config options.
56
58
  # https://github.com/puppetlabs/bolt/blob/main/schemas/README.md
57
59
  OPTIONS = {
60
+ "analytics" => {
61
+ description: "Whether to disable analytics. Setting this option to 'false' in the system-wide "\
62
+ "or user-level configuration will disable analytics for all projects, even if this "\
63
+ "option is set to 'true' at the project level.",
64
+ type: [TrueClass, FalseClass],
65
+ _example: false
66
+ },
58
67
  "apply-settings" => {
59
68
  description: "A map of Puppet settings to use when applying Puppet code using the `apply` "\
60
69
  "plan function or the `bolt apply` command.",
@@ -133,6 +142,20 @@ module Bolt
133
142
  _example: "json",
134
143
  _default: "human"
135
144
  },
145
+ "future" => {
146
+ description: "Enable new Bolt features that may include breaking changes.",
147
+ type: Hash,
148
+ properties: {
149
+ "file_paths" => {
150
+ description: "Load scripts from the `scripts/` directory of a module",
151
+ type: [TrueClass, FalseClass],
152
+ _example: true,
153
+ _default: false
154
+ }
155
+ },
156
+ _plugin: false,
157
+ _example: { 'file_paths' => true }
158
+ },
136
159
  "hiera-config" => {
137
160
  description: "The path to the Hiera configuration file.",
138
161
  type: String,
@@ -497,6 +520,12 @@ module Bolt
497
520
  _plugin: true,
498
521
  _example: { "job-poll-interval" => 15, "job-poll-timeout" => 30 }
499
522
  },
523
+ "podman" => {
524
+ description: "A map of configuration options for the podman transport.",
525
+ type: Hash,
526
+ _plugin: true,
527
+ _example: { "cleanup" => false, "tmpdir" => "/mount/tmp" }
528
+ },
500
529
  "remote" => {
501
530
  description: "A map of configuration options for the remote transport.",
502
531
  type: Hash,
@@ -532,6 +561,7 @@ module Bolt
532
561
 
533
562
  # Options that are available in a bolt-defaults.yaml file
534
563
  DEFAULTS_OPTIONS = %w[
564
+ analytics
535
565
  color
536
566
  compile-concurrency
537
567
  concurrency
@@ -551,12 +581,14 @@ module Bolt
551
581
 
552
582
  # Options that are available in a bolt-project.yaml file
553
583
  PROJECT_OPTIONS = %w[
584
+ analytics
554
585
  apply-settings
555
586
  color
556
587
  compile-concurrency
557
588
  concurrency
558
589
  disable-warnings
559
590
  format
591
+ future
560
592
  hiera-config
561
593
  log
562
594
  modulepath
@@ -9,11 +9,13 @@ module Bolt
9
9
  class LXD < Base
10
10
  OPTIONS = %w[
11
11
  cleanup
12
+ remote
12
13
  tmpdir
13
14
  ].freeze
14
15
 
15
16
  DEFAULTS = {
16
- 'cleanup' => true
17
+ 'cleanup' => true,
18
+ 'remote' => 'local'
17
19
  }.freeze
18
20
  end
19
21
  end
@@ -266,6 +266,13 @@ module Bolt
266
266
  _plugin: true,
267
267
  _example: "BOLT.PRODUCTION"
268
268
  },
269
+ "remote" => {
270
+ type: String,
271
+ description: "The LXD remote host to use.",
272
+ _default: "local",
273
+ _plugin: false,
274
+ _example: 'myremote'
275
+ },
269
276
  "run-as" => {
270
277
  type: String,
271
278
  description: "The user to run commands as after login. The run-as user must be different than the "\
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bolt/error'
4
+ require 'bolt/config/transport/base'
5
+
6
+ module Bolt
7
+ class Config
8
+ module Transport
9
+ class Podman < Base
10
+ OPTIONS = %w[
11
+ cleanup
12
+ host
13
+ interpreters
14
+ shell-command
15
+ tmpdir
16
+ tty
17
+ ].freeze
18
+
19
+ DEFAULTS = {
20
+ 'cleanup' => true
21
+ }.freeze
22
+
23
+ private def validate
24
+ super
25
+
26
+ if @config['interpreters']
27
+ @config['interpreters'] = normalize_interpreters(@config['interpreters'])
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'bolt/error'
5
+ require 'bolt/result'
6
+
7
+ module Bolt
8
+ class ContainerResult
9
+ attr_reader :value, :object
10
+
11
+ def self.from_exception(exception, exit_code, image, position: [])
12
+ details = Bolt::Result.create_details(position)
13
+ error = {
14
+ 'kind' => 'puppetlabs.tasks/container-error',
15
+ 'issue_code' => 'CONTAINER_ERROR',
16
+ 'msg' => "Error running container '#{image}': #{exception}",
17
+ 'details' => details
18
+ }
19
+ error['details']['exit_code'] = exit_code
20
+ ContainerResult.new({ '_error' => error }, object: image)
21
+ end
22
+
23
+ def _pcore_init_hash
24
+ { 'value' => @value,
25
+ 'object' => @image }
26
+ end
27
+
28
+ # First argument can't be named given the way that Puppet deserializes variables
29
+ def initialize(value = nil, object: nil)
30
+ @value = value || {}
31
+ @object = object
32
+ end
33
+
34
+ def eql?(other)
35
+ self.class == other.class &&
36
+ value == other.value
37
+ end
38
+ alias == eql?
39
+
40
+ def [](key)
41
+ value[key]
42
+ end
43
+
44
+ def to_json(opts = nil)
45
+ to_data.to_json(opts)
46
+ end
47
+ alias to_s to_json
48
+
49
+ # This is the value with all non-UTF-8 characters removed, suitable for
50
+ # printing or converting to JSON. It *should* only be possible to have
51
+ # non-UTF-8 characters in stdout/stderr keys as they are not allowed from
52
+ # tasks but we scrub the whole thing just in case.
53
+ def safe_value
54
+ Bolt::Util.walk_vals(value) do |val|
55
+ if val.is_a?(String)
56
+ # Replace invalid bytes with hex codes, ie. \xDE\xAD\xBE\xEF
57
+ val.scrub { |c| c.bytes.map { |b| "\\x" + b.to_s(16).upcase }.join }
58
+ else
59
+ val
60
+ end
61
+ end
62
+ end
63
+
64
+ def stdout
65
+ value['stdout']
66
+ end
67
+
68
+ def stderr
69
+ value['stderr']
70
+ end
71
+
72
+ def to_data
73
+ {
74
+ "object" => object,
75
+ "status" => status,
76
+ "value" => safe_value
77
+ }
78
+ end
79
+
80
+ def status
81
+ ok? ? 'success' : 'failure'
82
+ end
83
+
84
+ def ok?
85
+ error_hash.nil?
86
+ end
87
+ alias ok ok?
88
+ alias success? ok?
89
+
90
+ # This allows access to errors outside puppet compilation
91
+ # it should be prefered over error in bolt code
92
+ def error_hash
93
+ value['_error']
94
+ end
95
+
96
+ # Warning: This will fail outside of a compilation.
97
+ # Use error_hash inside bolt.
98
+ # Is it crazy for this to behave differently outside a compiler?
99
+ def error
100
+ if error_hash
101
+ Puppet::DataTypes::Error.from_asserted_hash(error_hash)
102
+ end
103
+ end
104
+ end
105
+ end
data/lib/bolt/error.rb CHANGED
@@ -61,6 +61,21 @@ module Bolt
61
61
  end
62
62
  end
63
63
 
64
+ class ContainerFailure < Bolt::Error
65
+ attr_reader :result
66
+
67
+ def initialize(result)
68
+ details = {
69
+ 'value' => result.value,
70
+ 'object' => result.object
71
+ }
72
+ message = "Plan aborted: Running container '#{result.object}' failed."
73
+ super(message, 'bolt/container-failure', details)
74
+ @result = result
75
+ @error_code = 2
76
+ end
77
+ end
78
+
64
79
  class RunFailure < Bolt::Error
65
80
  attr_reader :result_set
66
81
 
data/lib/bolt/executor.rb CHANGED
@@ -12,34 +12,37 @@ require 'bolt/config'
12
12
  require 'bolt/result_set'
13
13
  require 'bolt/puppetdb'
14
14
  # Load transports
15
- require 'bolt/transport/ssh'
16
- require 'bolt/transport/winrm'
17
- require 'bolt/transport/orch'
15
+ require 'bolt/transport/docker'
18
16
  require 'bolt/transport/local'
19
17
  require 'bolt/transport/lxd'
20
- require 'bolt/transport/docker'
18
+ require 'bolt/transport/orch'
19
+ require 'bolt/transport/podman'
21
20
  require 'bolt/transport/remote'
21
+ require 'bolt/transport/ssh'
22
+ require 'bolt/transport/winrm'
22
23
  require 'bolt/yarn'
23
24
 
24
25
  module Bolt
25
26
  TRANSPORTS = {
26
- ssh: Bolt::Transport::SSH,
27
- winrm: Bolt::Transport::WinRM,
28
- pcp: Bolt::Transport::Orch,
27
+ docker: Bolt::Transport::Docker,
29
28
  local: Bolt::Transport::Local,
30
29
  lxd: Bolt::Transport::LXD,
31
- docker: Bolt::Transport::Docker,
32
- remote: Bolt::Transport::Remote
30
+ pcp: Bolt::Transport::Orch,
31
+ podman: Bolt::Transport::Podman,
32
+ remote: Bolt::Transport::Remote,
33
+ ssh: Bolt::Transport::SSH,
34
+ winrm: Bolt::Transport::WinRM
33
35
  }.freeze
34
36
 
35
37
  class Executor
36
- attr_reader :noop, :transports, :in_parallel
38
+ attr_reader :noop, :transports, :in_parallel, :future
37
39
  attr_accessor :run_as
38
40
 
39
41
  def initialize(concurrency = 1,
40
42
  analytics = Bolt::Analytics::NoopClient.new,
41
43
  noop = false,
42
- modified_concurrency = false)
44
+ modified_concurrency = false,
45
+ future = {})
43
46
  # lazy-load expensive gem code
44
47
  require 'concurrent'
45
48
  @analytics = analytics
@@ -64,6 +67,7 @@ module Bolt
64
67
  @noop = noop
65
68
  @run_as = nil
66
69
  @in_parallel = false
70
+ @future = future
67
71
  @pool = if concurrency > 0
68
72
  Concurrent::ThreadPoolExecutor.new(name: 'exec', max_threads: concurrency)
69
73
  else
@@ -217,7 +221,7 @@ module Bolt
217
221
  results
218
222
  end
219
223
 
220
- def report_transport(transport, count)
224
+ private def report_transport(transport, count)
221
225
  name = transport.class.name.split('::').last.downcase
222
226
  unless @reported_transports.include?(name)
223
227
  @analytics&.event('Transport', 'initialize', label: name, value: count)
@@ -475,7 +479,7 @@ module Bolt
475
479
  Time.now
476
480
  end
477
481
 
478
- def wait_until(timeout, retry_interval)
482
+ private def wait_until(timeout, retry_interval)
479
483
  start = wait_now
480
484
  until yield
481
485
  raise(TimeoutError, 'Timed out waiting for target') if (wait_now - start).to_i >= timeout
@@ -484,22 +488,30 @@ module Bolt
484
488
  end
485
489
 
486
490
  def prompt(prompt, options)
487
- @prompting = true
488
491
  unless $stdin.tty?
492
+ return options[:default] if options[:default]
489
493
  raise Bolt::Error.new('STDIN is not a tty, unable to prompt', 'bolt/no-tty-error')
490
494
  end
491
495
 
492
- $stderr.print("#{prompt}: ")
496
+ @prompting = true
497
+
498
+ if options[:default] && !options[:sensitive]
499
+ $stderr.print("#{prompt} [#{options[:default]}]: ")
500
+ else
501
+ $stderr.print("#{prompt}: ")
502
+ end
493
503
 
494
504
  value = if options[:sensitive]
495
505
  $stdin.noecho(&:gets).to_s.chomp
496
506
  else
497
507
  $stdin.gets.to_s.chomp
498
508
  end
509
+
499
510
  @prompting = false
500
511
 
501
512
  $stderr.puts if options[:sensitive]
502
513
 
514
+ value = options[:default] if value.empty?
503
515
  value
504
516
  end
505
517