bolt 2.16.0 → 2.21.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/Puppetfile +3 -1
- data/bolt-modules/boltlib/lib/puppet/functions/apply_prep.rb +20 -9
- data/bolt-modules/boltlib/lib/puppet/functions/download_file.rb +123 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +2 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +6 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +6 -4
- data/bolt-modules/dir/lib/puppet/functions/dir/children.rb +35 -0
- data/lib/bolt/applicator.rb +19 -14
- data/lib/bolt/apply_result.rb +1 -1
- data/lib/bolt/bolt_option_parser.rb +60 -16
- data/lib/bolt/catalog.rb +3 -2
- data/lib/bolt/cli.rb +121 -43
- data/lib/bolt/config.rb +37 -34
- data/lib/bolt/config/options.rb +340 -173
- data/lib/bolt/config/transport/options.rb +315 -160
- data/lib/bolt/config/transport/ssh.rb +24 -10
- data/lib/bolt/executor.rb +21 -0
- data/lib/bolt/inventory/group.rb +3 -2
- data/lib/bolt/inventory/inventory.rb +4 -3
- data/lib/bolt/logger.rb +24 -1
- data/lib/bolt/outputter.rb +1 -1
- data/lib/bolt/outputter/rainbow.rb +14 -3
- data/lib/bolt/pal.rb +28 -10
- data/lib/bolt/pal/yaml_plan/evaluator.rb +23 -2
- data/lib/bolt/pal/yaml_plan/step.rb +24 -2
- data/lib/bolt/pal/yaml_plan/step/download.rb +38 -0
- data/lib/bolt/pal/yaml_plan/step/message.rb +30 -0
- data/lib/bolt/pal/yaml_plan/step/upload.rb +3 -3
- data/lib/bolt/plugin/module.rb +2 -4
- data/lib/bolt/plugin/puppetdb.rb +3 -2
- data/lib/bolt/project.rb +20 -6
- data/lib/bolt/puppetdb/client.rb +2 -0
- data/lib/bolt/puppetdb/config.rb +16 -0
- data/lib/bolt/result.rb +7 -0
- data/lib/bolt/shell/bash.rb +45 -37
- data/lib/bolt/shell/powershell.rb +21 -11
- data/lib/bolt/shell/powershell/snippets.rb +15 -6
- data/lib/bolt/transport/base.rb +24 -0
- data/lib/bolt/transport/docker.rb +16 -4
- data/lib/bolt/transport/docker/connection.rb +20 -2
- data/lib/bolt/transport/local/connection.rb +14 -1
- data/lib/bolt/transport/orch.rb +20 -0
- data/lib/bolt/transport/simple.rb +6 -0
- data/lib/bolt/transport/ssh.rb +7 -1
- data/lib/bolt/transport/ssh/connection.rb +9 -1
- data/lib/bolt/transport/ssh/exec_connection.rb +23 -2
- data/lib/bolt/transport/winrm/connection.rb +118 -8
- data/lib/bolt/util.rb +26 -11
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/transport_app.rb +3 -2
- data/lib/bolt_spec/bolt_context.rb +7 -2
- data/lib/bolt_spec/plans.rb +15 -2
- data/lib/bolt_spec/plans/action_stubs.rb +2 -1
- data/lib/bolt_spec/plans/action_stubs/download_stub.rb +66 -0
- data/lib/bolt_spec/plans/mock_executor.rb +14 -1
- data/lib/bolt_spec/run.rb +22 -0
- data/libexec/bolt_catalog +3 -2
- data/modules/secure_env_vars/plans/init.pp +20 -0
- metadata +21 -29
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bolt
|
4
|
+
class PAL
|
5
|
+
class YamlPlan
|
6
|
+
class Step
|
7
|
+
class Message < Step
|
8
|
+
def self.allowed_keys
|
9
|
+
super + Set['message']
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.required_keys
|
13
|
+
Set['message']
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(step_body)
|
17
|
+
super
|
18
|
+
@message = step_body['message']
|
19
|
+
end
|
20
|
+
|
21
|
+
def transpile
|
22
|
+
code = String.new(" ")
|
23
|
+
code << function_call('out::message', [@message])
|
24
|
+
code << "\n"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -6,16 +6,16 @@ module Bolt
|
|
6
6
|
class Step
|
7
7
|
class Upload < Step
|
8
8
|
def self.allowed_keys
|
9
|
-
super + Set['source', 'destination']
|
9
|
+
super + Set['source', 'destination', 'upload']
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.required_keys
|
13
|
-
Set['
|
13
|
+
Set['upload', 'destination', 'targets']
|
14
14
|
end
|
15
15
|
|
16
16
|
def initialize(step_body)
|
17
17
|
super
|
18
|
-
@source = step_body['source']
|
18
|
+
@source = step_body['upload'] || step_body['source']
|
19
19
|
@destination = step_body['destination']
|
20
20
|
end
|
21
21
|
|
data/lib/bolt/plugin/module.rb
CHANGED
@@ -184,12 +184,10 @@ module Bolt
|
|
184
184
|
# Raises a deprecation warning if the pkcs7 plugin is using deprecated keys and
|
185
185
|
# modifies the keys so they are the correct format
|
186
186
|
def handle_deprecated_pkcs7_keys(params)
|
187
|
-
if
|
188
|
-
@deprecation_warning_issued = true
|
189
|
-
|
187
|
+
if params.key?('private-key') || params.key?('public-key')
|
190
188
|
message = "pkcs7 keys 'private-key' and 'public-key' have been deprecated and will be "\
|
191
189
|
"removed in a future version of Bolt; use 'private_key' and 'public_key' instead."
|
192
|
-
|
190
|
+
Bolt::Logger.deprecation_warning('PKCS7 keys using hyphens, not underscores', message)
|
193
191
|
end
|
194
192
|
|
195
193
|
params['private_key'] = params.delete('private-key') if params.key?('private-key')
|
data/lib/bolt/plugin/puppetdb.rb
CHANGED
@@ -85,7 +85,8 @@ module Bolt
|
|
85
85
|
|
86
86
|
def resolve_facts(config, certname, target_data)
|
87
87
|
Bolt::Util.walk_vals(config) do |value|
|
88
|
-
|
88
|
+
case value
|
89
|
+
when String
|
89
90
|
if value == 'certname'
|
90
91
|
certname
|
91
92
|
else
|
@@ -94,7 +95,7 @@ module Bolt
|
|
94
95
|
# If there's no fact data this will be nil
|
95
96
|
data&.fetch('value', nil)
|
96
97
|
end
|
97
|
-
|
98
|
+
when Array, Hash
|
98
99
|
value
|
99
100
|
else
|
100
101
|
raise FactLookupError.new(value, "fact lookups must be a string")
|
data/lib/bolt/project.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'pathname'
|
4
|
-
require 'bolt/pal'
|
5
4
|
require 'bolt/config'
|
5
|
+
require 'bolt/pal'
|
6
6
|
|
7
7
|
module Bolt
|
8
8
|
class Project
|
@@ -16,7 +16,8 @@ module Bolt
|
|
16
16
|
}.freeze
|
17
17
|
|
18
18
|
attr_reader :path, :data, :config_file, :inventory_file, :modulepath, :hiera_config,
|
19
|
-
:puppetfile, :rerunfile, :type, :resource_types, :warnings, :project_file
|
19
|
+
:puppetfile, :rerunfile, :type, :resource_types, :warnings, :project_file,
|
20
|
+
:deprecations, :downloads
|
20
21
|
|
21
22
|
def self.default_project
|
22
23
|
create_project(File.expand_path(File.join('~', '.puppetlabs', 'bolt')), 'user')
|
@@ -31,6 +32,7 @@ module Bolt
|
|
31
32
|
# hierarchy, falling back to the default if we reach the root.
|
32
33
|
def self.find_boltdir(dir)
|
33
34
|
dir = Pathname.new(dir)
|
35
|
+
|
34
36
|
if (dir + BOLTDIR_NAME).directory?
|
35
37
|
create_project(dir + BOLTDIR_NAME, 'embedded')
|
36
38
|
elsif (dir + 'bolt.yaml').file? || (dir + 'bolt-project.yaml').file?
|
@@ -44,6 +46,15 @@ module Bolt
|
|
44
46
|
|
45
47
|
def self.create_project(path, type = 'option')
|
46
48
|
fullpath = Pathname.new(path).expand_path
|
49
|
+
|
50
|
+
if !Bolt::Util.windows? && type != 'environment' && fullpath.world_writable?
|
51
|
+
raise Bolt::Error.new(
|
52
|
+
"Project directory '#{fullpath}' is world-writable which poses a security risk. Set "\
|
53
|
+
"BOLT_PROJECT='#{fullpath}' to force the use of this project directory.",
|
54
|
+
"bolt/world-writable-error"
|
55
|
+
)
|
56
|
+
end
|
57
|
+
|
47
58
|
project_file = File.join(fullpath, 'bolt-project.yaml')
|
48
59
|
data = Bolt::Util.read_optional_yaml_hash(File.expand_path(project_file), 'project')
|
49
60
|
new(data, path, type)
|
@@ -51,14 +62,16 @@ module Bolt
|
|
51
62
|
|
52
63
|
def initialize(raw_data, path, type = 'option')
|
53
64
|
@path = Pathname.new(path).expand_path
|
65
|
+
|
54
66
|
@project_file = @path + 'bolt-project.yaml'
|
55
67
|
|
56
68
|
@warnings = []
|
69
|
+
@deprecations = []
|
57
70
|
if (@path + 'bolt.yaml').file? && project_file?
|
58
71
|
msg = "Project-level configuration in bolt.yaml is deprecated if using bolt-project.yaml. "\
|
59
72
|
"Transport config should be set in inventory.yaml, all other config should be set in "\
|
60
73
|
"bolt-project.yaml."
|
61
|
-
@
|
74
|
+
@deprecations << { type: 'Using bolt.yaml for project configuration', msg: msg }
|
62
75
|
end
|
63
76
|
|
64
77
|
@inventory_file = @path + 'inventory.yaml'
|
@@ -68,6 +81,7 @@ module Bolt
|
|
68
81
|
@rerunfile = @path + '.rerun.json'
|
69
82
|
@resource_types = @path + '.resource_types'
|
70
83
|
@type = type
|
84
|
+
@downloads = @path + 'downloads'
|
71
85
|
|
72
86
|
tc = Bolt::Config::INVENTORY_OPTIONS.keys & raw_data.keys
|
73
87
|
if tc.any?
|
@@ -98,7 +112,7 @@ module Bolt
|
|
98
112
|
# This API is used to prepend the project as a module to Puppet's internal
|
99
113
|
# module_references list. CHANGE AT YOUR OWN RISK
|
100
114
|
def to_h
|
101
|
-
{ path: @path, name: name }
|
115
|
+
{ path: @path.to_s, name: name }
|
102
116
|
end
|
103
117
|
|
104
118
|
def eql?(other)
|
@@ -147,8 +161,8 @@ module Bolt
|
|
147
161
|
|
148
162
|
def check_deprecated_file
|
149
163
|
if (@path + 'project.yaml').file?
|
150
|
-
|
151
|
-
|
164
|
+
msg = "Project configuration file 'project.yaml' is deprecated; use 'bolt-project.yaml' instead."
|
165
|
+
Bolt::Logger.deprecation_warning('Using project.yaml instead of bolt-project.yaml', msg)
|
152
166
|
end
|
153
167
|
end
|
154
168
|
end
|
data/lib/bolt/puppetdb/client.rb
CHANGED
@@ -96,6 +96,8 @@ module Bolt
|
|
96
96
|
@http = HTTPClient.new
|
97
97
|
@http.ssl_config.set_client_cert_file(@config.cert, @config.key) if @config.cert
|
98
98
|
@http.ssl_config.add_trust_ca(@config.cacert)
|
99
|
+
@http.connect_timeout = @config.connect_timeout if @config.connect_timeout
|
100
|
+
@http.receive_timeout = @config.read_timeout if @config.read_timeout
|
99
101
|
|
100
102
|
@http
|
101
103
|
end
|
data/lib/bolt/puppetdb/config.rb
CHANGED
@@ -132,6 +132,22 @@ module Bolt
|
|
132
132
|
end
|
133
133
|
end
|
134
134
|
|
135
|
+
def connect_timeout
|
136
|
+
validate_timeout('connect_timeout')
|
137
|
+
@settings['connect_timeout']
|
138
|
+
end
|
139
|
+
|
140
|
+
def read_timeout
|
141
|
+
validate_timeout('read_timeout')
|
142
|
+
@settings['read_timeout']
|
143
|
+
end
|
144
|
+
|
145
|
+
def validate_timeout(timeout)
|
146
|
+
unless @settings[timeout].nil? || (@settings[timeout].is_a?(Integer) && @settings[timeout] > 0)
|
147
|
+
raise Bolt::PuppetDBError, "#{timeout} must be a positive integer, received #{@settings[timeout]}"
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
135
151
|
def to_hash
|
136
152
|
@settings.dup
|
137
153
|
end
|
data/lib/bolt/result.rb
CHANGED
@@ -79,6 +79,13 @@ module Bolt
|
|
79
79
|
new(target, message: "Uploaded '#{source}' to '#{target.host}:#{destination}'", action: 'upload', object: source)
|
80
80
|
end
|
81
81
|
|
82
|
+
def self.for_download(target, source, destination, download)
|
83
|
+
msg = "Downloaded '#{target.host}:#{source}' to '#{destination}'"
|
84
|
+
value = { 'path' => download }
|
85
|
+
|
86
|
+
new(target, value: value, message: msg, action: 'download', object: source)
|
87
|
+
end
|
88
|
+
|
82
89
|
# Satisfies the Puppet datatypes API
|
83
90
|
def self.from_asserted_args(target, value)
|
84
91
|
new(target, value: value)
|
data/lib/bolt/shell/bash.rb
CHANGED
@@ -23,7 +23,7 @@ module Bolt
|
|
23
23
|
|
24
24
|
def run_command(command, options = {})
|
25
25
|
running_as(options[:run_as]) do
|
26
|
-
output = execute(command, sudoable: true)
|
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,
|
@@ -37,7 +37,7 @@ module Bolt
|
|
37
37
|
with_tmpdir do |dir|
|
38
38
|
basename = File.basename(source)
|
39
39
|
tmpfile = File.join(dir.to_s, basename)
|
40
|
-
conn.
|
40
|
+
conn.upload_file(source, tmpfile)
|
41
41
|
# pass over file ownership if we're using run-as to be a different user
|
42
42
|
dir.chown(run_as)
|
43
43
|
result = execute(['mv', '-f', tmpfile, destination], sudoable: true)
|
@@ -50,6 +50,27 @@ module Bolt
|
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
+
def download(source, destination, options = {})
|
54
|
+
running_as(options[:run_as]) do
|
55
|
+
# Target OS may be either Unix or Windows. Without knowing the target OS before-hand
|
56
|
+
# we can't assume whether the path separator is '/' or '\'. Assume we're connecting
|
57
|
+
# to a target with Unix and then check if the path exists after downloading.
|
58
|
+
download = File.join(destination, Bolt::Util.unix_basename(source))
|
59
|
+
|
60
|
+
conn.download_file(source, destination, download)
|
61
|
+
|
62
|
+
# If the download path doesn't exist, then the file was likely downloaded from Windows
|
63
|
+
# using a source path with backslashes (e.g. 'C:\Users\Administrator\foo'). The file
|
64
|
+
# should be saved to the expected location, so update the download path assuming a
|
65
|
+
# Windows basename so the result shows the correct local path.
|
66
|
+
unless File.exist?(download)
|
67
|
+
download = File.join(destination, Bolt::Util.windows_basename(source))
|
68
|
+
end
|
69
|
+
|
70
|
+
Bolt::Result.for_download(target, source, destination, download)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
53
74
|
def run_script(script, arguments, options = {})
|
54
75
|
# unpack any Sensitive data
|
55
76
|
arguments = unwrap_sensitive_args(arguments)
|
@@ -58,7 +79,7 @@ module Bolt
|
|
58
79
|
with_tmpdir do |dir|
|
59
80
|
path = write_executable(dir.to_s, script)
|
60
81
|
dir.chown(run_as)
|
61
|
-
output = execute([path, *arguments], sudoable: true)
|
82
|
+
output = execute([path, *arguments], environment: options[:env_vars], sudoable: true)
|
62
83
|
Bolt::Result.for_command(target,
|
63
84
|
output.stdout.string,
|
64
85
|
output.stderr.string,
|
@@ -95,7 +116,7 @@ module Bolt
|
|
95
116
|
task_dir = File.join(dir.to_s, task.tasks_dir)
|
96
117
|
dir.mkdirs([task.tasks_dir] + extra_files.map { |file| File.dirname(file['name']) })
|
97
118
|
extra_files.each do |file|
|
98
|
-
conn.
|
119
|
+
conn.upload_file(file['path'], File.join(dir.to_s, file['name']))
|
99
120
|
end
|
100
121
|
end
|
101
122
|
|
@@ -170,7 +191,7 @@ module Bolt
|
|
170
191
|
def check_sudo(out, inp, stdin)
|
171
192
|
buffer = out.readpartial(CHUNK_SIZE)
|
172
193
|
# Split on newlines, including the newline
|
173
|
-
lines = buffer.split(/(
|
194
|
+
lines = buffer.split(/(?<=\n)/)
|
174
195
|
# handle_sudo will return the line if it is not a sudo prompt or error
|
175
196
|
lines.map! { |line| handle_sudo(inp, line, stdin) }
|
176
197
|
lines.join("")
|
@@ -257,7 +278,7 @@ module Bolt
|
|
257
278
|
def write_executable(dir, file, filename = nil)
|
258
279
|
filename ||= File.basename(file)
|
259
280
|
remote_path = File.join(dir.to_s, filename)
|
260
|
-
conn.
|
281
|
+
conn.upload_file(file, remote_path)
|
261
282
|
make_executable(remote_path)
|
262
283
|
remote_path
|
263
284
|
end
|
@@ -277,31 +298,8 @@ module Bolt
|
|
277
298
|
end
|
278
299
|
end
|
279
300
|
|
280
|
-
|
281
|
-
|
282
|
-
# for task input data because the sudo password has already either been
|
283
|
-
# provided on stdin or was not needed.
|
284
|
-
def prepend_sudo_success(sudo_id, command_str)
|
285
|
-
command_str = "cd; #{command_str}" if conn.reset_cwd?
|
286
|
-
"sh -c #{Shellwords.shellescape("echo #{sudo_id} 1>&2; #{command_str}")}"
|
287
|
-
end
|
288
|
-
|
289
|
-
def prepend_chdir(command_str)
|
290
|
-
"sh -c #{Shellwords.shellescape("cd; #{command_str}")}"
|
291
|
-
end
|
292
|
-
|
293
|
-
# A helper to build up a single string that contains all of the options for
|
294
|
-
# privilege escalation. A wrapper script is used to direct task input to stdin
|
295
|
-
# when a tty is allocated and thus we do not need to prepend_sudo_success when
|
296
|
-
# using the wrapper or when the task does not require stdin data.
|
297
|
-
def build_sudoable_command_str(command_str, sudo_str, sudo_id, options)
|
298
|
-
if options[:stdin] && !options[:wrapper]
|
299
|
-
"#{sudo_str} #{prepend_sudo_success(sudo_id, command_str)}"
|
300
|
-
elsif conn.reset_cwd?
|
301
|
-
"#{sudo_str} #{prepend_chdir(command_str)}"
|
302
|
-
else
|
303
|
-
"#{sudo_str} #{command_str}"
|
304
|
-
end
|
301
|
+
def sudo_success(sudo_id)
|
302
|
+
"echo #{sudo_id} 1>&2"
|
305
303
|
end
|
306
304
|
|
307
305
|
# Returns string with the interpreter conditionally prepended
|
@@ -322,27 +320,37 @@ module Bolt
|
|
322
320
|
escalate = sudoable && run_as && conn.user != run_as
|
323
321
|
use_sudo = escalate && @target.options['run-as-command'].nil?
|
324
322
|
|
325
|
-
|
323
|
+
# Depending on the transport, whether we're using sudo and whether
|
324
|
+
# there are environment variables to set, we may need to stitch
|
325
|
+
# together multiple commands into a single sh invocation
|
326
|
+
commands = [inject_interpreter(options[:interpreter], command)]
|
326
327
|
|
327
328
|
if options[:environment]
|
328
|
-
|
329
|
+
env_decl = options[:environment].map do |env, val|
|
329
330
|
"#{env}=#{Shellwords.shellescape(val)}"
|
330
|
-
end
|
331
|
-
command_str = "#{env_decls.join(' ')} #{command_str}"
|
331
|
+
end.join(' ')
|
332
332
|
end
|
333
333
|
|
334
334
|
if escalate
|
335
335
|
sudo_str = if use_sudo
|
336
336
|
sudo_exec = target.options['sudo-executable'] || "sudo"
|
337
337
|
sudo_flags = [sudo_exec, "-S", "-H", "-u", run_as, "-p", sudo_prompt]
|
338
|
-
sudo_flags += ["-E"] if options[:environment]
|
339
338
|
Shellwords.shelljoin(sudo_flags)
|
340
339
|
else
|
341
340
|
Shellwords.shelljoin(@target.options['run-as-command'] + [run_as])
|
342
341
|
end
|
343
|
-
|
342
|
+
commands.unshift('cd') if conn.reset_cwd?
|
343
|
+
commands.unshift(sudo_success(@sudo_id)) if options[:stdin] && !options[:wrapper]
|
344
344
|
end
|
345
345
|
|
346
|
+
command_str = if sudo_str || env_decl
|
347
|
+
"sh -c #{Shellwords.shellescape(commands.join('; '))}"
|
348
|
+
else
|
349
|
+
commands.last
|
350
|
+
end
|
351
|
+
|
352
|
+
command_str = [sudo_str, env_decl, command_str].compact.join(' ')
|
353
|
+
|
346
354
|
@logger.debug { "Executing: #{command_str}" }
|
347
355
|
|
348
356
|
in_buffer = if !use_sudo && options[:stdin]
|
@@ -71,8 +71,10 @@ module Bolt
|
|
71
71
|
end
|
72
72
|
end
|
73
73
|
|
74
|
-
def
|
75
|
-
|
74
|
+
def env_declarations(env_vars)
|
75
|
+
env_vars.map do |var, val|
|
76
|
+
"[Environment]::SetEnvironmentVariable('#{var}', @'\n#{val}\n'@)"
|
77
|
+
end
|
76
78
|
end
|
77
79
|
|
78
80
|
def quote_string(string)
|
@@ -83,7 +85,7 @@ module Bolt
|
|
83
85
|
filename ||= File.basename(file)
|
84
86
|
validate_extensions(File.extname(filename))
|
85
87
|
destination = "#{dir}\\#{filename}"
|
86
|
-
conn.
|
88
|
+
conn.upload_file(file, destination)
|
87
89
|
destination
|
88
90
|
end
|
89
91
|
|
@@ -162,11 +164,19 @@ module Bolt
|
|
162
164
|
end
|
163
165
|
|
164
166
|
def upload(source, destination, _options = {})
|
165
|
-
conn.
|
167
|
+
conn.upload_file(source, destination)
|
166
168
|
Bolt::Result.for_upload(target, source, destination)
|
167
169
|
end
|
168
170
|
|
169
|
-
def
|
171
|
+
def download(source, destination, _options = {})
|
172
|
+
download = File.join(destination, Bolt::Util.windows_basename(source))
|
173
|
+
conn.download_file(source, destination, download)
|
174
|
+
Bolt::Result.for_download(target, source, destination, download)
|
175
|
+
end
|
176
|
+
|
177
|
+
def run_command(command, options = {})
|
178
|
+
command = [*env_declarations(options[:env_vars]), command].join("\r\n") if options[:env_vars]
|
179
|
+
|
170
180
|
output = execute(command)
|
171
181
|
Bolt::Result.for_command(target,
|
172
182
|
output.stdout.string,
|
@@ -175,7 +185,7 @@ module Bolt
|
|
175
185
|
'command', command)
|
176
186
|
end
|
177
187
|
|
178
|
-
def run_script(script, arguments,
|
188
|
+
def run_script(script, arguments, options = {})
|
179
189
|
# unpack any Sensitive data
|
180
190
|
arguments = unwrap_sensitive_args(arguments)
|
181
191
|
with_tmpdir do |dir|
|
@@ -187,6 +197,8 @@ module Bolt
|
|
187
197
|
args += escape_arguments(arguments)
|
188
198
|
execute_process(path, args)
|
189
199
|
end
|
200
|
+
command = [*env_declarations(options[:env_vars]), command].join("\r\n") if options[:env_vars]
|
201
|
+
|
190
202
|
output = execute(command)
|
191
203
|
Bolt::Result.for_command(target,
|
192
204
|
output.stdout.string,
|
@@ -214,7 +226,7 @@ module Bolt
|
|
214
226
|
task_dir = File.join(dir, task.tasks_dir)
|
215
227
|
mkdirs([task_dir] + extra_files.map { |file| File.join(dir, File.dirname(file['name'])) })
|
216
228
|
extra_files.each do |file|
|
217
|
-
conn.
|
229
|
+
conn.upload_file(file['path'], File.join(dir, file['name']))
|
218
230
|
end
|
219
231
|
end
|
220
232
|
|
@@ -237,9 +249,7 @@ module Bolt
|
|
237
249
|
end
|
238
250
|
|
239
251
|
env_assignments = if Bolt::Task::ENVIRONMENT_METHODS.include?(input_method)
|
240
|
-
envify_params(arguments)
|
241
|
-
set_env(arg, val)
|
242
|
-
end
|
252
|
+
env_declarations(envify_params(arguments))
|
243
253
|
else
|
244
254
|
[]
|
245
255
|
end
|
@@ -258,7 +268,7 @@ module Bolt
|
|
258
268
|
return with_tmpdir do |dir|
|
259
269
|
command += "\r\nif (!$?) { if($LASTEXITCODE) { exit $LASTEXITCODE } else { exit 1 } }"
|
260
270
|
script_file = File.join(dir, "#{SecureRandom.uuid}_wrapper.ps1")
|
261
|
-
conn.
|
271
|
+
conn.upload_file(StringIO.new(command), script_file)
|
262
272
|
args = escape_arguments([script_file])
|
263
273
|
script_invocation = ['powershell.exe', *PS_ARGS, '-File', *args].join(' ')
|
264
274
|
execute(script_invocation)
|