bolt 3.15.0 → 3.16.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.

@@ -52,7 +52,7 @@ module Bolt
52
52
  # tracking which Futures to wait on when `wait()` is called without
53
53
  # arguments.
54
54
  @id += 1
55
- future = Bolt::PlanFuture.new(future, @id, name: name, plan_id: plan_id)
55
+ future = Bolt::PlanFuture.new(future, @id, name: name, plan_id: plan_id, scope: newscope)
56
56
  @logger.trace("Created future #{future.name}")
57
57
 
58
58
  # Register the PlanFuture with the FiberExecutor to be executed
@@ -68,11 +68,15 @@ module Bolt
68
68
  #
69
69
  def round_robin
70
70
  active_futures.each do |future|
71
- # If the Fiber is still running and can be resumed, then resume it
71
+ # If the Fiber is still running and can be resumed, then resume it.
72
+ # Override Puppet's global_scope to prevent ephemerals in other scopes
73
+ # from being popped off in the wrong order due to race conditions.
74
+ # This primarily happens when running executor functions from custom
75
+ # Puppet language functions, but may happen elsewhere.
72
76
  @logger.trace("Checking future '#{future.name}'")
73
77
  if future.alive?
74
78
  @logger.trace("Resuming future '#{future.name}'")
75
- future.resume
79
+ Puppet.override(global_scope: future.scope) { future.resume }
76
80
  end
77
81
 
78
82
  # Once we've restarted the Fiber, check to see if it's finished again
@@ -310,7 +310,12 @@ module Bolt
310
310
  )
311
311
  end
312
312
 
313
- def print_tasks(tasks, modulepath)
313
+ # List available tasks.
314
+ #
315
+ # @param tasks [Array] A list of task names and descriptions.
316
+ # @param modulepath [Array] The modulepath.
317
+ #
318
+ def print_tasks(tasks:, modulepath:)
314
319
  command = Bolt::Util.powershell? ? 'Get-BoltTask -Name <TASK NAME>' : 'bolt task show <TASK NAME>'
315
320
 
316
321
  tasks = tasks.map do |name, description|
@@ -330,8 +335,11 @@ module Bolt
330
335
  @stream.puts indent(2, "Use '#{command}' to view details and parameters for a specific task.")
331
336
  end
332
337
 
333
- # @param [Hash] task A hash representing the task
334
- def print_task_info(task)
338
+ # Print information about a task.
339
+ #
340
+ # @param task [Bolt::Task] The task information.
341
+ #
342
+ def print_task_info(task:)
335
343
  params = (task.parameters || []).sort
336
344
 
337
345
  info = +''
@@ -443,7 +451,7 @@ module Bolt
443
451
  @stream.puts info
444
452
  end
445
453
 
446
- def print_plans(plans, modulepath)
454
+ def print_plans(plans:, modulepath:)
447
455
  command = Bolt::Util.powershell? ? 'Get-BoltPlan -Name <PLAN NAME>' : 'bolt plan show <PLAN NAME>'
448
456
 
449
457
  plans = plans.map do |name, description|
@@ -463,7 +471,11 @@ module Bolt
463
471
  @stream.puts indent(2, "Use '#{command}' to view details and parameters for a specific plan.")
464
472
  end
465
473
 
466
- def print_topics(topics)
474
+ # Print available guide topics.
475
+ #
476
+ # @param topics [Array] The available topics.
477
+ #
478
+ def print_topics(topics:, **_kwargs)
467
479
  info = +"#{colorize(:cyan, 'Topics')}\n"
468
480
  info << indent(2, topics.join("\n"))
469
481
  info << "\n\n#{colorize(:cyan, 'Additional information')}\n"
@@ -471,7 +483,11 @@ module Bolt
471
483
  @stream.puts info
472
484
  end
473
485
 
474
- def print_guide(topic:, guide:, documentation: nil)
486
+ # Print the guide for the specified topic.
487
+ #
488
+ # @param guide [String] The guide.
489
+ #
490
+ def print_guide(topic:, guide:, documentation: nil, **_kwargs)
475
491
  info = +"#{colorize(:cyan, topic)}\n"
476
492
  info << indent(2, guide)
477
493
 
@@ -595,17 +611,17 @@ module Bolt
595
611
  @stream.puts info
596
612
  end
597
613
 
598
- def print_plugin_list(plugin_list, modulepath)
614
+ def print_plugin_list(plugins:, modulepath:)
599
615
  info = +''
600
- length = plugin_list.values.map(&:keys).flatten.map(&:length).max + 4
616
+ length = plugins.values.map(&:keys).flatten.map(&:length).max + 4
601
617
 
602
- plugin_list.each do |hook, plugins|
603
- next if plugins.empty?
618
+ plugins.each do |hook, plugin|
619
+ next if plugin.empty?
604
620
  next if hook == :validate_resolve_reference
605
621
 
606
622
  info << colorize(:cyan, "#{hook}\n")
607
623
 
608
- plugins.each do |name, description|
624
+ plugin.each do |name, description|
609
625
  info << indent(2, name.ljust(length))
610
626
  info << truncate(description, 80 - length) if description
611
627
  info << "\n"
@@ -623,12 +639,37 @@ module Bolt
623
639
  @stream.puts info.chomp
624
640
  end
625
641
 
626
- def print_targets(target_list, inventory_source, default_inventory, target_flag)
627
- adhoc = colorize(:yellow, "(Not found in inventory file)")
642
+ def print_new_plan(name:, path:)
643
+ if Bolt::Util.powershell?
644
+ show_command = 'Get-BoltPlan -Name '
645
+ run_command = 'Invoke-BoltPlan -Name '
646
+ else
647
+ show_command = 'bolt plan show'
648
+ run_command = 'bolt plan run'
649
+ end
650
+
651
+ print_message(<<~OUTPUT)
652
+ Created plan '#{name}' at '#{path}'
653
+
654
+ Show this plan with:
655
+ #{show_command} #{name}
656
+ Run this plan with:
657
+ #{run_command} #{name}
658
+ OUTPUT
659
+ end
660
+
661
+ # Print target names and where they came from.
662
+ #
663
+ # @param adhoc [Hash] Adhoc targets provided on the command line.
664
+ # @param inventory [Hash] Targets provided from the inventory.
665
+ # @param flag [Boolean] Whether a targeting command-line option was used.
666
+ #
667
+ def print_targets(adhoc:, inventory:, flag:, **_kwargs)
668
+ adhoc_text = colorize(:yellow, "(Not found in inventory file)")
628
669
 
629
670
  targets = []
630
- targets += target_list[:inventory].map { |target| [target.name, nil] }
631
- targets += target_list[:adhoc].map { |target| [target.name, adhoc] }
671
+ targets += inventory[:targets].map { |target| [target['name'], nil] }
672
+ targets += adhoc[:targets].map { |target| [target['name'], adhoc_text] }
632
673
 
633
674
  info = +''
634
675
 
@@ -641,27 +682,31 @@ module Bolt
641
682
  end
642
683
  info << "\n\n"
643
684
 
644
- info << format_inventory_source(inventory_source, default_inventory)
645
- info << format_target_summary(target_list[:inventory].count, target_list[:adhoc].count, target_flag, false)
685
+ info << format_inventory_source(inventory[:file], inventory[:default])
686
+ info << format_target_summary(inventory[:count], adhoc[:count], flag, false)
646
687
 
647
688
  @stream.puts info
648
689
  end
649
690
 
650
- def print_target_info(target_list, inventory_source, default_inventory, target_flag)
651
- adhoc_targets = target_list[:adhoc].map(&:name).to_set
652
- inventory_targets = target_list[:inventory].map(&:name).to_set
653
- targets = target_list.values.flatten.sort_by(&:name)
691
+ # Print detailed target information.
692
+ #
693
+ # @param adhoc [Hash] Adhoc targets provided on the command line.
694
+ # @param inventory [Hash] Targets provided from the inventory.
695
+ # @param flag [Boolean] Whether a targeting command-line option was used.
696
+ #
697
+ def print_target_info(adhoc:, inventory:, flag:, **_kwargs)
698
+ targets = (adhoc[:targets] + inventory[:targets]).sort_by { |t| t['name'] }
654
699
 
655
700
  info = +''
656
701
 
657
702
  if targets.any?
658
- adhoc = colorize(:yellow, " (Not found in inventory file)")
703
+ adhoc_text = colorize(:yellow, " (Not found in inventory file)")
659
704
 
660
705
  targets.each do |target|
661
- info << colorize(:cyan, target.name)
662
- info << adhoc if adhoc_targets.include?(target.name)
706
+ info << colorize(:cyan, target['name'])
707
+ info << adhoc_text if adhoc[:targets].include?(target)
663
708
  info << "\n"
664
- info << indent(2, target.detail.to_yaml.lines.drop(1).join)
709
+ info << indent(2, target.to_yaml.lines.drop(1).join)
665
710
  info << "\n"
666
711
  end
667
712
  else
@@ -669,8 +714,8 @@ module Bolt
669
714
  info << indent(2, "No targets\n\n")
670
715
  end
671
716
 
672
- info << format_inventory_source(inventory_source, default_inventory)
673
- info << format_target_summary(inventory_targets.count, adhoc_targets.count, target_flag, true)
717
+ info << format_inventory_source(inventory[:file], inventory[:default])
718
+ info << format_target_summary(inventory[:count], adhoc[:count], flag, true)
674
719
 
675
720
  @stream.puts info
676
721
  end
@@ -716,7 +761,13 @@ module Bolt
716
761
  info
717
762
  end
718
763
 
719
- def print_groups(groups, inventory_source, default_inventory)
764
+ # Print inventory group information.
765
+ #
766
+ # @param count [Integer] Number of groups in the inventory.
767
+ # @param groups [Array] Names of groups in the inventory.
768
+ # @param inventory [Hash] Where the inventory was loaded from.
769
+ #
770
+ def print_groups(count:, groups:, inventory:)
720
771
  info = +''
721
772
 
722
773
  # Add group list
@@ -725,18 +776,18 @@ module Bolt
725
776
  info << "\n\n"
726
777
 
727
778
  # Add inventory file source
728
- info << format_inventory_source(inventory_source, default_inventory)
779
+ info << format_inventory_source(inventory[:source], inventory[:default])
729
780
 
730
781
  # Add group count summary
731
782
  info << colorize(:cyan, "Group count\n")
732
- info << indent(2, "#{groups.count} total")
783
+ info << indent(2, "#{count} total")
733
784
 
734
785
  @stream.puts info
735
786
  end
736
787
 
737
788
  # @param [Bolt::ResultSet] apply_result A ResultSet object representing the result of a `bolt apply`
738
- def print_apply_result(apply_result, elapsed_time)
739
- print_summary(apply_result, elapsed_time)
789
+ def print_apply_result(apply_result)
790
+ print_summary(apply_result, apply_result.elapsed_time)
740
791
  end
741
792
 
742
793
  # @param [Bolt::PlanResult] plan_result A PlanResult object
@@ -49,7 +49,11 @@ module Bolt
49
49
  alias print_module_list print_table
50
50
  alias print_module_info print_table
51
51
 
52
- def print_task_info(task)
52
+ # Print information about a task.
53
+ #
54
+ # @param task [Bolt::Task] The task information.
55
+ #
56
+ def print_task_info(task:)
53
57
  path = task.files.first['path'].chomp("/tasks/#{task.files.first['name']}")
54
58
  module_dir = if path.start_with?(Bolt::Config::Modulepath::MODULES_PATH)
55
59
  "built-in module"
@@ -59,11 +63,16 @@ module Bolt
59
63
  @stream.puts task.to_h.merge(module_dir: module_dir).to_json
60
64
  end
61
65
 
62
- def print_tasks(tasks, modulepath)
63
- print_table('tasks' => tasks, 'modulepath' => modulepath)
66
+ # List available tasks.
67
+ #
68
+ # @param tasks [Array] A list of task names and descriptions.
69
+ # @param modulepath [Array] The modulepath.
70
+ #
71
+ def print_tasks(**kwargs)
72
+ print_table(**kwargs)
64
73
  end
65
74
 
66
- def print_plugin_list(plugins, modulepath)
75
+ def print_plugin_list(plugins:, modulepath:)
67
76
  plugins.delete(:validate_resolve_reference)
68
77
  print_table('plugins' => plugins, 'modulepath' => modulepath)
69
78
  end
@@ -78,11 +87,15 @@ module Bolt
78
87
  @stream.puts plan.to_json
79
88
  end
80
89
 
81
- def print_plans(plans, modulepath)
82
- print_table('plans' => plans, 'modulepath' => modulepath)
90
+ def print_plans(**kwargs)
91
+ print_table(**kwargs)
83
92
  end
84
93
 
85
- def print_apply_result(apply_result, _elapsed_time)
94
+ def print_new_plan(**kwargs)
95
+ print_table(**kwargs)
96
+ end
97
+
98
+ def print_apply_result(apply_result)
86
99
  @stream.puts apply_result.to_json
87
100
  end
88
101
 
@@ -95,10 +108,19 @@ module Bolt
95
108
  @stream.puts result_set.to_json
96
109
  end
97
110
 
98
- def print_topics(topics)
99
- print_table('topics' => topics)
111
+ # Print available guide topics.
112
+ #
113
+ # @param topics [Array] The available topics.
114
+ #
115
+ def print_topics(**kwargs)
116
+ print_table(kwargs)
100
117
  end
101
118
 
119
+ # Print the guide for the specified topic.
120
+ #
121
+ # @param guide [String] The guide.
122
+ # @param topic [String] The topic.
123
+ #
102
124
  def print_guide(**kwargs)
103
125
  @stream.puts(kwargs.to_json)
104
126
  end
@@ -113,35 +135,38 @@ module Bolt
113
135
  moduledir: moduledir.to_s }.to_json)
114
136
  end
115
137
 
116
- def print_targets(target_list, inventory_source, default_inventory, _target_flag)
117
- @stream.puts ::JSON.pretty_generate(
118
- inventory: {
119
- targets: target_list[:inventory].map(&:name),
120
- count: target_list[:inventory].count,
121
- file: (inventory_source || default_inventory).to_s
122
- },
123
- adhoc: {
124
- targets: target_list[:adhoc].map(&:name),
125
- count: target_list[:adhoc].count
126
- },
127
- targets: target_list.values.flatten.map(&:name),
128
- count: target_list.values.flatten.count
129
- )
130
- end
131
-
132
- def print_target_info(target_list, _inventory_source, _default_inventory, _target_flag)
133
- targets = target_list.values.flatten
134
-
135
- @stream.puts ::JSON.pretty_generate(
136
- targets: targets.map(&:detail),
137
- count: targets.count
138
- )
139
- end
140
-
141
- def print_groups(groups, _inventory_source, _default_inventory)
142
- count = groups.count
143
- @stream.puts({ groups: groups,
144
- count: count }.to_json)
138
+ # Print target names and where they came from.
139
+ #
140
+ # @param adhoc [Hash] Adhoc targets provided on the command line.
141
+ # @param inventory [Hash] Targets provided from the inventory.
142
+ # @param targets [Array] All targets.
143
+ # @param count [Integer] Number of targets.
144
+ #
145
+ def print_targets(adhoc:, inventory:, targets:, count:, **_kwargs)
146
+ adhoc[:targets] = adhoc[:targets].map { |t| t['name'] }
147
+ inventory[:targets] = inventory[:targets].map { |t| t['name'] }
148
+ targets = targets.map { |t| t['name'] }
149
+ @stream.puts({ adhoc: adhoc, inventory: inventory, targets: targets, count: count }.to_json)
150
+ end
151
+
152
+ # Print target names and where they came from.
153
+ #
154
+ # @param adhoc [Hash] Adhoc targets provided on the command line.
155
+ # @param inventory [Hash] Targets provided from the inventory.
156
+ # @param targets [Array] All targets.
157
+ # @param count [Integer] Number of targets.
158
+ #
159
+ def print_target_info(adhoc:, inventory:, targets:, count:, **_kwargs)
160
+ @stream.puts({ adhoc: adhoc, inventory: inventory, targets: targets, count: count }.to_json)
161
+ end
162
+
163
+ # Print inventory group information.
164
+ #
165
+ # @param count [Integer] Number of groups in the inventory.
166
+ # @param groups [Array] Names of groups in the inventory.
167
+ #
168
+ def print_groups(count:, groups:, **_kwargs)
169
+ @stream.puts({ count: count, groups: groups }.to_json)
145
170
  end
146
171
 
147
172
  def fatal_error(err)
@@ -51,7 +51,7 @@ module Bolt
51
51
  end
52
52
  end
53
53
 
54
- def self.create_plan(plans_path, plan_name, outputter, is_puppet)
54
+ def self.create_plan(plans_path, plan_name, is_puppet)
55
55
  _, name_segments, basename = segment_plan_name(plan_name)
56
56
  dir_path = plans_path.join(*name_segments)
57
57
 
@@ -77,25 +77,7 @@ module Bolt
77
77
  )
78
78
  end
79
79
 
80
- if Bolt::Util.powershell?
81
- show_command = 'Get-BoltPlan -Name '
82
- run_command = 'Invoke-BoltPlan -Name '
83
- else
84
- show_command = 'bolt plan show'
85
- run_command = 'bolt plan run'
86
- end
87
-
88
- output = <<~OUTPUT
89
- Created plan '#{plan_name}' at '#{plan_path}'
90
-
91
- Show this plan with:
92
- #{show_command} #{plan_name}
93
- Run this plan with:
94
- #{run_command} #{plan_name}
95
- OUTPUT
96
-
97
- outputter.print_message(output)
98
- 0
80
+ { name: plan_name, path: plan_path }
99
81
  end
100
82
 
101
83
  def self.segment_plan_name(plan_name)
@@ -4,14 +4,19 @@ require 'fiber'
4
4
 
5
5
  module Bolt
6
6
  class PlanFuture
7
- attr_reader :fiber, :id
7
+ attr_reader :fiber, :id, :scope
8
8
  attr_accessor :value, :plan_stack
9
9
 
10
- def initialize(fiber, id, plan_id:, name: nil)
11
- @fiber = fiber
12
- @id = id
13
- @name = name
14
- @value = nil
10
+ def initialize(fiber, id, plan_id:, name: nil, scope: nil)
11
+ @fiber = fiber
12
+ @id = id
13
+ @name = name
14
+ @value = nil
15
+
16
+ # Default to Puppet's current global_scope, otherwise things will
17
+ # blow up when the Fiber Executor tries to override the global_scope.
18
+ @scope = scope || Puppet.lookup(:global_scope) { nil }
19
+
15
20
  # The plan invocation ID when the Future is created may be
16
21
  # different from the plan ID of the Future when we switch to it if a new
17
22
  # plan was run inside the Future, so keep track of the plans that a
@@ -6,7 +6,7 @@ require 'bolt/util'
6
6
 
7
7
  module Bolt
8
8
  class PlanResult
9
- attr_accessor :value, :status
9
+ attr_accessor :status, :value
10
10
 
11
11
  # This must be called from inside a compiler
12
12
  def self.from_pcore(result, status)