Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Changeset 7245

Show
Ignore:
Timestamp:
07/27/07 19:34:55 (1 year ago)
Author:
minam
Message:

most functionality working with net-ssh v2

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • branches/cap2-on-netssh2/lib/capistrano/command.rb

    r7112 r7245  
    3939          next if ch[:closed] 
    4040          active += 1 
    41           ch.connection.process(true
     41          ch.connection.process(0.01
    4242        end 
    4343 
    4444        break if active == 0 
    45         if Time.now - since >= 1 
    46           since = Time.now 
    47           @channels.each { |ch| ch.connection.ping! } 
    48         end 
    49         sleep 0.01 # a brief respite, to keep the CPU from going crazy 
     45        # FIXME add connection.ping! 
     46        #if Time.now - since >= 1 
     47        #  since = Time.now 
     48        #  @channels.each { |ch| ch.connection.ping! } 
     49        #end 
    5050      end 
    5151 
     
    8484            channel[:host] = server.host 
    8585            channel[:options] = options 
    86             channel.request_pty :want_reply => true 
    87  
    88             channel.on_success do |ch| 
    89               logger.trace "executing command", ch[:server] if logger 
    90               escaped = replace_placeholders(command, ch).gsub(/[$\\`"]/) { |m| "\\#{m}" } 
    91               command_line = [environment, options[:shell] || "sh", "-c", "\"#{escaped}\""].compact.join(" ") 
    92               ch.exec(command_line) 
    93               ch.send_data(options[:data]) if options[:data] 
    94             end 
    95  
    96             channel.on_failure do |ch| 
    97               # just log it, don't actually raise an exception, since the 
    98               # process method will see that the status is not zero and will 
    99               # raise an exception then. 
    100               logger.important "could not open channel", ch[:server] if logger 
    101               ch.close 
    102             end 
    103  
    104             channel.on_data do |ch, data| 
    105               @callback[ch, :out, data] if @callback 
    106             end 
    107  
    108             channel.on_extended_data do |ch, type, data| 
    109               @callback[ch, :err, data] if @callback 
    110             end 
    111  
    112             channel.on_request do |ch, request, reply, data| 
    113               ch[:status] = data.read_long if request == "exit-status" 
    114             end 
    11586 
    11687            channel.on_close do |ch| 
    11788              ch[:closed] = true 
     89            end 
     90 
     91            channel.request_pty do |ch, success| 
     92              if success 
     93                logger.trace "executing command", ch[:server] if logger 
     94                escaped = replace_placeholders(command, ch).gsub(/[$\\`"]/) { |m| "\\#{m}" } 
     95                command_line = [environment, options[:shell] || "sh", "-c", "\"#{escaped}\""].compact.join(" ") 
     96                ch.exec(command_line) 
     97                ch.send_data(options[:data]) if options[:data] 
     98 
     99                channel.on_data do |ch, data| 
     100                  @callback[ch, :out, data] if @callback 
     101                end 
     102 
     103                channel.on_extended_data do |ch, type, data| 
     104                  @callback[ch, :err, data] if @callback 
     105                end 
     106 
     107                channel.on_request do |ch, request, reply, data| 
     108                  ch[:status] = data.read_long if request == "exit-status" 
     109                end 
     110              else 
     111                # just log it, don't actually raise an exception, since the 
     112                # process method will see that the status is not zero and will 
     113                # raise an exception then. 
     114                logger.important "could not open channel", ch[:server] if logger 
     115                ch.close 
     116              end 
    118117            end 
    119118          end 
  • branches/cap2-on-netssh2/lib/capistrano/gateway.rb

    r7145 r7245  
    8888      local_port = next_port 
    8989 
    90       thread = Thread.new do 
    91         begin 
    92           local_host = ServerDefinition.new("127.0.0.1", :user => server.user, :port => local_port) 
    93           session.forward.local(local_port, server.host, server.port || 22) 
    94           connection = SSH.connect(local_host, @options) 
    95           connection.xserver = server 
    96           logger.trace "connected: `#{server}' (via gateway)" if logger 
    97         rescue Errno::EADDRINUSE 
    98           local_port = next_port 
    99           retry 
    100         rescue Exception => e 
    101           warn "#{e.class}: #{e.message}" 
    102           warn e.backtrace.join("\n") 
    103         end 
     90      begin 
     91        local_host = ServerDefinition.new("127.0.0.1", :user => server.user, :port => local_port) 
     92        session.forward.local(local_port, server.host, server.port || 22) 
     93        connection = SSH.connect(local_host, @options) 
     94        connection.xserver = server 
     95        logger.trace "connected: `#{server}' (via gateway)" if logger 
     96      rescue Errno::EADDRINUSE 
     97        local_port = next_port 
     98        retry 
     99      rescue Exception => e 
     100        warn "#{e.class}: #{e.message}" 
     101        warn e.backtrace.join("\n") 
    104102      end 
    105103 
    106       thread.join 
    107104      if connection.nil? 
    108105        error = ConnectionError.new("could not establish connection to `#{server}'") 
  • branches/cap2-on-netssh2/lib/capistrano/ssh.rb

    r7194 r7245  
     1$LOAD_PATH.unshift "/Users/jamis/Projects/net-ssh.v2/lib" 
    12require 'net/ssh' 
    23 
     
    89    if !Version.check(Version::SSH_REQUIRED, ssh_version) 
    910      raise "You have Net::SSH #{ssh_version.join(".")}, but you need at least #{Version::SSH_REQUIRED.join(".")}" 
    10     end 
    11   end 
    12  
    13   # Now, Net::SSH is kind of silly, and tries to lazy-load everything. This 
    14   # wreaks havoc with the parallel connection trick that Capistrano wants to 
    15   # use, so we're going to do something hideously ugly here and force all the 
    16   # files that Net::SSH uses to load RIGHT NOW, rather than lazily. 
    17  
    18   net_ssh_dependencies = %w(connection/services connection/channel connection/driver 
    19     service/agentforward/services service/agentforward/driver 
    20     service/process/driver util/prompter 
    21     service/forward/services service/forward/driver service/forward/local-network-handler service/forward/remote-network-handler 
    22     service/shell/services service/shell/driver 
    23     lenient-host-key-verifier 
    24     transport/compress/services transport/compress/zlib-compressor transport/compress/none-compressor transport/compress/zlib-decompressor transport/compress/none-decompressor 
    25     transport/kex/services transport/kex/dh transport/kex/dh-gex 
    26     transport/ossl/services 
    27     transport/ossl/hmac/services transport/ossl/hmac/sha1 transport/ossl/hmac/sha1-96 transport/ossl/hmac/md5 transport/ossl/hmac/md5-96 transport/ossl/hmac/none 
    28     transport/ossl/cipher-factory transport/ossl/hmac-factory transport/ossl/buffer-factory transport/ossl/key-factory transport/ossl/digest-factory 
    29     transport/identity-cipher transport/packet-stream transport/version-negotiator transport/algorithm-negotiator transport/session 
    30     userauth/methods/services userauth/methods/password userauth/methods/keyboard-interactive userauth/methods/publickey userauth/methods/hostbased 
    31     userauth/services userauth/agent userauth/userkeys userauth/driver 
    32     transport/services service/services 
    33   ) 
    34  
    35   net_ssh_dependencies << "userauth/pageant" if File::ALT_SEPARATOR 
    36   net_ssh_dependencies.each do |path| 
    37     begin 
    38       require "net/ssh/#{path}" 
    39     rescue LoadError 
    40       # Ignore load errors from this, since some files are in the list which 
    41       # do not exist in different (supported) versions of Net::SSH. We know 
    42       # (by this point) that Net::SSH is installed, though, since we do a 
    43       # require 'net/ssh' at the very top of this file, and we know the 
    44       # installed version meets the minimum version requirements because of 
    45       # the version check, also at the top of this file. So, if we get a 
    46       # LoadError, it's simply because the file in question does not exist in 
    47       # the version of Net::SSH that is installed. 
    48       # 
    49       # Whew! 
    5011    end 
    5112  end 
     
    8849       
    8950      ssh_options = (options[:ssh_options] || {}).dup 
    90       ssh_options[:username] = server.user || options[:user] || ssh_options[:username] 
     51      ssh_options[:username] = server.user || options[:user] || ssh_options[:username] || ENV["USER"] || ENV["USERNAME"] 
    9152      ssh_options[:port]     = server.port || options[:port] || ssh_options[:port] || DEFAULT_PORT 
     53 
     54      username = ssh_options[:username] 
    9255 
    9356      begin 
     
    9760        ) 
    9861 
    99         connection = Net::SSH.start(server.host, connection_options, &block) 
     62        connection = Net::SSH.start(server.host, username, connection_options, &block) 
    10063        Server.apply_to(connection, server) 
    10164 
  • branches/cap2-on-netssh2/lib/capistrano/upload.rb

    r7180 r7245  
    1 require 'net/sftp' 
    2 require 'net/sftp/operations/errors' 
    3 require 'capistrano/errors' 
    4  
    5 module Capistrano 
    6   unless ENV['SKIP_VERSION_CHECK'] 
    7     require 'capistrano/version'  
    8     require 'net/sftp/version' 
    9     sftp_version = [Net::SFTP::Version::MAJOR, Net::SFTP::Version::MINOR, Net::SFTP::Version::TINY] 
    10     required_version = [1,1,0] 
    11     if !Capistrano::Version.check(required_version, sftp_version) 
    12       raise "You have Net::SFTP #{sftp_version.join(".")}, but you need at least #{required_version.join(".")}. Net::SFTP will not be used." 
    13     end 
    14   end 
    15  
    16   # This class encapsulates a single file upload to be performed in parallel 
    17   # across multiple machines, using the SFTP protocol. Although it is intended 
    18   # to be used primarily from within Capistrano, it may also be used standalone 
    19   # if you need to simply upload a file to multiple servers. 
    20   # 
    21   # Basic Usage: 
    22   # 
    23   #   begin 
    24   #     uploader = Capistrano::Upload.new(sessions, "remote-file.txt", 
    25   #         :data => "the contents of the file to upload") 
    26   #     uploader.process! 
    27   #   rescue Capistrano::UploadError => e 
    28   #     warn "Could not upload the file: #{e.message}" 
    29   #   end 
    30   class Upload 
    31     def self.process(sessions, filename, options) 
    32       new(sessions, filename, options).process! 
    33     end 
    34    
    35     attr_reader :sessions, :filename, :options 
    36     attr_reader :failed, :completed 
    37  
    38     # Creates and prepares a new Upload instance. The +sessions+ parameter 
    39     # must be an array of open Net::SSH sessions. The +filename+ is the name 
    40     # (including path) of the destination file on the remote server. The 
    41     # +options+ hash accepts the following keys (as symbols): 
    42     # 
    43     # * data: required. Should refer to a String containing the contents of 
    44     #   the file to upload. 
    45     # * mode: optional. The "mode" of the destination file. Defaults to 0660. 
    46     # * logger: optional. Should point to a Capistrano::Logger instance, if 
    47     #   given. 
    48     def initialize(sessions, filename, options) 
    49       raise ArgumentError, "you must specify the data to upload via the :data option" unless options[:data] 
    50  
    51       @sessions = sessions 
    52       @filename = filename 
    53       @options  = options 
    54  
    55       @completed = @failed = 0 
    56       @sftps = setup_sftp 
    57     end 
    58      
    59     # Uploads to all specified servers in parallel. If any one of the servers 
    60     # fails, an exception will be raised (UploadError). 
    61     def process! 
    62       logger.debug "uploading #{filename}" if logger 
    63       while running? 
    64         @sftps.each do |sftp| 
    65           next if sftp.channel[:done] 
    66           begin 
    67             sftp.channel.connection.process(true) 
    68           rescue Net::SFTP::Operations::StatusException => error 
    69             logger.important "uploading failed: #{error.description}", sftp.channel[:server] if logger 
    70             failed!(sftp) 
    71           end 
    72         end 
    73         sleep 0.01 # a brief respite, to keep the CPU from going crazy 
    74       end 
    75       logger.trace "upload finished" if logger 
    76  
    77       if (failed = @sftps.select { |sftp| sftp.channel[:failed] }).any? 
    78         hosts = failed.map { |sftp| sftp.channel[:server] } 
    79         error = UploadError.new("upload of #{filename} failed on #{hosts.join(',')}") 
    80         error.hosts = hosts 
    81         raise error 
    82       end 
    83  
    84       self 
    85     end 
    86  
    87     private 
    88  
    89       def logger 
    90         options[:logger] 
    91       end 
    92  
    93       def setup_sftp 
    94         sessions.map do |session| 
    95           server = session.xserver 
    96           sftp = session.sftp 
    97           sftp.connect unless sftp.state == :open 
    98  
    99           sftp.channel[:server] = server 
    100           sftp.channel[:done] = false 
    101           sftp.channel[:failed] = false 
    102  
    103           real_filename = filename.gsub(/\$CAPISTRANO:HOST\$/, server.host) 
    104           sftp.open(real_filename, IO::WRONLY | IO::CREAT | IO::TRUNC, options[:mode] || 0660) do |status, handle| 
    105             break unless check_status(sftp, "open #{real_filename}", server, status) 
    106              
    107             logger.info "uploading data to #{server}:#{real_filename}" if logger 
    108             sftp.write(handle, options[:data] || "") do |status| 
    109               break unless check_status(sftp, "write to #{server}:#{real_filename}", server, status) 
    110               sftp.close_handle(handle) do 
    111                 logger.debug "done uploading data to #{server}:#{real_filename}" if logger 
    112                 completed!(sftp) 
    113               end 
    114             end 
    115           end 
    116            
    117           sftp 
    118         end 
    119       end 
    120        
    121       def check_status(sftp, action, server, status) 
    122         return true if status.code == Net::SFTP::Session::FX_OK 
    123  
    124         logger.error "could not #{action} on #{server} (#{status.message})" if logger 
    125         failed!(sftp) 
    126  
    127         return false 
    128       end 
    129  
    130       def running? 
    131         completed < @sftps.length 
    132       end 
    133  
    134       def failed!(sftp) 
    135         completed!(sftp) 
    136         @failed += 1 
    137         sftp.channel[:failed] = true 
    138       end 
    139  
    140       def completed!(sftp) 
    141         @completed += 1 
    142         sftp.channel[:done] = true 
    143       end 
    144   end 
    145  
    146 end 
     1# require 'net/sftp' 
     2# require 'net/sftp/operations/errors' 
     3# require 'capistrano/errors' 
     4#  
     5# module Capistrano 
     6#   unless ENV['SKIP_VERSION_CHECK'] 
     7#     require 'capistrano/version'  
     8#     require 'net/sftp/version' 
     9#     sftp_version = [Net::SFTP::Version::MAJOR, Net::SFTP::Version::MINOR, Net::SFTP::Version::TINY] 
     10#     required_version = [1,1,0] 
     11#     if !Capistrano::Version.check(required_version, sftp_version) 
     12#       raise "You have Net::SFTP #{sftp_version.join(".")}, but you need at least #{required_version.join(".")}. Net::SFTP will not be used." 
     13#     end 
     14#   end 
     15#  
     16#   # This class encapsulates a single file upload to be performed in parallel 
     17#   # across multiple machines, using the SFTP protocol. Although it is intended 
     18#   # to be used primarily from within Capistrano, it may also be used standalone 
     19#   # if you need to simply upload a file to multiple servers. 
     20#   # 
     21#   # Basic Usage: 
     22#   # 
     23#   #   begin 
     24#   #     uploader = Capistrano::Upload.new(sessions, "remote-file.txt", 
     25#   #         :data => "the contents of the file to upload") 
     26#   #     uploader.process! 
     27#   #   rescue Capistrano::UploadError => e 
     28#   #     warn "Could not upload the file: #{e.message}" 
     29#   #   end 
     30#   class Upload 
     31#     def self.process(sessions, filename, options) 
     32#       new(sessions, filename, options).process! 
     33#     end 
     34#    
     35#     attr_reader :sessions, :filename, :options 
     36#     attr_reader :failed, :completed 
     37#  
     38#     # Creates and prepares a new Upload instance. The +sessions+ parameter 
     39#     # must be an array of open Net::SSH sessions. The +filename+ is the name 
     40#     # (including path) of the destination file on the remote server. The 
     41#     # +options+ hash accepts the following keys (as symbols): 
     42#     # 
     43#     # * data: required. Should refer to a String containing the contents of 
     44#     #   the file to upload. 
     45#     # * mode: optional. The "mode" of the destination file. Defaults to 0660. 
     46#     # * logger: optional. Should point to a Capistrano::Logger instance, if 
     47#     #   given. 
     48#     def initialize(sessions, filename, options) 
     49#       raise ArgumentError, "you must specify the data to upload via the :data option" unless options[:data] 
     50#  
     51#       @sessions = sessions 
     52#       @filename = filename 
     53#       @options  = options 
     54#  
     55#       @completed = @failed = 0 
     56#       @sftps = setup_sftp 
     57#     end 
     58#      
     59#     # Uploads to all specified servers in parallel. If any one of the servers 
     60#     # fails, an exception will be raised (UploadError). 
     61#     def process! 
     62#       logger.debug "uploading #{filename}" if logger 
     63#       while running? 
     64#         @sftps.each do |sftp| 
     65#           next if sftp.channel[:done] 
     66#           begin 
     67#             sftp.channel.connection.process(true) 
     68#           rescue Net::SFTP::Operations::StatusException => error 
     69#             logger.important "uploading failed: #{error.description}", sftp.channel[:server] if logger 
     70#             failed!(sftp) 
     71#           end 
     72#         end 
     73#         sleep 0.01 # a brief respite, to keep the CPU from going crazy 
     74#       end 
     75#       logger.trace "upload finished" if logger 
     76#  
     77#       if (failed = @sftps.select { |sftp| sftp.channel[:failed] }).any? 
     78#         hosts = failed.map { |sftp| sftp.channel[:server] } 
     79#         error = UploadError.new("upload of #{filename} failed on #{hosts.join(',')}") 
     80#         error.hosts = hosts 
     81#         raise error 
     82#       end 
     83#  
     84#       self 
     85#     end 
     86#  
     87#     private 
     88#  
     89#       def logger 
     90#         options[:logger] 
     91#       end 
     92#  
     93#       def setup_sftp 
     94#         sessions.map do |session| 
     95#           server = session.xserver 
     96#           sftp = session.sftp 
     97#           sftp.connect unless sftp.state == :open 
     98#  
     99#           sftp.channel[:server] = server 
     100#           sftp.channel[:done] = false 
     101#           sftp.channel[:failed] = false 
     102#  
     103#           real_filename = filename.gsub(/\$CAPISTRANO:HOST\$/, server.host) 
     104#           sftp.open(real_filename, IO::WRONLY | IO::CREAT | IO::TRUNC, options[:mode] || 0660) do |status, handle| 
     105#             break unless check_status(sftp, "open #{real_filename}", server, status) 
     106#              
     107#             logger.info "uploading data to #{server}:#{real_filename}" if logger 
     108#             sftp.write(handle, options[:data] || "") do |status| 
     109#               break unless check_status(sftp, "write to #{server}:#{real_filename}", server, status) 
     110#               sftp.close_handle(handle) do 
     111#                 logger.debug "done uploading data to #{server}:#{real_filename}" if logger 
     112#                 completed!(sftp) 
     113#               end 
     114#             end 
     115#           end 
     116#            
     117#           sftp 
     118#         end 
     119#       end 
     120#        
     121#       def check_status(sftp, action, server, status) 
     122#         return true if status.code == Net::SFTP::Session::FX_OK 
     123#  
     124#         logger.error "could not #{action} on #{server} (#{status.message})" if logger 
     125#         failed!(sftp) 
     126#  
     127#         return false 
     128#       end 
     129#  
     130#       def running? 
     131#         completed < @sftps.length 
     132#       end 
     133#  
     134#       def failed!(sftp) 
     135#         completed!(sftp) 
     136#         @failed += 1 
     137#         sftp.channel[:failed] = true 
     138#       end 
     139#  
     140#       def completed!(sftp) 
     141#         @completed += 1 
     142#         sftp.channel[:done] = true 
     143#       end 
     144#   end 
     145#  
     146# end