sshkit 1.7.1 → 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -2
- data/BREAKING_API_WISHLIST.md +14 -0
- data/CHANGELOG.md +74 -0
- data/CONTRIBUTING.md +43 -0
- data/EXAMPLES.md +265 -169
- data/Gemfile +7 -0
- data/README.md +274 -9
- data/RELEASING.md +16 -8
- data/Rakefile +8 -0
- data/lib/sshkit.rb +0 -9
- data/lib/sshkit/all.rb +6 -4
- data/lib/sshkit/backends/abstract.rb +42 -42
- data/lib/sshkit/backends/connection_pool.rb +57 -8
- data/lib/sshkit/backends/local.rb +21 -50
- data/lib/sshkit/backends/netssh.rb +45 -98
- data/lib/sshkit/backends/printer.rb +3 -23
- data/lib/sshkit/backends/skipper.rb +4 -8
- data/lib/sshkit/color.rb +51 -20
- data/lib/sshkit/command.rb +68 -47
- data/lib/sshkit/configuration.rb +38 -5
- data/lib/sshkit/deprecation_logger.rb +17 -0
- data/lib/sshkit/formatters/abstract.rb +28 -4
- data/lib/sshkit/formatters/black_hole.rb +1 -2
- data/lib/sshkit/formatters/dot.rb +3 -10
- data/lib/sshkit/formatters/pretty.rb +31 -56
- data/lib/sshkit/formatters/simple_text.rb +6 -44
- data/lib/sshkit/host.rb +5 -6
- data/lib/sshkit/logger.rb +0 -1
- data/lib/sshkit/mapping_interaction_handler.rb +47 -0
- data/lib/sshkit/runners/parallel.rb +1 -1
- data/lib/sshkit/runners/sequential.rb +1 -1
- data/lib/sshkit/version.rb +1 -1
- data/sshkit.gemspec +0 -1
- data/test/functional/backends/test_local.rb +14 -1
- data/test/functional/backends/test_netssh.rb +58 -50
- data/test/helper.rb +2 -2
- data/test/unit/backends/test_abstract.rb +145 -0
- data/test/unit/backends/test_connection_pool.rb +27 -2
- data/test/unit/backends/test_printer.rb +47 -47
- data/test/unit/formatters/test_custom.rb +65 -0
- data/test/unit/formatters/test_dot.rb +25 -32
- data/test/unit/formatters/test_pretty.rb +114 -22
- data/test/unit/formatters/test_simple_text.rb +83 -0
- data/test/unit/test_color.rb +69 -5
- data/test/unit/test_command.rb +53 -18
- data/test/unit/test_command_map.rb +0 -4
- data/test/unit/test_configuration.rb +47 -7
- data/test/unit/test_coordinator.rb +45 -52
- data/test/unit/test_deprecation_logger.rb +38 -0
- data/test/unit/test_host.rb +3 -4
- data/test/unit/test_logger.rb +0 -1
- data/test/unit/test_mapping_interaction_handler.rb +101 -0
- metadata +37 -41
- data/lib/sshkit/utils/capture_output_methods.rb +0 -13
- data/test/functional/test_coordinator.rb +0 -17
data/Gemfile
CHANGED
@@ -6,3 +6,10 @@ platforms :rbx do
|
|
6
6
|
gem 'rubysl', '~> 2.0'
|
7
7
|
gem 'json'
|
8
8
|
end
|
9
|
+
|
10
|
+
# Chandler requires Ruby >= 2.1.0, but depending on the Travis environment,
|
11
|
+
# we may not meet that requirement. Only include the chandler gem if the Ruby
|
12
|
+
# requirement is met. (Chandler is used only for `rake release`; see Rakefile.)
|
13
|
+
if Gem::Requirement.new('>= 2.1.0').satisfied_by?(Gem::Version.new(RUBY_VERSION))
|
14
|
+
gem 'chandler', '>= 0.1.1'
|
15
|
+
end
|
data/README.md
CHANGED
@@ -11,23 +11,82 @@ more servers.
|
|
11
11
|
The typical use-case looks something like this:
|
12
12
|
|
13
13
|
```ruby
|
14
|
+
require 'sshkit'
|
14
15
|
require 'sshkit/dsl'
|
15
16
|
|
16
|
-
on %w{1.example.com 2.example.com}, in: :sequence, wait: 5 do
|
17
|
+
on %w{1.example.com 2.example.com}, in: :sequence, wait: 5 do |host|
|
17
18
|
within "/opt/sites/example.com" do
|
18
19
|
as :deploy do
|
19
20
|
with rails_env: :production do
|
20
21
|
rake "assets:precompile"
|
21
22
|
runner "S3::Sync.notify"
|
22
|
-
execute
|
23
|
+
execute :node, "socket_server.js"
|
23
24
|
end
|
24
25
|
end
|
25
26
|
end
|
26
27
|
end
|
27
28
|
```
|
28
29
|
|
29
|
-
|
30
|
-
|
30
|
+
You can find many other examples of how to use SSHKit over in [EXAMPLES.md](EXAMPLES.md).
|
31
|
+
|
32
|
+
## Basic usage
|
33
|
+
|
34
|
+
The `on()` method is used to specify the backends on which you'd like to run the commands.
|
35
|
+
You can pass one or more hosts as parameters; this runs commands via SSH. Alternatively you can
|
36
|
+
pass `:local` to run commands locally. By default SSKit will run the commands on all hosts in
|
37
|
+
parallel.
|
38
|
+
|
39
|
+
#### Running commands
|
40
|
+
|
41
|
+
All backends support the `execute(*args)`, `test(*args)` & `capture(*args)` methods
|
42
|
+
for executing a command. You can call any of these methods in the context of an `on()`
|
43
|
+
block.
|
44
|
+
|
45
|
+
**Note: In SSHKit, the first parameter of the `execute` / `test` / `capture` methods
|
46
|
+
has a special significance. If the first parameter isn't a Symbol,
|
47
|
+
SSHKit assumes that you want to execute the raw command and the
|
48
|
+
`as` / `within` / `with` methods, `SSHKit.config.umask` and [the comand map](#the-command-map)
|
49
|
+
have no effect.**
|
50
|
+
|
51
|
+
Typically, you would pass a Symbol for the command name and it's args as follows:
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
on '1.example.com'
|
55
|
+
if test("[ -f somefile.txt ]")
|
56
|
+
execute(:cp, 'somefile.txt', 'somewhere_else.txt')
|
57
|
+
end
|
58
|
+
ls_output = capture(:ls, '-l')
|
59
|
+
end
|
60
|
+
```
|
61
|
+
|
62
|
+
By default the `capture` methods strips whitespace. If you need to preserve whitespace
|
63
|
+
you can pass the `strip: false` option: `capture(:ls, '-l', strip: false)`
|
64
|
+
|
65
|
+
#### Transferring files
|
66
|
+
|
67
|
+
All backends also support the `upload!` and `download!` methods for transferring files.
|
68
|
+
For the remote backed, the file is tranferred with scp.
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
on '1.example.com' do
|
72
|
+
upload! 'some_local_file.txt', '/home/some_user/somewhere'
|
73
|
+
download! '/home/some_user/some_remote_file.txt', 'somewhere_local'
|
74
|
+
end
|
75
|
+
```
|
76
|
+
|
77
|
+
#### Users, working directories, environment variables and umask
|
78
|
+
|
79
|
+
When running commands, you can tell SSHKit to set up the context for those
|
80
|
+
commands using the following methods:
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
as(user: 'un', group: 'grp') { execute('cmd') } # Executes sudo -u un -- sh -c 'sg grp cmd'
|
84
|
+
within('/somedir') { execute('cmd') } # Executes cd /somedir && cmd
|
85
|
+
with(env_var: 'value') { execute('cmd') } # Executes ENV_VAR=value cmd
|
86
|
+
SSHKit.config.umask = '077' # All commands are executed with umask 077 && cmd
|
87
|
+
```
|
88
|
+
|
89
|
+
The `as()` / `within()` / `with()` are nestable in any order, repeatable, and stackable.
|
31
90
|
|
32
91
|
When used inside a block in this way, `as()` and `within()` will guard
|
33
92
|
the block they are given with a check.
|
@@ -99,7 +158,7 @@ end
|
|
99
158
|
# they will all exist (all tar commands exited with a success status, or
|
100
159
|
# that we will have raised an exception if one of them failed.
|
101
160
|
on all_servers do |host|
|
102
|
-
|
161
|
+
within site_dir do
|
103
162
|
backup_filename = "backup-#{host.hostname}.tar.gz"
|
104
163
|
target_filename = "backups/#{Time.now.utc.iso8601}/#{host.hostname}.tar.gz"
|
105
164
|
puts capture(:s3cmd, 'put', backup_filename, target_filename)
|
@@ -197,6 +256,145 @@ desirable.
|
|
197
256
|
*Note:* All keys should be symbolised, as the *Command* object will symbolize it's
|
198
257
|
first argument before attempting to find it in the *command map*.
|
199
258
|
|
259
|
+
## Interactive commands
|
260
|
+
> (BETA) (Added in version #.##)
|
261
|
+
|
262
|
+
By default, commands against remote servers are run in a *non-login, non-interactive* ssh session.
|
263
|
+
This is by design, to try and isolate the environment and make sure that things work as expected,
|
264
|
+
regardless of any changes that might happen on the server side. This means that,
|
265
|
+
although the server may have prompted you, and be waiting for it,
|
266
|
+
**you cannot send data to the server by typing into your terminal window**.
|
267
|
+
Wherever possible, you should call commands in a way that doesn't require interaction
|
268
|
+
(eg by specifying all options as command arguments).
|
269
|
+
|
270
|
+
However in some cases, you may want to programmatically drive interaction with a command
|
271
|
+
and this can be achieved by specifying an `:interaction_handler` option when you `execute`, `capture` or `test` a command.
|
272
|
+
|
273
|
+
**It is not necessary, or desirable to enable `Netssh.config.pty` to use the `interaction_handler` option.
|
274
|
+
Only enable `Netssh.config.pty` if the command you are calling won't work without a pty.**
|
275
|
+
|
276
|
+
An `interaction_handler` is an object which responds to `on_data(command, stream_name, data, channel)`.
|
277
|
+
The `interaction_handler`'s `on_data` method will be called each time `stdout` or `stderr` data is available from
|
278
|
+
the server. Data can be sent back to the server using the `channel` parameter. This allows scripting of command
|
279
|
+
interaction by responding to `stdout` or `stderr` lines with any input required.
|
280
|
+
|
281
|
+
For example, an interaction handler to change the password of your linux user using the `passwd` command could look like this:
|
282
|
+
|
283
|
+
```ruby
|
284
|
+
class PasswdInteractionHandler
|
285
|
+
def on_data(command, stream_name, data, channel)
|
286
|
+
puts data
|
287
|
+
case data
|
288
|
+
when '(current) UNIX password: '
|
289
|
+
channel.send_data("old_pw\n")
|
290
|
+
when 'Enter new UNIX password: ', 'Retype new UNIX password: '
|
291
|
+
channel.send_data("new_pw\n")
|
292
|
+
when 'passwd: password updated successfully'
|
293
|
+
else
|
294
|
+
raise "Unexpected stderr #{stderr}"
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
# ...
|
300
|
+
|
301
|
+
execute(:passwd, interaction_handler: PasswdInteractionHandler.new)
|
302
|
+
```
|
303
|
+
|
304
|
+
#### Using the `SSHKit::MappingInteractionHandler`
|
305
|
+
|
306
|
+
Often, you want to map directly from a short output string returned by the server (either stdout or stderr)
|
307
|
+
to a corresponding input string (as in the case above). For this case you can specify
|
308
|
+
the `interaction_handler` option as a hash. This is used to create a `SSHKit::MappingInteractionHandler` which
|
309
|
+
provides similar functionality to the linux [expect](http://expect.sourceforge.net/) library:
|
310
|
+
|
311
|
+
```ruby
|
312
|
+
execute(:passwd, interaction_handler: {
|
313
|
+
'(current) UNIX password: ' => "old_pw\n",
|
314
|
+
/(Enter|Retype) new UNIX password: / => "new_pw\n"
|
315
|
+
})
|
316
|
+
```
|
317
|
+
|
318
|
+
Note: the key to the hash keys are matched against the server output `data` using the case equals `===` method.
|
319
|
+
This means that regexes and any objects which define `===` can be used as hash keys.
|
320
|
+
|
321
|
+
Hash keys are matched in order, which allows for default wildcard matches:
|
322
|
+
|
323
|
+
```ruby
|
324
|
+
execute(:my_command, interaction_handler: {
|
325
|
+
"some specific line\n" => "specific input\n",
|
326
|
+
/.*/ => "default input\n"
|
327
|
+
})
|
328
|
+
```
|
329
|
+
|
330
|
+
You can also pass a Proc object to map the output line from the server:
|
331
|
+
|
332
|
+
```ruby
|
333
|
+
execute(:passwd, interaction_handler: lambda { |server_data|
|
334
|
+
case server_data
|
335
|
+
when '(current) UNIX password: '
|
336
|
+
"old_pw\n",
|
337
|
+
when /(Enter|Retype) new UNIX password: /
|
338
|
+
"new_pw\n"
|
339
|
+
end
|
340
|
+
})
|
341
|
+
```
|
342
|
+
|
343
|
+
`MappingInteractionHandler`s are stateless, so you can assign one to a constant and reuse it:
|
344
|
+
|
345
|
+
```ruby
|
346
|
+
ENTER_PASSWORD = SSHKit::MappingInteractionHandler.new(
|
347
|
+
"Please Enter Password\n" => "some_password\n"
|
348
|
+
)
|
349
|
+
|
350
|
+
execute(:first_command, interaction_handler: ENTER_PASSWORD)
|
351
|
+
execute(:second_command, interaction_handler: ENTER_PASSWORD)
|
352
|
+
```
|
353
|
+
|
354
|
+
#### Exploratory logging
|
355
|
+
|
356
|
+
By default, the `MappingInteractionHandler` does not log, in case the server output or input contains sensitive
|
357
|
+
information. However, if you pass a second `log_level` parameter to the constructor, the `MappingInteractionHandler`
|
358
|
+
will log information about what output is being returned by the server, and what input is being sent
|
359
|
+
in response. This can be helpful if you don't know exactly what the server is sending back (whitespace, newlines etc).
|
360
|
+
|
361
|
+
```ruby
|
362
|
+
# Start with this and run your script
|
363
|
+
execute(:unfamiliar_command, MappingInteractionHandler.new({}, :debug))
|
364
|
+
# DEBUG log => Unable to find interaction handler mapping for stdout:
|
365
|
+
# "Please type your input:\r\n" so no response was sent"
|
366
|
+
|
367
|
+
# Update mapping:
|
368
|
+
execute(:unfamiliar_command, MappingInteractionHandler.new(
|
369
|
+
{"Please type your input:\r\n" => "Some input\n"}
|
370
|
+
:debug
|
371
|
+
))
|
372
|
+
```
|
373
|
+
|
374
|
+
#### The `data` parameter
|
375
|
+
|
376
|
+
The `data` parameter of `on_data(command, stream_name, data, channel)` is a string containing the latest data
|
377
|
+
delivered from the backend.
|
378
|
+
|
379
|
+
When using the `Netssh` backend for commands where a small amount of data is returned (eg prompting for sudo passwords),
|
380
|
+
`on_data` will normally be called once per line and `data` will be terminated by a newline. For commands with
|
381
|
+
larger amounts of output, `data` is delivered as it arrives from the underlying network stack, which depends on
|
382
|
+
network conditions, buffer sizes, etc. In this case, you may need to implement a more complex `interaction_handler`
|
383
|
+
to concatenate `data` from multiple calls to `on_data` before matching the required output.
|
384
|
+
|
385
|
+
When using the `Local` backend, `on_data` is always called once per line.
|
386
|
+
|
387
|
+
#### The `channel` parameter
|
388
|
+
|
389
|
+
When using the `Netssh` backend, the `channel` parameter of `on_data(command, stream_name, data, channel)` is a
|
390
|
+
[Net::SSH Channel](http://net-ssh.github.io/ssh/v2/api/classes/Net/SSH/Connection/Channel.html).
|
391
|
+
When using the `Local` backend, it is a [ruby IO](http://ruby-doc.org/core/IO.html) object.
|
392
|
+
If you need to support both sorts of backends with the same interaction handler,
|
393
|
+
you need to call methods on the appropriate API depending on the channel type.
|
394
|
+
One approach is to detect the presence of the API methods you need -
|
395
|
+
eg `channel.respond_to?(:send_data) # Net::SSH channel` and `channel.respond_to?(:write) # IO`.
|
396
|
+
See the `SSHKit::MappingInteractionHandler` for an example of this.
|
397
|
+
|
200
398
|
## Output Handling
|
201
399
|
|
202
400
|
![Example Output](https://raw.github.com/leehambley/sshkit/master/examples/images/example_output.png)
|
@@ -204,15 +402,63 @@ first argument before attempting to find it in the *command map*.
|
|
204
402
|
By default, the output format is set to `:pretty`:
|
205
403
|
|
206
404
|
```ruby
|
207
|
-
SSHKit.config.
|
405
|
+
SSHKit.config.use_format :pretty
|
406
|
+
```
|
407
|
+
|
408
|
+
However, if you prefer non colored text you can use the `:simpletext` formatter. If you want minimal output,
|
409
|
+
there is also a `:dot` formatter which will simply output red or green dots based on the success or failure of commands.
|
410
|
+
There is also a `:blackhole` formatter which does not output anything.
|
411
|
+
|
412
|
+
By default, formatters log to `$stdout`, but they can be constructed with any object which implements `<<`
|
413
|
+
for example any `IO` subclass, `String`, `Logger` etc:
|
414
|
+
|
415
|
+
```ruby
|
416
|
+
# Output to a String:
|
417
|
+
output = String.new
|
418
|
+
SSHKit.config.output = SSHKit::Formatter::Pretty.new(output)
|
419
|
+
# Do something with output
|
420
|
+
|
421
|
+
# Or output to a file:
|
422
|
+
SSHKit.config.output = SSHKit::Formatter::SimpleText.new(File.open('log/deploy.log', 'wb'))
|
423
|
+
```
|
424
|
+
|
425
|
+
#### Output Colors
|
426
|
+
|
427
|
+
By default, SSHKit will color the output using ANSI color escape sequences
|
428
|
+
if the output you are using is associated with a terminal device (tty).
|
429
|
+
This means that you should see colors if you are writing output to the terminal (the default),
|
430
|
+
but you shouldn't see ANSI color escape sequences if you are writing to a file.
|
431
|
+
|
432
|
+
Colors are supported for the `Pretty` and `Dot` formatters, but for historical reasons
|
433
|
+
the `SimpleText` formatter never shows colors.
|
434
|
+
|
435
|
+
If you want to force SSHKit to show colors, you can set the `SSHKIT_COLOR` environment variable:
|
436
|
+
|
437
|
+
```ruby
|
438
|
+
ENV['SSHKIT_COLOR'] = 'TRUE'
|
208
439
|
```
|
209
440
|
|
210
|
-
|
441
|
+
#### Custom formatters
|
211
442
|
|
212
|
-
|
443
|
+
Want custom output formatting? Here's what you have to do:
|
444
|
+
|
445
|
+
1. Write a new formatter class in the `SSHKit::Formatter` module. As an example, check out the default [pretty](https://github.com/capistrano/sshkit/blob/master/lib/sshkit/formatters/pretty.rb) formatter.
|
446
|
+
1. Set the output format as described above. E.g. if your new formatter is called `FooBar`:
|
447
|
+
|
448
|
+
```ruby
|
449
|
+
SSHKit.config.use_format :foobar
|
450
|
+
```
|
451
|
+
|
452
|
+
If your formatter class takes a second `options` argument in its constructor, you can pass options to it like this:
|
213
453
|
|
214
454
|
```ruby
|
215
|
-
SSHKit.config.
|
455
|
+
SSHKit.config.use_format :foobar, :my_option => "value"
|
456
|
+
```
|
457
|
+
|
458
|
+
Which will call your constructor:
|
459
|
+
|
460
|
+
```ruby
|
461
|
+
SSHKit::Formatter::FooBar.new($stdout, :my_option => "value")
|
216
462
|
```
|
217
463
|
|
218
464
|
## Output Verbosity
|
@@ -226,6 +472,19 @@ and defaults to `Logger::INFO`.
|
|
226
472
|
|
227
473
|
At present the `Logger::WARN`, `ERROR` and `FATAL` are not used.
|
228
474
|
|
475
|
+
## Deprecation warnings
|
476
|
+
|
477
|
+
Deprecation warnings are logged directly to `stderr` by default. This behaviour
|
478
|
+
can be changed by setting the `SSHKit.config.deprecation_output` option:
|
479
|
+
|
480
|
+
```ruby
|
481
|
+
# Disable deprecation warnings
|
482
|
+
SSHKit.config.deprecation_output = nil
|
483
|
+
|
484
|
+
# Log deprecation warnings to a file
|
485
|
+
SSHKit.config.deprecation_output = File.open('log/deprecation_warnings.log', 'wb')
|
486
|
+
```
|
487
|
+
|
229
488
|
## Connection Pooling
|
230
489
|
|
231
490
|
SSHKit uses a simple connection pool (enabled by default) to reduce the
|
@@ -249,6 +508,12 @@ pooling behaviour entirely by setting the idle_timeout to zero:
|
|
249
508
|
SSHKit::Backend::Netssh.pool.idle_timeout = 0 # disabled
|
250
509
|
```
|
251
510
|
|
511
|
+
## Tunneling and other related SSH themes
|
512
|
+
|
513
|
+
In order to do special gymnasitcs with SSH, tunneling, aliasing, complex options, etc with SSHKit it is possible to use [the underlying Net::SSH API](https://github.com/capistrano/sshkit/blob/master/EXAMPLES.md#setting-global-ssh-options) however in many cases it is preferred to use the system SSH configuration file at [`~/.ssh/config`](http://man.cx/ssh_config). This allows you to have personal configuration tied to your machine that does not have to be committed with the repository. If this is not suitable (everyone on the team needs a proxy command, or some special aliasing) a file in the same format can be placed in the project directory at `~/yourproject/.ssh/config`, this will be merged with the system settings in `~/.ssh/config`, and with any configuration specified in [`SSHKit::Backend::Netssh.config.ssh_options`](https://github.com/capistrano/sshkit/blob/master/lib/sshkit/backends/netssh.rb#L133).
|
514
|
+
|
515
|
+
These system level files are the preferred way of setting up tunneling and proxies because the system implementations of these things are faster and better than the Ruby implementations you would get if you were to configure them through Net::SSH. In cases where it's not possible (Windows?), it should be possible to make use of the Net::SSH APIs to setup tunnels and proxy commands before deferring control to Capistrano/SSHKit..
|
516
|
+
|
252
517
|
## SSHKit Related Blog Posts
|
253
518
|
|
254
519
|
[SSHKit Gem Basics](http://www.rubyplus.com/articles/591)
|
data/RELEASING.md
CHANGED
@@ -1,10 +1,18 @@
|
|
1
1
|
# Releasing
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
*
|
6
|
-
*
|
7
|
-
*
|
8
|
-
*
|
9
|
-
|
10
|
-
|
3
|
+
## Prerequisites
|
4
|
+
|
5
|
+
* You must have commit rights to the SSHKit repository.
|
6
|
+
* You must have push rights for the sshkit gem on rubygems.org.
|
7
|
+
* You must be using Ruby >= 2.1.0.
|
8
|
+
* Your `~/.netrc` must be configured with your GitHub credentials, [as explained here](https://github.com/mattbrictson/chandler#2-configure-netrc).
|
9
|
+
|
10
|
+
## How to release
|
11
|
+
|
12
|
+
1. Run `bundle install` to make sure that you have all the gems necessary for testing and releasing.
|
13
|
+
2. **Ensure the tests are passing by running `rake test`.** If functional tests fail, ensure you have [Vagrant](https://www.vagrantup.com) installed and have started it with `vagrant up`.
|
14
|
+
3. Determine which would be the correct next version number according to [semver](http://semver.org/).
|
15
|
+
4. Update the version in `./lib/sshkit/version.rb`.
|
16
|
+
5. Update the `CHANGELOG`.
|
17
|
+
6. Commit the changelog and version in a single commit, the message should be "Preparing vX.Y.Z"
|
18
|
+
7. Run `rake release`; this will tag, push to GitHub, publish to rubygems.org, and upload the latest changelog entry to the [GitHub releases page](https://github.com/capistrano/sshkit/releases).
|
data/Rakefile
CHANGED
@@ -25,3 +25,11 @@ end
|
|
25
25
|
Rake::Task["test:functional"].enhance do
|
26
26
|
warn "Remember there are still some VMs running, kill them with `vagrant halt` if you are finished using them."
|
27
27
|
end
|
28
|
+
|
29
|
+
task "release:rubygem_push" do
|
30
|
+
# Delay loading Chandler until this point, since it requires Ruby >= 2.1,
|
31
|
+
# which may not be available in all environments (e.g. Travis).
|
32
|
+
# We assume that the person doing `rake release` has Ruby >= 2.1.
|
33
|
+
require "chandler/tasks"
|
34
|
+
Rake.application.invoke_task("chandler:push")
|
35
|
+
end
|
data/lib/sshkit.rb
CHANGED
@@ -6,15 +6,6 @@ module SSHKit
|
|
6
6
|
|
7
7
|
attr_accessor :config
|
8
8
|
|
9
|
-
def capture_output(io, &block)
|
10
|
-
original_io = config.output
|
11
|
-
config.output = io
|
12
|
-
config.output.extend(SSHKit::Utils::CaptureOutputMethods)
|
13
|
-
yield
|
14
|
-
ensure
|
15
|
-
config.output = original_io
|
16
|
-
end
|
17
|
-
|
18
9
|
def configure
|
19
10
|
@@config ||= Configuration.new
|
20
11
|
yield config
|
data/lib/sshkit/all.rb
CHANGED
@@ -9,15 +9,19 @@ require_relative 'command_map'
|
|
9
9
|
require_relative 'configuration'
|
10
10
|
require_relative 'coordinator'
|
11
11
|
|
12
|
+
require_relative 'deprecation_logger'
|
13
|
+
|
12
14
|
require_relative 'exception'
|
13
15
|
|
14
16
|
require_relative 'logger'
|
15
17
|
require_relative 'log_message'
|
16
18
|
|
19
|
+
require_relative 'mapping_interaction_handler'
|
20
|
+
|
17
21
|
require_relative 'formatters/abstract'
|
18
22
|
require_relative 'formatters/black_hole'
|
19
|
-
require_relative 'formatters/simple_text'
|
20
23
|
require_relative 'formatters/pretty'
|
24
|
+
require_relative 'formatters/simple_text'
|
21
25
|
require_relative 'formatters/dot'
|
22
26
|
|
23
27
|
require_relative 'runners/abstract'
|
@@ -31,6 +35,4 @@ require_relative 'backends/connection_pool'
|
|
31
35
|
require_relative 'backends/printer'
|
32
36
|
require_relative 'backends/netssh'
|
33
37
|
require_relative 'backends/local'
|
34
|
-
require_relative 'backends/skipper'
|
35
|
-
|
36
|
-
require_relative 'utils/capture_output_methods'
|
38
|
+
require_relative 'backends/skipper'
|
@@ -6,10 +6,13 @@ module SSHKit
|
|
6
6
|
|
7
7
|
class Abstract
|
8
8
|
|
9
|
+
extend Forwardable
|
10
|
+
def_delegators :output, :log, :fatal, :error, :warn, :info, :debug
|
11
|
+
|
9
12
|
attr_reader :host
|
10
13
|
|
11
14
|
def run
|
12
|
-
|
15
|
+
instance_exec(@host, &@block)
|
13
16
|
end
|
14
17
|
|
15
18
|
def initialize(host, &block)
|
@@ -18,55 +21,39 @@ module SSHKit
|
|
18
21
|
@block = block
|
19
22
|
end
|
20
23
|
|
21
|
-
def log(messages)
|
22
|
-
info(messages)
|
23
|
-
end
|
24
|
-
|
25
|
-
def fatal(messages)
|
26
|
-
output << LogMessage.new(Logger::FATAL, messages)
|
27
|
-
end
|
28
|
-
|
29
|
-
def error(messages)
|
30
|
-
output << LogMessage.new(Logger::ERROR, messages)
|
31
|
-
end
|
32
|
-
|
33
|
-
def warn(messages)
|
34
|
-
output << LogMessage.new(Logger::WARN, messages)
|
35
|
-
end
|
36
|
-
|
37
|
-
def info(messages)
|
38
|
-
output << LogMessage.new(Logger::INFO, messages)
|
39
|
-
end
|
40
|
-
|
41
|
-
def debug(messages)
|
42
|
-
output << LogMessage.new(Logger::DEBUG, messages)
|
43
|
-
end
|
44
|
-
|
45
|
-
def trace(messages)
|
46
|
-
output << LogMessage.new(Logger::TRACE, messages)
|
47
|
-
end
|
48
|
-
|
49
24
|
def make(commands=[])
|
50
|
-
|
25
|
+
execute :make, commands
|
51
26
|
end
|
52
27
|
|
53
28
|
def rake(commands=[])
|
54
|
-
|
29
|
+
execute :rake, commands
|
55
30
|
end
|
56
31
|
|
57
|
-
def test(
|
58
|
-
|
32
|
+
def test(*args)
|
33
|
+
options = args.extract_options!.merge(raise_on_non_zero_exit: false, verbosity: Logger::DEBUG)
|
34
|
+
create_command_and_execute(args, options).success?
|
59
35
|
end
|
60
36
|
|
61
|
-
def
|
62
|
-
|
37
|
+
def capture(*args)
|
38
|
+
options = { verbosity: Logger::DEBUG, strip: true }.merge(args.extract_options!)
|
39
|
+
result = create_command_and_execute(args, options).full_stdout
|
40
|
+
options[:strip] ? result.strip : result
|
63
41
|
end
|
64
42
|
|
65
|
-
def
|
66
|
-
|
43
|
+
def background(*args)
|
44
|
+
SSHKit.config.deprecation_logger.log(
|
45
|
+
'The background method is deprecated. Blame badly behaved pseudo-daemons!'
|
46
|
+
)
|
47
|
+
options = args.extract_options!.merge(run_in_background: true)
|
48
|
+
create_command_and_execute(args, options).success?
|
49
|
+
end
|
50
|
+
|
51
|
+
def execute(*args)
|
52
|
+
options = args.extract_options!
|
53
|
+
create_command_and_execute(args, options).success?
|
67
54
|
end
|
68
55
|
|
69
|
-
def within(directory, &
|
56
|
+
def within(directory, &_block)
|
70
57
|
(@pwd ||= []).push directory.to_s
|
71
58
|
execute <<-EOTEST, verbosity: Logger::DEBUG
|
72
59
|
if test ! -d #{File.join(@pwd)}
|
@@ -79,7 +66,7 @@ module SSHKit
|
|
79
66
|
@pwd.pop
|
80
67
|
end
|
81
68
|
|
82
|
-
def with(environment, &
|
69
|
+
def with(environment, &_block)
|
83
70
|
@_env = (@env ||= {})
|
84
71
|
@env = @_env.merge environment
|
85
72
|
yield
|
@@ -88,7 +75,7 @@ module SSHKit
|
|
88
75
|
remove_instance_variable(:@_env)
|
89
76
|
end
|
90
77
|
|
91
|
-
def as(who, &
|
78
|
+
def as(who, &_block)
|
92
79
|
if who.is_a? Hash
|
93
80
|
@user = who[:user] || who["user"]
|
94
81
|
@group = who[:group] || who["group"]
|
@@ -118,10 +105,23 @@ module SSHKit
|
|
118
105
|
end
|
119
106
|
end
|
120
107
|
|
108
|
+
# Backends which extend the Abstract backend should implement the following methods:
|
109
|
+
def upload!(_local, _remote, _options = {}) raise MethodUnavailableError end
|
110
|
+
def download!(_remote, _local=nil, _options = {}) raise MethodUnavailableError end
|
111
|
+
def execute_command(_cmd) raise MethodUnavailableError end
|
112
|
+
private :execute_command # Can inline after Ruby 2.1
|
113
|
+
|
121
114
|
private
|
122
115
|
|
123
|
-
def
|
124
|
-
|
116
|
+
def output
|
117
|
+
SSHKit.config.output
|
118
|
+
end
|
119
|
+
|
120
|
+
def create_command_and_execute(args, options)
|
121
|
+
command(args, options).tap { |cmd| execute_command(cmd) }
|
122
|
+
end
|
123
|
+
|
124
|
+
def command(args, options)
|
125
125
|
SSHKit::Command.new(*[*args, options.merge({in: @pwd.nil? ? nil : File.join(@pwd), env: @env, host: @host, user: @user, group: @group})])
|
126
126
|
end
|
127
127
|
|