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