skipper-ssh 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +98 -0
- data/Rakefile +2 -0
- data/bin/skipper +8 -0
- data/lib/skipper/banner.rb +34 -0
- data/lib/skipper/cli.rb +39 -0
- data/lib/skipper/file.rb +28 -0
- data/lib/skipper/repl.rb +71 -0
- data/lib/skipper/runner.rb +64 -0
- data/lib/skipper/servers/basic.rb +17 -0
- data/lib/skipper/servers/ec2.rb +70 -0
- data/lib/skipper/version.rb +3 -0
- data/lib/skipper-ssh.rb +1 -0
- data/lib/skipper.rb +13 -0
- data/skipper-ssh.gemspec +30 -0
- data/spec/lib/skipper/runner_spec.rb +84 -0
- data/spec/spec_helper.rb +26 -0
- metadata +194 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 925d869492d476537b3ce3bbaebbd43458e19c41
|
4
|
+
data.tar.gz: d7f386189a0403db3297b026e771dc57f05ca64a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b2de854783362763f24cb723a5538192796d9bd03f1d23a288f33116bf3db0b8c85e5202e84fc982ff9f1bfcb617d8e76292b5507b0c85cfce9f5e74bd932ab3
|
7
|
+
data.tar.gz: 3fd05b6fed7c0cce260a228f607e262e6760873e5baa71d3c1e7e26d48050e28308566ded55189abb7cb6b45ce383aa317845e91c2c38fd57e507ad6fc2f3f16
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 James Brennan
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
# Skipper
|
2
|
+
|
3
|
+
A captain for your fleet.
|
4
|
+
|
5
|
+
## Basic Usage
|
6
|
+
|
7
|
+
### REPL Commands
|
8
|
+
|
9
|
+
```
|
10
|
+
> help
|
11
|
+
Skipper Help
|
12
|
+
|
13
|
+
help - this message
|
14
|
+
servers - list the servers that commands will be executed on
|
15
|
+
exit - bye, bye
|
16
|
+
```
|
17
|
+
|
18
|
+
### Command Line Options
|
19
|
+
|
20
|
+
```
|
21
|
+
Usage:
|
22
|
+
skipper
|
23
|
+
|
24
|
+
Options:
|
25
|
+
[--servers=one two three]
|
26
|
+
[--region=REGION]
|
27
|
+
# Default: us-east-1
|
28
|
+
[--tags=key:value]
|
29
|
+
[--auto-scaling-groups=one two three]
|
30
|
+
[--auto-scaling-roles=one two three]
|
31
|
+
[--identity-file=IDENTITY_FILE]
|
32
|
+
[--forward-agent=FORWARD_AGENT]
|
33
|
+
[--user=USER]
|
34
|
+
# Default: `whoami`
|
35
|
+
[--run-in=RUN_IN]
|
36
|
+
[--wait=N]
|
37
|
+
[--limit=N]
|
38
|
+
[--output], [--no-output]
|
39
|
+
# Default: true
|
40
|
+
[--file=FILE]
|
41
|
+
|
42
|
+
Run a command on remote servers
|
43
|
+
```
|
44
|
+
|
45
|
+
```
|
46
|
+
# Get a SSH for multiple servers
|
47
|
+
skipper --servers server-1.example.com server-2.example.com
|
48
|
+
|
49
|
+
# Use a SSH identity file other than your default
|
50
|
+
skipper --identity-file path/to/id_rsa.pub
|
51
|
+
|
52
|
+
# Enable SSH forward agent
|
53
|
+
skipper --forward-agent
|
54
|
+
|
55
|
+
# Set the SSH username (defaults to `whoami`)
|
56
|
+
skipper --user myusername
|
57
|
+
|
58
|
+
# Limit output (do no display the output of commands)
|
59
|
+
skipper --no-output
|
60
|
+
|
61
|
+
# Run commands from STDIN
|
62
|
+
echo 'pwd' | skipper
|
63
|
+
|
64
|
+
# Run commands from a file
|
65
|
+
skipper --file myscript.sh
|
66
|
+
```
|
67
|
+
|
68
|
+
#### Running Commands in Sequence or Parallel
|
69
|
+
|
70
|
+
By default, commands will run on all servers in parallel.
|
71
|
+
|
72
|
+
```
|
73
|
+
# Only run on two instances at one
|
74
|
+
skipper --limit 2
|
75
|
+
|
76
|
+
# Run on only one server at a time
|
77
|
+
skipper --run-in sequence # or
|
78
|
+
skipper --limit 1
|
79
|
+
|
80
|
+
# Wait 5 seconds between server groups (default group size is 1)
|
81
|
+
skipper --wait 5
|
82
|
+
```
|
83
|
+
|
84
|
+
#### Search AWS EC2 Instances
|
85
|
+
|
86
|
+
```
|
87
|
+
# Skipper can only run in the context of one region (default is `us-east-1`)
|
88
|
+
skipper --region us-west-1
|
89
|
+
|
90
|
+
# By tag(s)
|
91
|
+
skipper --tags name:value
|
92
|
+
|
93
|
+
# By auto scaling group name
|
94
|
+
skipper --auto-scaling-group groupname
|
95
|
+
|
96
|
+
# By auto scaling role
|
97
|
+
skipper --auto-scaling-role rolename
|
98
|
+
```
|
data/Rakefile
ADDED
data/bin/skipper
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
module Skipper
|
2
|
+
module Banner
|
3
|
+
|
4
|
+
def self.print
|
5
|
+
puts %q[
|
6
|
+
SSSSSSSSSSSSSSS kkkkkkkk iiii
|
7
|
+
SS:::::::::::::::Sk::::::k i::::i
|
8
|
+
S:::::SSSSSS::::::Sk::::::k iiii
|
9
|
+
S:::::S SSSSSSSk::::::k
|
10
|
+
S:::::S k:::::k kkkkkkkiiiiiiippppp ppppppppp ppppp ppppppppp eeeeeeeeeeee rrrrr rrrrrrrrr
|
11
|
+
S:::::S k:::::k k:::::k i:::::ip::::ppp:::::::::p p::::ppp:::::::::p ee::::::::::::ee r::::rrr:::::::::r
|
12
|
+
S::::SSSS k:::::k k:::::k i::::ip:::::::::::::::::p p:::::::::::::::::p e::::::eeeee:::::eer:::::::::::::::::r
|
13
|
+
SS::::::SSSSS k:::::k k:::::k i::::ipp::::::ppppp::::::ppp::::::ppppp::::::pe::::::e e:::::err::::::rrrrr::::::r
|
14
|
+
SSS::::::::SS k::::::k:::::k i::::i p:::::p p:::::p p:::::p p:::::pe:::::::eeeee::::::e r:::::r r:::::r
|
15
|
+
SSSSSS::::S k:::::::::::k i::::i p:::::p p:::::p p:::::p p:::::pe:::::::::::::::::e r:::::r rrrrrrr
|
16
|
+
S:::::S k:::::::::::k i::::i p:::::p p:::::p p:::::p p:::::pe::::::eeeeeeeeeee r:::::r
|
17
|
+
S:::::S k::::::k:::::k i::::i p:::::p p::::::p p:::::p p::::::pe:::::::e r:::::r
|
18
|
+
SSSSSSS S:::::Sk::::::k k:::::k i::::::ip:::::ppppp:::::::p p:::::ppppp:::::::pe::::::::e r:::::r
|
19
|
+
S::::::SSSSSS:::::Sk::::::k k:::::k i::::::ip::::::::::::::::p p::::::::::::::::p e::::::::eeeeeeee r:::::r
|
20
|
+
S:::::::::::::::SS k::::::k k:::::k i::::::ip::::::::::::::pp p::::::::::::::pp ee:::::::::::::e r:::::r
|
21
|
+
SSSSSSSSSSSSSSS kkkkkkkk kkkkkkkiiiiiiiip::::::pppppppp p::::::pppppppp eeeeeeeeeeeeee rrrrrrr
|
22
|
+
p:::::p p:::::p
|
23
|
+
p:::::p p:::::p
|
24
|
+
p:::::::p p:::::::p
|
25
|
+
p:::::::p p:::::::p
|
26
|
+
p:::::::p p:::::::p
|
27
|
+
ppppppppp ppppppppp
|
28
|
+
|
29
|
+
]
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
data/lib/skipper/cli.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
module Skipper
|
2
|
+
class Cli < Thor
|
3
|
+
include Thor::Actions
|
4
|
+
|
5
|
+
namespace :default
|
6
|
+
default_task :ssh
|
7
|
+
|
8
|
+
desc 'ssh', 'Run a command on remote servers'
|
9
|
+
method_option :servers, type: :array
|
10
|
+
|
11
|
+
method_option :region, type: :string, default: 'us-east-1'
|
12
|
+
method_option :tags, type: :hash, default: {}
|
13
|
+
method_option :auto_scaling_groups, type: :array, default: []
|
14
|
+
method_option :auto_scaling_roles, type: :array, default: []
|
15
|
+
|
16
|
+
method_option :identity_file, type: :string
|
17
|
+
method_option :forward_agent, tyep: :boolean, default: false
|
18
|
+
method_option :user, type: :string, default: `whoami`.strip
|
19
|
+
|
20
|
+
method_option :run_in, type: :string
|
21
|
+
method_option :wait, type: :numeric
|
22
|
+
method_option :limit, type: :numeric
|
23
|
+
method_option :output, type: :boolean, default: true
|
24
|
+
|
25
|
+
method_option :file, type: :string
|
26
|
+
def ssh
|
27
|
+
Skipper::Banner.print
|
28
|
+
|
29
|
+
if Skipper::File.stdin_has_data?
|
30
|
+
Skipper::File.new(options, self).run($stdin)
|
31
|
+
elsif options.file?
|
32
|
+
Skipper::File.new(options, self).run(::File.new(options.file))
|
33
|
+
else
|
34
|
+
Skipper::Repl.new(options, self).run
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
data/lib/skipper/file.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'fcntl'
|
2
|
+
|
3
|
+
module Skipper
|
4
|
+
class File
|
5
|
+
attr_reader :options, :cli
|
6
|
+
|
7
|
+
def initialize(options, cli)
|
8
|
+
@options = options
|
9
|
+
@cli = cli
|
10
|
+
|
11
|
+
if options.servers?
|
12
|
+
servers = Skipper::Servers::Basic.new(options)
|
13
|
+
else
|
14
|
+
servers = Skipper::Servers::EC2.new(options)
|
15
|
+
end
|
16
|
+
|
17
|
+
@runner = Skipper::Runner.new(servers, options, cli)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.stdin_has_data?
|
21
|
+
$stdin.fcntl(Fcntl::F_GETFL, 0) == 0
|
22
|
+
end
|
23
|
+
|
24
|
+
def run(file)
|
25
|
+
@runner.run(file.read)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/skipper/repl.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'readline'
|
2
|
+
|
3
|
+
module Skipper
|
4
|
+
class Repl
|
5
|
+
attr_accessor :options, :runner, :cli, :servers
|
6
|
+
|
7
|
+
def initialize(options, cli)
|
8
|
+
@options = options
|
9
|
+
@cli = cli
|
10
|
+
|
11
|
+
if options.servers?
|
12
|
+
@servers = Skipper::Servers::Basic.new(options)
|
13
|
+
else
|
14
|
+
@servers = Skipper::Servers::EC2.new(options)
|
15
|
+
end
|
16
|
+
|
17
|
+
@runner = Skipper::Runner.new(@servers, options, cli)
|
18
|
+
end
|
19
|
+
|
20
|
+
def run
|
21
|
+
loop do
|
22
|
+
begin
|
23
|
+
repl
|
24
|
+
rescue Interrupt
|
25
|
+
puts ''
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def repl
|
33
|
+
while command = Readline.readline("> ", true)
|
34
|
+
handle_command(command.chomp)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def handle_command(command)
|
39
|
+
case command
|
40
|
+
when 'h'
|
41
|
+
when 'help'
|
42
|
+
print_help
|
43
|
+
when 'servers'
|
44
|
+
print_servers
|
45
|
+
when 'quit'
|
46
|
+
when 'exit'
|
47
|
+
quit
|
48
|
+
else
|
49
|
+
runner.run(command)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def print_help
|
54
|
+
puts %q[Skipper Help
|
55
|
+
|
56
|
+
help - this message
|
57
|
+
servers - list the servers that commands will be executed on
|
58
|
+
exit - bye, bye]
|
59
|
+
end
|
60
|
+
|
61
|
+
def print_servers
|
62
|
+
puts servers.to_s
|
63
|
+
end
|
64
|
+
|
65
|
+
def quit
|
66
|
+
puts 'See ya!'
|
67
|
+
exit 0
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'sshkit'
|
2
|
+
require 'sshkit/dsl'
|
3
|
+
|
4
|
+
module Skipper
|
5
|
+
class Runner
|
6
|
+
|
7
|
+
def initialize(servers, options = {}, cli)
|
8
|
+
@servers = servers
|
9
|
+
@options = options
|
10
|
+
@cli = cli
|
11
|
+
|
12
|
+
SSHKit::Backend::Netssh.configure do |ssh|
|
13
|
+
ssh.ssh_options = ssh_options
|
14
|
+
end
|
15
|
+
|
16
|
+
SSHKit.config.output_verbosity = Logger::DEBUG if options.output?
|
17
|
+
end
|
18
|
+
|
19
|
+
def run(command)
|
20
|
+
on servers.hosts, on_options do
|
21
|
+
execute command
|
22
|
+
end
|
23
|
+
rescue SSHKit::Runner::ExecuteError => e
|
24
|
+
cli.say e, :red
|
25
|
+
rescue Interrupt
|
26
|
+
puts ''
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
attr_reader :servers, :options, :cli
|
32
|
+
|
33
|
+
def run_in
|
34
|
+
return options.run_in.to_sym if options.run_in?
|
35
|
+
|
36
|
+
if options.limit?
|
37
|
+
:groups
|
38
|
+
elsif options.wait?
|
39
|
+
:sequence
|
40
|
+
else
|
41
|
+
:parallel
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def on_options
|
46
|
+
opts = { in: run_in }
|
47
|
+
|
48
|
+
[:limit, :wait].each do |key|
|
49
|
+
opts[key] = options[key].to_i if options.send("#{key}?")
|
50
|
+
end
|
51
|
+
|
52
|
+
opts
|
53
|
+
end
|
54
|
+
|
55
|
+
def ssh_options
|
56
|
+
opts = {}
|
57
|
+
opts[:keys] = [options.identity_file] if options.identiy_file?
|
58
|
+
opts[:user] = options.user
|
59
|
+
opts[:forward_agent] = options.foward_agent
|
60
|
+
opts
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'aws'
|
2
|
+
require 'table_print'
|
3
|
+
|
4
|
+
module Skipper
|
5
|
+
module Servers
|
6
|
+
class EC2
|
7
|
+
attr_reader :options
|
8
|
+
|
9
|
+
def initialize(options)
|
10
|
+
@options = options
|
11
|
+
end
|
12
|
+
|
13
|
+
def instances
|
14
|
+
@instances ||= find_instances
|
15
|
+
end
|
16
|
+
|
17
|
+
def hosts
|
18
|
+
@hosts ||= instances.map { |instance| instance.ip_address }
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
@to_s ||= details_table(instances)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def ec2
|
28
|
+
@ec2 ||= AWS::EC2.new(region: options[:region])
|
29
|
+
end
|
30
|
+
|
31
|
+
def find_instances
|
32
|
+
filtered = filter_tags(ec2.instances)
|
33
|
+
filtered = filter_auto_scaling_groups(filtered)
|
34
|
+
filter_auto_scaling_roles(filtered)
|
35
|
+
end
|
36
|
+
|
37
|
+
def filter_tags(filtered)
|
38
|
+
options.tags.each do |tag, value|
|
39
|
+
filtered = filtered.filter("tag:#{tag}", value.split(','))
|
40
|
+
end
|
41
|
+
filtered
|
42
|
+
end
|
43
|
+
|
44
|
+
def filter_auto_scaling_groups(filtered)
|
45
|
+
options.auto_scaling_groups.each do |value|
|
46
|
+
filtered = filtered.filter('tag:aws:autoscaling:groupName', value)
|
47
|
+
end
|
48
|
+
filtered
|
49
|
+
end
|
50
|
+
|
51
|
+
def filter_auto_scaling_roles(filtered)
|
52
|
+
options.auto_scaling_roles.each do |value|
|
53
|
+
filtered = filtered.filter('tag:aws:autoscaling:role', value)
|
54
|
+
end
|
55
|
+
filtered
|
56
|
+
end
|
57
|
+
|
58
|
+
def details_table(list)
|
59
|
+
table = StringIO.new
|
60
|
+
original_stdout, $stdout = $stdout, table
|
61
|
+
|
62
|
+
tp(list, :id, :ip_address)
|
63
|
+
|
64
|
+
$stdout = original_stdout
|
65
|
+
table.read
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/lib/skipper-ssh.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'skipper'
|
data/lib/skipper.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'thor/runner'
|
3
|
+
require 'skipper/version'
|
4
|
+
require 'skipper/banner'
|
5
|
+
require 'skipper/runner'
|
6
|
+
require 'skipper/servers/basic'
|
7
|
+
require 'skipper/servers/ec2'
|
8
|
+
require 'skipper/cli'
|
9
|
+
require 'skipper/repl'
|
10
|
+
require 'skipper/file'
|
11
|
+
|
12
|
+
module Skipper
|
13
|
+
end
|
data/skipper-ssh.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'skipper/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'skipper-ssh'
|
7
|
+
spec.version = Skipper::VERSION
|
8
|
+
spec.authors = ['James Brennan']
|
9
|
+
spec.email = ['james@jamesbrennan.ca']
|
10
|
+
spec.summary = 'A captain for your fleet.'
|
11
|
+
spec.description = 'An interactive SSH shell for multiple remote hosts, with EC2 search functionality.'
|
12
|
+
spec.homepage = 'https://github.com/jpb/skipper-ssh'
|
13
|
+
spec.license = 'MIT'
|
14
|
+
|
15
|
+
spec.files = `git ls-files`.split("\n")
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ['lib']
|
19
|
+
|
20
|
+
spec.add_dependency 'sshkit', '1.5.1'
|
21
|
+
spec.add_dependency 'thor', '0.19.1'
|
22
|
+
spec.add_dependency 'table_print', '1.5.2'
|
23
|
+
|
24
|
+
spec.add_development_dependency 'bundler', '~> 1.6'
|
25
|
+
spec.add_development_dependency 'rake', '~> 10.3.2'
|
26
|
+
spec.add_development_dependency 'rspec', '~> 3.0.0'
|
27
|
+
spec.add_development_dependency 'vcr', '~> 2.9.2'
|
28
|
+
spec.add_development_dependency 'simplecov', '~> 0.8.2'
|
29
|
+
spec.add_development_dependency 'webmock', '~> 1.18.0'
|
30
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'skipper/runner'
|
3
|
+
require 'thor/core_ext/hash_with_indifferent_access'
|
4
|
+
|
5
|
+
describe Skipper::Runner do
|
6
|
+
|
7
|
+
describe '#run' do
|
8
|
+
let(:servers) { double(hosts: ['example.com']) }
|
9
|
+
let(:params) { {} }
|
10
|
+
let(:options) { Thor::CoreExt::HashWithIndifferentAccess.new(params) }
|
11
|
+
let(:cli) { double(say: nil, execute: nil) }
|
12
|
+
let(:command) { 'echo cats' }
|
13
|
+
let(:runner) { Skipper::Runner.new(servers, options, cli) }
|
14
|
+
subject { runner }
|
15
|
+
|
16
|
+
before do
|
17
|
+
runner.stub(:execute)
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'options' do
|
21
|
+
before do
|
22
|
+
runner.stub(:on).and_yield
|
23
|
+
runner.run(command)
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'no params' do
|
27
|
+
it { should have_received(:on).with(['example.com'], in: :parallel) }
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'with --limit' do
|
31
|
+
let(:params) { { limit: 5 } }
|
32
|
+
it { should have_received(:on).with(['example.com'], in: :groups, limit: 5) }
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'with --limit and --wait' do
|
36
|
+
let(:params) { { limit: 5, wait: 10 } }
|
37
|
+
it { should have_received(:on).with(['example.com'], in: :groups, limit: 5, wait: 10) }
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'with --wait' do
|
41
|
+
let(:params) { { wait: 10 } }
|
42
|
+
it { should have_received(:on).with(['example.com'], in: :sequence, wait: 10) }
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'with --run-in' do
|
46
|
+
let(:params) { { run_in: 'cats' } }
|
47
|
+
it { should have_received(:on).with(['example.com'], in: :cats) }
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'with all options' do
|
51
|
+
let(:params) { { run_in: 'cats', limit: 5, wait: 10 } }
|
52
|
+
it { should have_received(:on).with(['example.com'], in: :cats, limit: 5, wait: 10) }
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'when Interrupt is encountered' do
|
58
|
+
before do
|
59
|
+
runner.stub(:on).and_raise(Interrupt.new)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'does not raise' do
|
63
|
+
expect { runner.run(command) }.not_to raise_error
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'when a SSH error occurs' do
|
68
|
+
before do
|
69
|
+
runner.stub(:on).and_raise(SSHKit::Runner::ExecuteError.new(double(backtrace: 'cats')))
|
70
|
+
runner.run(command)
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'does not raise' do
|
74
|
+
expect { runner.run(command) }.not_to raise_error
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'should print the error' do
|
78
|
+
cli.should have_received(:say).with(instance_of(SSHKit::Runner::ExecuteError), :red)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'vcr'
|
2
|
+
require 'simplecov'
|
3
|
+
|
4
|
+
SimpleCov.start
|
5
|
+
|
6
|
+
VCR.configure do |c|
|
7
|
+
c.configure_rspec_metadata!
|
8
|
+
c.hook_into :webmock
|
9
|
+
c.allow_http_connections_when_no_cassette = false
|
10
|
+
c.default_cassette_options = { record: :once }
|
11
|
+
c.cassette_library_dir = 'spec/support/vcr_cassettes'
|
12
|
+
end
|
13
|
+
|
14
|
+
RSpec.configure do |config|
|
15
|
+
config.run_all_when_everything_filtered = true
|
16
|
+
config.filter_run :focus
|
17
|
+
config.order = 'random'
|
18
|
+
|
19
|
+
config.mock_with :rspec do |c|
|
20
|
+
c.syntax = [:should, :expect]
|
21
|
+
end
|
22
|
+
|
23
|
+
config.expect_with :rspec do |c|
|
24
|
+
c.syntax = [:should, :expect]
|
25
|
+
end
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,194 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: skipper-ssh
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- James Brennan
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-07-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: sshkit
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.5.1
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.5.1
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: thor
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.19.1
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.19.1
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: table_print
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 1.5.2
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.5.2
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bundler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.6'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.6'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 10.3.2
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 10.3.2
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 3.0.0
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ~>
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 3.0.0
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: vcr
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ~>
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 2.9.2
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ~>
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 2.9.2
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: simplecov
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 0.8.2
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ~>
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 0.8.2
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: webmock
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ~>
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: 1.18.0
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ~>
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: 1.18.0
|
139
|
+
description: An interactive SSH shell for multiple remote hosts, with EC2 search functionality.
|
140
|
+
email:
|
141
|
+
- james@jamesbrennan.ca
|
142
|
+
executables:
|
143
|
+
- skipper
|
144
|
+
extensions: []
|
145
|
+
extra_rdoc_files: []
|
146
|
+
files:
|
147
|
+
- .gitignore
|
148
|
+
- .rspec
|
149
|
+
- Gemfile
|
150
|
+
- LICENSE.txt
|
151
|
+
- README.md
|
152
|
+
- Rakefile
|
153
|
+
- bin/skipper
|
154
|
+
- lib/skipper-ssh.rb
|
155
|
+
- lib/skipper.rb
|
156
|
+
- lib/skipper/banner.rb
|
157
|
+
- lib/skipper/cli.rb
|
158
|
+
- lib/skipper/file.rb
|
159
|
+
- lib/skipper/repl.rb
|
160
|
+
- lib/skipper/runner.rb
|
161
|
+
- lib/skipper/servers/basic.rb
|
162
|
+
- lib/skipper/servers/ec2.rb
|
163
|
+
- lib/skipper/version.rb
|
164
|
+
- skipper-ssh.gemspec
|
165
|
+
- spec/lib/skipper/runner_spec.rb
|
166
|
+
- spec/spec_helper.rb
|
167
|
+
homepage: https://github.com/jpb/skipper-ssh
|
168
|
+
licenses:
|
169
|
+
- MIT
|
170
|
+
metadata: {}
|
171
|
+
post_install_message:
|
172
|
+
rdoc_options: []
|
173
|
+
require_paths:
|
174
|
+
- lib
|
175
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
176
|
+
requirements:
|
177
|
+
- - '>='
|
178
|
+
- !ruby/object:Gem::Version
|
179
|
+
version: '0'
|
180
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
181
|
+
requirements:
|
182
|
+
- - '>='
|
183
|
+
- !ruby/object:Gem::Version
|
184
|
+
version: '0'
|
185
|
+
requirements: []
|
186
|
+
rubyforge_project:
|
187
|
+
rubygems_version: 2.2.2
|
188
|
+
signing_key:
|
189
|
+
specification_version: 4
|
190
|
+
summary: A captain for your fleet.
|
191
|
+
test_files:
|
192
|
+
- spec/lib/skipper/runner_spec.rb
|
193
|
+
- spec/spec_helper.rb
|
194
|
+
has_rdoc:
|