remote-session 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -8,7 +8,9 @@ require 'base64'
8
8
 
9
9
  module Remote
10
10
  class Session
11
- SUDO_PROMPT = 'sudo_prompt'
11
+ SUDO_PASSWORD_PROMPT = 'remote-session-sudo-prompt'
12
+ ROOT_COMMAND_PROMPT = 'remote-session-prompt#'
13
+ ROOT_COMMAND_PROMPT_MATCH = /#{ ROOT_COMMAND_PROMPT }$/
12
14
 
13
15
  def self.open( host, options = {}, &block )
14
16
  rs = new( host, options )
@@ -47,7 +49,7 @@ module Remote
47
49
 
48
50
  def sudo( commands )
49
51
  raise "Session is closed" if @session.nil?
50
- commands = [ *commands ] + [ 'exit' ]
52
+ commands = [ *commands ]
51
53
 
52
54
  @session.open_channel do |ch|
53
55
  ch.request_pty do |ch, success|
@@ -89,10 +91,10 @@ module Remote
89
91
  def channel_exec( ch, commands )
90
92
  ch[ :commands ] = commands
91
93
 
92
- ch.exec "sudo -k -p '#{ SUDO_PROMPT }' su -" do |ch, success|
94
+ ch.exec "sudo -k -p '#{ SUDO_PASSWORD_PROMPT }' su -" do |ch, success|
93
95
  raise "Could not execute sudo su command" if ! success
94
96
 
95
- ch.on_data( &method( :on_data ) )
97
+ ch.on_data &method( :handle_sudo_password_prompt )
96
98
 
97
99
  ch.on_extended_data do |ch, type, data|
98
100
  $stderr.puts data
@@ -100,11 +102,35 @@ module Remote
100
102
  end
101
103
  end
102
104
 
103
- def on_data( ch, data )
104
- if data =~ Regexp.new( SUDO_PROMPT )
105
+ def handle_sudo_password_prompt( ch, data )
106
+ $stdout.write data
107
+
108
+ if data =~ Regexp.new( SUDO_PASSWORD_PROMPT )
105
109
  ch.send_data "#{ @sudo_password }\n"
106
- return
110
+ ch.on_data &method( :set_command_prompt )
107
111
  end
112
+ end
113
+
114
+ # Set the root command prompt to something we can
115
+ # recognise, and wait until that prompt comes back
116
+ def set_command_prompt( ch, data )
117
+ $stdout.write data
118
+
119
+ if data =~ ROOT_COMMAND_PROMPT_MATCH
120
+ # Got it, now we can switch so sending commands
121
+ ch[ :awaiting_prompt ] = false
122
+ ch.on_data &method( :on_data )
123
+ do_command ch, data
124
+ elsif ! ch[ :awaiting_prompt ]
125
+ # this is the first time through...
126
+ ch[ :awaiting_prompt ] = true
127
+ ch.send_data "export PS1='#{ ROOT_COMMAND_PROMPT }'"
128
+ # else: Waiting for new root prompt
129
+ end
130
+ end
131
+
132
+ def on_data( ch, data )
133
+ $stdout.write data
108
134
 
109
135
  @prompts.each_pair do | prompt, send |
110
136
  if data =~ Regexp.new( prompt )
@@ -113,15 +139,21 @@ module Remote
113
139
  end
114
140
  end
115
141
 
116
- $stdout.write( data )
117
-
118
- return if ch[ :commands ].size == 0
142
+ if data =~ ROOT_COMMAND_PROMPT_MATCH
143
+ do_command ch, data
144
+ end
145
+ end
119
146
 
120
- command = ch[ :commands ].shift
121
- if command.is_a?( Remote::Session::Send )
122
- send_file_chunk( ch, command )
147
+ def do_command( ch, data )
148
+ if ch[ :commands ].size > 0
149
+ command = ch[ :commands ].shift
150
+ if command.is_a?( Remote::Session::Send )
151
+ send_file_chunk( ch, command )
152
+ else
153
+ ch.send_data "#{command}\n"
154
+ end
123
155
  else
124
- ch.send_data "#{command}\n"
156
+ ch.send_data "exit\n"
125
157
  end
126
158
  end
127
159
 
@@ -2,7 +2,7 @@ module Remote
2
2
  class Session
3
3
  MAJOR = 0
4
4
  MINOR = 0
5
- REVISION = 4
5
+ REVISION = 5
6
6
  VERSION = [ MAJOR, MINOR, REVISION ].map( &:to_s ).join( '.' )
7
7
  end
8
8
  end
@@ -6,19 +6,28 @@ module SpecOutputCapture
6
6
  def expect_output
7
7
  stdout = []
8
8
  @rs.stub!( :puts ) do | s |
9
- stdout << "{ s }\n"
9
+ stdout << s + "\n"
10
+ end
11
+ $stdout.stub!( :puts ) do | s |
12
+ stdout << s + "\n"
10
13
  end
11
14
  $stdout.stub!( :write ) do | s |
12
15
  stdout << s
13
16
  end
14
17
  stderr = []
18
+ $stderr.stub!( :puts ) do | s |
19
+ stderr << s + "\n"
20
+ end
15
21
  $stderr.stub!( :write ) do | s |
16
22
  stderr << s
17
23
  end
18
24
 
19
25
  yield
20
26
 
21
- [ stdout, stderr ]
27
+ $stdout.rspec_reset
28
+ $stderr.rspec_reset
29
+
30
+ { :stdout => stdout, :stderr => stderr }
22
31
  end
23
32
 
24
33
  end
@@ -179,7 +188,7 @@ describe Remote::Session do
179
188
  request_pty_block.call( @ch, true )
180
189
  end
181
190
 
182
- @ch.should_receive( :exec ).with( "sudo -k -p 'sudo_prompt' su -" )
191
+ @ch.should_receive( :exec ).with( "sudo -k -p 'remote-session-sudo-prompt' su -" )
183
192
 
184
193
  open_channel_block.call @ch
185
194
  end
@@ -234,187 +243,182 @@ describe Remote::Session do
234
243
  @ch.stub!( :exec ) do |&block|
235
244
  block.call( @ch, true )
236
245
  end
246
+ @ch.stub!( :send_data => nil )
237
247
  @ch.stub!( :on_extended_data => nil )
238
- @ch.stub!( :send_data )
239
- $stdout.stub!( :write => nil )
240
- end
248
+ $stdout.stub( :write => nil )
241
249
 
242
- it 'should print the command to stdout' do
250
+ # For each call to Channel.on_data, @@data contains the strings
251
+ # to send back to the block
252
+ @@data = [ [ 'remote-session-sudo-prompt' ], # channel_exec call
253
+ [ 'any prompt#', 'remote-session-prompt#' ], # handle_sudo_password_prompt call
254
+ [ 'remote-session-prompt#', 'remote-session-prompt#', 'remote-session-prompt#' ] ]
243
255
  @ch.stub!( :on_data ) do |&block|
244
- block.call( @ch, 'the_prompt' )
256
+ @@data.shift.each { |response| block.call @ch, response }
245
257
  end
258
+ end
246
259
 
247
- @ch.should_receive( :send_data ).with( "pwd\n" )
260
+ it 'should supply the sudo password, when prompted' do
261
+ @ch.should_receive( :send_data ).with( "secret\n" )
248
262
 
249
- subject.sudo( 'pwd' )
263
+ @rs.sudo( 'pwd' )
250
264
  end
251
265
 
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' )
266
+ it 'should echo the sudo prompt' do
267
+ output = expect_output do
268
+ @rs.sudo( 'pwd' )
258
269
  end
270
+
271
+ output[ :stdout ][ 0 ].should == 'remote-session-sudo-prompt'
272
+ end
259
273
 
260
- sent = []
261
- @ch.stub!( :send_data ) { | s | sent << s }
262
-
263
- subject.sudo( [ 'pwd', 'cd /etc', 'ls' ] )
274
+ context 'after sudo prompt' do
264
275
 
265
- sent.should == [ "pwd\n", "cd /etc\n", "ls\n", "exit\n" ]
266
- end
276
+ it 'should set the root command prompt' do
277
+ @ch.should_receive( :send_data ).with( "export PS1='remote-session-prompt#'" )
267
278
 
268
- it 'should output returning data' do
269
- @ch.stub!( :on_data ) do |&block|
270
- block.call( @ch, 'some_data' )
279
+ @rs.sudo( 'pwd' )
271
280
  end
272
281
 
273
- expect_output do
274
- @rs.sudo( 'pwd' )
275
- end.should == [ [ 'some_data' ], [] ]
276
- end
282
+ it 'should echo until the prompt is set' do
283
+ @@data[ 1 ] = [ 'prompt#', 'stuff1', 'stuff2', 'remote-session-prompt#' ]
277
284
 
278
- context 'sending files' do
285
+ output = expect_output do
286
+ @rs.sudo( 'pwd' )
287
+ end
279
288
 
280
- before :each do
281
- @sf = stub( 'Remote::Session::SendFile instance', :open => nil, :close => nil )
289
+ output[ :stdout ].should include 'stuff1'
290
+ output[ :stdout ].should include 'stuff2'
282
291
  end
283
292
 
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
293
+ context 'with special command prompt' do
290
294
 
291
- @sf.should_receive( :is_a? ).with( Remote::Session::Send ).twice.and_return( true )
292
- @sf.should_receive( :remote_path ).twice.and_return( '/remote/path' )
295
+ it 'should send the command' do
296
+ @ch.should_receive( :send_data ).with( "pwd\n" )
293
297
 
294
- chunk = 0
295
- open = false
296
- @sf.should_receive( :open ) do
297
- chunk = 1
298
- open = true
298
+ subject.sudo( 'pwd' )
299
299
  end
300
300
 
301
- @sf.stub!( :open? ) do
302
- open
301
+ it 'should run multiple commands' do
302
+ sent = []
303
+ @ch.stub!( :send_data ) { | s | sent << s }
304
+
305
+ subject.sudo( [ 'pwd', 'cd /etc', 'ls' ] )
306
+
307
+ sent[-4..-1].should == [ "pwd\n", "cd /etc\n", "ls\n", "exit\n" ]
303
308
  end
304
309
 
305
- @sf.stub!( :eof? ) do
306
- case chunk
307
- when 0
308
- true
309
- when 1, 2
310
- false
311
- else
312
- true
310
+ it 'should output returning data' do
311
+ @@data[ 2 ] = [ 'remote-session-prompt#', 'some_data', 'remote-session-prompt#' ]
312
+ output = expect_output do
313
+ @rs.sudo( 'pwd' )
313
314
  end
314
- end
315
315
 
316
- data = [ nil, 'first_chunk', 'second_chunk' ]
317
- @sf.should_receive( :read ).twice do
318
- d = data[ chunk ]
319
- chunk += 1
320
- d
316
+ output[ :stdout ].should include 'some_data'
321
317
  end
322
318
 
323
- sent = []
324
- @ch.stub!( :send_data ) { | d | sent << d }
325
-
326
- @rs.sudo( @sf )
319
+ context 'sending files' do
327
320
 
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
321
+ before :each do
322
+ @sf = stub( 'Remote::Session::SendFile instance', :open => nil, :close => nil )
323
+ end
334
324
 
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
325
+ it 'should copy files' do
326
+ @sf.should_receive( :is_a? ).with( Remote::Session::Send ).twice.and_return( true )
327
+ @sf.should_receive( :remote_path ).twice.and_return( '/remote/path' )
328
+
329
+ chunk = 0
330
+ open = false
331
+ @sf.should_receive( :open ) do
332
+ chunk = 1
333
+ open = true
334
+ end
335
+
336
+ @sf.stub!( :open? ) do
337
+ open
338
+ end
339
+
340
+ @sf.stub!( :eof? ) do
341
+ case chunk
342
+ when 0
343
+ true
344
+ when 1, 2
345
+ false
346
+ else
347
+ true
348
+ end
349
+ end
350
+
351
+ data = [ nil, 'first_chunk', 'second_chunk' ]
352
+ @sf.should_receive( :read ).twice do
353
+ d = data[ chunk ]
354
+ chunk += 1
355
+ d
356
+ end
357
+
358
+ sent = []
359
+ @ch.stub!( :send_data ) { | d | sent << d }
360
+
361
+ @rs.sudo( @sf )
362
+
363
+ sent.should include "echo -n 'Zmlyc3RfY2h1bms=\n' | base64 -d > /remote/path\n"
364
+ sent.should include "echo -n 'c2Vjb25kX2NodW5r\n' | base64 -d >> /remote/path\n"
365
+ end
340
366
 
341
- @sf.stub!( :is_a? ).with( Remote::Session::Send ).and_return( true )
342
- @sf.stub!( :open? => false )
343
- @sf.stub!( :remote_path ).and_return( '/remote/path' )
344
- @sf.stub!( :eof? => true )
367
+ it 'should copy empty files' do
368
+ @sf.stub!( :is_a? ).with( Remote::Session::Send ).and_return( true )
369
+ @sf.stub!( :open? => false )
370
+ @sf.stub!( :remote_path ).and_return( '/remote/path' )
371
+ @sf.stub!( :eof? => true )
345
372
 
346
- sent = []
347
- @ch.stub!( :send_data ) { | d | sent << d }
373
+ sent = []
374
+ @ch.stub!( :send_data ) { | d | sent << d }
348
375
 
349
376
 
350
- @rs.sudo( @sf )
377
+ @rs.sudo( @sf )
351
378
 
352
- sent.should == [
353
- "echo -n '' | base64 -d > /remote/path\n",
354
- "exit\n"
355
- ]
356
- end
357
-
358
- end
379
+ sent.should include "echo -n '' | base64 -d > /remote/path\n"
380
+ end
359
381
 
360
- context 'with password prompt' do
361
- before :each do
362
- $stdout.stub( :write )
363
- @ch.stub!( :on_data ) do |&block|
364
- block.call( @ch, 'sudo_prompt' )
365
- block.call( @ch, 'root#' )
366
382
  end
367
- end
368
-
369
- it 'should supply the sudo password, when prompted' do
370
- @ch.should_receive( :send_data ).with( "secret\n" )
371
383
 
372
- @rs.sudo( 'pwd' )
373
- end
384
+ context 'with user-supplied prompt' do
385
+ it 'should send the supplied data' do
386
+ @@data[ 2 ] = [ 'remote-session-prompt#', 'Supply user password:', 'remote-session-prompt#' ]
387
+ @ch.should_receive( :send_data ).with( "this data\n" )
388
+ @rs.prompts[ 'user password:' ] = 'this data'
374
389
 
375
- it 'should not echo the standard prompt' do
376
- expect_output do
377
- @rs.sudo( 'pwd' )
378
- end.should == [ [ 'root#' ], [] ]
379
- end
380
- end
390
+ @rs.sudo( 'pwd' )
391
+ end
381
392
 
382
- context 'with user-supplied prompt' do
383
- before :each do
384
- $stdout.stub( :write )
385
- @ch.stub!( :on_data ) do |&block|
386
- block.call( @ch, 'Here is my prompt:' )
387
- block.call( @ch, 'root#' )
388
- end
389
- end
393
+ it 'should echo the prompt' do
394
+ @@data[ 2 ] = [ 'remote-session-prompt#', 'Supply user password:', 'remote-session-prompt#' ]
395
+ @rs.prompts[ 'user password:' ] = 'this data'
390
396
 
391
- it 'should send the supplied data' do
392
- @ch.should_receive( :send_data ).with( "this data\n" )
393
- @rs.prompts[ 'my prompt' ] = 'this data'
397
+ output = expect_output do
398
+ @rs.sudo( 'pwd' )
399
+ end
400
+
401
+ output[ :stdout ].should include 'Supply user password:'
402
+ end
394
403
 
395
- @rs.sudo( 'pwd' )
396
- end
404
+ end
397
405
 
398
- it 'should not echo the prompt' do
399
- @rs.prompts[ 'my prompt' ] = 'this data'
406
+ it 'should send error data to stdout' do
407
+ @ch.stub!( :on_data )
400
408
 
401
- expect_output do
402
- @rs.sudo( 'pwd' )
403
- end.should == [ [ 'root#' ], [] ]
404
- end
409
+ @ch.stub!( :on_extended_data ) do |&block|
410
+ block.call @ch, 'foo', 'It failed'
411
+ end
405
412
 
406
- end
413
+ output = expect_output do
414
+ @rs.sudo( 'pwd' )
415
+ end
407
416
 
408
- it 'should send error data to stdout' do
409
- @ch.stub!( :on_data )
417
+ output[ :stderr ].should include "It failed\n"
418
+ end
410
419
 
411
- @ch.stub!( :on_extended_data ) do |&block|
412
- block.call @ch, 'foo', 'It failed'
413
420
  end
414
421
 
415
- expect_output do
416
- @rs.sudo( 'pwd' )
417
- end.should == [ [], [ "It failed", "\n" ] ]
418
422
  end
419
423
 
420
424
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: remote-session
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 21
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 4
10
- version: 0.0.4
9
+ - 5
10
+ version: 0.0.5
11
11
  platform: ruby
12
12
  authors:
13
13
  - Joe Yates
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-05-20 00:00:00 Z
18
+ date: 2012-06-03 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  prerelease: false