net-ssh-simple 1.3.2 → 1.4.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/README.rdoc +0 -14
- data/lib/net/ssh/simple/core.rb +52 -23
- data/lib/net/ssh/simple/version.rb +1 -1
- data/spec/net-ssh-simple.rb +68 -5
- metadata +17 -17
data/README.rdoc
CHANGED
@@ -57,21 +57,7 @@ Net::SSH::Simple is a simple wrapper around Net::SSH and Net::SCP.
|
|
57
57
|
s.scp_dl 'example3.com', '/tmp/remote_foo', '/tmp/local_bar'
|
58
58
|
s.close
|
59
59
|
|
60
|
-
=== Handling a Timeout
|
61
60
|
|
62
|
-
require 'net/ssh/simple'
|
63
|
-
|
64
|
-
# Hint: Set timeout=0 to disable, default is 60
|
65
|
-
begin
|
66
|
-
Net::SSH::Simple.sync({:timeout => 5}) do
|
67
|
-
ssh('example1.com', 'sleep 1') # I will (probably) succeed!
|
68
|
-
ssh('example2.com', 'sleep 60') # I will fail :(
|
69
|
-
end
|
70
|
-
rescue Net::SSH::Simple::Error => e
|
71
|
-
puts e.result.timed_out #=> true
|
72
|
-
end
|
73
|
-
|
74
|
-
|
75
61
|
== Documentation
|
76
62
|
|
77
63
|
See {Net::SSH::Simple}[http://rubydoc.info/gems/net-ssh-simple/Net/SSH/Simple] for more examples and full API.
|
data/lib/net/ssh/simple/core.rb
CHANGED
@@ -157,20 +157,6 @@ module Net
|
|
157
157
|
# end
|
158
158
|
#
|
159
159
|
# @example
|
160
|
-
# # Timeout handling
|
161
|
-
# #
|
162
|
-
# # Note: The timeout applies to each command independently.
|
163
|
-
# # Hint: Set timeout=0 to disable, default is 60
|
164
|
-
# begin
|
165
|
-
# Net::SSH::Simple.sync({:timeout => 5}) do
|
166
|
-
# ssh('example1.com', 'sleep 1') # I will (probably) succeed!
|
167
|
-
# ssh('example2.com', 'sleep 60') # I will fail :(
|
168
|
-
# end
|
169
|
-
# rescue Net::SSH::Simple::Error => e
|
170
|
-
# puts e.result.timed_out #=> true
|
171
|
-
# end
|
172
|
-
#
|
173
|
-
# @example
|
174
160
|
# # Using the SCP progress callback
|
175
161
|
# Net::SSH::Simple.sync do
|
176
162
|
# scp_ul 'example1.com', '/tmp/local_foo', '/tmp/remote_bar' do |sent, total|
|
@@ -459,7 +445,17 @@ module Net
|
|
459
445
|
# the max number of packets to process before rekeying
|
460
446
|
#
|
461
447
|
# @option opts [Integer] :timeout (60)
|
462
|
-
#
|
448
|
+
# maximum idle time before a connection will time out (0 = disable).
|
449
|
+
#
|
450
|
+
# @option opts [Integer] :operation_timeout (3600)
|
451
|
+
# maximum time before aborting an operation (0 = disable).
|
452
|
+
# you may use this to guard against run-away processes.
|
453
|
+
#
|
454
|
+
# @option opts [Integer] :close_timeout (5)
|
455
|
+
# grace-period on close before the connection will be terminated forcefully
|
456
|
+
# (0 = terminate immediately).
|
457
|
+
#
|
458
|
+
# @option opts [String] :user
|
463
459
|
#
|
464
460
|
# @option opts [String] :user
|
465
461
|
# the username to log in as
|
@@ -482,26 +478,31 @@ module Net
|
|
482
478
|
with_session(host, opts) do |session|
|
483
479
|
@result = Result.new(
|
484
480
|
{ :op => :ssh, :host => host, :cmd => cmd, :start_at => Time.new,
|
485
|
-
:opts => opts, :stdout => '', :stderr => '',
|
481
|
+
:last_event_at => Time.new, :opts => opts, :stdout => '', :stderr => '',
|
482
|
+
:success => nil
|
486
483
|
} )
|
487
484
|
|
488
485
|
channel = session.open_channel do |chan|
|
489
486
|
chan.exec cmd do |ch, success|
|
490
487
|
@result[:success] = success
|
491
488
|
ch.on_data do |c, data|
|
489
|
+
@result[:last_event_at] = Time.new
|
492
490
|
r = block.call(:stdout, ch, data) if block
|
493
491
|
@result[:stdout] += data.to_s unless r == :no_append
|
494
492
|
end
|
495
493
|
ch.on_extended_data do |c, type, data|
|
494
|
+
@result[:last_event_at] = Time.new
|
496
495
|
r = block.call(:stderr, ch, data) if block
|
497
496
|
@result[:stderr] += data.to_s unless r == :no_append
|
498
497
|
end
|
499
498
|
ch.on_request('exit-status') do |c, data|
|
499
|
+
@result[:last_event_at] = Time.new
|
500
500
|
exit_code = data.read_long
|
501
501
|
block.call(:exit_code, ch, exit_code) if block
|
502
502
|
@result[:exit_code] = exit_code
|
503
503
|
end
|
504
504
|
ch.on_request('exit-signal') do |c, data|
|
505
|
+
@result[:last_event_at] = Time.new
|
505
506
|
exit_signal = data.read_string
|
506
507
|
r = block.call(:exit_signal, ch, exit_signal) if block
|
507
508
|
@result[:exit_signal] = exit_signal
|
@@ -513,7 +514,7 @@ module Net
|
|
513
514
|
block.call(:start, ch, nil) if block
|
514
515
|
end
|
515
516
|
end
|
516
|
-
channel
|
517
|
+
wait_for_channel session, channel, @result, opts[:timeout]
|
517
518
|
@result[:finish_at] = Time.new
|
518
519
|
block.call(:finish, channel, nil) if block
|
519
520
|
@result
|
@@ -561,18 +562,34 @@ module Net
|
|
561
562
|
#
|
562
563
|
def close
|
563
564
|
Thread.current[:ssh_simple_sessions].values.each do |session|
|
564
|
-
|
565
|
+
begin
|
566
|
+
Timeout.timeout(@opts[:close_timeout] || 5) { session.close }
|
567
|
+
rescue => e
|
568
|
+
begin
|
569
|
+
session.shutdown!
|
570
|
+
rescue
|
571
|
+
end
|
572
|
+
end
|
565
573
|
end
|
566
574
|
@result
|
567
575
|
end
|
568
576
|
|
577
|
+
|
569
578
|
private
|
570
|
-
|
579
|
+
EXTRA_OPTS = [:operation_timeout, :close_timeout]
|
580
|
+
|
581
|
+
def with_session(host, opts={}, &block)
|
582
|
+
opts[:timeout] ||= 60
|
583
|
+
opts[:timeout] = 2**32 if opts[:timeout] == 0
|
584
|
+
opts[:operation_timeout] ||= 3600
|
585
|
+
opts[:operation_timeout] = 2**32 if opts[:operation_timeout] == 0
|
586
|
+
opts[:close_timeout] ||= 5
|
571
587
|
begin
|
572
|
-
|
588
|
+
net_ssh_opts = opts.reject{|k,v| EXTRA_OPTS.include? k }
|
589
|
+
Timeout.timeout(opts[:operation_timeout]) do
|
573
590
|
session = Thread.current[:ssh_simple_sessions][host.hash] \
|
574
591
|
= Thread.current[:ssh_simple_sessions][host.hash] \
|
575
|
-
|| Net::SSH.start(*[host, opts[:user],
|
592
|
+
|| Net::SSH.start(*[host, opts[:user], net_ssh_opts])
|
576
593
|
block.call(session)
|
577
594
|
end
|
578
595
|
rescue => e
|
@@ -585,10 +602,20 @@ module Net
|
|
585
602
|
end
|
586
603
|
end
|
587
604
|
|
605
|
+
def wait_for_channel(session, channel, result, timeout)
|
606
|
+
session.loop(1) do
|
607
|
+
if timeout < Time.now - result[:last_event_at]
|
608
|
+
raise Timeout::Error, 'idle timeout'
|
609
|
+
end
|
610
|
+
channel.active?
|
611
|
+
end
|
612
|
+
end
|
613
|
+
|
588
614
|
def scp(mode, host, src, dst, opts={}, &block)
|
589
615
|
@result = Result.new(
|
590
616
|
{ :op => :scp, :host => host, :opts => opts, :cmd => :scp_dl,
|
591
|
-
:
|
617
|
+
:last_event_at => Time.new, :start_at => Time.new,
|
618
|
+
:src => src, :dst => dst, :success => false
|
592
619
|
} )
|
593
620
|
with_session(host, opts) do |session|
|
594
621
|
lt = 0
|
@@ -596,9 +623,10 @@ module Net
|
|
596
623
|
@result[:name] ||= name
|
597
624
|
@result[:total] ||= total
|
598
625
|
@result[:sent] = sent
|
626
|
+
@result[:last_event_at] = Time.new
|
599
627
|
block.call(sent, total) unless block.nil?
|
600
628
|
end
|
601
|
-
channel
|
629
|
+
wait_for_channel session, channel, @result, opts[:timeout]
|
602
630
|
@result[:finish_at] = Time.new
|
603
631
|
@result[:success] = @result[:sent] == @result[:total]
|
604
632
|
@result
|
@@ -634,6 +662,7 @@ module Net
|
|
634
662
|
# @attr [String] cmd Shell command (SSH only)
|
635
663
|
# @attr [Time] start_at Operation start timestamp
|
636
664
|
# @attr [Time] finish_at Operation finish timestamp
|
665
|
+
# @attr [Time] last_event_at Timestamp of last activity
|
637
666
|
# @attr [Boolean] timed_out Set to true if the operation timed out
|
638
667
|
# @attr [String] stdout Output to stdout (SSH only)
|
639
668
|
# @attr [String] stderr Output to stderr (SSH only)
|
data/spec/net-ssh-simple.rb
CHANGED
@@ -42,10 +42,57 @@ describe Net::SSH::Simple do
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
-
it "enforces timeout" do
|
45
|
+
it "enforces idle timeout" do
|
46
46
|
raised = false
|
47
47
|
begin
|
48
48
|
r = Net::SSH::Simple.ssh('localhost', 'sleep 60', {:timeout => 1})
|
49
|
+
rescue => e
|
50
|
+
raised = true
|
51
|
+
e.to_s.should match /^idle timeout @ .*/
|
52
|
+
e.result.op == :ssh
|
53
|
+
e.result.timed_out.should == true
|
54
|
+
end
|
55
|
+
raised.should == true
|
56
|
+
end
|
57
|
+
|
58
|
+
it "enforces operation timeout on ssh" do
|
59
|
+
raised = false
|
60
|
+
begin
|
61
|
+
r = Net::SSH::Simple.ssh('localhost', 'while true; do echo "buh"; sleep 1; done', {:operation_timeout => 2})
|
62
|
+
rescue => e
|
63
|
+
raised = true
|
64
|
+
e.to_s.should match /^execution expired @ .*/
|
65
|
+
e.result.op == :ssh
|
66
|
+
e.result.timed_out.should == true
|
67
|
+
end
|
68
|
+
raised.should == true
|
69
|
+
end
|
70
|
+
|
71
|
+
it "enforces operation timeout on scp_ul" do
|
72
|
+
raised = false
|
73
|
+
begin
|
74
|
+
r = Net::SSH::Simple.scp_ul('localhost', '/tmp/ssh_test_in0',
|
75
|
+
'/tmp/ssh_test_out0', {:operation_timeout=>1}) \
|
76
|
+
do |sent,total|
|
77
|
+
sleep 5
|
78
|
+
end
|
79
|
+
rescue => e
|
80
|
+
raised = true
|
81
|
+
e.to_s.should match /^execution expired @ .*/
|
82
|
+
e.result.op == :ssh
|
83
|
+
e.result.timed_out.should == true
|
84
|
+
end
|
85
|
+
raised.should == true
|
86
|
+
end
|
87
|
+
|
88
|
+
it "enforces operation timeout on scp_dl" do
|
89
|
+
raised = false
|
90
|
+
begin
|
91
|
+
r = Net::SSH::Simple.scp_dl('localhost', '/tmp/ssh_test_in0',
|
92
|
+
'/tmp/ssh_test_out0', {:operation_timeout=>1}) \
|
93
|
+
do |sent,total|
|
94
|
+
sleep 5
|
95
|
+
end
|
49
96
|
rescue => e
|
50
97
|
raised = true
|
51
98
|
e.to_s.should match /^execution expired @ .*/
|
@@ -59,6 +106,10 @@ describe Net::SSH::Simple do
|
|
59
106
|
Net::SSH::Simple.ssh('localhost', 'sleep 2', {:timeout => 0})
|
60
107
|
end
|
61
108
|
|
109
|
+
it "interprets operation_timeout=0 as no timeout" do
|
110
|
+
Net::SSH::Simple.ssh('localhost', 'sleep 2', {:operation_timeout => 0})
|
111
|
+
end
|
112
|
+
|
62
113
|
it "fails gently" do
|
63
114
|
raised = false
|
64
115
|
begin
|
@@ -182,6 +233,13 @@ describe Net::SSH::Simple do
|
|
182
233
|
it "returns a result" do
|
183
234
|
Net::SSH::Simple.sync do
|
184
235
|
ssh('localhost', 'true').success.should == true
|
236
|
+
# see coverage-report to see if session#shutdown! was exercised
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
it "force closes" do
|
241
|
+
Net::SSH::Simple.sync({:close_timeout => true}) do
|
242
|
+
ssh('localhost', 'true').success.should == true
|
185
243
|
end
|
186
244
|
end
|
187
245
|
|
@@ -317,7 +375,7 @@ describe Net::SSH::Simple do
|
|
317
375
|
end
|
318
376
|
|
319
377
|
it "handles signals" do
|
320
|
-
victim = Net::SSH::Simple.async do
|
378
|
+
victim = Net::SSH::Simple.async({:timeout => 10}) do
|
321
379
|
begin
|
322
380
|
ssh('localhost', 'sleep 1020304157')
|
323
381
|
rescue => e
|
@@ -325,12 +383,14 @@ describe Net::SSH::Simple do
|
|
325
383
|
end
|
326
384
|
end
|
327
385
|
|
328
|
-
killer = Net::SSH::Simple.async do
|
386
|
+
killer = Net::SSH::Simple.async({:operation_timeout => 5}) do
|
387
|
+
sleep 1 while 0 != ssh('localhost', "pgrep -f 'sleep 1020304157'").exit_code
|
329
388
|
ssh('localhost', "pkill -f 'sleep 1020304157'")
|
330
389
|
end
|
331
390
|
|
332
391
|
k = killer.value
|
333
392
|
k.success.should == true
|
393
|
+
k.exit_code.should == 0
|
334
394
|
|
335
395
|
v = victim.value
|
336
396
|
v.to_s.should match /Killed by SIGTERM @ .*/
|
@@ -610,7 +670,8 @@ describe Net::SSH::Simple do
|
|
610
670
|
end
|
611
671
|
end
|
612
672
|
|
613
|
-
killer = Net::SSH::Simple.async do
|
673
|
+
killer = Net::SSH::Simple.async({:operation_timeout => 5}) do
|
674
|
+
sleep 1 while 0 != ssh('localhost', "pgrep -f 'sleep 1020304157'").exit_code
|
614
675
|
ssh('localhost', "pkill -f 'sleep 1020304157'")
|
615
676
|
end
|
616
677
|
|
@@ -649,10 +710,12 @@ describe Net::SSH::Simple do
|
|
649
710
|
end
|
650
711
|
end
|
651
712
|
|
652
|
-
killer = Net::SSH::Simple.async do
|
713
|
+
killer = Net::SSH::Simple.async({:operation_timeout => 5}) do
|
714
|
+
sleep 1 while 0 != ssh('localhost', "pgrep -f 'sleep 1020304157'").exit_code
|
653
715
|
ssh('localhost', "pkill -f 'sleep 1020304157'")
|
654
716
|
end
|
655
717
|
|
718
|
+
|
656
719
|
k = killer.value
|
657
720
|
k.success.should == true
|
658
721
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: net-ssh-simple
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-10-
|
12
|
+
date: 2011-10-28 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: net-ssh
|
16
|
-
requirement: &
|
16
|
+
requirement: &9197060 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 2.1.4
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *9197060
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: net-scp
|
27
|
-
requirement: &
|
27
|
+
requirement: &9194900 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: 1.0.4
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *9194900
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: blockenspiel
|
38
|
-
requirement: &
|
38
|
+
requirement: &9193640 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: 0.4.3
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *9193640
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: hashie
|
49
|
-
requirement: &
|
49
|
+
requirement: &9192620 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ~>
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: 1.1.0
|
55
55
|
type: :runtime
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *9192620
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: rake
|
60
|
-
requirement: &
|
60
|
+
requirement: &9191660 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ~>
|
@@ -65,10 +65,10 @@ dependencies:
|
|
65
65
|
version: 0.9.2.2
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *9191660
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rspec
|
71
|
-
requirement: &
|
71
|
+
requirement: &9180700 !ruby/object:Gem::Requirement
|
72
72
|
none: false
|
73
73
|
requirements:
|
74
74
|
- - ! '>='
|
@@ -76,10 +76,10 @@ dependencies:
|
|
76
76
|
version: '0'
|
77
77
|
type: :development
|
78
78
|
prerelease: false
|
79
|
-
version_requirements: *
|
79
|
+
version_requirements: *9180700
|
80
80
|
- !ruby/object:Gem::Dependency
|
81
81
|
name: cover_me
|
82
|
-
requirement: &
|
82
|
+
requirement: &9179600 !ruby/object:Gem::Requirement
|
83
83
|
none: false
|
84
84
|
requirements:
|
85
85
|
- - ! '>='
|
@@ -87,7 +87,7 @@ dependencies:
|
|
87
87
|
version: '0'
|
88
88
|
type: :development
|
89
89
|
prerelease: false
|
90
|
-
version_requirements: *
|
90
|
+
version_requirements: *9179600
|
91
91
|
description: Net::SSH::Simple is a simple wrapper around Net::SSH and Net::SCP.
|
92
92
|
email:
|
93
93
|
- moe@busyloop.net
|
@@ -124,7 +124,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
124
124
|
version: '0'
|
125
125
|
segments:
|
126
126
|
- 0
|
127
|
-
hash:
|
127
|
+
hash: 2108785634158212741
|
128
128
|
requirements: []
|
129
129
|
rubyforge_project:
|
130
130
|
rubygems_version: 1.8.10
|