#!/usr/bin/ruby # = Rudy # # === Not your granparent's deployment tool # # See rudy -h for usage # $:.unshift File.join(File.dirname(__FILE__), '..', 'lib') # Put our local lib in first place $:.unshift File.join(File.dirname(__FILE__), '..', '..', 'drydock', 'lib') #$SAFE = 1 # require is unsafe in Ruby 1.9?? require 'drydock' require 'rudy' require 'rudy/cli' # Command-line interface for /bin/rudy class RudyCLI < Rudy::CLI::Base default :machines # when no command is provided trawler :passthrough # unknown command names will forward here. # ------------------------------------------ RUDY GLOBALS -------- # ------------------------------------------------------------------ global :e, :environment, String, "Connect to the specified environment (e.g. #{Rudy::DEFAULT_ENVIRONMENT})" global :r, :role, String, "Connect to a machine with the specified role (e.g. #{Rudy::DEFAULT_ROLE})" global :p, :position, String, "Position in the machine in its group" global :b, :bucket, String, "An S3 bucket name (used when creating images)" global :t, :testrun, "Test run. Don't execute action (EXPERIMENTAL)." global :P, :parallel, "Execute remote commands in parallel (EXPERIMENTAL)." global :F, :force, "Force an action despite warnings" # ------------------------------------------ RUDY OBJECTS -------- # ------------------------------------------------------------------ about "View Routines" usage "rudy routines" usage "rudy routines -l" option :l, :all, "Display routines for all environments and roles" command :routines => Rudy::CLI::Routines command_alias :routines, :r about "View Machines" usage "rudy" usage "rudy machines" usage "rudy machines -l" option :l, :all, "Display machines for all environments and roles" action :W, :wash, "Wash machine metadata." command :machines => Rudy::CLI::Machines command_alias :machines, :m about "View Disks" usage "rudy disks" usage "rudy disks -l" action :W, :wash, "Wash disk metadata." option :b, :backups, "Display backups" option :l, :all, "Display all disks" command :disks => Rudy::CLI::Disks command_alias :disks, :d about "View Backups" usage "rudy backups" usage "rudy backups -l" action :W, :wash, "Wash backups metadata." option :l, :all, "Display all backups" command :backups => Rudy::CLI::Backups command_alias :backups, :b about "View raw metadata" usage "rudy metadata" usage "rudy metadata -l -r [disk|back|m]" usage "rudy metadata -D object-id" option :r, :rtype, String, "Record type. One of: disk, back, m (default)" option :l, :all, "Display metadata for all environments and roles" action :D, :delete, "Delete an object" argv :oid command :metadata => Rudy::CLI::Metadata about "Log in to a machine" command :ssh => Rudy::CLI::Machines #about "Open the machine in your default browser (OSX only)" #option :s, :https, "Use HTTPS" #option :p, :port, Integer, "Port" #command :open => Rudy::CLI::Candy # ----------------------------------------- RUDY ROUTINES -------- # ------------------------------------------------------------------ # A "do nothing" routine. Passthrough simply executes a routine # config block. Drydock's trawler uses this for unknown commands. about "A passthrough for custom routines" usage "rudy [custom-routine]" option :m, :message, String, "A message" command :passthrough => Rudy::CLI::Routines about "Startup a machine group" usage "rudy startup" option :m, :message, String, "A message" command :startup => Rudy::CLI::Routines about "Shutdown a machine group" usage "rudy shutdown" option :m, :message, String, "A message" command :shutdown => Rudy::CLI::Routines about "Reboot a machine group" usage "rudy reboot" option :m, :message, String, "A message" command :reboot => Rudy::CLI::Routines # ------------------------------------ RUDY MISCELLANEOUS -------- # ------------------------------------------------------------------ usage "rudy [-f config-file] config [param-name]" about "Check Rudy configuration." option :l, :all, "Display all configs for all machines" option :commands, "Display commands configuration" option :defaults, "Display defaults configuration" option :machines, "Display machines configuration" option :accounts, "Display accounts configuration" option :routines, "Display routines configuration" option :script, "Output configuration identical to what is provided to scripts called in routines" option :project, "Output a skeleton Rudyfile" #option :d, :defaults, "Display the default value for the supplied parameter" #option :g, :group, String, "Display configuration for a specific group" argv :name command :config => Rudy::CLI::Config command :print_global => Rudy::CLI::Config command_alias :print_global, :global about "Initialize Rudy configuration" command :init do |obj| Rudy::Huxtable.update_config unless File.exists? Rudy::CONFIG_FILE Rudy::Config.init_config_dir end begin if Rudy::Huxtable.domain_exists? puts "SimpleDB domain #{Rudy::Huxtable.domain} already exists" else puts "Creating SimpleDB domain #{Rudy::Huxtable.domain}" Rudy::Huxtable.create_domain end user, host = Rudy.sysinfo.user, Rudy::Huxtable.global.localhost puts "Authorizing public keys for #{user}@#{host}" rbox = Rye::Box.new host rbox.authorize_keys_local rescue Rudy::AWS::SDB::NoSecretKey, Rudy::AWS::SDB::NoAccessKey, Rudy::NoConfig => ex puts "AWS credentials must be configured to continue." puts "You can modify these in #{Rudy::CONFIG_FILE}" exit 1 end obj.global.quiet = true # don't print elapsed time end about "Display time (in UTC)" option :l, :local, "Display local time" command :time do |obj| t = obj.option.local ? Time.now : Time.now.utc puts '%s' % t.strftime("%Y-%m-%d %T %Z (%z)") end usage "rudy myaddress [-i] [-e]" about "Displays you current internal and external IP addresses" option :e, :external, "Display only external IP address" option :i, :internal, "Display only internal IP address" command :myaddress do |obj| ea = Rudy::Utils::external_ip_address || '' ia = Rudy::Utils::internal_ip_address || '' if obj.global.quiet puts ia unless obj.option.external && !obj.option.internal puts ea unless obj.option.internal && !obj.option.external else puts "%10s: %s" % ['Internal', ia] unless obj.option.external && !obj.option.internal puts "%10s: %s" % ['External', ea] unless obj.option.internal && !obj.option.external end obj.global.quiet = true # don't print elapsed time end usage "rudy [global options] annoy [-h -m -l] [-e]" about "Play around with Rudy's annoying challenges" option :s, :string, "A numeric challenge" option :n, :numeric, "A numeric challenge" option :i, :insane, "Insane annoyance factor" option :h, :high, "High annoyance factor" option :m, :medium, "Medium annoyance factor" option :l, :low, "Low annoyance factor" option :r, :rand, "Random challenge type" command :annoy do |obj| srand(Time.now.to_f) flavor = [:numeric, :string, :rand].detect { |v| obj.option.send(v) } || :string factor = [:insane, :high, :medium, :low].detect { |v| obj.option.send(v) } || :medium success = Annoy.challenge?("Is this annoying?", factor, flavor) puts (success ? "Correct!" : "WRONG!").bright obj.global.quiet = true # don't print elapsed time end about "Display the current Rudy slogan" command :slogan do |obj| puts "Rudy: Not your grandparent's deployment tool!" obj.global.quiet = true # don't print elapsed time end about "Generates a configuration template to #{Rudy::CONFIG_FILE}" command :generate_config do |obj| unless File.exists?(Rudy::CONFIG_FILE) Rudy::Config.init_config_dir puts "Add your AWS credentials to #{Rudy::CONFIG_FILE}" else puts "#{Rudy::CONFIG_FILE} already exists" end end command :sysinfo do puts Rudy.sysinfo.to_yaml end end begin Drydock.run!(ARGV, STDIN) if Drydock.run? && !Drydock.has_run? rescue Drydock::ArgError, Drydock::OptError => ex STDERR.puts ex.message STDERR.puts ex.usage rescue Drydock::InvalidArgument => ex STDERR.puts ex.message rescue Rudy::Error => ex STDERR.puts ex.message STDERR.puts ex.backtrace if Drydock.debug? rescue Interrupt puts "#{$/}Exiting... " exit 1 rescue => ex STDERR.puts "ERROR (#{ex.class.to_s}): #{ex.message}" STDERR.puts ex.backtrace if Drydock.debug? end