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

root/tools/capistrano/lib/capistrano/ssh.rb

Revision 8750, 5.1 kB (checked in by minam, 2 years ago)

make sure capistrano only tries to load Net::SSH and Net::SFTP versions less than 1.99.0

Line 
1 begin
2   require 'rubygems'
3   gem 'net-ssh', "< 1.99.0"
4 rescue LoadError, NameError
5 end
6
7 require 'net/ssh'
8
9 module Capistrano
10   unless ENV['SKIP_VERSION_CHECK']
11     require 'capistrano/version'
12     require 'net/ssh/version'
13     ssh_version = [Net::SSH::Version::MAJOR, Net::SSH::Version::MINOR, Net::SSH::Version::TINY]
14     if !Version.check(Version::SSH_REQUIRED, ssh_version)
15       raise "You have Net::SSH #{ssh_version.join(".")}, but you need at least #{Version::SSH_REQUIRED.join(".")}"
16     end
17   end
18
19   # Now, Net::SSH is kind of silly, and tries to lazy-load everything. This
20   # wreaks havoc with the parallel connection trick that Capistrano wants to
21   # use, so we're going to do something hideously ugly here and force all the
22   # files that Net::SSH uses to load RIGHT NOW, rather than lazily.
23
24   net_ssh_dependencies = %w(connection/services connection/channel connection/driver
25     service/agentforward/services service/agentforward/driver
26     service/process/driver util/prompter
27     service/forward/services service/forward/driver service/forward/local-network-handler service/forward/remote-network-handler
28     service/shell/services service/shell/driver
29     lenient-host-key-verifier
30     transport/compress/services transport/compress/zlib-compressor transport/compress/none-compressor transport/compress/zlib-decompressor transport/compress/none-decompressor
31     transport/kex/services transport/kex/dh transport/kex/dh-gex
32     transport/ossl/services
33     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
34     transport/ossl/cipher-factory transport/ossl/hmac-factory transport/ossl/buffer-factory transport/ossl/key-factory transport/ossl/digest-factory
35     transport/identity-cipher transport/packet-stream transport/version-negotiator transport/algorithm-negotiator transport/session
36     userauth/methods/services userauth/methods/password userauth/methods/keyboard-interactive userauth/methods/publickey userauth/methods/hostbased
37     userauth/services userauth/agent userauth/userkeys userauth/driver
38     transport/services service/services
39   )
40
41   net_ssh_dependencies << "userauth/pageant" if File::ALT_SEPARATOR
42   net_ssh_dependencies.each do |path|
43     begin
44       require "net/ssh/#{path}"
45     rescue LoadError
46       # Ignore load errors from this, since some files are in the list which
47       # do not exist in different (supported) versions of Net::SSH. We know
48       # (by this point) that Net::SSH is installed, though, since we do a
49       # require 'net/ssh' at the very top of this file, and we know the
50       # installed version meets the minimum version requirements because of
51       # the version check, also at the top of this file. So, if we get a
52       # LoadError, it's simply because the file in question does not exist in
53       # the version of Net::SSH that is installed.
54       #
55       # Whew!
56     end
57   end
58
59   # A helper class for dealing with SSH connections.
60   class SSH
61     # Patch an accessor onto an SSH connection so that we can record the server
62     # definition object that defines the connection. This is useful because
63     # the gateway returns connections whose "host" is 127.0.0.1, instead of
64     # the host on the other side of the tunnel.
65     module Server #:nodoc:
66       def self.apply_to(connection, server)
67         connection.extend(Server)
68         connection.xserver = server
69         connection
70       end
71
72       attr_accessor :xserver
73     end
74
75     # The default port for SSH.
76     DEFAULT_PORT = 22
77
78     # An abstraction to make it possible to connect to the server via public key
79     # without prompting for the password. If the public key authentication fails
80     # this will fall back to password authentication.
81     #
82     # +server+ must be an instance of ServerDefinition.
83     #
84     # If a block is given, the new session is yielded to it, otherwise the new
85     # session is returned.
86     #
87     # If an :ssh_options key exists in +options+, it is passed to the Net::SSH
88     # constructor. Values in +options+ are then merged into it, and any
89     # connection information in +server+ is added last, so that +server+ info
90     # takes precedence over +options+, which takes precendence over ssh_options.
91     def self.connect(server, options={}, &block)
92       methods = [ %w(publickey hostbased), %w(password keyboard-interactive) ]
93       password_value = nil
94      
95       ssh_options = (options[:ssh_options] || {}).dup
96       ssh_options[:username] = server.user || options[:user] || ssh_options[:username]
97       ssh_options[:port]     = server.port || options[:port] || ssh_options[:port] || DEFAULT_PORT
98
99       begin
100         connection_options = ssh_options.merge(
101           :password => password_value,
102           :auth_methods => ssh_options[:auth_methods] || methods.shift
103         )
104
105         connection = Net::SSH.start(server.host, connection_options, &block)
106         Server.apply_to(connection, server)
107
108       rescue Net::SSH::AuthenticationFailed
109         raise if methods.empty? || ssh_options[:auth_methods]
110         password_value = options[:password]
111         retry
112       end
113     end
114   end
115 end
Note: See TracBrowser for help on using the browser.