#!/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') require 'rubygems' #$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 debug :on default :machines trawler :passthrough # ------------------------------------------ RUDY GLOBALS -------- # ------------------------------------------------------------------ global :e, :environment, String, "Connect to the specified environment (ie: #{Rudy::DEFAULT_ENVIRONMENT})" global :r, :role, String, "Connect to a machine with the specified role (ie: #{Rudy::DEFAULT_ROLE})" global :p, :position, String, "Position in the machine in its group (ie: #{Rudy::DEFAULT_POSITION})" # ------------------------------------------ RUDY OBJECTS -------- # ------------------------------------------------------------------ about "View Machines" option :l, :all, "Display all machines" action :W, :wash, "Wash machine metadata." command :machines => Rudy::CLI::Machines command_alias :machines, :m about "View Disks" 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 "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 skeleton routine" command :passthrough => Rudy::CLI::Routines about "Startup a machine group" usage "rudy startup" command :startup => Rudy::CLI::Routines about "Shutdown a machine group" usage "rudy shutdown" command :shutdown => Rudy::CLI::Routines about "Reboot a machine group" usage "rudy reboot" command :reboot => Rudy::CLI::Routines about "Create a release" usage "rudy release" command :release => Rudy::CLI::Routines command_alias :release, :rerelease #about "Update the release currently running in a machine group" #command :rerelease => Rudy::CLI::Routines #command_alias :rerelease, :rere #about "Deploy disk snapshots from one machine to another" #command :deploy => 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 unless Rudy::Huxtable.domain_exists? puts "Creating SimpleDB domain #{Rudy::Huxtable.domain}" Rudy::Huxtable.create_domain puts "Initialized" else puts "Already Initialized" end 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 # We call Drydock specifically otherwise it will run at_exit. Rye also # uses at_exit for shutting down the ssh-agent. Ruby executes at_exit # blocks in reverse order so if Drydock is required first, it's block # will run after Rye shuts down the ssh-agent. 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 => ex STDERR.puts "ERROR (#{ex.class.to_s}): #{ex.message}" STDERR.puts ex.backtrace if Drydock.debug? rescue Interrupt puts "#{$/}Exiting... " exit 1 rescue SystemExit # Don't balk end