bolt 0.7.0 → 0.8.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/lib/bolt/cli.rb +65 -28
  3. data/lib/bolt/config.rb +18 -8
  4. data/lib/bolt/executor.rb +21 -6
  5. data/lib/bolt/node.rb +3 -0
  6. data/lib/bolt/node/result.rb +5 -0
  7. data/lib/bolt/node/ssh.rb +81 -25
  8. data/lib/bolt/node/winrm.rb +70 -31
  9. data/lib/bolt/notifier.rb +20 -0
  10. data/lib/bolt/outputter.rb +21 -0
  11. data/lib/bolt/outputter/human.rb +30 -0
  12. data/lib/bolt/outputter/json.rb +51 -0
  13. data/lib/bolt/result.rb +32 -5
  14. data/lib/bolt/version.rb +1 -1
  15. data/vendored/puppet/lib/puppet.rb +4 -5
  16. data/vendored/puppet/lib/puppet/agent.rb +22 -2
  17. data/vendored/puppet/lib/puppet/application/agent.rb +1 -1
  18. data/vendored/puppet/lib/puppet/application/apply.rb +1 -1
  19. data/vendored/puppet/lib/puppet/configurer/downloader_factory.rb +10 -0
  20. data/vendored/puppet/lib/puppet/configurer/plugin_handler.rb +4 -4
  21. data/vendored/puppet/lib/puppet/defaults.rb +21 -2
  22. data/vendored/puppet/lib/puppet/external/nagios/parser.rb +1 -1
  23. data/vendored/puppet/lib/puppet/file_serving/configuration.rb +3 -0
  24. data/vendored/puppet/lib/puppet/file_serving/configuration/parser.rb +2 -0
  25. data/vendored/puppet/lib/puppet/file_serving/mount/locales.rb +35 -0
  26. data/vendored/puppet/lib/puppet/forge.rb +9 -3
  27. data/vendored/puppet/lib/puppet/forge/repository.rb +1 -1
  28. data/vendored/puppet/lib/puppet/functions/file_upload.rb +20 -15
  29. data/vendored/puppet/lib/puppet/functions/new.rb +1 -4
  30. data/vendored/puppet/lib/puppet/functions/run_command.rb +15 -13
  31. data/vendored/puppet/lib/puppet/functions/run_script.rb +27 -14
  32. data/vendored/puppet/lib/puppet/functions/run_task.rb +21 -19
  33. data/vendored/puppet/lib/puppet/gettext/config.rb +86 -28
  34. data/vendored/puppet/lib/puppet/indirector/catalog/compiler.rb +25 -5
  35. data/vendored/puppet/lib/puppet/indirector/file_bucket_file/file.rb +1 -1
  36. data/vendored/puppet/lib/puppet/module.rb +13 -17
  37. data/vendored/puppet/lib/puppet/pops/evaluator/access_operator.rb +20 -21
  38. data/vendored/puppet/lib/puppet/pops/evaluator/compare_operator.rb +3 -3
  39. data/vendored/puppet/lib/puppet/pops/evaluator/evaluator_impl.rb +9 -0
  40. data/vendored/puppet/lib/puppet/pops/loader/static_loader.rb +20 -1
  41. data/vendored/puppet/lib/puppet/pops/loader/task_instantiator.rb +2 -1
  42. data/vendored/puppet/lib/puppet/pops/loaders.rb +6 -41
  43. data/vendored/puppet/lib/puppet/pops/pcore.rb +9 -0
  44. data/vendored/puppet/lib/puppet/pops/serialization/from_data_converter.rb +64 -10
  45. data/vendored/puppet/lib/puppet/pops/serialization/json_path.rb +2 -1
  46. data/vendored/puppet/lib/puppet/pops/types/execution_result.rb +7 -4
  47. data/vendored/puppet/lib/puppet/pops/types/p_binary_type.rb +9 -2
  48. data/vendored/puppet/lib/puppet/pops/types/p_init_type.rb +1 -1
  49. data/vendored/puppet/lib/puppet/pops/types/p_meta_type.rb +4 -0
  50. data/vendored/puppet/lib/puppet/pops/types/p_object_type.rb +81 -4
  51. data/vendored/puppet/lib/puppet/pops/types/p_object_type_extension.rb +213 -0
  52. data/vendored/puppet/lib/puppet/pops/types/p_sem_ver_type.rb +10 -2
  53. data/vendored/puppet/lib/puppet/pops/types/puppet_object.rb +11 -1
  54. data/vendored/puppet/lib/puppet/pops/types/type_calculator.rb +2 -2
  55. data/vendored/puppet/lib/puppet/pops/types/type_factory.rb +16 -6
  56. data/vendored/puppet/lib/puppet/pops/types/type_formatter.rb +22 -14
  57. data/vendored/puppet/lib/puppet/pops/types/type_parser.rb +17 -15
  58. data/vendored/puppet/lib/puppet/pops/types/types.rb +181 -72
  59. data/vendored/puppet/lib/puppet/provider.rb +18 -8
  60. data/vendored/puppet/lib/puppet/provider/package/yum.rb +22 -7
  61. data/vendored/puppet/lib/puppet/provider/service/base.rb +21 -8
  62. data/vendored/puppet/lib/puppet/provider/service/launchd.rb +2 -3
  63. data/vendored/puppet/lib/puppet/provider/user/aix.rb +1 -0
  64. data/vendored/puppet/lib/puppet/provider/user/user_role_add.rb +7 -1
  65. data/vendored/puppet/lib/puppet/provider/user/useradd.rb +3 -2
  66. data/vendored/puppet/lib/puppet/provider/zfs/zfs.rb +5 -1
  67. data/vendored/puppet/lib/puppet/type/exec.rb +5 -4
  68. data/vendored/puppet/lib/puppet/type/macauthorization.rb +1 -1
  69. data/vendored/puppet/lib/puppet/type/user.rb +19 -0
  70. data/vendored/puppet/lib/puppet/util/log/destinations.rb +10 -0
  71. data/vendored/puppet/lib/puppet/util/windows/file.rb +35 -4
  72. data/vendored/puppet/lib/puppet/vendor/semantic_puppet/lib/semantic_puppet.rb +1 -1
  73. data/vendored/puppet/lib/puppet/version.rb +1 -1
  74. data/vendored/puppet/lib/puppet_pal.rb +15 -5
  75. metadata +8 -3
  76. data/vendored/puppet/lib/puppet/pops/types/p_error_type.rb +0 -158
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b7f70a1d9878c8a40b79b597ed62e5c7307d200b
4
- data.tar.gz: f8bdfadbfe729a572acd6f6feabfb3b334c1f16e
3
+ metadata.gz: 4d17e85d4ead68354a59253671eaa9246fcd9c25
4
+ data.tar.gz: e00bb08318f54e3d03cd1e144a646e9c4e7eb3fc
5
5
  SHA512:
6
- metadata.gz: 68e190c03a5ad2be65fd0832dec9080aaf03de408514d575244997a96663f9f9bdc7ee1b0e18fa4fd35ff3a705fde3b99e40ed5213aa374fa8a359ef28f8a2bf
7
- data.tar.gz: 14cc9838705602039e3fd902f229f25b48a96c9580f54efeeb624087debb8ffec0167b5288bcb23d4c7f0309505f39856a4eb37ce24c068fcccba3dc222247ca
6
+ metadata.gz: 0c8d07c175cda227ca2138956167fec92b403deeb967cdb1d83116f9012c13e9b6ba5d4a4bd2178a40cc04ee7153ebb59734263e2056d3054da5e89ede11bc4e
7
+ data.tar.gz: e750e86b36f107295719ae0f6b8340c0e216208ebf7c15bbf17f99ac5785895a1b556a3ce8aead3b267f90abd3256eb2afa0d8f8df7b365334af5caaf7a6379f
@@ -6,6 +6,7 @@ require 'json'
6
6
  require 'bolt/node'
7
7
  require 'bolt/version'
8
8
  require 'bolt/executor'
9
+ require 'bolt/outputter'
9
10
  require 'bolt/config'
10
11
  require 'io/console'
11
12
 
@@ -17,6 +18,12 @@ module Bolt
17
18
  super(msg)
18
19
  @error_code = error_code
19
20
  end
21
+
22
+ def to_json
23
+ { kind: "bolt/cli-error",
24
+ msg: message,
25
+ details: { error_code: @error_code } }.to_json
26
+ end
20
27
  end
21
28
 
22
29
  class CLIExit < StandardError; end
@@ -149,6 +156,17 @@ HELP
149
156
  "Parameters to a task or plan") do |params|
150
157
  results[:task_options] = parse_params(params)
151
158
  end
159
+
160
+ results[:format] = 'human'
161
+ opts.on('--format FORMAT',
162
+ "Output format to use") do |format|
163
+ if %w[human json].include? format
164
+ results[:format] = format
165
+ else
166
+ raise ArgumentError "Unsupported format: #{format}"
167
+ end
168
+ end
169
+ results[:insecure] = false
152
170
  opts.on('-k', '--insecure',
153
171
  "Whether to connect insecurely ") do |insecure|
154
172
  results[:insecure] = insecure
@@ -157,6 +175,23 @@ HELP
157
175
  "Specify a default transport: #{TRANSPORTS.join(', ')}") do |t|
158
176
  options[:transport] = t
159
177
  end
178
+ opts.on('--run-as USER',
179
+ "User to run as using privilege escalation") do |user|
180
+ options[:run_as] = user
181
+ end
182
+ opts.on('--sudo [PROGRAM]',
183
+ "Program to execute for privilege escalation. " \
184
+ "Currently only sudo is supported.") do |program|
185
+ options[:sudo] = program || 'sudo'
186
+ if options[:sudo] != 'sudo'
187
+ raise Bolt::CLIError,
188
+ "Only 'sudo' is supported for privilege escalation."
189
+ end
190
+ end
191
+ opts.on('--sudo-password [PASSWORD]',
192
+ 'Password for privilege escalation') do |password|
193
+ options[:sudo_password] = password
194
+ end
160
195
  opts.on_tail('--[no-]tty',
161
196
  "Request a pseudo TTY on nodes that support it") do |tty|
162
197
  results[:tty] = tty
@@ -202,6 +237,8 @@ HELP
202
237
  @config[:log_level] = Logger::INFO
203
238
  end
204
239
 
240
+ @config[:format] = options[:format]
241
+
205
242
  if options[:help]
206
243
  print_help(options[:mode])
207
244
  raise Bolt::CLIExit
@@ -327,7 +364,8 @@ HELP
327
364
  end
328
365
 
329
366
  def execute(options)
330
- %i[concurrency user password tty insecure transport].each do |key|
367
+ %i[concurrency user password tty insecure transport
368
+ sudo sudo_password run_as].each do |key|
331
369
  config[key] = options[key]
332
370
  end
333
371
 
@@ -354,15 +392,23 @@ HELP
354
392
  nodes = executor.from_uris(options[:nodes])
355
393
 
356
394
  results = nil
395
+ outputter.print_head
396
+
357
397
  elapsed_time = Benchmark.realtime do
358
398
  results =
359
399
  case options[:mode]
360
400
  when 'command'
361
- executor.run_command(nodes, options[:object])
401
+ executor.run_command(nodes, options[:object]) do |node, result|
402
+ outputter.print_result(node, result)
403
+ end
362
404
  when 'script'
363
405
  script = options[:object]
364
406
  validate_file('script', script)
365
- executor.run_script(nodes, script, options[:leftovers])
407
+ executor.run_script(
408
+ nodes, script, options[:leftovers]
409
+ ) do |node, result|
410
+ outputter.print_result(node, result)
411
+ end
366
412
  when 'task'
367
413
  task_name = options[:object]
368
414
 
@@ -372,7 +418,9 @@ HELP
372
418
  input_method ||= 'both'
373
419
  executor.run_task(
374
420
  nodes, path, input_method, options[:task_options]
375
- )
421
+ ) do |node, result|
422
+ outputter.print_result(node, result)
423
+ end
376
424
  when 'file'
377
425
  src = options[:object]
378
426
  dest = options[:leftovers].first
@@ -381,46 +429,31 @@ HELP
381
429
  raise Bolt::CLIError, "A destination path must be specified"
382
430
  end
383
431
  validate_file('source file', src)
384
- executor.file_upload(nodes, src, dest)
432
+ executor.file_upload(nodes, src, dest) do |node, result|
433
+ outputter.print_result(node, result)
434
+ end
385
435
  end
386
436
  end
387
437
 
388
- print_results(results, elapsed_time)
438
+ outputter.print_summary(results, elapsed_time)
389
439
  end
440
+ rescue Bolt::CLIError => e
441
+ outputter.fatal_error(e)
442
+ raise e
390
443
  end
391
444
 
392
445
  def execute_plan(executor, options)
446
+ # Plans return null here?
393
447
  result = Puppet.override(bolt_executor: executor) do
394
448
  run_plan(options[:object],
395
449
  options[:task_options],
396
450
  options[:modulepath])
397
451
  end
398
- puts result
452
+ outputter.print_plan(result)
399
453
  rescue Puppet::Error
400
454
  raise Bolt::CLIError, "Exiting because of an error in Puppet code"
401
455
  end
402
456
 
403
- def colorize(result, stream)
404
- color = result.success? ? "\033[32m" : "\033[31m"
405
- stream.print color if stream.isatty
406
- yield
407
- stream.print "\033[0m" if stream.isatty
408
- end
409
-
410
- def print_results(results, elapsed_time)
411
- results.each_pair do |node, result|
412
- colorize(result, $stdout) { $stdout.puts "#{node.host}:" }
413
- $stdout.puts
414
- $stdout.puts result.message
415
- $stdout.puts
416
- end
417
-
418
- $stdout.puts format("Ran on %d node%s in %.2f seconds",
419
- results.size,
420
- results.size > 1 ? 's' : '',
421
- elapsed_time)
422
- end
423
-
424
457
  def validate_file(type, path)
425
458
  if path.nil?
426
459
  raise Bolt::CLIError, "A #{type} must be specified"
@@ -441,6 +474,10 @@ HELP
441
474
  File.stat(path)
442
475
  end
443
476
 
477
+ def outputter
478
+ @outputter ||= Bolt::Outputter.for_format(@config[:format])
479
+ end
480
+
444
481
  def load_task_data(name, modulepath)
445
482
  module_name, file_name = name.split('::', 2)
446
483
  file_name ||= 'init'
@@ -1,14 +1,20 @@
1
1
  require 'logger'
2
2
 
3
3
  module Bolt
4
- Config = Struct.new(:concurrency,
5
- :user,
6
- :password,
7
- :tty,
8
- :insecure,
9
- :transport,
10
- :log_level,
11
- :log_destination) do
4
+ Config = Struct.new(
5
+ :concurrency,
6
+ :format,
7
+ :insecure,
8
+ :log_destination,
9
+ :log_level,
10
+ :password,
11
+ :run_as,
12
+ :sudo,
13
+ :sudo_password,
14
+ :transport,
15
+ :tty,
16
+ :user
17
+ ) do
12
18
  DEFAULTS = {
13
19
  concurrency: 100,
14
20
  tty: false,
@@ -22,5 +28,9 @@ module Bolt
22
28
  super()
23
29
  DEFAULTS.merge(kwargs).each { |k, v| self[k] = v }
24
30
  end
31
+
32
+ def escalate?
33
+ sudo || run_as
34
+ end
25
35
  end
26
36
  end
@@ -3,6 +3,7 @@ require 'concurrent'
3
3
  require 'bolt/result'
4
4
  require 'bolt/config'
5
5
  require 'bolt/formatter'
6
+ require 'bolt/notifier'
6
7
 
7
8
  module Bolt
8
9
  class Executor
@@ -12,6 +13,7 @@ module Bolt
12
13
  @logger.progname = 'executor'
13
14
  @logger.level = config[:log_level]
14
15
  @logger.formatter = Bolt::Formatter.new
16
+ @notifier = Bolt::Notifier.new
15
17
  end
16
18
 
17
19
  def from_uris(nodes)
@@ -20,7 +22,7 @@ module Bolt
20
22
  end
21
23
  end
22
24
 
23
- def on(nodes)
25
+ def on(nodes, callback = nil)
24
26
  results = Concurrent::Map.new
25
27
 
26
28
  poolsize = [nodes.length, @config[:concurrency]].min
@@ -33,7 +35,7 @@ module Bolt
33
35
 
34
36
  nodes.each { |node|
35
37
  pool.post do
36
- results[node] =
38
+ result =
37
39
  begin
38
40
  node.connect
39
41
  yield node
@@ -45,34 +47,47 @@ module Bolt
45
47
  ensure
46
48
  node.disconnect
47
49
  end
50
+ results[node] = result
51
+ @notifier.notify(callback, node, result) if callback
52
+ result
48
53
  end
49
54
  }
50
55
  pool.shutdown
51
56
  pool.wait_for_termination
52
57
 
58
+ @notifier.shutdown
59
+
53
60
  results_to_hash(results)
54
61
  end
55
62
 
56
63
  def run_command(nodes, command)
57
- on(nodes) do |node|
64
+ callback = block_given? ? Proc.new : nil
65
+
66
+ on(nodes, callback) do |node|
58
67
  node.run_command(command)
59
68
  end
60
69
  end
61
70
 
62
71
  def run_script(nodes, script, arguments)
63
- on(nodes) do |node|
72
+ callback = block_given? ? Proc.new : nil
73
+
74
+ on(nodes, callback) do |node|
64
75
  node.run_script(script, arguments)
65
76
  end
66
77
  end
67
78
 
68
79
  def run_task(nodes, task, input_method, arguments)
69
- on(nodes) do |node|
80
+ callback = block_given? ? Proc.new : nil
81
+
82
+ on(nodes, callback) do |node|
70
83
  node.run_task(task, input_method, arguments)
71
84
  end
72
85
  end
73
86
 
74
87
  def file_upload(nodes, source, destination)
75
- on(nodes) do |node|
88
+ callback = block_given? ? Proc.new : nil
89
+
90
+ on(nodes, callback) do |node|
76
91
  node.upload(source, destination)
77
92
  end
78
93
  end
@@ -40,6 +40,9 @@ module Bolt
40
40
  @tty = config[:tty]
41
41
  @insecure = config[:insecure]
42
42
  @uri = uri
43
+ @sudo = config[:sudo]
44
+ @sudo_password = config[:sudo_password]
45
+ @run_as = config[:run_as]
43
46
 
44
47
  @logger = init_logger(config[:log_destination], config[:log_level])
45
48
  @transport_logger = init_logger(config[:log_destination], Logger::WARN)
@@ -38,6 +38,11 @@ module Bolt
38
38
  def exit_code
39
39
  0
40
40
  end
41
+
42
+ def ensure
43
+ yield
44
+ self
45
+ end
41
46
  end
42
47
 
43
48
  class Success < Result
@@ -54,9 +54,19 @@ module Bolt
54
54
  end
55
55
  end
56
56
 
57
- def execute(command, options = {})
57
+ def execute(command, sudoable: false, **options)
58
58
  result_output = Bolt::Node::ResultOutput.new
59
59
  status = {}
60
+ use_sudo = sudoable && (@sudo || @run_as)
61
+ sudo_prompt = '[sudo] Bolt needs to run as another user, password: '
62
+ if use_sudo
63
+ user_clause = if @run_as
64
+ "-u #{@run_as}"
65
+ else
66
+ ''
67
+ end
68
+ command = "sudo -S #{user_clause} -p '#{sudo_prompt}' #{command}"
69
+ end
60
70
 
61
71
  @logger.debug { "Executing: #{command}" }
62
72
 
@@ -68,12 +78,22 @@ module Bolt
68
78
  raise "could not execute command: #{command.inspect}" unless success
69
79
 
70
80
  channel.on_data do |_, data|
71
- result_output.stdout << data
81
+ if use_sudo && data == sudo_prompt
82
+ channel.send_data "#{@sudo_password}\n"
83
+ channel.wait
84
+ else
85
+ result_output.stdout << data
86
+ end
72
87
  @logger.debug { "stdout: #{data}" }
73
88
  end
74
89
 
75
90
  channel.on_extended_data do |_, _, data|
76
- result_output.stderr << data
91
+ if use_sudo && data == sudo_prompt
92
+ channel.send_data "#{@sudo_password}\n"
93
+ channel.wait
94
+ else
95
+ result_output.stderr << data
96
+ end
77
97
  @logger.debug { "stderr: #{data}" }
78
98
  end
79
99
 
@@ -113,31 +133,66 @@ module Bolt
113
133
  Bolt::Node::ExceptionFailure.new(e)
114
134
  end
115
135
 
116
- def with_remote_file(file)
117
- remote_path = ''
118
- dir = ''
119
- result = nil
120
-
121
- make_tempdir.then do |value|
122
- dir = value
123
- remote_path = "#{dir}/#{File.basename(file)}"
124
- Bolt::Node::Success.new
125
- end.then do
126
- _upload(file, remote_path)
127
- end.then do
136
+ def with_remote_tempdir
137
+ make_tempdir.then do |dir|
138
+ (yield dir).ensure do
139
+ execute("rm -rf '#{dir}'")
140
+ end
141
+ end
142
+ end
143
+
144
+ def with_remote_script(dir, file)
145
+ remote_path = "#{dir}/#{File.basename(file)}"
146
+ _upload(file, remote_path).then do
128
147
  execute("chmod u+x '#{remote_path}'")
129
148
  end.then do
130
- result = yield remote_path
131
- end.then do
132
- execute("rm -f '#{remote_path}'")
149
+ yield remote_path
150
+ end
151
+ end
152
+
153
+ def with_remote_file(file)
154
+ with_remote_tempdir do |dir|
155
+ with_remote_script(dir, file) do |remote_path|
156
+ yield remote_path
157
+ end
158
+ end
159
+ end
160
+
161
+ def make_wrapper_stringio(task_path, stdin)
162
+ StringIO.new(<<-SCRIPT)
163
+ #!/bin/sh
164
+ '#{task_path}' <<EOF
165
+ #{stdin}
166
+ EOF
167
+ SCRIPT
168
+ end
169
+
170
+ def with_task_wrapper(remote_task, dir, stdin)
171
+ wrapper = make_wrapper_stringio(remote_task, stdin)
172
+ command = "#{dir}/wrapper.sh"
173
+ _upload(wrapper, command).then do
174
+ execute("chmod u+x '#{command}'")
133
175
  end.then do
134
- execute("rmdir '#{dir}'")
135
- result
176
+ yield command
177
+ end
178
+ end
179
+
180
+ def with_remote_task(task_file, stdin)
181
+ with_remote_tempdir do |dir|
182
+ with_remote_script(dir, task_file) do |remote_task|
183
+ if stdin
184
+ with_task_wrapper(remote_task, dir, stdin) do |command|
185
+ yield command
186
+ end
187
+ else
188
+ yield remote_task
189
+ end
190
+ end
136
191
  end
137
192
  end
138
193
 
139
194
  def _run_command(command)
140
- execute(command)
195
+ execute(command, sudoable: true)
141
196
  end
142
197
 
143
198
  def _run_script(script, arguments)
@@ -145,7 +200,8 @@ module Bolt
145
200
  @logger.debug { "arguments: #{arguments}" }
146
201
 
147
202
  with_remote_file(script) do |remote_path|
148
- execute("'#{remote_path}' #{Shellwords.join(arguments)}")
203
+ execute("'#{remote_path}' #{Shellwords.join(arguments)}",
204
+ sudoable: true)
149
205
  end
150
206
  end
151
207
 
@@ -166,13 +222,13 @@ module Bolt
166
222
  end.join(' ')
167
223
  end
168
224
 
169
- with_remote_file(task) do |remote_path|
225
+ with_remote_task(task, stdin) do |remote_path|
170
226
  command = if export_args.empty?
171
227
  "'#{remote_path}'"
172
228
  else
173
- "export #{export_args} && '#{remote_path}'"
229
+ "#{export_args} '#{remote_path}'"
174
230
  end
175
- execute(command, stdin: stdin)
231
+ execute(command, sudoable: true)
176
232
  end
177
233
  end
178
234
  end