remote-session 0.0.2 → 0.0.3
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/remote/session/send_file.rb +49 -0
- data/lib/remote/session/version.rb +2 -1
- data/lib/remote/session.rb +66 -35
- data/spec/unit/send_file_spec.rb +110 -0
- data/spec/unit/session_spec.rb +207 -39
- metadata +97 -90
@@ -0,0 +1,49 @@
|
|
1
|
+
module Remote
|
2
|
+
|
3
|
+
class Session
|
4
|
+
|
5
|
+
class SendFile
|
6
|
+
|
7
|
+
attr_accessor :local_path
|
8
|
+
attr_accessor :remote_path
|
9
|
+
attr_accessor :chunk_size
|
10
|
+
|
11
|
+
def initialize( local_path, remote_path )
|
12
|
+
@local_path = local_path
|
13
|
+
@remote_path = remote_path
|
14
|
+
@chunk_size = 1024
|
15
|
+
@file = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def open?
|
19
|
+
! @file.nil?
|
20
|
+
end
|
21
|
+
|
22
|
+
def eof?
|
23
|
+
return true if ! open?
|
24
|
+
@file.eof?
|
25
|
+
end
|
26
|
+
|
27
|
+
def read
|
28
|
+
open if ! open?
|
29
|
+
|
30
|
+
@file.read( @chunk_size )
|
31
|
+
end
|
32
|
+
|
33
|
+
def close
|
34
|
+
return if ! open?
|
35
|
+
@file.close
|
36
|
+
@file = nil
|
37
|
+
end
|
38
|
+
|
39
|
+
def open
|
40
|
+
close if open?
|
41
|
+
@file = File.open( @local_path, 'r' )
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
data/lib/remote/session.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
+
require 'remote/session/send_file'
|
1
2
|
require 'remote/session/version'
|
2
3
|
require 'net/sftp'
|
3
4
|
require 'net/ssh'
|
5
|
+
require 'base64'
|
4
6
|
|
5
7
|
module Remote
|
6
8
|
class Session
|
@@ -41,15 +43,15 @@ module Remote
|
|
41
43
|
puts @session.exec!( command )
|
42
44
|
end
|
43
45
|
|
44
|
-
def sudo(
|
46
|
+
def sudo( commands )
|
45
47
|
raise "Session is closed" if @session.nil?
|
48
|
+
commands = [ *commands ]
|
46
49
|
|
47
|
-
puts "@#{ @host }: sudo #{ command }"
|
48
50
|
@session.open_channel do |ch|
|
49
51
|
ch.request_pty do |ch, success|
|
50
52
|
raise "Could not obtain pty" if ! success
|
51
53
|
|
52
|
-
channel_exec ch,
|
54
|
+
channel_exec ch, commands
|
53
55
|
end
|
54
56
|
end
|
55
57
|
@session.loop
|
@@ -60,22 +62,10 @@ module Remote
|
|
60
62
|
@session = nil
|
61
63
|
end
|
62
64
|
|
63
|
-
def sudo_put( remote_path, &block )
|
64
|
-
temp_path = "/tmp/remote-session.#{ Time.now.to_f }"
|
65
|
-
run "mkdir #{ temp_path }"
|
66
|
-
run "chmod 0700 #{ temp_path }"
|
67
|
-
|
68
|
-
temp_file = File.join( temp_path, File.basename( remote_path ) )
|
69
|
-
put temp_file, &block
|
70
|
-
|
71
|
-
sudo "cp -f #{ temp_file } #{ remote_path }"
|
72
|
-
run "rm -rf #{ temp_path }"
|
73
|
-
end
|
74
|
-
|
75
65
|
def put( remote_path, &block )
|
76
66
|
sftp = Net::SFTP::Session.new( @session ).connect!
|
77
67
|
sftp.file.open( remote_path, 'w' ) do |f|
|
78
|
-
f.
|
68
|
+
f.write block.call
|
79
69
|
end
|
80
70
|
sftp.close_channel
|
81
71
|
end
|
@@ -94,29 +84,70 @@ module Remote
|
|
94
84
|
@session = Net::SSH.start( @host, @username, ssh_options )
|
95
85
|
end
|
96
86
|
|
97
|
-
def channel_exec( ch,
|
98
|
-
ch
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
else
|
105
|
-
prompt_matched = false
|
106
|
-
@prompts.each_pair do | prompt, send |
|
107
|
-
if data =~ Regexp.new( prompt )
|
108
|
-
ch.send_data "#{ send }\n"
|
109
|
-
prompt_matched = true
|
110
|
-
end
|
111
|
-
end
|
112
|
-
puts data if ! prompt_matched
|
113
|
-
end
|
114
|
-
end
|
87
|
+
def channel_exec( ch, commands )
|
88
|
+
ch[ :commands ] = commands
|
89
|
+
|
90
|
+
ch.exec "sudo -k -p '#{ SUDO_PROMPT }' su -" do |ch, success|
|
91
|
+
raise "Could not execute sudo su command" if ! success
|
92
|
+
|
93
|
+
ch.on_data( &method( :on_data ) )
|
115
94
|
|
116
95
|
ch.on_extended_data do |ch, type, data|
|
117
|
-
|
96
|
+
$stderr.puts data
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def on_data( ch, data )
|
102
|
+
if data =~ Regexp.new( SUDO_PROMPT )
|
103
|
+
ch.send_data "#{ @sudo_password }\n"
|
104
|
+
return
|
105
|
+
end
|
106
|
+
|
107
|
+
@prompts.each_pair do | prompt, send |
|
108
|
+
if data =~ Regexp.new( prompt )
|
109
|
+
ch.send_data "#{ send }\n"
|
110
|
+
return
|
118
111
|
end
|
119
112
|
end
|
113
|
+
|
114
|
+
$stdout.write( data )
|
115
|
+
|
116
|
+
if ch[ :commands ].size == 0
|
117
|
+
ch.send_data "exit\n"
|
118
|
+
return
|
119
|
+
end
|
120
|
+
|
121
|
+
command = ch[ :commands ].shift
|
122
|
+
if command.is_a?( Remote::Session::SendFile )
|
123
|
+
send_file_chunk( ch, command )
|
124
|
+
else
|
125
|
+
ch.send_data "#{command}\n"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def send_file_chunk( ch, send_file )
|
130
|
+
if send_file.open?
|
131
|
+
operator = '>>'
|
132
|
+
else
|
133
|
+
send_file.open
|
134
|
+
operator = '>'
|
135
|
+
end
|
136
|
+
|
137
|
+
chunk =
|
138
|
+
if ! send_file.eof?
|
139
|
+
Base64.encode64( send_file.read )
|
140
|
+
else
|
141
|
+
# Handle empty files
|
142
|
+
''
|
143
|
+
end
|
144
|
+
ch.send_data "echo -n '#{ chunk }' | base64 -d #{ operator } #{ send_file.remote_path }\n"
|
145
|
+
|
146
|
+
if send_file.eof?
|
147
|
+
send_file.close
|
148
|
+
else
|
149
|
+
ch[ :commands ].unshift send_file
|
150
|
+
end
|
120
151
|
end
|
121
152
|
|
122
153
|
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
load File.expand_path( '../spec_helper.rb', File.dirname(__FILE__) )
|
3
|
+
|
4
|
+
describe Remote::Session::SendFile do
|
5
|
+
|
6
|
+
context '#initialize' do
|
7
|
+
|
8
|
+
it 'should require two parameters'do
|
9
|
+
expect do
|
10
|
+
Remote::Session::SendFile.new( 'foo' )
|
11
|
+
end.to raise_error( ArgumentError, 'wrong number of arguments (1 for 2)' )
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'attributes' do
|
17
|
+
subject { Remote::Session::SendFile.new( 'foo', 'bar' ) }
|
18
|
+
|
19
|
+
specify( 'local_path' ) { subject.local_path.should == 'foo' }
|
20
|
+
specify( 'remote_path' ) { subject.remote_path.should == 'bar' }
|
21
|
+
specify( 'chunk_size' ) { subject.chunk_size.should == 1024 }
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'instance_methods' do
|
25
|
+
subject { Remote::Session::SendFile.new( '/local/path', '/remote/path' ) }
|
26
|
+
|
27
|
+
context '#open?' do
|
28
|
+
specify { subject.open?.should be_false }
|
29
|
+
end
|
30
|
+
|
31
|
+
context '#eof?' do
|
32
|
+
specify { subject.eof?.should be_true }
|
33
|
+
|
34
|
+
it 'should delegate to the file' do
|
35
|
+
@file = stub( 'file' )
|
36
|
+
File.stub!( :open ).with( '/local/path', 'r' ).and_return( @file )
|
37
|
+
@file.should_receive( :eof? ).and_return( false )
|
38
|
+
|
39
|
+
subject.open
|
40
|
+
|
41
|
+
subject.eof?.should be_false
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context '#close' do
|
46
|
+
|
47
|
+
before :each do
|
48
|
+
@file = stub( 'file' )
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should close the file' do
|
52
|
+
File.stub!( :open ).with( '/local/path', 'r' ).and_return( @file )
|
53
|
+
|
54
|
+
subject.open
|
55
|
+
|
56
|
+
@file.should_receive( :close )
|
57
|
+
|
58
|
+
subject.close
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should do nothing is the file is not open' do
|
62
|
+
File.should_not_receive( :open )
|
63
|
+
@file.should_not_receive( :close )
|
64
|
+
|
65
|
+
subject.close
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
context '#read' do
|
71
|
+
|
72
|
+
before :each do
|
73
|
+
@file = stub( 'file' )
|
74
|
+
File.stub!( :open ).with( '/local/path', 'r' ).and_return( @file )
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'should open the file' do
|
78
|
+
@file.stub!( :read ).and_return( 'aaa' )
|
79
|
+
|
80
|
+
File.should_receive( :open ).with( '/local/path', 'r' ).and_return( @file )
|
81
|
+
|
82
|
+
subject.read
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should fail, if the local file does non exist' do
|
86
|
+
File.stub!( :open ).and_raise( 'Stubbed error' )
|
87
|
+
|
88
|
+
expect do
|
89
|
+
subject.read
|
90
|
+
end.to raise_error
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'should read chunks' do
|
94
|
+
@file.should_receive( :read ).and_return( 'stuff' )
|
95
|
+
|
96
|
+
subject.read.should == 'stuff'
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should read chunk_size bytes at a time' do
|
100
|
+
@file.should_receive( :read ).with( 1024 ).and_return( 'x' * 1024 )
|
101
|
+
|
102
|
+
subject.read.should == 'x' * 1024
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
data/spec/unit/session_spec.rb
CHANGED
@@ -1,8 +1,32 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
load File.expand_path( '../spec_helper.rb', File.dirname(__FILE__) )
|
3
3
|
|
4
|
+
module SpecOutputCapture
|
5
|
+
|
6
|
+
def expect_output
|
7
|
+
stdout = []
|
8
|
+
@rs.stub!( :puts ) do | s |
|
9
|
+
stdout << "{ s }\n"
|
10
|
+
end
|
11
|
+
$stdout.stub!( :write ) do | s |
|
12
|
+
stdout << s
|
13
|
+
end
|
14
|
+
stderr = []
|
15
|
+
$stderr.stub!( :write ) do | s |
|
16
|
+
stderr << s
|
17
|
+
end
|
18
|
+
|
19
|
+
yield
|
20
|
+
|
21
|
+
[ stdout, stderr ]
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
4
26
|
describe Remote::Session do
|
5
27
|
|
28
|
+
include SpecOutputCapture
|
29
|
+
|
6
30
|
TEST_HOST = 'host.example.com'
|
7
31
|
|
8
32
|
context 'initialization' do
|
@@ -125,6 +149,17 @@ describe Remote::Session do
|
|
125
149
|
|
126
150
|
context '#sudo' do
|
127
151
|
|
152
|
+
before :each do
|
153
|
+
@ch = stub( 'channel' )
|
154
|
+
@commands = {}
|
155
|
+
@ch.stub!( :[]= ) do | k, v |
|
156
|
+
@commands[ k ] = v
|
157
|
+
end
|
158
|
+
@ch.stub!( :[] ) do | k |
|
159
|
+
@commands[ k ]
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
128
163
|
subject { Remote::Session.new( TEST_HOST ) }
|
129
164
|
|
130
165
|
it 'should fail, if the session is closed' do
|
@@ -136,28 +171,17 @@ describe Remote::Session do
|
|
136
171
|
end.to raise_error( RuntimeError, 'Session is closed' )
|
137
172
|
end
|
138
173
|
|
139
|
-
it 'should
|
140
|
-
@ssh.stub!( :open_channel => nil )
|
141
|
-
@ssh.stub!( :loop => nil )
|
142
|
-
|
143
|
-
subject.should_receive( :puts ).with( "@#{TEST_HOST}: sudo pwd" )
|
144
|
-
|
145
|
-
subject.sudo( 'pwd' )
|
146
|
-
end
|
147
|
-
|
148
|
-
it 'should run the command' do
|
174
|
+
it 'should run the su command' do
|
149
175
|
subject.stub!( :puts )
|
150
176
|
|
151
177
|
@ssh.should_receive( :open_channel ) do |&open_channel_block|
|
152
|
-
@
|
153
|
-
|
154
|
-
@channel.should_receive( :request_pty ) do |&request_pty_block|
|
155
|
-
request_pty_block.call( @channel, true )
|
178
|
+
@ch.should_receive( :request_pty ) do |&request_pty_block|
|
179
|
+
request_pty_block.call( @ch, true )
|
156
180
|
end
|
157
181
|
|
158
|
-
@
|
182
|
+
@ch.should_receive( :exec ).with( "sudo -k -p 'sudo_prompt' su -" )
|
159
183
|
|
160
|
-
open_channel_block.call @
|
184
|
+
open_channel_block.call @ch
|
161
185
|
end
|
162
186
|
@ssh.should_receive( :loop )
|
163
187
|
|
@@ -171,7 +195,6 @@ describe Remote::Session do
|
|
171
195
|
@rs.stub!( :puts )
|
172
196
|
|
173
197
|
@ssh.stub!( :loop => nil )
|
174
|
-
@ch = stub( 'channel' )
|
175
198
|
@ssh.stub!( :open_channel ) do |&block|
|
176
199
|
block.call @ch
|
177
200
|
end
|
@@ -195,23 +218,51 @@ describe Remote::Session do
|
|
195
218
|
end
|
196
219
|
end
|
197
220
|
|
198
|
-
it 'should fail if the command fails' do
|
221
|
+
it 'should fail if the sudo command fails' do
|
199
222
|
@ch.stub!( :exec ) do |&block|
|
200
223
|
expect do
|
201
224
|
block.call( @ch, false )
|
202
|
-
end.to raise_error( RuntimeError, 'Could not execute sudo command
|
225
|
+
end.to raise_error( RuntimeError, 'Could not execute sudo su command' )
|
203
226
|
end
|
204
227
|
|
205
228
|
@rs.sudo( 'pwd' )
|
206
229
|
end
|
207
230
|
|
208
231
|
context 'in exec' do
|
232
|
+
|
209
233
|
before :each do
|
210
234
|
@ch.stub!( :exec ) do |&block|
|
211
235
|
block.call( @ch, true )
|
212
236
|
end
|
213
237
|
@ch.stub!( :on_extended_data => nil )
|
214
238
|
@ch.stub!( :send_data )
|
239
|
+
$stdout.stub!( :write => nil )
|
240
|
+
end
|
241
|
+
|
242
|
+
it 'should print the command to stdout' do
|
243
|
+
@ch.stub!( :on_data ) do |&block|
|
244
|
+
block.call( @ch, 'the_prompt' )
|
245
|
+
end
|
246
|
+
|
247
|
+
@ch.should_receive( :send_data ).with( "pwd\n" )
|
248
|
+
|
249
|
+
subject.sudo( 'pwd' )
|
250
|
+
end
|
251
|
+
|
252
|
+
it 'should run multiple commands' do
|
253
|
+
@ch.stub!( :on_data ) do |&block|
|
254
|
+
block.call( @ch, 'the_prompt' )
|
255
|
+
block.call( @ch, 'the_prompt' )
|
256
|
+
block.call( @ch, 'the_prompt' )
|
257
|
+
block.call( @ch, 'the_prompt' )
|
258
|
+
end
|
259
|
+
|
260
|
+
sent = []
|
261
|
+
@ch.stub!( :send_data ) { | s | sent << s }
|
262
|
+
|
263
|
+
subject.sudo( [ 'pwd', 'cd /etc', 'ls' ] )
|
264
|
+
|
265
|
+
sent.should == [ "pwd\n", "cd /etc\n", "ls\n", "exit\n" ]
|
215
266
|
end
|
216
267
|
|
217
268
|
it 'should output returning data' do
|
@@ -219,15 +270,99 @@ describe Remote::Session do
|
|
219
270
|
block.call( @ch, 'some_data' )
|
220
271
|
end
|
221
272
|
|
222
|
-
|
273
|
+
expect_output do
|
274
|
+
@rs.sudo( 'pwd' )
|
275
|
+
end.should == [ [ 'some_data' ], [] ]
|
276
|
+
end
|
277
|
+
|
278
|
+
context 'sending files' do
|
279
|
+
|
280
|
+
before :each do
|
281
|
+
@sf = stub( 'Remote::Session::SendFile instance', :open => nil, :close => nil )
|
282
|
+
end
|
283
|
+
|
284
|
+
it 'should copy files' do
|
285
|
+
@ch.stub!( :on_data ) do |&block|
|
286
|
+
block.call( @ch, 'root_prompt#' )
|
287
|
+
block.call( @ch, 'root_prompt#' )
|
288
|
+
block.call( @ch, 'root_prompt#' )
|
289
|
+
end
|
290
|
+
|
291
|
+
@sf.should_receive( :is_a? ).with( Remote::Session::SendFile ).twice.and_return( true )
|
292
|
+
@sf.should_receive( :remote_path ).twice.and_return( '/remote/path' )
|
293
|
+
|
294
|
+
chunk = 0
|
295
|
+
open = false
|
296
|
+
@sf.should_receive( :open ) do
|
297
|
+
chunk = 1
|
298
|
+
open = true
|
299
|
+
end
|
300
|
+
|
301
|
+
@sf.stub!( :open? ) do
|
302
|
+
open
|
303
|
+
end
|
304
|
+
|
305
|
+
@sf.stub!( :eof? ) do
|
306
|
+
case chunk
|
307
|
+
when 0
|
308
|
+
true
|
309
|
+
when 1, 2
|
310
|
+
false
|
311
|
+
else
|
312
|
+
true
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
data = [ nil, 'first_chunk', 'second_chunk' ]
|
317
|
+
@sf.should_receive( :read ).twice do
|
318
|
+
d = data[ chunk ]
|
319
|
+
chunk += 1
|
320
|
+
d
|
321
|
+
end
|
322
|
+
|
323
|
+
sent = []
|
324
|
+
@ch.stub!( :send_data ) { | d | sent << d }
|
325
|
+
|
326
|
+
@rs.sudo( @sf )
|
327
|
+
|
328
|
+
sent.should == [
|
329
|
+
"echo -n 'Zmlyc3RfY2h1bms=\n' | base64 -d > /remote/path\n",
|
330
|
+
"echo -n 'c2Vjb25kX2NodW5r\n' | base64 -d >> /remote/path\n",
|
331
|
+
"exit\n"
|
332
|
+
]
|
333
|
+
end
|
334
|
+
|
335
|
+
it 'should copy empty files' do
|
336
|
+
@ch.stub!( :on_data ) do |&block|
|
337
|
+
block.call( @ch, 'root_prompt#' )
|
338
|
+
block.call( @ch, 'root_prompt#' )
|
339
|
+
end
|
340
|
+
|
341
|
+
@sf.stub!( :is_a? ).with( Remote::Session::SendFile ).and_return( true )
|
342
|
+
@sf.stub!( :open? => false )
|
343
|
+
@sf.stub!( :remote_path ).and_return( '/remote/path' )
|
344
|
+
@sf.stub!( :eof? => true )
|
345
|
+
|
346
|
+
sent = []
|
347
|
+
@ch.stub!( :send_data ) { | d | sent << d }
|
348
|
+
|
223
349
|
|
224
|
-
|
350
|
+
@rs.sudo( @sf )
|
351
|
+
|
352
|
+
sent.should == [
|
353
|
+
"echo -n '' | base64 -d > /remote/path\n",
|
354
|
+
"exit\n"
|
355
|
+
]
|
356
|
+
end
|
357
|
+
|
225
358
|
end
|
226
359
|
|
227
360
|
context 'with password prompt' do
|
228
361
|
before :each do
|
362
|
+
$stdout.stub( :write )
|
229
363
|
@ch.stub!( :on_data ) do |&block|
|
230
364
|
block.call( @ch, 'sudo_prompt' )
|
365
|
+
block.call( @ch, 'root#' )
|
231
366
|
end
|
232
367
|
end
|
233
368
|
|
@@ -238,21 +373,18 @@ describe Remote::Session do
|
|
238
373
|
end
|
239
374
|
|
240
375
|
it 'should not echo the standard prompt' do
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
end
|
245
|
-
|
246
|
-
@rs.sudo( 'pwd' )
|
247
|
-
|
248
|
-
output.should == ["@#{TEST_HOST}: sudo pwd"]
|
376
|
+
expect_output do
|
377
|
+
@rs.sudo( 'pwd' )
|
378
|
+
end.should == [ [ 'root#' ], [] ]
|
249
379
|
end
|
250
380
|
end
|
251
381
|
|
252
382
|
context 'with user-supplied prompt' do
|
253
383
|
before :each do
|
384
|
+
$stdout.stub( :write )
|
254
385
|
@ch.stub!( :on_data ) do |&block|
|
255
386
|
block.call( @ch, 'Here is my prompt:' )
|
387
|
+
block.call( @ch, 'root#' )
|
256
388
|
end
|
257
389
|
end
|
258
390
|
|
@@ -264,29 +396,25 @@ describe Remote::Session do
|
|
264
396
|
end
|
265
397
|
|
266
398
|
it 'should not echo the prompt' do
|
267
|
-
output = []
|
268
|
-
@rs.stub!( :puts ) do | s |
|
269
|
-
output << s
|
270
|
-
end
|
271
399
|
@rs.prompts[ 'my prompt' ] = 'this data'
|
272
400
|
|
273
|
-
|
274
|
-
|
275
|
-
|
401
|
+
expect_output do
|
402
|
+
@rs.sudo( 'pwd' )
|
403
|
+
end.should == [ [ 'root#' ], [] ]
|
276
404
|
end
|
277
405
|
|
278
406
|
end
|
279
407
|
|
280
|
-
it 'should
|
408
|
+
it 'should send error data to stdout' do
|
281
409
|
@ch.stub!( :on_data )
|
282
410
|
|
283
411
|
@ch.stub!( :on_extended_data ) do |&block|
|
284
412
|
block.call @ch, 'foo', 'It failed'
|
285
413
|
end
|
286
414
|
|
287
|
-
|
415
|
+
expect_output do
|
288
416
|
@rs.sudo( 'pwd' )
|
289
|
-
end.
|
417
|
+
end.should == [ [], [ "It failed", "\n" ] ]
|
290
418
|
end
|
291
419
|
|
292
420
|
end
|
@@ -297,6 +425,46 @@ describe Remote::Session do
|
|
297
425
|
|
298
426
|
end
|
299
427
|
|
428
|
+
context '#put' do
|
429
|
+
|
430
|
+
before :each do
|
431
|
+
@file2 = stub( 'file2' )
|
432
|
+
@file1 = stub( 'file1', :open => lambda { |&block| block.call @file2 } )
|
433
|
+
@sftp2 = stub( 'Net::SFTP::Session instance', :file => @file1,
|
434
|
+
:close_channel => nil )
|
435
|
+
@sftp1 = stub( 'Net::SFTP::Session instance', :connect! => @sftp2 )
|
436
|
+
Net::SFTP::Session.stub!( :new => @sftp1 )
|
437
|
+
end
|
438
|
+
|
439
|
+
subject { Remote::Session.new( TEST_HOST ) }
|
440
|
+
|
441
|
+
it 'creates an SFTP session' do
|
442
|
+
Net::SFTP::Session.should_receive( :new ).and_return( @sftp1 )
|
443
|
+
@sftp1.should_receive( :connect! ).once.and_return( @sftp2 )
|
444
|
+
|
445
|
+
subject.put( '/path' ) {}
|
446
|
+
end
|
447
|
+
|
448
|
+
it 'opens the file' do
|
449
|
+
@file1.should_receive( :open ) do | *args, &block |
|
450
|
+
args.should == ["/path", "w"]
|
451
|
+
end
|
452
|
+
|
453
|
+
subject.put( '/path' ) { 'content' }
|
454
|
+
end
|
455
|
+
|
456
|
+
it 'writes the data to the file' do
|
457
|
+
@file1.stub!( :open ) do | *args, &block |
|
458
|
+
block.call @file2
|
459
|
+
end
|
460
|
+
|
461
|
+
@file2.should_receive( :write ).with( 'content' )
|
462
|
+
|
463
|
+
subject.put( '/path' ) { 'content' }
|
464
|
+
end
|
465
|
+
|
466
|
+
end
|
467
|
+
|
300
468
|
end
|
301
469
|
|
302
470
|
end
|
metadata
CHANGED
@@ -1,107 +1,108 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: remote-session
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 25
|
5
5
|
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 3
|
10
|
+
version: 0.0.3
|
6
11
|
platform: ruby
|
7
|
-
authors:
|
12
|
+
authors:
|
8
13
|
- Joe Yates
|
9
14
|
autorequire:
|
10
15
|
bindir: bin
|
11
16
|
cert_chain: []
|
12
|
-
|
13
|
-
|
14
|
-
|
17
|
+
|
18
|
+
date: 2012-05-20 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
prerelease: false
|
15
22
|
name: rake
|
16
|
-
|
23
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
17
24
|
none: false
|
18
|
-
requirements:
|
19
|
-
- -
|
20
|
-
- !ruby/object:Gem::Version
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 49
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
- 8
|
32
|
+
- 7
|
21
33
|
version: 0.8.7
|
34
|
+
requirement: *id001
|
22
35
|
type: :runtime
|
36
|
+
- !ruby/object:Gem::Dependency
|
23
37
|
prerelease: false
|
24
|
-
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
|
-
requirements:
|
27
|
-
- - ! '>='
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
version: 0.8.7
|
30
|
-
- !ruby/object:Gem::Dependency
|
31
38
|
name: net-sftp
|
32
|
-
|
39
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
33
40
|
none: false
|
34
|
-
requirements:
|
35
|
-
- -
|
36
|
-
- !ruby/object:Gem::Version
|
37
|
-
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 3
|
45
|
+
segments:
|
46
|
+
- 0
|
47
|
+
version: "0"
|
48
|
+
requirement: *id002
|
38
49
|
type: :runtime
|
50
|
+
- !ruby/object:Gem::Dependency
|
39
51
|
prerelease: false
|
40
|
-
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
|
-
requirements:
|
43
|
-
- - ! '>='
|
44
|
-
- !ruby/object:Gem::Version
|
45
|
-
version: '0'
|
46
|
-
- !ruby/object:Gem::Dependency
|
47
52
|
name: net-ssh
|
48
|
-
|
53
|
+
version_requirements: &id003 !ruby/object:Gem::Requirement
|
49
54
|
none: false
|
50
|
-
requirements:
|
51
|
-
- -
|
52
|
-
- !ruby/object:Gem::Version
|
53
|
-
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
hash: 3
|
59
|
+
segments:
|
60
|
+
- 0
|
61
|
+
version: "0"
|
62
|
+
requirement: *id003
|
54
63
|
type: :runtime
|
64
|
+
- !ruby/object:Gem::Dependency
|
55
65
|
prerelease: false
|
56
|
-
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
|
-
requirements:
|
59
|
-
- - ! '>='
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
62
|
-
- !ruby/object:Gem::Dependency
|
63
66
|
name: rspec
|
64
|
-
|
67
|
+
version_requirements: &id004 !ruby/object:Gem::Requirement
|
65
68
|
none: false
|
66
|
-
requirements:
|
67
|
-
- -
|
68
|
-
- !ruby/object:Gem::Version
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
hash: 3
|
73
|
+
segments:
|
74
|
+
- 2
|
75
|
+
- 3
|
76
|
+
- 0
|
69
77
|
version: 2.3.0
|
78
|
+
requirement: *id004
|
70
79
|
type: :development
|
80
|
+
- !ruby/object:Gem::Dependency
|
71
81
|
prerelease: false
|
72
|
-
|
73
|
-
|
74
|
-
requirements:
|
75
|
-
- - ! '>='
|
76
|
-
- !ruby/object:Gem::Version
|
77
|
-
version: 2.3.0
|
78
|
-
- !ruby/object:Gem::Dependency
|
79
|
-
name: simplecov
|
80
|
-
requirement: !ruby/object:Gem::Requirement
|
82
|
+
name: rcov
|
83
|
+
version_requirements: &id005 !ruby/object:Gem::Requirement
|
81
84
|
none: false
|
82
|
-
requirements:
|
83
|
-
- -
|
84
|
-
- !ruby/object:Gem::Version
|
85
|
-
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
hash: 3
|
89
|
+
segments:
|
90
|
+
- 0
|
91
|
+
version: "0"
|
92
|
+
requirement: *id005
|
86
93
|
type: :development
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
- - ! '>='
|
92
|
-
- !ruby/object:Gem::Version
|
93
|
-
version: '0'
|
94
|
-
description: ! 'This gem uses Net::SSH to create a connection and allow command execution
|
95
|
-
over it.
|
96
|
-
|
97
|
-
Run commands as the logged on user, or via sudo as any permitetd user (defaults
|
98
|
-
to root).'
|
99
|
-
email:
|
94
|
+
description: |-
|
95
|
+
This gem uses Net::SSH to create a connection and allow command execution over it.
|
96
|
+
Run commands as the logged on user, or via sudo as any permitetd user (defaults to root).
|
97
|
+
email:
|
100
98
|
- joe.g.yates@gmail.com
|
101
99
|
executables: []
|
100
|
+
|
102
101
|
extensions: []
|
102
|
+
|
103
103
|
extra_rdoc_files: []
|
104
|
-
|
104
|
+
|
105
|
+
files:
|
105
106
|
- .gitignore
|
106
107
|
- .travis.yml
|
107
108
|
- Gemfile
|
@@ -109,42 +110,48 @@ files:
|
|
109
110
|
- README.md
|
110
111
|
- Rakefile
|
111
112
|
- lib/remote/session.rb
|
113
|
+
- lib/remote/session/send_file.rb
|
112
114
|
- lib/remote/session/version.rb
|
113
115
|
- remote-session.gemspec
|
114
116
|
- spec/gather_rspec_coverage.rb
|
115
117
|
- spec/spec_helper.rb
|
118
|
+
- spec/unit/send_file_spec.rb
|
116
119
|
- spec/unit/session_spec.rb
|
117
|
-
homepage:
|
120
|
+
homepage: ""
|
118
121
|
licenses: []
|
122
|
+
|
119
123
|
post_install_message:
|
120
124
|
rdoc_options: []
|
121
|
-
|
125
|
+
|
126
|
+
require_paths:
|
122
127
|
- lib
|
123
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
128
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
124
129
|
none: false
|
125
|
-
requirements:
|
126
|
-
- -
|
127
|
-
- !ruby/object:Gem::Version
|
128
|
-
|
129
|
-
segments:
|
130
|
+
requirements:
|
131
|
+
- - ">="
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
hash: 3
|
134
|
+
segments:
|
130
135
|
- 0
|
131
|
-
|
132
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
136
|
+
version: "0"
|
137
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
133
138
|
none: false
|
134
|
-
requirements:
|
135
|
-
- -
|
136
|
-
- !ruby/object:Gem::Version
|
137
|
-
|
138
|
-
segments:
|
139
|
+
requirements:
|
140
|
+
- - ">="
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
hash: 3
|
143
|
+
segments:
|
139
144
|
- 0
|
140
|
-
|
145
|
+
version: "0"
|
141
146
|
requirements: []
|
147
|
+
|
142
148
|
rubyforge_project: nowarning
|
143
149
|
rubygems_version: 1.8.24
|
144
150
|
signing_key:
|
145
151
|
specification_version: 3
|
146
152
|
summary: Run user commands, and sudo, commands over an SSH connection
|
147
|
-
test_files:
|
153
|
+
test_files:
|
148
154
|
- spec/gather_rspec_coverage.rb
|
149
155
|
- spec/spec_helper.rb
|
156
|
+
- spec/unit/send_file_spec.rb
|
150
157
|
- spec/unit/session_spec.rb
|