bolt 2.6.0 → 2.11.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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/Puppetfile +4 -3
  3. data/bolt-modules/boltlib/lib/puppet/datatypes/applyresult.rb +2 -0
  4. data/bolt-modules/boltlib/lib/puppet/datatypes/resourceinstance.rb +27 -0
  5. data/bolt-modules/boltlib/lib/puppet/datatypes/result.rb +2 -0
  6. data/bolt-modules/boltlib/lib/puppet/datatypes/resultset.rb +2 -0
  7. data/bolt-modules/boltlib/lib/puppet/datatypes/target.rb +4 -3
  8. data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +1 -1
  9. data/bolt-modules/boltlib/lib/puppet/functions/run_task_with.rb +192 -0
  10. data/bolt-modules/boltlib/lib/puppet/functions/set_resources.rb +122 -0
  11. data/bolt-modules/boltlib/types/planresult.pp +12 -1
  12. data/bolt-modules/file/lib/puppet/functions/file/exists.rb +3 -1
  13. data/bolt-modules/file/lib/puppet/functions/file/join.rb +1 -1
  14. data/bolt-modules/file/lib/puppet/functions/file/read.rb +2 -1
  15. data/bolt-modules/file/lib/puppet/functions/file/readable.rb +3 -1
  16. data/bolt-modules/file/lib/puppet/functions/file/write.rb +3 -1
  17. data/bolt-modules/prompt/lib/puppet/functions/prompt.rb +43 -0
  18. data/lib/bolt/analytics.rb +1 -1
  19. data/lib/bolt/applicator.rb +3 -2
  20. data/lib/bolt/apply_inventory.rb +1 -1
  21. data/lib/bolt/apply_result.rb +1 -1
  22. data/lib/bolt/apply_target.rb +11 -2
  23. data/lib/bolt/bolt_option_parser.rb +27 -7
  24. data/lib/bolt/catalog.rb +32 -3
  25. data/lib/bolt/cli.rb +52 -22
  26. data/lib/bolt/config.rb +51 -27
  27. data/lib/bolt/config/transport/base.rb +3 -3
  28. data/lib/bolt/config/transport/docker.rb +7 -1
  29. data/lib/bolt/config/transport/local.rb +9 -1
  30. data/lib/bolt/config/transport/orch.rb +4 -2
  31. data/lib/bolt/config/transport/remote.rb +2 -0
  32. data/lib/bolt/config/transport/ssh.rb +81 -3
  33. data/lib/bolt/config/transport/winrm.rb +6 -1
  34. data/lib/bolt/executor.rb +38 -0
  35. data/lib/bolt/inventory.rb +2 -1
  36. data/lib/bolt/inventory/group.rb +1 -0
  37. data/lib/bolt/inventory/inventory.rb +9 -0
  38. data/lib/bolt/inventory/target.rb +17 -1
  39. data/lib/bolt/node/output.rb +1 -1
  40. data/lib/bolt/outputter/human.rb +5 -4
  41. data/lib/bolt/outputter/json.rb +1 -1
  42. data/lib/bolt/pal.rb +32 -14
  43. data/lib/bolt/pal/yaml_plan.rb +1 -0
  44. data/lib/bolt/plugin.rb +14 -8
  45. data/lib/bolt/plugin/env_var.rb +2 -1
  46. data/lib/bolt/plugin/module.rb +40 -7
  47. data/lib/bolt/plugin/prompt.rb +1 -1
  48. data/lib/bolt/plugin/puppetdb.rb +5 -2
  49. data/lib/bolt/project.rb +135 -0
  50. data/lib/bolt/puppetdb/config.rb +16 -28
  51. data/lib/bolt/rerun.rb +1 -1
  52. data/lib/bolt/resource_instance.rb +126 -0
  53. data/lib/bolt/result.rb +46 -23
  54. data/lib/bolt/result_set.rb +2 -5
  55. data/lib/bolt/secret.rb +20 -4
  56. data/lib/bolt/shell/bash.rb +27 -14
  57. data/lib/bolt/shell/bash/tmpdir.rb +1 -1
  58. data/lib/bolt/shell/powershell.rb +43 -15
  59. data/lib/bolt/shell/powershell/snippets.rb +1 -1
  60. data/lib/bolt/target.rb +18 -2
  61. data/lib/bolt/transport/base.rb +24 -8
  62. data/lib/bolt/transport/docker.rb +3 -3
  63. data/lib/bolt/transport/docker/connection.rb +11 -7
  64. data/lib/bolt/transport/local/connection.rb +13 -7
  65. data/lib/bolt/transport/orch.rb +5 -1
  66. data/lib/bolt/transport/ssh.rb +6 -2
  67. data/lib/bolt/transport/ssh/connection.rb +26 -1
  68. data/lib/bolt/transport/ssh/exec_connection.rb +110 -0
  69. data/lib/bolt/transport/winrm/connection.rb +10 -2
  70. data/lib/bolt/version.rb +1 -1
  71. data/lib/bolt_server/pe/pal.rb +1 -38
  72. data/lib/bolt_server/transport_app.rb +7 -7
  73. data/lib/bolt_spec/bolt_context.rb +3 -6
  74. data/lib/bolt_spec/plans.rb +78 -8
  75. data/lib/bolt_spec/plans/action_stubs.rb +37 -7
  76. data/lib/bolt_spec/plans/action_stubs/plan_stub.rb +55 -0
  77. data/lib/bolt_spec/plans/mock_executor.rb +62 -2
  78. data/lib/bolt_spec/run.rb +10 -13
  79. metadata +26 -7
  80. data/lib/bolt/boltdir.rb +0 -54
  81. data/lib/bolt/plugin/pkcs7.rb +0 -104
  82. data/lib/bolt/secret/base.rb +0 -41
@@ -99,17 +99,14 @@ module Bolt
99
99
  self.class == other.class && @results == other.results
100
100
  end
101
101
 
102
- def to_a
103
- @results.map(&:status_hash)
104
- end
105
-
106
102
  def to_json(opts = nil)
107
- @results.map(&:status_hash).to_json(opts)
103
+ to_data.to_json(opts)
108
104
  end
109
105
 
110
106
  def to_data
111
107
  @results.map(&:to_data)
112
108
  end
109
+ alias to_a to_data
113
110
 
114
111
  def to_s
115
112
  to_json
@@ -1,17 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'bolt/plugin'
4
+
3
5
  module Bolt
4
6
  class Secret
7
+ KNOWN_KEYS = {
8
+ 'createkeys' => %w[keysize private_key public_key],
9
+ 'encrypt' => %w[public_key],
10
+ 'decrypt' => %w[private_key public_key]
11
+ }.freeze
12
+
5
13
  def self.execute(plugins, outputter, options)
6
- plugin = options[:plugin] || 'pkcs7'
14
+ name = options[:plugin] || 'pkcs7'
15
+ plugin = plugins.by_name(name)
16
+
17
+ unless plugin
18
+ raise Bolt::Plugin::PluginError::Unknown, name
19
+ end
20
+
7
21
  case options[:action]
8
22
  when 'createkeys'
9
- plugins.get_hook(plugin, :secret_createkeys).call
23
+ opts = { 'force' => options[:force] }.compact
24
+ result = plugins.get_hook(name, :secret_createkeys).call(opts)
25
+ outputter.print_message(result)
10
26
  when 'encrypt'
11
- encrypted = plugins.get_hook(plugin, :secret_encrypt).call('plaintext_value' => options[:object])
27
+ encrypted = plugins.get_hook(name, :secret_encrypt).call('plaintext_value' => options[:object])
12
28
  outputter.print_message(encrypted)
13
29
  when 'decrypt'
14
- decrypted = plugins.get_hook(plugin, :secret_decrypt).call('encrypted_value' => options[:object])
30
+ decrypted = plugins.get_hook(name, :secret_decrypt).call('encrypted_value' => options[:object])
15
31
  outputter.print_message(decrypted)
16
32
  end
17
33
 
@@ -34,7 +34,7 @@ module Bolt
34
34
 
35
35
  def upload(source, destination, options = {})
36
36
  running_as(options[:run_as]) do
37
- with_tempdir do |dir|
37
+ with_tmpdir do |dir|
38
38
  basename = File.basename(destination)
39
39
  tmpfile = File.join(dir.to_s, basename)
40
40
  conn.copy_file(source, tmpfile)
@@ -55,7 +55,7 @@ module Bolt
55
55
  arguments = unwrap_sensitive_args(arguments)
56
56
 
57
57
  running_as(options[:run_as]) do
58
- with_tempdir do |dir|
58
+ with_tmpdir do |dir|
59
59
  path = write_executable(dir.to_s, script)
60
60
  dir.chown(run_as)
61
61
  output = execute([path, *arguments], sudoable: true)
@@ -86,7 +86,7 @@ module Bolt
86
86
  # unpack any Sensitive data
87
87
  arguments = unwrap_sensitive_args(arguments)
88
88
 
89
- with_tempdir do |dir|
89
+ with_tmpdir do |dir|
90
90
  if extra_files.empty?
91
91
  task_dir = dir
92
92
  else
@@ -150,8 +150,14 @@ module Bolt
150
150
  end
151
151
  elsif err =~ /^#{@sudo_id}/
152
152
  if sudo_stdin
153
- stdin.write("#{sudo_stdin}\n")
154
- stdin.close
153
+ begin
154
+ stdin.write("#{sudo_stdin}\n")
155
+ stdin.close
156
+ # If a task has stdin as an input_method but doesn't actually read
157
+ # from stdin, the task may return and close the input stream before
158
+ # we finish writing
159
+ rescue Errno::EPIPE
160
+ end
155
161
  end
156
162
  ''
157
163
  else
@@ -234,7 +240,7 @@ module Bolt
234
240
  end
235
241
  end
236
242
 
237
- def make_tempdir
243
+ def make_tmpdir
238
244
  tmpdir = @target.options.fetch('tmpdir', '/tmp')
239
245
  script_dir = @target.options.fetch('script-dir', SecureRandom.uuid)
240
246
  tmppath = File.join(tmpdir, script_dir)
@@ -242,7 +248,7 @@ module Bolt
242
248
 
243
249
  result = execute(command)
244
250
  if result.exit_code != 0
245
- raise Bolt::Node::FileError.new("Could not make tempdir: #{result.stderr.string}", 'TEMPDIR_ERROR')
251
+ raise Bolt::Node::FileError.new("Could not make tmpdir: #{result.stderr.string}", 'TMPDIR_ERROR')
246
252
  end
247
253
  path = tmppath || result.stdout.string.chomp
248
254
  Bolt::Shell::Bash::Tmpdir.new(self, path)
@@ -256,13 +262,19 @@ module Bolt
256
262
  remote_path
257
263
  end
258
264
 
259
- # A helper to create and delete a tempdir on the remote system. Yields the
265
+ # A helper to create and delete a tmpdir on the remote system. Yields the
260
266
  # directory name.
261
- def with_tempdir
262
- dir = make_tempdir
267
+ def with_tmpdir
268
+ dir = make_tmpdir
263
269
  yield dir
264
270
  ensure
265
- dir&.delete
271
+ if dir
272
+ if target.options['cleanup']
273
+ dir.delete
274
+ else
275
+ @logger.warn("Skipping cleanup of tmpdir #{dir}")
276
+ end
277
+ end
266
278
  end
267
279
 
268
280
  # In the case where a task is run with elevated privilege and needs stdin
@@ -341,9 +353,9 @@ module Bolt
341
353
  # Chunks of this size will be read in one iteration
342
354
  index = 0
343
355
  timeout = 0.1
356
+ result_output = Bolt::Node::Output.new
344
357
 
345
358
  inp, out, err, t = conn.execute(command_str)
346
- result_output = Bolt::Node::Output.new
347
359
  read_streams = { out => String.new,
348
360
  err => String.new }
349
361
  write_stream = in_buffer.empty? ? [] : [inp]
@@ -393,8 +405,9 @@ module Bolt
393
405
  write_stream = []
394
406
  end
395
407
  end
396
- # If a task has stdin as an input_method but doesn't actually
397
- # read from stdin, the task may return and close the input stream
408
+ # If a task has stdin as an input_method but doesn't actually read
409
+ # from stdin, the task may return and close the input stream before
410
+ # we finish writing
398
411
  rescue Errno::EPIPE
399
412
  write_stream = []
400
413
  end
@@ -48,7 +48,7 @@ module Bolt
48
48
  def delete
49
49
  result = @shell.execute(['rm', '-rf', @path], sudoable: true, run_as: @owner)
50
50
  if result.exit_code != 0
51
- @logger.warn("Failed to clean up tempdir '#{@path}': #{result.stderr.string}")
51
+ @logger.warn("Failed to clean up tmpdir '#{@path}': #{result.stderr.string}")
52
52
  end
53
53
  # For testing
54
54
  result.stderr.string
@@ -6,7 +6,7 @@ module Bolt
6
6
  class Shell
7
7
  class Powershell < Shell
8
8
  DEFAULT_EXTENSIONS = Set.new(%w[.ps1 .rb .pp])
9
- PS_ARGS = %w[-NoProfile -NonInteractive -NoLogo -ExecutionPolicy Bypass -File].freeze
9
+ PS_ARGS = %w[-NoProfile -NonInteractive -NoLogo -ExecutionPolicy Bypass].freeze
10
10
 
11
11
  def initialize(target, conn)
12
12
  super
@@ -45,7 +45,7 @@ module Bolt
45
45
  when '.ps1'
46
46
  [
47
47
  'powershell.exe',
48
- [*PS_ARGS, path]
48
+ [*PS_ARGS, '-File', path]
49
49
  ]
50
50
  when '.pp'
51
51
  [
@@ -119,11 +119,11 @@ module Bolt
119
119
  end
120
120
  end
121
121
 
122
- def make_tempdir
122
+ def make_tmpdir
123
123
  find_parent = target.options['tmpdir'] ? "\"#{target.options['tmpdir']}\"" : '[System.IO.Path]::GetTempPath()'
124
- result = execute(Snippets.make_tempdir(find_parent))
124
+ result = execute(Snippets.make_tmpdir(find_parent))
125
125
  if result.exit_code != 0
126
- raise Bolt::Node::FileError.new("Could not make tempdir: #{result.stderr.string}", 'TEMPDIR_ERROR')
126
+ raise Bolt::Node::FileError.new("Could not make tmpdir: #{result.stderr.string}", 'TMPDIR_ERROR')
127
127
  end
128
128
  result.stdout.string.chomp
129
129
  end
@@ -132,11 +132,21 @@ module Bolt
132
132
  execute(Snippets.rmdir(dir))
133
133
  end
134
134
 
135
- def with_tempdir
136
- dir = make_tempdir
137
- yield dir
135
+ def with_tmpdir
136
+ unless @tmpdir
137
+ # Only cleanup the directory afterward if we made it to begin with
138
+ owner = true
139
+ @tmpdir = make_tmpdir
140
+ end
141
+ yield @tmpdir
138
142
  ensure
139
- rmdir(dir)
143
+ if owner && @tmpdir
144
+ if target.options['cleanup']
145
+ rmdir(@tmpdir)
146
+ else
147
+ @logger.warn("Skipping cleanup of tmpdir '#{@tmpdir}'")
148
+ end
149
+ end
140
150
  end
141
151
 
142
152
  def run_ps_task(task_path, arguments, input_method)
@@ -167,7 +177,7 @@ module Bolt
167
177
  def run_script(script, arguments, _options = {})
168
178
  # unpack any Sensitive data
169
179
  arguments = unwrap_sensitive_args(arguments)
170
- with_tempdir do |dir|
180
+ with_tmpdir do |dir|
171
181
  script_path = write_executable(dir, script)
172
182
  command = if powershell_file?(script_path)
173
183
  Snippets.run_script(arguments, script_path)
@@ -194,7 +204,7 @@ module Bolt
194
204
 
195
205
  # unpack any Sensitive data
196
206
  arguments = unwrap_sensitive_args(arguments)
197
- with_tempdir do |dir|
207
+ with_tmpdir do |dir|
198
208
  if extra_files.empty?
199
209
  task_dir = dir
200
210
  else
@@ -243,14 +253,32 @@ module Bolt
243
253
  end
244
254
 
245
255
  def execute(command)
256
+ if conn.max_command_length && command.length > conn.max_command_length
257
+ return with_tmpdir do |dir|
258
+ command += "\r\nif (!$?) { if($LASTEXITCODE) { exit $LASTEXITCODE } else { exit 1 } }"
259
+ script_file = File.join(dir, "#{SecureRandom.uuid}_wrapper.ps1")
260
+ conn.copy_file(StringIO.new(command), script_file)
261
+ args = escape_arguments([script_file])
262
+ script_invocation = ['powershell.exe', *PS_ARGS, '-File', *args].join(' ')
263
+ execute(script_invocation)
264
+ end
265
+ end
246
266
  inp, out, err, t = conn.execute(command)
247
267
 
248
268
  result = Bolt::Node::Output.new
249
269
  inp.close
250
- out.binmode
251
- err.binmode
252
- stdout = Thread.new { result.stdout << out.read }
253
- stderr = Thread.new { result.stderr << err.read }
270
+ stdout = Thread.new do
271
+ # Set to binmode to preserve \r\n line endings, but save and restore
272
+ # the proper encoding so the string isn't later misinterpreted
273
+ encoding = out.external_encoding
274
+ out.binmode
275
+ result.stdout << out.read.force_encoding(encoding)
276
+ end
277
+ stderr = Thread.new do
278
+ encoding = err.external_encoding
279
+ err.binmode
280
+ result.stderr << err.read.force_encoding(encoding)
281
+ end
254
282
 
255
283
  stdout.join
256
284
  stderr.join
@@ -20,7 +20,7 @@ module Bolt
20
20
  PS
21
21
  end
22
22
 
23
- def make_tempdir(parent)
23
+ def make_tmpdir(parent)
24
24
  <<~PS
25
25
  $parent = #{parent}
26
26
  $name = [System.IO.Path]::GetRandomFileName()
@@ -31,7 +31,8 @@ module Bolt
31
31
  facts = nil,
32
32
  vars = nil,
33
33
  features = nil,
34
- plugin_hooks = nil)
34
+ plugin_hooks = nil,
35
+ resources = nil)
35
36
  from_asserted_hash('uri' => uri)
36
37
  end
37
38
  # rubocop:enable Lint/UnusedMethodArgument
@@ -75,6 +76,16 @@ module Bolt
75
76
  inventory_target.target_alias
76
77
  end
77
78
 
79
+ def resources
80
+ inventory_target.resources
81
+ end
82
+
83
+ # rubocop:disable Naming/AccessorMethodName
84
+ def set_resource(resource)
85
+ inventory_target.set_resource(resource)
86
+ end
87
+ # rubocop:enable Naming/AccessorMethodName
88
+
78
89
  def to_h
79
90
  options.to_h.merge(
80
91
  'name' => name,
@@ -99,7 +110,8 @@ module Bolt
99
110
  'vars' => vars,
100
111
  'features' => features,
101
112
  'facts' => facts,
102
- 'plugin_hooks' => plugin_hooks
113
+ 'plugin_hooks' => plugin_hooks,
114
+ 'groups' => @inventory.group_names_for(name)
103
115
  }
104
116
  end
105
117
 
@@ -154,5 +166,9 @@ module Bolt
154
166
  self.class.equal?(other.class) && @name == other.name
155
167
  end
156
168
  alias == eql?
169
+
170
+ def hash
171
+ @name.hash
172
+ end
157
173
  end
158
174
  end
@@ -32,7 +32,7 @@ module Bolt
32
32
  # Transports that need their own batching, like the Orch transport, can
33
33
  # instead override the batches() method to split Targets into sets that can
34
34
  # be executed together, and override the batch_task() and related methods
35
- # to execute a batch of nodes. In that case, those Transports should accept
35
+ # to execute a batch of targets. In that case, those Transports should accept
36
36
  # a block argument and call it with a :node_start event for each Target
37
37
  # before executing, and a :node_result event for each Target after
38
38
  # execution.
@@ -90,12 +90,12 @@ module Bolt
90
90
  # case and raises an error if it's not.
91
91
  def assert_batch_size_one(method, targets)
92
92
  if targets.length > 1
93
- message = "#{self.class.name} must implement #{method} to support batches (got #{targets.length} nodes)"
93
+ message = "#{self.class.name} must implement #{method} to support batches (got #{targets.length} targets)"
94
94
  raise NotImplementedError, message
95
95
  end
96
96
  end
97
97
 
98
- # Runs the given task on a batch of nodes.
98
+ # Runs the given task on a batch of targets.
99
99
  #
100
100
  # The default implementation only supports batches of size 1 and will fail otherwise.
101
101
  #
@@ -104,12 +104,28 @@ module Bolt
104
104
  assert_batch_size_one("batch_task()", targets)
105
105
  target = targets.first
106
106
  with_events(target, callback, 'task') do
107
- @logger.debug { "Running task run '#{task}' on #{target.safe_name}" }
107
+ @logger.debug { "Running task '#{task.name}' on #{target.safe_name}" }
108
108
  run_task(target, task, arguments, options)
109
109
  end
110
110
  end
111
111
 
112
- # Runs the given command on a batch of nodes.
112
+ # Runs the given task on a batch of targets with variable parameters.
113
+ #
114
+ # The default implementation only supports batches of size 1 and will fail otherwise.
115
+ #
116
+ # Transports may override this method to implment their own batch processing.
117
+ def batch_task_with(targets, task, target_mapping, options = {}, &callback)
118
+ assert_batch_size_one("batch_task_with()", targets)
119
+ target = targets.first
120
+ arguments = target_mapping[target]
121
+
122
+ with_events(target, callback, 'task') do
123
+ @logger.debug { "Running task '#{task.name}' on #{target.safe_name} with '#{arguments.to_json}'" }
124
+ run_task(target, task, arguments, options)
125
+ end
126
+ end
127
+
128
+ # Runs the given command on a batch of targets.
113
129
  #
114
130
  # The default implementation only supports batches of size 1 and will fail otherwise.
115
131
  #
@@ -123,7 +139,7 @@ module Bolt
123
139
  end
124
140
  end
125
141
 
126
- # Runs the given script on a batch of nodes.
142
+ # Runs the given script on a batch of targets.
127
143
  #
128
144
  # The default implementation only supports batches of size 1 and will fail otherwise.
129
145
  #
@@ -137,7 +153,7 @@ module Bolt
137
153
  end
138
154
  end
139
155
 
140
- # Uploads the given source file to the destination location on a batch of nodes.
156
+ # Uploads the given source file to the destination location on a batch of targets.
141
157
  #
142
158
  # The default implementation only supports batches of size 1 and will fail otherwise.
143
159
  #
@@ -157,7 +173,7 @@ module Bolt
157
173
  end
158
174
 
159
175
  # Split the given list of targets into a list of batches. The default
160
- # implementation returns single-node batches.
176
+ # implementation returns single-target batches.
161
177
  #
162
178
  # Transports may override this method, and the corresponding batch_*
163
179
  # methods, to implement their own batch processing.
@@ -19,7 +19,7 @@ module Bolt
19
19
 
20
20
  def upload(target, source, destination, _options = {})
21
21
  with_connection(target) do |conn|
22
- conn.with_remote_tempdir do |dir|
22
+ conn.with_remote_tmpdir do |dir|
23
23
  basename = File.basename(destination)
24
24
  tmpfile = "#{dir}/#{basename}"
25
25
  if File.directory?(source)
@@ -57,7 +57,7 @@ module Bolt
57
57
  arguments = unwrap_sensitive_args(arguments)
58
58
 
59
59
  with_connection(target) do |conn|
60
- conn.with_remote_tempdir do |dir|
60
+ conn.with_remote_tmpdir do |dir|
61
61
  remote_path = conn.write_remote_executable(dir, script)
62
62
  stdout, stderr, exitcode = conn.execute(remote_path, *arguments, {})
63
63
  Bolt::Result.for_command(target, stdout, stderr, exitcode, 'script', script)
@@ -77,7 +77,7 @@ module Bolt
77
77
  with_connection(target) do |conn|
78
78
  execute_options = {}
79
79
  execute_options[:interpreter] = select_interpreter(executable, target.options['interpreters'])
80
- conn.with_remote_tempdir do |dir|
80
+ conn.with_remote_tmpdir do |dir|
81
81
  if extra_files.empty?
82
82
  task_dir = dir
83
83
  else
@@ -103,25 +103,29 @@ module Bolt
103
103
  end
104
104
  end
105
105
 
106
- def make_tempdir
106
+ def make_tmpdir
107
107
  tmpdir = @target.options.fetch('tmpdir', container_tmpdir)
108
108
  tmppath = "#{tmpdir}/#{SecureRandom.uuid}"
109
109
 
110
110
  stdout, stderr, exitcode = execute('mkdir', '-m', '700', tmppath, {})
111
111
  if exitcode != 0
112
- raise Bolt::Node::FileError.new("Could not make tempdir: #{stderr}", 'TEMPDIR_ERROR')
112
+ raise Bolt::Node::FileError.new("Could not make tmpdir: #{stderr}", 'TMPDIR_ERROR')
113
113
  end
114
114
  tmppath || stdout.first
115
115
  end
116
116
 
117
- def with_remote_tempdir
118
- dir = make_tempdir
117
+ def with_remote_tmpdir
118
+ dir = make_tmpdir
119
119
  yield dir
120
120
  ensure
121
121
  if dir
122
- _, stderr, exitcode = execute('rm', '-rf', dir, {})
123
- if exitcode != 0
124
- @logger.warn("Failed to clean up tempdir '#{dir}': #{stderr}")
122
+ if @target.options['cleanup']
123
+ _, stderr, exitcode = execute('rm', '-rf', dir, {})
124
+ if exitcode != 0
125
+ @logger.warn("Failed to clean up tmpdir '#{dir}': #{stderr}")
126
+ end
127
+ else
128
+ @logger.warn("Skipping cleanup of tmpdir '#{dir}'")
125
129
  end
126
130
  end
127
131
  end