public
Rubygem
Description: Apache Buildr
Homepage: http://incubator.apache.org/buildr
Clone URL: git://github.com/vic/buildr.git
Search Repo:
Added:  Reading files from SFTP server.
Changed: Upgraded to Net::SSH 2.0 and Net::SFTP 2.0.


git-svn-id: https://svn.apache.org/repos/asf/incubator/buildr/trunk@652955 
13f79535-47bb-0310-9956-ffa450edef68
Assaf (author)
Fri May 02 17:07:41 -0700 2008
commit  b9a0106ccf8e00d67f96abc87e2f44262081542c
tree    e44389934faf88b3cdb8807b087949234261c0a9
parent  f72d72932993263202c2c51b587f429b7bc475aa
...
1
 
 
2
3
4
...
1
2
3
4
5
6
0
@@ -1,4 +1,6 @@
0
 1.3.1 (Pending)
0
+* Added: reading files from SFTP server.
0
+* Changed: Upgraded to Net::SSH 2.0 and Net::SFTP 2.0.
0
 
0
 1.3.0 (2008-04-25)
0
 * Added: Testing with EasyB (Nicolas Modrzyk).
...
38
39
40
41
42
 
 
43
44
45
...
38
39
40
 
 
41
42
43
44
45
0
@@ -38,8 +38,8 @@
0
   # Tested against these dependencies.
0
   spec.add_dependency 'rake', '~> 0.8'
0
   spec.add_dependency 'builder', '~> 2.1'
0
- spec.add_dependency 'net-ssh', '~> 1.1'
0
- spec.add_dependency 'net-sftp', '~> 1.1'
0
+ spec.add_dependency 'net-ssh', '~> 2.0'
0
+ spec.add_dependency 'net-sftp', '~> 2.0'
0
   spec.add_dependency 'rubyzip', '~> 0.9'
0
   spec.add_dependency 'highline', '~> 1.4'
0
   spec.add_dependency 'Antwrap', '~> 0.7'
...
18
19
20
21
22
23
24
...
18
19
20
 
21
22
23
0
@@ -18,7 +18,6 @@
0
 require 'tempfile'
0
 require 'open-uri'
0
 $LOADED_FEATURES << 'rubygems/open-uri.rb' # avoid loading rubygems' open-uri
0
-require 'uri/open-sftp'
0
 require 'buildr/core/util'
0
 require 'buildr/core/transports'
0
 
...
20
21
22
23
24
25
26
27
...
342
343
344
345
 
346
 
 
 
347
348
349
350
351
352
...
351
352
353
354
 
 
 
355
356
 
357
358
 
359
360
361
362
363
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
364
365
366
367
368
369
370
371
372
...
370
371
372
 
373
374
375
 
376
377
378
379
380
381
382
383
384
 
 
 
 
 
 
 
 
 
385
386
387
388
389
390
391
392
393
394
395
396
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
397
398
399
400
 
 
 
 
 
 
 
 
401
402
403
404
 
 
405
406
407
...
20
21
22
 
23
24
25
26
...
341
342
343
 
344
345
346
347
348
349
350
351
352
353
354
...
353
354
355
 
356
357
358
359
 
360
361
 
362
363
364
365
 
 
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
...
396
397
398
399
400
 
 
401
402
 
 
 
 
 
 
 
 
403
404
405
406
407
408
409
410
411
412
 
 
 
 
 
 
 
 
 
 
 
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
 
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
0
@@ -20,7 +20,6 @@
0
 require 'net/ssh'
0
 require 'net/sftp'
0
 require 'uri'
0
-require 'uri/sftp'
0
 require 'digest/md5'
0
 require 'digest/sha1'
0
 require 'tempfile'
0
0
@@ -342,8 +341,11 @@
0
   end
0
 
0
 
0
- class SFTP #:nodoc:
0
+ class SFTP < Generic #:nodoc:
0
 
0
+ DEFAULT_PORT = 22
0
+ COMPONENT = [ :scheme, :userinfo, :host, :port, :path ].freeze
0
+
0
     class << self
0
       # Caching of passwords, so we only need to ask once.
0
       def passwords
0
0
0
0
@@ -351,16 +353,40 @@
0
       end
0
     end
0
 
0
- protected
0
+ def initialize(*arg)
0
+ super
0
+ end
0
 
0
- def write_internal(options, &block) #:nodoc:
0
+ def read(options = {}, &block)
0
       # SSH options are based on the username/password from the URI.
0
- ssh_options = { :port=>port, :username=>user, :password=>password }.merge(options[:ssh_options] || {})
0
+ ssh_options = { :port=>port, :, :password=>password }.merge(options[:ssh_options] || {})
0
       ssh_options[:password] ||= SFTP.passwords[host]
0
       begin
0
         puts "Connecting to #{host}" if Buildr.application.options.trace
0
- session = Net::SSH.start(host, ssh_options)
0
- SFTP.passwords[host] = ssh_options[:password]
0
+ result = nil
0
+ Net::SFTP.start(host, user, ssh_options) do |sftp|
0
+ SFTP.passwords[host] = ssh_options[:password]
0
+ puts 'connected' if Buildr.application.options.trace
0
+
0
+ with_progress_bar options[:progress] && options[:size], path.split('/'), options[:size] || 0 do |progress|
0
+ puts "Downloading to #{path}" if Buildr.application.options.trace
0
+ sftp.file.open(path, 'r') do |file|
0
+ if block
0
+ while chunk = file.read(32 * 4096)
0
+ block.call chunk
0
+ progress << chunk
0
+ end
0
+ else
0
+ result = ''
0
+ while chunk = file.read(32 * 4096)
0
+ result << chunk
0
+ progress << chunk
0
+ end
0
+ end
0
+ end
0
+ end
0
+ end
0
+ return result
0
       rescue Net::SSH::AuthenticationFailed=>ex
0
         # Only if running with console, prompt for password.
0
         if !ssh_options[:password] && $stdout.isatty
0
0
0
0
0
0
0
@@ -370,38 +396,54 @@
0
         end
0
         raise
0
       end
0
+ end
0
 
0
- session.sftp.connect do |sftp|
0
- puts 'connected' if Buildr.application.options.trace
0
+ protected
0
 
0
- # To create a path, we need to create all its parent. We use realpath to determine if
0
- # the path already exists, otherwise mkdir fails.
0
- puts "Creating path #{path}" if Buildr.application.options.trace
0
- File.dirname(path).split('/').inject('') do |base, part|
0
- combined = base + part
0
- sftp.realpath combined rescue sftp.mkdir combined, {}
0
- "#{combined}/"
0
- end
0
+ def write_internal(options, &block) #:nodoc:
0
+ # SSH options are based on the username/password from the URI.
0
+ ssh_options = { :port=>port, :password=>password }.merge(options[:ssh_options] || {})
0
+ ssh_options[:password] ||= SFTP.passwords[host]
0
+ begin
0
+ puts "Connecting to #{host}" if Buildr.application.options.trace
0
+ Net::SFTP.start(host, user, ssh_options) do |sftp|
0
+ SFTP.passwords[host] = ssh_options[:password]
0
+ puts 'connected' if Buildr.application.options.trace
0
 
0
- with_progress_bar options[:progress] && options[:size], path.split('/'), options[:size] || 0 do |progress|
0
- puts "Uploading to #{path}" if Buildr.application.options.trace
0
- sftp.open_handle(path, 'w') do |handle|
0
- # Writing in chunks gives us the benefit of a progress bar,
0
- # but also require that we maintain a position in the file,
0
- # since write() with two arguments always writes at position 0.
0
- pos = 0
0
- while chunk = yield(32 * 4096)
0
- sftp.write(handle, chunk, pos)
0
- pos += chunk.size
0
- progress << chunk
0
+ # To create a path, we need to create all its parent. We use realpath to determine if
0
+ # the path already exists, otherwise mkdir fails.
0
+ puts "Creating path #{path}" if Buildr.application.options.trace
0
+ File.dirname(path).split('/').inject('') do |base, part|
0
+ combined = base + part
0
+ sftp.realpath combined rescue sftp.mkdir combined, {}
0
+ "#{combined}/"
0
+ end
0
+
0
+ with_progress_bar options[:progress] && options[:size], path.split('/'), options[:size] || 0 do |progress|
0
+ puts "Uploading to #{path}" if Buildr.application.options.trace
0
+ sftp.file.open(path, 'w') do |file|
0
+ while chunk = yield(32 * 4096)
0
+ file.write chunk
0
+ progress << chunk
0
+ end
0
+ sftp.setstat(path, :permissions => options[:permissions]) if options[:permissions]
0
             end
0
- sftp.setstat(path, :permissions => options[:permissions]) if options[:permissions]
0
           end
0
         end
0
+ rescue Net::SSH::AuthenticationFailed=>ex
0
+ # Only if running with console, prompt for password.
0
+ if !ssh_options[:password] && $stdout.isatty
0
+ password = ask("Password for #{host}:") { |q| q.echo = '*' }
0
+ ssh_options[:password] = password
0
+ retry
0
+ end
0
+ raise
0
       end
0
     end
0
 
0
   end
0
+
0
+ @@schemes['SFTP'] = SFTP
0
 
0
 
0
   # File URL. Keep in mind that file URLs take the form of <code>file://host/path</code>, although the host
...
298
299
300
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
0
@@ -298,4 +298,107 @@
0
     URI("http://john:secret@#{@host_domain}").read
0
   end
0
 end
0
+
0
+
0
+describe URI::HTTP, '#write' do
0
+end
0
+
0
+
0
+describe URI::SFTP, '#write' do
0
+ before do
0
+ @uri = URI('sftp://john:secret@localhost/path/readme')
0
+ @content = 'Readme. Please!'
0
+
0
+ @ssh_session = mock('Net::SSH::Session')
0
+ @sftp_session = mock('Net::SFTP::Session')
0
+ @file_factory = mock('Net::SFTP::Operations::FileFactory')
0
+ Net::SSH.stub!(:start).with('localhost', 'john', :password=>'secret', :port=>22) do
0
+ Net::SFTP::Session.should_receive(:new).with(@ssh_session).and_yield(@sftp_session).and_return(@sftp_session)
0
+ @sftp_session.should_receive(:connect!).and_return(@sftp_session)
0
+ @sftp_session.should_receive(:loop)
0
+ @sftp_session.stub!(:mkdir)
0
+ @sftp_session.should_receive(:file).with.and_return(@file_factory)
0
+ @file_factory.stub!(:open)
0
+ @ssh_session.should_receive(:close)
0
+ @ssh_session
0
+ end
0
+ end
0
+
0
+ it 'should open connection to SFTP server' do
0
+ @uri.write @content
0
+ end
0
+
0
+ it 'should create path recursively on SFTP server' do
0
+ @sftp_session.should_receive(:mkdir).ordered.with('', {})
0
+ @sftp_session.should_receive(:mkdir).ordered.with('/path', {})
0
+ @uri.write @content
0
+ end
0
+
0
+ it 'should only create paths that don\'t exist' do
0
+ @sftp_session.should_receive(:realpath).any_number_of_times
0
+ @sftp_session.should_not_receive(:mkdir)
0
+ @uri.write @content
0
+ end
0
+
0
+ it 'should open file for writing' do
0
+ @file_factory.should_receive(:open).with('/path/readme', 'w')
0
+ @uri.write @content
0
+ end
0
+
0
+ it 'should write contents to file' do
0
+ file = mock('Net::SFTP::Operations::File')
0
+ file.should_receive(:write).with(@content)
0
+ @file_factory.should_receive(:open).with('/path/readme', 'w').and_yield(file)
0
+ @uri.write @content
0
+ end
0
+
0
+end
0
+
0
+
0
+describe URI::SFTP, '#read' do
0
+ before do
0
+ @uri = URI('sftp://john:secret@localhost/path/readme')
0
+ @content = 'Readme. Please!'
0
+
0
+ @ssh_session = mock('Net::SSH::Session')
0
+ @sftp_session = mock('Net::SFTP::Session')
0
+ @file_factory = mock('Net::SFTP::Operations::FileFactory')
0
+ Net::SSH.stub!(:start).with('localhost', 'john', :password=>'secret', :port=>22) do
0
+ Net::SFTP::Session.should_receive(:new).with(@ssh_session).and_yield(@sftp_session).and_return(@sftp_session)
0
+ @sftp_session.should_receive(:connect!).and_return(@sftp_session)
0
+ @sftp_session.should_receive(:loop)
0
+ @sftp_session.should_receive(:file).with.and_return(@file_factory)
0
+ @file_factory.stub!(:open)
0
+ @ssh_session.should_receive(:close)
0
+ @ssh_session
0
+ end
0
+ end
0
+
0
+ it 'should open connection to SFTP server' do
0
+ @uri.read
0
+ end
0
+
0
+ it 'should open file for reading' do
0
+ @file_factory.should_receive(:open).with('/path/readme', 'r')
0
+ @uri.read
0
+ end
0
+
0
+ it 'should read contents of file and return it' do
0
+ file = mock('Net::SFTP::Operations::File')
0
+ file.should_receive(:read).with(an_instance_of(Numeric)).once.and_return(@content, nil)
0
+ @file_factory.should_receive(:open).with('/path/readme', 'r').and_yield(file)
0
+ @uri.read.should eql(@content)
0
+ end
0
+
0
+ it 'should read contents of file and pass it to block' do
0
+ file = mock('Net::SFTP::Operations::File')
0
+ file.should_receive(:read).with(an_instance_of(Numeric)).once.and_return(@content, nil)
0
+ @file_factory.should_receive(:open).with('/path/readme', 'r').and_yield(file)
0
+ content = ''
0
+ @uri.read do |chunk|
0
+ content << chunk
0
+ end
0
+ content.should eql(@content)
0
+ end
0
+end

Comments

    No one has commented yet.