#!/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?? begin require 'drydock' require 'rudy' require 'rudy/cli' rescue Interrupt exit end # Command-line interface for /bin/rudy class RudyCLI < Rudy::CLI::Base debug :off 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 (PARTIALLY SUPPORTED)." global :P, :parallel, "Execute remote commands in parallel (PARTIALLY SUPPORTED)." global :F, :force, "Force an action despite warnings" global :positions, Integer, "Override positions number for the current role" # ------------------------------------------ RUDY OBJECTS -------- # ------------------------------------------------------------------ about "View Machines" usage "rudy" usage "rudy machines" usage "rudy machines -l" usage "rudy machines -U" usage "rudy machines -A [static ip address]" usage "rudy machines -N" usage "rudy machines -T" usage "rudy machines -P" usage "rudy machines -O" action :W, :wash, "Wash machine metadata" action :T, :available, "Test availablity" action :U, :update, "Update machines based on configuration" action :A, :associate, "Associate static IP addresses or display existing ones" action :N, :disassociate, "Disassociate static IP addresses" action :P, :password, "Display admin password (Windows only)" action :O, :console, "Display console output" option :l, :all, "Display machines for all environments and roles" command :machines => Rudy::CLI::Machines command_alias :machines, :m about "View Keypairs" usage "rudy keypairs" #usage "rudy keypairs -A" usage "rudy keypairs -S" action :A, :add, "Add a keypair" action :S, :show, "Show a private key" command :keypairs => Rudy::CLI::Keypairs command_alias :keypairs, :k about "View Disks" usage "rudy disks" usage "rudy disks -l" usage "rudy disks -C -s 1 /path/2/mount" option :s, :size, Integer, "Volume size (GB)" option :d, :device, String, "Device ID" option :f, :fstype, String, "Filesystem type" option :b, :backups, "Display backups" option :l, :all, "Display all disks" action :C, :create, "Create disk" action :D, :destroy, "Destroy disk" action :W, :wash, "Wash disk metadata" command :disks => Rudy::CLI::Disks command_alias :disks, :d about "View Backups" usage "rudy backups" usage "rudy backups -l" usage "rudy backups -C /disk/mount/point" option :l, :all, "Display all backups" action :W, :wash, "Wash backup metadata" action :C, :create, "Create backup" 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 "View Network configuration" usage "rudy networks" usage "rudy networks -C" usage "rudy networks -D" usage "rudy networks -L" usage "rudy networks -L -i" usage "rudy networks -L -e" usage "rudy networks -U" usage "rudy networks -A [-p ports] [-a addresses] [-r protocols] " usage "rudy networks -A -p 81,8000-9000 -a 127.0.0.1,127.0.0.2" usage "rudy networks -A -g default -o 123456789012" usage "rudy networks -R -g default" option :e, :external, "Display only external IP address" option :i, :internal, "Display only internal IP address" option :r, :protocols, Array, "List of protocols. One of: tcp (default), udp, icmp" option :p, :ports, Array, "List of port ranges (default: 22,80,443)" option :a, :addresses, Array, "List of IP addresses (default: your current external IP)" option :g, :group, String, "Other group to authorize or revoke. Use with -o!" option :o, :owner, String, "Other group owner ID (account number). Use with -g!" action :U, :update, "Update networks based on configuration" action :L, :local, "Show local network configuration" action :C, :create, "Create the network security group" action :D, :destroy, "Create the network security group" action :A, :authorize, "Authorize a rule for a network security group" action :R, :revoke, "Revoke a rule for a network security group" command :networks => Rudy::CLI::Networks command_alias :networks, :n # ----------------------------------------- RUDY ROUTINES -------- # ------------------------------------------------------------------ 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 # 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 -------- # ------------------------------------------------------------------ about "Display existing environment objects" option :l, :all, "Include all regions" command :info => Rudy::CLI::Info 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 about "Check Rudy configuration." usage "rudy [-f config-file] config [param-name]" 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_alias :config, :configs command :print_global => Rudy::CLI::Config command_alias :print_global, :globals command_alias :print_global, :global about "Create a machine image from a running instance (Windows only)" usage "rudy -b BUCKET-NAME bundle IMAGE-NAME" argv :name command :bundle => Rudy::CLI::Images about "Check status of bundling process (Windows only)" usage "rudy -b BUCKET-NAME bundle-status IMAGE-NAME" argv :name command :bundle_status => Rudy::CLI::Images about "Display machine images" usage "rudy images" usage "rudy images -o amazon" usage "rudy -b BUCKET-NAME images -R IMAGE-NAME" usage "rudy images -D AMI" option :o, :owner, String, "Owner ID (default: self)" action :R, :register, "Register a machine image" action :D, :deregister, "De-register a machine image (does not delete from S3)" argv :name command :images => Rudy::CLI::Images command_alias :images, :im 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 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 0 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 [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 about "Display basic system information" 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