bolt 3.7.1 → 3.9.2

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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/Puppetfile +3 -3
  3. data/bolt-modules/boltlib/lib/puppet/datatypes/future.rb +25 -0
  4. data/bolt-modules/boltlib/lib/puppet/functions/apply_prep.rb +9 -6
  5. data/bolt-modules/boltlib/lib/puppet/functions/background.rb +61 -0
  6. data/bolt-modules/boltlib/lib/puppet/functions/download_file.rb +5 -9
  7. data/bolt-modules/boltlib/lib/puppet/functions/parallelize.rb +28 -13
  8. data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +5 -15
  9. data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +10 -18
  10. data/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +8 -17
  11. data/bolt-modules/boltlib/lib/puppet/functions/run_task_with.rb +8 -15
  12. data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +10 -18
  13. data/bolt-modules/boltlib/lib/puppet/functions/wait.rb +91 -0
  14. data/bolt-modules/boltlib/types/planresult.pp +1 -0
  15. data/bolt-modules/file/lib/puppet/functions/file/exists.rb +9 -3
  16. data/bolt-modules/file/lib/puppet/functions/file/read.rb +6 -2
  17. data/bolt-modules/file/lib/puppet/functions/file/readable.rb +8 -3
  18. data/guides/inventory.txt +5 -0
  19. data/lib/bolt/applicator.rb +4 -3
  20. data/lib/bolt/bolt_option_parser.rb +24 -2
  21. data/lib/bolt/catalog.rb +2 -1
  22. data/lib/bolt/cli.rb +65 -28
  23. data/lib/bolt/config/options.rb +2 -1
  24. data/lib/bolt/config/transport/docker.rb +1 -1
  25. data/lib/bolt/config/transport/lxd.rb +1 -1
  26. data/lib/bolt/config/transport/options.rb +2 -1
  27. data/lib/bolt/config/transport/podman.rb +1 -1
  28. data/lib/bolt/error.rb +11 -1
  29. data/lib/bolt/executor.rb +55 -72
  30. data/lib/bolt/fiber_executor.rb +141 -0
  31. data/lib/bolt/logger.rb +1 -1
  32. data/lib/bolt/module_installer/specs.rb +1 -1
  33. data/lib/bolt/outputter/human.rb +9 -4
  34. data/lib/bolt/outputter/json.rb +4 -0
  35. data/lib/bolt/pal.rb +64 -3
  36. data/lib/bolt/plan_future.rb +66 -0
  37. data/lib/bolt/result.rb +5 -0
  38. data/lib/bolt/transport/docker/connection.rb +5 -2
  39. data/lib/bolt/transport/lxd/connection.rb +4 -0
  40. data/lib/bolt/transport/podman/connection.rb +4 -0
  41. data/lib/bolt/util.rb +73 -1
  42. data/lib/bolt/version.rb +1 -1
  43. data/lib/bolt_server/config.rb +1 -1
  44. data/lib/bolt_server/request_error.rb +11 -0
  45. data/lib/bolt_server/transport_app.rb +133 -95
  46. data/lib/bolt_spec/plans/mock_executor.rb +40 -45
  47. data/lib/bolt_spec/run.rb +4 -1
  48. data/modules/puppet_connect/plans/test_input_data.pp +8 -3
  49. metadata +8 -3
  50. data/lib/bolt/yarn.rb +0 -23
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 741f798df2b4c7a46e45c5c87a41cd4e994506cdd202d26b6acafae7774135f8
4
- data.tar.gz: 803789a5f6872e27656a733e94998acc9dde4d325d2d920b345a1c2848683f2f
3
+ metadata.gz: b54ca78ebd584ad7e8bd223f086ac1dfc86f6f8bca3e7a7a2aab93a466eebc44
4
+ data.tar.gz: f68a3dfe2dad3a15bd9d970860ad629de9fac40882291d6cd7b677bf0da5814e
5
5
  SHA512:
6
- metadata.gz: 85cb83b0a0bf02d1ba15b14ba5e5e75c6ff290e1acee71b0de06ea716f3698e91358aa6f9961757b088afa3c98d519ba10f79a6195c767492a87603759c2fc81
7
- data.tar.gz: 8180f0827c3576e676e6fca59af7744d3e375023b44c0ebc2e691801a25f8fc1cac2487276661f7b40503e8ab1164ae07ee5f8343be9bb5be3088851e81eafaf
6
+ metadata.gz: 879279d0db3506639ed99118623e90435e4f3ee70239aebd43e50d0fb8527eebf1ecfcf4298b6b110fcd34b560f4037cfc44439b2ad9858f67d9dfa1e5ad27e6
7
+ data.tar.gz: 36f05f2209c085c607200a857c380114eac7c631a6dd2b98550d27ece0488f3d0c3883693f83c74c3e87387a81e63d446727931084e40d27e6443a9c05eb1c4a
data/Puppetfile CHANGED
@@ -6,7 +6,7 @@ moduledir File.join(File.dirname(__FILE__), 'modules')
6
6
 
7
7
  # Core modules used by 'apply'
8
8
  mod 'puppetlabs-service', '2.0.0'
9
- mod 'puppetlabs-puppet_agent', '4.5.0'
9
+ mod 'puppetlabs-puppet_agent', '4.6.1'
10
10
  mod 'puppetlabs-facts', '1.4.0'
11
11
 
12
12
  # Core types and providers for Puppet 6
@@ -17,7 +17,7 @@ mod 'puppetlabs-sshkeys_core', '2.2.0'
17
17
  mod 'puppetlabs-zfs_core', '1.2.0'
18
18
  mod 'puppetlabs-cron_core', '1.0.5'
19
19
  mod 'puppetlabs-mount_core', '1.0.4'
20
- mod 'puppetlabs-selinux_core', '1.0.4'
20
+ mod 'puppetlabs-selinux_core', '1.1.0'
21
21
  mod 'puppetlabs-yumrepo_core', '1.0.7'
22
22
  mod 'puppetlabs-zone_core', '1.0.3'
23
23
 
@@ -29,7 +29,7 @@ mod 'puppetlabs-python_task_helper', '0.5.0'
29
29
  mod 'puppetlabs-reboot', '4.0.2'
30
30
  mod 'puppetlabs-ruby_task_helper', '0.6.0'
31
31
  mod 'puppetlabs-ruby_plugin_helper', '0.2.0'
32
- mod 'puppetlabs-stdlib', '7.0.0'
32
+ mod 'puppetlabs-stdlib', '7.0.1'
33
33
 
34
34
  # Plugin modules
35
35
  mod 'puppetlabs-aws_inventory', '0.7.0'
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The [`background()` plan function](plan_functions.md#background) returns a
4
+ # `Future` object, which can be passed to the [`wait()` plan
5
+ # function](plan_functions.md#wait) to block on the result of the backgrounded
6
+ # code block.
7
+ #
8
+ # @!method state
9
+ # Either 'running' if the Future is still executing, 'done' if the Future
10
+ # finished successfully, or 'error' if the Future finished with an error.
11
+ #
12
+ Puppet::DataTypes.create_type('Future') do
13
+ interface <<-PUPPET
14
+ attributes => {},
15
+ functions => {
16
+ state => Callable[[], Enum['running', 'done', 'error']],
17
+ }
18
+ PUPPET
19
+
20
+ load_file('bolt/plan_future')
21
+
22
+ # Needed for Puppet to recognize Bolt::Result as a Puppet object when deserializing
23
+ Bolt::PlanFuture.include(Puppet::Pops::Types::PuppetObject)
24
+ implementation_class Bolt::PlanFuture
25
+ end
@@ -16,6 +16,7 @@ Puppet::Functions.create_function(:apply_prep) do
16
16
  # @param targets A pattern or array of patterns identifying a set of targets.
17
17
  # @param options Options hash.
18
18
  # @option options [Array] _required_modules An array of modules to sync to the target.
19
+ # @option options [String] _run_as User to run as using privilege escalation.
19
20
  # @return [nil]
20
21
  # @example Prepare targets by name.
21
22
  # apply_prep('target1,target2')
@@ -71,7 +72,8 @@ Puppet::Functions.create_function(:apply_prep) do
71
72
  .from_issue_and_stack(Bolt::PAL::Issues::PLAN_OPERATION_NOT_SUPPORTED_WHEN_COMPILING, action: 'apply_prep')
72
73
  end
73
74
 
74
- options = options.transform_keys { |k| k.sub(/^_/, '').to_sym }
75
+ # Unfreeze this
76
+ options = options.slice(*%w[_run_as _required_modules])
75
77
 
76
78
  applicator = Puppet.lookup(:apply_executor)
77
79
 
@@ -79,14 +81,14 @@ Puppet::Functions.create_function(:apply_prep) do
79
81
 
80
82
  targets = inventory.get_targets(target_spec)
81
83
 
82
- required_modules = options[:required_modules].nil? ? nil : Array(options[:required_modules])
83
- if required_modules&.any?
84
+ required_modules = options.delete('_required_modules').to_a
85
+ if required_modules.any?
84
86
  Puppet.debug("Syncing only required modules: #{required_modules.join(',')}.")
85
87
  end
86
88
 
87
89
  # Gather facts, including custom facts
88
90
  plugins = applicator.build_plugin_tarball do |mod|
89
- next unless required_modules.nil? || required_modules.include?(mod.name)
91
+ next unless required_modules.empty? || required_modules.include?(mod.name)
90
92
  search_dirs = []
91
93
  search_dirs << mod.plugins if mod.plugins?
92
94
  search_dirs << mod.pluginfacts if mod.pluginfacts?
@@ -107,8 +109,9 @@ Puppet::Functions.create_function(:apply_prep) do
107
109
  opts = t.plugin_hooks&.fetch('puppet_library').dup
108
110
  plugin_name = opts.delete('plugin')
109
111
  hook = inventory.plugins.get_hook(plugin_name, :puppet_library)
112
+ # Give plan function options precedence over inventory options
110
113
  { 'target' => t,
111
- 'hook_proc' => hook.call(opts, t, self) }
114
+ 'hook_proc' => hook.call(opts.merge(options), t, self) }
112
115
  rescue StandardError => e
113
116
  Bolt::Result.from_exception(t, e)
114
117
  end
@@ -132,7 +135,7 @@ Puppet::Functions.create_function(:apply_prep) do
132
135
 
133
136
  task = applicator.custom_facts_task
134
137
  arguments = { 'plugins' => Puppet::Pops::Types::PSensitiveType::Sensitive.new(plugins) }
135
- results = executor.run_task(targets, task, arguments)
138
+ results = run_task(targets, task, arguments, options)
136
139
 
137
140
  # TODO: Standardize RunFailure type with error above
138
141
  raise Bolt::RunFailure.new(results, 'run_task', task.name) unless results.ok?
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Starts a block of code in parallel with the main plan without blocking.
4
+ # Returns a Future object.
5
+ #
6
+ # > **Note:** Not available in apply block
7
+ Puppet::Functions.create_function(:background, Puppet::Functions::InternalFunction) do
8
+ # Starts a block of code in parallel with the main plan without blocking.
9
+ # Returns a Future object.
10
+ # @param name An optional name for legible logs.
11
+ # @param block The code block to run in the background.
12
+ # @return A Bolt Future object
13
+ # @example Start a long-running process
14
+ # background() || {
15
+ # run_task('superlong::task', $targets)
16
+ # }
17
+ # run_command("echo 'Continue immediately'", $targets)
18
+ dispatch :background do
19
+ scope_param
20
+ optional_param 'String[1]', :name
21
+ block_param 'Callable[0, 0]', :block
22
+ return_type 'Future'
23
+ end
24
+
25
+ def background(scope, name = nil, &block)
26
+ unless Puppet[:tasks]
27
+ raise Puppet::ParseErrorWithIssue
28
+ .from_issue_and_stack(Bolt::PAL::Issues::PLAN_OPERATION_NOT_SUPPORTED_WHEN_COMPILING, action: 'background')
29
+ end
30
+
31
+ executor = Puppet.lookup(:bolt_executor)
32
+ executor.report_function_call(self.class.name)
33
+
34
+ executor.create_future(scope: scope, name: name) do |newscope|
35
+ # Catch 'return' calls inside the block
36
+ result = catch(:return) do
37
+ # Execute the block. Individual plan steps in the block will yield
38
+ # the Fiber if they haven't finished, so all this needs to do is run
39
+ # the block.
40
+ block.closure.call_by_name_with_scope(newscope, {}, true)
41
+ end
42
+
43
+ # If we got a return from the block, get its value
44
+ # Otherwise the result is the last line from the block
45
+ result = result.value if result.is_a?(Puppet::Pops::Evaluator::Return)
46
+
47
+ # Validate the result is a PlanResult
48
+ unless Puppet::Pops::Types::TypeParser.singleton.parse('Boltlib::PlanResult').instance?(result)
49
+ raise Bolt::InvalidParallelResult.new(result.to_s, *Puppet::Pops::PuppetStack.top_of_stack)
50
+ end
51
+
52
+ result
53
+ rescue Puppet::PreformattedError => e
54
+ if e.cause.is_a?(Bolt::Error)
55
+ e.cause
56
+ else
57
+ raise e
58
+ end
59
+ end
60
+ end
61
+ end
@@ -112,17 +112,13 @@ Puppet::Functions.create_function(:download_file, Puppet::Functions::InternalFun
112
112
  call_function('debug', "Simulating file download of '#{source}' - no targets given - no action taken")
113
113
  Bolt::ResultSet.new([])
114
114
  else
115
- r = if executor.in_parallel
116
- require 'concurrent'
117
- require 'fiber'
118
- future = Concurrent::Future.execute do
119
- executor.download_file(targets, source, destination, options, Puppet::Pops::PuppetStack.top_of_stack)
115
+ file_line = Puppet::Pops::PuppetStack.top_of_stack
116
+ r = if executor.in_parallel?
117
+ executor.run_in_thread do
118
+ executor.download_file(targets, source, destination, options, file_line)
120
119
  end
121
-
122
- Fiber.yield('unfinished') while future.incomplete?
123
- future.value || future.reason
124
120
  else
125
- executor.download_file(targets, source, destination, options, Puppet::Pops::PuppetStack.top_of_stack)
121
+ executor.download_file(targets, source, destination, options, file_line)
126
122
  end
127
123
 
128
124
  if !r.ok && !options[:catch_errors]
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'bolt/yarn'
4
-
5
3
  # Map a code block onto an array, where each array element executes in parallel.
6
4
  # This function is experimental.
7
5
  #
@@ -35,21 +33,38 @@ Puppet::Functions.create_function(:parallelize, Puppet::Functions::InternalFunct
35
33
  executor = Puppet.lookup(:bolt_executor)
36
34
  executor.report_function_call(self.class.name)
37
35
 
38
- skein = data.each_with_index.map do |object, index|
39
- executor.create_yarn(scope, block, object, index)
40
- end
36
+ futures = data.map do |object|
37
+ executor.create_future(scope: scope) do |newscope|
38
+ # Catch 'return' calls inside the block
39
+ result = catch(:return) do
40
+ # Add the object to the block parameters
41
+ args = { block.parameters[0][1].to_s => object }
42
+ # Execute the block. Individual plan steps in the block will yield
43
+ # the Fiber if they haven't finished, so all this needs to do is run
44
+ # the block.
45
+ block.closure.call_by_name_with_scope(newscope, args, true)
46
+ end
41
47
 
42
- result = executor.round_robin(skein)
48
+ # If we got a return from the block, get its value
49
+ # Otherwise the result is the last line from the block
50
+ result = result.value if result.is_a?(Puppet::Pops::Evaluator::Return)
43
51
 
44
- failed_indices = result.each_index.select do |i|
45
- result[i].is_a?(Bolt::Error)
46
- end
52
+ # Validate the result is a PlanResult
53
+ unless Puppet::Pops::Types::TypeParser.singleton.parse('Boltlib::PlanResult').instance?(result)
54
+ raise Bolt::InvalidParallelResult.new(result.to_s, *Puppet::Pops::PuppetStack.top_of_stack)
55
+ end
47
56
 
48
- # TODO: Inner catch errors block?
49
- if failed_indices.any?
50
- raise Bolt::ParallelFailure.new(result, failed_indices)
57
+ result
58
+ rescue Puppet::PreformattedError => e
59
+ if e.cause.is_a?(Bolt::Error)
60
+ e.cause
61
+ else
62
+ raise e
63
+ end
64
+ end
51
65
  end
52
66
 
53
- result
67
+ # We may eventually want parallelize to accept a timeout
68
+ executor.wait(futures)
54
69
  end
55
70
  end
@@ -87,23 +87,13 @@ Puppet::Functions.create_function(:run_command) do
87
87
  call_function('debug', "Simulating run_command('#{command}') - no targets given - no action taken")
88
88
  Bolt::ResultSet.new([])
89
89
  else
90
- r = if executor.in_parallel
91
- require 'concurrent'
92
- require 'fiber'
93
- future = Concurrent::Future.execute do
94
- executor.run_command(targets,
95
- command,
96
- options,
97
- Puppet::Pops::PuppetStack.top_of_stack)
90
+ file_line = Puppet::Pops::PuppetStack.top_of_stack
91
+ r = if executor.in_parallel?
92
+ executor.run_in_thread do
93
+ executor.run_command(targets, command, options, file_line)
98
94
  end
99
-
100
- Fiber.yield('unfinished') while future.incomplete?
101
- future.value || future.reason
102
95
  else
103
- executor.run_command(targets,
104
- command,
105
- options,
106
- Puppet::Pops::PuppetStack.top_of_stack)
96
+ executor.run_command(targets, command, options, file_line)
107
97
  end
108
98
 
109
99
  if !r.ok && !options[:catch_errors]
@@ -108,7 +108,11 @@ Puppet::Functions.create_function(:run_script, Puppet::Functions::InternalFuncti
108
108
  # Send Analytics Report
109
109
  executor.report_function_call(self.class.name)
110
110
 
111
- found = Puppet::Parser::Files.find_file(script, scope.compiler.environment)
111
+ future = executor&.future || {}
112
+ fallback = future.fetch('file_paths', false)
113
+
114
+ # Find the file path if it exists, otherwise return nil
115
+ found = Bolt::Util.find_file_from_scope(script, scope, fallback)
112
116
  unless found && Puppet::FileSystem.exist?(found)
113
117
  raise Puppet::ParseErrorWithIssue.from_issue_and_stack(
114
118
  Puppet::Pops::Issues::NO_SUCH_FILE_OR_DIRECTORY, file: script
@@ -126,25 +130,13 @@ Puppet::Functions.create_function(:run_script, Puppet::Functions::InternalFuncti
126
130
  if targets.empty?
127
131
  Bolt::ResultSet.new([])
128
132
  else
129
- r = if executor.in_parallel
130
- require 'concurrent'
131
- require 'fiber'
132
- future = Concurrent::Future.execute do
133
- executor.run_script(targets,
134
- found,
135
- arguments,
136
- options,
137
- Puppet::Pops::PuppetStack.top_of_stack)
133
+ file_line = Puppet::Pops::PuppetStack.top_of_stack
134
+ r = if executor.in_parallel?
135
+ executor.run_in_thread do
136
+ executor.run_script(targets, found, arguments, options, file_line)
138
137
  end
139
-
140
- Fiber.yield('unfinished') while future.incomplete?
141
- future.value || future.reason
142
138
  else
143
- executor.run_script(targets,
144
- found,
145
- arguments,
146
- options,
147
- Puppet::Pops::PuppetStack.top_of_stack)
139
+ executor.run_script(targets, found, arguments, options, file_line)
148
140
  end
149
141
 
150
142
  if !r.ok && !options[:catch_errors]
@@ -130,28 +130,19 @@ Puppet::Functions.create_function(:run_task) do
130
130
  end
131
131
  end
132
132
 
133
+ # Report whether the task was run in noop mode.
134
+ executor.report_noop_mode(executor.noop || options[:noop])
135
+
133
136
  if targets.empty?
134
137
  Bolt::ResultSet.new([])
135
138
  else
136
- result = if executor.in_parallel
137
- require 'concurrent'
138
- require 'fiber'
139
- future = Concurrent::Future.execute do
140
- executor.run_task(targets,
141
- task,
142
- params,
143
- options,
144
- Puppet::Pops::PuppetStack.top_of_stack)
139
+ file_line = Puppet::Pops::PuppetStack.top_of_stack
140
+ result = if executor.in_parallel?
141
+ executor.run_in_thread do
142
+ executor.run_task(targets, task, params, options, file_line)
145
143
  end
146
-
147
- Fiber.yield('unfinished') while future.incomplete?
148
- future.value || future.reason
149
144
  else
150
- executor.run_task(targets,
151
- task,
152
- params,
153
- options,
154
- Puppet::Pops::PuppetStack.top_of_stack)
145
+ executor.run_task(targets, task, params, options, file_line)
155
146
  end
156
147
 
157
148
  if !result.ok && !options[:catch_errors]
@@ -175,28 +175,21 @@ Puppet::Functions.create_function(:run_task_with) do
175
175
  end
176
176
  end
177
177
 
178
+ # Report whether the task was run in noop mode.
179
+ executor.report_noop_mode(executor.noop || options[:noop])
180
+
178
181
  if targets.empty?
179
182
  Bolt::ResultSet.new([])
180
183
  else
181
184
  # Combine the results from the task run with any failing results that were
182
185
  # generated earlier when creating the target mapping
183
- task_result = if executor.in_parallel
184
- require 'concurrent'
185
- require 'fiber'
186
- future = Concurrent::Future.execute do
187
- executor.run_task_with(target_mapping,
188
- task,
189
- options,
190
- Puppet::Pops::PuppetStack.top_of_stack)
186
+ file_line = Puppet::Pops::PuppetStack.top_of_stack
187
+ task_result = if executor.in_parallel?
188
+ executor.run_in_thread do
189
+ executor.run_task_with(target_mapping, task, options, file_line)
191
190
  end
192
-
193
- Fiber.yield('unfinished') while future.incomplete?
194
- future.value || future.reason
195
191
  else
196
- executor.run_task_with(target_mapping,
197
- task,
198
- options,
199
- Puppet::Pops::PuppetStack.top_of_stack)
192
+ executor.run_task_with(target_mapping, task, options, file_line)
200
193
  end
201
194
  result = Bolt::ResultSet.new(task_result.results + error_set)
202
195
 
@@ -70,7 +70,11 @@ Puppet::Functions.create_function(:upload_file, Puppet::Functions::InternalFunct
70
70
  # Send Analytics Report
71
71
  executor.report_function_call(self.class.name)
72
72
 
73
- found = Puppet::Parser::Files.find_file(source, scope.compiler.environment)
73
+ future = executor&.future || {}
74
+ fallback = future.fetch('file_paths', false)
75
+
76
+ # Find the file path if it exists, otherwise return nil
77
+ found = Bolt::Util.find_file_from_scope(source, scope, fallback)
74
78
  unless found && Puppet::FileSystem.exist?(found)
75
79
  raise Puppet::ParseErrorWithIssue.from_issue_and_stack(
76
80
  Puppet::Pops::Issues::NO_SUCH_FILE_OR_DIRECTORY, file: source
@@ -83,25 +87,13 @@ Puppet::Functions.create_function(:upload_file, Puppet::Functions::InternalFunct
83
87
  call_function('debug', "Simulating file upload of '#{found}' - no targets given - no action taken")
84
88
  Bolt::ResultSet.new([])
85
89
  else
86
- r = if executor.in_parallel
87
- require 'concurrent'
88
- require 'fiber'
89
- future = Concurrent::Future.execute do
90
- executor.upload_file(targets,
91
- found,
92
- destination,
93
- options,
94
- Puppet::Pops::PuppetStack.top_of_stack)
90
+ file_line = Puppet::Pops::PuppetStack.top_of_stack
91
+ r = if executor.in_parallel?
92
+ executor.run_in_thread do
93
+ executor.upload_file(targets, found, destination, options, file_line)
95
94
  end
96
-
97
- Fiber.yield('unfinished') while future.incomplete?
98
- future.value || future.reason
99
95
  else
100
- executor.upload_file(targets,
101
- found,
102
- destination,
103
- options,
104
- Puppet::Pops::PuppetStack.top_of_stack)
96
+ executor.upload_file(targets, found, destination, options, file_line)
105
97
  end
106
98
  if !r.ok && !options[:catch_errors]
107
99
  raise Bolt::RunFailure.new(r, 'upload_file', source)