lib/rye/box.rb in rye-0.4.1 vs lib/rye/box.rb in rye-0.4.3
- old
+ new
@@ -15,11 +15,11 @@
#
# You can also run local commands through SSH
#
# rbox = Rye::Box.new('localhost')
# rbox.hostname # => localhost
- # rbox.uname # => Darwin
+ # rbox.uname(:a) # => Darwin vanya 9.6.0 ...
#
class Box
include Rye::Cmd
# An instance of Net::SSH::Connection::Session
@@ -76,16 +76,21 @@
@safe = @opts.delete(:safe)
@debug = @opts.delete(:debug)
@error = @opts.delete(:error)
+ debug @opts.inspect
+
add_keys(@opts[:keys])
# We don't want Net::SSH to handle the keypairs. This may change
# but for we're letting ssh-agent do it.
- @opts.delete(:keys)
+ #@opts.delete(:keys)
+
+ debug "ssh-agent info: #{Rye.sshagent_info.inspect}"
+
end
# Returns an Array of system commands available over SSH
def can
@@ -121,19 +126,51 @@
alias :cd :'[]'
# Open an SSH session with +@host+. This called automatically
# when you the first comamnd is run if it's not already connected.
# Raises a Rye::NoHost exception if +@host+ is not specified.
+ # Will attempt a password login up to 3 times if the initial
+ # authentication fails.
def connect
raise Rye::NoHost unless @host
disconnect if @ssh
debug "Opening connection to #{@host}"
- @ssh = Net::SSH.start(@host, @opts[:user], @opts || {})
+ highline = HighLine.new # Used for password prompt
+ retried = 0
+ begin
+ @ssh = Net::SSH.start(@host, @opts[:user], @opts || {})
+ rescue Net::SSH::AuthenticationFailed => ex
+ retried += 1
+ if STDIN.tty? && retried <= 3
+ @opts[:password] = highline.ask("Password: ") { |q| q.echo = '' }
+ @opts[:auth_methods] ||= []
+ @opts[:auth_methods] = %w(password)
+ retry
+ else
+ STDERR.puts "Authentication failed."
+ exit 0
+ end
+ end
+
@ssh.is_a?(Net::SSH::Connection::Session) && !@ssh.closed?
self
end
-
+
+ # Open an interactive SSH session. This only works if STDIN.tty?
+ # returns true. Otherwise it returns the SSH command that would
+ # have been run. This requires the SSH command-line executable (ssh).
+ # * +run+ when set to false, it will return the SSH command as a String
+ # and not open an SSH session.
+ #
+ def interactive_ssh(run=true)
+ debug "interactive_ssh with keys: #{Rye.keys.inspect}"
+ run = false unless STDIN.tty?
+ cmd = Rye.prepare_command("ssh", "#{@opts[:user]}@#{@host}")
+ return cmd unless run
+ system(cmd)
+ end
+
# Close the SSH session with +@host+. This is called
# automatically at exit if the connection is open.
def disconnect
return unless @ssh && !@ssh.closed?
@ssh.loop(0.1) { @ssh.busy? }
@@ -144,11 +181,17 @@
# Add one or more private keys to the SSH Agent.
# * +additional_keys+ is a list of file paths to private keys
# Returns the instance of Box
def add_keys(*additional_keys)
additional_keys = [additional_keys].flatten.compact || []
- Rye.add_keys(additional_keys)
+ return if additional_keys.empty?
+ ret = Rye.add_keys(additional_keys)
+ if ret.is_a?(Rye::Rap)
+ debug "ssh-add exit_code: #{ret.exit_code}"
+ debug "ssh-add stdout: #{ret.stdout}"
+ debug "ssh-add stderr: #{ret.stderr}"
+ end
self
end
alias :add_key :add_keys
# Add an environment variable. +n+ and +v+ are the name and value.
@@ -200,25 +243,31 @@
@safe= false
Rye.keys.each do |key|
path = key[2]
debug "# Public key for #{path}"
k = Rye::Key.from_file(path).public_key.to_ssh2
- self.mkdir('-p', '$HOME/.ssh') # Silently create dir if it doesn't exist
+ self.mkdir(:p, :m, '700', '$HOME/.ssh') # Silently create dir if it doesn't exist
self.echo("'#{k}' >> $HOME/.ssh/authorized_keys")
self.echo("'#{k}' >> $HOME/.ssh/authorized_keys2")
self.chmod('-R', '0600', '$HOME/.ssh/authorized_keys*')
added_keys << path
end
@safe = true
added_keys
end
+ # A handler for undefined commands.
+ # Raises Rye::CommandNotFound exception.
+ def method_missing(meth, *args, &block)
+ raise Rye::CommandNotFound, "#{meth.to_s} (args: #{args.join(' ')})"
+ end
+
private
- def debug(msg); @debug.puts msg if @debug; end
- def error(msg); @error.puts msg if @error; end
+ def debug(msg="unknown debug msg"); @debug.puts msg if @debug; end
+ def error(msg="unknown error msg"); @error.puts msg if @error; end
# Add the current environment variables to the beginning of +cmd+
def prepend_env(cmd)
return cmd unless @current_environment_variables.is_a?(Hash)
@@ -228,10 +277,11 @@
end
[env, cmd].join(' ')
end
+
# Execute a command over SSH
#
# * +args+ is a command name and list of arguments.
# The command name is the literal name of the command
# that will be executed in the remote shell. The arguments
@@ -246,10 +296,12 @@
#
# This method will try to connect to the host automatically
# but if it fails it will raise a Rye::NotConnected exception.
#
def run_command(*args)
+ debug "run_command with keys: #{Rye.keys.inspect}"
+
connect if !@ssh || @ssh.closed?
args = args.flatten.compact
args = args.first.split(/\s+/) if args.size == 1
cmd = args.shift
@@ -299,10 +351,10 @@
channel.on_request("exit-signal") do |ch, data|
# This should be the POSIX SIGNAL that ended the process
channel[:exit_signal] = data.read_long
end
# For long-running commands like top, this will print the output.
- # It cool, but we'd also need to enable STDIN to interact with
+ # It's cool, but we'd also need to enable STDIN to interact with
# command.
#channel.on_data do |ch, data|
# puts "got stdout: #{data}"
# channel.send_data "something for stdin\n"
#end