bolt 0.6.1 → 0.7.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.rb +0 -6
- data/lib/bolt/cli.rb +106 -58
- data/lib/bolt/config.rb +10 -3
- data/lib/bolt/executor.rb +14 -2
- data/lib/bolt/formatter.rb +9 -0
- data/lib/bolt/node.rb +12 -10
- data/lib/bolt/node/orch.rb +3 -2
- data/lib/bolt/node/ssh.rb +14 -3
- data/lib/bolt/node/winrm.rb +39 -11
- data/lib/bolt/node_uri.rb +6 -5
- data/lib/bolt/version.rb +1 -1
- metadata +3 -3
- data/lib/bolt/node/formatter.rb +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b7f70a1d9878c8a40b79b597ed62e5c7307d200b
|
4
|
+
data.tar.gz: f8bdfadbfe729a572acd6f6feabfb3b334c1f16e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 68e190c03a5ad2be65fd0832dec9080aaf03de408514d575244997a96663f9f9bdc7ee1b0e18fa4fd35ff3a705fde3b99e40ed5213aa374fa8a359ef28f8a2bf
|
7
|
+
data.tar.gz: 14cc9838705602039e3fd902f229f25b48a96c9580f54efeeb624087debb8ffec0167b5288bcb23d4c7f0309505f39856a4eb37ce24c068fcccba3dc222247ca
|
data/lib/bolt.rb
CHANGED
data/lib/bolt/cli.rb
CHANGED
@@ -56,7 +56,7 @@ Available options are:
|
|
56
56
|
HELP
|
57
57
|
|
58
58
|
SCRIPT_HELP = <<-HELP.freeze
|
59
|
-
Usage: bolt script <action> <script> [options]
|
59
|
+
Usage: bolt script <action> <script> [[arg1] ... [argN]] [options]
|
60
60
|
|
61
61
|
Available actions are:
|
62
62
|
run Upload a local script and run it remotely
|
@@ -86,14 +86,19 @@ HELP
|
|
86
86
|
|
87
87
|
MODES = %w[command script task plan file].freeze
|
88
88
|
ACTIONS = %w[run upload download].freeze
|
89
|
+
TRANSPORTS = %w[ssh winrm pcp].freeze
|
89
90
|
|
90
|
-
attr_reader :parser
|
91
|
+
attr_reader :parser, :config
|
91
92
|
attr_accessor :options
|
92
93
|
|
93
94
|
def initialize(argv)
|
94
|
-
@argv
|
95
|
-
@options = {
|
96
|
-
|
95
|
+
@argv = argv
|
96
|
+
@options = {
|
97
|
+
nodes: [],
|
98
|
+
insecure: false,
|
99
|
+
transport: 'ssh'
|
100
|
+
}
|
101
|
+
@config = Bolt::Config.new
|
97
102
|
@parser = create_option_parser(@options)
|
98
103
|
end
|
99
104
|
|
@@ -111,7 +116,8 @@ HELP
|
|
111
116
|
'* protocol is `ssh` by default, may be `ssh` or `winrm`',
|
112
117
|
'* port is `22` by default for SSH, `5985` for winrm (Optional)'
|
113
118
|
) do |nodes|
|
114
|
-
results[:nodes]
|
119
|
+
results[:nodes] += parse_nodes(nodes)
|
120
|
+
results[:nodes].uniq!
|
115
121
|
end
|
116
122
|
opts.on('-u', '--user USER',
|
117
123
|
"User to authenticate as (Optional)") do |user|
|
@@ -134,18 +140,23 @@ HELP
|
|
134
140
|
"(Optional, defaults to 100)") do |concurrency|
|
135
141
|
results[:concurrency] = concurrency
|
136
142
|
end
|
137
|
-
opts.on('--
|
138
|
-
|
143
|
+
opts.on('--modulepath MODULES',
|
144
|
+
"List of directories containing modules, " \
|
145
|
+
"separated by #{File::PATH_SEPARATOR}") do |modulepath|
|
146
|
+
results[:modulepath] = modulepath.split(File::PATH_SEPARATOR)
|
139
147
|
end
|
140
148
|
opts.on('--params PARAMETERS',
|
141
149
|
"Parameters to a task or plan") do |params|
|
142
150
|
results[:task_options] = parse_params(params)
|
143
151
|
end
|
144
|
-
results[:insecure] = false
|
145
152
|
opts.on('-k', '--insecure',
|
146
153
|
"Whether to connect insecurely ") do |insecure|
|
147
154
|
results[:insecure] = insecure
|
148
155
|
end
|
156
|
+
opts.on('--transport TRANSPORT', TRANSPORTS,
|
157
|
+
"Specify a default transport: #{TRANSPORTS.join(', ')}") do |t|
|
158
|
+
options[:transport] = t
|
159
|
+
end
|
149
160
|
opts.on_tail('--[no-]tty',
|
150
161
|
"Request a pseudo TTY on nodes that support it") do |tty|
|
151
162
|
results[:tty] = tty
|
@@ -167,8 +178,6 @@ HELP
|
|
167
178
|
end
|
168
179
|
|
169
180
|
def parse
|
170
|
-
Bolt.log_level = Logger::WARN
|
171
|
-
|
172
181
|
if @argv.empty?
|
173
182
|
options[:help] = true
|
174
183
|
end
|
@@ -188,9 +197,9 @@ HELP
|
|
188
197
|
options[:object] = remaining.shift
|
189
198
|
|
190
199
|
if options[:debug]
|
191
|
-
|
200
|
+
@config[:log_level] = Logger::DEBUG
|
192
201
|
elsif options[:verbose]
|
193
|
-
|
202
|
+
@config[:log_level] = Logger::INFO
|
194
203
|
end
|
195
204
|
|
196
205
|
if options[:help]
|
@@ -270,31 +279,58 @@ HELP
|
|
270
279
|
"#{MODES.join(', ')}"
|
271
280
|
end
|
272
281
|
|
282
|
+
if options[:action].nil?
|
283
|
+
raise Bolt::CLIError,
|
284
|
+
"Expected an action of the form 'bolt #{options[:mode]} <action>'"
|
285
|
+
end
|
286
|
+
|
273
287
|
unless ACTIONS.include?(options[:action])
|
274
288
|
raise Bolt::CLIError,
|
275
289
|
"Expected action '#{options[:action]}' to be one of " \
|
276
290
|
"#{ACTIONS.join(', ')}"
|
277
291
|
end
|
278
292
|
|
279
|
-
if options[:mode] != 'file' &&
|
293
|
+
if options[:mode] != 'file' && options[:mode] != 'script' &&
|
294
|
+
!options[:leftovers].empty?
|
280
295
|
raise Bolt::CLIError,
|
281
|
-
"
|
296
|
+
"Unknown argument(s) #{options[:leftovers].join(', ')}"
|
297
|
+
end
|
298
|
+
|
299
|
+
if %w[task plan].include?(options[:mode])
|
300
|
+
if options[:object].nil?
|
301
|
+
raise Bolt::CLIError, "Must specify a #{options[:mode]} to run"
|
302
|
+
end
|
303
|
+
# This may mean that we parsed a parameter as the object
|
304
|
+
unless options[:object] =~ /\A([a-z][a-z0-9_]*)?(::[a-z][a-z0-9_]*)*\Z/
|
305
|
+
raise Bolt::CLIError,
|
306
|
+
"Invalid #{options[:mode]} '#{options[:object]}'"
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
unless !options[:nodes].empty? || options[:mode] == 'plan'
|
311
|
+
raise Bolt::CLIError, "Option '--nodes' must be specified"
|
282
312
|
end
|
283
313
|
|
284
|
-
|
285
|
-
raise Bolt::CLIError,
|
314
|
+
if %w[task plan].include?(options[:mode]) && options[:modulepath].nil?
|
315
|
+
raise Bolt::CLIError,
|
316
|
+
"Option '--modulepath' must be specified when running" \
|
317
|
+
" a task or plan"
|
286
318
|
end
|
287
319
|
end
|
288
320
|
|
289
321
|
def handle_parser_errors
|
290
322
|
yield
|
291
323
|
rescue OptionParser::MissingArgument => e
|
292
|
-
raise Bolt::CLIError, "
|
324
|
+
raise Bolt::CLIError, "Option '#{e.args.first}' needs a parameter"
|
293
325
|
rescue OptionParser::InvalidOption => e
|
294
|
-
raise Bolt::CLIError, "
|
326
|
+
raise Bolt::CLIError, "Unknown argument '#{e.args.first}'"
|
295
327
|
end
|
296
328
|
|
297
329
|
def execute(options)
|
330
|
+
%i[concurrency user password tty insecure transport].each do |key|
|
331
|
+
config[key] = options[key]
|
332
|
+
end
|
333
|
+
|
298
334
|
if options[:mode] == 'plan' || options[:mode] == 'task'
|
299
335
|
begin
|
300
336
|
require_relative '../../vendored/require_vendored'
|
@@ -303,19 +339,14 @@ HELP
|
|
303
339
|
end
|
304
340
|
|
305
341
|
Puppet::Util::Log.newdestination(:console)
|
306
|
-
Puppet[:log_level] = if
|
342
|
+
Puppet[:log_level] = if @config[:log_level] == Logger::DEBUG
|
307
343
|
'debug'
|
308
344
|
else
|
309
345
|
'notice'
|
310
346
|
end
|
311
347
|
end
|
312
348
|
|
313
|
-
|
314
|
-
user: options[:user],
|
315
|
-
password: options[:password],
|
316
|
-
tty: options[:tty],
|
317
|
-
insecure: options[:insecure])
|
318
|
-
executor = Bolt::Executor.new(config)
|
349
|
+
executor = Bolt::Executor.new(@config)
|
319
350
|
|
320
351
|
if options[:mode] == 'plan'
|
321
352
|
execute_plan(executor, options)
|
@@ -329,11 +360,13 @@ HELP
|
|
329
360
|
when 'command'
|
330
361
|
executor.run_command(nodes, options[:object])
|
331
362
|
when 'script'
|
332
|
-
|
363
|
+
script = options[:object]
|
364
|
+
validate_file('script', script)
|
365
|
+
executor.run_script(nodes, script, options[:leftovers])
|
333
366
|
when 'task'
|
334
367
|
task_name = options[:object]
|
335
368
|
|
336
|
-
path, metadata = load_task_data(task_name, options[:
|
369
|
+
path, metadata = load_task_data(task_name, options[:modulepath])
|
337
370
|
input_method = metadata['input_method']
|
338
371
|
|
339
372
|
input_method ||= 'both'
|
@@ -346,10 +379,8 @@ HELP
|
|
346
379
|
|
347
380
|
if dest.nil?
|
348
381
|
raise Bolt::CLIError, "A destination path must be specified"
|
349
|
-
elsif !file_exist?(src)
|
350
|
-
raise Bolt::CLIError, "The source file '#{src}' does not exist"
|
351
382
|
end
|
352
|
-
|
383
|
+
validate_file('source file', src)
|
353
384
|
executor.file_upload(nodes, src, dest)
|
354
385
|
end
|
355
386
|
end
|
@@ -362,7 +393,7 @@ HELP
|
|
362
393
|
result = Puppet.override(bolt_executor: executor) do
|
363
394
|
run_plan(options[:object],
|
364
395
|
options[:task_options],
|
365
|
-
options[:
|
396
|
+
options[:modulepath])
|
366
397
|
end
|
367
398
|
puts result
|
368
399
|
rescue Puppet::Error
|
@@ -390,44 +421,61 @@ HELP
|
|
390
421
|
elapsed_time)
|
391
422
|
end
|
392
423
|
|
393
|
-
def
|
394
|
-
|
395
|
-
|
424
|
+
def validate_file(type, path)
|
425
|
+
if path.nil?
|
426
|
+
raise Bolt::CLIError, "A #{type} must be specified"
|
427
|
+
end
|
396
428
|
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
429
|
+
stat = file_stat(path)
|
430
|
+
|
431
|
+
if !stat.readable?
|
432
|
+
raise Bolt::CLIError, "The #{type} '#{path}' is unreadable"
|
433
|
+
elsif !stat.file?
|
434
|
+
raise Bolt::CLIError, "The #{type} '#{path}' is not a file"
|
401
435
|
end
|
436
|
+
rescue Errno::ENOENT
|
437
|
+
raise Bolt::CLIError, "The #{type} '#{path}' does not exist"
|
438
|
+
end
|
439
|
+
|
440
|
+
def file_stat(path)
|
441
|
+
File.stat(path)
|
442
|
+
end
|
402
443
|
|
444
|
+
def load_task_data(name, modulepath)
|
403
445
|
module_name, file_name = name.split('::', 2)
|
404
446
|
file_name ||= 'init'
|
405
447
|
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
448
|
+
begin
|
449
|
+
env = Puppet::Node::Environment.create('bolt', modulepath)
|
450
|
+
Puppet.override(environments: Puppet::Environments::Static.new(env)) do
|
451
|
+
data = Puppet::InfoService::TaskInformationService.task_data(
|
452
|
+
env.name, module_name, name
|
453
|
+
)
|
411
454
|
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
end
|
416
|
-
|
417
|
-
metadata =
|
418
|
-
if data[:metadata_file]
|
419
|
-
JSON.parse(File.read(data[:metadata_file]))
|
420
|
-
else
|
421
|
-
{}
|
455
|
+
file = data[:files].find { |f| File.basename(f, '.*') == file_name }
|
456
|
+
if file.nil?
|
457
|
+
raise Bolt::CLIError, "Failed to load task file for '#{name}'"
|
422
458
|
end
|
423
459
|
|
424
|
-
|
460
|
+
metadata =
|
461
|
+
if data[:metadata_file]
|
462
|
+
JSON.parse(File.read(data[:metadata_file]))
|
463
|
+
else
|
464
|
+
{}
|
465
|
+
end
|
466
|
+
|
467
|
+
[file, metadata]
|
468
|
+
end
|
469
|
+
rescue Puppet::Module::Task::TaskNotFound
|
470
|
+
raise Bolt::CLIError,
|
471
|
+
"Could not find task '#{name}' in module '#{module_name}'"
|
472
|
+
rescue Puppet::Module::MissingModule
|
473
|
+
# Generate message so we don't expose "bolt environment"
|
474
|
+
raise Bolt::CLIError, "Could not find module '#{module_name}'"
|
425
475
|
end
|
426
476
|
end
|
427
477
|
|
428
|
-
def run_plan(plan, args,
|
429
|
-
modulepath = modules ? [modules] : []
|
430
|
-
|
478
|
+
def run_plan(plan, args, modulepath)
|
431
479
|
Dir.mktmpdir('bolt') do |dir|
|
432
480
|
cli = []
|
433
481
|
Puppet::Settings::REQUIRED_APP_SETTINGS.each do |setting|
|
data/lib/bolt/config.rb
CHANGED
@@ -1,14 +1,21 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
1
3
|
module Bolt
|
2
4
|
Config = Struct.new(:concurrency,
|
3
5
|
:user,
|
4
6
|
:password,
|
5
7
|
:tty,
|
6
|
-
:insecure
|
7
|
-
|
8
|
+
:insecure,
|
9
|
+
:transport,
|
10
|
+
:log_level,
|
11
|
+
:log_destination) do
|
8
12
|
DEFAULTS = {
|
9
13
|
concurrency: 100,
|
10
14
|
tty: false,
|
11
|
-
insecure: false
|
15
|
+
insecure: false,
|
16
|
+
transport: 'ssh',
|
17
|
+
log_level: Logger::WARN,
|
18
|
+
log_destination: STDERR
|
12
19
|
}.freeze
|
13
20
|
|
14
21
|
def initialize(**kwargs)
|
data/lib/bolt/executor.rb
CHANGED
@@ -1,11 +1,17 @@
|
|
1
|
+
require 'logger'
|
1
2
|
require 'concurrent'
|
2
3
|
require 'bolt/result'
|
3
4
|
require 'bolt/config'
|
5
|
+
require 'bolt/formatter'
|
4
6
|
|
5
7
|
module Bolt
|
6
8
|
class Executor
|
7
9
|
def initialize(config = Bolt::Config.new)
|
8
10
|
@config = config
|
11
|
+
@logger = Logger.new(config[:log_destination])
|
12
|
+
@logger.progname = 'executor'
|
13
|
+
@logger.level = config[:log_level]
|
14
|
+
@logger.formatter = Bolt::Formatter.new
|
9
15
|
end
|
10
16
|
|
11
17
|
def from_uris(nodes)
|
@@ -19,6 +25,12 @@ module Bolt
|
|
19
25
|
|
20
26
|
poolsize = [nodes.length, @config[:concurrency]].min
|
21
27
|
pool = Concurrent::FixedThreadPool.new(poolsize)
|
28
|
+
@logger.debug { "Started with #{poolsize} thread(s)" }
|
29
|
+
|
30
|
+
nodes.map(&:class).uniq.each do |klass|
|
31
|
+
klass.initialize_transport(@logger)
|
32
|
+
end
|
33
|
+
|
22
34
|
nodes.each { |node|
|
23
35
|
pool.post do
|
24
36
|
results[node] =
|
@@ -47,9 +59,9 @@ module Bolt
|
|
47
59
|
end
|
48
60
|
end
|
49
61
|
|
50
|
-
def run_script(nodes, script)
|
62
|
+
def run_script(nodes, script, arguments)
|
51
63
|
on(nodes) do |node|
|
52
|
-
node.run_script(script)
|
64
|
+
node.run_script(script, arguments)
|
53
65
|
end
|
54
66
|
end
|
55
67
|
|
data/lib/bolt/node.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'logger'
|
2
2
|
require 'bolt/node_uri'
|
3
|
-
require 'bolt/
|
3
|
+
require 'bolt/formatter'
|
4
4
|
require 'bolt/result'
|
5
5
|
require 'bolt/config'
|
6
6
|
|
@@ -10,7 +10,7 @@ module Bolt
|
|
10
10
|
ENVIRONMENT_METHODS = %w[both environment].freeze
|
11
11
|
|
12
12
|
def self.from_uri(uri_string, **kwargs)
|
13
|
-
uri = NodeURI.new(uri_string)
|
13
|
+
uri = NodeURI.new(uri_string, kwargs[:config][:transport])
|
14
14
|
klass = case uri.scheme
|
15
15
|
when 'winrm'
|
16
16
|
Bolt::WinRM
|
@@ -27,11 +27,12 @@ module Bolt
|
|
27
27
|
**kwargs)
|
28
28
|
end
|
29
29
|
|
30
|
+
def self.initialize_transport(_logger); end
|
31
|
+
|
30
32
|
attr_reader :logger, :host, :uri, :user, :password
|
31
33
|
|
32
34
|
def initialize(host, port = nil, user = nil, password = nil, uri: nil,
|
33
|
-
config: Bolt::Config.new
|
34
|
-
log_level: Bolt.log_level || Logger::WARN)
|
35
|
+
config: Bolt::Config.new)
|
35
36
|
@host = host
|
36
37
|
@port = port
|
37
38
|
@user = user || config[:user]
|
@@ -40,14 +41,15 @@ module Bolt
|
|
40
41
|
@insecure = config[:insecure]
|
41
42
|
@uri = uri
|
42
43
|
|
43
|
-
@logger = init_logger(
|
44
|
-
@transport_logger = init_logger(
|
44
|
+
@logger = init_logger(config[:log_destination], config[:log_level])
|
45
|
+
@transport_logger = init_logger(config[:log_destination], Logger::WARN)
|
45
46
|
end
|
46
47
|
|
47
|
-
def init_logger(destination
|
48
|
+
def init_logger(destination, level)
|
48
49
|
logger = Logger.new(destination)
|
50
|
+
logger.progname = @host
|
49
51
|
logger.level = level
|
50
|
-
logger.formatter = Bolt::
|
52
|
+
logger.formatter = Bolt::Formatter.new
|
51
53
|
logger
|
52
54
|
end
|
53
55
|
|
@@ -66,8 +68,8 @@ module Bolt
|
|
66
68
|
_run_command(command).to_command_result
|
67
69
|
end
|
68
70
|
|
69
|
-
def run_script(script)
|
70
|
-
_run_script(script).to_command_result
|
71
|
+
def run_script(script, arguments)
|
72
|
+
_run_script(script, arguments).to_command_result
|
71
73
|
end
|
72
74
|
|
73
75
|
def run_task(task, input_method, arguments)
|
data/lib/bolt/node/orch.rb
CHANGED
@@ -105,12 +105,13 @@ module Bolt
|
|
105
105
|
_run_task(BOLT_MOCK_FILE, 'stdin', params)
|
106
106
|
end
|
107
107
|
|
108
|
-
def _run_script(script)
|
108
|
+
def _run_script(script, arguments)
|
109
109
|
content = File.open(script, &:read)
|
110
110
|
content = Base64.encode64(content)
|
111
111
|
params = {
|
112
112
|
action: 'script',
|
113
|
-
content: content
|
113
|
+
content: content,
|
114
|
+
arguments: arguments
|
114
115
|
}
|
115
116
|
unwrap_bolt_result(_run_task(BOLT_MOCK_FILE, 'stdin', params))
|
116
117
|
end
|
data/lib/bolt/node/ssh.rb
CHANGED
@@ -1,10 +1,19 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'shellwords'
|
1
3
|
require 'net/ssh'
|
2
4
|
require 'net/sftp'
|
3
|
-
require 'json'
|
4
5
|
require 'bolt/node/result'
|
5
6
|
|
6
7
|
module Bolt
|
7
8
|
class SSH < Node
|
9
|
+
def self.initialize_transport(logger)
|
10
|
+
require 'net/ssh/krb'
|
11
|
+
rescue LoadError
|
12
|
+
logger.debug {
|
13
|
+
"Authentication method 'gssapi-with-mic' is not available"
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
8
17
|
def connect
|
9
18
|
options = {
|
10
19
|
logger: @transport_logger,
|
@@ -131,10 +140,12 @@ module Bolt
|
|
131
140
|
execute(command)
|
132
141
|
end
|
133
142
|
|
134
|
-
def _run_script(script)
|
143
|
+
def _run_script(script, arguments)
|
135
144
|
@logger.info { "Running script '#{script}'" }
|
145
|
+
@logger.debug { "arguments: #{arguments}" }
|
146
|
+
|
136
147
|
with_remote_file(script) do |remote_path|
|
137
|
-
execute("'#{remote_path}'")
|
148
|
+
execute("'#{remote_path}' #{Shellwords.join(arguments)}")
|
138
149
|
end
|
139
150
|
end
|
140
151
|
|
data/lib/bolt/node/winrm.rb
CHANGED
@@ -41,7 +41,8 @@ module Bolt
|
|
41
41
|
return Bolt::Node::Success.new if @shell_initialized
|
42
42
|
result = execute(<<-PS)
|
43
43
|
|
44
|
-
$ENV:PATH += ";${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\
|
44
|
+
$ENV:PATH += ";${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\bin\\;" +
|
45
|
+
"${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\sys\\ruby\\bin\\"
|
45
46
|
$ENV:RUBYLIB = "${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\puppet\\lib;" +
|
46
47
|
"${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\facter\\lib;" +
|
47
48
|
"${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\hiera\\lib;" +
|
@@ -70,6 +71,7 @@ function Invoke-Interpreter
|
|
70
71
|
|
71
72
|
$startInfo = New-Object System.Diagnostics.ProcessStartInfo($Path, $Arguments)
|
72
73
|
$startInfo.UseShellExecute = $false
|
74
|
+
$startInfo.WorkingDirectory = Split-Path -Parent (Get-Command $Path).Path
|
73
75
|
if ($StdinInput) { $startInfo.RedirectStandardInput = $true }
|
74
76
|
$startInfo.RedirectStandardOutput = $true
|
75
77
|
$startInfo.RedirectStandardError = $true
|
@@ -129,12 +131,20 @@ PS
|
|
129
131
|
# 10 minutes in milliseconds
|
130
132
|
DEFAULT_EXECUTION_TIMEOUT = 10 * 60 * 1000
|
131
133
|
|
132
|
-
def execute_process(path = '', arguments =
|
134
|
+
def execute_process(path = '', arguments = [], stdin = nil,
|
133
135
|
timeout_ms = DEFAULT_EXECUTION_TIMEOUT)
|
136
|
+
quoted_args = arguments.map do |arg|
|
137
|
+
"'" + arg.gsub("'", "''") + "'"
|
138
|
+
end.join(',')
|
139
|
+
|
134
140
|
execute(<<-PS)
|
141
|
+
$quoted_array = @(
|
142
|
+
#{quoted_args}
|
143
|
+
)
|
144
|
+
|
135
145
|
$invokeArgs = @{
|
136
146
|
Path = "#{path}"
|
137
|
-
Arguments =
|
147
|
+
Arguments = $quoted_array -Join ' '
|
138
148
|
Timeout = #{timeout_ms}
|
139
149
|
#{stdin.nil? ? '' : "StdinInput = @'\n" + stdin + "\n'@"}
|
140
150
|
}
|
@@ -144,22 +154,28 @@ $LASTEXITCODE = Invoke-Interpreter @invokeArgs
|
|
144
154
|
PS
|
145
155
|
end
|
146
156
|
|
147
|
-
VALID_EXTENSIONS = ['.ps1', '.rb'].freeze
|
157
|
+
VALID_EXTENSIONS = ['.ps1', '.rb', '.pp'].freeze
|
148
158
|
|
149
|
-
PS_ARGS =
|
150
|
-
|
159
|
+
PS_ARGS = %w[
|
160
|
+
-NoProfile -NonInteractive -NoLogo -ExecutionPolicy Bypass
|
161
|
+
].freeze
|
151
162
|
|
152
163
|
def process_from_extension(path)
|
153
164
|
case Pathname(path).extname.downcase
|
154
165
|
when '.rb'
|
155
166
|
[
|
156
167
|
'ruby.exe',
|
157
|
-
|
168
|
+
['-S', "\"#{path}\""]
|
158
169
|
]
|
159
170
|
when '.ps1'
|
160
171
|
[
|
161
172
|
'powershell.exe',
|
162
|
-
|
173
|
+
[*PS_ARGS, '-File', "\"#{path}\""]
|
174
|
+
]
|
175
|
+
when '.pp'
|
176
|
+
[
|
177
|
+
'puppet.bat',
|
178
|
+
['apply', "\"#{path}\""]
|
163
179
|
]
|
164
180
|
end
|
165
181
|
end
|
@@ -214,10 +230,11 @@ PS
|
|
214
230
|
execute(command)
|
215
231
|
end
|
216
232
|
|
217
|
-
def _run_script(script)
|
233
|
+
def _run_script(script, arguments)
|
218
234
|
@logger.info { "Running script '#{script}'" }
|
219
235
|
with_remote_file(script) do |remote_path|
|
220
|
-
args =
|
236
|
+
args = [*PS_ARGS, '-File', "\"#{remote_path}\""]
|
237
|
+
args += escape_arguments(arguments)
|
221
238
|
execute_process('powershell.exe', args)
|
222
239
|
end
|
223
240
|
end
|
@@ -241,7 +258,18 @@ PS
|
|
241
258
|
Bolt::Node::Success.new
|
242
259
|
end.then do
|
243
260
|
with_remote_file(task) do |remote_path|
|
244
|
-
|
261
|
+
path, args = *process_from_extension(remote_path)
|
262
|
+
execute_process(path, args, stdin)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
def escape_arguments(arguments)
|
268
|
+
arguments.map do |arg|
|
269
|
+
if arg =~ / /
|
270
|
+
"\"#{arg}\""
|
271
|
+
else
|
272
|
+
arg
|
245
273
|
end
|
246
274
|
end
|
247
275
|
end
|
data/lib/bolt/node_uri.rb
CHANGED
@@ -2,19 +2,20 @@ require 'addressable'
|
|
2
2
|
|
3
3
|
module Bolt
|
4
4
|
class NodeURI
|
5
|
-
def initialize(string)
|
6
|
-
@uri = parse(string)
|
5
|
+
def initialize(string, transport = 'ssh')
|
6
|
+
@uri = parse(string, transport)
|
7
7
|
end
|
8
8
|
|
9
|
-
def parse(string)
|
10
|
-
uri = if string =~ %r{^
|
9
|
+
def parse(string, transport)
|
10
|
+
uri = if string =~ %r{^[^:]+://}
|
11
11
|
Addressable::URI.parse(string)
|
12
12
|
else
|
13
|
-
Addressable::URI.parse("
|
13
|
+
Addressable::URI.parse("#{transport}://#{string}")
|
14
14
|
end
|
15
15
|
uri.port ||= 5985 if uri.scheme == 'winrm'
|
16
16
|
uri
|
17
17
|
end
|
18
|
+
private :parse
|
18
19
|
|
19
20
|
def hostname
|
20
21
|
@uri.hostname
|
data/lib/bolt/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bolt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Puppet
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-11-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|
@@ -281,9 +281,9 @@ files:
|
|
281
281
|
- lib/bolt/cli.rb
|
282
282
|
- lib/bolt/config.rb
|
283
283
|
- lib/bolt/executor.rb
|
284
|
+
- lib/bolt/formatter.rb
|
284
285
|
- lib/bolt/node.rb
|
285
286
|
- lib/bolt/node/errors.rb
|
286
|
-
- lib/bolt/node/formatter.rb
|
287
287
|
- lib/bolt/node/orch.rb
|
288
288
|
- lib/bolt/node/result.rb
|
289
289
|
- lib/bolt/node/ssh.rb
|
data/lib/bolt/node/formatter.rb
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
require 'logger'
|
2
|
-
|
3
|
-
module Bolt
|
4
|
-
class Node
|
5
|
-
class Formatter < Logger::Formatter
|
6
|
-
def initialize(host)
|
7
|
-
@host = host
|
8
|
-
end
|
9
|
-
|
10
|
-
def call(severity, time, _progname, msg)
|
11
|
-
"#{format_datetime(time)} #{severity} #{@host}: #{msg2str(msg)}\n"
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|