lib/rye/box.rb in rye-0.9.2 vs lib/rye/box.rb in rye-0.9.3
- old
+ new
@@ -1,5 +1,7 @@
+# vim: set sw=2 ts=2 :
+
require 'annoy'
require 'readline'
module Rye
DEBUG = false unless defined?(Rye::DEBUG)
@@ -48,14 +50,16 @@
def sudo?; @rye_sudo == true end
# Returns the current value of the stash +@rye_stash+
def stash; @rye_stash; end
def quiet; @rye_quiet; end
+ def via; @rye_via; end
def nickname; @rye_nickname || host; end
def host=(val); @rye_host = val; end
def opts=(val); @rye_opts = val; end
+ def via=(val); @rye_via = val; end
# Store a value to the stash +@rye_stash+
def stash=(val); @rye_stash = val; end
def nickname=(val); @rye_nickname = val; end
@@ -70,10 +74,11 @@
def current_working_directory; @rye_current_working_directory; end
# The most recent valud for umask (or 0022)
def current_umask; @rye_current_umask; end
+ def via?; !@rye_via.nil?; end
def info?; !@rye_info.nil?; end
def debug?; !@rye_debug.nil?; end
def error?; !@rye_error.nil?; end
def ostype=(val); @rye_ostype = val; end
@@ -91,10 +96,11 @@
# The +opts+ hash excepts the following keys:
#
# * :safe => should Rye be safe? Default: true
# * :port => remote server ssh port. Default: SSH config file or 22
# * :keys => one or more private key file paths (passwordless login)
+ # * :via => the Rye::Hop to access this host through
# * :info => an IO object to print Rye::Box command info to. Default: nil
# * :debug => an IO object to print Rye::Box debugging info to. Default: nil
# * :error => an IO object to print Rye::Box errors to. Default: STDERR
# * :getenv => pre-fetch +host+ environment variables? (default: true)
# * :password => the user's password (ignored if there's a valid private key)
@@ -118,10 +124,11 @@
# These opts are use by Rye::Box and also passed to Net::SSH
@rye_opts = {
:safe => true,
:port => ssh_opts[:port],
:keys => Rye.keys,
+ :via => nil,
:info => nil,
:debug => nil,
:error => STDERR,
:getenv => true,
:templates => :erb,
@@ -129,10 +136,13 @@
}.merge(opts)
# Close the SSH session before Ruby exits. This will do nothing
# if disconnect has already been called explicitly.
at_exit { self.disconnect }
+
+ # Properly handle whether the opt :via is a +Rye::Hop+ or a +String+
+ via_hop(@rye_opts.delete(:via))
# @rye_opts gets sent to Net::SSH so we need to remove the keys
# that are not meant for it.
@rye_safe, @rye_debug = @rye_opts.delete(:safe), @rye_opts.delete(:debug)
@rye_info, @rye_error = @rye_opts.delete(:info), @rye_opts.delete(:error)
@@ -147,11 +157,11 @@
unless @rye_templates.nil?
require @rye_templates.to_s # should be :erb
end
@rye_opts[:logger] = Logger.new(@rye_debug) if @rye_debug # Enable Net::SSH debugging
- @rye_opts[:paranoid] = true unless @rye_safe == false # See Net::SSH.start
+ @rye_opts[:paranoid] ||= true unless @rye_safe == false # See Net::SSH.start
@rye_opts[:keys] = [@rye_opts[:keys]].flatten.compact
# Just in case someone sends a true value rather than IO object
@rye_debug = STDERR if @rye_debug == true || DEBUG
@rye_error = STDERR if @rye_error == true
@@ -176,10 +186,43 @@
# Parse SSH config files for use with Net::SSH
def ssh_config_options(host)
return Net::SSH::Config.for(host)
end
+ # * +hops+ Rye::Hop objects will be added directly
+ # to the set. Hostnames will be used to create new instances of Rye::Hop
+ # h1 = Rye::Hop.new "host1"
+ # h1.via_hop "host2", :user => "service_user"
+ #
+ # OR
+ #
+ # h1 = Rye::Hop.new "host1"
+ # h2 = Rye::Hop.new "host2"
+ # h1.via_hop h2
+ #
+ def via_hop(*args)
+ args = args.flatten.compact
+ if args.first.nil?
+ return @rye_via
+ elsif args.first.is_a?(Rye::Hop)
+ @rye_via = args.first
+ elsif args.first.is_a?(String)
+ hop = args.shift
+ if args.first.is_a?(Hash)
+ @rye_via = Rye::Hop.new(hop, args.first.merge(
+ :debug => @rye_debug,
+ :info => @rye_info,
+ :error => @rye_error)
+ )
+ else
+ @rye_via = Rye::Hop.new(hop)
+ end
+ end
+ disconnect
+ self
+ end
+
# Change the current working directory (sort of).
#
# I haven't been able to wrangle Net::SSH to do my bidding.
# "My bidding" in this case, is maintaining an open channel between commands.
# I'm using Net::SSH::Connection::Session#exec for all commands
@@ -600,16 +643,28 @@
# connected.
def connect(reconnect=true)
raise Rye::NoHost unless @rye_host
return if @rye_ssh && !reconnect
disconnect if @rye_ssh
- debug "Opening connection to #{@rye_host} as #{@rye_user}"
+ if @rye_via
+ debug "Opening connection to #{@rye_host} as #{@rye_user}, via #{@rye_via.host}"
+ else
+ debug "Opening connection to #{@rye_host} as #{@rye_user}"
+ end
highline = HighLine.new # Used for password prompt
retried = 0
@rye_opts[:keys].compact! # A quick fix in Windows. TODO: Why is there a nil?
begin
- @rye_ssh = Net::SSH.start(@rye_host, @rye_user, @rye_opts || {})
+ if @rye_via
+ # tell the +Rye::Hop+ what and where to setup,
+ # it returns the local port used
+ @rye_localport = @rye_via.fetch_port(@rye_host, @rye_opts[:port].nil? ? 22 : @rye_opts[:port] )
+ debug "fetched localport #{@rye_localport}"
+ @rye_ssh = Net::SSH.start("localhost", @rye_user, @rye_opts.merge(:port => @rye_localport) || {})
+ else
+ @rye_ssh = Net::SSH.start(@rye_host, @rye_user, @rye_opts || {})
+ end
rescue Net::SSH::HostKeyMismatch => ex
STDERR.puts ex.message
print "\a" if @rye_info # Ring the bell
if highline.ask("Continue? ").strip.match(/\Ay|yes|sure|ya\z/i)
@rye_opts[:paranoid] = false
@@ -651,11 +706,16 @@
@rye_ssh.loop(0.3) { @rye_ssh.busy?; }
end
end
debug "Closing connection to #{@rye_ssh.host}"
@rye_ssh.close
+ if @rye_via
+ debug "disconnecting Hop #{@rye_via.host}"
+ @rye_via.disconnect
+ end
rescue SystemCallError, Timeout::Error => ex
- error "Disconnect timeout"
+ error "Rye::Box: Disconnect timeout (#{ex.message})"
+ debug ex.backtrace
rescue Interrupt
debug "Exiting..."
end
end