bolt 2.7.0 → 2.11.1
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/Puppetfile +4 -3
- data/bolt-modules/boltlib/lib/puppet/datatypes/applyresult.rb +2 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/resourceinstance.rb +27 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/result.rb +2 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/resultset.rb +2 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/target.rb +4 -3
- data/bolt-modules/boltlib/lib/puppet/functions/run_task_with.rb +192 -0
- data/bolt-modules/boltlib/lib/puppet/functions/set_resources.rb +122 -0
- data/bolt-modules/boltlib/types/planresult.pp +12 -1
- data/bolt-modules/file/lib/puppet/functions/file/exists.rb +3 -1
- data/bolt-modules/file/lib/puppet/functions/file/join.rb +1 -1
- data/bolt-modules/file/lib/puppet/functions/file/read.rb +2 -1
- data/bolt-modules/file/lib/puppet/functions/file/readable.rb +3 -1
- data/bolt-modules/file/lib/puppet/functions/file/write.rb +3 -1
- data/lib/bolt/applicator.rb +3 -2
- data/lib/bolt/apply_inventory.rb +1 -1
- data/lib/bolt/apply_result.rb +1 -1
- data/lib/bolt/apply_target.rb +11 -2
- data/lib/bolt/bolt_option_parser.rb +22 -6
- data/lib/bolt/cli.rb +52 -22
- data/lib/bolt/config.rb +57 -27
- data/lib/bolt/config/transport/base.rb +3 -3
- data/lib/bolt/config/transport/docker.rb +2 -0
- data/lib/bolt/config/transport/local.rb +2 -0
- data/lib/bolt/config/transport/orch.rb +4 -2
- data/lib/bolt/config/transport/remote.rb +2 -0
- data/lib/bolt/config/transport/ssh.rb +51 -2
- data/lib/bolt/config/transport/winrm.rb +3 -1
- data/lib/bolt/executor.rb +16 -0
- data/lib/bolt/inventory.rb +2 -1
- data/lib/bolt/inventory/group.rb +1 -0
- data/lib/bolt/inventory/inventory.rb +5 -0
- data/lib/bolt/inventory/target.rb +17 -1
- data/lib/bolt/node/output.rb +1 -1
- data/lib/bolt/outputter/human.rb +5 -4
- data/lib/bolt/outputter/json.rb +1 -1
- data/lib/bolt/pal.rb +32 -14
- data/lib/bolt/pal/yaml_plan.rb +1 -0
- data/lib/bolt/plugin.rb +14 -8
- data/lib/bolt/plugin/module.rb +40 -7
- data/lib/bolt/plugin/puppetdb.rb +5 -2
- data/lib/bolt/project.rb +135 -0
- data/lib/bolt/puppetdb/config.rb +16 -28
- data/lib/bolt/rerun.rb +1 -1
- data/lib/bolt/resource_instance.rb +126 -0
- data/lib/bolt/result.rb +46 -23
- data/lib/bolt/result_set.rb +2 -5
- data/lib/bolt/secret.rb +20 -4
- data/lib/bolt/shell/bash.rb +12 -5
- data/lib/bolt/shell/powershell.rb +12 -4
- data/lib/bolt/target.rb +16 -1
- data/lib/bolt/transport/base.rb +24 -8
- data/lib/bolt/transport/orch.rb +4 -0
- data/lib/bolt/transport/ssh.rb +6 -2
- data/lib/bolt/transport/ssh/connection.rb +4 -0
- data/lib/bolt/transport/ssh/exec_connection.rb +110 -0
- data/lib/bolt/transport/winrm/connection.rb +6 -2
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/pe/pal.rb +1 -38
- data/lib/bolt_server/transport_app.rb +7 -7
- data/lib/bolt_spec/bolt_context.rb +3 -6
- data/lib/bolt_spec/plans.rb +1 -1
- data/lib/bolt_spec/plans/mock_executor.rb +1 -0
- data/lib/bolt_spec/run.rb +10 -13
- metadata +10 -7
- data/lib/bolt/boltdir.rb +0 -54
- data/lib/bolt/plugin/pkcs7.rb +0 -104
- data/lib/bolt/secret/base.rb +0 -41
data/lib/bolt/result.rb
CHANGED
@@ -40,18 +40,23 @@ module Bolt
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def self.for_task(target, stdout, stderr, exit_code, task)
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
43
|
+
stdout.force_encoding('utf-8') unless stdout.encoding == Encoding::UTF_8
|
44
|
+
value = if stdout.valid_encoding?
|
45
|
+
parse_hash(stdout) || { '_output' => stdout }
|
46
|
+
else
|
47
|
+
{ '_error' => { 'kind' => 'puppetlabs.tasks/task-error',
|
48
|
+
'issue_code' => 'TASK_ERROR',
|
49
|
+
'msg' => 'The task result contained invalid UTF-8 on stdout',
|
50
|
+
'details' => {} } }
|
51
|
+
end
|
52
|
+
|
52
53
|
if exit_code != 0 && value['_error'].nil?
|
53
54
|
msg = if stdout.empty?
|
54
|
-
|
55
|
+
if stderr.empty?
|
56
|
+
"The task failed with exit code #{exit_code} and no output"
|
57
|
+
else
|
58
|
+
"The task failed with exit code #{exit_code} and no stdout, but stderr contained:\n#{stderr}"
|
59
|
+
end
|
55
60
|
else
|
56
61
|
"The task failed with exit code #{exit_code}"
|
57
62
|
end
|
@@ -63,6 +68,13 @@ module Bolt
|
|
63
68
|
new(target, value: value, action: 'task', object: task)
|
64
69
|
end
|
65
70
|
|
71
|
+
def self.parse_hash(string)
|
72
|
+
value = JSON.parse(string)
|
73
|
+
value if value.is_a? Hash
|
74
|
+
rescue JSON::ParserError
|
75
|
+
nil
|
76
|
+
end
|
77
|
+
|
66
78
|
def self.for_upload(target, source, destination)
|
67
79
|
new(target, message: "Uploaded '#{source}' to '#{target.host}:#{destination}'", action: 'upload', object: source)
|
68
80
|
end
|
@@ -110,18 +122,8 @@ module Bolt
|
|
110
122
|
message && !message.strip.empty?
|
111
123
|
end
|
112
124
|
|
113
|
-
def status_hash
|
114
|
-
{
|
115
|
-
target: @target.name,
|
116
|
-
action: action,
|
117
|
-
object: object,
|
118
|
-
status: status,
|
119
|
-
value: @value
|
120
|
-
}
|
121
|
-
end
|
122
|
-
|
123
125
|
def generic_value
|
124
|
-
|
126
|
+
safe_value.reject { |k, _| %w[_error _output].include? k }
|
125
127
|
end
|
126
128
|
|
127
129
|
def eql?(other)
|
@@ -139,15 +141,36 @@ module Bolt
|
|
139
141
|
end
|
140
142
|
|
141
143
|
def to_json(opts = nil)
|
142
|
-
|
144
|
+
to_data.to_json(opts)
|
143
145
|
end
|
144
146
|
|
145
147
|
def to_s
|
146
148
|
to_json
|
147
149
|
end
|
148
150
|
|
151
|
+
# This is the value with all non-UTF-8 characters removed, suitable for
|
152
|
+
# printing or converting to JSON. It *should* only be possible to have
|
153
|
+
# non-UTF-8 characters in stdout/stderr keys as they are not allowed from
|
154
|
+
# tasks but we scrub the whole thing just in case.
|
155
|
+
def safe_value
|
156
|
+
Bolt::Util.walk_vals(value) do |val|
|
157
|
+
if val.is_a?(String)
|
158
|
+
# Replace invalid bytes with hex codes, ie. \xDE\xAD\xBE\xEF
|
159
|
+
val.scrub { |c| c.bytes.map { |b| "\\x" + b.to_s(16).upcase }.join }
|
160
|
+
else
|
161
|
+
val
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
149
166
|
def to_data
|
150
|
-
|
167
|
+
{
|
168
|
+
"target" => @target.name,
|
169
|
+
"action" => action,
|
170
|
+
"object" => object,
|
171
|
+
"status" => status,
|
172
|
+
"value" => safe_value
|
173
|
+
}
|
151
174
|
end
|
152
175
|
|
153
176
|
def status
|
data/lib/bolt/result_set.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/bolt/secret.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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(
|
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(
|
30
|
+
decrypted = plugins.get_hook(name, :secret_decrypt).call('encrypted_value' => options[:object])
|
15
31
|
outputter.print_message(decrypted)
|
16
32
|
end
|
17
33
|
|
data/lib/bolt/shell/bash.rb
CHANGED
@@ -150,8 +150,14 @@ module Bolt
|
|
150
150
|
end
|
151
151
|
elsif err =~ /^#{@sudo_id}/
|
152
152
|
if sudo_stdin
|
153
|
-
|
154
|
-
|
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
|
@@ -347,9 +353,9 @@ module Bolt
|
|
347
353
|
# Chunks of this size will be read in one iteration
|
348
354
|
index = 0
|
349
355
|
timeout = 0.1
|
356
|
+
result_output = Bolt::Node::Output.new
|
350
357
|
|
351
358
|
inp, out, err, t = conn.execute(command_str)
|
352
|
-
result_output = Bolt::Node::Output.new
|
353
359
|
read_streams = { out => String.new,
|
354
360
|
err => String.new }
|
355
361
|
write_stream = in_buffer.empty? ? [] : [inp]
|
@@ -399,8 +405,9 @@ module Bolt
|
|
399
405
|
write_stream = []
|
400
406
|
end
|
401
407
|
end
|
402
|
-
# If a task has stdin as an input_method but doesn't actually
|
403
|
-
#
|
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
|
404
411
|
rescue Errno::EPIPE
|
405
412
|
write_stream = []
|
406
413
|
end
|
@@ -267,10 +267,18 @@ module Bolt
|
|
267
267
|
|
268
268
|
result = Bolt::Node::Output.new
|
269
269
|
inp.close
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
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
|
274
282
|
|
275
283
|
stdout.join
|
276
284
|
stderr.join
|
data/lib/bolt/target.rb
CHANGED
@@ -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,
|
@@ -155,5 +166,9 @@ module Bolt
|
|
155
166
|
self.class.equal?(other.class) && @name == other.name
|
156
167
|
end
|
157
168
|
alias == eql?
|
169
|
+
|
170
|
+
def hash
|
171
|
+
@name.hash
|
172
|
+
end
|
158
173
|
end
|
159
174
|
end
|
data/lib/bolt/transport/base.rb
CHANGED
@@ -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
|
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}
|
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
|
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
|
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
|
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
|
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
|
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-
|
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.
|
data/lib/bolt/transport/orch.rb
CHANGED
@@ -210,6 +210,10 @@ module Bolt
|
|
210
210
|
end
|
211
211
|
end
|
212
212
|
|
213
|
+
def batch_task_with(_targets, _task, _target_mapping, _options = {})
|
214
|
+
raise NotImplementedError, "pcp transport does not support run_task_with()"
|
215
|
+
end
|
216
|
+
|
213
217
|
def batch_connected?(targets)
|
214
218
|
resp = get_connection(targets.first.options).query_inventory(targets)
|
215
219
|
resp['items'].all? { |node| node['connected'] }
|
data/lib/bolt/transport/ssh.rb
CHANGED
@@ -16,13 +16,16 @@ module Bolt
|
|
16
16
|
rescue LoadError
|
17
17
|
logger.debug("Authentication method 'gssapi-with-mic' (Kerberos) is not available.")
|
18
18
|
end
|
19
|
-
|
20
19
|
@transport_logger = Logging.logger[Net::SSH]
|
21
20
|
@transport_logger.level = :warn
|
22
21
|
end
|
23
22
|
|
24
23
|
def with_connection(target)
|
25
|
-
conn =
|
24
|
+
conn = if target.transport_config['ssh-command']
|
25
|
+
ExecConnection.new(target)
|
26
|
+
else
|
27
|
+
Connection.new(target, @transport_logger)
|
28
|
+
end
|
26
29
|
conn.connect
|
27
30
|
yield conn
|
28
31
|
ensure
|
@@ -37,3 +40,4 @@ module Bolt
|
|
37
40
|
end
|
38
41
|
|
39
42
|
require 'bolt/transport/ssh/connection'
|
43
|
+
require 'bolt/transport/ssh/exec_connection'
|
@@ -207,6 +207,10 @@ module Bolt
|
|
207
207
|
err_wr.close
|
208
208
|
end
|
209
209
|
[in_wr, out_rd, err_rd, th]
|
210
|
+
rescue Errno::EMFILE => e
|
211
|
+
msg = "#{e.message}. This may be resolved by increasing your user limit "\
|
212
|
+
"with 'ulimit -n 1024'. See https://puppet.com/docs/bolt/latest/bolt_known_issues.html for details."
|
213
|
+
raise Bolt::Error.new(msg, 'bolt/too-many-files')
|
210
214
|
end
|
211
215
|
|
212
216
|
def copy_file(source, destination)
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'open3'
|
4
|
+
|
5
|
+
module Bolt
|
6
|
+
module Transport
|
7
|
+
class SSH < Simple
|
8
|
+
class ExecConnection
|
9
|
+
attr_reader :user, :target
|
10
|
+
|
11
|
+
def initialize(target)
|
12
|
+
raise Bolt::ValidationError, "Target #{target.safe_name} does not have a host" unless target.host
|
13
|
+
|
14
|
+
@target = target
|
15
|
+
ssh_config = Net::SSH::Config.for(target.host)
|
16
|
+
@user = @target.user || ssh_config[:user] || Etc.getlogin
|
17
|
+
@logger = Logging.logger[self]
|
18
|
+
end
|
19
|
+
|
20
|
+
# This is used to verify we can connect to targets with `connected?`
|
21
|
+
def connect
|
22
|
+
cmd = build_ssh_command('exit')
|
23
|
+
_, err, stat = Open3.capture3(*cmd)
|
24
|
+
unless stat.success?
|
25
|
+
raise Bolt::Node::ConnectError.new(
|
26
|
+
"Failed to connect to #{@target.safe_name}: #{err}",
|
27
|
+
'CONNECT_ERROR'
|
28
|
+
)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def disconnect; end
|
33
|
+
|
34
|
+
def shell
|
35
|
+
Bolt::Shell::Bash.new(@target, self)
|
36
|
+
end
|
37
|
+
|
38
|
+
def userhost
|
39
|
+
"#{@user}@#{@target.host}"
|
40
|
+
end
|
41
|
+
|
42
|
+
def ssh_opts
|
43
|
+
cmd = []
|
44
|
+
# BatchMode is SSH's noninteractive option: if key authentication
|
45
|
+
# fails it will error out instead of falling back to password prompt
|
46
|
+
cmd += %w[-o BatchMode=yes]
|
47
|
+
cmd += %W[-o Port=#{@target.port}] if @target.port
|
48
|
+
|
49
|
+
if @target.transport_config.key?('host-key-check')
|
50
|
+
hkc = @target.transport_config['host-key-check'] ? 'yes' : 'no'
|
51
|
+
cmd += %W[-o StrictHostKeyChecking=#{hkc}]
|
52
|
+
end
|
53
|
+
|
54
|
+
if (key = target.transport_config['private-key'])
|
55
|
+
cmd += ['-i', key]
|
56
|
+
end
|
57
|
+
cmd
|
58
|
+
end
|
59
|
+
|
60
|
+
def build_ssh_command(command)
|
61
|
+
ssh_conf = @target.transport_config['ssh-command']
|
62
|
+
ssh_cmd = Array(ssh_conf)
|
63
|
+
ssh_cmd += ssh_opts
|
64
|
+
ssh_cmd << userhost
|
65
|
+
ssh_cmd << command
|
66
|
+
end
|
67
|
+
|
68
|
+
def copy_file(source, dest)
|
69
|
+
@logger.debug { "Uploading #{source}, to #{userhost}:#{dest}" } unless source.is_a?(StringIO)
|
70
|
+
|
71
|
+
cp_conf = @target.transport_config['copy-command'] || ["scp", "-r"]
|
72
|
+
cp_cmd = Array(cp_conf)
|
73
|
+
cp_cmd += ssh_opts
|
74
|
+
|
75
|
+
_, err, stat = if source.is_a?(StringIO)
|
76
|
+
Tempfile.create(File.basename(dest)) do |f|
|
77
|
+
f.write(source.read)
|
78
|
+
f.close
|
79
|
+
cp_cmd << f.path
|
80
|
+
cp_cmd << "#{userhost}:#{Shellwords.escape(dest)}"
|
81
|
+
Open3.capture3(*cp_cmd)
|
82
|
+
end
|
83
|
+
else
|
84
|
+
cp_cmd << source
|
85
|
+
cp_cmd << "#{userhost}:#{Shellwords.escape(dest)}"
|
86
|
+
Open3.capture3(*cp_cmd)
|
87
|
+
end
|
88
|
+
|
89
|
+
if stat.success?
|
90
|
+
@logger.debug "Successfully uploaded #{source} to #{dest}"
|
91
|
+
else
|
92
|
+
message = "Could not copy file to #{dest}: #{err}"
|
93
|
+
raise Bolt::Node::FileError.new(message, 'COPY_ERROR')
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def execute(command)
|
98
|
+
cmd_array = build_ssh_command(command)
|
99
|
+
Open3.popen3(*cmd_array)
|
100
|
+
end
|
101
|
+
|
102
|
+
# This is used by the Bash shell to decide whether to `cd` before
|
103
|
+
# executing commands as a run-as user
|
104
|
+
def reset_cwd?
|
105
|
+
true
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|