vim: set sw=2 ts=2 :
vim: set sw=2 ts=2 :
vim: set sw=2 ts=2 :
vim: set sw=2 ts=2 :
vim: set sw=2 ts=2 :
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/try for a bunch of working examples.
-
See Rye::Box#initialize for info about disabling safe-mode.
Methods
Public Class
Public Instance
Classes and Modules
Constants
DEBUG | = | false unless defined?(Rye::DEBUG) | ||
HOME | = | File.expand_path( File.join(File.dirname(__FILE__), '..') ) |
Public Class methods
Accessor for an instance of SystemInfo
# File lib/rye.rb, line 75 def Rye.sysinfo @@sysinfo = SysInfo.new if @@sysinfo.nil? @@sysinfo end
Public Instance methods
Add one or more private keys to the SSH Agent.
-
keys one or more file paths to private keys used for passwordless logins.
# File lib/rye.rb, line 146 def add_keys(*keys) keys = keys.flatten.compact || [] return if keys.empty? Rye.shell("ssh-add", keys) end
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.
# File lib/rye.rb, line 256 def escape(safe, cmd, *args) args = args.flatten.compact || [] safe ? Escape.shell_command(cmd, *args).to_s : [cmd, args].flatten.compact.join(' ') end
Looks for private keys in path and returns an Array of paths to the files it finds. 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.
# File lib/rye.rb, line 123 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
Returns an Array of info about the currently available SSH keys, as provided by the SSH Agent.
Returns: [[bits, finger-print, file-path], ...]
# File lib/rye.rb, line 157 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
Takes a command with arguments and returns it in a single String with escaped args and some other stuff.
-
cmd The shell command name or absolute path.
-
args an 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).
# File lib/rye.rb, line 190 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 Rye dynamically. Useful with irb. NOTE: does not reload rye.rb.
# File lib/rye.rb, line 110 def reload pat = File.join(File.dirname(__FILE__), 'rye') %{key rap cmd box set hop}.each {|lib| load File.join(pat, "#{lib}.rb") } end
# File lib/rye.rb, line 168 def remote_host_keys(*hostnames) hostnames = hostnames.flatten.compact || [] return if hostnames.empty? Rye.shell("ssh-keyscan", hostnames) end
Execute a local system command (via the shell, not SSH)
-
cmd the executable path (relative or absolute)
-
args Array 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.
# File lib/rye.rb, line 231 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_status($?) rap end
Generates a string of random alphanumeric characters.
-
len is the length, an Integer. Default: 8
-
safe in safe-mode, ambiguous characters are removed (default: true):
i l o 1 0
# File lib/rye.rb, line 277 def strand( len=8, safe=true ) chars = ("a".."z").to_a + ("0".."9").to_a chars.delete_if { |v| %(i l o 1 0).member?(v) } if safe str = "" 1.upto(len) { |i| str << chars[rand(chars.size-1)] } str end
Accessor for an instance of SystemInfo
# File lib/rye.rb, line 81 def sysinfo; Rye.sysinfo end
An all ruby implementation of unix "which" command.
-
executable the name of the executable
Returns the absolute path if found in PATH otherwise nil.
# File lib/rye.rb, line 208 def which(executable) return unless executable.is_a?(String) return executable if Rye.sysinfo.os == :windows #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
Returns str with the leading indentation removed. Stolen from github.com/mynyml/unindent/ because it was better.
# File lib/rye.rb, line 267 def without_indent(str) indent = str.split($/).each {|line| !line.strip.empty? }.map {|line| line.index(/[^\s]/) }.compact.min str.gsub(/^[[:blank:]]{#{indent}}/, '') end