bolt 3.0.0 → 3.5.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 (76) hide show
  1. checksums.yaml +4 -4
  2. data/Puppetfile +13 -11
  3. data/bolt-modules/boltlib/lib/puppet/datatypes/containerresult.rb +24 -0
  4. data/bolt-modules/boltlib/lib/puppet/functions/add_facts.rb +1 -1
  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_plan.rb +2 -2
  8. data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +44 -5
  9. data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +1 -1
  10. data/bolt-modules/boltlib/types/planresult.pp +1 -0
  11. data/bolt-modules/file/lib/puppet/functions/file/read.rb +3 -2
  12. data/bolt-modules/prompt/lib/puppet/functions/prompt.rb +20 -2
  13. data/bolt-modules/prompt/lib/puppet/functions/prompt/menu.rb +103 -0
  14. data/lib/bolt/apply_result.rb +1 -1
  15. data/lib/bolt/bolt_option_parser.rb +6 -3
  16. data/lib/bolt/cli.rb +96 -16
  17. data/lib/bolt/config.rb +4 -0
  18. data/lib/bolt/config/options.rb +21 -3
  19. data/lib/bolt/config/transport/lxd.rb +23 -0
  20. data/lib/bolt/config/transport/options.rb +8 -1
  21. data/lib/bolt/container_result.rb +105 -0
  22. data/lib/bolt/error.rb +15 -0
  23. data/lib/bolt/executor.rb +22 -7
  24. data/lib/bolt/inventory/options.rb +9 -0
  25. data/lib/bolt/inventory/target.rb +16 -0
  26. data/lib/bolt/logger.rb +8 -0
  27. data/lib/bolt/module_installer.rb +2 -2
  28. data/lib/bolt/module_installer/puppetfile.rb +2 -2
  29. data/lib/bolt/module_installer/specs/forge_spec.rb +2 -2
  30. data/lib/bolt/module_installer/specs/git_spec.rb +2 -2
  31. data/lib/bolt/node/output.rb +14 -4
  32. data/lib/bolt/outputter/human.rb +106 -23
  33. data/lib/bolt/outputter/logger.rb +17 -0
  34. data/lib/bolt/pal.rb +25 -4
  35. data/lib/bolt/pal/yaml_plan.rb +1 -2
  36. data/lib/bolt/pal/yaml_plan/evaluator.rb +5 -141
  37. data/lib/bolt/pal/yaml_plan/step.rb +91 -31
  38. data/lib/bolt/pal/yaml_plan/step/command.rb +21 -13
  39. data/lib/bolt/pal/yaml_plan/step/download.rb +15 -16
  40. data/lib/bolt/pal/yaml_plan/step/eval.rb +11 -11
  41. data/lib/bolt/pal/yaml_plan/step/message.rb +13 -4
  42. data/lib/bolt/pal/yaml_plan/step/plan.rb +19 -15
  43. data/lib/bolt/pal/yaml_plan/step/resources.rb +82 -21
  44. data/lib/bolt/pal/yaml_plan/step/script.rb +36 -17
  45. data/lib/bolt/pal/yaml_plan/step/task.rb +19 -16
  46. data/lib/bolt/pal/yaml_plan/step/upload.rb +16 -17
  47. data/lib/bolt/pal/yaml_plan/transpiler.rb +3 -3
  48. data/lib/bolt/plan_creator.rb +1 -1
  49. data/lib/bolt/project_manager.rb +1 -1
  50. data/lib/bolt/project_manager/module_migrator.rb +1 -1
  51. data/lib/bolt/result.rb +11 -15
  52. data/lib/bolt/shell.rb +16 -0
  53. data/lib/bolt/shell/bash.rb +61 -31
  54. data/lib/bolt/shell/bash/tmpdir.rb +2 -2
  55. data/lib/bolt/shell/powershell.rb +34 -12
  56. data/lib/bolt/shell/powershell/snippets.rb +30 -3
  57. data/lib/bolt/task.rb +1 -1
  58. data/lib/bolt/transport/base.rb +0 -9
  59. data/lib/bolt/transport/docker.rb +1 -125
  60. data/lib/bolt/transport/docker/connection.rb +77 -167
  61. data/lib/bolt/transport/lxd.rb +26 -0
  62. data/lib/bolt/transport/lxd/connection.rb +99 -0
  63. data/lib/bolt/transport/orch.rb +13 -5
  64. data/lib/bolt/transport/ssh/connection.rb +1 -1
  65. data/lib/bolt/transport/winrm/connection.rb +1 -1
  66. data/lib/bolt/util.rb +31 -0
  67. data/lib/bolt/version.rb +1 -1
  68. data/lib/bolt_server/transport_app.rb +61 -33
  69. data/lib/bolt_spec/bolt_context.rb +9 -4
  70. data/lib/bolt_spec/plans.rb +1 -109
  71. data/lib/bolt_spec/plans/action_stubs.rb +1 -1
  72. data/lib/bolt_spec/plans/action_stubs/command_stub.rb +8 -1
  73. data/lib/bolt_spec/plans/action_stubs/script_stub.rb +8 -1
  74. data/lib/bolt_spec/plans/mock_executor.rb +90 -7
  75. data/modules/puppet_connect/plans/test_input_data.pp +65 -7
  76. metadata +9 -2
@@ -177,7 +177,7 @@ module BoltSpec
177
177
  if data['msg'] && data['kind'] && (data.keys - %w[msg kind details issue_code]).empty?
178
178
  @data[:default] = clazz.new(data['msg'], data['kind'], data['details'], data['issue_code'])
179
179
  else
180
- $stderr.puts "In the future 'error_with()' may require msg and kind, and " \
180
+ $stderr.puts "In the future 'error_with()' might require msg and kind, and " \
181
181
  "optionally accept only details and issue_code."
182
182
  @data[:default] = data
183
183
  end
@@ -29,7 +29,14 @@ module BoltSpec
29
29
  end
30
30
 
31
31
  def result_for(target, stdout: '', stderr: '')
32
- Bolt::Result.for_command(target, stdout, stderr, 0, 'command', '', [])
32
+ value = {
33
+ 'stdout' => stdout,
34
+ 'stderr' => stderr,
35
+ 'merged_output' => "#{stdout}\n#{stderr}".strip,
36
+ 'exit_code' => 0
37
+ }
38
+
39
+ Bolt::Result.for_command(target, value, 'command', '', [])
33
40
  end
34
41
 
35
42
  # Public methods
@@ -35,7 +35,14 @@ module BoltSpec
35
35
  end
36
36
 
37
37
  def result_for(target, stdout: '', stderr: '')
38
- Bolt::Result.for_command(target, stdout, stderr, 0, 'script', '', [])
38
+ value = {
39
+ 'stdout' => stdout,
40
+ 'stderr' => stderr,
41
+ 'merged_output' => "#{stdout}\n#{stderr}".strip,
42
+ 'exit_code' => 0
43
+ }
44
+
45
+ Bolt::Result.for_command(target, value, 'script', '', [])
39
46
  end
40
47
 
41
48
  # Public methods
@@ -17,7 +17,7 @@ module BoltSpec
17
17
 
18
18
  # Nothing on the executor is 'public'
19
19
  class MockExecutor
20
- attr_reader :noop, :error_message, :in_parallel
20
+ attr_reader :noop, :error_message, :in_parallel, :transports
21
21
  attr_accessor :run_as, :transport_features, :execute_any_plan
22
22
 
23
23
  def initialize(modulepath)
@@ -91,6 +91,14 @@ module BoltSpec
91
91
  result
92
92
  end
93
93
 
94
+ def run_task_with(target_mapping, task, options = {}, _position = [])
95
+ resultsets = target_mapping.map do |target, arguments|
96
+ run_task([target], task, arguments, options)
97
+ end.compact
98
+
99
+ Bolt::ResultSet.new(resultsets.map(&:results).flatten)
100
+ end
101
+
94
102
  def download_file(targets, source, destination, options = {}, _position = [])
95
103
  result = nil
96
104
  if (doub = @download_doubles[source] || @download_doubles[:default])
@@ -210,12 +218,6 @@ module BoltSpec
210
218
  yield
211
219
  end
212
220
 
213
- def report_function_call(_function); end
214
-
215
- def report_bundled_content(_mode, _name); end
216
-
217
- def report_apply(_statements, _resources); end
218
-
219
221
  def publish_event(event)
220
222
  if event[:type] == :message
221
223
  unless @stub_out_message
@@ -253,6 +255,87 @@ module BoltSpec
253
255
  end.new(transport_features)
254
256
  end
255
257
  # End apply_prep mocking
258
+
259
+ # Evaluates a `parallelize()` block and returns the result. Normally,
260
+ # Bolt's executor wraps this in a Yarn for each object passed to the
261
+ # `parallelize()` function, and then executes them in parallel before
262
+ # returning the result from the block. However, in BoltSpec the block is
263
+ # executed for each object sequentially, and this function returns the
264
+ # result itself.
265
+ #
266
+ def create_yarn(scope, block, object, _index)
267
+ # Create the new scope
268
+ newscope = Puppet::Parser::Scope.new(scope.compiler)
269
+ local = Puppet::Parser::Scope::LocalScope.new
270
+
271
+ # Compress the current scopes into a single vars hash to add to the new scope
272
+ current_scope = scope.effective_symtable(true)
273
+ until current_scope.nil?
274
+ current_scope.instance_variable_get(:@symbols)&.each_pair { |k, v| local[k] = v }
275
+ current_scope = current_scope.parent
276
+ end
277
+ newscope.push_ephemerals([local])
278
+
279
+ begin
280
+ result = catch(:return) do
281
+ args = { block.parameters[0][1].to_s => object }
282
+ block.closure.call_by_name_with_scope(newscope, args, true)
283
+ end
284
+
285
+ # If we got a return from the block, get it's value
286
+ # Otherwise the result is the last line from the block
287
+ result = result.value if result.is_a?(Puppet::Pops::Evaluator::Return)
288
+
289
+ # Validate the result is a PlanResult
290
+ unless Puppet::Pops::Types::TypeParser.singleton.parse('Boltlib::PlanResult').instance?(result)
291
+ raise Bolt::InvalidParallelResult.new(result.to_s, *Puppet::Pops::PuppetStack.top_of_stack)
292
+ end
293
+
294
+ result
295
+ rescue Puppet::PreformattedError => e
296
+ if e.cause.is_a?(Bolt::Error)
297
+ e.cause
298
+ else
299
+ raise e
300
+ end
301
+ end
302
+ end
303
+
304
+ # BoltSpec already evaluated the `parallelize()` block for each object
305
+ # passed to the function, so these results can be returned as-is.
306
+ #
307
+ def round_robin(results)
308
+ results
309
+ end
310
+
311
+ # Public methods on Bolt::Executor that need to be mocked so there aren't
312
+ # "undefined method" errors.
313
+
314
+ def batch_execute(_targets); end
315
+
316
+ def finish_plan(_plan_result); end
317
+
318
+ def handle_event(_event); end
319
+
320
+ def prompt(_prompt, _options); end
321
+
322
+ def report_function_call(_function); end
323
+
324
+ def report_bundled_content(_mode, _name); end
325
+
326
+ def report_file_source(_plan_function, _source); end
327
+
328
+ def report_apply(_statements, _resources); end
329
+
330
+ def report_yaml_plan(_plan); end
331
+
332
+ def shutdown; end
333
+
334
+ def start_plan(_plan_context); end
335
+
336
+ def subscribe(_subscriber, _types = nil); end
337
+
338
+ def unsubscribe(_subscriber, _types = nil); end
256
339
  end
257
340
  end
258
341
  end
@@ -13,15 +13,73 @@
13
13
  #
14
14
  plan puppet_connect::test_input_data(TargetSpec $targets = 'all') {
15
15
  $targs = get_targets($targets)
16
+ $unique_plugins = $targs.group_by |$t| {$t.plugin_hooks['puppet_library']}
17
+ if ($unique_plugins.keys.length > 1) {
18
+ out::message('Multiple puppet_library plugin hooks detected')
19
+ $unique_plugins.each |$plug, $target_list| {
20
+ $target_message = if ($target_list.length > 10) {
21
+ "${target_list.length} targets"
22
+ } else {
23
+ $target_list.join(', ')
24
+ }
25
+ out::message("Plugin hook ${plug} configured for ${target_message}")
26
+ }
27
+ fail_plan("The puppet_library plugin config must be the same across all targets")
28
+ }
16
29
  $targs.each |$target| {
17
- if $target.transport != 'ssh' and $target.transport != 'winrm' {
18
- fail_plan("Inventory contains target ${target} with unsupported transport, must be ssh or winrm")
30
+ case $target.transport {
31
+ 'ssh': {
32
+ $private_key_config = dig($target.config, 'ssh', 'private-key')
33
+ if $private_key_config =~ String {
34
+ $msg = @("END")
35
+ The SSH private key of the ${$target} target points to a filepath on disk,
36
+ which is not allowed in Puppet Connect. Instead, the private key contents must
37
+ be specified and this should be done via the PuppetConnectData plugin. Below is
38
+ an example of a Puppet Connect-compatible specification of the private-key. First,
39
+ we start with the inventory file:
40
+ ...
41
+ private-key:
42
+ _plugin: puppet_connect_data
43
+ key: ssh_private_key
44
+ ...
45
+
46
+ Next is the corresponding entry in the input data file:
47
+ ...
48
+ ssh_private_key:
49
+ key-data:
50
+ <private_key_contents>
51
+ ...
52
+ | END
53
+
54
+ out::message($msg)
55
+ fail_plan("The SSH private key of the ${$target} target points to a filepath on disk")
56
+ }
57
+
58
+ # Disable SSH autoloading to prevent false positive results
59
+ # (input data is wrong but target is still connectable due
60
+ # to autoloaded config)
61
+ set_config($target, ['ssh', 'load-config'], false)
62
+ # Maintain configuration parity with Puppet Connect to improve
63
+ # the reliability of our test
64
+ set_config($target, ['ssh', 'host-key-check'], false)
65
+ }
66
+ 'winrm': {
67
+ # Maintain configuration parity with Puppet Connect
68
+ set_config($target, ['winrm', 'ssl'], false)
69
+ set_config($target, ['winrm', 'ssl-verify'], false)
70
+ }
71
+ default: {
72
+ fail_plan("Inventory contains target ${target} with unsupported transport, must be ssh or winrm")
73
+ }
19
74
  }
20
- if $target.transport == 'ssh' {
21
- # Disable SSH autoloading to prevent false positive results
22
- # (input data is wrong but target is still connectable due
23
- # to autoloaded config)
24
- set_config($target, ['ssh', 'load-config'], false)
75
+
76
+ # Bolt defaults to using the "module" based form of the puppet_agent plugin. Connect defaults
77
+ # to using the "task" based form as *only* the task based form in supported in Connect. This check
78
+ # ensures that if the default is not being used, only task based plugins are allowed.
79
+ $plugin = $target.plugin_hooks["puppet_library"]
80
+ $user_configured_plugin = $plugin != { "plugin"=> "puppet_agent", "stop_service"=> true }
81
+ if ($user_configured_plugin and $plugin["plugin"] != "task"){
82
+ fail_plan("Only task plugins are acceptable for puppet_library hook")
25
83
  }
26
84
  }
27
85
  # The SSH/WinRM transports will report an 'unknown host' error for targets where
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bolt
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 3.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-02-04 00:00:00.000000000 Z
11
+ date: 2021-03-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -398,6 +398,7 @@ extra_rdoc_files: []
398
398
  files:
399
399
  - Puppetfile
400
400
  - bolt-modules/boltlib/lib/puppet/datatypes/applyresult.rb
401
+ - bolt-modules/boltlib/lib/puppet/datatypes/containerresult.rb
401
402
  - bolt-modules/boltlib/lib/puppet/datatypes/resourceinstance.rb
402
403
  - bolt-modules/boltlib/lib/puppet/datatypes/result.rb
403
404
  - bolt-modules/boltlib/lib/puppet/datatypes/resultset.rb
@@ -419,6 +420,7 @@ files:
419
420
  - bolt-modules/boltlib/lib/puppet/functions/resolve_references.rb
420
421
  - bolt-modules/boltlib/lib/puppet/functions/resource.rb
421
422
  - bolt-modules/boltlib/lib/puppet/functions/run_command.rb
423
+ - bolt-modules/boltlib/lib/puppet/functions/run_container.rb
422
424
  - bolt-modules/boltlib/lib/puppet/functions/run_plan.rb
423
425
  - bolt-modules/boltlib/lib/puppet/functions/run_script.rb
424
426
  - bolt-modules/boltlib/lib/puppet/functions/run_task.rb
@@ -444,6 +446,7 @@ files:
444
446
  - bolt-modules/file/lib/puppet/functions/file/write.rb
445
447
  - bolt-modules/out/lib/puppet/functions/out/message.rb
446
448
  - bolt-modules/prompt/lib/puppet/functions/prompt.rb
449
+ - bolt-modules/prompt/lib/puppet/functions/prompt/menu.rb
447
450
  - bolt-modules/system/lib/puppet/functions/system/env.rb
448
451
  - exe/bolt
449
452
  - guides/inventory.txt
@@ -467,11 +470,13 @@ files:
467
470
  - lib/bolt/config/transport/base.rb
468
471
  - lib/bolt/config/transport/docker.rb
469
472
  - lib/bolt/config/transport/local.rb
473
+ - lib/bolt/config/transport/lxd.rb
470
474
  - lib/bolt/config/transport/options.rb
471
475
  - lib/bolt/config/transport/orch.rb
472
476
  - lib/bolt/config/transport/remote.rb
473
477
  - lib/bolt/config/transport/ssh.rb
474
478
  - lib/bolt/config/transport/winrm.rb
479
+ - lib/bolt/container_result.rb
475
480
  - lib/bolt/error.rb
476
481
  - lib/bolt/executor.rb
477
482
  - lib/bolt/inventory.rb
@@ -555,6 +560,8 @@ files:
555
560
  - lib/bolt/transport/docker/connection.rb
556
561
  - lib/bolt/transport/local.rb
557
562
  - lib/bolt/transport/local/connection.rb
563
+ - lib/bolt/transport/lxd.rb
564
+ - lib/bolt/transport/lxd/connection.rb
558
565
  - lib/bolt/transport/orch.rb
559
566
  - lib/bolt/transport/orch/connection.rb
560
567
  - lib/bolt/transport/remote.rb