ztk 0.2.4 → 0.2.5
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.
- data/lib/ztk/base.rb +70 -24
- data/lib/ztk/benchmark.rb +12 -15
- data/lib/ztk/command.rb +62 -43
- data/lib/ztk/logger.rb +8 -2
- data/lib/ztk/parallel.rb +16 -15
- data/lib/ztk/rescue_retry.rb +10 -12
- data/lib/ztk/spinner.rb +12 -5
- data/lib/ztk/ssh.rb +139 -109
- data/lib/ztk/tcp_socket_check.rb +16 -24
- data/lib/ztk/version.rb +1 -1
- data/spec/spec_helper.rb +8 -0
- data/spec/ztk/base_spec.rb +41 -0
- data/spec/ztk/benchmark_spec.rb +10 -12
- data/spec/ztk/command_spec.rb +142 -9
- data/spec/ztk/config_spec.rb +16 -16
- data/spec/ztk/logger_spec.rb +1 -1
- data/spec/ztk/rescue_retry_spec.rb +3 -3
- data/spec/ztk/spinner_spec.rb +51 -0
- data/spec/ztk/ssh_spec.rb +385 -119
- data/spec/ztk/tcp_socket_check_spec.rb +6 -6
- metadata +8 -4
data/lib/ztk/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -28,6 +28,14 @@ SimpleCov.start do
|
|
28
28
|
add_filter '/spec/'
|
29
29
|
end if ENV["COVERAGE"]
|
30
30
|
|
31
|
+
ENV['LOG_LEVEL'] = "DEBUG"
|
32
|
+
|
33
|
+
WAIT_SMALL = 3
|
34
|
+
|
31
35
|
$logger = ZTK::Logger.new(File.join("/tmp", "test.log"))
|
32
36
|
|
37
|
+
$logger.info { "=" * 80 }
|
38
|
+
$logger.info { "STARTING ZTK v#{ZTK::VERSION} TEST RUN @ #{Time.now.utc}" }
|
39
|
+
$logger.info { "=" * 80 }
|
40
|
+
|
33
41
|
################################################################################
|
@@ -0,0 +1,41 @@
|
|
1
|
+
################################################################################
|
2
|
+
#
|
3
|
+
# Author: Zachary Patten <zachary@jovelabs.net>
|
4
|
+
# Copyright: Copyright (c) Jove Labs
|
5
|
+
# License: Apache License, Version 2.0
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
################################################################################
|
20
|
+
|
21
|
+
require "spec_helper"
|
22
|
+
|
23
|
+
describe ZTK::Base do
|
24
|
+
|
25
|
+
subject { ZTK::Base }
|
26
|
+
|
27
|
+
before(:all) do
|
28
|
+
$stdout = File.open("/dev/null", "w")
|
29
|
+
$stderr = File.open("/dev/null", "w")
|
30
|
+
$stdin = File.open("/dev/null", "r")
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "class" do
|
34
|
+
|
35
|
+
it "should be ZTK::Base" do
|
36
|
+
subject.should be ZTK::Base
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
data/spec/ztk/benchmark_spec.rb
CHANGED
@@ -53,25 +53,23 @@ describe ZTK::Benchmark do
|
|
53
53
|
mark.should be_an_instance_of Float
|
54
54
|
end
|
55
55
|
|
56
|
-
it "should
|
56
|
+
it "should throw an exception if executed with a message but without a mark" do
|
57
57
|
stdout = StringIO.new
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
stdout.size.should == 0
|
58
|
+
lambda {
|
59
|
+
ZTK::Benchmark.bench(:stdout => stdout, :message => "Hello World")
|
60
|
+
}.should raise_error ZTK::BenchmarkError
|
62
61
|
end
|
63
62
|
|
64
|
-
it "should
|
63
|
+
it "should throw an exception if executed without a message but with a mark" do
|
65
64
|
stdout = StringIO.new
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
stdout.size.should == 0
|
65
|
+
lambda {
|
66
|
+
ZTK::Benchmark.bench(:stdout => stdout, :mark => "%0.4f")
|
67
|
+
}.should raise_error ZTK::BenchmarkError
|
70
68
|
end
|
71
69
|
|
72
|
-
it "should not write to STDOUT if not given a mark" do
|
70
|
+
it "should not write to STDOUT if not given a message or mark" do
|
73
71
|
stdout = StringIO.new
|
74
|
-
ZTK::Benchmark.bench(:stdout => stdout
|
72
|
+
ZTK::Benchmark.bench(:stdout => stdout) do
|
75
73
|
sleep(0.1)
|
76
74
|
end
|
77
75
|
stdout.size.should == 0
|
data/spec/ztk/command_spec.rb
CHANGED
@@ -62,16 +62,149 @@ describe ZTK::Command do
|
|
62
62
|
|
63
63
|
end
|
64
64
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
65
|
+
describe "behaviour" do
|
66
|
+
|
67
|
+
describe "execute" do
|
68
|
+
|
69
|
+
it "should be able to execute the command \"hostname -f\"" do
|
70
|
+
stdout = StringIO.new
|
71
|
+
subject.config do |config|
|
72
|
+
config.stdout = stdout
|
73
|
+
end
|
74
|
+
hostname = %x(hostname -f).chomp
|
75
|
+
status = subject.exec("hostname -f")
|
76
|
+
status.exit_code.should == 0
|
77
|
+
stdout.rewind
|
78
|
+
stdout.read.chomp.should == hostname
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should timeout after the period specified" do
|
82
|
+
stdout = StringIO.new
|
83
|
+
subject.config do |config|
|
84
|
+
config.stdout = stdout
|
85
|
+
config.timeout = WAIT_SMALL
|
86
|
+
end
|
87
|
+
hostname = %x(hostname -f).chomp
|
88
|
+
lambda { subject.exec("hostname -f ; sleep 10") }.should raise_error ZTK::CommandError
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should throw an exception if the exit status is not as expected" do
|
92
|
+
stdout = StringIO.new
|
93
|
+
subject.config do |config|
|
94
|
+
config.stdout = stdout
|
95
|
+
end
|
96
|
+
lambda { subject.exec("/bin/bash -c 'exit 64'") }.should raise_error ZTK::CommandError
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should return a instance of an OpenStruct object" do
|
100
|
+
stdout = StringIO.new
|
101
|
+
subject.config do |config|
|
102
|
+
config.stdout = stdout
|
103
|
+
end
|
104
|
+
result = subject.exec(%q{echo "Hello World"})
|
105
|
+
result.should be_an_instance_of OpenStruct
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should return the exit code" do
|
109
|
+
stdout = StringIO.new
|
110
|
+
subject.config do |config|
|
111
|
+
config.stdout = stdout
|
112
|
+
end
|
113
|
+
data = 64
|
114
|
+
|
115
|
+
result = subject.exec(%Q{/bin/bash -c 'exit #{data}'}, :exit_code => data)
|
116
|
+
result.exit_code.should == data
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should return the output" do
|
120
|
+
stdout = StringIO.new
|
121
|
+
subject.config do |config|
|
122
|
+
config.stdout = stdout
|
123
|
+
end
|
124
|
+
data = "Hello World @ #{Time.now.utc}"
|
125
|
+
|
126
|
+
result = subject.exec(%Q{echo "#{data}"})
|
127
|
+
result.output.match(data).should_not be nil
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should allow us to change the expected exit code" do
|
131
|
+
stdout = StringIO.new
|
132
|
+
subject.config do |config|
|
133
|
+
config.stdout = stdout
|
134
|
+
end
|
135
|
+
data = 32
|
136
|
+
result = subject.exec(%Q{/bin/bash -c 'exit #{data}'}, :exit_code => data)
|
137
|
+
end
|
138
|
+
|
139
|
+
describe "stdout" do
|
140
|
+
|
141
|
+
it "should capture STDOUT and send it to the appropriate pipe" do
|
142
|
+
stdout, stderr, stdin = StringIO.new, StringIO.new, StringIO.new
|
143
|
+
|
144
|
+
subject.config do |config|
|
145
|
+
config.stdout = stdout
|
146
|
+
config.stderr = stderr
|
147
|
+
config.stdin = stdin
|
148
|
+
end
|
149
|
+
data = "Hello World @ #{Time.now.utc}"
|
150
|
+
|
151
|
+
subject.exec(%Q{echo "#{data}" -f >&1})
|
152
|
+
|
153
|
+
stdout.rewind
|
154
|
+
stdout.read.match(data).should_not be nil
|
155
|
+
|
156
|
+
stderr.rewind
|
157
|
+
stderr.read.match(data).should be nil
|
158
|
+
|
159
|
+
stdin.rewind
|
160
|
+
stdin.read.match(data).should be nil
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
describe "stderr" do
|
166
|
+
|
167
|
+
it "should capture STDERR and send it to the appropriate pipe" do
|
168
|
+
stdout, stderr, stdin = StringIO.new, StringIO.new, StringIO.new
|
169
|
+
|
170
|
+
subject.config do |config|
|
171
|
+
config.stdout = stdout
|
172
|
+
config.stderr = stderr
|
173
|
+
config.stdin = stdin
|
174
|
+
end
|
175
|
+
data = "Hello World @ #{Time.now.utc}"
|
176
|
+
|
177
|
+
subject.exec(%Q{echo "#{data}" -f >&2})
|
178
|
+
|
179
|
+
stdout.rewind
|
180
|
+
stdout.read.match(data).should be nil
|
181
|
+
|
182
|
+
stderr.rewind
|
183
|
+
stderr.read.match(data).should_not be nil
|
184
|
+
|
185
|
+
stdin.rewind
|
186
|
+
stdin.read.match(data).should be nil
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
69
190
|
end
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
191
|
+
|
192
|
+
describe "upload" do
|
193
|
+
|
194
|
+
it "should raise a 'Not Supported' exception when attempting to upload" do
|
195
|
+
lambda { subject.upload("abc", "123") }.should raise_error
|
196
|
+
end
|
197
|
+
|
198
|
+
end
|
199
|
+
|
200
|
+
describe "download" do
|
201
|
+
|
202
|
+
it "should raise a 'Not Supported' exception when attempting to download" do
|
203
|
+
lambda { subject.download("abc", "123") }.should raise_error
|
204
|
+
end
|
205
|
+
|
206
|
+
end
|
207
|
+
|
75
208
|
end
|
76
209
|
|
77
210
|
end
|
data/spec/ztk/config_spec.rb
CHANGED
@@ -45,26 +45,26 @@ describe ZTK::Config do
|
|
45
45
|
|
46
46
|
end
|
47
47
|
|
48
|
-
|
48
|
+
end
|
49
49
|
|
50
|
-
|
51
|
-
subject.thing = "something"
|
52
|
-
subject.thing.should == "something"
|
53
|
-
subject[:thing].should == "something"
|
54
|
-
end
|
50
|
+
describe "behaviour" do
|
55
51
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
52
|
+
it "should allow setting of arbratary configuration keys" do
|
53
|
+
subject.thing = "something"
|
54
|
+
subject.thing.should == "something"
|
55
|
+
subject[:thing].should == "something"
|
56
|
+
end
|
60
57
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
subject.thing.should == 2
|
66
|
-
end
|
58
|
+
it "should allow hash bracket style access to configuration keys" do
|
59
|
+
subject[:thing] = "nothing"
|
60
|
+
subject[:thing].should == "nothing"
|
61
|
+
end
|
67
62
|
|
63
|
+
it "should allow loading of configurations from disk" do
|
64
|
+
config_file = File.expand_path(File.join(File.dirname(__FILE__), "..", "support", "test-config.rb"))
|
65
|
+
subject.from_file(config_file)
|
66
|
+
subject.message.should == "Hello World"
|
67
|
+
subject.thing.should == 2
|
68
68
|
end
|
69
69
|
|
70
70
|
end
|
data/spec/ztk/logger_spec.rb
CHANGED
@@ -46,7 +46,7 @@ describe ZTK::RescueRetry do
|
|
46
46
|
}.should raise_error ZTK::RescueRetryError, "You must supply a block!"
|
47
47
|
end
|
48
48
|
|
49
|
-
it "should retry on all exceptions" do
|
49
|
+
it "should retry on all exceptions by default if one is not supplied" do
|
50
50
|
$counter = 0
|
51
51
|
lambda {
|
52
52
|
ZTK::RescueRetry.try(:tries => 3) do
|
@@ -57,7 +57,7 @@ describe ZTK::RescueRetry do
|
|
57
57
|
$counter.should == 3
|
58
58
|
end
|
59
59
|
|
60
|
-
it "should retry on
|
60
|
+
it "should retry on supplied exception" do
|
61
61
|
$counter = 0
|
62
62
|
lambda {
|
63
63
|
ZTK::RescueRetry.try(:tries => 3, :on => EOFError) do
|
@@ -68,7 +68,7 @@ describe ZTK::RescueRetry do
|
|
68
68
|
$counter.should == 3
|
69
69
|
end
|
70
70
|
|
71
|
-
it "should not retry on
|
71
|
+
it "should not retry on exception if it does not match the supplied exception" do
|
72
72
|
$counter = 0
|
73
73
|
lambda {
|
74
74
|
ZTK::RescueRetry.try(:tries => 3, :on => EOFError) do
|
@@ -0,0 +1,51 @@
|
|
1
|
+
################################################################################
|
2
|
+
#
|
3
|
+
# Author: Zachary Patten <zachary@jovelabs.net>
|
4
|
+
# Copyright: Copyright (c) Jove Labs
|
5
|
+
# License: Apache License, Version 2.0
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
################################################################################
|
20
|
+
|
21
|
+
require "spec_helper"
|
22
|
+
|
23
|
+
describe ZTK::Spinner do
|
24
|
+
|
25
|
+
subject { ZTK::Spinner }
|
26
|
+
|
27
|
+
before(:all) do
|
28
|
+
$stdout = File.open("/dev/null", "w")
|
29
|
+
$stderr = File.open("/dev/null", "w")
|
30
|
+
$stdin = File.open("/dev/null", "r")
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "class" do
|
34
|
+
|
35
|
+
it "should be ZTK::Spinner" do
|
36
|
+
subject.should be ZTK::Spinner
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "behaviour" do
|
42
|
+
|
43
|
+
it "should throw an exception if executed without a block" do
|
44
|
+
lambda {
|
45
|
+
ZTK::Spinner.spin
|
46
|
+
}.should raise_error ZTK::SpinnerError, "You must supply a block!"
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
data/spec/ztk/ssh_spec.rb
CHANGED
@@ -60,183 +60,449 @@ describe ZTK::SSH do
|
|
60
60
|
|
61
61
|
end
|
62
62
|
|
63
|
-
|
64
|
-
if !ENV['CI'] && !ENV['TRAVIS']
|
63
|
+
end
|
65
64
|
|
66
|
-
|
65
|
+
# this stuff doesn't work as is under travis-ci right now
|
66
|
+
describe "direct SSH behaviour" do
|
67
67
|
|
68
|
-
|
68
|
+
describe "execute" do
|
69
69
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
config.stderr = stderr
|
75
|
-
config.user = ENV["USER"]
|
76
|
-
config.host_name = "127.0.0.1"
|
77
|
-
end
|
70
|
+
it "should be able to connect to 127.0.0.1 as the current user and execute a command (your key must be in ssh-agent)" do
|
71
|
+
stdout, stderr, stdin = StringIO.new, StringIO.new, StringIO.new
|
72
|
+
subject.config do |config|
|
73
|
+
config.stdout, config.stderr, config.stdin = stdout, stderr, stdin
|
78
74
|
|
79
|
-
|
75
|
+
config.user = ENV["USER"]
|
76
|
+
config.host_name = "127.0.0.1"
|
77
|
+
end
|
80
78
|
|
81
|
-
|
82
|
-
status.exit.exitstatus.should == 0
|
83
|
-
stdout.rewind
|
84
|
-
stdout.read.chomp.should == data
|
85
|
-
end
|
79
|
+
data = %x(hostname -f).chomp
|
86
80
|
|
81
|
+
status = subject.exec("hostname -f")
|
82
|
+
status.exit_code.should == 0
|
83
|
+
stdout.rewind
|
84
|
+
stdout.read.chomp.should == data
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should timeout after the period specified" do
|
88
|
+
stdout, stderr, stdin = StringIO.new, StringIO.new, StringIO.new
|
89
|
+
subject.config do |config|
|
90
|
+
config.stdout, config.stderr, config.stdin = stdout, stderr, stdin
|
91
|
+
|
92
|
+
config.user = ENV["USER"]
|
93
|
+
config.host_name = "127.0.0.1"
|
94
|
+
config.timeout = WAIT_SMALL
|
87
95
|
end
|
96
|
+
hostname = %x(hostname -f).chomp
|
97
|
+
lambda { subject.exec("hostname -f ; sleep 10") }.should raise_error ZTK::SSHError
|
98
|
+
end
|
88
99
|
|
89
|
-
|
100
|
+
it "should throw an exception if the exit status is not as expected" do
|
101
|
+
stdout, stderr, stdin = StringIO.new, StringIO.new, StringIO.new
|
102
|
+
subject.config do |config|
|
103
|
+
config.stdout, config.stderr, config.stdin = stdout, stderr, stdin
|
90
104
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
config.user = ENV["USER"]
|
97
|
-
config.host_name = "127.0.0.1"
|
98
|
-
end
|
105
|
+
config.user = ENV["USER"]
|
106
|
+
config.host_name = "127.0.0.1"
|
107
|
+
end
|
108
|
+
lambda { subject.exec("/bin/bash -c 'exit 64'") }.should raise_error ZTK::SSHError
|
109
|
+
end
|
99
110
|
|
100
|
-
|
111
|
+
it "should return a instance of an OpenStruct object" do
|
112
|
+
stdout, stderr, stdin = StringIO.new, StringIO.new, StringIO.new
|
113
|
+
subject.config do |config|
|
114
|
+
config.stdout, config.stderr, config.stdin = stdout, stderr, stdin
|
101
115
|
|
102
|
-
|
103
|
-
|
116
|
+
config.user = ENV["USER"]
|
117
|
+
config.host_name = "127.0.0.1"
|
118
|
+
end
|
119
|
+
result = subject.exec(%q{echo "Hello World"})
|
120
|
+
result.should be_an_instance_of OpenStruct
|
121
|
+
end
|
104
122
|
|
105
|
-
|
106
|
-
|
123
|
+
it "should return the exit code" do
|
124
|
+
stdout, stderr, stdin = StringIO.new, StringIO.new, StringIO.new
|
125
|
+
subject.config do |config|
|
126
|
+
config.stdout, config.stderr, config.stdin = stdout, stderr, stdin
|
107
127
|
|
108
|
-
|
109
|
-
|
110
|
-
|
128
|
+
config.user = ENV["USER"]
|
129
|
+
config.host_name = "127.0.0.1"
|
130
|
+
end
|
131
|
+
data = 64
|
111
132
|
|
112
|
-
|
113
|
-
|
114
|
-
|
133
|
+
result = subject.exec(%Q{/bin/bash -c 'exit #{data}'}, :exit_code => data)
|
134
|
+
result.exit_code.should == data
|
135
|
+
end
|
115
136
|
|
137
|
+
it "should return the output" do
|
138
|
+
stdout, stderr, stdin = StringIO.new, StringIO.new, StringIO.new
|
139
|
+
subject.config do |config|
|
140
|
+
config.stdout, config.stderr, config.stdin = stdout, stderr, stdin
|
141
|
+
|
142
|
+
config.user = ENV["USER"]
|
143
|
+
config.host_name = "127.0.0.1"
|
116
144
|
end
|
145
|
+
data = "Hello World @ #{Time.now.utc}"
|
146
|
+
|
147
|
+
result = subject.exec(%Q{echo "#{data}"})
|
148
|
+
result.output.match(data).should_not be nil
|
149
|
+
end
|
150
|
+
|
151
|
+
it "should allow us to change the expected exit code" do
|
152
|
+
stdout, stderr, stdin = StringIO.new, StringIO.new, StringIO.new
|
153
|
+
subject.config do |config|
|
154
|
+
config.stdout, config.stderr, config.stdin = stdout, stderr, stdin
|
155
|
+
|
156
|
+
config.user = ENV["USER"]
|
157
|
+
config.host_name = "127.0.0.1"
|
158
|
+
end
|
159
|
+
data = 32
|
160
|
+
result = subject.exec(%Q{/bin/bash -c 'exit #{data}'}, :exit_code => data)
|
161
|
+
end
|
117
162
|
|
118
|
-
|
163
|
+
describe "stdout" do
|
119
164
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
config.stderr = stderr
|
125
|
-
config.user = ENV["USER"]
|
126
|
-
config.host_name = "127.0.0.1"
|
127
|
-
end
|
165
|
+
it "should capture STDOUT and send it to the appropriate pipe" do
|
166
|
+
stdout, stderr, stdin = StringIO.new, StringIO.new, StringIO.new
|
167
|
+
subject.config do |config|
|
168
|
+
config.stdout, config.stderr, config.stdin = stdout, stderr, stdin
|
128
169
|
|
129
|
-
|
170
|
+
config.user = ENV["USER"]
|
171
|
+
config.host_name = "127.0.0.1"
|
172
|
+
end
|
173
|
+
data = "Hello World @ #{Time.now.utc}"
|
174
|
+
|
175
|
+
subject.exec(%Q{echo "#{data}" -f >&1})
|
176
|
+
|
177
|
+
stdout.rewind
|
178
|
+
stdout.read.match(data).should_not be nil
|
179
|
+
|
180
|
+
stderr.rewind
|
181
|
+
stderr.read.match(data).should be nil
|
130
182
|
|
131
|
-
|
132
|
-
|
183
|
+
stdin.rewind
|
184
|
+
stdin.read.match(data).should be nil
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
133
188
|
|
134
|
-
|
135
|
-
IO.write(remote_file, data)
|
189
|
+
describe "stderr" do
|
136
190
|
|
137
|
-
|
138
|
-
|
139
|
-
|
191
|
+
it "should capture STDERR and send it to the appropriate pipe" do
|
192
|
+
stdout, stderr, stdin = StringIO.new, StringIO.new, StringIO.new
|
193
|
+
subject.config do |config|
|
194
|
+
config.stdout, config.stderr, config.stdin = stdout, stderr, stdin
|
140
195
|
|
141
|
-
|
142
|
-
|
196
|
+
config.user = ENV["USER"]
|
197
|
+
config.host_name = "127.0.0.1"
|
143
198
|
end
|
199
|
+
data = "Hello World @ #{Time.now.utc}"
|
144
200
|
|
201
|
+
subject.exec(%Q{echo "#{data}" -f >&2})
|
202
|
+
|
203
|
+
stdout.rewind
|
204
|
+
stdout.read.match(data).should be nil
|
205
|
+
|
206
|
+
stderr.rewind
|
207
|
+
stderr.read.match(data).should_not be nil
|
208
|
+
|
209
|
+
stdin.rewind
|
210
|
+
stdin.read.match(data).should be nil
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
end
|
215
|
+
|
216
|
+
describe "upload" do
|
217
|
+
|
218
|
+
it "should be able to upload a file to 127.0.0.1 as the current user and execute a command (your key must be in ssh-agent)" do
|
219
|
+
stdout, stderr, stdin = StringIO.new, StringIO.new, StringIO.new
|
220
|
+
subject.config do |config|
|
221
|
+
config.stdout, config.stderr, config.stdin = stdout, stderr, stdin
|
222
|
+
|
223
|
+
config.user = ENV["USER"]
|
224
|
+
config.host_name = "127.0.0.1"
|
225
|
+
end
|
226
|
+
|
227
|
+
data = "Hello World @ #{Time.now.utc}"
|
228
|
+
|
229
|
+
remote_file = File.join("/tmp", "ssh-upload-remote")
|
230
|
+
File.exists?(remote_file) && File.delete(remote_file)
|
231
|
+
|
232
|
+
local_file = File.join("/tmp", "ssh-upload-local")
|
233
|
+
IO.write(local_file, data)
|
234
|
+
|
235
|
+
File.exists?(remote_file).should == false
|
236
|
+
subject.upload(local_file, remote_file)
|
237
|
+
File.exists?(remote_file).should == true
|
238
|
+
|
239
|
+
File.exists?(remote_file) && File.delete(remote_file)
|
240
|
+
File.exists?(local_file) && File.delete(local_file)
|
241
|
+
end
|
242
|
+
|
243
|
+
end
|
244
|
+
|
245
|
+
describe "download" do
|
246
|
+
|
247
|
+
it "should be able to download a file from 127.0.0.1 as the current user and execute a command (your key must be in ssh-agent)" do
|
248
|
+
stdout, stderr, stdin = StringIO.new, StringIO.new, StringIO.new
|
249
|
+
subject.config do |config|
|
250
|
+
config.stdout, config.stderr, config.stdin = stdout, stderr, stdin
|
251
|
+
|
252
|
+
config.user = ENV["USER"]
|
253
|
+
config.host_name = "127.0.0.1"
|
145
254
|
end
|
146
255
|
|
256
|
+
data = "Hello World @ #{Time.now.utc}"
|
257
|
+
|
258
|
+
local_file = File.join("/tmp", "ssh-download-local")
|
259
|
+
File.exists?(local_file) && File.delete(local_file)
|
260
|
+
|
261
|
+
remote_file = File.join("/tmp", "ssh-download-remote")
|
262
|
+
IO.write(remote_file, data)
|
263
|
+
|
264
|
+
File.exists?(local_file).should == false
|
265
|
+
subject.download(remote_file, local_file)
|
266
|
+
File.exists?(local_file).should == true
|
267
|
+
|
268
|
+
File.exists?(local_file) && File.delete(local_file)
|
269
|
+
File.exists?(remote_file) && File.delete(remote_file)
|
147
270
|
end
|
148
271
|
|
149
|
-
|
272
|
+
end
|
150
273
|
|
151
|
-
|
274
|
+
end if !ENV['CI'] && !ENV['TRAVIS']
|
152
275
|
|
153
|
-
|
154
|
-
stdout, stderr = StringIO.new, StringIO.new
|
155
|
-
subject.config do |config|
|
156
|
-
config.stdout = stdout
|
157
|
-
config.stderr = stderr
|
158
|
-
config.user = ENV["USER"]
|
159
|
-
config.host_name = "127.0.0.1"
|
160
|
-
config.proxy_user = ENV["USER"]
|
161
|
-
config.proxy_host_name = "127.0.0.1"
|
162
|
-
end
|
276
|
+
describe "proxy SSH behaviour" do
|
163
277
|
|
164
|
-
|
278
|
+
describe "execute" do
|
165
279
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
end
|
280
|
+
it "should be able to proxy through 127.0.0.1, connecting to 127.0.0.1 as the current user and execute a command (your key must be in ssh-agent)" do
|
281
|
+
stdout, stderr, stdin = StringIO.new, StringIO.new, StringIO.new
|
282
|
+
subject.config do |config|
|
283
|
+
config.stdout, config.stderr, config.stdin = stdout, stderr, stdin
|
171
284
|
|
285
|
+
config.user = ENV["USER"]
|
286
|
+
config.host_name = "127.0.0.1"
|
287
|
+
config.proxy_user = ENV["USER"]
|
288
|
+
config.proxy_host_name = "127.0.0.1"
|
172
289
|
end
|
173
290
|
|
174
|
-
|
291
|
+
data = %x( hostname -f ).chomp
|
175
292
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
config.user = ENV["USER"]
|
182
|
-
config.host_name = "127.0.0.1"
|
183
|
-
config.proxy_user = ENV["USER"]
|
184
|
-
config.proxy_host_name = "127.0.0.1"
|
185
|
-
end
|
293
|
+
status = subject.exec("hostname -f")
|
294
|
+
status.exit_code.should == 0
|
295
|
+
stdout.rewind
|
296
|
+
stdout.read.chomp.should == data
|
297
|
+
end
|
186
298
|
|
187
|
-
|
299
|
+
it "should timeout after the period specified" do
|
300
|
+
stdout, stderr, stdin = StringIO.new, StringIO.new, StringIO.new
|
301
|
+
subject.config do |config|
|
302
|
+
config.stdout, config.stderr, config.stdin = stdout, stderr, stdin
|
188
303
|
|
189
|
-
|
190
|
-
|
304
|
+
config.user = ENV["USER"]
|
305
|
+
config.host_name = "127.0.0.1"
|
306
|
+
config.proxy_user = ENV["USER"]
|
307
|
+
config.proxy_host_name = "127.0.0.1"
|
308
|
+
config.timeout = WAIT_SMALL
|
309
|
+
end
|
310
|
+
hostname = %x(hostname -f).chomp
|
311
|
+
lambda { subject.exec("hostname -f ; sleep 10") }.should raise_error ZTK::SSHError
|
312
|
+
end
|
191
313
|
|
192
|
-
|
193
|
-
|
314
|
+
it "should throw an exception if the exit status is not as expected" do
|
315
|
+
stdout, stderr, stdin = StringIO.new, StringIO.new, StringIO.new
|
316
|
+
subject.config do |config|
|
317
|
+
config.stdout, config.stderr, config.stdin = stdout, stderr, stdin
|
194
318
|
|
195
|
-
|
196
|
-
|
197
|
-
|
319
|
+
config.user = ENV["USER"]
|
320
|
+
config.host_name = "127.0.0.1"
|
321
|
+
config.proxy_user = ENV["USER"]
|
322
|
+
config.proxy_host_name = "127.0.0.1"
|
323
|
+
end
|
324
|
+
lambda { subject.exec("/bin/bash -c 'exit 64'") }.should raise_error ZTK::SSHError
|
325
|
+
end
|
198
326
|
|
199
|
-
|
200
|
-
|
201
|
-
|
327
|
+
it "should return a instance of an OpenStruct object" do
|
328
|
+
stdout, stderr, stdin = StringIO.new, StringIO.new, StringIO.new
|
329
|
+
subject.config do |config|
|
330
|
+
config.stdout, config.stderr, config.stdin = stdout, stderr, stdin
|
331
|
+
|
332
|
+
config.user = ENV["USER"]
|
333
|
+
config.host_name = "127.0.0.1"
|
334
|
+
config.proxy_user = ENV["USER"]
|
335
|
+
config.proxy_host_name = "127.0.0.1"
|
336
|
+
end
|
337
|
+
result = subject.exec(%q{echo "Hello World"})
|
338
|
+
result.should be_an_instance_of OpenStruct
|
339
|
+
end
|
202
340
|
|
341
|
+
it "should return the exit code" do
|
342
|
+
stdout, stderr, stdin = StringIO.new, StringIO.new, StringIO.new
|
343
|
+
subject.config do |config|
|
344
|
+
config.stdout, config.stderr, config.stdin = stdout, stderr, stdin
|
345
|
+
|
346
|
+
config.user = ENV["USER"]
|
347
|
+
config.host_name = "127.0.0.1"
|
348
|
+
config.proxy_user = ENV["USER"]
|
349
|
+
config.proxy_host_name = "127.0.0.1"
|
203
350
|
end
|
351
|
+
data = 64
|
204
352
|
|
205
|
-
|
353
|
+
result = subject.exec(%Q{/bin/bash -c 'exit #{data}'}, :exit_code => data)
|
354
|
+
result.exit_code.should == data
|
355
|
+
end
|
356
|
+
|
357
|
+
it "should return the output" do
|
358
|
+
stdout, stderr, stdin = StringIO.new, StringIO.new, StringIO.new
|
359
|
+
subject.config do |config|
|
360
|
+
config.stdout, config.stderr, config.stdin = stdout, stderr, stdin
|
361
|
+
|
362
|
+
config.user = ENV["USER"]
|
363
|
+
config.host_name = "127.0.0.1"
|
364
|
+
config.proxy_user = ENV["USER"]
|
365
|
+
config.proxy_host_name = "127.0.0.1"
|
366
|
+
end
|
367
|
+
data = "Hello World @ #{Time.now.utc}"
|
206
368
|
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
config.stdout = stdout
|
211
|
-
config.stderr = stderr
|
212
|
-
config.user = ENV["USER"]
|
213
|
-
config.host_name = "127.0.0.1"
|
214
|
-
config.proxy_user = ENV["USER"]
|
215
|
-
config.proxy_host_name = "127.0.0.1"
|
216
|
-
end
|
369
|
+
result = subject.exec(%Q{echo "#{data}"})
|
370
|
+
result.output.match(data).should_not be nil
|
371
|
+
end
|
217
372
|
|
218
|
-
|
373
|
+
it "should allow us to change the expected exit code" do
|
374
|
+
stdout, stderr, stdin = StringIO.new, StringIO.new, StringIO.new
|
375
|
+
subject.config do |config|
|
376
|
+
config.stdout, config.stderr, config.stdin = stdout, stderr, stdin
|
219
377
|
|
220
|
-
|
221
|
-
|
378
|
+
config.user = ENV["USER"]
|
379
|
+
config.host_name = "127.0.0.1"
|
380
|
+
config.proxy_user = ENV["USER"]
|
381
|
+
config.proxy_host_name = "127.0.0.1"
|
382
|
+
end
|
383
|
+
data = 32
|
384
|
+
result = subject.exec(%Q{/bin/bash -c 'exit #{data}'}, :exit_code => data)
|
385
|
+
end
|
222
386
|
|
223
|
-
|
224
|
-
IO.write(remote_file, data)
|
387
|
+
describe "stdout" do
|
225
388
|
|
226
|
-
|
227
|
-
|
228
|
-
|
389
|
+
it "should capture STDOUT and send it to the appropriate pipe" do
|
390
|
+
stdout, stderr, stdin = StringIO.new, StringIO.new, StringIO.new
|
391
|
+
subject.config do |config|
|
392
|
+
config.stdout, config.stderr, config.stdin = stdout, stderr, stdin
|
229
393
|
|
230
|
-
|
231
|
-
|
394
|
+
config.user = ENV["USER"]
|
395
|
+
config.host_name = "127.0.0.1"
|
396
|
+
config.proxy_user = ENV["USER"]
|
397
|
+
config.proxy_host_name = "127.0.0.1"
|
232
398
|
end
|
399
|
+
data = "Hello World @ #{Time.now.utc}"
|
400
|
+
|
401
|
+
subject.exec(%Q{echo "#{data}" -f >&1})
|
233
402
|
|
403
|
+
stdout.rewind
|
404
|
+
stdout.read.match(data).should_not be nil
|
405
|
+
|
406
|
+
stderr.rewind
|
407
|
+
stderr.read.match(data).should be nil
|
408
|
+
|
409
|
+
stdin.rewind
|
410
|
+
stdin.read.match(data).should be nil
|
234
411
|
end
|
235
412
|
|
236
413
|
end
|
237
414
|
|
415
|
+
describe "stderr" do
|
416
|
+
|
417
|
+
it "should capture STDERR and send it to the appropriate pipe" do
|
418
|
+
stdout, stderr, stdin = StringIO.new, StringIO.new, StringIO.new
|
419
|
+
subject.config do |config|
|
420
|
+
config.stdout, config.stderr, config.stdin = stdout, stderr, stdin
|
421
|
+
|
422
|
+
config.user = ENV["USER"]
|
423
|
+
config.host_name = "127.0.0.1"
|
424
|
+
config.proxy_user = ENV["USER"]
|
425
|
+
config.proxy_host_name = "127.0.0.1"
|
426
|
+
end
|
427
|
+
data = "Hello World @ #{Time.now.utc}"
|
428
|
+
|
429
|
+
subject.exec(%Q{echo "#{data}" -f >&2})
|
430
|
+
|
431
|
+
stdout.rewind
|
432
|
+
stdout.read.match(data).should be nil
|
433
|
+
|
434
|
+
stderr.rewind
|
435
|
+
stderr.read.match(data).should_not be nil
|
436
|
+
|
437
|
+
stdin.rewind
|
438
|
+
stdin.read.match(data).should be nil
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
238
442
|
end
|
239
443
|
|
240
|
-
|
444
|
+
describe "upload" do
|
445
|
+
|
446
|
+
it "should be able to upload a file to 127.0.0.1 as the current user and execute a command (your key must be in ssh-agent)" do
|
447
|
+
stdout, stderr, stdin = StringIO.new, StringIO.new, StringIO.new
|
448
|
+
subject.config do |config|
|
449
|
+
config.stdout, config.stderr, config.stdin = stdout, stderr, stdin
|
450
|
+
|
451
|
+
config.user = ENV["USER"]
|
452
|
+
config.host_name = "127.0.0.1"
|
453
|
+
config.proxy_user = ENV["USER"]
|
454
|
+
config.proxy_host_name = "127.0.0.1"
|
455
|
+
end
|
456
|
+
|
457
|
+
data = "Hello World @ #{Time.now.utc}"
|
458
|
+
|
459
|
+
remote_file = File.join("/tmp", "ssh-upload-remote")
|
460
|
+
File.exists?(remote_file) && File.delete(remote_file)
|
461
|
+
|
462
|
+
local_file = File.join("/tmp", "ssh-upload-local")
|
463
|
+
IO.write(local_file, data)
|
464
|
+
|
465
|
+
File.exists?(remote_file).should == false
|
466
|
+
subject.upload(local_file, remote_file)
|
467
|
+
File.exists?(remote_file).should == true
|
468
|
+
|
469
|
+
File.exists?(remote_file) && File.delete(remote_file)
|
470
|
+
File.exists?(local_file) && File.delete(local_file)
|
471
|
+
end
|
472
|
+
|
473
|
+
end
|
474
|
+
|
475
|
+
describe "download" do
|
476
|
+
|
477
|
+
it "should be able to download a file from 127.0.0.1 as the current user and execute a command (your key must be in ssh-agent)" do
|
478
|
+
stdout, stderr, stdin = StringIO.new, StringIO.new, StringIO.new
|
479
|
+
subject.config do |config|
|
480
|
+
config.stdout, config.stderr, config.stdin = stdout, stderr, stdin
|
481
|
+
|
482
|
+
config.user = ENV["USER"]
|
483
|
+
config.host_name = "127.0.0.1"
|
484
|
+
config.proxy_user = ENV["USER"]
|
485
|
+
config.proxy_host_name = "127.0.0.1"
|
486
|
+
end
|
487
|
+
|
488
|
+
data = "Hello World @ #{Time.now.utc}"
|
489
|
+
|
490
|
+
local_file = File.join("/tmp", "ssh-download-local")
|
491
|
+
File.exists?(local_file) && File.delete(local_file)
|
492
|
+
|
493
|
+
remote_file = File.join("/tmp", "ssh-download-remote")
|
494
|
+
IO.write(remote_file, data)
|
495
|
+
|
496
|
+
File.exists?(local_file).should == false
|
497
|
+
subject.download(remote_file, local_file)
|
498
|
+
File.exists?(local_file).should == true
|
499
|
+
|
500
|
+
File.exists?(local_file) && File.delete(local_file)
|
501
|
+
File.exists?(remote_file) && File.delete(remote_file)
|
502
|
+
end
|
503
|
+
|
504
|
+
end
|
505
|
+
|
506
|
+
end if !ENV['CI'] && !ENV['TRAVIS']
|
241
507
|
|
242
508
|
end
|