bolt 2.32.0 → 2.36.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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/Puppetfile +6 -6
  3. data/bolt-modules/boltlib/lib/puppet/datatypes/applyresult.rb +1 -0
  4. data/bolt-modules/boltlib/lib/puppet/functions/catch_errors.rb +1 -3
  5. data/bolt-modules/boltlib/lib/puppet/functions/download_file.rb +17 -6
  6. data/bolt-modules/boltlib/lib/puppet/functions/facts.rb +6 -0
  7. data/bolt-modules/boltlib/lib/puppet/functions/parallelize.rb +56 -0
  8. data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_query.rb +2 -2
  9. data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +24 -6
  10. data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +27 -8
  11. data/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +21 -1
  12. data/bolt-modules/boltlib/lib/puppet/functions/run_task_with.rb +18 -1
  13. data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +24 -6
  14. data/guides/logging.txt +18 -0
  15. data/lib/bolt/analytics.rb +27 -8
  16. data/lib/bolt/apply_result.rb +3 -3
  17. data/lib/bolt/bolt_option_parser.rb +43 -15
  18. data/lib/bolt/cli.rb +79 -227
  19. data/lib/bolt/config.rb +131 -52
  20. data/lib/bolt/config/options.rb +46 -8
  21. data/lib/bolt/config/transport/base.rb +10 -19
  22. data/lib/bolt/config/transport/local.rb +0 -7
  23. data/lib/bolt/config/transport/options.rb +1 -1
  24. data/lib/bolt/config/transport/ssh.rb +8 -14
  25. data/lib/bolt/config/validator.rb +231 -0
  26. data/lib/bolt/error.rb +37 -3
  27. data/lib/bolt/executor.rb +103 -17
  28. data/lib/bolt/inventory/group.rb +2 -1
  29. data/lib/bolt/module_installer.rb +2 -1
  30. data/lib/bolt/module_installer/specs/forge_spec.rb +5 -4
  31. data/lib/bolt/module_installer/specs/git_spec.rb +4 -3
  32. data/lib/bolt/outputter/human.rb +21 -9
  33. data/lib/bolt/outputter/rainbow.rb +1 -1
  34. data/lib/bolt/pal.rb +48 -30
  35. data/lib/bolt/pal/yaml_plan.rb +11 -2
  36. data/lib/bolt/pal/yaml_plan/evaluator.rb +23 -1
  37. data/lib/bolt/pal/yaml_plan/loader.rb +14 -9
  38. data/lib/bolt/plan_creator.rb +160 -0
  39. data/lib/bolt/plugin.rb +1 -8
  40. data/lib/bolt/project.rb +30 -36
  41. data/lib/bolt/project_manager.rb +199 -0
  42. data/lib/bolt/{project_migrator/config.rb → project_manager/config_migrator.rb} +43 -5
  43. data/lib/bolt/{project_migrator/inventory.rb → project_manager/inventory_migrator.rb} +5 -5
  44. data/lib/bolt/{project_migrator/base.rb → project_manager/migrator.rb} +2 -2
  45. data/lib/bolt/{project_migrator/modules.rb → project_manager/module_migrator.rb} +3 -3
  46. data/lib/bolt/puppetdb/client.rb +3 -2
  47. data/lib/bolt/puppetdb/config.rb +9 -8
  48. data/lib/bolt/result.rb +23 -11
  49. data/lib/bolt/shell/bash.rb +12 -7
  50. data/lib/bolt/shell/powershell.rb +12 -7
  51. data/lib/bolt/task/run.rb +1 -1
  52. data/lib/bolt/transport/base.rb +18 -18
  53. data/lib/bolt/transport/docker.rb +23 -6
  54. data/lib/bolt/transport/orch.rb +23 -19
  55. data/lib/bolt/transport/orch/connection.rb +10 -3
  56. data/lib/bolt/transport/remote.rb +3 -3
  57. data/lib/bolt/transport/simple.rb +6 -6
  58. data/lib/bolt/transport/ssh/exec_connection.rb +6 -2
  59. data/lib/bolt/util.rb +19 -7
  60. data/lib/bolt/version.rb +1 -1
  61. data/lib/bolt/yarn.rb +23 -0
  62. data/lib/bolt_server/base_config.rb +3 -1
  63. data/lib/bolt_server/config.rb +3 -1
  64. data/lib/bolt_server/file_cache.rb +2 -0
  65. data/lib/bolt_server/schemas/partials/task.json +2 -2
  66. data/lib/bolt_server/transport_app.rb +42 -11
  67. data/lib/bolt_spec/plans/action_stubs/command_stub.rb +1 -1
  68. data/lib/bolt_spec/plans/action_stubs/script_stub.rb +1 -1
  69. data/lib/bolt_spec/plans/mock_executor.rb +9 -6
  70. data/libexec/apply_catalog.rb +1 -1
  71. data/libexec/custom_facts.rb +1 -1
  72. data/libexec/query_resources.rb +1 -1
  73. metadata +12 -14
  74. data/lib/bolt/project_migrator.rb +0 -80
  75. data/modules/secure_env_vars/plans/init.pp +0 -20
@@ -6,20 +6,19 @@ require 'bolt/util'
6
6
  module Bolt
7
7
  module PuppetDB
8
8
  class Config
9
- if !ENV['HOME'].nil?
10
- DEFAULT_TOKEN = File.expand_path('~/.puppetlabs/token')
11
- DEFAULT_CONFIG = { user: File.expand_path('~/.puppetlabs/client-tools/puppetdb.conf'),
12
- global: '/etc/puppetlabs/client-tools/puppetdb.conf' }.freeze
13
- else
9
+ if ENV['HOME'].nil?
14
10
  DEFAULT_TOKEN = Bolt::Util.windows? ? 'nul' : '/dev/null'
15
11
  DEFAULT_CONFIG = { user: '/etc/puppetlabs/puppet/puppetdb.conf',
16
12
  global: '/etc/puppetlabs/puppet/puppetdb.conf' }.freeze
13
+ else
14
+ DEFAULT_TOKEN = File.expand_path('~/.puppetlabs/token')
15
+ DEFAULT_CONFIG = { user: File.expand_path('~/.puppetlabs/client-tools/puppetdb.conf'),
16
+ global: '/etc/puppetlabs/client-tools/puppetdb.conf' }.freeze
17
17
 
18
18
  end
19
19
 
20
20
  def self.default_windows_config
21
- require 'win32/dir'
22
- File.expand_path(File.join(Dir::COMMON_APPDATA, 'PuppetLabs/client-tools/puppetdb.conf'))
21
+ File.expand_path(File.join(ENV['ALLUSERSPROFILE'], 'PuppetLabs/client-tools/puppetdb.conf'))
23
22
  end
24
23
 
25
24
  def self.load_config(options, project_path = nil)
@@ -89,6 +88,8 @@ module Bolt
89
88
 
90
89
  def uri
91
90
  return @uri if @uri
91
+ require 'addressable/uri'
92
+
92
93
  uri = case @settings['server_urls']
93
94
  when String
94
95
  @settings['server_urls']
@@ -100,7 +101,7 @@ module Bolt
100
101
  raise Bolt::PuppetDBError, "server_urls must be a string or array"
101
102
  end
102
103
 
103
- @uri = URI.parse(uri)
104
+ @uri = Addressable::URI.parse(uri)
104
105
  @uri.port ||= 8081
105
106
  @uri
106
107
  end
@@ -7,47 +7,58 @@ module Bolt
7
7
  class Result
8
8
  attr_reader :target, :value, :action, :object
9
9
 
10
- def self.from_exception(target, exception, action: 'action')
10
+ def self.from_exception(target, exception, action: 'action', position: [])
11
+ details = create_details(position)
11
12
  if exception.is_a?(Bolt::Error)
12
- error = exception.to_h
13
+ error = Bolt::Util.deep_merge({ 'details' => details }, exception.to_h)
13
14
  else
15
+ details['class'] = exception.class.to_s
14
16
  error = {
15
17
  'kind' => 'puppetlabs.tasks/exception-error',
16
18
  'issue_code' => 'EXCEPTION',
17
19
  'msg' => exception.message,
18
- 'details' => { 'class' => exception.class.to_s }
20
+ 'details' => details
19
21
  }
20
22
  error['details']['stack_trace'] = exception.backtrace.join('\n') if exception.backtrace
21
23
  end
22
24
  Result.new(target, error: error, action: action)
23
25
  end
24
26
 
25
- def self.for_command(target, stdout, stderr, exit_code, action, command)
27
+ def self.create_details(position)
28
+ %w[file line].zip(position).to_h.compact
29
+ end
30
+
31
+ def self.for_command(target, stdout, stderr, exit_code, action, command, position)
26
32
  value = {
27
33
  'stdout' => stdout,
28
34
  'stderr' => stderr,
29
35
  'exit_code' => exit_code
30
36
  }
37
+
38
+ details = create_details(position)
31
39
  unless exit_code == 0
40
+ details['exit_code'] = exit_code
32
41
  value['_error'] = {
33
42
  'kind' => 'puppetlabs.tasks/command-error',
34
43
  'issue_code' => 'COMMAND_ERROR',
35
44
  'msg' => "The command failed with exit code #{exit_code}",
36
- 'details' => { 'exit_code' => exit_code }
45
+ 'details' => details
37
46
  }
38
47
  end
39
48
  new(target, value: value, action: action, object: command)
40
49
  end
41
50
 
42
- def self.for_task(target, stdout, stderr, exit_code, task)
51
+ def self.for_task(target, stdout, stderr, exit_code, task, position)
43
52
  stdout.force_encoding('utf-8') unless stdout.encoding == Encoding::UTF_8
53
+
54
+ details = create_details(position)
44
55
  value = if stdout.valid_encoding?
45
56
  parse_hash(stdout) || { '_output' => stdout }
46
57
  else
47
58
  { '_error' => { 'kind' => 'puppetlabs.tasks/task-error',
48
59
  'issue_code' => 'TASK_ERROR',
49
60
  'msg' => 'The task result contained invalid UTF-8 on stdout',
50
- 'details' => {} } }
61
+ 'details' => details } }
51
62
  end
52
63
 
53
64
  if exit_code != 0 && value['_error'].nil?
@@ -60,24 +71,26 @@ module Bolt
60
71
  else
61
72
  "The task failed with exit code #{exit_code}"
62
73
  end
74
+ details['exit_code'] = exit_code
63
75
  value['_error'] = { 'kind' => 'puppetlabs.tasks/task-error',
64
76
  'issue_code' => 'TASK_ERROR',
65
77
  'msg' => msg,
66
- 'details' => { 'exit_code' => exit_code } }
78
+ 'details' => details }
67
79
  end
68
80
 
69
81
  if value.key?('_error')
70
82
  unless value['_error'].is_a?(Hash) && value['_error'].key?('msg')
83
+ details['original_error'] = value['_error']
71
84
  value['_error'] = {
72
85
  'msg' => "Invalid error returned from task #{task}: #{value['_error'].inspect}. Error "\
73
86
  "must be an object with a msg key.",
74
87
  'kind' => 'bolt/invalid-task-error',
75
- 'details' => { 'original_error' => value['_error'] }
88
+ 'details' => details
76
89
  }
77
90
  end
78
91
 
79
92
  value['_error']['kind'] ||= 'bolt/error'
80
- value['_error']['details'] ||= {}
93
+ value['_error']['details'] ||= details
81
94
  end
82
95
 
83
96
  if value.key?('_sensitive')
@@ -221,7 +234,6 @@ module Bolt
221
234
  def error
222
235
  if error_hash
223
236
  Puppet::DataTypes::Error.from_asserted_hash(error_hash)
224
-
225
237
  end
226
238
  end
227
239
 
@@ -21,14 +21,16 @@ module Bolt
21
21
  ['shell']
22
22
  end
23
23
 
24
- def run_command(command, options = {})
24
+ def run_command(command, options = {}, position = [])
25
25
  running_as(options[:run_as]) do
26
26
  output = execute(command, environment: options[:env_vars], sudoable: true)
27
27
  Bolt::Result.for_command(target,
28
28
  output.stdout.string,
29
29
  output.stderr.string,
30
30
  output.exit_code,
31
- 'command', command)
31
+ 'command',
32
+ command,
33
+ position)
32
34
  end
33
35
  end
34
36
 
@@ -71,7 +73,7 @@ module Bolt
71
73
  end
72
74
  end
73
75
 
74
- def run_script(script, arguments, options = {})
76
+ def run_script(script, arguments, options = {}, position = [])
75
77
  # unpack any Sensitive data
76
78
  arguments = unwrap_sensitive_args(arguments)
77
79
 
@@ -84,12 +86,14 @@ module Bolt
84
86
  output.stdout.string,
85
87
  output.stderr.string,
86
88
  output.exit_code,
87
- 'script', script)
89
+ 'script',
90
+ script,
91
+ position)
88
92
  end
89
93
  end
90
94
  end
91
95
 
92
- def run_task(task, arguments, options = {})
96
+ def run_task(task, arguments, options = {}, position = [])
93
97
  implementation = select_implementation(target, task)
94
98
  executable = implementation['path']
95
99
  input_method = implementation['input_method']
@@ -148,7 +152,8 @@ module Bolt
148
152
  Bolt::Result.for_task(target, output.stdout.string,
149
153
  output.stderr.string,
150
154
  output.exit_code,
151
- task.name)
155
+ task.name,
156
+ position)
152
157
  end
153
158
  end
154
159
 
@@ -194,7 +199,7 @@ module Bolt
194
199
  lines = buffer.split(/(?<=\n)/)
195
200
  # handle_sudo will return the line if it is not a sudo prompt or error
196
201
  lines.map! { |line| handle_sudo(inp, line, stdin) }
197
- lines.join("")
202
+ lines.join
198
203
  # If stream has reached EOF, no password prompt is expected
199
204
  # return an empty string
200
205
  rescue EOFError
@@ -11,7 +11,7 @@ module Bolt
11
11
  def initialize(target, conn)
12
12
  super
13
13
 
14
- extensions = [target.options['extensions'] || []].flatten.map { |ext| ext[0] != '.' ? '.' + ext : ext }
14
+ extensions = [target.options['extensions'] || []].flatten.map { |ext| ext[0] == '.' ? ext : '.' + ext }
15
15
  extensions += target.options['interpreters'].keys if target.options['interpreters']
16
16
  @extensions = DEFAULT_EXTENSIONS + extensions
17
17
  end
@@ -174,7 +174,7 @@ module Bolt
174
174
  Bolt::Result.for_download(target, source, destination, download)
175
175
  end
176
176
 
177
- def run_command(command, options = {})
177
+ def run_command(command, options = {}, position = [])
178
178
  command = [*env_declarations(options[:env_vars]), command].join("\r\n") if options[:env_vars]
179
179
 
180
180
  output = execute(command)
@@ -182,10 +182,12 @@ module Bolt
182
182
  output.stdout.string,
183
183
  output.stderr.string,
184
184
  output.exit_code,
185
- 'command', command)
185
+ 'command',
186
+ command,
187
+ position)
186
188
  end
187
189
 
188
- def run_script(script, arguments, options = {})
190
+ def run_script(script, arguments, options = {}, position = [])
189
191
  # unpack any Sensitive data
190
192
  arguments = unwrap_sensitive_args(arguments)
191
193
  with_tmpdir do |dir|
@@ -204,11 +206,13 @@ module Bolt
204
206
  output.stdout.string,
205
207
  output.stderr.string,
206
208
  output.exit_code,
207
- 'script', script)
209
+ 'script',
210
+ script,
211
+ position)
208
212
  end
209
213
  end
210
214
 
211
- def run_task(task, arguments, _options = {})
215
+ def run_task(task, arguments, _options = {}, position = [])
212
216
  implementation = select_implementation(target, task)
213
217
  executable = implementation['path']
214
218
  input_method = implementation['input_method']
@@ -259,7 +263,8 @@ module Bolt
259
263
  Bolt::Result.for_task(target, output.stdout.string,
260
264
  output.stderr.string,
261
265
  output.exit_code,
262
- task.name)
266
+ task.name,
267
+ position)
263
268
  end
264
269
  end
265
270
 
@@ -42,7 +42,7 @@ module Bolt
42
42
  if targets.empty?
43
43
  Bolt::ResultSet.new([])
44
44
  else
45
- result = executor.run_task(targets, task, params, options)
45
+ result = executor.run_task(targets, task, params, options, [], :trace)
46
46
 
47
47
  if !result.ok && !options[:catch_errors]
48
48
  raise Bolt::RunFailure.new(result, 'run_task', task.name)
@@ -43,13 +43,13 @@ module Bolt
43
43
  @logger = Bolt::Logger.logger(self)
44
44
  end
45
45
 
46
- def with_events(target, callback, action)
46
+ def with_events(target, callback, action, position)
47
47
  callback&.call(type: :node_start, target: target)
48
48
 
49
49
  result = begin
50
50
  yield
51
51
  rescue StandardError, NotImplementedError => e
52
- Bolt::Result.from_exception(target, e, action: action)
52
+ Bolt::Result.from_exception(target, e, action: action, position: position)
53
53
  end
54
54
 
55
55
  callback&.call(type: :node_result, result: result)
@@ -100,12 +100,12 @@ module Bolt
100
100
  # The default implementation only supports batches of size 1 and will fail otherwise.
101
101
  #
102
102
  # Transports may override this method to implement their own batch processing.
103
- def batch_task(targets, task, arguments, options = {}, &callback)
103
+ def batch_task(targets, task, arguments, options = {}, position = [], &callback)
104
104
  assert_batch_size_one("batch_task()", targets)
105
105
  target = targets.first
106
- with_events(target, callback, 'task') do
106
+ with_events(target, callback, 'task', position) do
107
107
  @logger.debug { "Running task '#{task.name}' on #{target.safe_name}" }
108
- run_task(target, task, arguments, options)
108
+ run_task(target, task, arguments, options, position)
109
109
  end
110
110
  end
111
111
 
@@ -114,14 +114,14 @@ module Bolt
114
114
  # The default implementation only supports batches of size 1 and will fail otherwise.
115
115
  #
116
116
  # Transports may override this method to implment their own batch processing.
117
- def batch_task_with(targets, task, target_mapping, options = {}, &callback)
117
+ def batch_task_with(targets, task, target_mapping, options = {}, position = [], &callback)
118
118
  assert_batch_size_one("batch_task_with()", targets)
119
119
  target = targets.first
120
120
  arguments = target_mapping[target]
121
121
 
122
- with_events(target, callback, 'task') do
122
+ with_events(target, callback, 'task', position) do
123
123
  @logger.debug { "Running task '#{task.name}' on #{target.safe_name} with '#{arguments.to_json}'" }
124
- run_task(target, task, arguments, options)
124
+ run_task(target, task, arguments, options, position)
125
125
  end
126
126
  end
127
127
 
@@ -130,12 +130,12 @@ module Bolt
130
130
  # The default implementation only supports batches of size 1 and will fail otherwise.
131
131
  #
132
132
  # Transports may override this method to implement their own batch processing.
133
- def batch_command(targets, command, options = {}, &callback)
133
+ def batch_command(targets, command, options = {}, position = [], &callback)
134
134
  assert_batch_size_one("batch_command()", targets)
135
135
  target = targets.first
136
- with_events(target, callback, 'command') do
136
+ with_events(target, callback, 'command', position) do
137
137
  @logger.debug("Running command '#{command}' on #{target.safe_name}")
138
- run_command(target, command, options)
138
+ run_command(target, command, options, position)
139
139
  end
140
140
  end
141
141
 
@@ -144,12 +144,12 @@ module Bolt
144
144
  # The default implementation only supports batches of size 1 and will fail otherwise.
145
145
  #
146
146
  # Transports may override this method to implement their own batch processing.
147
- def batch_script(targets, script, arguments, options = {}, &callback)
147
+ def batch_script(targets, script, arguments, options = {}, position = [], &callback)
148
148
  assert_batch_size_one("batch_script()", targets)
149
149
  target = targets.first
150
- with_events(target, callback, 'script') do
150
+ with_events(target, callback, 'script', position) do
151
151
  @logger.debug { "Running script '#{script}' on #{target.safe_name}" }
152
- run_script(target, script, arguments, options)
152
+ run_script(target, script, arguments, options, position)
153
153
  end
154
154
  end
155
155
 
@@ -158,10 +158,10 @@ module Bolt
158
158
  # The default implementation only supports batches of size 1 and will fail otherwise.
159
159
  #
160
160
  # Transports may override this method to implement their own batch processing.
161
- def batch_upload(targets, source, destination, options = {}, &callback)
161
+ def batch_upload(targets, source, destination, options = {}, position = [], &callback)
162
162
  assert_batch_size_one("batch_upload()", targets)
163
163
  target = targets.first
164
- with_events(target, callback, 'upload') do
164
+ with_events(target, callback, 'upload', position) do
165
165
  @logger.debug { "Uploading: '#{source}' to #{destination} on #{target.safe_name}" }
166
166
  upload(target, source, destination, options)
167
167
  end
@@ -173,12 +173,12 @@ module Bolt
173
173
  # The default implementation only supports batches of size 1 and will fail otherwise.
174
174
  #
175
175
  # Transports may override this method to implement their own batch processing.
176
- def batch_download(targets, source, destination, options = {}, &callback)
176
+ def batch_download(targets, source, destination, options = {}, position = [], &callback)
177
177
  require 'erb'
178
178
 
179
179
  assert_batch_size_one("batch_download()", targets)
180
180
  target = targets.first
181
- with_events(target, callback, 'download') do
181
+ with_events(target, callback, 'download', position) do
182
182
  escaped_name = ERB::Util.url_encode(target.safe_name)
183
183
  target_destination = File.expand_path(escaped_name, destination)
184
184
  @logger.debug { "Downloading: '#{source}' on #{target.safe_name} to #{target_destination}" }
@@ -46,7 +46,7 @@ module Bolt
46
46
  end
47
47
  end
48
48
 
49
- def run_command(target, command, options = {})
49
+ def run_command(target, command, options = {}, position = [])
50
50
  execute_options = {}
51
51
  execute_options[:tty] = target.options['tty']
52
52
  execute_options[:environment] = options[:env_vars]
@@ -58,11 +58,17 @@ module Bolt
58
58
  end
59
59
  with_connection(target) do |conn|
60
60
  stdout, stderr, exitcode = conn.execute(*Shellwords.split(command), execute_options)
61
- Bolt::Result.for_command(target, stdout, stderr, exitcode, 'command', command)
61
+ Bolt::Result.for_command(target,
62
+ stdout,
63
+ stderr,
64
+ exitcode,
65
+ 'command',
66
+ command,
67
+ position)
62
68
  end
63
69
  end
64
70
 
65
- def run_script(target, script, arguments, options = {})
71
+ def run_script(target, script, arguments, options = {}, position = [])
66
72
  # unpack any Sensitive data
67
73
  arguments = unwrap_sensitive_args(arguments)
68
74
  execute_options = {}
@@ -72,12 +78,18 @@ module Bolt
72
78
  conn.with_remote_tmpdir do |dir|
73
79
  remote_path = conn.write_remote_executable(dir, script)
74
80
  stdout, stderr, exitcode = conn.execute(remote_path, *arguments, execute_options)
75
- Bolt::Result.for_command(target, stdout, stderr, exitcode, 'script', script)
81
+ Bolt::Result.for_command(target,
82
+ stdout,
83
+ stderr,
84
+ exitcode,
85
+ 'script',
86
+ script,
87
+ position)
76
88
  end
77
89
  end
78
90
  end
79
91
 
80
- def run_task(target, task, arguments, _options = {})
92
+ def run_task(target, task, arguments, _options = {}, position = [])
81
93
  implementation = task.select_implementation(target, provided_features)
82
94
  executable = implementation['path']
83
95
  input_method = implementation['input_method']
@@ -113,7 +125,12 @@ module Bolt
113
125
  end
114
126
 
115
127
  stdout, stderr, exitcode = conn.execute(remote_task_path, execute_options)
116
- Bolt::Result.for_task(target, stdout, stderr, exitcode, task.name)
128
+ Bolt::Result.for_task(target,
129
+ stdout,
130
+ stderr,
131
+ exitcode,
132
+ task.name,
133
+ position)
117
134
  end
118
135
  end
119
136
  end
@@ -10,11 +10,6 @@ require 'bolt/transport/orch/connection'
10
10
  module Bolt
11
11
  module Transport
12
12
  class Orch < Base
13
- CONF_FILE = if !ENV['HOME'].nil?
14
- File.expand_path('~/.puppetlabs/client-tools/orchestrator.conf')
15
- else
16
- '/etc/puppetlabs/client-tools/orchestrator.conf'
17
- end
18
13
  BOLT_COMMAND_TASK = Struct.new(:name).new('bolt_shim::command').freeze
19
14
  BOLT_SCRIPT_TASK = Struct.new(:name).new('bolt_shim::script').freeze
20
15
  BOLT_UPLOAD_TASK = Struct.new(:name).new('bolt_shim::upload').freeze
@@ -53,7 +48,7 @@ module Bolt
53
48
  conn
54
49
  end
55
50
 
56
- def process_run_results(targets, results, task_name)
51
+ def process_run_results(targets, results, task_name, position = [])
57
52
  targets_by_name = Hash[targets.map { |t| t.host || t.name }.zip(targets)]
58
53
  results.map do |node_result|
59
54
  target = targets_by_name[node_result['name']]
@@ -63,25 +58,31 @@ module Bolt
63
58
  # If it's finished or already has a proper error simply pass it to the
64
59
  # the result otherwise make sure an error is generated
65
60
  if state == 'finished' || (result && result['_error'])
61
+ if result['_error']
62
+ file_line = %w[file line].zip(position).to_h.compact
63
+ result['_error']['details'].merge!(file_line) unless result['_error']['details']['file']
64
+ end
65
+
66
66
  Bolt::Result.new(target, value: result, action: 'task', object: task_name)
67
67
  elsif state == 'skipped'
68
+ details = %w[file line].zip(position).to_h.compact
68
69
  Bolt::Result.new(
69
70
  target,
70
71
  value: { '_error' => {
71
72
  'kind' => 'puppetlabs.tasks/skipped-node',
72
73
  'msg' => "Target #{target.safe_name} was skipped",
73
- 'details' => {}
74
+ 'details' => details
74
75
  } },
75
76
  action: 'task', object: task_name
76
77
  )
77
78
  else
78
79
  # Make a generic error with a unkown exit_code
79
- Bolt::Result.for_task(target, result.to_json, '', 'unknown', task_name)
80
+ Bolt::Result.for_task(target, result.to_json, '', 'unknown', task_name, position)
80
81
  end
81
82
  end
82
83
  end
83
84
 
84
- def batch_command(targets, command, options = {}, &callback)
85
+ def batch_command(targets, command, options = {}, position = [], &callback)
85
86
  if options[:env_vars] && !options[:env_vars].empty?
86
87
  raise NotImplementedError, "pcp transport does not support setting environment variables"
87
88
  end
@@ -93,6 +94,7 @@ module Bolt
93
94
  BOLT_COMMAND_TASK,
94
95
  params,
95
96
  options,
97
+ position,
96
98
  &callback)
97
99
  callback ||= proc {}
98
100
  results.map! { |result| unwrap_bolt_result(result.target, result, 'command', command) }
@@ -101,7 +103,7 @@ module Bolt
101
103
  end
102
104
  end
103
105
 
104
- def batch_script(targets, script, arguments, options = {}, &callback)
106
+ def batch_script(targets, script, arguments, options = {}, position = [], &callback)
105
107
  if options[:env_vars] && !options[:env_vars].empty?
106
108
  raise NotImplementedError, "pcp transport does not support setting environment variables"
107
109
  end
@@ -114,7 +116,7 @@ module Bolt
114
116
  'name' => Pathname(script).basename.to_s
115
117
  }
116
118
  callback ||= proc {}
117
- results = run_task_job(targets, BOLT_SCRIPT_TASK, params, options, &callback)
119
+ results = run_task_job(targets, BOLT_SCRIPT_TASK, params, options, position, &callback)
118
120
  results.map! { |result| unwrap_bolt_result(result.target, result, 'script', script) }
119
121
  results.each do |result|
120
122
  callback.call(type: :node_result, result: result)
@@ -155,7 +157,7 @@ module Bolt
155
157
  output&.close
156
158
  end
157
159
 
158
- def batch_upload(targets, source, destination, options = {}, &callback)
160
+ def batch_upload(targets, source, destination, options = {}, position = [], &callback)
159
161
  stat = File.stat(source)
160
162
  content = if stat.directory?
161
163
  pack(source)
@@ -171,7 +173,7 @@ module Bolt
171
173
  'directory' => stat.directory?
172
174
  }
173
175
  callback ||= proc {}
174
- results = run_task_job(targets, BOLT_UPLOAD_TASK, params, options, &callback)
176
+ results = run_task_job(targets, BOLT_UPLOAD_TASK, params, options, position, &callback)
175
177
  results.map! do |result|
176
178
  if result.error_hash
177
179
  result
@@ -200,7 +202,7 @@ module Bolt
200
202
  targets.group_by { |target| Connection.get_key(target.options) }.values
201
203
  end
202
204
 
203
- def run_task_job(targets, task, arguments, options)
205
+ def run_task_job(targets, task, arguments, options, position)
204
206
  targets.each do |target|
205
207
  yield(type: :node_start, target: target) if block_given?
206
208
  end
@@ -210,7 +212,7 @@ module Bolt
210
212
  arguments = unwrap_sensitive_args(arguments)
211
213
  results = get_connection(targets.first.options).run_task(targets, task, arguments, options)
212
214
 
213
- process_run_results(targets, results, task.name)
215
+ process_run_results(targets, results, task.name, position)
214
216
  rescue OrchestratorClient::ApiError => e
215
217
  targets.map do |target|
216
218
  Bolt::Result.new(target, error: e.data)
@@ -222,15 +224,15 @@ module Bolt
222
224
  end
223
225
  end
224
226
 
225
- def batch_task(targets, task, arguments, options = {}, &callback)
227
+ def batch_task(targets, task, arguments, options = {}, position = [], &callback)
226
228
  callback ||= proc {}
227
- results = run_task_job(targets, task, arguments, options, &callback)
229
+ results = run_task_job(targets, task, arguments, options, position, &callback)
228
230
  results.each do |result|
229
231
  callback.call(type: :node_result, result: result)
230
232
  end
231
233
  end
232
234
 
233
- def batch_task_with(_targets, _task, _target_mapping, _options = {})
235
+ def batch_task_with(_targets, _task, _target_mapping, _options = {}, _position = [])
234
236
  raise NotImplementedError, "pcp transport does not support run_task_with()"
235
237
  end
236
238
 
@@ -248,11 +250,13 @@ module Bolt
248
250
  return result
249
251
  end
250
252
 
253
+ # If we get here, there's no error so we don't need the file or line
254
+ # number
251
255
  Bolt::Result.for_command(target,
252
256
  result.value['stdout'],
253
257
  result.value['stderr'],
254
258
  result.value['exit_code'],
255
- action, obj)
259
+ action, obj, [])
256
260
  end
257
261
  end
258
262
  end