net-ssh-simple 1.0.1 → 1.1.0
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/net/ssh/simple/core.rb +89 -9
- data/lib/net/ssh/simple/version.rb +1 -1
- data/spec/net-ssh-simple.rb +211 -0
- metadata +5 -5
data/lib/net/ssh/simple/core.rb
CHANGED
@@ -5,7 +5,10 @@ module Net
|
|
5
5
|
# @example
|
6
6
|
# # Block Syntax (synchronous)
|
7
7
|
# Net::SSH::Simple.sync do
|
8
|
-
# ssh
|
8
|
+
# r = ssh 'example1.com', 'echo "Hello World."'
|
9
|
+
# puts r.stdout #=> "Hello World."
|
10
|
+
# puts r.exit_code #=> 0
|
11
|
+
#
|
9
12
|
# scp_ul 'example2.com', '/tmp/local_foo', '/tmp/remote_bar'
|
10
13
|
# scp_dl 'example3.com', '/tmp/remote_foo', '/tmp/local_bar'
|
11
14
|
# end
|
@@ -133,6 +136,72 @@ module Net
|
|
133
136
|
# end
|
134
137
|
# end
|
135
138
|
#
|
139
|
+
# @example
|
140
|
+
# #
|
141
|
+
# # Here be dragons: Using the event-API for a stdin->stdout pipeline
|
142
|
+
# #
|
143
|
+
# Net::SSH::Simple.sync do
|
144
|
+
# # open a shell
|
145
|
+
# r = ssh('localhost', '/bin/sh') do |e,c,d|
|
146
|
+
# # e = :start, :stdout, :stderr, :exit_code, :exit_signal or :finish
|
147
|
+
# # c = our Net::SSH::Connection::Channel instance
|
148
|
+
# # d = data for this event
|
149
|
+
# case e
|
150
|
+
# # :start is triggered exactly once per connection
|
151
|
+
# when :start
|
152
|
+
# # we can send data using Channel#send_data
|
153
|
+
# c.send_data("echo 'hello stdout'\n")
|
154
|
+
# c.send_data("echo 'hello stderr' 1>&2\n")
|
155
|
+
# # don't forget to eof when done feeding!
|
156
|
+
# c.eof!
|
157
|
+
#
|
158
|
+
# # :stdout is triggered when there's stdout data from remote.
|
159
|
+
# # by default the data is also appended to result[:stdout].
|
160
|
+
# # you may return :no_append as seen below to avoid that.
|
161
|
+
# when :stdout
|
162
|
+
# # read the input line-wise (it *will* arrive fragmented!)
|
163
|
+
# (@buf ||= '') << d
|
164
|
+
# while line = @buf.slice!(/(.*)\r?\n/)
|
165
|
+
# puts line #=> "hello stdout"
|
166
|
+
# end
|
167
|
+
# :no_append
|
168
|
+
#
|
169
|
+
# # :stderr is triggered when there's stderr data from remote.
|
170
|
+
# # by default the data is also appended to result[:stderr].
|
171
|
+
# # you may return :no_append as seen below to avoid that.
|
172
|
+
# when :stderr
|
173
|
+
# # read the input line-wise (it *will* arrive fragmented!)
|
174
|
+
# (@buf ||= '') << d
|
175
|
+
# while line = @buf.slice!(/(.*)\r?\n/)
|
176
|
+
# puts line #=> "hello stderr"
|
177
|
+
# end
|
178
|
+
# :no_append
|
179
|
+
#
|
180
|
+
# # :exit_code is triggered when the remote process exits normally.
|
181
|
+
# # it does *not* trigger when the remote process exits by signal!
|
182
|
+
# when :exit_code
|
183
|
+
# puts d #=> 0
|
184
|
+
#
|
185
|
+
# # :exit_signal is triggered when the remote is killed by signal.
|
186
|
+
# # this would normally raise a Net::SSH::Simple::Error but
|
187
|
+
# # we suppress that here by returning :no_raise
|
188
|
+
# when :exit_signal
|
189
|
+
# puts d # won't fire in this example, could be "TERM"
|
190
|
+
# :no_raise
|
191
|
+
#
|
192
|
+
# # :finish triggers after :exit_code when the command exits normally.
|
193
|
+
# # it does *not* trigger when the remote process exits by signal!
|
194
|
+
# when :finish
|
195
|
+
# puts "we are finished!"
|
196
|
+
# end
|
197
|
+
# end
|
198
|
+
# end
|
199
|
+
#
|
200
|
+
# # Our Result has been populated normally, except for
|
201
|
+
# # :stdout and :stdin (because we used :no_append).
|
202
|
+
# puts r #=> Net::SSH::Simple::Result
|
203
|
+
#
|
204
|
+
#
|
136
205
|
# @author moe@busyloop.net
|
137
206
|
#
|
138
207
|
class Simple
|
@@ -221,7 +290,7 @@ module Net
|
|
221
290
|
# @param [String] host Destination hostname or ip-address
|
222
291
|
# @param [String] cmd Shell command to execute
|
223
292
|
# @param opts (see Net::SSH::Simple#ssh)
|
224
|
-
# @param [Block] block Progress callback (optional)
|
293
|
+
# @param [Block] &block Progress callback (optional)
|
225
294
|
# @return [Net::SSH::Simple::Result] Result
|
226
295
|
#
|
227
296
|
def scp_ul(host, src, dst, opts={}, &block)
|
@@ -236,7 +305,7 @@ module Net
|
|
236
305
|
# @param [String] host Destination hostname or ip-address
|
237
306
|
# @param [String] cmd Shell command to execute
|
238
307
|
# @param [Hash] opts Parameters for the underlying Net::SSH
|
239
|
-
# @param [Block] block Progress callback (optional)
|
308
|
+
# @param [Block] &block Progress callback (optional)
|
240
309
|
# @return [Net::SSH::Simple::Result] Result
|
241
310
|
# @see Net::SSH::Simple#scp_ul
|
242
311
|
#
|
@@ -253,6 +322,7 @@ module Net
|
|
253
322
|
# @param [String] host Destination hostname or ip-address
|
254
323
|
# @param [String] cmd Shell command to execute
|
255
324
|
# @param [Hash] opts Parameters for the underlying Net::SSH
|
325
|
+
# @param [Block] &block Use the event-API (see example above)
|
256
326
|
# @option opts [Array] :auth_methods
|
257
327
|
# an array of authentication methods to try
|
258
328
|
#
|
@@ -360,7 +430,7 @@ module Net
|
|
360
430
|
#
|
361
431
|
# @see http://net-ssh.github.com/ssh/v2/api/classes/Net/SSH/Config.html
|
362
432
|
# Net::SSH documentation for the 'opts'-hash
|
363
|
-
def ssh(host, cmd, opts={})
|
433
|
+
def ssh(host, cmd, opts={}, &block)
|
364
434
|
with_session(host, opts) do |session|
|
365
435
|
@result = Result.new(
|
366
436
|
{ :host => host, :cmd => cmd, :start_at => Time.new,
|
@@ -371,23 +441,33 @@ module Net
|
|
371
441
|
chan.exec cmd do |ch, success|
|
372
442
|
@result[:success] = success
|
373
443
|
ch.on_data do |c, data|
|
374
|
-
|
444
|
+
r = block.call(:stdout, ch, data) if block
|
445
|
+
@result[:stdout] += data.to_s unless r == :no_append
|
375
446
|
end
|
376
447
|
ch.on_extended_data do |c, type, data|
|
377
|
-
|
448
|
+
r = block.call(:stderr, ch, data) if block
|
449
|
+
@result[:stderr] += data.to_s unless r == :no_append
|
378
450
|
end
|
379
451
|
ch.on_request('exit-status') do |c, data|
|
380
|
-
|
452
|
+
exit_code = data.read_long
|
453
|
+
block.call(:exit_code, ch, exit_code) if block
|
454
|
+
@result[:exit_code] = exit_code
|
381
455
|
end
|
382
456
|
ch.on_request('exit-signal') do |c, data|
|
383
|
-
|
457
|
+
exit_signal = data.read_string
|
458
|
+
r = block.call(:exit_signal, ch, exit_signal) if block
|
459
|
+
@result[:exit_signal] = exit_signal
|
384
460
|
@result[:success] = false
|
385
|
-
|
461
|
+
unless r == :no_raise
|
462
|
+
raise "Killed by SIG#{@result[:exit_signal]}"
|
463
|
+
end
|
386
464
|
end
|
465
|
+
block.call(:start, ch, nil) if block
|
387
466
|
end
|
388
467
|
end
|
389
468
|
channel.wait
|
390
469
|
@result[:finish_at] = Time.new
|
470
|
+
block.call(:finish, channel, nil) if block
|
391
471
|
@result
|
392
472
|
end
|
393
473
|
end
|
data/spec/net-ssh-simple.rb
CHANGED
@@ -272,4 +272,215 @@ describe Net::SSH::Simple do
|
|
272
272
|
v.to_s.should == 'Killed by SIGTERM @ ["localhost", {}]'
|
273
273
|
end
|
274
274
|
end
|
275
|
+
|
276
|
+
describe "event api" do
|
277
|
+
it "handles long stdin->stdout pipe" do
|
278
|
+
mockie = mock(:callbacks)
|
279
|
+
mockie.should_receive(:start).once.ordered
|
280
|
+
mockie.should_receive(:exit_code).once.ordered
|
281
|
+
mockie.should_receive(:finish).once.ordered
|
282
|
+
mockie.should_not_receive(:exit_signal)
|
283
|
+
|
284
|
+
stdout_copy = ''
|
285
|
+
a = Net::SSH::Simple.sync do
|
286
|
+
i = 0
|
287
|
+
r = ssh('localhost', 'sed "s/0/X/g"') do |e,c,d|
|
288
|
+
case e
|
289
|
+
when :start
|
290
|
+
mockie.start()
|
291
|
+
(0..16384).each do |i|
|
292
|
+
c.send_data("foobar #{i}\n")
|
293
|
+
end
|
294
|
+
c.eof!
|
295
|
+
when :stdout
|
296
|
+
stdout_copy << d
|
297
|
+
(@buf ||= '') << d
|
298
|
+
while line = @buf.slice!(/(.*)\r?\n/)
|
299
|
+
line.chop.should == "foobar #{i}".gsub('0','X')
|
300
|
+
i += 1
|
301
|
+
end
|
302
|
+
when :exit_code
|
303
|
+
mockie.exit_code()
|
304
|
+
when :exit_signal
|
305
|
+
mockie.exit_signal()
|
306
|
+
when :finish
|
307
|
+
mockie.finish()
|
308
|
+
end
|
309
|
+
end
|
310
|
+
r.stdout.should == stdout_copy
|
311
|
+
r.stderr.should == ''
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
it "handles intermingled stdout/stderr" do
|
316
|
+
mockie = mock(:callbacks)
|
317
|
+
mockie.should_receive(:start).once.ordered
|
318
|
+
mockie.should_receive(:exit_code).once.ordered
|
319
|
+
mockie.should_receive(:finish).once.ordered
|
320
|
+
mockie.should_not_receive(:exit_signal)
|
321
|
+
a = Net::SSH::Simple.sync do
|
322
|
+
stdout_c = 0
|
323
|
+
stderr_c = 0
|
324
|
+
stdout_copy = ''
|
325
|
+
stderr_copy = ''
|
326
|
+
r = ssh('localhost', '/bin/sh') do |e,c,d|
|
327
|
+
case e
|
328
|
+
when :start
|
329
|
+
mockie.start()
|
330
|
+
(1..420).each do |i|
|
331
|
+
c.send_data("echo 'hello stderr' 1>&2\n")
|
332
|
+
c.send_data("echo 'hello stdout'\n")
|
333
|
+
c.send_data("echo 'HELLO STDERR' 1>&2\n")
|
334
|
+
c.send_data("echo 'HELLO STDOUT'\n")
|
335
|
+
end
|
336
|
+
c.eof!
|
337
|
+
when :stdout
|
338
|
+
stdout_copy << d
|
339
|
+
(@buf ||= '') << d
|
340
|
+
while line = @buf.slice!(/(.*)\r?\n/)
|
341
|
+
oddeven = stdout_c % 2
|
342
|
+
case oddeven
|
343
|
+
when 0
|
344
|
+
line.chop.should == "hello stdout"
|
345
|
+
else
|
346
|
+
line.chop.should == "HELLO STDOUT"
|
347
|
+
end
|
348
|
+
stdout_c += 1
|
349
|
+
end
|
350
|
+
when :stderr
|
351
|
+
stderr_copy << d
|
352
|
+
(@buf ||= '') << d
|
353
|
+
while line = @buf.slice!(/(.*)\r?\n/)
|
354
|
+
oddeven = stderr_c % 2
|
355
|
+
case oddeven
|
356
|
+
when 0
|
357
|
+
line.chop.should == "hello stderr"
|
358
|
+
else
|
359
|
+
line.chop.should == "HELLO STDERR"
|
360
|
+
end
|
361
|
+
stderr_c += 1
|
362
|
+
end
|
363
|
+
when :exit_code
|
364
|
+
mockie.exit_code()
|
365
|
+
|
366
|
+
when :exit_signal
|
367
|
+
mockie.exit_signal()
|
368
|
+
|
369
|
+
when :finish
|
370
|
+
stdout_c.should == 840
|
371
|
+
stderr_c.should == 840
|
372
|
+
mockie.finish()
|
373
|
+
end
|
374
|
+
end
|
375
|
+
# result should be populated
|
376
|
+
r.stdout.should == stdout_copy
|
377
|
+
r.stderr.should == stderr_copy
|
378
|
+
r.exit_code.should == 0
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
it "handles signals" do
|
383
|
+
mockie = mock(:callbacks)
|
384
|
+
mockie.should_receive(:start).once.ordered
|
385
|
+
mockie.should_not_receive(:exit_code)
|
386
|
+
mockie.should_receive(:exit_signal).once
|
387
|
+
mockie.should_not_receive(:finish)
|
388
|
+
|
389
|
+
victim = Net::SSH::Simple.async do
|
390
|
+
begin
|
391
|
+
ssh('localhost', 'sleep 1020304157') do |e,c,d|
|
392
|
+
case e
|
393
|
+
when :start
|
394
|
+
mockie.start()
|
395
|
+
when :exit_signal
|
396
|
+
d.should == 'TERM'
|
397
|
+
mockie.exit_signal()
|
398
|
+
when :exit_code
|
399
|
+
mockie.exit_code()
|
400
|
+
when :finish
|
401
|
+
mockie.finish()
|
402
|
+
end
|
403
|
+
end
|
404
|
+
rescue => e
|
405
|
+
e
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
killer = Net::SSH::Simple.async do
|
410
|
+
ssh('localhost', "pkill -f 'sleep 1020304157'")
|
411
|
+
end
|
412
|
+
|
413
|
+
k = killer.value
|
414
|
+
k.success.should == true
|
415
|
+
|
416
|
+
v = victim.value
|
417
|
+
v.to_s.should == 'Killed by SIGTERM @ ["localhost", {}]'
|
418
|
+
end
|
419
|
+
|
420
|
+
it "handles signals (:no_raise)" do
|
421
|
+
mockie = mock(:callbacks)
|
422
|
+
mockie.should_receive(:start).once.ordered
|
423
|
+
mockie.should_not_receive(:exit_code)
|
424
|
+
mockie.should_receive(:exit_signal).once
|
425
|
+
mockie.should_receive(:finish).once.ordered
|
426
|
+
|
427
|
+
victim = Net::SSH::Simple.async do
|
428
|
+
begin
|
429
|
+
ssh('localhost', 'sleep 1020304157') do |e,c,d|
|
430
|
+
case e
|
431
|
+
when :start
|
432
|
+
mockie.start()
|
433
|
+
when :exit_signal
|
434
|
+
d.should == 'TERM'
|
435
|
+
mockie.exit_signal()
|
436
|
+
:no_raise
|
437
|
+
when :exit_code
|
438
|
+
mockie.exit_code()
|
439
|
+
when :finish
|
440
|
+
mockie.finish()
|
441
|
+
end
|
442
|
+
end
|
443
|
+
rescue => e
|
444
|
+
e
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
killer = Net::SSH::Simple.async do
|
449
|
+
ssh('localhost', "pkill -f 'sleep 1020304157'")
|
450
|
+
end
|
451
|
+
|
452
|
+
k = killer.value
|
453
|
+
k.success.should == true
|
454
|
+
|
455
|
+
v = victim.value
|
456
|
+
v.success.should == false
|
457
|
+
v.exit_signal.should == 'TERM'
|
458
|
+
end
|
459
|
+
|
460
|
+
it "respects :no_append" do
|
461
|
+
r = Net::SSH::Simple.sync do
|
462
|
+
stdout_c = 0
|
463
|
+
stderr_c = 0
|
464
|
+
stdout_copy = ''
|
465
|
+
stderr_copy = ''
|
466
|
+
ssh('localhost', '/bin/sh') do |e,c,d|
|
467
|
+
case e
|
468
|
+
when :start
|
469
|
+
c.send_data("echo 'hello stderr' 1>&2\n")
|
470
|
+
c.send_data("echo 'hello stdout'\n")
|
471
|
+
c.send_data("echo 'HELLO STDERR' 1>&2\n")
|
472
|
+
c.send_data("echo 'HELLO STDOUT'\n")
|
473
|
+
c.eof!
|
474
|
+
when :stdout
|
475
|
+
:no_append
|
476
|
+
when :stderr
|
477
|
+
:no_append
|
478
|
+
end
|
479
|
+
end
|
480
|
+
end
|
481
|
+
r.stdout.should == ''
|
482
|
+
r.stderr.should == ''
|
483
|
+
end
|
484
|
+
|
485
|
+
end
|
275
486
|
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 1
|
7
|
-
- 0
|
8
7
|
- 1
|
9
|
-
|
8
|
+
- 0
|
9
|
+
version: 1.1.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Moe
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2011-10-
|
17
|
+
date: 2011-10-25 00:00:00 +02:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -149,7 +149,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
149
149
|
requirements:
|
150
150
|
- - ">="
|
151
151
|
- !ruby/object:Gem::Version
|
152
|
-
hash:
|
152
|
+
hash: -3127829972184651328
|
153
153
|
segments:
|
154
154
|
- 0
|
155
155
|
version: "0"
|
@@ -158,7 +158,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
158
158
|
requirements:
|
159
159
|
- - ">="
|
160
160
|
- !ruby/object:Gem::Version
|
161
|
-
hash:
|
161
|
+
hash: -3127829972184651328
|
162
162
|
segments:
|
163
163
|
- 0
|
164
164
|
version: "0"
|