Module: Rye
- Extended by:
- Rye
- Included in:
- Rye
- Defined in:
- lib/rye.rb,
lib/rye/box.rb,
lib/rye/cmd.rb,
lib/rye/key.rb,
lib/rye/rap.rb,
lib/rye/set.rb
Overview
Rye
Safely run remote commands via SSH in Ruby.
Rye is similar to Rush but everything happens over SSH (no HTTP daemon) and the default settings are less dangerous (for safety). For example, file globs and the “rm” command are disabled so unless otherwise specified, you can’t do this: rbox.rm('/etc/*/').
However, you can do this:
rset = Rye::Set.new(“dev-machines”) rset.add_boxes(‘host1’, ‘host2’, ‘host3’, ‘host4’) rset.ps(‘aux’)
-
See
bin/tryfor a bunch of working examples. -
See Rye::Box#initialize for info about disabling safe-mode.
– The following posts were really helpful when I first wrote Rye: www.nofluffjuststuff.com/blog/david_bock/2008/10/ruby_s_closure_cleanup_idiom_and_net_ssh.html groups.google.com/group/ruby-talk-google/browse_thread/thread/674a6f6de15ceb49?pli=1 paste.lisp.org/display/6912 ++
Defined Under Namespace
Modules: Cmd Classes: Box, CommandError, CommandNotFound, Key, NoBoxes, NoHost, NoPassword, NoPty, NotConnected, Rap, RyeError, Set
Constant Summary collapse
- VERSION =
"0.8.7".freeze
- SYSINFO =
SysInfo.new.freeze
- @@agent_env =
holds ssh-agent env vars
Hash.new
- @@mutex =
for synchronizing threads
Mutex.new
Class Method Summary collapse
-
.sysinfo ⇒ Object
Accessor for an instance of SystemInfo.
Instance Method Summary collapse
-
#add_keys(*keys) ⇒ Object
Add one or more private keys to the SSH Agent.
-
#escape(safe, cmd, *args) ⇒ Object
Creates a string from
cmdandargs. -
#find_private_keys(path) ⇒ Object
Looks for private keys in
pathand returns and Array of paths to the files it fines. -
#keys ⇒ Object
Returns an Array of info about the currently available SSH keys, as provided by the SSH Agent.
- #mutex ⇒ Object
-
#prepare_command(cmd, *args) ⇒ Object
Takes a command with arguments and returns it in a single String with escaped args and some other stuff.
-
#reload ⇒ Object
Reload Rye dynamically.
- #remote_host_keys(*hostnames) ⇒ Object
-
#shell(cmd, *args) ⇒ Object
Execute a local system command (via the shell, not SSH) *
cmdthe executable path (relative or absolute) *argsArray of arguments to be sent to the command. - #sshagent_info ⇒ Object
-
#strand(len = 8, safe = true) ⇒ Object
Generates a string of random alphanumeric characters.
-
#sysinfo ⇒ Object
Accessor for an instance of SystemInfo.
-
#which(executable) ⇒ Object
An all ruby implementation of unix “which” command.
-
#without_indent(str) ⇒ Object
Returns
strwith the leading indentation removed.
Class Method Details
.sysinfo ⇒ Object
Accessor for an instance of SystemInfo
54 |
# File 'lib/rye.rb', line 54 def Rye.sysinfo; SYSINFO; end |
Instance Method Details
#add_keys(*keys) ⇒ Object
Add one or more private keys to the SSH Agent.
-
keysone or more file paths to private keys used for passwordless logins.
122 123 124 125 126 |
# File 'lib/rye.rb', line 122 def add_keys(*keys) keys = keys.flatten.compact || [] return if keys.empty? Rye.shell("ssh-add", keys) end |
#escape(safe, cmd, *args) ⇒ Object
Creates a string from cmd and args. If safe is true it will send them through Escape.shell_command otherwise it will return them joined by a space character.
232 233 234 235 |
# File 'lib/rye.rb', line 232 def escape(safe, cmd, *args) args = args.flatten.compact || [] safe ? Escape.shell_command(cmd, *args).to_s : [cmd, args].flatten.compact.join(' ') end |
#find_private_keys(path) ⇒ Object
Looks for private keys in path and returns and Array of paths to the files it fines. Raises an Exception if path does not exist. If path is a file rather than a directory, it will check whether that single file is a private key.
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/rye.rb', line 99 def find_private_keys(path) raise "#{path} does not exist" unless File.exists?(path || '') if File.directory?(path) files = Dir.entries(path).collect { |file| File.join(path, file) } else files = [path] end files = files.select do |file| next if File.directory?(file) pk = nil begin tmp = Rye::Key.from_file(file) pk = tmp if tmp.private? rescue OpenSSL::PKey::PKeyError end !pk.nil? end files || [] end |
#keys ⇒ Object
Returns an Array of info about the currently available SSH keys, as provided by the SSH Agent. See Rye.start_sshagent_environment
Returns: [[bits, finger-print, file-path], …]
134 135 136 137 138 139 140 141 142 143 |
# File 'lib/rye.rb', line 134 def keys # 2048 76:cb:d7:82:90:92:ad:75:3d:68:6c:a9:21:ca:7b:7f /Users/rye/.ssh/id_rsa (RSA) # 2048 7b:a6:ba:55:b1:10:1d:91:9f:73:3a:aa:0c:d4:88:0e /Users/rye/.ssh/id_dsa (DSA) #keystr = Rye.shell("ssh-add", '-l') #return nil unless keystr #keystr.collect do |key| # key.split(/\s+/) #end Dir.glob(File.join(Rye.sysinfo.home, '.ssh', 'id_*sa')) end |
#mutex ⇒ Object
91 92 93 |
# File 'lib/rye.rb', line 91 def mutex @@mutex end |
#prepare_command(cmd, *args) ⇒ Object
Takes a command with arguments and returns it in a single String with escaped args and some other stuff.
-
cmdThe shell command name or absolute path. -
argsan Array of command arguments.
The command is searched for in the local PATH (where Rye is running). An exception is raised if it’s not found. NOTE: Because this happens locally, you won’t want to use this method if the environment is quite different from the remote machine it will be executed on.
The command arguments are passed through Escape.shell_command (that means you can’t use environment variables or asterisks).
167 168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/rye.rb', line 167 def prepare_command(cmd, *args) args &&= [args].flatten.compact found_cmd = Rye.which(cmd) raise CommandNotFound.new(cmd || '[unknown]') unless found_cmd # Symbols to switches. :l -> -l, :help -> --help args.collect! do |a| a = "-#{a}" if a.is_a?(Symbol) && a.to_s.size == 1 a = "--#{a}" if a.is_a?(Symbol) a end Rye.escape(@safe, found_cmd, *args) end |
#reload ⇒ Object
Reload Rye dynamically. Useful with irb. NOTE: does not reload rye.rb.
86 87 88 89 |
# File 'lib/rye.rb', line 86 def reload pat = File.join(File.dirname(__FILE__), 'rye') %w{key rap cmd box set}.each {|lib| load File.join(pat, "#{lib}.rb") } end |
#remote_host_keys(*hostnames) ⇒ Object
145 146 147 148 149 |
# File 'lib/rye.rb', line 145 def remote_host_keys(*hostnames) hostnames = hostnames.flatten.compact || [] return if hostnames.empty? Rye.shell("ssh-keyscan", hostnames) end |
#shell(cmd, *args) ⇒ Object
Execute a local system command (via the shell, not SSH)
-
cmdthe executable path (relative or absolute) -
argsArray of arguments to be sent to the command. Each element
is one argument:. i.e. ['-l', 'some/path']
NOTE: shell is a bit paranoid so it escapes every argument. This means you can only use literal values. That means no asterisks too.
Returns a Rye::Rap object.
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 |
# File 'lib/rye.rb', line 207 def shell(cmd, *args) args = args.flatten.compact cmd = cmd.to_s if cmd.is_a?(Symbol) # TODO: allow stdin to be sent to the cmd tf = Tempfile.new(cmd) cmd = Rye.prepare_command(cmd, args) cmd << " 2>#{tf.path}" # Redirect STDERR to file. Works in DOS too. # Deal with STDOUT handle = IO.popen(cmd, "r") stdout = handle.read.chomp handle.close # Then STDERR stderr = File.exists?(tf.path) ? File.read(tf.path) : '' tf.delete # Create the response object rap = Rye::Rap.new(self) rap.add_stdout(stdout || '') rap.add_stderr(stderr || '') rap.add_exit_code($?) rap end |
#sshagent_info ⇒ Object
237 238 239 |
# File 'lib/rye.rb', line 237 def sshagent_info @@agent_env end |
#strand(len = 8, safe = true) ⇒ Object
Generates a string of random alphanumeric characters.
-
lenis the length, an Integer. Default: 8 -
safein safe-mode, ambiguous characters are removed (default: true):i l o 1 0
253 254 255 256 257 258 259 |
# File 'lib/rye.rb', line 253 def strand( len=8, safe=true ) chars = ("a".."z").to_a + ("0".."9").to_a chars.delete_if { |v| %w(i l o 1 0).member?(v) } if safe str = "" 1.upto(len) { |i| str << chars[rand(chars.size-1)] } str end |
#sysinfo ⇒ Object
Accessor for an instance of SystemInfo
57 |
# File 'lib/rye.rb', line 57 def sysinfo; SYSINFO; end |
#which(executable) ⇒ Object
An all ruby implementation of unix “which” command.
-
executablethe name of the executable
Returns the absolute path if found in PATH otherwise nil.
185 186 187 188 189 190 191 192 193 194 |
# File 'lib/rye.rb', line 185 def which(executable) return unless executable.is_a?(String) #return executable if File.exists?(executable) # SHOULD WORK, MUST TEST shortname = File.basename(executable) dir = Rye.sysinfo.paths.select do |path| # dir contains all of the next unless File.exists? path # occurrences of shortname Dir.new(path).entries.member?(shortname) # found in the paths. end File.join(dir.first, shortname) unless dir.empty? # Return just the first end |
#without_indent(str) ⇒ Object
Returns str with the leading indentation removed. Stolen from github.com/mynyml/unindent/ because it was better.
243 244 245 246 |
# File 'lib/rye.rb', line 243 def without_indent(str) indent = str.split($/).each {|line| !line.strip.empty? }.map {|line| line.index(/[^\s]/) }.compact.min str.gsub(/^[[:blank:]]{#{indent}}/, '') end |