Class: Rye::Box
Overview
Rye::Box
The Rye::Box class represents a machine. All system commands are made through this class.
rbox = Rye::Box.new('filibuster')
rbox.hostname # => filibuster
rbox.uname # => FreeBSD
rbox.uptime # => 20:53 up 1 day, 1:52, 4 users
You can also run local commands through SSH
rbox = Rye::Box.new('localhost')
rbox.hostname # => localhost
rbox.uname(:a) # => Darwin vanya 9.6.0 ...
Instance Attribute Summary collapse
-
#current_working_directory ⇒ Object
readonly
The most recent value from Box.cd or Box.[].
-
#host ⇒ Object
Returns the value of attribute host.
-
#opts ⇒ Object
Returns the value of attribute opts.
-
#safe ⇒ Object
Returns the value of attribute safe.
-
#ssh ⇒ Object
readonly
An instance of Net::SSH::Connection::Session.
Instance Method Summary collapse
-
#==(other) ⇒ Object
Compares itself with the
other
box. -
#[](key = nil) ⇒ Object
Change the current working directory (sort of).
-
#add_env(n, v) ⇒ Object
(also: #add_environment_variable)
Add an environment variable.
-
#add_keys(*additional_keys) ⇒ Object
(also: #add_key)
Add one or more private keys to the SSH Agent.
-
#authorize_keys ⇒ Object
Copy the local public keys (as specified by Rye.keys) to this box into ~/.ssh/authorized_keys and ~/.ssh/authorized_keys2.
-
#authorize_keys_local ⇒ Object
Authorize the current user to login to the local machine via SSH without a password.
-
#can ⇒ Object
(also: #commands, #cmds)
Returns an Array of system commands available over SSH.
-
#cd(key = nil) ⇒ Object
alias :cd :‘[]’ # fix for jruby.
-
#connect ⇒ Object
Open an SSH session with @host.
-
#disconnect ⇒ Object
Close the SSH session with @host.
-
#host_key ⇒ Object
Returns the host SSH keys for this box.
-
#initialize(host = 'localhost', opts = {}) ⇒ Box
constructor
-
host
The hostname to connect to.
-
- #inspect ⇒ Object
-
#interactive_ssh(run = true) ⇒ Object
Open an interactive SSH session.
-
#keys ⇒ Object
See Rye.keys.
-
#method_missing(meth, *args, &block) ⇒ Object
A handler for undefined commands.
- #preview_command(*args) ⇒ Object
-
#switch_user(newuser) ⇒ Object
Reconnect as another user *
newuser
The username to reconnect as . -
#to_s ⇒ Object
Returns @host.
- #user ⇒ Object
Methods included from Cmd
#awk, #bash, #cat, #chmod, #cp, #cvs, #date, #df, #du, #echo, #env, #exists?, #git, #grep, #history, #hostname, #ls, #mkdir, #mkfs, #mount, #mv, #perl, #printenv, #ps, #pwd, #python, #ruby, #sed, #sh, #sleep, #sudo, #svn, #test, #touch, #umount, #uname, #uptime, #wc
Constructor Details
#initialize(host = 'localhost', opts = {}) ⇒ Box
-
host
The hostname to connect to. The default is localhost. -
opts
a hash of optional arguments.
The opts
hash excepts the following keys:
-
:user => the username to connect as. Default: the current user.
-
:safe => should Rye be safe? Default: true
-
:keys => one or more private key file paths (passwordless login)
-
:password => the user’s password (ignored if there’s a valid private key)
-
: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
NOTE: opts
can also contain any parameter supported by Net::SSH.start that is not already mentioned above.
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/rye/box.rb', line 54 def initialize(host='localhost', opts={}) # These opts are use by Rye::Box and also passed to Net::SSH @opts = { :user => Rye.sysinfo.user, :safe => true, :port => 22, :keys => [], :debug => nil, :error => STDERR, }.merge(opts) # See Net::SSH.start @opts[:paranoid] = true unless @opts[:safe] == false # Close the SSH session before Ruby exits. This will do nothing # if disconnect has already been called explicitly. at_exit { self.disconnect } @host = host @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) debug "ssh-agent info: #{Rye.sshagent_info.inspect}" end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(meth, *args, &block) ⇒ Object
A handler for undefined commands. Raises Rye::CommandNotFound exception.
308 309 310 |
# File 'lib/rye/box.rb', line 308 def method_missing(meth, *args, &block) raise Rye::CommandNotFound, "#{meth.to_s} (args: #{args.join(' ')})" end |
Instance Attribute Details
#current_working_directory ⇒ Object (readonly)
The most recent value from Box.cd or Box.[]
37 38 39 |
# File 'lib/rye/box.rb', line 37 def current_working_directory @current_working_directory end |
#host ⇒ Object
Returns the value of attribute host.
31 32 33 |
# File 'lib/rye/box.rb', line 31 def host @host end |
#opts ⇒ Object
Returns the value of attribute opts.
34 35 36 |
# File 'lib/rye/box.rb', line 34 def opts @opts end |
#safe ⇒ Object
Returns the value of attribute safe.
33 34 35 |
# File 'lib/rye/box.rb', line 33 def safe @safe end |
#ssh ⇒ Object (readonly)
An instance of Net::SSH::Connection::Session
26 27 28 |
# File 'lib/rye/box.rb', line 26 def ssh @ssh end |
Instance Method Details
#==(other) ⇒ Object
Compares itself with the other
box. If the hostnames are the same, this will return true. Otherwise false.
253 254 255 |
# File 'lib/rye/box.rb', line 253 def ==(other) @host == other.host end |
#[](key = nil) ⇒ Object
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 which is like a funky helper method that opens a new channel each time it’s called. This seems to be okay for one-off commands but changing the directory only works for the channel it’s executed in. The next time exec is called, there’s a new channel which is back in the default (home) directory.
Long story short, the work around is to maintain the current directory locally and send it with each command.
rbox.pwd # => /home/rye ($ pwd )
rbox['/usr/bin'].pwd # => /usr/bin ($ cd /usr/bin && pwd)
rbox.pwd # => /usr/bin ($ cd /usr/bin && pwd)
122 123 124 125 |
# File 'lib/rye/box.rb', line 122 def [](key=nil) @current_working_directory = key self end |
#add_env(n, v) ⇒ Object Also known as: add_environment_variable
Add an environment variable. n
and v
are the name and value. Returns the instance of Rye::Box
223 224 225 226 227 |
# File 'lib/rye/box.rb', line 223 def add_env(n, v) debug "Added env: #{n}=#{v}" (@current_environment_variables ||= {})[n] = v self end |
#add_keys(*additional_keys) ⇒ Object Also known as: add_key
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
208 209 210 211 212 213 214 215 216 217 218 |
# File 'lib/rye/box.rb', line 208 def add_keys(*additional_keys) additional_keys = [additional_keys].flatten.compact || [] 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 #MUST RETURN itself end |
#authorize_keys ⇒ Object
Copy the local public keys (as specified by Rye.keys) to this box into ~/.ssh/authorized_keys and ~/.ssh/authorized_keys2. Returns an Array of the private keys files used to generate the public keys.
NOTE: authorize_keys disables safe-mode for this box while it runs which will hit you funky style if your using a single instance of Rye::Box in a multithreaded situation.
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 |
# File 'lib/rye/box.rb', line 271 def added_keys = [] @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, :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 |
#authorize_keys_local ⇒ Object
Authorize the current user to login to the local machine via SSH without a password. This is the same functionality as authorize_keys except run with local shell commands.
291 292 293 294 295 296 297 298 299 300 301 302 303 304 |
# File 'lib/rye/box.rb', line 291 def added_keys = [] Rye.keys.each do |key| path = key[2] debug "# Public key for #{path}" k = Rye::Key.from_file(path).public_key.to_ssh2 Rye.shell(:mkdir, :p, :m, '700', '$HOME/.ssh') # Silently create dir if it doesn't exist Rye.shell(:echo, "'#{k}' >> $HOME/.ssh/authorized_keys") Rye.shell(:echo, "'#{k}' >> $HOME/.ssh/authorized_keys2") Rye.shell(:chmod, '-R', '0600', '$HOME/.ssh/authorized_keys*') added_keys << path end added_keys end |
#can ⇒ Object Also known as: commands, cmds
Returns an Array of system commands available over SSH
96 97 98 |
# File 'lib/rye/box.rb', line 96 def can Rye::Cmd.instance_methods end |
#cd(key = nil) ⇒ Object
alias :cd :‘[]’ # fix for jruby
127 128 129 130 |
# File 'lib/rye/box.rb', line 127 def cd(key=nil); @current_working_directory = key self end |
#connect ⇒ Object
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.
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/rye/box.rb', line 137 def connect raise Rye::NoHost unless @host disconnect if @ssh debug "Opening connection to #{@host} as #{@opts[:user]}" 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] << 'password' retry else STDERR.puts "Authentication failed." exit 0 end end # We add :auth_methods (a Net::SSH joint) to force asking for a # password if the initial (key-based) authentication fails. We # need to delete the key from @opts otherwise it lingers until # the next connection (if we switch_user is called for example). @opts.delete :auth_methods if @opts.has_key?(:auth_methods) @ssh.is_a?(Net::SSH::Connection::Session) && !@ssh.closed? self end |
#disconnect ⇒ Object
Close the SSH session with @host. This is called automatically at exit if the connection is open.
171 172 173 174 175 176 |
# File 'lib/rye/box.rb', line 171 def disconnect return unless @ssh && !@ssh.closed? @ssh.loop(0.1) { @ssh.busy? } debug "Closing connection to #{@ssh.host}" @ssh.close end |
#host_key ⇒ Object
Returns the host SSH keys for this box
258 259 260 261 |
# File 'lib/rye/box.rb', line 258 def host_key raise "No host" unless @host Rye.remote_host_keys(@host) end |
#inspect ⇒ Object
244 245 246 247 248 249 |
# File 'lib/rye/box.rb', line 244 def inspect %q{#<%s:%s cwd=%s env=%s safe=%s opts=%s>} % [self.class.to_s, self.host, @current_working_directory, (@current_environment_variables || '').inspect, self.safe, self.opts.inspect] end |
#interactive_ssh(run = true) ⇒ Object
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.
197 198 199 200 201 202 203 |
# File 'lib/rye/box.rb', line 197 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 |
#preview_command(*args) ⇒ Object
311 312 313 |
# File 'lib/rye/box.rb', line 311 def preview_command(*args) prep_args(*args).join(' ') end |
#switch_user(newuser) ⇒ Object
Reconnect as another user
-
newuser
The username to reconnect as
NOTE: if there is an open connection, it’s disconnected and a new one is opened for the given user.
183 184 185 186 187 188 189 |
# File 'lib/rye/box.rb', line 183 def switch_user(newuser) return if newuser.to_s == self.user.to_s @opts ||= {} @opts[:user] = newuser disconnect connect end |
#to_s ⇒ Object
Returns @host
240 241 242 |
# File 'lib/rye/box.rb', line 240 def to_s @host end |
#user ⇒ Object
230 231 232 |
# File 'lib/rye/box.rb', line 230 def user (@opts || {})[:user] end |