remote-session 0.0.4 → 0.0.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.
@@ -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