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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0e2611b80200c25247a4eab55d365449cebb736f
4
- data.tar.gz: f95c519a312580492f059a4c94b289cce46872be
3
+ metadata.gz: b7f70a1d9878c8a40b79b597ed62e5c7307d200b
4
+ data.tar.gz: f8bdfadbfe729a572acd6f6feabfb3b334c1f16e
5
5
  SHA512:
6
- metadata.gz: d6b368afa1ee1a1ef1ead7731681223aacceb78232560ada3fe99d89ba47a4e8d8a01957d49fa697267458942bcc068f8140d579de0d5aa1836ae92b4e2ec231
7
- data.tar.gz: 41c0349f16bef422ecec574a4a8b29f7bb34169b8778404b265a4d236a9dc3b8af8cf8f53d4bd2df0641b82132881ba0efff4d53fc733294255a31c34327eba0
6
+ metadata.gz: 68e190c03a5ad2be65fd0832dec9080aaf03de408514d575244997a96663f9f9bdc7ee1b0e18fa4fd35ff3a705fde3b99e40ed5213aa374fa8a359ef28f8a2bf
7
+ data.tar.gz: 14cc9838705602039e3fd902f229f25b48a96c9580f54efeeb624087debb8ffec0167b5288bcb23d4c7f0309505f39856a4eb37ce24c068fcccba3dc222247ca
data/lib/bolt.rb CHANGED
@@ -1,10 +1,4 @@
1
- require 'logger'
2
-
3
1
  module Bolt
4
- class << self
5
- attr_accessor :log_level
6
- end
7
-
8
2
  require 'bolt/executor'
9
3
  require 'bolt/node'
10
4
  end
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 = 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] = parse_nodes(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('--modules MODULES', "Path to modules directory") do |modules|
138
- results[:modules] = modules
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
- Bolt.log_level = Logger::DEBUG
200
+ @config[:log_level] = Logger::DEBUG
192
201
  elsif options[:verbose]
193
- Bolt.log_level = Logger::INFO
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' && !options[:leftovers].empty?
293
+ if options[:mode] != 'file' && options[:mode] != 'script' &&
294
+ !options[:leftovers].empty?
280
295
  raise Bolt::CLIError,
281
- "unknown argument(s) #{options[:leftovers].join(', ')}"
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
- unless options[:nodes] || options[:mode] == 'plan'
285
- raise Bolt::CLIError, "option --nodes must be specified"
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, "option '#{e.args.first}' needs a parameter"
324
+ raise Bolt::CLIError, "Option '#{e.args.first}' needs a parameter"
293
325
  rescue OptionParser::InvalidOption => e
294
- raise Bolt::CLIError, "unknown argument '#{e.args.first}'"
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 Bolt.log_level == Logger::DEBUG
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
- config = Bolt::Config.new(concurrency: options[:concurrency],
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
- executor.run_script(nodes, options[:object])
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[:modules])
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[:modules])
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 file_exist?(path)
394
- File.exist?(path)
395
- end
424
+ def validate_file(type, path)
425
+ if path.nil?
426
+ raise Bolt::CLIError, "A #{type} must be specified"
427
+ end
396
428
 
397
- def load_task_data(name, modules)
398
- if modules.nil?
399
- raise Bolt::CLIError,
400
- "The '--modules' option must be specified to run a task"
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
- env = Puppet::Node::Environment.create('bolt', [modules])
407
- Puppet.override(environments: Puppet::Environments::Static.new(env)) do
408
- data = Puppet::InfoService::TaskInformationService.task_data(
409
- env.name, module_name, name
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
- file = data[:files].find { |f| File.basename(f, '.*') == file_name }
413
- if file.nil?
414
- raise Bolt::CLIError, "Failed to load task file for '#{name}'"
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
- [file, metadata]
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, modules)
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) do
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
 
@@ -0,0 +1,9 @@
1
+ require 'logger'
2
+
3
+ module Bolt
4
+ class Formatter < Logger::Formatter
5
+ def call(severity, time, progname, msg)
6
+ "#{format_datetime(time)} #{severity} #{progname}: #{msg2str(msg)}\n"
7
+ end
8
+ end
9
+ end
data/lib/bolt/node.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  require 'logger'
2
2
  require 'bolt/node_uri'
3
- require 'bolt/node/formatter'
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(level: log_level)
44
- @transport_logger = init_logger(level: Logger::WARN)
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: STDERR, level: Logger::WARN)
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::Node::Formatter.new(@host)
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)
@@ -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
 
@@ -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\\sys\\ruby\\bin\\"
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 = '', stdin = nil,
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 = "#{arguments.gsub('"', '""')}"
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
- '-NoProfile -NonInteractive -NoLogo -ExecutionPolicy Bypass'.freeze
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
- "-S \"#{path}\""
168
+ ['-S', "\"#{path}\""]
158
169
  ]
159
170
  when '.ps1'
160
171
  [
161
172
  'powershell.exe',
162
- "#{PS_ARGS} -File \"#{path}\""
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 = "#{PS_ARGS} -File \"#{remote_path}\""
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
- execute_process(*process_from_extension(remote_path), stdin)
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{^(ssh|winrm|pcp)://}
9
+ def parse(string, transport)
10
+ uri = if string =~ %r{^[^:]+://}
11
11
  Addressable::URI.parse(string)
12
12
  else
13
- Addressable::URI.parse("ssh://#{string}")
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
@@ -1,3 +1,3 @@
1
1
  module Bolt
2
- VERSION = '0.6.1'.freeze
2
+ VERSION = '0.7.0'.freeze
3
3
  end
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.6.1
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-10-24 00:00:00.000000000 Z
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
@@ -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