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
@@ -6,6 +6,7 @@ module SSHKit
|
|
6
6
|
class TestConnectionPool < UnitTest
|
7
7
|
|
8
8
|
def setup
|
9
|
+
super
|
9
10
|
pool.flush_connections
|
10
11
|
end
|
11
12
|
|
@@ -14,11 +15,11 @@ module SSHKit
|
|
14
15
|
end
|
15
16
|
|
16
17
|
def connect
|
17
|
-
->(*
|
18
|
+
->(*_args) { Object.new }
|
18
19
|
end
|
19
20
|
|
20
21
|
def connect_and_close
|
21
|
-
->(*
|
22
|
+
->(*_args) { OpenStruct.new(:closed? => true) }
|
22
23
|
end
|
23
24
|
|
24
25
|
def echo_args
|
@@ -94,6 +95,18 @@ module SSHKit
|
|
94
95
|
refute_equal conn1, conn2
|
95
96
|
end
|
96
97
|
|
98
|
+
def test_expired_connection_is_closed
|
99
|
+
pool.idle_timeout = 0.1
|
100
|
+
conn1 = mock
|
101
|
+
conn1.expects(:closed?).returns(false)
|
102
|
+
conn1.expects(:close)
|
103
|
+
|
104
|
+
entry1 = pool.checkout("conn1"){|*args| conn1 }
|
105
|
+
pool.checkin entry1
|
106
|
+
sleep(pool.idle_timeout)
|
107
|
+
pool.checkout("conn2"){|*args| Object.new}
|
108
|
+
end
|
109
|
+
|
97
110
|
def test_closed_connection_is_not_reused
|
98
111
|
conn1 = pool.checkout("conn", &connect_and_close)
|
99
112
|
pool.checkin conn1
|
@@ -110,6 +123,18 @@ module SSHKit
|
|
110
123
|
refute_equal conn1, conn2
|
111
124
|
end
|
112
125
|
|
126
|
+
def test_close_connections
|
127
|
+
conn1 = mock
|
128
|
+
conn1.expects(:closed?).returns(false)
|
129
|
+
conn1.expects(:close)
|
130
|
+
entry1 = pool.checkout("conn1"){|*args| conn1 }
|
131
|
+
pool.checkin entry1
|
132
|
+
entry2 = pool.checkout("conn2", &connect)
|
133
|
+
# entry2 isn't closed if close_connections is called
|
134
|
+
|
135
|
+
pool.close_connections
|
136
|
+
end
|
137
|
+
|
113
138
|
end
|
114
139
|
end
|
115
140
|
end
|
@@ -1,73 +1,73 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
3
|
module SSHKit
|
4
|
-
|
5
4
|
module Backend
|
6
|
-
|
7
5
|
class TestPrinter < UnitTest
|
8
6
|
|
9
|
-
def
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
def backend
|
16
|
-
@backend ||= Printer
|
7
|
+
def setup
|
8
|
+
super
|
9
|
+
SSHKit.config.output = SSHKit::Formatter::Pretty.new(output)
|
10
|
+
SSHKit.config.output_verbosity = Logger::DEBUG
|
11
|
+
Command.any_instance.stubs(:uuid).returns('aaaaaa')
|
17
12
|
end
|
18
13
|
|
19
|
-
def
|
20
|
-
@
|
14
|
+
def output
|
15
|
+
@output ||= String.new
|
21
16
|
end
|
22
17
|
|
23
18
|
def printer
|
24
|
-
Printer.new(Host.new(
|
19
|
+
@printer ||= Printer.new(Host.new('example.com'))
|
25
20
|
end
|
26
21
|
|
27
|
-
def
|
28
|
-
|
22
|
+
def test_execute
|
23
|
+
printer.execute 'uname -a'
|
24
|
+
assert_output_lines(
|
25
|
+
' INFO [aaaaaa] Running /usr/bin/env uname -a on example.com',
|
26
|
+
' DEBUG [aaaaaa] Command: uname -a'
|
27
|
+
)
|
29
28
|
end
|
30
29
|
|
31
|
-
def
|
32
|
-
|
33
|
-
SSHKit.capture_output(result) do
|
34
|
-
printer.run
|
35
|
-
end
|
36
|
-
result.rewind
|
37
|
-
assert_equal "/usr/bin/env ls -l /some/directory\n", result.read
|
38
|
-
end
|
30
|
+
def test_test_method
|
31
|
+
printer.test '[ -d /some/file ]'
|
39
32
|
|
40
|
-
|
41
|
-
|
33
|
+
assert_output_lines(
|
34
|
+
' DEBUG [aaaaaa] Running /usr/bin/env [ -d /some/file ] on example.com',
|
35
|
+
' DEBUG [aaaaaa] Command: [ -d /some/file ]'
|
36
|
+
)
|
42
37
|
end
|
43
38
|
|
44
|
-
def
|
45
|
-
|
46
|
-
ssh.pty = true
|
47
|
-
ssh.connection_timeout = 30
|
48
|
-
ssh.ssh_options = {
|
49
|
-
keys: %w(/home/user/.ssh/id_rsa),
|
50
|
-
forward_agent: false,
|
51
|
-
auth_methods: %w(publickey password)
|
52
|
-
}
|
53
|
-
end
|
39
|
+
def test_capture
|
40
|
+
result = printer.capture 'ls -l'
|
54
41
|
|
55
|
-
assert_equal
|
56
|
-
assert_equal true, backend.config.pty
|
42
|
+
assert_equal '', result
|
57
43
|
|
58
|
-
|
59
|
-
|
60
|
-
|
44
|
+
assert_output_lines(
|
45
|
+
' DEBUG [aaaaaa] Running /usr/bin/env ls -l on example.com',
|
46
|
+
' DEBUG [aaaaaa] Command: ls -l'
|
47
|
+
)
|
61
48
|
end
|
62
49
|
|
63
|
-
def
|
64
|
-
|
65
|
-
|
66
|
-
|
50
|
+
def test_upload
|
51
|
+
printer.upload! '/some/file', '/remote'
|
52
|
+
assert_output_lines(
|
53
|
+
' INFO [aaaaaa] Running /usr/bin/env /some/file /remote on example.com',
|
54
|
+
' DEBUG [aaaaaa] Command: /usr/bin/env /some/file /remote'
|
55
|
+
)
|
67
56
|
end
|
68
57
|
|
69
|
-
|
58
|
+
def test_download
|
59
|
+
printer.download! 'remote/file', '/local/path'
|
60
|
+
assert_output_lines(
|
61
|
+
' INFO [aaaaaa] Running /usr/bin/env remote/file /local/path on example.com',
|
62
|
+
' DEBUG [aaaaaa] Command: /usr/bin/env remote/file /local/path'
|
63
|
+
)
|
64
|
+
end
|
70
65
|
|
71
|
-
|
66
|
+
private
|
72
67
|
|
73
|
-
|
68
|
+
def assert_output_lines(*expected_lines)
|
69
|
+
assert_equal(expected_lines, output.split("\n"))
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
module SSHKit
|
4
|
+
# Try to maintain backwards compatibility with Custom formatters defined by other people
|
5
|
+
class TestCustom < UnitTest
|
6
|
+
|
7
|
+
def setup
|
8
|
+
super
|
9
|
+
SSHKit.config.output_verbosity = Logger::DEBUG
|
10
|
+
end
|
11
|
+
|
12
|
+
def output
|
13
|
+
@output ||= String.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def custom
|
17
|
+
@custom ||= CustomFormatter.new(output)
|
18
|
+
end
|
19
|
+
|
20
|
+
{
|
21
|
+
log: 'LM 1 Test',
|
22
|
+
fatal: 'LM 4 Test',
|
23
|
+
error: 'LM 3 Test',
|
24
|
+
warn: 'LM 2 Test',
|
25
|
+
info: 'LM 1 Test',
|
26
|
+
debug: 'LM 0 Test'
|
27
|
+
}.each do |level, expected_output|
|
28
|
+
define_method("test_#{level}_logging") do
|
29
|
+
custom.send(level, 'Test')
|
30
|
+
assert_log_output expected_output
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_write_logs_commands
|
35
|
+
custom.write(Command.new(:ls))
|
36
|
+
|
37
|
+
assert_log_output 'C 1 /usr/bin/env ls'
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_double_chevron_logs_commands
|
41
|
+
custom << Command.new(:ls)
|
42
|
+
|
43
|
+
assert_log_output 'C 1 /usr/bin/env ls'
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def assert_log_output(expected_output)
|
49
|
+
assert_equal expected_output, output
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
class CustomFormatter < SSHKit::Formatter::Abstract
|
55
|
+
def write(obj)
|
56
|
+
original_output << case obj
|
57
|
+
when SSHKit::Command then "C #{obj.verbosity} #{obj}"
|
58
|
+
when SSHKit::LogMessage then "LM #{obj.verbosity} #{obj}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
alias :<< :write
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
@@ -1,65 +1,58 @@
|
|
1
1
|
require 'helper'
|
2
|
-
require 'sshkit'
|
3
2
|
|
4
3
|
module SSHKit
|
5
4
|
class TestDot < UnitTest
|
6
5
|
|
7
6
|
def setup
|
7
|
+
super
|
8
8
|
SSHKit.config.output_verbosity = Logger::DEBUG
|
9
9
|
end
|
10
10
|
|
11
11
|
def output
|
12
|
-
@
|
12
|
+
@output ||= String.new
|
13
13
|
end
|
14
14
|
|
15
15
|
def dot
|
16
|
-
@
|
16
|
+
@dot ||= SSHKit::Formatter::Dot.new(output)
|
17
17
|
end
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
%w(fatal error warn info debug).each do |level|
|
20
|
+
define_method("test_#{level}_output") do
|
21
|
+
dot.send(level, 'Test')
|
22
|
+
assert_log_output('')
|
23
|
+
end
|
23
24
|
end
|
24
25
|
|
25
|
-
def
|
26
|
-
dot
|
27
|
-
|
26
|
+
def test_log_command_start
|
27
|
+
dot.log_command_start(SSHKit::Command.new(:ls))
|
28
|
+
assert_log_output('')
|
28
29
|
end
|
29
30
|
|
30
|
-
def
|
31
|
-
dot
|
32
|
-
|
33
|
-
end
|
34
|
-
|
35
|
-
def test_logging_warn
|
36
|
-
dot << SSHKit::LogMessage.new(Logger::WARN, "Test")
|
37
|
-
assert_equal "", output.strip
|
38
|
-
end
|
39
|
-
|
40
|
-
def test_logging_info
|
41
|
-
dot << SSHKit::LogMessage.new(Logger::INFO, "Test")
|
42
|
-
assert_equal "", output.strip
|
43
|
-
end
|
44
|
-
|
45
|
-
def test_logging_debug
|
46
|
-
dot << SSHKit::LogMessage.new(Logger::DEBUG, "Test")
|
47
|
-
assert_equal "", output.strip
|
31
|
+
def test_log_command_data
|
32
|
+
dot.log_command_data(SSHKit::Command.new(:ls), :stdout, 'Some output')
|
33
|
+
assert_log_output('')
|
48
34
|
end
|
49
35
|
|
50
36
|
def test_command_success
|
37
|
+
output.stubs(:tty?).returns(true)
|
51
38
|
command = SSHKit::Command.new(:ls)
|
52
39
|
command.exit_status = 0
|
53
|
-
dot
|
54
|
-
|
40
|
+
dot.log_command_exit(command)
|
41
|
+
assert_log_output("\e[0;32;49m.\e[0m")
|
55
42
|
end
|
56
43
|
|
57
44
|
def test_command_failure
|
45
|
+
output.stubs(:tty?).returns(true)
|
58
46
|
command = SSHKit::Command.new(:ls, {raise_on_non_zero_exit: false})
|
59
47
|
command.exit_status = 1
|
60
|
-
dot
|
61
|
-
|
48
|
+
dot.log_command_exit(command)
|
49
|
+
assert_log_output("\e[0;31;49m.\e[0m")
|
62
50
|
end
|
63
51
|
|
52
|
+
private
|
53
|
+
|
54
|
+
def assert_log_output(expected_output)
|
55
|
+
assert_equal expected_output, output
|
56
|
+
end
|
64
57
|
end
|
65
58
|
end
|
@@ -1,50 +1,142 @@
|
|
1
1
|
require 'helper'
|
2
|
-
require 'sshkit'
|
3
2
|
|
4
3
|
module SSHKit
|
5
4
|
class TestPretty < UnitTest
|
6
5
|
|
7
6
|
def setup
|
7
|
+
super
|
8
8
|
SSHKit.config.output_verbosity = Logger::DEBUG
|
9
|
+
Command.any_instance.stubs(:uuid).returns('aaaaaa')
|
9
10
|
end
|
10
11
|
|
11
12
|
def output
|
12
|
-
@
|
13
|
+
@output ||= String.new
|
13
14
|
end
|
14
15
|
|
15
16
|
def pretty
|
16
|
-
@
|
17
|
+
@pretty ||= SSHKit::Formatter::Pretty.new(output)
|
17
18
|
end
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
{
|
21
|
+
log: "\e[0;34;49mINFO\e[0m Test\n",
|
22
|
+
fatal: "\e[0;31;49mFATAL\e[0m Test\n",
|
23
|
+
error: "\e[0;31;49mERROR\e[0m Test\n",
|
24
|
+
warn: "\e[0;33;49mWARN\e[0m Test\n",
|
25
|
+
info: "\e[0;34;49mINFO\e[0m Test\n",
|
26
|
+
debug: "\e[0;30;49mDEBUG\e[0m Test\n"
|
27
|
+
}.each do |level, expected_output|
|
28
|
+
define_method("test_#{level}_output_with_color") do
|
29
|
+
output.stubs(:tty?).returns(true)
|
30
|
+
pretty.send(level, 'Test')
|
31
|
+
assert_log_output(expected_output)
|
32
|
+
end
|
23
33
|
end
|
24
34
|
|
25
|
-
def
|
26
|
-
|
27
|
-
|
35
|
+
def test_command_lifecycle_logging_with_color
|
36
|
+
output.stubs(:tty?).returns(true)
|
37
|
+
simulate_command_lifecycle(pretty)
|
38
|
+
|
39
|
+
expected_log_lines = [
|
40
|
+
"\e[0;34;49mINFO\e[0m [\e[0;32;49maaaaaa\e[0m] Running \e[1;33;49m/usr/bin/env a_cmd some args\e[0m as \e[0;34;49muser\e[0m@\e[0;34;49mlocalhost\e[0m",
|
41
|
+
"\e[0;30;49mDEBUG\e[0m [\e[0;32;49maaaaaa\e[0m] Command: \e[0;34;49m/usr/bin/env a_cmd some args\e[0m",
|
42
|
+
"\e[0;30;49mDEBUG\e[0m [\e[0;32;49maaaaaa\e[0m] \e[0;32;49m\tstdout message\e[0m",
|
43
|
+
"\e[0;30;49mDEBUG\e[0m [\e[0;32;49maaaaaa\e[0m] \e[0;31;49m\tstderr message\e[0m",
|
44
|
+
"\e[0;34;49mINFO\e[0m [\e[0;32;49maaaaaa\e[0m] Finished in 1.000 seconds with exit status 0 (\e[1;32;49msuccessful\e[0m)."
|
45
|
+
]
|
46
|
+
assert_equal expected_log_lines, output.split("\n")
|
47
|
+
end
|
48
|
+
|
49
|
+
{
|
50
|
+
log: " INFO Test\n",
|
51
|
+
fatal: " FATAL Test\n",
|
52
|
+
error: " ERROR Test\n",
|
53
|
+
warn: " WARN Test\n",
|
54
|
+
info: " INFO Test\n",
|
55
|
+
debug: " DEBUG Test\n"
|
56
|
+
}.each do |level, expected_output|
|
57
|
+
define_method("test_#{level}_output_without_color") do
|
58
|
+
pretty.send(level, "Test")
|
59
|
+
assert_log_output expected_output
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_logging_message_with_leading_and_trailing_space
|
64
|
+
pretty.log(" some spaces\n\n \t")
|
65
|
+
assert_log_output " INFO some spaces\n"
|
28
66
|
end
|
29
67
|
|
30
|
-
def
|
31
|
-
pretty
|
32
|
-
|
68
|
+
def test_can_log_non_strings
|
69
|
+
pretty.log(Pathname.new('/var/log/my.log'))
|
70
|
+
assert_log_output " INFO /var/log/my.log\n"
|
33
71
|
end
|
34
72
|
|
35
|
-
def
|
36
|
-
pretty
|
37
|
-
|
73
|
+
def test_command_lifecycle_logging_without_color
|
74
|
+
simulate_command_lifecycle(pretty)
|
75
|
+
|
76
|
+
expected_log_lines = [
|
77
|
+
' INFO [aaaaaa] Running /usr/bin/env a_cmd some args as user@localhost',
|
78
|
+
' DEBUG [aaaaaa] Command: /usr/bin/env a_cmd some args',
|
79
|
+
" DEBUG [aaaaaa] \tstdout message",
|
80
|
+
" DEBUG [aaaaaa] \tstderr message",
|
81
|
+
' INFO [aaaaaa] Finished in 1.000 seconds with exit status 0 (successful).'
|
82
|
+
]
|
83
|
+
|
84
|
+
assert_equal expected_log_lines, output.split("\n")
|
38
85
|
end
|
39
86
|
|
40
|
-
def
|
41
|
-
|
42
|
-
|
87
|
+
def test_unsupported_class
|
88
|
+
raised_error = assert_raises RuntimeError do
|
89
|
+
pretty << Pathname.new('/tmp')
|
90
|
+
end
|
91
|
+
assert_equal('write only supports formatting SSHKit::LogMessage, called with Pathname: #<Pathname:/tmp>', raised_error.message)
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_does_not_log_message_when_verbosity_is_too_low
|
95
|
+
SSHKit.config.output_verbosity = Logger::WARN
|
96
|
+
pretty.info('Some info')
|
97
|
+
assert_log_output('')
|
98
|
+
|
99
|
+
SSHKit.config.output_verbosity = Logger::INFO
|
100
|
+
pretty.info('Some other info')
|
101
|
+
assert_log_output(" INFO Some other info\n")
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_does_not_log_command_when_verbosity_is_too_low
|
105
|
+
SSHKit.config.output_verbosity = Logger::WARN
|
106
|
+
command = Command.new(:ls, host: Host.new('user@localhost'), verbosity: Logger::INFO)
|
107
|
+
pretty.log_command_start(command)
|
108
|
+
assert_log_output('')
|
109
|
+
|
110
|
+
SSHKit.config.output_verbosity = Logger::INFO
|
111
|
+
pretty.log_command_start(command)
|
112
|
+
assert_log_output(" INFO [aaaaaa] Running /usr/bin/env ls as user@localhost\n")
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
def test_can_write_to_output_which_just_supports_append
|
117
|
+
# Note output doesn't have to be an IO, it only needs to support <<
|
118
|
+
output = stub(:<<)
|
119
|
+
pretty = SSHKit::Formatter::Pretty.new(output)
|
120
|
+
simulate_command_lifecycle(pretty)
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
def simulate_command_lifecycle(pretty)
|
126
|
+
command = SSHKit::Command.new(:a_cmd, 'some args', host: Host.new('user@localhost'))
|
127
|
+
command.stubs(:runtime).returns(1)
|
128
|
+
pretty.log_command_start(command)
|
129
|
+
command.started = true
|
130
|
+
command.on_stdout(nil, 'stdout message')
|
131
|
+
pretty.log_command_data(command, :stdout, 'stdout message')
|
132
|
+
command.on_stderr(nil, 'stderr message')
|
133
|
+
pretty.log_command_data(command, :stderr, 'stderr message')
|
134
|
+
command.exit_status = 0
|
135
|
+
pretty.log_command_exit(command)
|
43
136
|
end
|
44
137
|
|
45
|
-
def
|
46
|
-
|
47
|
-
assert_equal output.strip, "\e[0;30;49mDEBUG\e[0m Test".strip
|
138
|
+
def assert_log_output(expected_output)
|
139
|
+
assert_equal expected_output, output
|
48
140
|
end
|
49
141
|
|
50
142
|
end
|