bolt 0.8.0 → 0.9.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.
- checksums.yaml +4 -4
- data/lib/bolt/cli.rb +31 -21
- data/lib/bolt/config.rb +1 -0
- data/lib/bolt/error.rb +28 -0
- data/lib/bolt/executor.rb +10 -6
- data/lib/bolt/node.rb +7 -5
- data/lib/bolt/node/errors.rb +25 -3
- data/lib/bolt/node/orch.rb +7 -16
- data/lib/bolt/node/output.rb +17 -0
- data/lib/bolt/node/ssh.rb +101 -44
- data/lib/bolt/node/winrm.rb +86 -40
- data/lib/bolt/outputter/human.rb +65 -8
- data/lib/bolt/outputter/json.rb +8 -1
- data/lib/bolt/result.rb +74 -124
- data/lib/bolt/version.rb +1 -1
- data/{vendored/puppet → modules/boltlib}/lib/puppet/functions/file_upload.rb +9 -7
- data/{vendored/puppet → modules/boltlib}/lib/puppet/functions/run_command.rb +8 -9
- data/{vendored/puppet → modules/boltlib}/lib/puppet/functions/run_plan.rb +8 -12
- data/{vendored/puppet → modules/boltlib}/lib/puppet/functions/run_script.rb +12 -8
- data/{vendored/puppet → modules/boltlib}/lib/puppet/functions/run_task.rb +10 -10
- data/vendored/puppet/lib/puppet/agent.rb +1 -1
- data/vendored/puppet/lib/puppet/application/lookup.rb +1 -3
- data/vendored/puppet/lib/puppet/configurer.rb +2 -3
- data/vendored/puppet/lib/puppet/configurer/plugin_handler.rb +25 -7
- data/vendored/puppet/lib/puppet/defaults.rb +9 -1
- data/vendored/puppet/lib/puppet/face/epp.rb +4 -2
- data/vendored/puppet/lib/puppet/face/module/build.rb +1 -1
- data/vendored/puppet/lib/puppet/face/module/list.rb +5 -16
- data/vendored/puppet/lib/puppet/face/module/uninstall.rb +14 -3
- data/vendored/puppet/lib/puppet/face/plugin.rb +1 -3
- data/vendored/puppet/lib/puppet/forge/errors.rb +17 -7
- data/vendored/puppet/lib/puppet/functions.rb +8 -6
- data/vendored/puppet/lib/puppet/functions/each.rb +10 -4
- data/vendored/puppet/lib/puppet/functions/lookup.rb +2 -2
- data/vendored/puppet/lib/puppet/functions/map.rb +12 -2
- data/vendored/puppet/lib/puppet/functions/slice.rb +2 -3
- data/vendored/puppet/lib/puppet/graph/simple_graph.rb +9 -5
- data/vendored/puppet/lib/puppet/interface.rb +1 -0
- data/vendored/puppet/lib/puppet/module_tool/errors/installer.rb +27 -17
- data/vendored/puppet/lib/puppet/module_tool/errors/shared.rb +143 -63
- data/vendored/puppet/lib/puppet/module_tool/errors/uninstaller.rb +37 -14
- data/vendored/puppet/lib/puppet/module_tool/errors/upgrader.rb +30 -18
- data/vendored/puppet/lib/puppet/network/auth_config_parser.rb +8 -8
- data/vendored/puppet/lib/puppet/network/http/error.rb +7 -7
- data/vendored/puppet/lib/puppet/network/http/rack.rb +2 -2
- data/vendored/puppet/lib/puppet/network/http/webrick.rb +1 -1
- data/vendored/puppet/lib/puppet/node.rb +10 -0
- data/vendored/puppet/lib/puppet/node/facts.rb +9 -0
- data/vendored/puppet/lib/puppet/parameter/value_collection.rb +16 -6
- data/vendored/puppet/lib/puppet/parser/resource.rb +103 -31
- data/vendored/puppet/lib/puppet/pops/evaluator/access_operator.rb +13 -0
- data/vendored/puppet/lib/puppet/pops/evaluator/evaluator_impl.rb +22 -6
- data/vendored/puppet/lib/puppet/pops/loader/static_loader.rb +1 -1
- data/vendored/puppet/lib/puppet/pops/lookup/lookup_adapter.rb +13 -4
- data/vendored/puppet/lib/puppet/pops/model/ast_transformer.rb +1 -1
- data/vendored/puppet/lib/puppet/pops/parser/eparser.rb +527 -529
- data/vendored/puppet/lib/puppet/pops/serialization/abstract_reader.rb +4 -0
- data/vendored/puppet/lib/puppet/pops/serialization/abstract_writer.rb +6 -0
- data/vendored/puppet/lib/puppet/pops/serialization/extension.rb +1 -0
- data/vendored/puppet/lib/puppet/pops/serialization/serializer.rb +2 -1
- data/vendored/puppet/lib/puppet/pops/types/execution_result.rb +8 -0
- data/vendored/puppet/lib/puppet/pops/types/iterable.rb +2 -0
- data/vendored/puppet/lib/puppet/pops/types/p_object_type.rb +3 -0
- data/vendored/puppet/lib/puppet/pops/types/p_object_type_extension.rb +6 -0
- data/vendored/puppet/lib/puppet/pops/types/p_uri_type.rb +191 -0
- data/vendored/puppet/lib/puppet/pops/types/string_converter.rb +17 -0
- data/vendored/puppet/lib/puppet/pops/types/type_calculator.rb +5 -0
- data/vendored/puppet/lib/puppet/pops/types/type_factory.rb +7 -0
- data/vendored/puppet/lib/puppet/pops/types/type_formatter.rb +16 -18
- data/vendored/puppet/lib/puppet/pops/types/type_mismatch_describer.rb +15 -5
- data/vendored/puppet/lib/puppet/pops/types/type_parser.rb +6 -0
- data/vendored/puppet/lib/puppet/pops/types/type_with_members.rb +43 -0
- data/vendored/puppet/lib/puppet/pops/types/types.rb +3 -0
- data/vendored/puppet/lib/puppet/provider/package/gem.rb +1 -1
- data/vendored/puppet/lib/puppet/provider/package/nim.rb +7 -8
- data/vendored/puppet/lib/puppet/provider/package/opkg.rb +1 -1
- data/vendored/puppet/lib/puppet/provider/package/pkg.rb +6 -4
- data/vendored/puppet/lib/puppet/provider/package/pkgutil.rb +3 -3
- data/vendored/puppet/lib/puppet/provider/service/init.rb +1 -1
- data/vendored/puppet/lib/puppet/syntax_checkers/base64.rb +5 -6
- data/vendored/puppet/lib/puppet/transaction.rb +1 -1
- data/vendored/puppet/lib/puppet/type.rb +1 -9
- data/vendored/puppet/lib/puppet/type/schedule.rb +1 -1
- data/vendored/puppet/lib/puppet/util/log.rb +2 -3
- data/vendored/puppet/lib/puppet/util/plist.rb +1 -1
- data/vendored/puppet/lib/puppet/util/reference.rb +2 -3
- data/vendored/puppet/lib/puppet_pal.rb +326 -53
- metadata +28 -12
- data/lib/bolt/node/result.rb +0 -115
- data/vendored/puppet/lib/puppet/configurer/downloader_factory.rb +0 -44
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 80377af090a1f1d5fd9515eac97623064459ce9f
|
4
|
+
data.tar.gz: 1b73aeaa5dce3a2c3b37581905212e00c0ed2afa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 445a3d714f38f2e8ab548df6c8b7b7ec777f407ec44625533c7fe1b1983210f5d82c648d149f9c1ba3de295b53422af27e89d1f1267ac2980d08bd12fabd4d66
|
7
|
+
data.tar.gz: d0ce607c758b9431c48e978d5ca1e67470a2afd3be1ce35e5694e5a3bed5f82487c5c082daaf19cf1880c2fac16ec9297efaf8105c4b9fc7ab8970dc69eab5ca
|
data/lib/bolt/cli.rb
CHANGED
@@ -5,25 +5,20 @@ require 'logger'
|
|
5
5
|
require 'json'
|
6
6
|
require 'bolt/node'
|
7
7
|
require 'bolt/version'
|
8
|
+
require 'bolt/error'
|
8
9
|
require 'bolt/executor'
|
9
10
|
require 'bolt/outputter'
|
10
11
|
require 'bolt/config'
|
11
12
|
require 'io/console'
|
12
13
|
|
13
14
|
module Bolt
|
14
|
-
class CLIError <
|
15
|
+
class CLIError < Bolt::Error
|
15
16
|
attr_reader :error_code
|
16
17
|
|
17
18
|
def initialize(msg, error_code: 1)
|
18
|
-
super(msg)
|
19
|
+
super(msg, "bolt/cli-error")
|
19
20
|
@error_code = error_code
|
20
21
|
end
|
21
|
-
|
22
|
-
def to_json
|
23
|
-
{ kind: "bolt/cli-error",
|
24
|
-
msg: message,
|
25
|
-
details: { error_code: @error_code } }.to_json
|
26
|
-
end
|
27
22
|
end
|
28
23
|
|
29
24
|
class CLIExit < StandardError; end
|
@@ -94,6 +89,7 @@ HELP
|
|
94
89
|
MODES = %w[command script task plan file].freeze
|
95
90
|
ACTIONS = %w[run upload download].freeze
|
96
91
|
TRANSPORTS = %w[ssh winrm pcp].freeze
|
92
|
+
BOLTLIB_PATH = File.join(__FILE__, '../../../modules')
|
97
93
|
|
98
94
|
attr_reader :parser, :config
|
99
95
|
attr_accessor :options
|
@@ -142,6 +138,10 @@ HELP
|
|
142
138
|
end
|
143
139
|
end
|
144
140
|
results[:concurrency] = 100
|
141
|
+
opts.on('--private-key KEY',
|
142
|
+
"Private ssh key to authenticate with (Optional)") do |key|
|
143
|
+
results[:key] = key
|
144
|
+
end
|
145
145
|
opts.on('-c', '--concurrency CONCURRENCY', Integer,
|
146
146
|
"Maximum number of simultaneous connections " \
|
147
147
|
"(Optional, defaults to 100)") do |concurrency|
|
@@ -159,11 +159,11 @@ HELP
|
|
159
159
|
|
160
160
|
results[:format] = 'human'
|
161
161
|
opts.on('--format FORMAT',
|
162
|
-
"Output format to use") do |format|
|
162
|
+
"Output format to use: human or json") do |format|
|
163
163
|
if %w[human json].include? format
|
164
164
|
results[:format] = format
|
165
165
|
else
|
166
|
-
raise
|
166
|
+
raise Bolt::CLIError, "Unsupported format: #{format}"
|
167
167
|
end
|
168
168
|
end
|
169
169
|
results[:insecure] = false
|
@@ -190,7 +190,13 @@ HELP
|
|
190
190
|
end
|
191
191
|
opts.on('--sudo-password [PASSWORD]',
|
192
192
|
'Password for privilege escalation') do |password|
|
193
|
-
|
193
|
+
if password.nil?
|
194
|
+
STDOUT.print "Please enter your privilege escalation password: "
|
195
|
+
results[:sudo_password] = STDIN.noecho(&:gets).chomp
|
196
|
+
STDOUT.puts
|
197
|
+
else
|
198
|
+
results[:sudo_password] = password
|
199
|
+
end
|
194
200
|
end
|
195
201
|
opts.on_tail('--[no-]tty',
|
196
202
|
"Request a pseudo TTY on nodes that support it") do |tty|
|
@@ -237,6 +243,8 @@ HELP
|
|
237
243
|
@config[:log_level] = Logger::INFO
|
238
244
|
end
|
239
245
|
|
246
|
+
@config[:key] = options[:key]
|
247
|
+
|
240
248
|
@config[:format] = options[:format]
|
241
249
|
|
242
250
|
if options[:help]
|
@@ -398,16 +406,16 @@ HELP
|
|
398
406
|
results =
|
399
407
|
case options[:mode]
|
400
408
|
when 'command'
|
401
|
-
executor.run_command(nodes, options[:object]) do |node,
|
402
|
-
outputter.
|
409
|
+
executor.run_command(nodes, options[:object]) do |node, event|
|
410
|
+
outputter.print_event(node, event)
|
403
411
|
end
|
404
412
|
when 'script'
|
405
413
|
script = options[:object]
|
406
414
|
validate_file('script', script)
|
407
415
|
executor.run_script(
|
408
416
|
nodes, script, options[:leftovers]
|
409
|
-
) do |node,
|
410
|
-
outputter.
|
417
|
+
) do |node, event|
|
418
|
+
outputter.print_event(node, event)
|
411
419
|
end
|
412
420
|
when 'task'
|
413
421
|
task_name = options[:object]
|
@@ -418,8 +426,8 @@ HELP
|
|
418
426
|
input_method ||= 'both'
|
419
427
|
executor.run_task(
|
420
428
|
nodes, path, input_method, options[:task_options]
|
421
|
-
) do |node,
|
422
|
-
outputter.
|
429
|
+
) do |node, event|
|
430
|
+
outputter.print_event(node, event)
|
423
431
|
end
|
424
432
|
when 'file'
|
425
433
|
src = options[:object]
|
@@ -429,8 +437,8 @@ HELP
|
|
429
437
|
raise Bolt::CLIError, "A destination path must be specified"
|
430
438
|
end
|
431
439
|
validate_file('source file', src)
|
432
|
-
executor.file_upload(nodes, src, dest) do |node,
|
433
|
-
outputter.
|
440
|
+
executor.file_upload(nodes, src, dest) do |node, event|
|
441
|
+
outputter.print_event(node, event)
|
434
442
|
end
|
435
443
|
end
|
436
444
|
end
|
@@ -519,8 +527,10 @@ HELP
|
|
519
527
|
cli << "--#{setting}" << dir
|
520
528
|
end
|
521
529
|
Puppet.initialize_settings(cli)
|
522
|
-
Puppet::Pal.in_tmp_environment('bolt', modulepath: modulepath) do |pal|
|
523
|
-
|
530
|
+
Puppet::Pal.in_tmp_environment('bolt', modulepath: [BOLTLIB_PATH] + modulepath) do |pal|
|
531
|
+
pal.with_script_compiler do |compiler|
|
532
|
+
compiler.call_function('run_plan', plan, args)
|
533
|
+
end
|
524
534
|
end
|
525
535
|
end
|
526
536
|
end
|
data/lib/bolt/config.rb
CHANGED
data/lib/bolt/error.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
module Bolt
|
2
|
+
class Error < RuntimeError
|
3
|
+
attr_reader :kind, :details, :issue_code
|
4
|
+
|
5
|
+
def initialize(msg, kind, details = nil, issue_code = nil)
|
6
|
+
super(msg)
|
7
|
+
@kind = kind
|
8
|
+
@issue_code = issue_code
|
9
|
+
@details = details || {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def msg
|
13
|
+
message
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_h
|
17
|
+
h = { 'kind' => kind,
|
18
|
+
'msg' => message,
|
19
|
+
'details' => details }
|
20
|
+
h['issue_code'] = issue_code if issue_code
|
21
|
+
h
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_json
|
25
|
+
to_h.to_json
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/bolt/executor.rb
CHANGED
@@ -37,18 +37,22 @@ module Bolt
|
|
37
37
|
pool.post do
|
38
38
|
result =
|
39
39
|
begin
|
40
|
+
@notifier.notify(callback, node, type: :node_start) if callback
|
40
41
|
node.connect
|
41
42
|
yield node
|
42
|
-
rescue Bolt::Node::BaseError => ex
|
43
|
-
Bolt::ErrorResult.new(ex.message, ex.issue_code, ex.kind)
|
44
43
|
rescue StandardError => ex
|
45
|
-
|
46
|
-
Bolt::ExceptionResult.new(ex)
|
44
|
+
Bolt::Result.from_exception(ex)
|
47
45
|
ensure
|
48
|
-
|
46
|
+
begin
|
47
|
+
node.disconnect
|
48
|
+
rescue StandardError => ex
|
49
|
+
@logger.info("Failed to close connection to #{node.uri} : #{ex.message}")
|
50
|
+
end
|
49
51
|
end
|
50
52
|
results[node] = result
|
51
|
-
|
53
|
+
if callback
|
54
|
+
@notifier.notify(callback, node, type: :node_result, result: result)
|
55
|
+
end
|
52
56
|
result
|
53
57
|
end
|
54
58
|
}
|
data/lib/bolt/node.rb
CHANGED
@@ -37,6 +37,7 @@ module Bolt
|
|
37
37
|
@port = port
|
38
38
|
@user = user || config[:user]
|
39
39
|
@password = password || config[:password]
|
40
|
+
@key = config[:key]
|
40
41
|
@tty = config[:tty]
|
41
42
|
@insecure = config[:insecure]
|
42
43
|
@uri = uri
|
@@ -60,23 +61,24 @@ module Bolt
|
|
60
61
|
@logger.debug { "Uploading #{source} to #{destination}" }
|
61
62
|
result = _upload(source, destination)
|
62
63
|
if result.success?
|
63
|
-
Bolt::Result.new("Uploaded '#{source}' to '#{host}:#{destination}'")
|
64
|
+
Bolt::Result.new(nil, "Uploaded '#{source}' to '#{host}:#{destination}'")
|
64
65
|
else
|
65
|
-
result
|
66
|
+
result
|
66
67
|
end
|
67
68
|
end
|
68
69
|
|
69
70
|
def run_command(command)
|
70
71
|
@logger.info { "Running command: #{command}" }
|
71
|
-
_run_command(command)
|
72
|
+
_run_command(command)
|
72
73
|
end
|
73
74
|
|
74
75
|
def run_script(script, arguments)
|
75
|
-
|
76
|
+
@logger.info { "Running script: #{command}" }
|
77
|
+
_run_script(script, arguments)
|
76
78
|
end
|
77
79
|
|
78
80
|
def run_task(task, input_method, arguments)
|
79
|
-
_run_task(task, input_method, arguments)
|
81
|
+
_run_task(task, input_method, arguments)
|
80
82
|
end
|
81
83
|
end
|
82
84
|
end
|
data/lib/bolt/node/errors.rb
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
module Bolt
|
2
2
|
class Node
|
3
|
-
class BaseError <
|
3
|
+
class BaseError < Bolt::Error
|
4
4
|
attr_reader :issue_code
|
5
5
|
|
6
6
|
def initialize(message, issue_code)
|
7
|
-
super(message)
|
8
|
-
@issue_code = issue_code
|
7
|
+
super(message, kind, nil, issue_code)
|
9
8
|
end
|
10
9
|
|
11
10
|
def kind
|
@@ -18,5 +17,28 @@ module Bolt
|
|
18
17
|
'puppetlabs.tasks/connect-error'
|
19
18
|
end
|
20
19
|
end
|
20
|
+
|
21
|
+
class EscalateError < BaseError
|
22
|
+
def kind
|
23
|
+
'puppetlabs.tasks/escalate-error'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class FileError < BaseError
|
28
|
+
def kind
|
29
|
+
'puppetlabs.tasks/task_file_error'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class EnvironmentVarError < BaseError
|
34
|
+
def initialize(var, val)
|
35
|
+
message = "Could not set environment variable '#{var}' to '#{val}'"
|
36
|
+
super(message, 'ENVVAR_ERROR')
|
37
|
+
end
|
38
|
+
|
39
|
+
def kind
|
40
|
+
'puppetlabs.tasks/environment-var-error'
|
41
|
+
end
|
42
|
+
end
|
21
43
|
end
|
22
44
|
end
|
data/lib/bolt/node/orch.rb
CHANGED
@@ -47,10 +47,8 @@ module Bolt
|
|
47
47
|
state = node_result['state']
|
48
48
|
result = node_result['result']
|
49
49
|
|
50
|
-
result_output = Bolt::Node::ResultOutput.new
|
51
|
-
result_output.stdout << result.to_json
|
52
50
|
if state == 'finished'
|
53
|
-
|
51
|
+
exit_code = 0
|
54
52
|
else
|
55
53
|
# Try to extract the exit_code from _error
|
56
54
|
begin
|
@@ -58,29 +56,20 @@ module Bolt
|
|
58
56
|
rescue NoMethodError
|
59
57
|
exit_code = 'unknown'
|
60
58
|
end
|
61
|
-
Bolt::Node::Failure.new(exit_code, result_output)
|
62
59
|
end
|
60
|
+
Bolt::TaskResult.new(result.to_json, "", exit_code)
|
63
61
|
end
|
64
62
|
|
65
63
|
# run_task generates a result that makes sense for a generic task which
|
66
64
|
# needs to be unwrapped to extract stdout/stderr/exitcode.
|
67
65
|
#
|
68
66
|
def unwrap_bolt_result(result)
|
69
|
-
|
70
|
-
if task_result['exit_code'].nil?
|
67
|
+
if result.error
|
71
68
|
# something went wrong return the failure
|
72
69
|
return result
|
73
70
|
end
|
74
71
|
|
75
|
-
|
76
|
-
result_output = Bolt::Node::ResultOutput.new
|
77
|
-
result_output.stdout << task_result['stdout']
|
78
|
-
result_output.stderr << task_result['stderr']
|
79
|
-
if (task_result['exit_code']).zero?
|
80
|
-
Bolt::Node::Success.new(task_result['stdout'], result_output)
|
81
|
-
else
|
82
|
-
Bolt::Node::Failure.new(task_result['exit_code'], result_output)
|
83
|
-
end
|
72
|
+
Bolt::CommandResult.new(result.value['stdout'], result.value['stderr'], result.value['exit_code'])
|
84
73
|
end
|
85
74
|
|
86
75
|
def _run_command(command, options = {})
|
@@ -102,7 +91,9 @@ module Bolt
|
|
102
91
|
content: content,
|
103
92
|
mode: mode
|
104
93
|
}
|
105
|
-
_run_task(BOLT_MOCK_FILE, 'stdin', params)
|
94
|
+
result = _run_task(BOLT_MOCK_FILE, 'stdin', params)
|
95
|
+
result = Bolt::Result.new unless result.error
|
96
|
+
result
|
106
97
|
end
|
107
98
|
|
108
99
|
def _run_script(script, arguments)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'bolt/result'
|
3
|
+
|
4
|
+
module Bolt
|
5
|
+
class Node
|
6
|
+
class Output
|
7
|
+
attr_reader :stdout, :stderr
|
8
|
+
attr_accessor :exit_code
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@stdout = StringIO.new
|
12
|
+
@stderr = StringIO.new
|
13
|
+
@exit_code = 'unkown'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/bolt/node/ssh.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
require 'json'
|
2
2
|
require 'shellwords'
|
3
3
|
require 'net/ssh'
|
4
|
-
require 'net/
|
5
|
-
require 'bolt/node/
|
4
|
+
require 'net/scp'
|
5
|
+
require 'bolt/node/output'
|
6
6
|
|
7
7
|
module Bolt
|
8
8
|
class SSH < Node
|
@@ -22,6 +22,7 @@ module Bolt
|
|
22
22
|
|
23
23
|
options[:port] = @port if @port
|
24
24
|
options[:password] = @password if @password
|
25
|
+
options[:keys] = @key if @key
|
25
26
|
options[:verify_host_key] = if @insecure
|
26
27
|
Net::SSH::Verifiers::Lenient.new
|
27
28
|
else
|
@@ -54,11 +55,41 @@ module Bolt
|
|
54
55
|
end
|
55
56
|
end
|
56
57
|
|
58
|
+
def sudo_prompt
|
59
|
+
'[sudo] Bolt needs to run as another user, password: '
|
60
|
+
end
|
61
|
+
|
62
|
+
def handled_sudo(channel, data)
|
63
|
+
if data == sudo_prompt
|
64
|
+
if @sudo_password
|
65
|
+
channel.send_data "#{@sudo_password}\n"
|
66
|
+
channel.wait
|
67
|
+
return true
|
68
|
+
else
|
69
|
+
raise Bolt::Node::EscalateError.new(
|
70
|
+
"Sudo password for user #{@user} was not provided for #{@uri}",
|
71
|
+
'NO_PASSWORD'
|
72
|
+
)
|
73
|
+
end
|
74
|
+
elsif data =~ /^#{@user} is not in the sudoers file\./
|
75
|
+
@logger.info { data }
|
76
|
+
raise Bolt::Node::EscalateError.new(
|
77
|
+
"User #{@user} does not have sudo permission on #{@uri}",
|
78
|
+
'SUDO_DENIED'
|
79
|
+
)
|
80
|
+
elsif data =~ /^Sorry, try again\./
|
81
|
+
@logger.info { data }
|
82
|
+
raise Bolt::Node::EscalateError.new(
|
83
|
+
"Sudo password for user #{@user} not recognized on #{@uri}",
|
84
|
+
'BAD_PASSWORD'
|
85
|
+
)
|
86
|
+
end
|
87
|
+
false
|
88
|
+
end
|
89
|
+
|
57
90
|
def execute(command, sudoable: false, **options)
|
58
|
-
result_output = Bolt::Node::
|
59
|
-
status = {}
|
91
|
+
result_output = Bolt::Node::Output.new
|
60
92
|
use_sudo = sudoable && (@sudo || @run_as)
|
61
|
-
sudo_prompt = '[sudo] Bolt needs to run as another user, password: '
|
62
93
|
if use_sudo
|
63
94
|
user_clause = if @run_as
|
64
95
|
"-u #{@run_as}"
|
@@ -75,30 +106,29 @@ module Bolt
|
|
75
106
|
channel.request_pty if @tty
|
76
107
|
|
77
108
|
channel.exec(command) do |_, success|
|
78
|
-
|
109
|
+
unless success
|
110
|
+
raise Bolt::Node::ConnectError.new(
|
111
|
+
"Could not execute command: #{command.inspect}",
|
112
|
+
'EXEC_ERROR'
|
113
|
+
)
|
114
|
+
end
|
79
115
|
|
80
116
|
channel.on_data do |_, data|
|
81
|
-
|
82
|
-
channel.send_data "#{@sudo_password}\n"
|
83
|
-
channel.wait
|
84
|
-
else
|
117
|
+
unless use_sudo && handled_sudo(channel, data)
|
85
118
|
result_output.stdout << data
|
86
119
|
end
|
87
120
|
@logger.debug { "stdout: #{data}" }
|
88
121
|
end
|
89
122
|
|
90
123
|
channel.on_extended_data do |_, _, data|
|
91
|
-
|
92
|
-
channel.send_data "#{@sudo_password}\n"
|
93
|
-
channel.wait
|
94
|
-
else
|
124
|
+
unless use_sudo && handled_sudo(channel, data)
|
95
125
|
result_output.stderr << data
|
96
126
|
end
|
97
127
|
@logger.debug { "stderr: #{data}" }
|
98
128
|
end
|
99
129
|
|
100
130
|
channel.on_request("exit-status") do |_, data|
|
101
|
-
|
131
|
+
result_output.exit_code = data.read_long
|
102
132
|
end
|
103
133
|
|
104
134
|
if options[:stdin]
|
@@ -109,45 +139,52 @@ module Bolt
|
|
109
139
|
end
|
110
140
|
session_channel.wait
|
111
141
|
|
112
|
-
if
|
142
|
+
if result_output.exit_code == 0
|
113
143
|
@logger.debug { "Command returned successfully" }
|
114
|
-
Bolt::Node::Success.new(result_output.stdout.string, result_output)
|
115
144
|
else
|
116
|
-
@logger.info { "Command failed with exit code #{
|
117
|
-
Bolt::Node::Failure.new(status[:exit_code], result_output)
|
145
|
+
@logger.info { "Command failed with exit code #{result_output.exit_code}" }
|
118
146
|
end
|
147
|
+
result_output
|
119
148
|
end
|
120
149
|
|
121
150
|
def _upload(source, destination)
|
122
|
-
|
123
|
-
|
124
|
-
end
|
125
|
-
Bolt::Node::Success.new
|
151
|
+
write_remote_file(source, destination)
|
152
|
+
Bolt::Result.new
|
126
153
|
rescue StandardError => e
|
127
|
-
Bolt::
|
154
|
+
Bolt::Result.from_exception(e)
|
128
155
|
end
|
129
156
|
|
130
|
-
def
|
131
|
-
|
157
|
+
def write_remote_file(source, destination)
|
158
|
+
@session.scp.upload!(source, destination)
|
132
159
|
rescue StandardError => e
|
133
|
-
|
160
|
+
raise FileError.new(e.message, 'WRITE_ERROR')
|
161
|
+
end
|
162
|
+
|
163
|
+
def make_tempdir
|
164
|
+
result = execute('mktemp -d')
|
165
|
+
if result.exit_code != 0
|
166
|
+
raise FileError.new("Could not make tempdir: #{result.stderr.string}", 'TEMPDIR_ERROR')
|
167
|
+
end
|
168
|
+
result.stdout.string.chomp
|
134
169
|
end
|
135
170
|
|
136
171
|
def with_remote_tempdir
|
137
|
-
|
138
|
-
|
139
|
-
|
172
|
+
dir = make_tempdir
|
173
|
+
begin
|
174
|
+
yield dir
|
175
|
+
ensure
|
176
|
+
output = execute("rm -rf '#{dir}'")
|
177
|
+
if output.exit_code != 0
|
178
|
+
logger.warn("Failed to clean up tempdir '#{dir}': #{output.stderr.string}")
|
140
179
|
end
|
141
180
|
end
|
142
181
|
end
|
143
182
|
|
144
183
|
def with_remote_script(dir, file)
|
145
184
|
remote_path = "#{dir}/#{File.basename(file)}"
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
yield remote_path
|
150
|
-
end
|
185
|
+
write_remote_file(file, remote_path)
|
186
|
+
make_executable(remote_path)
|
187
|
+
yield remote_path
|
151
188
|
end
|
152
189
|
|
153
190
|
def with_remote_file(file)
|
@@ -167,14 +204,19 @@ EOF
|
|
167
204
|
SCRIPT
|
168
205
|
end
|
169
206
|
|
207
|
+
def make_executable(path)
|
208
|
+
result = execute("chmod u+x '#{path}'")
|
209
|
+
if result.exit_code != 0
|
210
|
+
raise FileError.new("Could not make file '#{path}' executable: #{result.stderr.string}", 'CHMOD_ERROR')
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
170
214
|
def with_task_wrapper(remote_task, dir, stdin)
|
171
215
|
wrapper = make_wrapper_stringio(remote_task, stdin)
|
172
216
|
command = "#{dir}/wrapper.sh"
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
yield command
|
177
|
-
end
|
217
|
+
write_remote_file(wrapper, command)
|
218
|
+
make_executable(command)
|
219
|
+
yield command
|
178
220
|
end
|
179
221
|
|
180
222
|
def with_remote_task(task_file, stdin)
|
@@ -192,7 +234,12 @@ SCRIPT
|
|
192
234
|
end
|
193
235
|
|
194
236
|
def _run_command(command)
|
195
|
-
execute(command, sudoable: true)
|
237
|
+
output = execute(command, sudoable: true)
|
238
|
+
Bolt::CommandResult.from_output(output)
|
239
|
+
# TODO: We should be able to rely on the excutor for this but it will mean
|
240
|
+
# a test refactor
|
241
|
+
rescue StandardError => e
|
242
|
+
Bolt::Result.from_exception(e)
|
196
243
|
end
|
197
244
|
|
198
245
|
def _run_script(script, arguments)
|
@@ -200,9 +247,14 @@ SCRIPT
|
|
200
247
|
@logger.debug { "arguments: #{arguments}" }
|
201
248
|
|
202
249
|
with_remote_file(script) do |remote_path|
|
203
|
-
execute("'#{remote_path}' #{Shellwords.join(arguments)}",
|
204
|
-
|
250
|
+
output = execute("'#{remote_path}' #{Shellwords.join(arguments)}",
|
251
|
+
sudoable: true)
|
252
|
+
Bolt::CommandResult.from_output(output)
|
205
253
|
end
|
254
|
+
# TODO: We should be able to rely on the excutor for this but it will mean
|
255
|
+
# a test refactor
|
256
|
+
rescue StandardError => e
|
257
|
+
Bolt::Result.from_exception(e)
|
206
258
|
end
|
207
259
|
|
208
260
|
def _run_task(task, input_method, arguments)
|
@@ -228,8 +280,13 @@ SCRIPT
|
|
228
280
|
else
|
229
281
|
"#{export_args} '#{remote_path}'"
|
230
282
|
end
|
231
|
-
execute(command, sudoable: true)
|
283
|
+
output = execute(command, sudoable: true)
|
284
|
+
Bolt::TaskResult.from_output(output)
|
232
285
|
end
|
286
|
+
# TODO: We should be able to rely on the excutor for this but it will mean
|
287
|
+
# a test refactor
|
288
|
+
rescue StandardError => e
|
289
|
+
Bolt::Result.from_exception(e)
|
233
290
|
end
|
234
291
|
end
|
235
292
|
end
|