├── .gitignore ├── CHANGES.txt ├── Gemfile ├── LICENSE.txt ├── README.rdoc ├── Rakefile ├── Rudyfile ├── UPGRADING-0.9 ├── bin ├── rudy ├── rudy-ec2 ├── rudy-s3 └── rudy-sdb ├── examples ├── authorize.rb ├── gem-test.rb ├── solaris.rb ├── spot_instances.rb └── windows.rb ├── lib ├── rudy.rb └── rudy │ ├── aws.rb │ ├── aws │ ├── ec2.rb │ ├── ec2 │ │ ├── address.rb │ │ ├── group.rb │ │ ├── image.rb │ │ ├── instance.rb │ │ ├── keypair.rb │ │ ├── snapshot.rb │ │ ├── spot_request.rb │ │ ├── volume.rb │ │ └── zone.rb │ ├── s3.rb │ ├── sdb.rb │ └── sdb │ │ └── error.rb │ ├── backups.rb │ ├── cli.rb │ ├── cli │ ├── aws │ │ ├── ec2 │ │ │ ├── addresses.rb │ │ │ ├── candy.rb │ │ │ ├── groups.rb │ │ │ ├── images.rb │ │ │ ├── info.rb │ │ │ ├── instances.rb │ │ │ ├── keypairs.rb │ │ │ ├── snapshots.rb │ │ │ ├── volumes.rb │ │ │ └── zones.rb │ │ ├── s3 │ │ │ ├── buckets.rb │ │ │ └── store.rb │ │ └── sdb │ │ │ ├── domains.rb │ │ │ ├── objects.rb │ │ │ └── select.rb │ ├── backups.rb │ ├── base.rb │ ├── candy.rb │ ├── config.rb │ ├── disks.rb │ ├── execbase.rb │ ├── images.rb │ ├── info.rb │ ├── keypairs.rb │ ├── machines.rb │ ├── metadata.rb │ ├── networks.rb │ └── routines.rb │ ├── config.rb │ ├── config │ └── objects.rb │ ├── disks.rb │ ├── exceptions.rb │ ├── global.rb │ ├── guidelines.rb │ ├── huxtable.rb │ ├── machines.rb │ ├── metadata.rb │ ├── metadata │ ├── backup.rb │ ├── disk.rb │ └── machine.rb │ ├── mixins.rb │ ├── routines.rb │ ├── routines │ ├── base.rb │ ├── handlers │ │ ├── base.rb │ │ ├── depends.rb │ │ ├── disks.rb │ │ ├── group.rb │ │ ├── host.rb │ │ ├── keypair.rb │ │ ├── rye.rb │ │ ├── script.rb │ │ ├── spot_request.rb │ │ └── user.rb │ ├── passthrough.rb │ ├── reboot.rb │ ├── shutdown.rb │ └── startup.rb │ └── utils.rb ├── rudy.gemspec ├── support ├── mailtest ├── randomize-root-password └── update-ec2-ami-tools └── tryouts ├── 01_mixins └── 01_hash_tryouts.rb ├── 10_require_time ├── 10_rudy_tryouts.rb └── 15_global_tryouts.rb ├── 12_config ├── 10_load_config_tryouts.rb ├── 20_defaults_tryouts.rb ├── 30_accounts_tryouts.rb ├── 40_machines_tryouts.rb ├── 50_commands_tryouts.rb └── 60_routines_tryouts.rb ├── 15_huxtable ├── 10_huxtable_tryouts.rb └── 20_user_tryouts.rb ├── 20_simpledb ├── 10_domains_tryouts.rb └── 20_objects_tryouts.rb ├── 25_ec2 ├── 10_keypairs_tryouts.rb ├── 20_groups_tryouts.rb ├── 21_groups_authorize_address_tryouts.rb ├── 22_groups_authorize_account_tryouts.rb ├── 30_addresses_tryouts.rb ├── 40_volumes_tryouts.rb └── 50_snapshots_tryouts.rb ├── 26_ec2_instances ├── 10_instance_tryouts.rb └── 50_images_tryouts.rb ├── 30_metadata ├── 10_include_tryouts.rb ├── 13_object_tryouts.rb ├── 50_disk_tryouts.rb ├── 51_disk_digest_tryouts.rb ├── 53_disk_list_tryouts.rb ├── 56_disk_volume_tryouts.rb ├── 60_backup_tryouts.rb ├── 63_backup_list_tryouts.rb ├── 64_backup_disk_tryouts.rb ├── 66_backup_snapshot_tryouts.rb ├── 70_machine_tryouts.rb ├── 73_machine_list_tryouts.rb ├── 76_machine_instance_tryouts.rb └── 77_machines_tryouts.rb ├── 40_routines ├── 10_keypair_handler_tryouts.rb └── 11_group_handler_tryouts.rb ├── 80_cli ├── 10_rudyec2_tryouts.rb └── 60_rudy_tryouts.rb ├── exploration ├── console.rb └── machine.rb └── failer /.gitignore: -------------------------------------------------------------------------------- 1 | .rudy 2 | pkg 3 | doc 4 | Gemfile.lock 5 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | gemspec -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 Solutious Inc, Delano Mandelbaum 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | 2 | require 'rake/clean' 3 | require 'rake/gempackagetask' 4 | require 'rake/testtask' 5 | require 'rake/runtest' 6 | require 'fileutils' 7 | include FileUtils 8 | 9 | task :default => :test 10 | 11 | begin 12 | require 'hanna/rdoctask' 13 | rescue LoadError 14 | require 'rake/rdoctask' 15 | end 16 | 17 | 18 | 19 | # PACKAGE ============================================================= 20 | 21 | name = "rudy" 22 | load "#{name}.gemspec" 23 | 24 | version = @spec.version 25 | 26 | Rake::GemPackageTask.new(@spec) do |p| 27 | p.need_tar = true if RUBY_PLATFORM !~ /mswin/ 28 | end 29 | 30 | task :test do 31 | puts "Success!" 32 | end 33 | 34 | task :release => [ "publish:gem", :clean, "publish:rdoc" ] do 35 | $: << File.join(File.dirname(__FILE__), 'lib') 36 | require "rudy" 37 | abort if Drydock.debug? 38 | end 39 | 40 | task :build => [ :package ] 41 | 42 | task :install => [ :rdoc, :package ] do 43 | sh %{sudo gem install pkg/#{name}-#{version}.gem} 44 | end 45 | 46 | task :uninstall => [ :clean ] do 47 | sh %{sudo gem uninstall #{name}} 48 | end 49 | 50 | 51 | # Rubyforge Release / Publish Tasks ================================== 52 | 53 | #about 'Publish website to rubyforge' 54 | task 'publish:rdoc' => 'doc/index.html' do 55 | sh "scp -rp doc/* rubyforge.org:/var/www/gforge-projects/#{name}/" 56 | end 57 | 58 | #about 'Public release to rubyforge' 59 | task 'publish:gem' => [:package] do |t| 60 | sh <<-end 61 | rubyforge add_release -o Any -a CHANGES.txt -f -n README.rdoc #{name} #{name} #{@spec.version} pkg/#{name}-#{@spec.version}.gem && 62 | rubyforge add_file -o Any -a CHANGES.txt -f -n README.rdoc #{name} #{name} #{@spec.version} pkg/#{name}-#{@spec.version}.tgz 63 | end 64 | end 65 | 66 | 67 | Rake::RDocTask.new do |t| 68 | t.rdoc_dir = 'doc' 69 | t.title = @spec.summary 70 | t.options << '--line-numbers' << '-A cattr_accessor=object' 71 | t.options << '--charset' << 'utf-8' 72 | t.rdoc_files.include('LICENSE.txt') 73 | t.rdoc_files.include('README.rdoc') 74 | t.rdoc_files.include('CHANGES.txt') 75 | #t.rdoc_files.include('Rudyfile') # why is the formatting f'd? 76 | t.rdoc_files.include('bin/*') 77 | t.rdoc_files.include('lib/**/*.rb') 78 | end 79 | 80 | CLEAN.include [ 'pkg', '*.gem', '.config', 'doc', 'coverage*' ] 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /UPGRADING-0.9: -------------------------------------------------------------------------------- 1 | *Rudy 0.9 is not compatible with previous releases*. If you wish to upgrade, you will need to shutdown and relaunch machines and disks created with previous releases. There are several improvements to the configuration syntax and also disk backups will not be compatible between releases. If you rely on backups _you will need to make special arrangements_ (see Questions / Problems). 2 | 3 | 4 | h2. The Process 5 | 6 | _This describes the process for upgrading from 0.8. If you are using an earlier version of Rudy, see Questions & Problems below._ 7 | 8 | 9 | * Get everything you need off of your existing machines instances and disk volumes. 10 | * Shutdown existing machines and destroy disks. 11 | * Install Rudy 0.9 12 | * Update your Rudy configuration. 13 | * Startup your new machines. 14 | 15 | h3. Shutdown 16 | 17 |
    $ rudy -V
 18 |     Rudy version: 0.8.5
 19 |     
 20 |     $ rudy -vv shutdown
 21 |     ...
 22 |     
 23 |     $ rudy machines
 24 |     No Machines
 25 |     $ rudy disks
 26 |     No disks
 27 |     
 28 |     # Double check
 29 |     $ rudy-ec2 instances
 30 |     No instances running
 31 |     $ rudy-ec2 volumes
 32 |     No volumes
 33 | 
34 | 35 | h3. Install 36 | 37 |
    $ sudo gem install rudy -V 
 38 |     $ rudy -V
 39 |     Rudy version 0.9.1
 40 | 
41 | 42 | h3. Configuration Changes 43 | 44 | You will need to make the following changes to your Rudy configuration: 45 | 46 | h4. SSH commands in routines 47 | 48 | The routine syntax has some minor improvements. The @before@ and @after@ keywords are now used exclusively for defining dependencies. Local and remote blocks are now simply, @local@ and @remote@. 49 | 50 | *Old:* 51 | 52 |
routines do
 53 |   sysupdate do
 54 |     script_local do
 55 |       uptime
 56 |     end
 57 |     before :root do                  
 58 |       apt_get 'update'               
 59 |       apt_get 'install', 'build-essential'
 60 |     end
 61 |   end
 62 | end 
63 | 64 | *New:* 65 | 66 |
routines do
 67 |   sysupdate do
 68 |     local do
 69 |       uptime
 70 |     end
 71 |     remote :root do                  
 72 |       apt_get 'update'               
 73 |       apt_get 'install', 'build-essential'
 74 |     end
 75 |   end
 76 | end
77 | 78 | For startup, shutdown, and reboot routines @before_remote@, @before_local@ and @after_remote@, @after_local@ are still available where appropriate (there's no @before_remote@ for startup). 79 | 80 | 81 | h4. config block in machines config 82 | 83 | This config block is no longer used. In 0.8 a YAML file containing this configuration would be uploaded to each machine during routines. This is also removed. However, it is now possible to upload any file you want inside the remote routine blocks. 84 | 85 | *Old:* 86 | 87 |
machines do
 88 |   config do                 
 89 |     dbmaster 'localhost'
 90 |     newparam 573114
 91 |   end
 92 | end
93 | 94 | 95 | *New:* 96 | 97 |
routines do
 98 |   startup do
 99 |   remote :root do
100 |     file_upload 'local/file/path', 'remote/path'
101 |   end
102 | end
103 | 
104 | 105 | h4. Startup 106 | 107 |
$ rudy startup
108 | 
109 | 110 | 111 | h2. Questions / Problems 112 | 113 | If you have any questions or problems either before or after you upgrade to Rudy 0.9, feel free to use the "discussion group":http://groups.google.com/group/rudy-deployment or email me directly (note: if my response could help others I will post it to the discussion group). 114 | 115 | -- Delano (@solutious.com) 116 | -------------------------------------------------------------------------------- /bin/rudy-s3: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # = Rudy S3 4 | # 5 | # === A Rudy interface to Amazon Simple Storage Service 6 | # 7 | # 8 | # 9 | # See rudy-s3 -h for usage 10 | # 11 | 12 | $:.unshift File.join(File.dirname(__FILE__), '..', 'lib') # Put our local lib in first place 13 | 14 | begin 15 | require 'drydock' 16 | require 'rudy' 17 | require 'rudy/cli' 18 | rescue Interrupt 19 | exit 20 | end 21 | 22 | # Command-line interface for bin/rudy-s3 23 | module RudyCLI_S3 24 | extend Drydock 25 | 26 | # ------------------------------------ AMAZON S3 COMMANDS -------- 27 | # ------------------------------------------------------------------ 28 | 29 | about "Amazon S3 Buckets" 30 | usage "rudy-s3 buckets" 31 | option :l, :location, String, "Specify a bucket location. One of: us, eu" 32 | action :C, :create, "Create a bucket" 33 | action :D, :destroy, "Destroy a bucket" 34 | argv :name 35 | command :buckets => Rudy::CLI::AWS::S3::Buckets 36 | command_alias :buckets, :b 37 | 38 | #about "Amazon S3 Storage Interface" 39 | #usage "rudy-s3 store file-path" 40 | #option :b, :bucket, String, "Bucket name" 41 | #argv :path 42 | #command :store => Rudy::CLI::AWS::S3::Store 43 | #command_alias :store, :s 44 | 45 | 46 | 47 | # --------------------------------- RUDY-S3 MISCELLANEOUS -------- 48 | # ------------------------------------------------------------------ 49 | 50 | default :buckets 51 | 52 | end 53 | 54 | 55 | begin 56 | Drydock.run!(ARGV, STDIN) if Drydock.run? && !Drydock.has_run? 57 | rescue Drydock::ArgError, Drydock::OptError => ex 58 | STDERR.puts ex.message 59 | STDERR.puts ex.usage 60 | rescue Drydock::InvalidArgument => ex 61 | STDERR.puts ex.message 62 | rescue Rudy::Error => ex 63 | STDERR.puts ex.message 64 | STDERR.puts ex.backtrace if Drydock.debug? 65 | rescue => ex 66 | STDERR.puts "ERROR (#{ex.class.to_s}): #{ex.message}" 67 | STDERR.puts ex.backtrace if Drydock.debug? 68 | rescue Interrupt 69 | puts "#{$/}Exiting... " 70 | exit 1 71 | end 72 | 73 | -------------------------------------------------------------------------------- /bin/rudy-sdb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # = Rudy SDB 4 | # 5 | # === A Rudy interface to Amazon Simple Storage Service 6 | # 7 | # 8 | # 9 | # See rudy-sdb -h for usage 10 | # 11 | 12 | $:.unshift File.join(File.dirname(__FILE__), '..', 'lib') # Put our local lib in first place 13 | 14 | begin 15 | require 'drydock' 16 | require 'rudy' 17 | require 'rudy/cli' 18 | rescue Interrupt 19 | exit 20 | end 21 | 22 | # Command-line interface for bin/rudy-sdb 23 | module RudyCLI_SDB 24 | extend Drydock 25 | 26 | # ----------------------------------- AMAZON SDB COMMANDS -------- 27 | # ------------------------------------------------------------------ 28 | 29 | 30 | about "Displays the SimpleDB domains associated to your account" 31 | action :C, :create, "Create a domain" 32 | action :D, :destroy, "Destroy a domain" 33 | argv :name 34 | command :domains => Rudy::CLI::AWS::SDB::Domains 35 | command_alias :domains, :domain 36 | 37 | about "Displays objects contained in a SimpleDB domain" 38 | action :D, :destroy, "Remove a key from the domain (careful!)" 39 | argv :name, :key 40 | command :objects => Rudy::CLI::AWS::SDB::Objects 41 | command_alias :objects, :object 42 | 43 | about "Execute a SimpleDB select query" 44 | argv :query 45 | command :query => Rudy::CLI::AWS::SDB::Select 46 | 47 | 48 | # -------------------------------- RUDY-SDB MISCELLANEOUS -------- 49 | # ------------------------------------------------------------------ 50 | 51 | default :domains 52 | 53 | end 54 | 55 | begin 56 | Drydock.run!(ARGV, STDIN) if Drydock.run? && !Drydock.has_run? 57 | rescue Drydock::ArgError, Drydock::OptError => ex 58 | STDERR.puts ex.message 59 | STDERR.puts ex.usage 60 | rescue Drydock::InvalidArgument => ex 61 | STDERR.puts ex.message 62 | rescue Rudy::Error => ex 63 | STDERR.puts ex.message 64 | STDERR.puts ex.backtrace if Drydock.debug? 65 | rescue => ex 66 | STDERR.puts "ERROR (#{ex.class.to_s}): #{ex.message}" 67 | STDERR.puts ex.backtrace if Drydock.debug? 68 | rescue Interrupt 69 | puts "#{$/}Exiting... " 70 | exit 1 71 | end 72 | 73 | -------------------------------------------------------------------------------- /examples/authorize.rb: -------------------------------------------------------------------------------- 1 | 2 | machines do 3 | env :stage do 4 | user :jira 5 | ami 'ami-e348af8a' # Alestic Debian 5.0, 32-bit (US) 6 | end 7 | end 8 | 9 | routines do 10 | setup do 11 | # NOTE: This fails b/c it's trying to login as jira rather than root. 12 | adduser :jira 13 | authorize :jira 14 | end 15 | end -------------------------------------------------------------------------------- /examples/gem-test.rb: -------------------------------------------------------------------------------- 1 | # Rudy Gem Test 2 | # 3 | # This configuration is used to 4 | # test installing the Rudy gem. 5 | 6 | defaults do 7 | color true 8 | environment :test 9 | role :rudy 10 | end 11 | 12 | machines do 13 | region :'us-east-1' do 14 | ami 'ami-e348af8a' # Alestic Debian 5.0, 32-bit (US) 15 | end 16 | env :test do 17 | role :rudy do 18 | user :root 19 | end 20 | end 21 | end 22 | 23 | commands do 24 | allow :apt_get, "apt-get", :y, :q 25 | allow :gem_install, "/usr/bin/gem", "install", :n, '/usr/bin', :y, :V, "--no-rdoc", "--no-ri" 26 | allow :gem_sources, "/usr/bin/gem", "sources" 27 | allow :gem_uninstall, "/usr/bin/gem", "uninstall", :V 28 | allow :update_rubygems 29 | allow :rake 30 | allow :rm 31 | end 32 | 33 | routines do 34 | 35 | install_rubyforge do 36 | remote :root do 37 | gem_install 'rudy', :V 38 | end 39 | end 40 | 41 | install_github do 42 | remote :root do 43 | gem_sources :a, "http://gems.github.com" 44 | gem_install 'solutious-rudy' 45 | end 46 | end 47 | 48 | package_gem do 49 | local do 50 | rm :r, :f, 'pkg' 51 | rake 'package' 52 | end 53 | end 54 | 55 | install_gem do 56 | before :package_gem 57 | remote :root do 58 | file_upload "pkg/rudy-#{Rudy::VERSION}.gem", "/tmp/" 59 | gem_install "/tmp/rudy-#{Rudy::VERSION}.gem" 60 | end 61 | end 62 | 63 | remove_rudy do 64 | remote :root do 65 | gem_uninstall 'rudy' 66 | rm :r, :f, '.rudy' 67 | end 68 | end 69 | 70 | init_rudy do 71 | before :install_gem 72 | remote do 73 | disable_safe_mode 74 | rudy :v, :v, 'init' # create home directory 75 | file_upload File.expand_path('~/.rudy/config'), '.rudy/' 76 | ls :l, '.rudy/config' 77 | rudy :v, :v, 'init' 78 | rudy 'info', :l 79 | end 80 | end 81 | 82 | sysupdate do 83 | remote :root do 84 | apt_get "update" 85 | apt_get "install", "build-essential", "git-core" 86 | apt_get "install", "ruby1.8-dev", "rdoc", "libzlib-ruby", "rubygems" 87 | mkdir :p, "/var/lib/gems/1.8/bin" # Doesn't get created, but causes Rubygems to fail 88 | gem_install "builder", "session" 89 | gem_install 'rubygems-update', "-v=1.3.4" # circular issue with 1.3.5 and hoe 90 | update_rubygems 91 | end 92 | end 93 | 94 | startup do 95 | after :sysupdate 96 | end 97 | end 98 | 99 | -------------------------------------------------------------------------------- /examples/solaris.rb: -------------------------------------------------------------------------------- 1 | # Rudy Solaris Machines 2 | # 3 | # This configuration is used to 4 | # test solaris instance support. 5 | 6 | 7 | defaults do 8 | color true 9 | environment :test 10 | role :solaris 11 | end 12 | 13 | machines do 14 | region :'us-east-1' do 15 | ami 'ami-8f30d1e6' # OpenSolaris 2009.06 32-bit (US) 16 | end 17 | region :'eu-west-1' do 18 | ami 'ami-2381a957' # OpenSolaris 2009.06 32-bit (EU) 19 | end 20 | env :test do 21 | role :solaris do 22 | user :root 23 | end 24 | end 25 | end 26 | 27 | routines do 28 | 29 | uname do 30 | remote :root do 31 | uname :a 32 | end 33 | end 34 | 35 | end 36 | -------------------------------------------------------------------------------- /examples/spot_instances.rb: -------------------------------------------------------------------------------- 1 | machines do 2 | env :stage do 3 | ami 'ami-e348af8a' # Alestic Debian 5.0, 32-bit (US) 4 | 5 | # Changing to spot pricing causes Rudy to aquire your instances via a "spot request" 6 | # to EC2, as opposed to asking EC2 to boot your instances directly (so-called "on 7 | # demand" instances). Consequently, the first stage of a a Rudy startup rotuine using 8 | # spot instances is to make a spot instance request and then wait for it to be 9 | # fulfilled (all instances from the request are active). 10 | # 11 | # Please see this Amazon AWS page about spot instances for more information: 12 | # 13 | # http://aws.amazon.com/ec2/spot-instances/ 14 | # 15 | # Here is what a startup routine looks like using spot instances: 16 | # 17 | # rudy git:project_name ❯ rudy startup ✹ 18 | # Executing routine: startup 19 | # 20 | # authorize port 22 access for: 70.99.250.82 21 | # Waiting for 1 spot requests to be fulfilled.............................. 22 | # Waiting for m-us-east-1d-stage-app-01 to boot......... 23 | # Waiting for public DNS on m-us-east-1d-stage-app-01 ... 24 | # Waiting for port 22 on m-us-east-1d-stage-app-01 ...... 25 | # root@m-us-east-1d-stage-app-01# hostname m-us-east-1d-stage-app-01 26 | # 27 | # The following machines are now available: 28 | # m-us-east-1d-stage-app-01; i-d7ba09b9; ec2-50-16-129-132.compute-1.amazonaws.com 29 | # 30 | # The time you will wait for your spot request to be fulfilled can vary from as little 31 | # as 60 seconds, to as long as 5 minutes, depending on availability of instances in 32 | # your region and/or availability zone, as well as your bid price. During peak EC2 33 | # usage seasons (around the holidays), spot requests may take up to 24-48 hours to be 34 | # fulfilled. 35 | # 36 | # The Rudy startup rotuine will wait for 3 minutes before timing out and asking you if 37 | # you want to continue waiting. If you do, simply type 'yes' when asked and hit 38 | # 'enter' to continue waiting. If you decide you do not want to wait, type 'no' and 39 | # the startup routine will abort with no machines created. 40 | pricing :spot do # Pricing may be :spot (with a block) or :on_demand 41 | bid 2.00 42 | end 43 | end 44 | end -------------------------------------------------------------------------------- /examples/windows.rb: -------------------------------------------------------------------------------- 1 | # Rudy Windows Machines 2 | # 3 | # This configuration is used to 4 | # test windows instance support. 5 | 6 | 7 | defaults do 8 | color true 9 | environment :test 10 | role :windows 11 | zone :'eu-west-1b' 12 | bucket 'rudy-ami-eu' 13 | end 14 | 15 | machines do 16 | region :'us-east-1' do 17 | ami 'ami-de4daab7' # Amazon Windows Server 2003 (US) 18 | size 'm1.small' 19 | end 20 | region :'eu-west-1' do 21 | ami 'ami-8696bef2' # Rudy Windows 2009-08-24 (EU) 22 | end 23 | env :test do 24 | role :windows do 25 | os :windows 26 | user :Administrator 27 | disks do 28 | path "F:" do 29 | size 1 30 | device 'xvdf' 31 | fstype 'ntfs' 32 | end 33 | path "E:" do 34 | size 2 35 | device 'xvde' 36 | end 37 | path 'L:' do 38 | size 3 39 | device 'xvdl' 40 | end 41 | end 42 | end 43 | end 44 | end 45 | 46 | 47 | commands do 48 | allow :rm 49 | end 50 | 51 | routines do 52 | 53 | create_disks do 54 | disks do 55 | create "L:" 56 | #create "F:" 57 | #create "E:" 58 | end 59 | 60 | end 61 | 62 | shutdown do 63 | disks do 64 | destroy "L:" 65 | #destroy "F:" 66 | #destroy "E:" 67 | end 68 | 69 | end 70 | 71 | 72 | upload_config do 73 | remote :root do 74 | puts "Uploading rudy config" 75 | home = guess_user_home 76 | mkdir :p, "#{home}/.rudy", "#{home}/.ssh", ".ssh" 77 | disable_safe_mode 78 | file_upload '~/.rudy/config', "#{home}/.rudy/config" 79 | puts "Uploading keypair" 80 | file_upload '~/.ssh/id_rsa', '~/.ssh/key-eu-west-1b-test-windows', "#{home}/.ssh/" 81 | file_upload '~/.ssh/id_rsa', '~/.ssh/key-eu-west-1b-test-windows', ".ssh/" 82 | end 83 | end 84 | 85 | end 86 | 87 | 88 | __END__ 89 | 90 | * diskpart script example 91 | * http://social.technet.microsoft.com/Forums/en-US/winserversetup/thread/2cfbaae1-6e33-4197-bb71-63434a34eb3c 92 | * http://technet.microsoft.com/en-us/library/cc766465(WS.10).aspx 93 | 94 | * format docs 95 | * http://www.computerhope.com/formathl.htm 96 | 97 | * Securing Remote Desktop with copSSH 98 | * http://www.teamhackaday.com/2008/04/23/securing-windows-remote-desktop-with-copssh/ 99 | 100 | * Windows SSH 101 | * http://www.windowsnetworking.com/articles_tutorials/install-SSH-Server-Windows-Server-2008.html 102 | -------------------------------------------------------------------------------- /lib/rudy.rb: -------------------------------------------------------------------------------- 1 | 2 | unless defined?(RUDY_HOME) 3 | RUDY_HOME = File.join(File.dirname(__FILE__), '..') 4 | RUDY_LIB = File.join(File.dirname(__FILE__), '..', 'lib') 5 | end 6 | 7 | local_libs = %w{net-ssh net-scp aws-s3 caesars drydock rye storable sysinfo annoy gibbler} 8 | local_libs.each { |dir| $:.unshift File.join(RUDY_HOME, '..', dir, 'lib') } 9 | require 'rubygems' 10 | 11 | begin; require 'json'; rescue LoadError; end # Silence! 12 | 13 | require 'digest/md5' 14 | require 'stringio' 15 | require 'ostruct' 16 | require 'logger' 17 | require 'socket' 18 | require 'resolv' 19 | 20 | require 'gibbler/aliases' 21 | require 'rudy/mixins' 22 | require 'storable' 23 | require 'sysinfo' 24 | require 'attic' 25 | 26 | require 'rye' 27 | require 'annoy' 28 | require 'tempfile' 29 | require 'timeout' 30 | require 'yaml' 31 | 32 | # = Rudy 33 | # 34 | # 35 | # Rudy is a development and deployment tool for the Amazon Elastic Compute Cloud 36 | # (EC2). Getting Started today! 37 | # 38 | # 39 | module Rudy 40 | extend self 41 | 42 | module VERSION #:nodoc: 43 | unless defined?(MAJOR) 44 | MAJOR = 0.freeze 45 | MINOR = 9.freeze 46 | TINY = 8.freeze 47 | PATCH = '020'.freeze 48 | end 49 | def self.to_s; [MAJOR, MINOR, TINY, PATCH].join('.'); end 50 | def self.to_f; self.to_s.to_f; end 51 | end 52 | 53 | def Rudy.sysinfo 54 | @@sysinfo = SysInfo.new.freeze if @@sysinfo.nil? 55 | @@sysinfo 56 | end 57 | def sysinfo; Rudy.sysinfo; end 58 | 59 | unless defined? Rudy::DEFAULT_DOMAIN # We can assume all constants are defined 60 | 61 | @@quiet = false 62 | @@auto = false 63 | @@debug = false 64 | @@sysinfo = nil 65 | 66 | # SimpleDB accepts dashes in the domain name on creation and with the query syntax. 67 | # However, with select syntax it says: "The specified query expression syntax is not valid" 68 | DEFAULT_DOMAIN = "rudy_state".freeze 69 | DELIM = '-'.freeze 70 | 71 | CONFIG_DIR = File.join(Rudy.sysinfo.home, '.rudy').freeze 72 | CONFIG_FILE = File.join(Rudy::CONFIG_DIR, 'config').freeze 73 | SSH_KEY_DIR = File.expand_path('~/.ssh').freeze 74 | 75 | DEFAULT_ZONE = :'us-east-1b'.freeze 76 | DEFAULT_REGION = DEFAULT_ZONE.to_s.gsub(/[a-z]$/, '').to_sym.freeze 77 | DEFAULT_ENVIRONMENT = :stage.freeze 78 | DEFAULT_ROLE = :app.freeze 79 | 80 | DEFAULT_EC2_HOST = "ec2.amazonaws.com" 81 | DEFAULT_EC2_PORT = 443 82 | 83 | DEFAULT_WINDOWS_FS = 'ntfs' 84 | DEFAULT_LINUX_FS = 'ext3' 85 | 86 | DEFAULT_WINDOWS_DEVICE = 'xvdf' 87 | DEFAULT_LINUX_DEVICE = '/dev/sdh' 88 | 89 | MAX_INSTANCES = 20.freeze 90 | 91 | ID_MAP = { 92 | :instance => 'i', 93 | :machine => 'm', 94 | :reservation => 'r', 95 | :pkey => 'pk', 96 | :volume => 'vol', 97 | :kernel => 'aki', 98 | :image => 'ami', 99 | :ramdisk => 'ari', 100 | :group => 'grp', 101 | :log => 'log', 102 | :key => 'key', 103 | :dns_public => 'ec2', 104 | :disk => 'disk', 105 | :backup => 'back', 106 | :snapshot => 'snap', 107 | :cert => 'cert', 108 | :dns_private => 'domU' 109 | }.freeze 110 | 111 | end 112 | 113 | def Rudy.quiet?; @@quiet == true; end 114 | def Rudy.enable_quiet; @@quiet = true; end 115 | def Rudy.disable_quiet; @@quiet = false; end 116 | 117 | def Rudy.auto?; @@auto == true; end 118 | def Rudy.enable_auto; @@auto = true; end 119 | def Rudy.disable_auto; @@auto = false; end 120 | 121 | def Rudy.debug?; @@debug == true; end 122 | def Rudy.enable_debug; @@debug = true; end 123 | def Rudy.disable_debug; @@debug = false; end 124 | 125 | require 'rudy/exceptions' 126 | require 'rudy/utils' # The 127 | require 'rudy/global' # order 128 | require 'rudy/config' # of 129 | require 'rudy/huxtable' # requires 130 | autoload :AWS, 'rudy/aws' # is 131 | autoload :Metadata, 'rudy/metadata' # super 132 | autoload :Machines, 'rudy/machines' # important. 133 | autoload :Backups, 'rudy/backups' 134 | autoload :Disks, 'rudy/disks' 135 | autoload :Routines, 'rudy/routines' 136 | 137 | end 138 | 139 | if Rudy.sysinfo.vm == :java 140 | require 'java' 141 | module Java 142 | include_class java.net.Socket unless defined?(Java::Socket) 143 | include_class java.net.InetSocketAddress unless defined?(Java::InetSocketAddress) 144 | end 145 | end 146 | 147 | -------------------------------------------------------------------------------- /lib/rudy/aws.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'AWS' # amazon-ec2 gem 4 | 5 | 6 | module Rudy 7 | module AWS 8 | extend self 9 | 10 | unless defined?(Rudy::AWS::VALID_REGIONS) 11 | VALID_REGIONS = [:'eu-west-1', :'us-east-1', :'us-west-1', :'ap-southeast-1'].freeze 12 | end 13 | 14 | def valid_region?(r); VALID_REGIONS.member?(r.to_sym || ''); end 15 | 16 | # Modifies +str+ by removing [\0\n\r\032\\\\] and escaping [\'\"] 17 | def escape(str) 18 | str.to_s.tr("[\0\n\r\032\\\\]", '').gsub(/([\'\"])/, '\\1\\1') 19 | end 20 | def escape!(str) 21 | str.to_s.tr!("[\0\n\r\032\\\\]", '').gsub!(/([\'\"])/, '\\1\\1') 22 | end 23 | 24 | autoload :SDB, 'rudy/aws/sdb' 25 | autoload :EC2, 'rudy/aws/ec2' 26 | autoload :S3, 'rudy/aws/s3' 27 | 28 | class Error < ::AWS::Error; end 29 | end 30 | 31 | end 32 | 33 | -------------------------------------------------------------------------------- /lib/rudy/aws/ec2.rb: -------------------------------------------------------------------------------- 1 | 2 | module Rudy; module AWS 3 | module EC2 4 | include Rudy::Huxtable 5 | 6 | @@mutex = Mutex.new 7 | 8 | def self.connect(access_key=nil, secret_key=nil, region=nil, logger=nil) 9 | 10 | if region 11 | raise InvalidRegion, region unless Rudy::AWS.valid_region?(region) 12 | host = "#{region}.ec2.amazonaws.com" 13 | elsif ENV['EC2_URL'] 14 | host = URL.parse(ENV['EC2_URL']).host 15 | end 16 | 17 | host ||= DEFAULT_EC2_HOST 18 | port ||= DEFAULT_EC2_PORT 19 | 20 | @@ec2 = ::AWS::EC2::Base.new(:port => port, :server=> host, :access_key_id => access_key, :secret_access_key => secret_key) 21 | end 22 | 23 | protected 24 | 25 | # Execute AWS requests safely. This will trap errors and return 26 | # a default value (if specified). 27 | # * +default+ A default response value 28 | # * +request+ A block which contains the AWS request 29 | # Returns the return value from the request is returned untouched 30 | # or the default value on error or if the request returned nil. 31 | def self.execute_request(default=nil, timeout=nil, &request) 32 | timeout ||= 30 33 | raise "No block provided" unless request 34 | response = nil 35 | @@mutex.synchronize { 36 | begin 37 | 38 | Timeout::timeout(timeout) do 39 | response = request.call 40 | end 41 | 42 | # Raise the EC2 exceptions 43 | rescue ::AWS::Error, ::AWS::InvalidInstanceIDMalformed => ex 44 | raise Rudy::AWS::Error, ex.message 45 | 46 | # NOTE: The InternalError is returned for non-existent volume IDs. 47 | # It's probably a bug so we're ignoring it -- Dave. 48 | rescue ::AWS::InternalError => ex 49 | raise Rudy::AWS::Error, ex.message 50 | 51 | rescue Timeout::Error => ex 52 | Rudy::Huxtable.le "Timeout (#{timeout}): #{ex.message}!" 53 | rescue SocketError => ex 54 | #Rudy::Huxtable.le ex.message 55 | #Rudy::Huxtable.le ex.backtrace 56 | raise SocketError, "Check your Internets!" unless @@global.offline 57 | ensure 58 | response ||= default 59 | end 60 | sleep 0.1 # defeat race conditions 61 | } 62 | response 63 | end 64 | 65 | class NoRunningInstances < Rudy::Error; end 66 | class MalformedResponse < Rudy::Error; end 67 | class InvalidRegion < Rudy::Error; end 68 | class UnknownState < Rudy::Error; end 69 | class NoGroup < Rudy::Error; end 70 | class NoKeypair < Rudy::Error; end 71 | class NoAMI < Rudy::Error; end 72 | 73 | class NoAddress < Rudy::Error; end 74 | class UnknownAddress < Rudy::Error; end 75 | class NoInstanceID < Rudy::Error; end 76 | class AddressAssociated < Rudy::Error; end 77 | class ErrorCreatingAddress < Rudy::Error; end 78 | class AddressNotAssociated < Rudy::Error; end 79 | class InsecureKeypairPermissions < Rudy::Error; end 80 | 81 | class InsecureKeypairPermissions < Rudy::Error; end 82 | class ErrorCreatingKeypair < Rudy::Error; end 83 | class NoPrivateKeyFile < Rudy::Error; end 84 | class KeypairExists < Rudy::Error; end 85 | class KeypairAlreadyDefined < Rudy::Error 86 | def message 87 | "A keypair is defined for #{@obj}. Check your Rudy config." 88 | end 89 | end 90 | 91 | class VolumeAlreadyAttached < Rudy::Error; end 92 | class VolumeNotAvailable < Rudy::Error; end 93 | class VolumeNotAttached < Rudy::Error; end 94 | class UnknownVolume < Rudy::Error; end 95 | class NoInstanceID < Rudy::Error; end 96 | class NoVolumeID < Rudy::Error; end 97 | class UnknownState < Rudy::Error; end 98 | class NoDevice < Rudy::Error; end 99 | 100 | 101 | autoload :Address, 'rudy/aws/ec2/address' 102 | autoload :Addresses, 'rudy/aws/ec2/address' 103 | autoload :Group, 'rudy/aws/ec2/group' 104 | autoload :Groups, 'rudy/aws/ec2/group' 105 | autoload :Image, 'rudy/aws/ec2/image' 106 | autoload :Images, 'rudy/aws/ec2/image' 107 | autoload :Instance, 'rudy/aws/ec2/instance' 108 | autoload :Instances, 'rudy/aws/ec2/instance' 109 | autoload :Keypair, 'rudy/aws/ec2/keypair' 110 | autoload :Keypairs, 'rudy/aws/ec2/keypair' 111 | autoload :Snapshot, 'rudy/aws/ec2/snapshot' 112 | autoload :Snapshots, 'rudy/aws/ec2/snapshot' 113 | autoload :Volume, 'rudy/aws/ec2/volume' 114 | autoload :Volumes, 'rudy/aws/ec2/volume' 115 | autoload :Zone, 'rudy/aws/ec2/zone' 116 | autoload :Zones, 'rudy/aws/ec2/zone' 117 | autoload :SpotRequest, 'rudy/aws/ec2/spot_request' 118 | autoload :SpotRequests, 'rudy/aws/ec2/spot_request' 119 | end 120 | end; end 121 | -------------------------------------------------------------------------------- /lib/rudy/aws/ec2/keypair.rb: -------------------------------------------------------------------------------- 1 | 2 | module Rudy::AWS 3 | module EC2 4 | 5 | class Keypair < Storable 6 | 7 | field :name => String 8 | field :fingerprint => String 9 | field :private_key => String 10 | 11 | def to_s(titles=false) 12 | [@name.bright, @fingerprint].join '; ' 13 | end 14 | 15 | def public_key 16 | return unless @private_key 17 | k = Rye::Key.new(@private_key) 18 | k.public_key.to_ssh2 19 | end 20 | 21 | end 22 | 23 | module EC2::Keypairs 24 | include Rudy::AWS::EC2 # important! include, 25 | extend self # then extend 26 | 27 | def create(name) 28 | raise "No name provided" unless name 29 | ret = @@ec2.create_keypair(:key_name => name) 30 | from_hash(ret) 31 | end 32 | 33 | def destroy(name) 34 | name = name.name if name.is_a?(Rudy::AWS::EC2::Keypair) 35 | raise "No name provided" unless name.is_a?(String) 36 | ret = @@ec2.delete_keypair(:key_name => name) 37 | (ret && ret['return'] == 'true') # BUG? Always returns true 38 | end 39 | 40 | def list(*names) 41 | keypairs = list_as_hash(names) 42 | keypairs &&= keypairs.values 43 | keypairs 44 | end 45 | 46 | def list_as_hash(*names) 47 | names = names.flatten 48 | klist = @@ec2.describe_keypairs(:key_name => names) 49 | return unless klist['keySet'].is_a?(Hash) 50 | keypairs = {} 51 | klist['keySet']['item'].each do |oldkp| 52 | kp = from_hash(oldkp) 53 | keypairs[kp.name] = kp 54 | end 55 | keypairs = nil if keypairs.empty? 56 | keypairs 57 | end 58 | 59 | def from_hash(h) 60 | # keyName: test-c5g4v3pe 61 | # keyFingerprint: 65:d0:ce:e7:6a:b0:88:4a:9c:c7:2d:b8:33:0c:fd:3b:c8:0f:0a:3c 62 | # keyMaterial: |- 63 | # -----BEGIN RSA PRIVATE KEY----- 64 | # 65 | keypair = Rudy::AWS::EC2::Keypair.new 66 | keypair.fingerprint = h['keyFingerprint'] 67 | keypair.name = h['keyName'] 68 | keypair.private_key = h['keyMaterial'] 69 | keypair 70 | end 71 | 72 | def any? 73 | keypairs = list || [] 74 | !keypairs.empty? 75 | end 76 | 77 | def get(name) 78 | keypairs = list(name) || [] 79 | return if keypairs.empty? 80 | keypairs.first 81 | end 82 | 83 | def exists?(name) 84 | return false unless name 85 | kp = get(name) rescue nil 86 | !kp.nil? 87 | end 88 | 89 | end 90 | 91 | end 92 | end 93 | 94 | 95 | -------------------------------------------------------------------------------- /lib/rudy/aws/ec2/snapshot.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Rudy::AWS 4 | module EC2 5 | class Snapshot < Storable 6 | 7 | field :awsid 8 | field :progress 9 | field :created 10 | field :volid 11 | field :status 12 | 13 | def to_s(with_title=false) 14 | [@awsid.bright, @volid, @created, @status, @progress].join '; ' 15 | end 16 | 17 | def completed? 18 | self.status && self.status == 'completed' 19 | end 20 | 21 | end 22 | 23 | module Snapshots 24 | include Rudy::AWS::EC2 # important! include, 25 | extend self # then extend 26 | 27 | 28 | def list(snap_id=[]) 29 | snapshots = list_as_hash(snap_id) 30 | if snapshots 31 | snapshots = snapshots.values.sort { |a,b| a.created <=> b.created } 32 | end 33 | snapshots 34 | end 35 | def list_as_hash(snap_id=[]) 36 | snap_id = [snap_id].flatten.compact 37 | slist = @@ec2.describe_snapshots(:snapshot_id => snap_id) 38 | return unless slist['snapshotSet'].is_a?(Hash) 39 | snapshots = {} 40 | slist['snapshotSet']['item'].each do |snap| 41 | kp = self.from_hash(snap) 42 | snapshots[kp.awsid] = kp 43 | end 44 | snapshots = nil if snapshots.empty? 45 | snapshots 46 | end 47 | 48 | def create(vol_id) 49 | vol_id = (vol_id.is_a?(Rudy::AWS::EC2::Volume)) ? vol_id.awsid : vol_id 50 | shash = @@ec2.create_snapshot(:volume_id => vol_id) 51 | snap = Snapshots.from_hash(shash) 52 | snap 53 | end 54 | 55 | def destroy(snap_id) 56 | ret = @@ec2.delete_snapshot(:snapshot_id => snap_id) 57 | (ret && ret['return'] == 'true') 58 | end 59 | 60 | 61 | def Snapshots.from_hash(h) 62 | #snapshotSet: 63 | # item: 64 | # - snapshotId: snap-5493653d 65 | # volumeId: vol-0836d761 66 | # status: completed 67 | # startTime: "2009-03-29T20:15:10.000Z" 68 | # progress: 100% 69 | vol = Rudy::AWS::EC2::Snapshot.new 70 | vol.awsid = h['snapshotId'] 71 | vol.volid = h['volumeId'] 72 | vol.created = h['startTime'] 73 | vol.progress = h['progress'] 74 | vol.status = h['status'] 75 | vol 76 | end 77 | 78 | def any? 79 | !(list_as_hash || {}).empty? 80 | end 81 | 82 | def get(snap_id) 83 | list(snap_id).first rescue nil 84 | end 85 | 86 | def exists?(snap_id) 87 | !get(snap_id).nil? 88 | end 89 | 90 | def completed?(snap_id) 91 | s = get(snap_id) 92 | return false if s.nil? 93 | s.completed? 94 | end 95 | end 96 | 97 | end 98 | end -------------------------------------------------------------------------------- /lib/rudy/aws/ec2/spot_request.rb: -------------------------------------------------------------------------------- 1 | 2 | module Rudy::AWS 3 | module EC2 4 | 5 | class SpotRequest < Storable 6 | field :id => String 7 | field :price => Float 8 | field :type => String 9 | field :state => String 10 | field :ami => String 11 | field :keyname => String 12 | field :groups => Array 13 | field :size => String 14 | field :zone => String 15 | field :create_time => Time 16 | field :instid => String 17 | 18 | def fulfilled? 19 | instid && !instid.empty? 20 | end 21 | end 22 | 23 | 24 | module SpotRequests 25 | include Rudy::AWS::EC2 # important! include, 26 | extend self # then extend 27 | 28 | # Return an Array of SpotRequest objects. 29 | # 30 | # +opts+ supports the following parameters: 31 | # 32 | # * +:price+ 33 | # * +:count+ 34 | # * +:zone+ 35 | # * +:ami+ 36 | # * +:group+ 37 | # * +:size+ 38 | # * +:keypair+ 39 | # * +:private+ true or false (default) 40 | def create(opts) 41 | raise NoAMI unless opts[:ami] 42 | raise NoGroup unless opts[:group] 43 | 44 | opts = { :size => 'm1.small', :count => 1}.merge(opts) 45 | 46 | old_opts = { 47 | :spot_price => opts[:price].to_s, 48 | :instance_count => opts[:count].to_i, 49 | :availability_zone_group => (opts[:zone] || @@global.zone).to_s, 50 | :image_id => opts[:ami].to_s, 51 | :key_name => opts[:keypair].to_s, 52 | :security_group => opts[:group].to_s, 53 | :instance_type => opts[:size] 54 | } 55 | 56 | response = Rudy::AWS::EC2.execute_request({}) { @@ec2.request_spot_instances(old_opts) } 57 | self.from_request_set(response['spotInstanceRequestSet']) 58 | end 59 | 60 | def list(requests = nil) 61 | opts = requests ? {:spot_instance_request_id => requests.map(&:id)} : {} 62 | 63 | response = Rudy::AWS::EC2.execute_request({}) do 64 | @@ec2.describe_spot_instance_requests(opts) 65 | end 66 | 67 | self.from_request_set(response['spotInstanceRequestSet']) 68 | end 69 | 70 | def fulfilled?(requests) 71 | list(requests).all? { |r| r.fulfilled? } 72 | end 73 | 74 | def self.from_hash(hash) 75 | Rudy::AWS::EC2::SpotRequest.new.tap do |request| 76 | request.id = hash['spotInstanceRequestId'] 77 | request.price = hash['spotPrice'] 78 | request.type = hash['type'] 79 | request.state = hash['state'] 80 | request.ami = hash['launchSpecification']['imageId'] 81 | request.keyname = hash['launchSpecification']['keyName'] 82 | request.size = hash['launchSpecification']['instanceType'] 83 | request.zone = hash['availabilityZoneGroup'] 84 | request.create_time = hash['createTime'] 85 | request.instid = hash['instanceId'] 86 | 87 | request.groups = hash['launchSpecification']['groupSet']['item'].map do |item| 88 | item['groupId'] 89 | end 90 | end 91 | end 92 | 93 | def cancel(requests) 94 | opts = requests ? {:spot_instance_request_id => requests.map(&:id)} : {} 95 | Rudy::AWS::EC2.execute_request({}) { @@ec2.cancel_spot_instance_requests(opts) } 96 | end 97 | 98 | private 99 | 100 | def self.from_request_set(request_set) 101 | return unless request_set.is_a?(Hash) 102 | request_set['item'].collect do |request| 103 | self.from_hash(request) 104 | end 105 | end 106 | 107 | 108 | end 109 | 110 | end 111 | 112 | end -------------------------------------------------------------------------------- /lib/rudy/aws/ec2/zone.rb: -------------------------------------------------------------------------------- 1 | 2 | module Rudy::AWS 3 | module EC2 4 | 5 | class Zone < Storable 6 | 7 | field :name 8 | field :region 9 | field :state 10 | 11 | def to_s(titles=false) 12 | [@name.bright, @region, @state].join '; ' 13 | end 14 | 15 | end 16 | 17 | module Zones 18 | include Rudy::AWS::EC2 # important! include, 19 | extend self # then extend 20 | 21 | def list(*names) 22 | zones = list_as_hash(names) 23 | zones &&= zones.values 24 | zones 25 | end 26 | 27 | def list_as_hash(*names) 28 | names = names.flatten 29 | zlist = @@ec2.describe_availability_zones(:zone_name => names) 30 | return unless zlist['availabilityZoneInfo'].is_a?(Hash) 31 | zones = {} 32 | zlist['availabilityZoneInfo']['item'].each do |zhash| 33 | zon = Zones.from_hash(zhash) 34 | zones[zon.name] = zon 35 | end 36 | zones = nil if zones.empty? 37 | zones 38 | end 39 | 40 | def self.from_hash(h) 41 | zone = Rudy::AWS::EC2::Zone.new 42 | zone.name = h['zoneName'] 43 | zone.region = h['regionName'] 44 | zone.state = h['zoneState'] 45 | zone 46 | end 47 | 48 | def any? 49 | zones = list || [] 50 | !zones.empty? 51 | end 52 | 53 | def get(name) 54 | zones = list(name) || [] 55 | return if zones.empty? 56 | zones.first 57 | end 58 | 59 | def zone?(name) 60 | begin 61 | kp = get(name) 62 | kp.is_a?(Rudy::AWS::EC2::Zone) 63 | rescue => ex 64 | false 65 | end 66 | end 67 | 68 | end 69 | 70 | end 71 | end 72 | 73 | 74 | -------------------------------------------------------------------------------- /lib/rudy/aws/s3.rb: -------------------------------------------------------------------------------- 1 | 2 | module Rudy::AWS 3 | class S3 4 | 5 | def initialize(access_key=nil, secret_key=nil, region=nil, debug=nil) 6 | require 'aws/s3' 7 | 8 | url ||= 'http://sdb.amazonaws.com' 9 | # There is a bug with passing :server to EC2::Base.new so 10 | # we'll use the environment variable for now. 11 | #if region && Rudy::AWS.valid_region?(region) 12 | # "#{region}.sdb.amazonaws.com" 13 | #end 14 | 15 | @access_key_id = access_key || ENV['AWS_ACCESS_KEY'] || ENV['AMAZON_ACCESS_KEY_ID'] 16 | @secret_access_key = secret_key || ENV['AWS_SECRET_KEY'] || ENV['AMAZON_SECRET_ACCESS_KEY'] 17 | @base_url = url 18 | @debug = debug || StringIO.new 19 | 20 | 21 | AWS::S3::Base.establish_connection!( 22 | :access_key_id => @access_key_id, 23 | :secret_access_key => @secret_access_key 24 | ) 25 | 26 | end 27 | 28 | def list_buckets 29 | ::AWS::S3::Service.buckets 30 | end 31 | 32 | def create_bucket(name, location=nil) 33 | opts = {} 34 | opts[:location] = location.to_s.upcase if location 35 | ::AWS::S3::Bucket.create(name, opts) 36 | end 37 | 38 | def destroy_bucket(name) 39 | ::AWS::S3::Bucket.delete(name) 40 | end 41 | 42 | def find_bucket(name) 43 | blist = ::AWS::S3::Service.buckets 44 | blist.select { |bobj| bobj.name == name }.first 45 | end 46 | 47 | def list_bucket_objects(name) 48 | ::AWS::S3::Bucket.objects(name) 49 | end 50 | 51 | #def store(path, bucket) 52 | # fname = File.basename(path) 53 | # S3Object.store(fname, open(path), bucket) 54 | #end 55 | 56 | def bucket_exists?(name) 57 | b = find_bucket(name) 58 | !b.nil? 59 | end 60 | 61 | autoload :Error, 'rudy/aws/sdb/error' 62 | 63 | end 64 | end -------------------------------------------------------------------------------- /lib/rudy/aws/sdb/error.rb: -------------------------------------------------------------------------------- 1 | module Rudy 2 | module AWS 3 | class SDB 4 | class Error < RuntimeError ; end 5 | 6 | class RequestError < Error 7 | attr_reader :request_id 8 | 9 | def initialize(message, request_id=nil) 10 | super(message) 11 | @request_id = request_id 12 | end 13 | end 14 | 15 | class InvalidDomainNameError < RequestError ; end 16 | class InvalidParameterValueError < RequestError ; end 17 | class InvalidNextTokenError < RequestError ; end 18 | class InvalidNumberPredicatesError < RequestError ; end 19 | class InvalidNumberValueTestsError < RequestError ; end 20 | class InvalidQueryExpressionError < RequestError ; end 21 | class MissingParameterError < RequestError ; end 22 | class NoSuchDomainError < RequestError ; end 23 | class NumberDomainsExceededError < RequestError ; end 24 | class NumberDomainAttributesExceededError < RequestError ; end 25 | class NumberDomainBytesExceededError < RequestError ; end 26 | class NumberItemAttributesExceededError < RequestError ; end 27 | class RequestTimeoutError < RequestError ; end 28 | 29 | class FeatureDeprecatedError < RequestError ; end 30 | 31 | class ConnectionError < Rudy::Error 32 | attr_reader :response 33 | 34 | def initialize(response) 35 | super( 36 | "#{response.code} \ 37 | #{response.message if response.respond_to?(:message)}" 38 | ) 39 | @response = response 40 | end 41 | 42 | end 43 | 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /lib/rudy/backups.rb: -------------------------------------------------------------------------------- 1 | module Rudy 2 | 3 | module Backups 4 | RTYPE = 'back'.freeze 5 | 6 | extend self 7 | extend Rudy::Metadata::ClassMethods 8 | include Rudy::Huxtable 9 | extend Rudy::Huxtable 10 | 11 | # Returns the most recent backup object for the given path 12 | def get(path) 13 | tmp = Rudy::Backup.new path 14 | backups = Rudy::Backups.list :path => path 15 | return nil unless backups.is_a?(Array) && !backups.empty? 16 | backups.first 17 | end 18 | 19 | def from_hash(h) 20 | Rudy::Backup.from_hash h 21 | end 22 | 23 | class NoDisk < Rudy::Error; end 24 | class NoBackup < Rudy::Error; end 25 | 26 | end 27 | 28 | end -------------------------------------------------------------------------------- /lib/rudy/cli.rb: -------------------------------------------------------------------------------- 1 | 2 | require 'drydock' 3 | 4 | 5 | module Rudy 6 | 7 | # = CLI 8 | # 9 | # These classes provide the functionality for the Command 10 | # line interfaces. See the bin/ files if you're interested. 11 | # 12 | module CLI 13 | 14 | require 'rudy/cli/execbase' 15 | require 'rudy/cli/base' 16 | 17 | class NoCred < RuntimeError #:nodoc 18 | end 19 | 20 | class Output < Storable 21 | # TODO: Use for all CLI responses 22 | # Messages and errors should be in @@global.format 23 | # Should print messages as they come 24 | end 25 | 26 | 27 | def self.generate_header(global, config) 28 | return "" if global.quiet 29 | header = StringIO.new 30 | title, name = "RUDY v#{Rudy::VERSION}", config.accounts.aws.name 31 | now_utc = Time.now.utc.strftime("%Y-%m-%d %H:%M:%S") 32 | criteria = [] 33 | [:region, :zone, :environment, :role, :position].each do |n| 34 | key, val = n.to_s.slice(0,1).att, global.send(n) 35 | key = 'R' if n == :region 36 | next unless val 37 | criteria << "#{key.att}:#{val.to_s.bright}" 38 | end 39 | if config.accounts && config.accounts.aws 40 | if global.verbose > 0 41 | header.puts '%s -- %s -- %s UTC' % [title, name, now_utc] 42 | end 43 | header.puts '[%s]' % [criteria.join(" ")], $/ 44 | end 45 | header.rewind 46 | header.read 47 | end 48 | 49 | autoload :Backups, 'rudy/cli/backups' 50 | autoload :Candy, 'rudy/cli/candy' 51 | autoload :Config, 'rudy/cli/config' 52 | autoload :Disks, 'rudy/cli/disks' 53 | autoload :Images, 'rudy/cli/images' 54 | autoload :Info, 'rudy/cli/info' 55 | autoload :Keypairs, 'rudy/cli/keypairs' 56 | autoload :Machines, 'rudy/cli/machines' 57 | autoload :Metadata, 'rudy/cli/metadata' 58 | autoload :Networks, 'rudy/cli/networks' 59 | autoload :Routines, 'rudy/cli/routines' 60 | 61 | module AWS 62 | module EC2 63 | autoload :Info, 'rudy/cli/aws/ec2/info' 64 | autoload :Candy, 'rudy/cli/aws/ec2/candy' 65 | autoload :Addresses, 'rudy/cli/aws/ec2/addresses' 66 | autoload :Addresses, 'rudy/cli/aws/ec2/addresses' 67 | autoload :Groups, 'rudy/cli/aws/ec2/groups' 68 | autoload :Images, 'rudy/cli/aws/ec2/images' 69 | autoload :Instances, 'rudy/cli/aws/ec2/instances' 70 | autoload :Keypairs, 'rudy/cli/aws/ec2/keypairs' 71 | autoload :Snapshots, 'rudy/cli/aws/ec2/snapshots' 72 | autoload :Volumes, 'rudy/cli/aws/ec2/volumes' 73 | autoload :Zones, 'rudy/cli/aws/ec2/zones' 74 | end 75 | module SDB 76 | autoload :Domains, 'rudy/cli/aws/sdb/domains' 77 | autoload :Objects, 'rudy/cli/aws/sdb/objects' 78 | autoload :Select, 'rudy/cli/aws/sdb/select' 79 | end 80 | module S3 81 | autoload :Buckets, 'rudy/cli/aws/s3/buckets' 82 | autoload :Store, 'rudy/cli/aws/s3/store' 83 | end 84 | end 85 | end 86 | end 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /lib/rudy/cli/aws/ec2/addresses.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Rudy; module CLI; 4 | module AWS; module EC2; 5 | 6 | class Addresses < Rudy::CLI::CommandBase 7 | 8 | def addresses_create 9 | address = Rudy::AWS::EC2::Addresses.create 10 | print_stobject address 11 | end 12 | 13 | def addresses_destroy_valid? 14 | raise Drydock::ArgError.new("IP address", @alias) unless @argv.ipaddress 15 | raise "#{@argv.ipaddress} is not allocated to you" unless Rudy::AWS::EC2::Addresses.exists?(@argv.ipaddress) 16 | raise "#{@argv.ipaddress} is associated!" if Rudy::AWS::EC2::Addresses.associated?(@argv.ipaddress) 17 | true 18 | end 19 | def addresses_destroy 20 | address = Rudy::AWS::EC2::Addresses.get(@argv.ipaddress) 21 | raise "Could not fetch #{address.ipaddress}" unless address 22 | 23 | li "Destroying address: #{@argv.ipaddress}" 24 | li "NOTE: this IP address will become available to other EC2 customers.".bright 25 | execute_check(:medium) 26 | execute_action { Rudy::AWS::EC2::Addresses.destroy(@argv.ipaddress) } 27 | end 28 | 29 | def associate_addresses_valid? 30 | raise Drydock::ArgError.new('IP address', @alias) if !@argv.ipaddress && !@option.newaddress 31 | raise Drydock::OptError.new('instance ID', @alias) if !@option.instance 32 | true 33 | end 34 | def associate_addresses 35 | raise "Instance #{@argv.instid} does not exist!" unless Rudy::AWS::EC2::Instances.exists?(@option.instance) 36 | 37 | if @option.newaddress 38 | print "Creating address... " 39 | tmp = Rudy::AWS::EC2::Addresses.create 40 | li "#{tmp.ipaddress}" 41 | address = tmp.ipaddress 42 | else 43 | address = @argv.ipaddress 44 | end 45 | 46 | raise "#{address} is not allocated to you" unless Rudy::AWS::EC2::Addresses.exists?(address) 47 | raise "#{address} is already associated!" if Rudy::AWS::EC2::Addresses.associated?(address) 48 | 49 | instance = Rudy::AWS::EC2::Instances.get(@option.instance) 50 | 51 | # If an instance was recently disassoiciated, the dns_public may 52 | # not be updated yet 53 | instance_name = instance.dns_public 54 | instance_name = instance.awsid if !instance_name || instance_name.empty? 55 | 56 | li "Associating #{address} to #{instance_name} (#{instance.groups.join(', ')})" 57 | execute_check(:low) 58 | execute_action { Rudy::AWS::EC2::Addresses.associate(address, instance.awsid) } 59 | address = Rudy::AWS::EC2::Addresses.get(address) 60 | print_stobject address 61 | end 62 | 63 | def disassociate_addresses_valid? 64 | raise "You have not supplied an IP addresses" unless @argv.ipaddress 65 | true 66 | end 67 | def disassociate_addresses 68 | raise "#{@argv.ipaddress} is not allocated to you" unless Rudy::AWS::EC2::Addresses.exists?(@argv.ipaddress) 69 | raise "#{@argv.ipaddress} is not associated!" unless Rudy::AWS::EC2::Addresses.associated?(@argv.ipaddress) 70 | 71 | address = Rudy::AWS::EC2::Addresses.get(@argv.ipaddress) 72 | instance = Rudy::AWS::EC2::Instances.get(address.instid) 73 | 74 | li "Disassociating #{address.ipaddress} from #{instance.awsid} (#{instance.groups.join(', ')})" 75 | execute_check(:medium) 76 | execute_action { Rudy::AWS::EC2::Addresses.disassociate(@argv.ipaddress) } 77 | address = Rudy::AWS::EC2::Addresses.get(@argv.ipaddress) 78 | print_stobject address 79 | end 80 | 81 | def addresses 82 | addresses = Rudy::AWS::EC2::Addresses.list || [] 83 | print_stobjects addresses 84 | end 85 | 86 | 87 | end 88 | 89 | end; end 90 | end; end 91 | 92 | -------------------------------------------------------------------------------- /lib/rudy/cli/aws/ec2/groups.rb: -------------------------------------------------------------------------------- 1 | 2 | module Rudy; module CLI; 3 | module AWS; module EC2; 4 | 5 | class Groups < Rudy::CLI::CommandBase 6 | 7 | 8 | 9 | def create_groups_valid? 10 | raise Drydock::ArgError.new('group name', @alias) unless @argv.name 11 | raise "Group #{@argv.name} alread exists" if Rudy::AWS::EC2::Groups.exists?(@argv.name) 12 | true 13 | end 14 | def create_groups 15 | opts = check_options 16 | execute_action { 17 | Rudy::AWS::EC2::Groups.create(@argv.name, @option.description, opts[:addresses], opts[:ports], opts[:protocols]) 18 | } 19 | Rudy::AWS::EC2::Groups.list(@argv.name) do |group| 20 | li @@global.verbose > 0 ? group.inspect : group.dump(@@global.format) 21 | end 22 | end 23 | 24 | 25 | def destroy_groups_valid? 26 | raise Drydock::ArgError.new('group name', @alias) unless @argv.name 27 | raise "Group #{@argv.name} does not exist" unless Rudy::AWS::EC2::Groups.exists?(@argv.name) 28 | true 29 | end 30 | 31 | def destroy_groups 32 | li "Destroying group: #{@argv.name}" 33 | execute_check(:medium) 34 | execute_action { Rudy::AWS::EC2::Groups.destroy(@argv.name) } 35 | @argv.clear # so groups will print all other groups 36 | end 37 | 38 | def revoke_groups_valid?; modify_group_valid?; end 39 | def revoke_groups; modify_group(:revoke); end 40 | 41 | def authorize_groups_valid?; modify_group_valid?; end 42 | def authorize_groups; modify_group(:authorize); end 43 | 44 | def groups 45 | opts = {} 46 | name = @option.all ? nil : @argv.name 47 | Rudy::AWS::EC2::Groups.list(name).each do |group| 48 | li @@global.verbose > 0 ? group.inspect : group.dump(@@global.format) 49 | end 50 | end 51 | 52 | private 53 | 54 | def modify_group_valid? 55 | if @option.owner == 'self' 56 | raise "AWS_ACCOUNT_NUMBER not set" unless @@global.accountnum 57 | @option.owner = @@global.accountnum 58 | end 59 | 60 | if (@option.addresses || @option.ports) && (@option.group || @option.owner) 61 | raise Drydock::OptError.new('', @alias, "Cannot mix group and network authorization") 62 | end 63 | if @option.owner && !@option.group 64 | raise Drydock::OptError.new('', @alias, "Must provide -g with -o") 65 | end 66 | 67 | raise Drydock::ArgError.new('group name', @alias) unless @argv.name 68 | true 69 | end 70 | 71 | def modify_group(action) 72 | opts = check_options 73 | if (@option.group || @option.owner) 74 | g = [opts[:owner], opts[:group]].join(':') 75 | li "#{action.to_s.capitalize} access to #{@argv.name.bright} from #{g.bright}" 76 | else 77 | print "#{action.to_s.capitalize} access to #{@argv.name.bright}" 78 | li " from #{opts[:addresses].join(', ').bright}" 79 | print "on #{opts[:protocols].join(', ').bright} " 80 | li "ports: #{opts[:ports].map { |p| "#{p.join(' to ').bright}" }.join(', ')}" 81 | end 82 | execute_check(:medium) 83 | execute_action { 84 | if (@option.group || @option.owner) 85 | Rudy::AWS::EC2::Groups.send("#{action.to_s}_group", @argv.name, opts[:group], opts[:owner]) 86 | else 87 | Rudy::AWS::EC2::Groups.send(action, @argv.name, opts[:addresses], opts[:ports], opts[:protocols]) 88 | end 89 | } 90 | groups # prints on the modified group b/c of @argv.name 91 | end 92 | 93 | def check_options 94 | opts = {} 95 | [:addresses, :protocols, :owner, :group, :ports].each do |opt| 96 | opts[opt] = @option.send(opt) if @option.respond_to?(opt) 97 | end 98 | unless @option.group || @option.owner 99 | opts[:ports].collect! { |port| port.split(/[:-]/) } if opts[:ports] 100 | opts[:ports] ||= [[22,22],[80,80],[443,443]] 101 | opts[:addresses] ||= [Rudy::Utils::external_ip_address] 102 | opts[:protocols] ||= [:tcp] 103 | else 104 | opts[:owner] ||= @@global.accountnum 105 | end 106 | opts 107 | end 108 | 109 | end 110 | 111 | 112 | end; end 113 | end; end 114 | -------------------------------------------------------------------------------- /lib/rudy/cli/aws/ec2/images.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Rudy; module CLI; 4 | module AWS; module EC2; 5 | 6 | class Images < Rudy::CLI::CommandBase 7 | 8 | def images_valid? 9 | if @option.owner == 'self' 10 | raise "AWS_ACCOUNT_NUMBER not set" unless @@global.accountnum 11 | @option.owner = @@global.accountnum 12 | end 13 | 14 | true 15 | end 16 | def images 17 | unless @option.all 18 | @option.owner ||= 'amazon' 19 | li "Images owned by #{@option.owner.bright}" unless @argv.awsid 20 | end 21 | images = Rudy::AWS::EC2::Images.list(@option.owner, @argv) || [] 22 | print_stobjects images 23 | end 24 | 25 | def destroy_images_valid? 26 | unless @argv.ami && Rudy::Utils.is_id?(:image, @argv.ami) 27 | raise "Must supply an AMI ID (ami-XXXXXXX)" 28 | end 29 | true 30 | end 31 | def destroy_images 32 | li Rudy::AWS::EC2::Images.deregister(@argv.ami) ? "Done" : "Unknown error" 33 | end 34 | 35 | def register_images_valid? 36 | unless @argv.first 37 | raise "Must supply a valid manifest path (bucket/ami-name.manifest.xml)" 38 | end 39 | true 40 | end 41 | def register_images 42 | if @option.snapshot 43 | opts = { 44 | :name => @argv.first, 45 | :architecture => @option.arch || 'i386', 46 | :description => @option.description || 'Made with Rudy', 47 | :root_device_name => "/dev/sda1", 48 | :block_device_mapping => [{ 49 | :device_name => "/dev/sda1", 50 | :ebs_snapshot_id => @option.snapshot, 51 | :ebs_delete_on_termination => true 52 | }] 53 | } 54 | opts[:kernel_id] = @option.kernel if @option.kernel 55 | opts[:ramdisk_id] = @option.ramdisk if @option.ramdisk 56 | else 57 | opts = { 58 | :image_location => @argv.first 59 | } 60 | end 61 | p opts if Rudy.debug? 62 | li Rudy::AWS::EC2::Images.register(opts) 63 | end 64 | 65 | 66 | end 67 | 68 | 69 | end; end 70 | end; end 71 | 72 | 73 | -------------------------------------------------------------------------------- /lib/rudy/cli/aws/ec2/info.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Rudy; module CLI 4 | module AWS; module EC2; 5 | 6 | class Info < Rudy::CLI::CommandBase 7 | 8 | def info 9 | process_region @@global.region 10 | oregions = Rudy::AWS::VALID_REGIONS - [@@global.region.to_sym] 11 | if @option.all 12 | oregions.each do |region| 13 | Rudy::AWS::EC2.connect @@global.accesskey, @@global.secretkey, region 14 | process_region region 15 | end 16 | else 17 | li $/, "Other regions: " << oregions.join(', ') 18 | end 19 | end 20 | 21 | 22 | private 23 | def process_region(region) 24 | li " Region: %s %30s".att(:reverse) % [region, ''] 25 | li " Instances".bright 26 | istatus = @option.all ? :any : :running 27 | (Rudy::AWS::EC2::Instances.list(istatus) || []).collect do |inst| 28 | #li " %s (%s): %s; %s; %s" % [inst.awsid, inst.state, inst.dns_public || '[no dns]', inst.size, inst.created] 29 | li " #{inst.to_s.noatt}" 30 | end 31 | 32 | li " Volumes".bright 33 | (Rudy::AWS::EC2::Volumes.list || []).collect do |vol| 34 | li " %s (%s): %sGB; %s" % [vol.awsid, vol.instid || vol.status, vol.size, vol.created] 35 | end 36 | 37 | li " Snapshots".bright 38 | (Rudy::AWS::EC2::Snapshots.list || []).collect do |snap| 39 | li " %s: %s; %s" % [snap.awsid, snap.volid, snap.created] 40 | end 41 | 42 | li " Addresses".bright 43 | (Rudy::AWS::EC2::Addresses.list || []).collect do |o| 44 | li " %s (%s)" % [o.ipaddress, o.instid || 'available'] 45 | end 46 | 47 | li " Groups".bright 48 | li (Rudy::AWS::EC2::Groups.list || []).collect { |o| " #{o.name}" } 49 | 50 | li " Keypairs".bright 51 | li (Rudy::AWS::EC2::Keypairs.list || []).collect { |o| " #{o.name}" } 52 | 53 | li " Images".bright 54 | (Rudy::AWS::EC2::Images.list('self') || []).collect do |o| 55 | li " %s: %s; %s; %s" % [o.awsid, o.location, o.arch, o.visibility] 56 | end 57 | li 58 | end 59 | 60 | end 61 | 62 | end; end 63 | end; end -------------------------------------------------------------------------------- /lib/rudy/cli/aws/ec2/keypairs.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Rudy; module CLI; 4 | module AWS; module EC2; 5 | 6 | class Keypairs < Rudy::CLI::CommandBase 7 | 8 | def create_keypairs_valid? 9 | raise Drydock::ArgError.new('name', @alias) unless @argv.name 10 | true 11 | end 12 | def create_keypairs 13 | kp = execute_action { Rudy::AWS::EC2::Keypairs.create(@argv.name) } 14 | if [:s, :string].member?(@@global.format) 15 | li "Name: #{kp.name}" 16 | li "Fingerprint: #{kp.fingerprint}", $/ 17 | li "Copy the following private key data into a file." 18 | li "Set the permissions to 0600 and keep it safe.", $/ 19 | li kp.private_key 20 | else 21 | print_stobject kp 22 | end 23 | end 24 | 25 | def destroy_keypairs_valid? 26 | raise Drydock::ArgError.new('name', @alias) unless @argv.name 27 | true 28 | end 29 | def destroy_keypairs 30 | raise "Keypair #{@argv.name} does not exist" unless Rudy::AWS::EC2::Keypairs.exists?(@argv.name) 31 | kp = Rudy::AWS::EC2::Keypairs.get(@argv.name) 32 | li "Destroying: #{kp.name}" 33 | execute_check(:medium) 34 | execute_action { Rudy::AWS::EC2::Keypairs.destroy(kp.name) } 35 | end 36 | 37 | def keypairs 38 | klist = Rudy::AWS::EC2::Keypairs.list 39 | print_stobjects klist 40 | end 41 | 42 | 43 | end 44 | 45 | 46 | end; end 47 | end; end 48 | -------------------------------------------------------------------------------- /lib/rudy/cli/aws/ec2/snapshots.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Rudy; module CLI; 4 | module AWS; module EC2; 5 | 6 | class Snapshots < Rudy::CLI::CommandBase 7 | 8 | def create_snapshots_valid? 9 | raise Drydock::ArgError.new('volume ID', @alias) unless @option.volume 10 | @volume = Rudy::AWS::EC2::Volumes.get(@option.volume) 11 | !@volume.nil? 12 | end 13 | def create_snapshots 14 | snap = execute_action { Rudy::AWS::EC2::Snapshots.create(@volume.awsid) } 15 | print_stobject snap 16 | end 17 | 18 | def destroy_snapshots_valid? 19 | raise Drydock::ArgError.new('snapshot ID', @alias) unless @argv.snapid 20 | @snap = Rudy::AWS::EC2::Snapshots.get(@argv.snapid) 21 | raise "Snapshot #{@snap.awsid} does not exist" unless @snap 22 | true 23 | end 24 | def destroy_snapshots 25 | li "Destroying: #{@snap.awsid}" 26 | execute_check(:medium) 27 | execute_action { Rudy::AWS::EC2::Snapshots.destroy(@snap.awsid) } 28 | end 29 | 30 | def snapshots 31 | snaps = Rudy::AWS::EC2::Snapshots.list || [] 32 | print_stobjects snaps 33 | end 34 | 35 | 36 | end 37 | 38 | 39 | end; end 40 | end; end 41 | -------------------------------------------------------------------------------- /lib/rudy/cli/aws/ec2/volumes.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Rudy; module CLI; 4 | module AWS; module EC2; 5 | 6 | class Volumes < Rudy::CLI::CommandBase 7 | 8 | 9 | def volumes_create_valid? 10 | raise "You must supply a volume size. See rudy volume -h" unless @option.size 11 | raise "You must supply a zone." unless @@global.zone 12 | true 13 | end 14 | def volumes_create 15 | li "Creating #{@option.size}GB volume in #{@@global.zone}" 16 | execute_check(:low) 17 | vol = execute_action("Create Failed") { 18 | Rudy::AWS::EC2::Volumes.create(@option.size, @@global.zone, @option.snapshot) 19 | } 20 | li @global.verbose > 1 ? vol.inspect : vol.dump(@@global.format) 21 | end 22 | 23 | 24 | def destroy_volumes_valid? 25 | raise "You must supply a volume ID. See rudy volume -h" unless @argv.volid 26 | 27 | unless Rudy::AWS::EC2::Volumes.exists? @argv.volid 28 | raise Rudy::AWS::EC2::UnknownVolume, @argv.volid 29 | end 30 | true 31 | end 32 | 33 | def destroy_volumes 34 | 35 | @volume = Rudy::AWS::EC2::Volumes.get(@argv.volid) 36 | 37 | raise "Volume #{@volume.awsid} does not exist" unless @volume 38 | raise "Volume #{@volume.awsid} is still in-use" if @volume.in_use? 39 | raise "Volume #{@volume.awsid} is still attached" if @volume.attached? 40 | raise "Volume #{@volume.awsid} is not available (#{@volume.state})" unless @volume.available? 41 | 42 | li "Destroying #{@volume.awsid}" 43 | execute_check(:medium) 44 | execute_action("Destroy Failed") { 45 | Rudy::AWS::EC2::Volumes.destroy(@volume.awsid) 46 | true 47 | } 48 | 49 | vol = Rudy::AWS::EC2::Volumes.get(@volume.awsid) 50 | 51 | li @global.verbose > 1 ? vol.inspect : vol.dump(@@global.format) 52 | end 53 | 54 | 55 | def volumes_attach_valid? 56 | raise "You must supply an instance ID." unless @option.instance 57 | raise "You must supply a volume ID." unless @argv.volid 58 | true 59 | end 60 | def volumes_attach 61 | @option.device ||= "/dev/sdh" 62 | raise "Volume #{@argv.volid} does not exist" unless Rudy::AWS::EC2::Volumes.exists?(@argv.volid) 63 | raise "Volume #{@argv.volid} is already attached" if Rudy::AWS::EC2::Volumes.attached?(@argv.volid) 64 | raise "Instance #{@option.instance} does not exist" unless Rudy::AWS::EC2::Instances.exists?(@option.instance) 65 | 66 | li "Attaching #{@argv.volid} to #{@option.instance} on #{@option.device}" 67 | execute_check(:low) 68 | execute_action("Attach Failed") { 69 | Rudy::AWS::EC2::Volumes.attach(@argv.volid, @option.instance, @option.device) 70 | } 71 | 72 | vol = Rudy::AWS::EC2::Volumes.get(@argv.volid) 73 | li @global.verbose > 1 ? vol.inspect : vol.dump(@@global.format) 74 | end 75 | 76 | def volumes_detach_valid? 77 | raise "You must supply a volume ID." unless @argv.volid 78 | true 79 | end 80 | 81 | def volumes_detach 82 | raise "Volume #{@argv.volid} does not exist" unless Rudy::AWS::EC2::Volumes.exists?(@argv.volid) 83 | vol = Rudy::AWS::EC2::Volumes.get(@argv.volid) 84 | raise "Volume #{vol.awsid} is not attached" unless vol.attached? 85 | 86 | li "Detaching #{vol.awsid} from #{vol.instid}" 87 | execute_check(:medium) 88 | execute_action("Detach Failed") { Rudy::AWS::EC2::Volumes.detach(vol.awsid) } 89 | 90 | vol = Rudy::AWS::EC2::Volumes.get(vol.awsid) 91 | li @global.verbose > 1 ? vol.inspect : vol.dump(@@global.format) 92 | end 93 | 94 | 95 | def volumes 96 | volumes = Rudy::AWS::EC2::Volumes.list || [] 97 | volumes.each do |vol| 98 | li @global.verbose > 1 ? vol.inspect : vol.dump(@@global.format) 99 | end 100 | end 101 | 102 | end 103 | 104 | end; end 105 | end; end 106 | -------------------------------------------------------------------------------- /lib/rudy/cli/aws/ec2/zones.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Rudy; module CLI; 4 | module AWS; module EC2; 5 | 6 | class Zones < Rudy::CLI::CommandBase 7 | 8 | 9 | def zones 10 | zlist = Rudy::AWS::EC2::Zones.list(@argv.name) 11 | print_stobjects zlist 12 | end 13 | 14 | 15 | end 16 | 17 | 18 | end; end 19 | end; end 20 | -------------------------------------------------------------------------------- /lib/rudy/cli/aws/s3/buckets.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Rudy; module CLI; 4 | module AWS; module S3; 5 | 6 | class Buckets < Rudy::CLI::CommandBase 7 | 8 | 9 | def buckets 10 | raise "No bucket name supplied" if !@argv.name && @option.list 11 | s3 = Rudy::AWS::S3.new(@@global.accesskey, @@global.secretkey, @@global.region) 12 | unless @option.list 13 | (s3.list_buckets || []).each do |b| 14 | puts b.name 15 | end 16 | else 17 | puts "All objects in #{@argv.name}:" 18 | (s3.list_bucket_objects(@argv.name) || []).each do |o| 19 | puts o 20 | end 21 | end 22 | end 23 | 24 | def create_buckets_valid? 25 | raise "No bucket name supplied" unless @argv.name 26 | true 27 | end 28 | def create_buckets 29 | s3 = Rudy::AWS::S3.new(@@global.accesskey, @@global.secretkey, @@global.region) 30 | s3.create_bucket(@argv.name, @option.location) 31 | buckets 32 | end 33 | 34 | def destroy_buckets_valid? 35 | raise "No bucket name supplied" unless @argv.name 36 | true 37 | end 38 | def destroy_buckets 39 | execute_check(:medium) 40 | s3 = Rudy::AWS::S3.new(@@global.accesskey, @@global.secretkey, @@global.region) 41 | s3.destroy_bucket(@argv.name) 42 | buckets 43 | end 44 | 45 | 46 | end 47 | 48 | end; end 49 | end; end -------------------------------------------------------------------------------- /lib/rudy/cli/aws/s3/store.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Rudy; module CLI; 4 | module AWS; module S3; 5 | 6 | class Store < Rudy::CLI::CommandBase 7 | 8 | def store_valid? 9 | raise "No path specified" unless @argv.path 10 | raise "No bucket specified" unless @option.bucket 11 | true 12 | end 13 | def store 14 | s3 = Rudy::AWS::S3.new(@@global.accesskey, @@global.secretkey, @@global.region) 15 | puts "Success: %s" % s3.store(@argv.path, @option.bucket) 16 | 17 | end 18 | 19 | end 20 | 21 | end;end 22 | end;end -------------------------------------------------------------------------------- /lib/rudy/cli/aws/sdb/domains.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Rudy; module CLI; 4 | module AWS; module SDB; 5 | 6 | class Domains < Rudy::CLI::CommandBase 7 | 8 | def domains 9 | @sdb = Rudy::AWS::SDB.new(@@global.accesskey, @@global.secretkey, @@global.region) 10 | domains = @sdb.list_domains 11 | puts domains 12 | puts "No domains" if domains.nil? || domains.empty? 13 | end 14 | 15 | def domains_create_valid? 16 | raise "No name specified" unless @argv.name 17 | true 18 | end 19 | def domains_create 20 | @sdb = Rudy::AWS::SDB.new(@@global.accesskey, @@global.secretkey, @@global.region) 21 | @sdb.create_domain @argv.name 22 | execute_check(:low) 23 | domains 24 | end 25 | 26 | def domains_destroy_valid? 27 | raise "No name specified" unless @argv.name 28 | true 29 | end 30 | def domains_destroy 31 | @sdb = Rudy::AWS::SDB.new(@@global.accesskey, @@global.secretkey, @@global.region) 32 | execute_check(:medium) 33 | @sdb.destroy_domain @argv.name 34 | domains 35 | end 36 | 37 | end 38 | 39 | end; end 40 | end; end -------------------------------------------------------------------------------- /lib/rudy/cli/aws/sdb/objects.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Rudy; module CLI; 4 | module AWS; module SDB; 5 | 6 | class Objects < Rudy::CLI::CommandBase 7 | 8 | 9 | def objects_valid? 10 | raise "Usage: rudy-sdb objects DOMAIN" if @argv.empty? 11 | true 12 | end 13 | def objects 14 | @sdb = Rudy::AWS::SDB.new(@@global.accesskey, @@global.secretkey, @@global.region) 15 | 16 | if @argv.key.nil? 17 | query = "select * from #{@argv.name}" 18 | items = @sdb.select query 19 | else 20 | items = [@sdb.get( @argv.name, @argv.key)] 21 | end 22 | 23 | exit unless items 24 | 25 | items.each do |i| 26 | p i 27 | end 28 | end 29 | 30 | def objects_destroy_valid? 31 | raise "Usage: rudy-sdb objects -D DOMAIN OBJECTNAME" if @argv.size < 2 32 | true 33 | end 34 | def objects_destroy 35 | @sdb = Rudy::AWS::SDB.new(@@global.accesskey, @@global.secretkey, @@global.region) 36 | domain, name = @argv 37 | puts "Deleteing #{name} from #{domain}" 38 | @sdb.destroy domain, name 39 | end 40 | 41 | end 42 | 43 | end; end 44 | end; end -------------------------------------------------------------------------------- /lib/rudy/cli/aws/sdb/select.rb: -------------------------------------------------------------------------------- 1 | 2 | module Rudy; module CLI; 3 | module AWS; module SDB; 4 | 5 | class Select < Rudy::CLI::CommandBase 6 | 7 | def query_valid? 8 | raise "No select query supplied" if @argv.empty? 9 | true 10 | end 11 | 12 | def query 13 | @sdb = Rudy::AWS::SDB.new(@@global.accesskey, @@global.secretkey, @@global.region) 14 | results = @sdb.select @argv.query 15 | return if results.nil? 16 | results.each do |r| 17 | p r 18 | end 19 | end 20 | end 21 | 22 | end; end 23 | end; end -------------------------------------------------------------------------------- /lib/rudy/cli/backups.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Rudy 4 | module CLI 5 | class Backups < Rudy::CLI::CommandBase 6 | 7 | 8 | def backups 9 | blist = get_backups 10 | print_stobjects blist 11 | end 12 | 13 | def backups_wash 14 | dirt = (get_backups || []).select { |b| !b.snapshot_exists? } 15 | if dirt.empty? 16 | li "Nothing to wash in #{current_machine_group}" 17 | return 18 | end 19 | 20 | li "The following backup metadata will be deleted:" 21 | li dirt.collect {|b| b.name } 22 | 23 | execute_check(:medium) 24 | 25 | dirt.each do |b| 26 | b.destroy 27 | end 28 | 29 | end 30 | 31 | def backups_create_valid? 32 | @dlist = Rudy::Disks.list 33 | raise "No disks" if @dlist.nil? 34 | raise "No path provided" unless @argv.first 35 | raise "Disk does not exist" unless Rudy::Disks.exists? @argv.first 36 | true 37 | end 38 | 39 | def backups_create 40 | @dlist.each do |d| 41 | li "Creating backup for #{d.name}" 42 | back = d.archive 43 | li back 44 | end 45 | end 46 | 47 | end 48 | end 49 | end -------------------------------------------------------------------------------- /lib/rudy/cli/candy.rb: -------------------------------------------------------------------------------- 1 | 2 | module Rudy 3 | module CLI 4 | class Candy < Rudy::CLI::CommandBase 5 | 6 | def open 7 | machines = Rudy::Machines.list 8 | 9 | if machines 10 | `open http://#{machines.first.dns_public}` 11 | else 12 | li "No machines" 13 | end 14 | end 15 | 16 | end 17 | end 18 | end -------------------------------------------------------------------------------- /lib/rudy/cli/config.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Rudy 4 | module CLI 5 | class Config < Rudy::CLI::CommandBase 6 | 7 | # We force the CLI::Base#print_header to be quiet 8 | def print_header 9 | #@@global.quiet = true 10 | #super 11 | end 12 | 13 | 14 | # Display configuration from the local user data file (~/.rudy/config). 15 | # This config contains user data which is sent to each EC2 when 16 | # it's created. 17 | # 18 | # The primary purpose of this command is to give other apps a way 19 | # to check various configuration values. (This is mostly useful for 20 | # debugging and checking configuration on an instance itself). 21 | # 22 | # It will return the most specific configuration available. If the 23 | # attribute isn'e found it will check each parent for the same attribute. 24 | # e.g. if [prod][app][ami] is not available, it will check [prod][ami] 25 | # and then [ami]. 26 | # 27 | # # Display all configuration 28 | # $ rudy config --all 29 | # 30 | # # Display just machines 31 | # $ rudy config --defaults 32 | # 33 | def config 34 | 35 | # TODO: Re-enable: 36 | # # Display the value for a specific machine. 37 | # $ rudy -e prod -r db config param-name 38 | 39 | if @@config.nil? || @@config.empty? 40 | return if @@global.quiet 41 | raise Rudy::NoConfig 42 | end 43 | 44 | outform = @@global.format == :json ? :to_json : :to_yaml 45 | 46 | types = @option.marshal_dump.keys & @@config.keys # Intersections only 47 | types = @@config.keys if @option.all 48 | types = [:machines] if types.empty? 49 | 50 | if @option.project 51 | rf = File.join(RUDY_HOME, 'Rudyfile') 52 | raise "Cannot find: #{rf}" unless File.exists?(rf) 53 | li File.read(rf) 54 | 55 | elsif @option.script 56 | conf = fetch_script_config 57 | li conf.to_hash.send(outform) if conf 58 | 59 | else 60 | #li "# ACCOUNTS: [not displayed]" if types.delete(:accounts) 61 | types.each do |conftype| 62 | li "# #{conftype.to_s.upcase}" 63 | next unless @@config[conftype] # Nothing to output 64 | if conftype == :accounts 65 | skey = @@config[conftype][:aws][:secretkey] 66 | @@config[conftype][:aws][:secretkey] = hide_secret_key(skey) 67 | end 68 | 69 | li @@config[conftype].to_hash.send(outform) 70 | end 71 | end 72 | 73 | end 74 | 75 | def print_global 76 | # NOTE: This method cannot be called just "global" b/c that conflicts 77 | # with the global accessor for Drydock::Command classes. 78 | if @@global.nil? 79 | raise Rudy::NoGlobal 80 | end 81 | gtmp = @@global.clone 82 | gtmp.format = "yaml" if gtmp.format == :s || gtmp.format == :string 83 | gtmp.secretkey = hide_secret_key(gtmp.secretkey) 84 | li gtmp.dump(gtmp.format) 85 | end 86 | 87 | private 88 | def hide_secret_key(skey) 89 | skey = skey.to_s 90 | "%s%s%s" % [skey[0], '.'*18, skey[-1]] 91 | end 92 | 93 | end 94 | end 95 | end 96 | -------------------------------------------------------------------------------- /lib/rudy/cli/disks.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Rudy 4 | module CLI 5 | class Disks < Rudy::CLI::CommandBase 6 | 7 | def disks 8 | disk_list = get_disks 9 | # If there are no disks currently, there could be backups 10 | # so we grab those to create a list of disks. 11 | if @option.backups 12 | backups = Rudy::Backups.list(more, less) || [] 13 | backups.each_with_index do |b, index| 14 | disk_list << b.disk 15 | end 16 | end 17 | # We go through the list of disks but we'll skip ones we've seen 18 | seen = [] 19 | disk_list.each do |d| 20 | next if seen.member?(d.name) 21 | seen << d.name 22 | print_stobject d 23 | if @option.backups 24 | d.backups.each_with_index do |b, index| 25 | li ' %s' % b.name 26 | ##break if @option.all.nil? && index >= 2 # display only 3, unless all 27 | end 28 | end 29 | end 30 | end 31 | 32 | def disks_wash 33 | dirt = (get_disks || []).select { |d| !d.volume_exists? } 34 | if dirt.empty? 35 | li "Nothing to wash in #{current_machine_group}" 36 | return 37 | end 38 | 39 | li "The following disk metadata will be deleted:" 40 | li dirt.collect {|d| d.name } 41 | 42 | execute_check(:medium) 43 | 44 | dirt.each do |d| 45 | d.destroy(:force) 46 | end 47 | 48 | end 49 | 50 | def disks_create_valid? 51 | @mlist = Rudy::Machines.list 52 | raise "No machines" if @mlist.nil? 53 | 54 | raise "No path provided" unless @argv.first 55 | if !@@global.force && Rudy::Disks.exists?( @argv.first) 56 | raise "Disk exists" if Rudy::Disks.get(@argv.first).volume_attached? 57 | end 58 | raise "No size provided" unless @option.size 59 | 60 | true 61 | end 62 | 63 | def disks_create 64 | @mlist.each do |m| 65 | li machine_separator(m.name, m.instid) 66 | rbox = Rudy::Routines::Handlers::RyeTools.create_box m 67 | rbox.stash = m 68 | disk = Rudy::Disk.new m.position, @argv.first 69 | disk.device = @option.device if @option.device 70 | disk.size = @option.size if @option.size 71 | disk.refresh! if disk.exists? # We need the volume ID if available 72 | li "Creating disk: #{disk.name}" 73 | volumes = m.attached_volumes 74 | # don't include the current disk in the count. 75 | volumes.reject! { |v| v.awsid == disk.volid } if disk.volid && disk.volume_attached? 76 | disk_index = volumes.size + 2 77 | Rudy::Routines::Handlers::Disks.create rbox, disk, disk_index 78 | end 79 | end 80 | 81 | def disks_destroy_valid? 82 | @dlist = Rudy::Disks.list 83 | raise "No disks" if @dlist.nil? 84 | 85 | @mlist = Rudy::Machines.list 86 | raise "No machines" if @mlist.nil? && !@@global.force 87 | 88 | raise "No path provided" unless @argv.first 89 | raise "Disk does not exist" unless Rudy::Disks.exists? @argv.first 90 | true 91 | end 92 | 93 | def disks_destroy 94 | execute_check(:medium) 95 | if !@mlist.empty? 96 | @mlist.each do |m| 97 | rbox = Rudy::Routines::Handlers::RyeTools.create_box m 98 | rbox.stash = m 99 | disk = Rudy::Disk.new m.position, @argv.first 100 | li "Destroying disk: #{disk.name}" 101 | Rudy::Routines::Handlers::Disks.destroy rbox, disk, 0 102 | end 103 | else 104 | @dlist.each do |d| 105 | li "Working on #{d.name}" 106 | Rudy::Routines::Handlers::Disks.destroy nil, d, 0 107 | end 108 | end 109 | end 110 | end 111 | end 112 | end -------------------------------------------------------------------------------- /lib/rudy/cli/execbase.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Rudy::CLI 4 | 5 | # A base for all Drydock executables (bin/rudy etc...). 6 | class Base 7 | extend Drydock 8 | 9 | before do |obj| 10 | # Don't print Rudy header unless requested to 11 | obj.global.print_header = false if (obj.global.verbose == 0) 12 | @start = Time.now 13 | end 14 | 15 | after do |obj| 16 | if obj.global.verbose > 0 17 | puts 18 | @elapsed = Time.now - @start 19 | puts "Elapsed: %.2f seconds" % @elapsed.to_f if @elapsed > 0.1 20 | end 21 | end 22 | 23 | # These globals are used by all bin/ executables 24 | global :A, :accesskey, String, "AWS Access Key" 25 | global :S, :secretkey, String, "AWS Secret Access Key" 26 | global :R, :region, String, "Amazon service region (e.g. #{Rudy::DEFAULT_REGION})" 27 | global :z, :zone, String, "Amazon Availability zone (e.g. #{Rudy::DEFAULT_ZONE})" 28 | global :u, :user, String, "Provide a username (ie: #{Rudy.sysinfo.user})" 29 | global :l, :localhost, String, "Provide a localhost (e.g. #{Rudy.sysinfo.hostname})" 30 | global :i, :identity, String, "Path to SSH identity (private key) for RSA or DSA authentication" 31 | global :k, :pkey, String, "AWS Private Encryption Key (pk-****.pem)" 32 | global :c, :cert, String, "AWS Private Certificate (cert-****.pem)" 33 | global :f, :format, String, "Output format" 34 | global :n, :nocolor, "Disable output colors" 35 | global :Y, :auto, "Skip interactive confirmation" 36 | global :q, :quiet, "Run with less output" 37 | global :O, :offline, "Be cool about the internet being down" 38 | global :C, :config, String, "Specify another configuration file to read (e.g. #{Rudy::CONFIG_FILE})" do |val| 39 | @configs ||= [] 40 | @configs << val 41 | end 42 | global :v, :verbose, "Increase verbosity of output (e.g. -v or -vv or -vvv)" do 43 | @verbose ||= 0 44 | @verbose += 1 45 | end 46 | global :V, :version, "Display version number" do 47 | puts "Rudy version: #{Rudy::VERSION}" 48 | exit 0 49 | end 50 | global :D, :debug, "Enable debug mode" do 51 | Drydock.debug true 52 | Rudy.enable_debug 53 | end 54 | 55 | end 56 | 57 | end 58 | -------------------------------------------------------------------------------- /lib/rudy/cli/images.rb: -------------------------------------------------------------------------------- 1 | 2 | module Rudy 3 | module CLI 4 | class Images < Rudy::CLI::CommandBase 5 | 6 | def bundle_valid? 7 | raise "No S3 bucket provided. See rudy bundle -h" unless @@global.bucket 8 | raise "No image name provided. See rudy bundle -h" unless @argv.name 9 | 10 | @machines = Rudy::Machines.list 11 | raise "No machines" if @machines.nil? 12 | 13 | @machines = @machines.select { |m| m.windows? } 14 | raise "No Windows machines" if @machines.nil? 15 | 16 | true 17 | end 18 | 19 | def bundle 20 | 21 | @machines.each do |m| 22 | li machine_separator(m.name, m.instid) 23 | 24 | cmd = "ec2-bundle-instance" 25 | args = [m.instid, "--region", @@global.region.to_s] 26 | args += ["-b", @@global.bucket, "-p", @argv.name] 27 | args += ["-o", @@global.accesskey, "-w", @@global.secretkey] 28 | args += ["-K", @@global.pkey, "-C", @@global.cert] 29 | 30 | # S3 returned 301 (Moved Permanently) for ACL on bucket [EU-BUCKET] 31 | args += ["--no-bucket-setup"] if @@global.region.to_s == 'eu-west-1' 32 | 33 | if @@global.verbose > 0 34 | li "Running: " << Rye.prepare_command(cmd, args), $/ 35 | end 36 | 37 | unless @@global.quiet 38 | li "Bundling can take up to 60 minutes." 39 | li "Check the status with the following command:" 40 | li Rudy::Huxtable.generate_rudy_command('bundle-status').bright 41 | li $/, "When complete, register the image with the command:" 42 | li Rudy::Huxtable.generate_rudy_command('images', '-R', @argv.name).bright 43 | end 44 | 45 | execute_check(:medium) 46 | 47 | ret = Rye.shell cmd, args 48 | li ret.stderr, ret.stdout 49 | end 50 | end 51 | 52 | def bundle_status 53 | cmd = 'ec2-describe-bundle-tasks' 54 | args = ["--region", @@global.region.to_s] 55 | args += ["-K", @@global.pkey, "-C", @@global.cert] 56 | 57 | if @@global.verbose > 0 58 | li "Running: " << Rye.prepare_command(cmd, args), $/ 59 | end 60 | 61 | ret = Rye.shell cmd, args 62 | li ret.stderr, ret.stdout 63 | 64 | end 65 | 66 | def register_images_valid? 67 | raise "No S3 bucket provided. See rudy bundle -h" unless @@global.bucket 68 | raise "No image name provided. See rudy bundle -h" unless @argv.name 69 | 70 | true 71 | end 72 | def register_images 73 | name = "#{@@global.bucket}/#{@argv.name}.manifest.xml" 74 | li Rudy::AWS::EC2::Images.register(name) 75 | end 76 | 77 | def deregister_images_valid? 78 | unless @argv.first && Rudy::Utils.is_id?(:image, @argv.first) 79 | raise "Must supply an AMI ID. See rudy images -h" 80 | end 81 | true 82 | end 83 | def deregister_images 84 | execute_check(:low) 85 | li Rudy::AWS::EC2::Images.deregister(@argv.ami) ? "Done" : "Unknown error" 86 | end 87 | 88 | def images 89 | @option.owner ||= 'self' 90 | images = Rudy::AWS::EC2::Images.list(@option.owner, @argv) || [] 91 | print_stobjects images 92 | end 93 | 94 | 95 | end 96 | end 97 | end -------------------------------------------------------------------------------- /lib/rudy/cli/info.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Rudy 4 | module CLI 5 | class Info < Rudy::CLI::CommandBase 6 | 7 | def info 8 | process_region @@global.region 9 | oregions = Rudy::AWS::VALID_REGIONS - [@@global.region.to_sym] 10 | if @option.all 11 | oregions.each do |region| 12 | Rudy::AWS::EC2.connect @@global.accesskey, @@global.secretkey, region 13 | process_region region 14 | end 15 | else 16 | li $/, "Other regions: " << oregions.join(', ') 17 | end 18 | end 19 | 20 | 21 | private 22 | def process_region(region) 23 | li " Region: %s %30s".att(:reverse) % [region, ''] 24 | li " Machines".bright 25 | 26 | (get_machines(:region => region) rescue []).collect do |m| 27 | m.refresh! 28 | li " " << m.to_s.noatt 29 | end 30 | 31 | li " Disks".bright 32 | (get_disks(:region => region) || []).collect do |d| 33 | d.refresh! 34 | li " " << d.to_s.noatt 35 | end 36 | 37 | li " Backups".bright 38 | (get_backups(:region => region) || []).collect do |b| 39 | b.refresh! 40 | li " " << b.to_s.noatt 41 | end 42 | 43 | li 44 | end 45 | 46 | end 47 | end 48 | end -------------------------------------------------------------------------------- /lib/rudy/cli/keypairs.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Rudy 4 | module CLI 5 | class Keypairs < Rudy::CLI::CommandBase 6 | 7 | 8 | def keypairs_add_valid? 9 | true 10 | end 11 | 12 | def keypairs_add 13 | li current_group_name 14 | end 15 | 16 | def keypairs_valid? 17 | @pkey = current_user_keypairpath 18 | unless File.exists? @pkey 19 | raise "No private key file for #{current_machine_user} in #{current_group_name}" 20 | end 21 | true 22 | end 23 | 24 | def keypairs 25 | li Rudy::AWS::EC2::Keypairs.get(current_user_keypairname) 26 | end 27 | 28 | def keypairs_show_valid? 29 | keypairs_valid? 30 | end 31 | 32 | def keypairs_show 33 | content = File.read(@pkey) 34 | rkey = Rye::Key.new content 35 | li "# #{@pkey}" 36 | li content 37 | li rkey.public_key 38 | end 39 | 40 | end 41 | end 42 | end 43 | 44 | -------------------------------------------------------------------------------- /lib/rudy/cli/metadata.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Rudy 4 | module CLI 5 | class Metadata < Rudy::CLI::CommandBase 6 | 7 | def metadata_valid? 8 | @option.rtype ||= 'm' 9 | @metaobj = Rudy::Metadata.get_rclass @option.rtype 10 | true 11 | end 12 | 13 | def metadata 14 | unless @argv.empty? 15 | h = Rudy::Metadata.get(@argv.first) 16 | return if h.nil? 17 | @metaobj = Rudy::Metadata.get_rclass h['rtype'].first 18 | objlist = Hash[@argv.first => @metaobj.from_hash(h)] 19 | else 20 | more, less = {}, [] 21 | less = [:environment, :role, :zone, :region, :position] if @option.all 22 | objlist = @metaobj.list_as_hash(more, less) || {} 23 | end 24 | 25 | objlist.each_pair do |k,o| 26 | li "#{k}: " << o.inspect 27 | end 28 | end 29 | 30 | def metadata_delete_valid? 31 | raise "Must supply object ID" unless @argv.oid 32 | raise Rudy::Metadata::UnknownObject, @argv.oid unless Rudy::Metadata.exists? @argv.oid 33 | true 34 | end 35 | 36 | def metadata_delete 37 | 38 | unless @@global.quiet 39 | msg = "NOTE: This will delete only the metadata and " 40 | msg << "not the EC2 object (volume, instance, etc...)" 41 | li msg 42 | end 43 | 44 | execute_check(:medium) 45 | 46 | Rudy::Metadata.destroy @argv.oid 47 | end 48 | 49 | end 50 | end 51 | end -------------------------------------------------------------------------------- /lib/rudy/cli/networks.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Rudy 4 | module CLI 5 | class Networks < Rudy::CLI::CommandBase 6 | 7 | def networks 8 | name = current_group_name 9 | Rudy::AWS::EC2::Groups.list(name).each do |group| 10 | li @@global.verbose > 0 ? group.inspect : group.dump(@@global.format) 11 | end 12 | end 13 | 14 | def create_networks 15 | Rudy::Routines::Handlers::Group.create rescue nil 16 | networks 17 | end 18 | 19 | def destroy_networks 20 | Rudy::Routines::Handlers::Group.destroy rescue nil 21 | end 22 | 23 | def update_networks 24 | Rudy::Routines::Handlers::Group.authorize rescue nil 25 | networks 26 | end 27 | 28 | def local_networks 29 | ea = Rudy::Utils::external_ip_address || '' 30 | ia = Rudy::Utils::internal_ip_address || '' 31 | if @global.quiet 32 | li ia unless @option.external && !@option.internal 33 | li ea unless @option.internal && !@option.external 34 | else 35 | li "%10s: %s" % ['Internal', ia] unless @option.external && !@option.internal 36 | li "%10s: %s" % ['External', ea] unless @option.internal && !@option.external 37 | end 38 | @global.quiet = true # don't print elapsed time 39 | end 40 | 41 | 42 | def modify_group_valid? 43 | if @option.owner == 'self' 44 | raise "AWS_ACCOUNT_NUMBER not set" unless @@global.accountnum 45 | @option.owner = @@global.accountnum 46 | end 47 | 48 | if (@option.addresses || @option.ports) && (@option.group || @option.owner) 49 | raise Drydock::OptError.new('', @alias, "Cannot mix group and network authorization") 50 | end 51 | if @option.owner && !@option.group 52 | raise Drydock::OptError.new('', @alias, "Must provide -g with -o") 53 | end 54 | 55 | true 56 | end 57 | 58 | def revoke_networks_valid?; modify_group_valid?; end 59 | def revoke_networks; modify_group(:revoke); end 60 | 61 | def authorize_networks_valid?; modify_group_valid?; end 62 | def authorize_networks; modify_group(:authorize); end 63 | 64 | private 65 | 66 | def modify_group(action) 67 | group = current_group_name 68 | 69 | opts = check_options 70 | if (@option.group || @option.owner) 71 | g = [opts[:owner], opts[:group]].join(':') 72 | li "#{action.to_s.capitalize} access to #{group.bright} from #{g.bright}" 73 | else 74 | print "#{action.to_s.capitalize} access to #{group.bright}" 75 | li " from #{opts[:addresses].join(', ').bright}" 76 | print "on #{opts[:protocols].join(', ').bright} " 77 | li "ports: #{opts[:ports].map { |p| "#{p.join(' to ').bright}" }.join(', ')}" 78 | end 79 | execute_check(:medium) 80 | execute_action { 81 | if (@option.group || @option.owner) 82 | Rudy::AWS::EC2::Groups.send("#{action.to_s}_group", group, opts[:group], opts[:owner]) 83 | else 84 | Rudy::AWS::EC2::Groups.send(action, group, opts[:addresses], opts[:ports], opts[:protocols]) 85 | end 86 | } 87 | networks 88 | end 89 | 90 | def check_options 91 | opts = {} 92 | [:addresses, :protocols, :owner, :group, :ports].each do |opt| 93 | opts[opt] = @option.send(opt) if @option.respond_to?(opt) 94 | end 95 | unless @option.group || @option.owner 96 | opts[:ports].collect! { |port| port.split(/[:-]/) } if opts[:ports] 97 | opts[:ports] ||= [[22,22],[80,80],[443,443]] 98 | opts[:addresses] ||= [Rudy::Utils::external_ip_address] 99 | opts[:protocols] ||= [:tcp] 100 | else 101 | opts[:owner] ||= @@global.accountnum 102 | end 103 | opts 104 | end 105 | 106 | end 107 | 108 | 109 | 110 | end 111 | end -------------------------------------------------------------------------------- /lib/rudy/cli/routines.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Rudy; module CLI; 4 | class Routines < Rudy::CLI::CommandBase 5 | 6 | def routines_valid? 7 | raise Rudy::NoRoutinesConfig unless @@config.routines 8 | true 9 | end 10 | 11 | def routines 12 | if @@config.nil? || @@config.empty? 13 | return if @@global.quiet 14 | raise Rudy::NoConfig 15 | end 16 | 17 | if @option.all 18 | routine = @@config.routines 19 | else 20 | routine = {} 21 | routine.merge! @@config.routines.find_deferred(@@global.environment, @@global.role) || {} 22 | routine.merge! @@config.routines.find(@@global.role) || {} 23 | #envs, roles = @@global.environment || [], @@global.role || [] 24 | #envs.each do |env| 25 | # roles.each do |role| 26 | # routine.merge! @@config.routines.find_deferred(env, role) || {} 27 | # end 28 | #end 29 | #roles.each do |role| 30 | # routine.merge! @@config.routines.find(role) || {} 31 | #end 32 | end 33 | 34 | outform = @@global.format == :json ? :to_json : :to_yaml 35 | 36 | li routine.to_hash.send(outform) 37 | end 38 | 39 | def startup_valid? 40 | @rr = Rudy::Routines::Startup.new(@alias, @option, @argv) 41 | @rr.raise_early_exceptions 42 | true 43 | end 44 | def startup 45 | machines = @rr.execute || [] 46 | li $/, "The following machines are now available:" unless machines.empty? 47 | print_stobjects machines, :noverbose 48 | end 49 | 50 | def reboot_valid? 51 | @rr = Rudy::Routines::Reboot.new(@alias, @option, @argv) 52 | @rr.raise_early_exceptions 53 | true 54 | end 55 | def reboot 56 | machines = @rr.execute 57 | li $/, "The following machines have been restarted:" 58 | print_stobjects machines, :noverbose 59 | end 60 | 61 | def passthrough_valid? 62 | @rr = Rudy::Routines::Passthrough.new(@alias, @option, @argv) 63 | @rr.raise_early_exceptions 64 | true 65 | end 66 | 67 | # All unknown commands are sent here (using Drydock's trawler). 68 | # By default, the generic passthrough routine is executed which 69 | # does nothing other than execute the routine config block that 70 | # matches +@alias+ (the name used on the command-line). Calling 71 | # 72 | # $ rudy unknown 73 | # 74 | # would end up here because it's an unknown command. Passthrough 75 | # then looks for a routine config in the current environment and 76 | # role called "unknown". If found, it's executed otherwise it'll 77 | # raise an exception. 78 | # 79 | def passthrough 80 | 81 | machines = @rr.execute 82 | 83 | if !@@global.quiet && !machines.empty? 84 | li $/, "The following machines were processed:" 85 | print_stobjects machines, :noverbose 86 | end 87 | 88 | end 89 | 90 | def shutdown_valid? 91 | @rr = Rudy::Routines::Shutdown.new(@alias, @option, @argv) 92 | @rr.raise_early_exceptions 93 | true 94 | end 95 | def shutdown 96 | routine = fetch_routine_config(:shutdown) rescue {} 97 | 98 | li "All machines in #{current_machine_group} will be shutdown".bright 99 | if routine && routine.disks 100 | if routine.disks.destroy 101 | li "The following filesystems will be destroyed:".bright 102 | li routine.disks.destroy.keys.join($/).bright 103 | end 104 | end 105 | 106 | execute_check :medium 107 | 108 | machines = @rr.execute 109 | 110 | li $/, "The following instances have been destroyed:" 111 | machines.each do |machine| 112 | li '%s %s ' % [machine.name.bright, machine.instid] 113 | end 114 | 115 | end 116 | 117 | 118 | end 119 | end; end 120 | 121 | -------------------------------------------------------------------------------- /lib/rudy/disks.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Rudy 4 | 5 | module Disks 6 | RTYPE = 'disk'.freeze 7 | 8 | extend self 9 | extend Rudy::Metadata::ClassMethods 10 | include Rudy::Huxtable 11 | extend Rudy::Huxtable 12 | 13 | def get(path) 14 | tmp = Rudy::Disk.new path 15 | record = Rudy::Metadata.get tmp.name 16 | return nil unless record.is_a?(Hash) 17 | tmp.from_hash record 18 | end 19 | 20 | def from_hash(h) 21 | Rudy::Disk.from_hash h 22 | end 23 | 24 | def exists?(path) 25 | !get(path).nil? 26 | end 27 | 28 | 29 | class NotAttached < Rudy::Error; end 30 | class NotFormatted < Rudy::Error; end 31 | class AlreadyFormatted < Rudy::Error; end 32 | class AlreadyMounted < Rudy::Error; end 33 | class AlreadyAttached < Rudy::Error; end 34 | class NotMounted < Rudy::Error; end 35 | class InUse < Rudy::Error; end 36 | 37 | end 38 | end -------------------------------------------------------------------------------- /lib/rudy/exceptions.rb: -------------------------------------------------------------------------------- 1 | module Rudy 2 | 3 | class Error < RuntimeError 4 | def initialize(obj=nil); @obj = obj; end 5 | def message; "#{self.class}: #{@obj}"; end 6 | end 7 | class InsecureKeyPermissions < Rudy::Error 8 | def message 9 | lines = ["Insecure file permissions for #{@obj}"] 10 | lines << "Try: chmod 600 #{@obj}" 11 | lines.join($/) 12 | end 13 | end 14 | 15 | #-- 16 | # TODO: Update exception Syntax based on: 17 | # http://blog.rubybestpractices.com/posts/gregory/anonymous_class_hacks.html 18 | #++ 19 | 20 | class NoConfig < Rudy::Error 21 | def message; "No configuration found!"; end 22 | end 23 | class NoGlobal < Rudy::Error 24 | def message; "No globals defined!"; end 25 | end 26 | class NoMachinesConfig < Rudy::Error 27 | def message; "No machines configuration. Check your configs!"; end 28 | end 29 | class NoRoutinesConfig < Rudy::Error 30 | def message; "No routines configuration. Check your configs!"; end 31 | end 32 | class ServiceUnavailable < Rudy::Error 33 | def message; "#{@obj} is not available. Check your internets!"; end 34 | end 35 | class MachineGroupAlreadyRunning < Rudy::Error 36 | def message; "Machine group #{@obj} is already running."; end 37 | end 38 | class MachineGroupNotRunning < Rudy::Error 39 | def message; "Machine group #{@obj} is not running."; end 40 | end 41 | class MachineGroupMetadataExists < Rudy::Error 42 | def message; "Machine group #{@obj} has existing metadata."; end 43 | end 44 | class MachineAlreadyRunning < Rudy::Error 45 | def message; "Machine #{@obj} is already running."; end 46 | end 47 | class MachineNotRunning < Rudy::Error 48 | def message; "Machine #{@obj} is not running."; end 49 | end 50 | class NoMachines < Rudy::Error; 51 | def message 52 | msg = "No machines running " 53 | msg << "in #{@obj}" if @obj 54 | msg 55 | end 56 | end 57 | class MachineGroupNotDefined < Rudy::Error 58 | def message; "#{@obj} is not defined in machines config."; end 59 | end 60 | class PrivateKeyFileExists < Rudy::Error 61 | def message; "Private key #{@obj} already exists."; end 62 | end 63 | class PrivateKeyNotFound < Rudy::Error 64 | def message; "Private key file #{@obj} not found."; end 65 | end 66 | class UnsupportedOS < Rudy::Error; end 67 | 68 | 69 | class NotImplemented < Rudy::Error; end 70 | 71 | class SpotRequestCancelled < Rudy::Error; end 72 | 73 | 74 | end -------------------------------------------------------------------------------- /lib/rudy/guidelines.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Rudy 4 | module Guidelines 5 | extend self 6 | AFE = "Always fail early" 7 | ABA = "Always be accurate" 8 | CBC = "Consistency before cuteness" 9 | UNO = "Ugly's not okay" 10 | WOC = "Write offensive code" 11 | def inspect 12 | all = Guidelines.constants 13 | g = all.collect { |c| '%s="%s"' % [c, const_get(c)] } 14 | %q{#} % [self.object_id, g.join(' ')] 15 | end 16 | end 17 | end 18 | 19 | puts Rudy::Guidelines.inspect if __FILE__ == $0 -------------------------------------------------------------------------------- /lib/rudy/machines.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Rudy 4 | 5 | module Machines 6 | RTYPE = 'm'.freeze 7 | 8 | extend self 9 | extend Rudy::Metadata::ClassMethods 10 | extend Rudy::Huxtable 11 | 12 | def get(position) 13 | tmp = Rudy::Machine.new position 14 | record = Rudy::Metadata.get tmp.name 15 | return nil unless record.is_a?(Hash) 16 | tmp.from_hash record 17 | end 18 | 19 | def self.list(*args, &blk) 20 | machs = super(*args, &blk) || [] 21 | manual = [fetch_machine_param(:hostname)].flatten.compact 22 | manual.reject! { |m| m.is_a?(Symbol) } 23 | machs.push *manual 24 | machs 25 | end 26 | def find_next_position 27 | raise "reimplement by looking at position values" 28 | list = Rudy::Machines.list({}, [:position]) || [] 29 | pos = list.size + 1 30 | pos.to_s.rjust(2, '0') 31 | end 32 | 33 | # Returns true if any machine metadata exists for this group 34 | def exists?(pos=nil) 35 | machines = pos.nil? ? list : get(pos) 36 | !machines.nil? 37 | end 38 | 39 | # Returns true if all machines in the group are running instances 40 | def running?(pos=nil) 41 | group = pos.nil? ? list : [get(pos)].compact 42 | return false if group.nil? || group.empty? 43 | group.collect! { |m| m.instance_running? } 44 | !group.member?(false) 45 | end 46 | 47 | # Returns an Array of newly created Rudy::Machine objects 48 | def create(size=nil) 49 | if Rudy::Huxtable.global.position.nil? 50 | size ||= current_machine_count.to_i || 1 51 | group = Array.new(size) do |i| 52 | m = Rudy::Machine.new(i + 1) 53 | m.create 54 | li "Created: #{m.to_s}" 55 | m 56 | end 57 | else 58 | m = Rudy::Machine.new(Rudy::Huxtable.global.position) 59 | m.create 60 | li "Created: #{m.to_s}" 61 | group = [m] 62 | end 63 | group 64 | end 65 | 66 | def from_spot_request(request) 67 | instances = Rudy::AWS::EC2::Instances.list(:any, request.map(&:instid)) 68 | 69 | current_machine_positions.zip(instances).map do |position, instance| 70 | Rudy::Machine.new(position).tap do |machine| 71 | machine.instid = instance.awsid 72 | machine.created = machine.started = Time.now 73 | machine.state = instance.state 74 | machine.save 75 | sleep 1 # Eventual consistency in SimpleDB 76 | end 77 | end 78 | end 79 | 80 | def restart 81 | group = list 82 | raise MachineGroupNotRunning, current_machine_group if group.nil? 83 | group.each do |inst| 84 | inst.restart 85 | end 86 | group 87 | end 88 | 89 | def from_hash(h) 90 | Rudy::Machine.from_hash h 91 | end 92 | 93 | end 94 | 95 | end -------------------------------------------------------------------------------- /lib/rudy/metadata/backup.rb: -------------------------------------------------------------------------------- 1 | 2 | require 'date' 3 | 4 | module Rudy 5 | class Backup < Storable 6 | include Rudy::Metadata 7 | include Gibbler::Complex 8 | 9 | field :created => Time 10 | field :rtype => String 11 | field :snapid => String 12 | field :volid => String 13 | 14 | field :path => String 15 | 16 | field :year => String 17 | field :month => String 18 | field :day => String 19 | field :hour => String 20 | field :minute => String 21 | field :second => String 22 | 23 | field :user => String 24 | field :size => String 25 | field :fstype => String 26 | 27 | # If one argument is supplied: 28 | # * +path+ is a an absolute filesystem path 29 | # * +opts+ is a hash of disk options. 30 | # 31 | # If two arguments are supplied: 32 | # * +position+ 33 | # * +path+ is a an absolute filesystem path 34 | # * +opts+ is a hash of disk options. 35 | # 36 | # Valid options are: 37 | # * +:path+ is a an absolute filesystem path (overridden by +path+ arg) 38 | # * +:position+ (overridden by +position+ arg) 39 | # * +:created+ is an instance of Time 40 | # * +:user+ is the name of the user which created the backup. 41 | # 42 | def initialize(position=nil, path=nil, opts={}) 43 | # Swap arg values if only one is supplied. 44 | path, position = position, nil if !position.nil? && path.nil? 45 | position ||= '01' 46 | 47 | opts = { 48 | :created => Time.now.utc, 49 | :user => Rudy.sysinfo.user 50 | }.merge opts 51 | 52 | super Rudy::Backups::RTYPE, opts # Rudy::Metadata#initialize 53 | 54 | @position, @path = position, path 55 | 56 | postprocess 57 | 58 | end 59 | 60 | def postprocess 61 | @position &&= @position.to_s.rjust(2, '0') 62 | @year = @created.year 63 | @month = @created.month.to_s.rjust(2, '0') 64 | @day = @created.mday.to_s.rjust(2, '0') 65 | @hour = @created.hour.to_s.rjust(2, '0') 66 | @minute = @created.min.to_s.rjust(2, '0') 67 | @second = @created.sec.to_s.rjust(2, '0') 68 | end 69 | 70 | def to_s(*args) 71 | [self.name.bright, self.snapid, self.volid, self.size, self.fstype].join '; ' 72 | end 73 | 74 | def name 75 | sep = File::SEPARATOR 76 | dirs = @path.split sep if @path && !@path.empty? 77 | unless @path == File::SEPARATOR 78 | dirs.shift while dirs && (dirs[0].nil? || dirs[0].empty?) 79 | end 80 | # Calls Rudy::Metadata#name with backup specific components 81 | super [dirs, date, time, second] 82 | end 83 | 84 | def date 85 | "%s%s%s" % [@year, @month, @day] 86 | end 87 | 88 | def time 89 | "%s%s" % [@hour, @minute] 90 | end 91 | 92 | def create 93 | raise DuplicateRecord, self.name if exists? 94 | disk = self.disk 95 | ld "DISK: #{disk.name}" 96 | raise Rudy::Backups::NoDisk, disk.name unless disk.exists? 97 | @volid ||= disk.volid 98 | snap = Rudy::AWS::EC2::Snapshots.create(@volid) 99 | #snap = Rudy::AWS::EC2::Snapshots.list.first # debugging 100 | ld "SNAP: #{snap.inspect}" 101 | @snapid, @raw = snap.awsid, true 102 | @size, @fstype = disk.size, disk.fstype 103 | self.save :replace 104 | self 105 | end 106 | 107 | def restore 108 | raise UnknownObject, self.name unless exists? 109 | raise Rudy::Backups::NoBackup, self.name unless any? 110 | 111 | end 112 | 113 | # Are there any backups for the associated disk? 114 | def any? 115 | backups = Rudy::Backups.list self.descriptors, [:year, :month, :day, :hour, :second] 116 | !backups.nil? 117 | end 118 | 119 | def descriptors 120 | super :position, :path, :year, :month, :day, :hour, :second 121 | end 122 | 123 | def destroy 124 | Rudy::AWS::EC2::Snapshots.destroy(@snapid) if snapshot_exists? 125 | super() 126 | end 127 | 128 | def valid? 129 | !@path.nil? && !@path.empty? && @created.is_a?(Time) && !@volid.nil? 130 | end 131 | 132 | def disk 133 | opts = { 134 | :region => @region, :zone => @zone, 135 | :environment => @environment, :role => @role, 136 | :size => @size, :fstype => @fstype 137 | } 138 | disk = Rudy::Disk.new @position, @path, opts 139 | disk.refresh! if disk.exists? 140 | disk.size = @size 141 | disk.fstype = @fstype 142 | disk 143 | end 144 | 145 | def disk_exists? 146 | self.disk.exists? 147 | end 148 | 149 | # Create snapshot_*? methods 150 | %w[exists? completed?].each do |state| 151 | define_method("snapshot_#{state}") do 152 | return false if @snapid.nil? || @snapid.empty? 153 | Rudy::AWS::EC2::Snapshots.send(state, @snapid) 154 | end 155 | end 156 | 157 | end 158 | end -------------------------------------------------------------------------------- /lib/rudy/mixins.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module REXML 4 | module Node 5 | include Gibbler::String 6 | end 7 | end 8 | 9 | class Hash 10 | # A depth-first look to find the deepest point in the Hash. 11 | # The top level Hash is counted in the total so the final 12 | # number is the depth of its children + 1. An example: 13 | # 14 | # ahash = { :level1 => { :level2 => {} } } 15 | # ahash.deepest_point # => 3 16 | # 17 | def deepest_point(h=self, steps=0) 18 | if h.is_a?(Hash) 19 | steps += 1 20 | h.each_pair do |n,possible_h| 21 | ret = deepest_point(possible_h, steps) 22 | steps = ret if steps < ret 23 | end 24 | else 25 | return 0 26 | end 27 | steps 28 | end 29 | end 30 | 31 | class Symbol 32 | unless method_defined? :empty? 33 | def empty? 34 | self.to_s.empty? 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/rudy/routines/base.rb: -------------------------------------------------------------------------------- 1 | 2 | module Rudy; module Routines; 3 | class Base 4 | include Rudy::Huxtable 5 | 6 | @@run = true 7 | 8 | def self.run?; @@run; end 9 | def self.disable_run; @@run = false; end 10 | def self.enable_run; @@run = true; end 11 | 12 | def run?; @@run; end 13 | def disable_run; @@run = false; end 14 | def enable_run; @@run = true; end 15 | 16 | # An Array Rudy::Machines objects that will be processed 17 | attr_reader :machines 18 | 19 | # * +name+ The name of the command specified on the command line 20 | # * +option+ A Hash or OpenStruct of named command line options. 21 | # If it's a Hash it will be converted to an OpenStruct. 22 | # * +argv+ An Array of arguments 23 | # 24 | # +option+ and +argv+ are made available to the routine block. 25 | # 26 | # routines do 27 | # magic do |options,argv| 28 | # ... 29 | # end 30 | # end 31 | # 32 | def initialize(name=nil, option={}, argv=[], *args) 33 | name ||= (self.class.to_s.split(/::/)).last.downcase 34 | option = OpenStruct.new(option) if option.is_a? Hash 35 | @name, @option, @argv = name.to_sym, option, argv 36 | a, s, r = @@global.accesskey, @@global.secretkey, @@global.region 37 | @@sdb ||= Rudy::AWS::SDB.new(a, s, r) 38 | 39 | # Grab the routines configuration for this routine name 40 | # e.g. startup, sysupdate, installdeps 41 | @routine = fetch_routine_config @name rescue {} 42 | 43 | ld "Routine: #{@routine.inspect}" 44 | 45 | if @routine 46 | # Removes the dependencies from the routines hash. 47 | # We run these separately from the other actions. 48 | @before, @after = @routine.delete(:before), @routine.delete(:after) 49 | end 50 | 51 | # Share one Rye::Box instance for localhost across all routines 52 | unless defined?(@@lbox) 53 | host, opts = @@global.localhost, { :user => Rudy.sysinfo.user } 54 | @@lbox = Rudy::Routines::Handlers::RyeTools.create_box host, opts 55 | end 56 | 57 | disable_run if @@global.testrun 58 | 59 | # We create these frozen globals for the benefit of 60 | # the local and remote routine blocks. 61 | $global = @@global.dup.freeze unless $global 62 | $option = option.dup.freeze unless $option 63 | 64 | ## TODO: get the machine config for just the current machine group. This 65 | ## probably requires Caesars to be aware of which nodes are structural. 66 | ##$config = fetch_machine_config unless $config 67 | 68 | init(*args) if respond_to? :init 69 | end 70 | 71 | def raise_early_exceptions; raise "Please override"; end 72 | def execute; raise "Please override"; end 73 | 74 | end 75 | 76 | end; end; -------------------------------------------------------------------------------- /lib/rudy/routines/handlers/base.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Rudy; module Routines; module Handlers; 4 | module Base 5 | include Rudy::Huxtable 6 | 7 | def trap_rbox_errors(ret=nil, &command) 8 | begin 9 | ret = command.call if command 10 | return unless ret.is_a?(Rye::Rap) 11 | li ' ' << ret.stdout.join("#{$/} ") if !ret.stdout.empty? 12 | print_response(ret) 13 | rescue IOError => ex 14 | le " Connection Error (#{ex.message})".color(:red) 15 | choice = Annoy.get_user_input('(S)kip (R)etry (F)orce (A)bort: ', nil, 3600) || '' 16 | if choice.match(/\AS/i) 17 | return 18 | elsif choice.match(/\AR/i) 19 | retry 20 | elsif choice.match(/\AF/i) 21 | @@global.force = true 22 | retry 23 | else 24 | exit 12 25 | end 26 | end 27 | 28 | ret 29 | end 30 | 31 | def keep_going? 32 | Annoy.pose_question(" Keep going?\a ", /yes|y|ya|sure|you bet!/i, STDERR) 33 | end 34 | 35 | 36 | private 37 | def print_response(rap) 38 | colour = rap.exit_status != 0 ? :red : :normal 39 | [:stderr].each do |sumpin| 40 | next if rap.send(sumpin).empty? 41 | le 42 | le((" #{sumpin.to_s.upcase} " << '-'*38).color(colour).bright) 43 | le " " << rap.send(sumpin).join("#{$/} ").color(colour) 44 | end 45 | le " Exit code: #{rap.exit_status}".color(colour) if rap.exit_status != 0 46 | end 47 | 48 | end 49 | 50 | end; end; end 51 | 52 | -------------------------------------------------------------------------------- /lib/rudy/routines/handlers/depends.rb: -------------------------------------------------------------------------------- 1 | 2 | module Rudy; module Routines; module Handlers; 3 | module Depends 4 | include Rudy::Routines::Handlers::Base 5 | extend self 6 | 7 | ## NOTE: Dependencies don't use Rudy::Routines.add_handler but we 8 | ## define them ehere anyway so raise_early_exceptions passes. 9 | Rudy::Routines.add_handler :before, self 10 | Rudy::Routines.add_handler :after, self 11 | 12 | def raise_early_exceptions(type, depends, rset, lbox, argv=nil) 13 | unless depends.kind_of? Array 14 | raise Rudy::Error, "#{type} must be a kind of Array (#{depends.class})" 15 | end 16 | raise Rudy::Routines::EmptyDepends, type if depends.nil? || depends.empty? 17 | depends.flatten.compact.each do |name| 18 | raise Rudy::Routines::NoRoutine, name unless valid_routine?(name) 19 | end 20 | end 21 | 22 | # A simple wrapper for executing a routine. 23 | # 24 | # * +routine_name+ should be a Symbol representing a routine 25 | # available to the current machine group. 26 | # 27 | # This method finds the handler for the given routine, 28 | # creates an instance, calls raise_early_exceptions, 29 | # and finally executes the routine. 30 | def execute(routine_name, argv=[]) 31 | routine_obj = Rudy::Routines.get_routine routine_name 32 | ld "Executing dependency: #{routine_name} (#{routine_obj}) (argv: #{argv})" 33 | routine = routine_obj.new routine_name, {}, argv 34 | routine.raise_early_exceptions 35 | routine.execute 36 | end 37 | 38 | # Calls execute for each routine name in +depends+ (an Array). 39 | # Does nothing if given an empty Array or nil. 40 | def execute_all(depends, argv=[]) 41 | return if depends.nil? || depends.empty? 42 | depends = depends.flatten.compact 43 | ld "Found depenencies: #{depends.join(', ')}" 44 | depends.each { |routine| execute(routine, argv) } 45 | end 46 | 47 | end 48 | 49 | end; end; end -------------------------------------------------------------------------------- /lib/rudy/routines/handlers/group.rb: -------------------------------------------------------------------------------- 1 | 2 | module Rudy; module Routines; module Handlers; 3 | module Group 4 | include Rudy::Routines::Handlers::Base 5 | extend self 6 | 7 | Rudy::Routines.add_handler :network, self 8 | 9 | 10 | def raise_early_exceptions(type, batch, rset, lbox, argv=nil) 11 | 12 | end 13 | 14 | def execute(type, routine, rset, lbox, argv=nil) 15 | routine.each_pair do |action, definition| 16 | unless respond_to?(action.to_sym) 17 | Rudy::Huxtable.le %Q(GroupHandler: unknown action "#{action}") 18 | next 19 | end 20 | Rudy::Huxtable.ld %Q(GroupHandler: "#{action}") 21 | Rudy::Routines::Handlers::Group.send(action, definition) 22 | end 23 | end 24 | 25 | def create(name=nil) 26 | name ||= current_group_name 27 | return if exists? name 28 | li "Creating group: #{name}" 29 | Rudy::AWS::EC2::Groups.create name 30 | end 31 | 32 | def authorize(ports=nil, addresses=nil, name=nil) 33 | modify(:authorize, ports, addresses, name) 34 | end 35 | 36 | def revoke(ports=nil, addresses=nil, name=nil) 37 | modify(:revoke, ports, addresses, name) 38 | end 39 | 40 | def exists?(name=nil) 41 | name ||= current_group_name 42 | Rudy::AWS::EC2::Groups.exists? name 43 | end 44 | 45 | def destroy(name=nil) 46 | name ||= current_group_name 47 | return unless exists? name 48 | li "Destroying group: #{name}" 49 | Rudy::AWS::EC2::Groups.destroy name 50 | end 51 | 52 | def get(name=nil) 53 | name ||= current_group_name 54 | Rudy::AWS::EC2::Groups.get name 55 | end 56 | 57 | def list 58 | Rudy::AWS::EC2::Groups.list 59 | end 60 | 61 | private 62 | def modify(action, ports=nil, addresses=nil, name=nil) 63 | name ||= current_group_name 64 | addresses ||= [Rudy::Utils::external_ip_address] 65 | 66 | if ports.nil? 67 | ports = current_machine_os.to_s == 'windows' ? [3389,22] : [22] 68 | else 69 | ports = [ports].flatten.compact 70 | end 71 | 72 | ports.each do |port| 73 | li "#{action} port #{port} access for: #{addresses.join(', ')}" 74 | Rudy::AWS::EC2::Groups.send(action, name, addresses, [[port, port]]) rescue nil 75 | end 76 | end 77 | 78 | end 79 | end; end; end -------------------------------------------------------------------------------- /lib/rudy/routines/handlers/host.rb: -------------------------------------------------------------------------------- 1 | 2 | module Rudy; module Routines; module Handlers; 3 | module Host 4 | include Rudy::Routines::Handlers::Base 5 | extend self 6 | 7 | ## NOTE: This handler doesn't use Rudy::Routines.add_handler 8 | 9 | def is_running?(rset) 10 | raise NoMachines if rset.boxes.empty? 11 | rset.boxes.each do |rbox| 12 | msg = "Waiting for #{rbox.nickname} to boot..." 13 | multi = rbox.stash.windows? ? 6 : 3 14 | interval, max = 1*multi, 80*multi 15 | Rudy::Utils.waiter(interval, max, Rudy::Huxtable.logger, msg, 0) { 16 | inst = rbox.stash.get_instance 17 | inst && inst.running? 18 | } 19 | end 20 | end 21 | 22 | # Add instance info to machine and save it. This is really important 23 | # for the initial startup so the metadata is updated right away. But 24 | # it's also important to call here because if a routine was executed 25 | # and an unexpected exception occurs before this update is executed 26 | # the machine metadata won't contain the DNS information. Calling it 27 | # here ensures that the metadata is always up-to-date. 28 | # 29 | # If a machine has an associated elastic IP address, it will also be 30 | # assigned in this step. 31 | # 32 | # Each Rye:Box instance has a Rudy::Machine instance in its stash so 33 | # rbox.stash.refresh! == machine.refresh! 34 | def update_dns(rset) 35 | raise NoMachines if rset.boxes.empty? 36 | rset.boxes.each do |rbox| 37 | mach = rbox.stash 38 | # Assign IP address only if we have one for that position 39 | if !mach.address.nil? && !mach.address.empty? 40 | begin 41 | # Make sure the address is associated to the current account 42 | if Rudy::AWS::EC2::Addresses.exists?(mach.address) 43 | li "Associating #{mach.address} to #{mach.instid}" 44 | Rudy::AWS::EC2::Addresses.associate(mach.address, mach.instid) 45 | else 46 | le "Unknown address: #{mach.address}" 47 | end 48 | rescue => ex 49 | le "Error associating address: #{ex.message}" 50 | ld ex.backtrace 51 | end 52 | end 53 | 54 | # Give EC2 some time to update their metadata 55 | msg = "Waiting for public DNS on #{rbox.nickname} ..." 56 | multi = rbox.stash.windows? ? 3 : 2 57 | interval, max = 2*multi, 60*multi 58 | Rudy::Utils.waiter(interval, max, STDOUT, msg, 0) { 59 | mach.refresh! 60 | if mach.address 61 | mach.dns_public.to_s =~ /#{mach.address.to_s.gsub('.', '-')}/ 62 | else 63 | !mach.dns_public.nil? && !mach.dns_public.empty? 64 | end 65 | } 66 | rbox.host = mach.dns_public 67 | end 68 | end 69 | 70 | def is_available?(rset, port=22) 71 | raise NoMachines if rset.boxes.empty? 72 | rset.boxes.each do |rbox| 73 | mach = rbox.stash 74 | # This updates the DNS. It's important this happens 75 | # before and after the address is updated otherwise 76 | # certain errors will causes it to not be updated. 77 | mach.refresh! 78 | msg = "Waiting for port #{port} on #{rbox.nickname} ..." 79 | port = 3389 if mach.windows? 80 | multi = mach.windows? ? 3 : 2 81 | interval, max = 1*multi, 30*multi 82 | Rudy::Utils.waiter(interval, max, STDOUT, msg, 0) { 83 | Rudy::Utils.service_available?(mach.dns_public, port) 84 | } 85 | end 86 | end 87 | 88 | def set_hostname(rset) 89 | raise NoMachines if rset.boxes.empty? 90 | 91 | # Set the hostname if specified in the machines config. 92 | # :rudy -> change to Rudy's machine name 93 | # :default -> leave the hostname as it is 94 | # Anything else other than nil -> change to that value 95 | # NOTE: This will set hostname every time a routine is 96 | # run so we may want to make this an explicit action. 97 | hntype = current_machine_hostname || :rudy 98 | return if hntype.to_s.to_sym == :default 99 | rset.batch do 100 | unless self.stash.os == :windows 101 | hn = hntype == :rudy ? self.stash.name : hntype 102 | if self.user.to_s == 'root' # ubuntu has a root user 103 | hostname hn 104 | else 105 | sudo do 106 | hostname hn 107 | end 108 | end 109 | end 110 | end 111 | end 112 | 113 | end 114 | end; end; end -------------------------------------------------------------------------------- /lib/rudy/routines/handlers/keypair.rb: -------------------------------------------------------------------------------- 1 | 2 | module Rudy; module Routines; module Handlers; 3 | module Keypair 4 | include Rudy::Routines::Handlers::Base 5 | extend self 6 | 7 | ##Rudy::Routines.add_handler :machines, self 8 | 9 | 10 | def raise_early_exceptions(name=nil) 11 | name ||= current_machine_root 12 | keyname = user_keypairname name 13 | kp_file = pkey name 14 | if registered? keyname 15 | # This means no keypair file can be found 16 | raise PrivateKeyNotFound, keyname if kp_file.nil? 17 | # This means we found a keypair in the config but we cannot find the private key file. 18 | raise PrivateKeyNotFound, kp_file if !File.exists?(kp_file) 19 | else 20 | raise PrivateKeyFileExists, kp_file if File.exists?(kp_file) 21 | end 22 | end 23 | 24 | def create(name=nil) 25 | name ||= current_machine_root 26 | keyname = user_keypairname name 27 | kp_file = pkey name 28 | 29 | if registered? name && !@@global.force 30 | raise PrivateKeyNotFound, kp_file if !File.exists?(kp_file) 31 | end 32 | 33 | if Rudy::AWS::EC2::Keypairs.exists? keyname 34 | if @@global.force 35 | li "Destroying existing keypair: #{keyname}" 36 | Rudy::AWS::EC2::Keypairs.destroy keyname 37 | else 38 | raise Rudy::AWS::EC2::KeypairAlreadyDefined, keyname 39 | end 40 | end 41 | 42 | if File.exists?(kp_file) 43 | if @@global.force 44 | delete_pkey name 45 | else 46 | raise PrivateKeyFileExists, kp_file 47 | end 48 | end 49 | 50 | li "Creating keypair: #{keyname}" 51 | kp = Rudy::AWS::EC2::Keypairs.create(keyname) 52 | li "Saving #{kp_file}" 53 | Rudy::Utils.write_to_file(kp_file, kp.private_key, 'w', 0600) 54 | 55 | kp 56 | end 57 | 58 | def unregister(name=nil) 59 | name ||= current_machine_root 60 | keyname = user_keypairname name 61 | raise "Keypair not registered: #{keyname}" unless registered?(name) 62 | Rudy::AWS::EC2::Keypairs.destroy keyname 63 | end 64 | 65 | def delete_pkey(name=nil) 66 | name ||= current_machine_root 67 | kp_file = pkey name 68 | raise PrivateKeyNotFound, kp_file unless pkey?(name) 69 | File.unlink kp_file 70 | end 71 | 72 | def exists?(name=nil) 73 | name ||= current_machine_root 74 | registered?(name) && pkey?(name) 75 | end 76 | 77 | def registered?(name=nil) 78 | name ||= current_machine_root 79 | keyname = user_keypairname name 80 | Rudy::AWS::EC2::Keypairs.exists?(keyname) 81 | end 82 | 83 | def pkey(name=nil) 84 | name ||= current_machine_root 85 | user_keypairpath name 86 | end 87 | 88 | def pkey?(name=nil) 89 | name ||= current_machine_root 90 | File.exists? pkey(name) 91 | end 92 | 93 | end 94 | end; end; end -------------------------------------------------------------------------------- /lib/rudy/routines/handlers/script.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Rudy; module Routines; module Handlers; 4 | module Script 5 | include Rudy::Routines::Handlers::Base 6 | extend self 7 | 8 | Rudy::Routines.add_handler :local, self 9 | Rudy::Routines.add_handler :remote, self 10 | 11 | Rudy::Routines.add_handler :xlocal, self 12 | Rudy::Routines.add_handler :xremote, self 13 | 14 | def raise_early_exceptions(type, batch, rset, lbox, argv=nil) 15 | 16 | end 17 | 18 | def execute(type, batch, rset, lbox, argv=nil) 19 | if type.to_s =~ /\Ax/ # (e.g. xremote, xlocal) 20 | # do nothing 21 | 22 | # It's important this stay a regex rather than a literal comparison 23 | elsif type.to_s =~ /local/ 24 | lbox.cd Dir.pwd 25 | batch = { lbox.user => batch } if batch.is_a?(Proc) 26 | execute_command(batch, lbox, argv) 27 | else 28 | batch = { rset.user => batch } if batch.is_a?(Proc) 29 | raise NoMachines if rset.boxes.empty? 30 | execute_command(batch, rset, argv) 31 | end 32 | end 33 | 34 | 35 | private 36 | 37 | # * +batch+ a single routine hash (startup, shutdown, etc...) 38 | # * +robj+ an instance of Rye::Set or Rye::Box 39 | # * +argv+ command line args 40 | def execute_command(batch, robj, argv=nil) 41 | 42 | original_user = robj.user 43 | original_dir = robj.current_working_directory 44 | 45 | batch.each_pair do |user, proc| 46 | 47 | # The error doesn't apply to the local Rye::Box instance 48 | if robj.is_a?(Rye::Set) && !File.exists?(user_keypairpath(user) || '') 49 | # TODO: This prints always even if an account is authorized with different keys. 50 | #le "Cannot find key for #{user}: #{user_keypairpath(user)}" 51 | end 52 | 53 | if user.to_s != robj.user 54 | begin 55 | ld "Switching user to: #{user} (was: #{robj.user})" 56 | ld "(key: #{user_keypairpath(user)})" 57 | 58 | robj.add_key user_keypairpath(user) 59 | robj.switch_user user 60 | 61 | rescue Net::SSH::AuthenticationFailed, Net::SSH::HostKeyMismatch => ex 62 | le "Error connecting: #{ex.message}".color(:red) 63 | le "Skipping user #{user}".color(:red) 64 | next 65 | end 66 | end 67 | 68 | ### EXECUTE THE COMMANDS BLOCK 69 | begin 70 | robj.batch(argv, &proc) 71 | rescue Rye::Err => ex 72 | # No need to bubble exception up when in parallel mode 73 | raise ex unless Rye::Set == robj 74 | ensure 75 | robj.enable_safe_mode # In case it was disabled 76 | robj.switch_user original_user # Return to the user it was provided with 77 | robj.cd # reset to home dir 78 | robj.cd original_dir # return to previous directory 79 | end 80 | 81 | end 82 | 83 | 84 | end 85 | end 86 | 87 | end;end;end -------------------------------------------------------------------------------- /lib/rudy/routines/handlers/spot_request.rb: -------------------------------------------------------------------------------- 1 | module Rudy; module Routines; module Handlers; 2 | module SpotRequest 3 | include Rudy::Routines::Handlers::Base 4 | extend self 5 | 6 | def needed? 7 | current_machine_pricing.is_a?(Hash) || current_machine_pricing.to_sym == :spot 8 | end 9 | 10 | def create 11 | opts = { 12 | :price => current_machine_pricing[:bid], 13 | :count => current_machine_positions.length, 14 | :size => current_machine_size, 15 | :os => current_machine_os, 16 | :ami => current_machine_image, 17 | :group => current_group_name, 18 | :keypair => root_keypairname 19 | } 20 | 21 | request = Rudy::AWS::EC2::SpotRequests.create(opts) 22 | raise NoMachines unless wait_for_fulfillment_of(request) 23 | Rudy::AWS::EC2::SpotRequests.list(request) 24 | rescue NoMachines 25 | Rudy::AWS::EC2::SpotRequests.cancel(request) 26 | raise SpotRequestCancelled 27 | end 28 | 29 | def wait_for_fulfillment_of(spot_requests) 30 | msg = "Waiting for #{spot_requests.length} spot requests to be fulfilled" 31 | Rudy::Utils.waiter(2, 180, Rudy::Huxtable.logger, msg, 2) { 32 | Rudy::AWS::EC2::SpotRequests.fulfilled?(spot_requests) 33 | } 34 | end 35 | 36 | end 37 | end; end; end -------------------------------------------------------------------------------- /lib/rudy/routines/handlers/user.rb: -------------------------------------------------------------------------------- 1 | 2 | module Rudy; module Routines; module Handlers; 3 | module User 4 | include Rudy::Routines::Handlers::Base 5 | extend self 6 | 7 | Rudy::Routines.add_handler :adduser, self 8 | Rudy::Routines.add_handler :authorize, self 9 | 10 | def raise_early_exceptions(type, user, rset, lbox, argv=nil) 11 | 12 | end 13 | 14 | def execute(type, user, rset, lbox, argv=nil) 15 | raise NoMachines if rset.boxes.empty? 16 | send(type, user, rset) 17 | end 18 | 19 | def adduser(user, robj) 20 | 21 | # On Solaris, the user's home directory needs to be specified 22 | # explicitly so we do it for linux too for fun. 23 | homedir = robj.guess_user_home(user.to_s) 24 | 25 | # When more than one machine is running, this will be an Array 26 | homedir = homedir.first if homedir.kind_of?(Array) 27 | 28 | args = [:m, :d, homedir, :s, '/bin/bash', user.to_s] 29 | 30 | # NOTE: We'll may to use platform specific code here. 31 | # Linux has adduser and useradd commands: 32 | # adduser can prompt for info which we don't want. 33 | # useradd does not prompt (on Debian/Ubuntu at least). 34 | # We need to specify bash b/c the default is /bin/sh 35 | 36 | if robj.user.to_s == 'root' 37 | robj.useradd args 38 | else 39 | robj.sudo do 40 | useradd args 41 | end 42 | end 43 | 44 | end 45 | 46 | def authorize(user, robj) 47 | robj.authorize_keys_remote(user.to_s) 48 | end 49 | 50 | 51 | end 52 | 53 | end; end; end -------------------------------------------------------------------------------- /lib/rudy/routines/passthrough.rb: -------------------------------------------------------------------------------- 1 | 2 | module Rudy; module Routines; 3 | class Passthrough < Rudy::Routines::Base 4 | 5 | Rudy::Routines.add_routine :startup, Rudy::Routines::Startup 6 | Rudy::Routines.add_routine :shutdown, Rudy::Routines::Shutdown 7 | Rudy::Routines.add_routine :reboot, Rudy::Routines::Reboot 8 | 9 | def init(*args) 10 | Rudy::Routines.rescue { 11 | @machines = Rudy::Machines.list || [] 12 | @@rset = Rudy::Routines::Handlers::RyeTools.create_set @machines 13 | } 14 | end 15 | 16 | def execute 17 | Rudy::Routines::Handlers::Depends.execute_all @before, @argv 18 | li " Executing routine: #{@name} ".att(:reverse), "" 19 | # Re-retreive the machine set to reflect dependency changes 20 | Rudy::Routines.rescue { 21 | @machines = Rudy::Machines.list || [] 22 | @@rset = Rudy::Routines::Handlers::RyeTools.create_set @machines 23 | } 24 | 25 | return @machines unless run? 26 | Rudy::Routines.runner(@routine, @@rset, @@lbox, @argv) 27 | Rudy::Routines::Handlers::Depends.execute_all @after, @argv 28 | @machines 29 | end 30 | 31 | # Called by generic_machine_runner 32 | def raise_early_exceptions 33 | raise Rudy::Error, "No routine name" unless @name 34 | raise NoRoutine, @name unless @routine 35 | ##raise MachineGroupNotDefined, current_machine_group unless known_machine_group? 36 | # Call raise_early_exceptions for each handler used in the routine 37 | @routine.each_pair do |action,definition| 38 | raise NoHandler, action unless Rudy::Routines.has_handler?(action) 39 | handler = Rudy::Routines.get_handler action 40 | handler.raise_early_exceptions(action, definition, @@rset, @@lbox, @argv) 41 | end 42 | end 43 | 44 | end 45 | 46 | end; end -------------------------------------------------------------------------------- /lib/rudy/routines/reboot.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Rudy; module Routines; 4 | class Reboot < Rudy::Routines::Base 5 | 6 | Rudy::Routines.add_routine :reboot, self 7 | 8 | @@allowed_actions = [:before, :disks, :adduser, :authorize, 9 | :before_local, :before_remote, 10 | :local, :remote, :after] 11 | 12 | def init(*args) 13 | @routine ||= {} 14 | Rudy::Routines.rescue { 15 | @machines = Rudy::Machines.list || [] 16 | @@rset = Rudy::Routines::Handlers::RyeTools.create_set @machines 17 | } 18 | end 19 | 20 | # Startup routines run in the following order: 21 | # * before_local (if present) 22 | # * before_remote (if present) 23 | # * Reboot instances 24 | # * Set hostname 25 | # * before dependencies 26 | # * all other actions 27 | # * after dependencies 28 | def execute 29 | 30 | if run? 31 | Rudy::Routines::Handlers::Depends.execute_all @before, @argv 32 | 33 | li " Executing routine: #{@name} ".att(:reverse), "" 34 | ld "[this is a generic routine]" if @routine.empty? 35 | 36 | # Re-retreive the machine set to reflect dependency changes 37 | Rudy::Routines.rescue { 38 | @machines = Rudy::Machines.list || [] 39 | @@rset = Rudy::Routines::Handlers::RyeTools.create_set @machines 40 | } 41 | 42 | Rudy::Routines.rescue { 43 | Rudy::Routines::Handlers::Group.authorize rescue nil 44 | } 45 | 46 | if @routine.has_key? :before_local 47 | handler = Rudy::Routines.get_handler :local 48 | Rudy::Routines.rescue { 49 | handler.execute(:local, @routine.delete(:before_local), nil, @@lbox, @argv) 50 | } 51 | end 52 | 53 | if @routine.has_key? :before_remote 54 | handler = Rudy::Routines.get_handler :remote 55 | Rudy::Routines.rescue { 56 | handler.execute(:remote, @routine.delete(:before_remote), @@rset, @@lbox, @argv) 57 | } 58 | end 59 | end 60 | 61 | Rudy::Routines.rescue { 62 | if Rudy::Routines::Handlers::Disks.mount? @routine 63 | fake = Hash[:umount => @routine.disks[:mount]] 64 | Rudy::Routines::Handlers::Disks.execute :umount, fake, @@rset, @@lbox, @argv 65 | end 66 | } 67 | 68 | li "Rebooting #{current_group_name}..." 69 | @machines.each { |m| m.restart } if run? 70 | 71 | 15.times { print '.'; Kernel.sleep 2 }; li $/ # Wait for 30 seconds 72 | 73 | Rudy::Routines.rescue { 74 | if !Rudy::Routines::Handlers::Host.is_running? @@rset 75 | a = @@rset.boxes.select { |box| !box.stash.instance_running? } 76 | raise GroupNotRunning, a 77 | end 78 | } 79 | 80 | # This is important b/c the machines will not 81 | # have DNS info until after they are running. 82 | Rudy::Routines.rescue { Rudy::Routines::Handlers::Host.update_dns @@rset } 83 | 84 | Rudy::Routines.rescue { 85 | if !Rudy::Routines::Handlers::Host.is_available? @@rset 86 | a = @@rset.boxes.select { |box| !box.stash.instance_available? } 87 | raise GroupNotAvailable, a 88 | end 89 | } 90 | Rudy::Routines.rescue { 91 | Rudy::Routines::Handlers::Host.set_hostname @@rset 92 | } 93 | 94 | if run? 95 | # This is the meat of the sandwich 96 | Rudy::Routines.runner @routine, @@rset, @@lbox, @argv 97 | 98 | Rudy::Routines.rescue { 99 | Rudy::Routines::Handlers::Depends.execute_all @after, @argv 100 | } 101 | end 102 | 103 | @machines 104 | end 105 | 106 | # Called by generic_machine_runner 107 | def raise_early_exceptions 108 | raise NoMachinesConfig unless @@config.machines 109 | # There's no keypair check here because Rudy::Machines will attempt 110 | # to create one. 111 | raise MachineGroupNotDefined, current_machine_group unless known_machine_group? 112 | 113 | # If this is a test run we don't care if the group is running 114 | if run? 115 | raise MachineGroupNotRunning, current_machine_group unless Rudy::Machines.running? 116 | end 117 | 118 | if @routine 119 | bad = @routine.keys - @@allowed_actions 120 | raise UnsupportedActions.new(@name, bad) unless bad.empty? 121 | end 122 | 123 | if @machines 124 | down = @@rset.boxes.select { |box| !box.stash.instance_running? } 125 | raise GroupNotAvailable, down unless down.empty? 126 | end 127 | 128 | end 129 | 130 | end 131 | 132 | end; end 133 | 134 | 135 | -------------------------------------------------------------------------------- /lib/rudy/routines/shutdown.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Rudy; module Routines; 4 | class Shutdown < Rudy::Routines::Base 5 | 6 | Rudy::Routines.add_routine :shutdown, self 7 | 8 | @@allowed_actions = [:before, :disks, :adduser, :authorize, 9 | :local, :remote, :after_local, :after] 10 | 11 | def init(*args) 12 | @routine ||= {} 13 | Rudy::Routines.rescue { 14 | @machines = Rudy::Machines.list || [] 15 | @@rset = Rudy::Routines::Handlers::RyeTools.create_set @machines 16 | } 17 | end 18 | 19 | # Startup routines run in the following order: 20 | # * before dependencies 21 | # * all other actions (except after_local) 22 | # * Shutdown instances 23 | # * after_local (if present) 24 | # * after dependencies 25 | def execute 26 | 27 | # We need to remove after_local so the runner doesn't see it 28 | after_local = @routine.delete(:after_local) 29 | 30 | if run? 31 | Rudy::Routines.rescue { 32 | Rudy::Routines::Handlers::Group.authorize rescue nil 33 | } 34 | 35 | Rudy::Routines::Handlers::Depends.execute_all @before, @argv 36 | 37 | li " Executing routine: #{@name} ".att(:reverse), "" 38 | ld "[this is a generic routine]" if @routine.empty? 39 | 40 | # Re-retreive the machine set to reflect dependency changes 41 | Rudy::Routines.rescue { 42 | @machines = Rudy::Machines.list || [] 43 | @@rset = Rudy::Routines::Handlers::RyeTools.create_set @machines 44 | } 45 | 46 | # This is the meat of the sandwich 47 | Rudy::Routines.runner(@routine, @@rset, @@lbox, @argv) 48 | 49 | @machines.each do |machine| 50 | Rudy::Routines.rescue { machine.destroy } 51 | end 52 | 53 | if after_local 54 | handler = Rudy::Routines.get_handler :local 55 | Rudy::Routines.rescue { 56 | handler.execute(:local, after_local, nil, @@lbox, @argv) 57 | } 58 | end 59 | 60 | Rudy::Routines::Handlers::Depends.execute_all @after, @argv 61 | end 62 | 63 | @machines 64 | end 65 | 66 | # Called by generic_machine_runner 67 | def raise_early_exceptions 68 | raise NoMachinesConfig unless @@config.machines 69 | 70 | # If this is a test run we don't care if the group is running 71 | if run? 72 | if @@global.position.nil? 73 | raise MachineGroupNotRunning, current_machine_group unless Rudy::Machines.running? 74 | else 75 | unless Rudy::Machines.running? @@global.position 76 | m = Rudy::Machine.new @@global.position 77 | raise MachineNotRunning, m.name 78 | end 79 | end 80 | end 81 | 82 | ## NOTE: This check is disabled for now. If the private key doesn't exist 83 | ## it prevents shutting down. 84 | # Check private key after machine group, otherwise we could get an error 85 | # about there being no key which doesn't make sense if the group isn't running. 86 | ##raise Rudy::PrivateKeyNotFound, root_keypairpath unless has_keypair?(current_machine_root) 87 | if @routine 88 | bad = @routine.keys - @@allowed_actions 89 | raise UnsupportedActions.new(@name, bad) unless bad.empty? 90 | end 91 | end 92 | 93 | end 94 | 95 | end; end 96 | 97 | -------------------------------------------------------------------------------- /lib/rudy/routines/startup.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Rudy; module Routines; 4 | class Startup < Rudy::Routines::Base 5 | 6 | Rudy::Routines.add_routine :startup, self 7 | 8 | @@allowed_actions = [:before, :before_local, :disks, :adduser, 9 | :authorize, :local, :remote, :after] 10 | 11 | def init(*args) 12 | @routine ||= {} 13 | end 14 | 15 | # Startup routines run in the following order: 16 | # * before dependencies 17 | # * before_local (if present) 18 | # * Startup instances 19 | # * Set hostname 20 | # * all other actions 21 | # * after dependencies 22 | def execute 23 | 24 | if run? 25 | Rudy::Routines::Handlers::Depends.execute_all @before, @argv 26 | 27 | li " Executing routine: #{@name} ".att(:reverse), "" 28 | ld "[this is a generic routine]" if @routine.empty? 29 | 30 | if @routine.has_key? :before_local 31 | handler = Rudy::Routines.get_handler :local 32 | Rudy::Routines.rescue { 33 | handler.execute(:local, @routine.delete(:before_local), nil, @@lbox, @argv) 34 | } 35 | end 36 | 37 | Rudy::Routines.rescue { 38 | unless Rudy::Routines::Handlers::Group.exists? 39 | Rudy::Routines::Handlers::Group.create 40 | end 41 | # Run this every startup incase the ip address has changed. 42 | # If there's an exception it's probably b/c the address is 43 | # already authorized for port 22. 44 | Rudy::Routines::Handlers::Group.authorize rescue nil 45 | } 46 | 47 | Rudy::Routines.rescue { 48 | unless Rudy::Routines::Handlers::Keypair.exists? 49 | Rudy::Routines::Handlers::Keypair.create 50 | end 51 | } 52 | 53 | end 54 | 55 | ## li Rudy::Routines.machine_separator(machine.name, machine.awsid) 56 | 57 | # If this is a testrun we won't create new instances 58 | # we'll just grab the list of machines in this group. 59 | # NOTE: Expect errors if there are no machines. 60 | Rudy::Routines.rescue { 61 | @machines = if !run? 62 | Rudy::Machines.list 63 | elsif Rudy::Routines::Handlers::SpotRequest.needed? 64 | request = Rudy::Routines::Handlers::SpotRequest.create 65 | Rudy::Machines.from_spot_request(request) 66 | else 67 | Rudy::Machines.create 68 | end 69 | 70 | @@rset = Rudy::Routines::Handlers::RyeTools.create_set @machines 71 | } 72 | 73 | 74 | Rudy::Routines.rescue { 75 | if !Rudy::Routines::Handlers::Host.is_running? @@rset 76 | a = @@rset.boxes.select { |box| !box.stash.instance_running? } 77 | raise GroupNotRunning, a 78 | end 79 | } 80 | 81 | # This is important b/c the machines will not 82 | # have DNS info until after they are running. 83 | # This will also assign elastic IP addresses. 84 | Rudy::Routines.rescue { Rudy::Routines::Handlers::Host.update_dns @@rset } 85 | 86 | Rudy::Routines.rescue { 87 | if !Rudy::Routines::Handlers::Host.is_available? @@rset 88 | a = @@rset.boxes.select { |box| !box.stash.instance_available? } 89 | raise GroupNotAvailable, a 90 | end 91 | } 92 | Rudy::Routines.rescue { 93 | Rudy::Routines::Handlers::Host.set_hostname @@rset 94 | } 95 | 96 | if run? 97 | # This is the meat of the sandwich 98 | Rudy::Routines.runner @routine, @@rset, @@lbox, @argv 99 | 100 | Rudy::Routines.rescue { 101 | Rudy::Routines::Handlers::Depends.execute_all @after, @argv 102 | } 103 | 104 | end 105 | 106 | @machines 107 | end 108 | 109 | # Called by generic_machine_runner 110 | def raise_early_exceptions 111 | raise NoMachinesConfig unless @@config.machines 112 | # There's no keypair check here because Rudy::Machines will create one 113 | raise MachineGroupNotDefined, current_machine_group unless known_machine_group? 114 | 115 | unless (1..MAX_INSTANCES).member?(current_machine_count) 116 | raise "Instance count must be more than 0, less than #{MAX_INSTANCES}" 117 | end 118 | 119 | # If this is a testrun, we don't create instances anyway so 120 | # it doesn't matter if there are already instances running. 121 | if run? && !@@global.force 122 | if @@global.position.nil? 123 | raise MachineGroupAlreadyRunning, current_machine_group if Rudy::Machines.running? 124 | #raise MachineGroupMetadataExists, current_machine_group if Rudy::Machines.exists? 125 | else 126 | if Rudy::Machines.running? @@global.position 127 | m = Rudy::Machine.new @@global.position 128 | raise MachineAlreadyRunning, m.name 129 | end 130 | end 131 | end 132 | 133 | if @routine 134 | bad = @routine.keys - @@allowed_actions 135 | raise UnsupportedActions.new(@name, bad) unless bad.empty? 136 | end 137 | end 138 | 139 | end 140 | 141 | end; end 142 | -------------------------------------------------------------------------------- /support/mailtest: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # A simple SMTP mailer. 4 | # You can use this to test mail configuration between machines. 5 | 6 | # Usage: mailtest hostname email 7 | 8 | require 'net/smtp' 9 | 10 | unless ARGV.size == 2 11 | puts "Usage: mailtest HOSTNAME EMAIL" 12 | exit 1 13 | end 14 | 15 | HOSTNAME = ARGV[0] 16 | EMAIL = ARGV[1] 17 | 18 | def send_email(from, from_alias, to, to_alias, subject, message) 19 | msg = < 21 | To: #{to_alias} <#{to}> 22 | Subject: #{subject} 23 | 24 | #{message} 25 | END_OF_MESSAGE 26 | 27 | Net::SMTP.start(HOSTNAME) do |smtp| 28 | smtp.send_message msg, from, to 29 | end 30 | end 31 | 32 | begin 33 | spice = sprintf("%.3f", rand) 34 | send_email(HOSTNAME, "Rudy", EMAIL, "Rudy's Friend", "Mail config (#{spice})", "You received this email via #{HOSTNAME}") 35 | rescue => ex 36 | puts "ERROR: #{ex.message}" 37 | exit 1 38 | end 39 | 40 | puts "Success!" 41 | -------------------------------------------------------------------------------- /support/randomize-root-password: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Randomizes the root password on first boot 5 | # 6 | # * When the file /root/firstboot is present, this script will 7 | # randomize the root password. 8 | # 9 | # who: delano@solutious.com 10 | # when: 2009-03-13 11 | # 12 | 13 | # NOTE: Works on Gentoo 14 | # TODO: Fix for debian 15 | 16 | if [ -f "/root/firstrun" ] ; then 17 | dd if=/dev/urandom count=50|md5sum|passwd --stdin root 18 | rm -f /root/firstrun 19 | else 20 | echo "* Firstrun *" && touch /root/firstrun 21 | fi 22 | 23 | # New Gentoo (to be tested) 24 | #if [ -f "/root/firstrun" ] ; then 25 | # pword=`dd if=/dev/urandom count=51 | sha1sum` 26 | # echo $pword | passwd --stdin root 27 | # rm -f /root/firstrun 28 | #else 29 | # echo "* Firstrun *" && touch /root/firstrun 30 | #fi 31 | 32 | # Fix for debian 33 | #if [ -f "/root/firstrun" ] ; then 34 | # pword=`dd if=/dev/urandom count=51 | sha1sum` 35 | # echo "root:$pword" | chpasswd 36 | # rm -f /root/firstrun 37 | #else 38 | # echo "* Firstrun *" && touch /root/firstrun 39 | #fi 40 | 41 | # Random root password for Gentoo: 42 | #dd if=/dev/urandom count=51 | sha1sum | passwd --stdin root 43 | 44 | # Random root password for Debian/Ubuntu: 45 | #echo "root:`dd if=/dev/urandom count=51 | sha1sum`" | chpasswd -------------------------------------------------------------------------------- /support/update-ec2-ami-tools: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Installs the latest EC2 AMI Tools 5 | # 6 | # * Downloads zip to /tmp 7 | # * Unzips and removes /usr/ec2 (if it exists) 8 | # * Moves tools to /usr/ec2 9 | # 10 | # who: delano@solutious.com 11 | # when: 2009-03-13 12 | # 13 | 14 | cd /tmp 15 | echo " + Updating ec2-ami-tools" 16 | wget http://s3.amazonaws.com/ec2-downloads/ec2-ami-tools.zip && \ 17 | unzip ec2-ami-tools.zip && rm ec2*.zip* && rm -rf /usr/ec2 && \ 18 | mv ec2-* ec2 && mv ec2 /usr/ && \ 19 | echo " + Updated ec2-ami-tools" 20 | 21 | -------------------------------------------------------------------------------- /tryouts/01_mixins/01_hash_tryouts.rb: -------------------------------------------------------------------------------- 1 | 2 | group "Mixins" 3 | library :rudy, File.expand_path(File.join(GYMNASIUM_HOME, '..', 'lib')) 4 | 5 | tryout "Hash" do 6 | setup do 7 | def one_lvl; {:empty=>1}; end 8 | def two_lvls; {:l1 => {:empty=>1}}; end 9 | def three_lvls; { :l1 => { :l2 => {:empty=>1, :empty=>1} } }; end 10 | def six_lvls; {:l1 => {:l2 => {:l3 => {:l4 => {:l5 => {}, :empty=>1}, :empty=>1}}}}; end 11 | end 12 | 13 | dream [1, 2, 3, 6] 14 | drill "should calculate deepest point" do 15 | [one_lvl.deepest_point, two_lvls.deepest_point, 16 | three_lvls.deepest_point, six_lvls.deepest_point] 17 | end 18 | end 19 | 20 | 21 | -------------------------------------------------------------------------------- /tryouts/10_require_time/10_rudy_tryouts.rb: -------------------------------------------------------------------------------- 1 | 2 | group "Require-time" 3 | library :rudy, File.expand_path(File.join(GYMNASIUM_HOME, '..', 'lib')) 4 | 5 | tryout "Rudy Initialization" do 6 | setup do 7 | end 8 | 9 | drill "version matches gemspec", Rudy::VERSION.to_s do 10 | require 'rubygems' unless defined?(Gem) 11 | eval( File.read(File.join(RUDY_HOME, 'rudy.gemspec'))) 12 | @spec.version.to_s 13 | end 14 | 15 | drill "has sysinfo", Rudy.sysinfo, :class, SysInfo 16 | drill "debug is disabled", Rudy.debug?, false 17 | drill "quiet is disabled", Rudy.quiet?, false 18 | drill "auto-yes is disabled", Rudy.auto?, false 19 | 20 | drill "debug can be enabled", true do 21 | #Rudy.enable_debug 22 | Rudy.debug? 23 | end 24 | 25 | drill "debug can be disabled", false do 26 | Rudy.disable_debug 27 | Rudy.debug? 28 | end 29 | 30 | end 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /tryouts/10_require_time/15_global_tryouts.rb: -------------------------------------------------------------------------------- 1 | rudy_lib_path = File.expand_path(File.join(GYMNASIUM_HOME, '..', 'lib')) 2 | 3 | group "Require-time" 4 | library :rudy, rudy_lib_path 5 | 6 | tryout "Initialization of Global" do 7 | global = Rudy::Huxtable.global 8 | drill "has global", global, :class, Rudy::Global 9 | drill "has default region", global.region, :'us-east-1' 10 | drill "has default zone", global.zone, :'us-east-1b' 11 | drill "has default environment", global.environment, :stage 12 | drill "has default role", global.role, :app 13 | drill "default position is nil", global.position, nil 14 | drill "default user is nil", global.user, nil 15 | end 16 | 17 | tryout "Global knows ENV" do 18 | 19 | dream 'ACCESS99' 20 | drill "reads AWS_ACCESS_KEY" do 21 | ENV['AWS_ACCESS_KEY'] = 'ACCESS99' and Rudy::Huxtable.reset_global 22 | Rudy::Huxtable.global.accesskey 23 | end 24 | 25 | dream 'SECRET33' 26 | drill "reads AWS_SECRET_KEY before AWS_SECRET_ACCESS_KEY" do 27 | ENV['AWS_SECRET_ACCESS_KEY'] = 'SACCESS7' 28 | ENV['AWS_SECRET_KEY'] = 'SECRET33' and Rudy::Huxtable.reset_global 29 | Rudy::Huxtable.global.secretkey 30 | end 31 | 32 | dream 'SACCESS7' 33 | dream :class, String 34 | drill "reads AWS_SECRET_ACCESS_KEY" do 35 | ENV['AWS_SECRET_ACCESS_KEY'] = 'SACCESS7' 36 | ENV['AWS_SECRET_KEY'] = nil or Rudy::Huxtable.reset_global 37 | Rudy::Huxtable.global.secretkey 38 | end 39 | 40 | dream File.expand_path('CERT22') 41 | drill "reads EC2_CERT" do 42 | ENV['EC2_CERT'] = 'CERT22' and Rudy::Huxtable.reset_global 43 | Rudy::Huxtable.global.cert 44 | end 45 | 46 | dream File.expand_path('PK100') 47 | drill "reads EC2_PRIVATE_KEY" do 48 | ENV['EC2_PRIVATE_KEY'] = 'PK100' and Rudy::Huxtable.reset_global 49 | Rudy::Huxtable.global.pkey 50 | end 51 | 52 | end 53 | 54 | tryout "Population of Global" do 55 | setup do 56 | Rudy::Huxtable.update_config 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /tryouts/12_config/10_load_config_tryouts.rb: -------------------------------------------------------------------------------- 1 | 2 | group "Config" 3 | library :rudy, File.expand_path(File.join(GYMNASIUM_HOME, '..', 'lib')) 4 | 5 | tryout "Loads config files" do 6 | 7 | drill "load configs when created", 1 do 8 | @@config = Rudy::Config.new Rudy::CONFIG_FILE 9 | @@config.paths.size 10 | end 11 | 12 | drill "has accounts", :class, Rudy::Config::Accounts do 13 | @@config.accounts 14 | end 15 | drill "has defaults", :class, Rudy::Config::Defaults do 16 | @@config.defaults 17 | end 18 | 19 | drill "loads additional configs", 2 do 20 | @@config.paths << File.join(RUDY_HOME, 'Rudyfile') 21 | @@config.refresh 22 | @@config.paths.size 23 | end 24 | 25 | drill "has machines", :class, Rudy::Config::Machines do 26 | @@config.machines 27 | end 28 | drill "has commands", :class, Rudy::Config::Commands do 29 | @@config.commands 30 | end 31 | drill "has routines", :class, Rudy::Config::Routines do 32 | @@config.routines 33 | end 34 | 35 | drill "autoloads known configs", true do 36 | conf = Rudy::Config.new 37 | conf.look_and_load # Needs to run before checking accounts, et al 38 | (!conf.paths.empty? && !conf.paths.nil?) 39 | end 40 | 41 | end 42 | 43 | 44 | -------------------------------------------------------------------------------- /tryouts/12_config/20_defaults_tryouts.rb: -------------------------------------------------------------------------------- 1 | 2 | group "Config" 3 | library :rudy, File.expand_path(File.join(GYMNASIUM_HOME, '..', 'lib')) 4 | 5 | tryout "Defaults" do 6 | setup do 7 | @@config = Rudy::Config.new 8 | @@config.look_and_load # looks for and loads config files 9 | end 10 | 11 | drill "has some defaults", ["color", "environment", "role", "zone"].sort do 12 | # Sorted so we can add new keys without breaking the test 13 | @@config.defaults.keys.collect { |v| v.to_s }.sort 14 | end 15 | 16 | end 17 | -------------------------------------------------------------------------------- /tryouts/12_config/30_accounts_tryouts.rb: -------------------------------------------------------------------------------- 1 | 2 | group "Config" 3 | library :rudy, File.expand_path(File.join(GYMNASIUM_HOME, '..', 'lib')) 4 | 5 | tryout "Accounts" do 6 | setup do 7 | @@config = Rudy::Config.new 8 | @@config.look_and_load # looks for and loads config files 9 | end 10 | 11 | dream ["accesskey", "accountnum", "cert", "name", "pkey", "secretkey"] 12 | drill "has aws account" do 13 | # Sorted so we can add new keys without breaking the test 14 | @@config.accounts.aws.keys.collect { |v| v.to_s }.sort 15 | end 16 | 17 | end 18 | -------------------------------------------------------------------------------- /tryouts/12_config/40_machines_tryouts.rb: -------------------------------------------------------------------------------- 1 | 2 | group "Config" 3 | library :rudy, File.expand_path(File.join(GYMNASIUM_HOME, '..', 'lib')) 4 | 5 | tryout "Machines" do 6 | ## drill "Setup vars", :dream => true do 7 | drill "Setup vars", true do 8 | @@config = Rudy::Config.new 9 | @@config.look_and_load # looks for and loads config files 10 | @@reg, @@zon = @@config.defaults.region, @@config.defaults.zone 11 | @@env, @@rol = @@config.defaults.environment, @@config.defaults.role 12 | true 13 | end 14 | 15 | dream :class, Rudy::Config::Machines 16 | dream :gibbler, "16073d994b669dc51a7109f5165364dce516e707" 17 | drill "has instance of Rudy::Config::Machines" do 18 | @@config.machines 19 | end 20 | 21 | drill "is not gibbled (yet)", false do 22 | @@config.machines.gibbled? 23 | end 24 | 25 | drill "has example AMIs by zone", ["ami-e348af8a", "ami-6ecde51a"] do 26 | [@@config.machines[:'us-east-1'].ami, @@config.machines[:'eu-west-1'].ami] 27 | end 28 | 29 | drill "can find us-east-1 AMI", 'ami-e348af8a' do 30 | @@config.machines.find(:"us-east-1", :ami) 31 | end 32 | drill "can find eu-west-1 AMI", 'ami-6ecde51a' do 33 | @@config.machines.find(:"eu-west-1", :ami) 34 | end 35 | drill "different default AMI for each zone", true do 36 | eu = @@config.machines.find(:"eu-west-1", :ami) 37 | us = @@config.machines.find(:"us-east-1", :ami) 38 | (eu != us && !eu.nil? && !us.nil?) 39 | end 40 | drill "conf hash and find are equal", true do 41 | conf = @@config.machines[@@env][@@rol] 42 | find = @@config.machines.find(@@env, @@rol) 43 | conf == find 44 | end 45 | drill "conf find and find_deferred are equal", true do 46 | find = @@config.machines.find(@@env, @@rol) 47 | find_def = @@config.machines.find_deferred(@@env, @@rol) 48 | find == find_def 49 | end 50 | 51 | 52 | end 53 | 54 | -------------------------------------------------------------------------------- /tryouts/12_config/50_commands_tryouts.rb: -------------------------------------------------------------------------------- 1 | 2 | rudy_lib_path = File.expand_path(File.join(GYMNASIUM_HOME, '..', 'lib')) 3 | 4 | group "Config" 5 | library :rudy, rudy_lib_path 6 | 7 | tryout "Commands" do 8 | setup do 9 | @@config = Rudy::Config.new 10 | @@config.look_and_load # looks for and loads config files 11 | end 12 | 13 | drill "is a well-formed hash", [:allow] do 14 | @@config.commands.to_hash.keys.uniq.sort 15 | end 16 | 17 | end 18 | -------------------------------------------------------------------------------- /tryouts/12_config/60_routines_tryouts.rb: -------------------------------------------------------------------------------- 1 | 2 | rudy_lib_path = File.expand_path(File.join(GYMNASIUM_HOME, '..', 'lib')) 3 | 4 | group "Config" 5 | library :rudy, rudy_lib_path 6 | 7 | tryout "Routines" do 8 | setup do 9 | @@config = Rudy::Config.new 10 | @@config.look_and_load # looks for and loads config files 11 | end 12 | 13 | xdrill "has aws account" do 14 | end 15 | 16 | end 17 | -------------------------------------------------------------------------------- /tryouts/15_huxtable/10_huxtable_tryouts.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | group "Rudy::Huxtable" 4 | library :rudy, 'lib' 5 | 6 | tryout "Is well kept" do 7 | set :oldlogger, Rudy::Huxtable.logger 8 | setup do 9 | #Rudy::Huxtable.update_config 10 | end 11 | clean do 12 | Rudy::Huxtable.update_logger oldlogger 13 | end 14 | 15 | drill "has config", Rudy::Huxtable.config, :class, Rudy::Config 16 | drill "has global", Rudy::Huxtable.global, :class, Rudy::Global 17 | drill "has logger", Rudy::Huxtable.logger, :class, StringIO 18 | drill "specify logger", :class, IO do 19 | Rudy::Huxtable.update_logger STDOUT 20 | Rudy::Huxtable.logger 21 | end 22 | 23 | # drill "knows where config lives", Rudy::Huxtable.config_dirname 24 | end 25 | 26 | tryout "Loads configuration" do 27 | 28 | end 29 | 30 | 31 | tryout "Knows the defaults" do 32 | setup do 33 | class ::Olivia # :: to define the class in the root context 34 | include Rudy::Huxtable 35 | end 36 | end 37 | 38 | drill "create olivia", 'Olivia' do 39 | @@olivia = Olivia.new 40 | @@olivia.class.to_s 41 | end 42 | 43 | drill "machine group", 'stage-app' do 44 | @@olivia.current_machine_group 45 | end 46 | 47 | end 48 | -------------------------------------------------------------------------------- /tryouts/15_huxtable/20_user_tryouts.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | group "Rudy::Huxtable" 4 | library :rudy, 'lib' 5 | 6 | tryout "User related methods" do 7 | set :user, Rudy::Utils.strand(4) 8 | set :keydir, '/tmp' 9 | set :global, Rudy::Huxtable.global 10 | set :config, Rudy::Huxtable.config 11 | setup do 12 | #Rudy.enable_debug 13 | Rudy::Huxtable.update_config # Read config files 14 | config.defaults[:keydir] = keydir 15 | Rudy::Huxtable.global.zone = 'us-east-1b' 16 | module Cousin 17 | extend Rudy::Huxtable 18 | end 19 | end 20 | 21 | drill "knows current user", :root do 22 | Cousin.current_machine_user 23 | end 24 | 25 | drill "keypair path for current user", "#{keydir}/key-us-east-1b-stage-app" do 26 | Cousin.user_keypairpath 27 | end 28 | drill "keypair path for arbitrary user", "#{keydir}/key-us-east-1b-stage-app-#{user}" do 29 | Cousin.user_keypairpath user 30 | end 31 | drill "keypair path for root user", "#{keydir}/key-us-east-1b-stage-app" do 32 | Cousin.user_keypairpath :root 33 | end 34 | 35 | drill "keypair name for current user", "key-us-east-1b-stage-app" do 36 | Cousin.user_keypairname 37 | end 38 | drill "keypair name for arbitrary user", "key-us-east-1b-stage-app-#{user}" do 39 | Cousin.user_keypairname user 40 | end 41 | drill "keypair name for root user", "key-us-east-1b-stage-app" do 42 | Cousin.user_keypairname :root 43 | end 44 | 45 | 46 | 47 | end -------------------------------------------------------------------------------- /tryouts/20_simpledb/10_domains_tryouts.rb: -------------------------------------------------------------------------------- 1 | 2 | library :rudy, 'lib' 3 | 4 | group "SimpleDB" 5 | 6 | tryouts "Domains" do 7 | set :test_domain, 'test_' << Rudy::Utils.strand 8 | 9 | setup do 10 | Rudy::Huxtable.update_config 11 | global = Rudy::Huxtable.global 12 | akey, skey, region = global.accesskey, global.secretkey, global.region 13 | @sdb = Rudy::AWS::SDB.new(akey, skey, region) 14 | end 15 | 16 | drill "create simpledb connection", :class, Rudy::AWS::SDB do 17 | @sdb 18 | end 19 | 20 | drill "create a domain (#{test_domain})", true do 21 | @sdb.create_domain test_domain 22 | end 23 | 24 | drill "list domains", :class, Array do 25 | stash :domains, @sdb.list_domains 26 | end 27 | 28 | drill "destroy a domain (#{test_domain})", true do 29 | @sdb.destroy_domain test_domain 30 | end 31 | 32 | end 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /tryouts/20_simpledb/20_objects_tryouts.rb: -------------------------------------------------------------------------------- 1 | 2 | library :rudy, 'lib' 3 | 4 | group "SimpleDB" 5 | 6 | tryouts "Objects" do 7 | 8 | set :test_domain, 'test_' << Rudy::Utils.strand 9 | set :produce, Hash[ 10 | 'orange' => (rand(100) * 10).to_s, 11 | 'celery' => (rand(100) * 100).to_s, 12 | 'grapes' => 'green' 13 | ] 14 | 15 | 16 | setup do 17 | Rudy::Huxtable.update_config 18 | global = Rudy::Huxtable.global 19 | akey, skey, region = global.accesskey, global.secretkey, global.region 20 | @sdb = Rudy::AWS::SDB.new(akey, skey, region) 21 | end 22 | 23 | drill "create test domain (#{test_domain})", true do 24 | @sdb.create_domain test_domain 25 | end 26 | 27 | drill "put object", true do 28 | stash :product1, produce 29 | @sdb.put(test_domain, 'produce1', produce, :replace) 30 | end 31 | 32 | drill "get object by name", [produce.keys.sort, produce.values.sort] do 33 | from_sdb = @sdb.get(test_domain, 'produce1') 34 | stash :product1, from_sdb 35 | [from_sdb.keys.sort, from_sdb.values.collect { |v| v.first }.sort ] 36 | end 37 | 38 | drill "select objects", :gt, 0 do 39 | stash[:query] = "select * from #{test_domain}" 40 | stash[:items] = @sdb.select stash[:query] 41 | stash[:items].is_a?(Hash) && stash[:items].keys.size 42 | end 43 | 44 | dream true 45 | drill "destroy objects by name" do 46 | items = @sdb.select "select * from #{test_domain}" 47 | items.keys.each { |name| @sdb.destroy test_domain, name } 48 | @sdb.select("select * from #{test_domain}").nil? 49 | end 50 | 51 | drill "destroy test domain (#{test_domain})", true do 52 | @sdb.destroy_domain test_domain 53 | end 54 | 55 | end 56 | 57 | -------------------------------------------------------------------------------- /tryouts/25_ec2/10_keypairs_tryouts.rb: -------------------------------------------------------------------------------- 1 | 2 | group "EC2" 3 | library :rudy, 'lib' 4 | 5 | 6 | tryouts "Keypairs" do 7 | set :global, Rudy::Huxtable.global 8 | set :keypair_name, 'key-' << Rudy::Utils.strand 9 | 10 | setup do 11 | Rudy::Huxtable.update_config 12 | #Rudy::Huxtable.global.region = :'eu-west-1' 13 | Rudy::AWS::EC2.connect global.accesskey, global.secretkey, global.region 14 | puts "NOTE: the test that deletes keypairs is disabled." 15 | end 16 | 17 | drill "no existing keypairs", false do 18 | Rudy::AWS::EC2::Keypairs.any? 19 | end 20 | 21 | dream [Rudy::AWS::EC2::Keypair, false] 22 | drill "create keypair" do 23 | k = Rudy::AWS::EC2::Keypairs.create keypair_name 24 | [k.class, k.private_key.nil?] 25 | end 26 | 27 | drill "get keypair", :class, Rudy::AWS::EC2::Keypair do 28 | Rudy::AWS::EC2::Keypairs.get keypair_name 29 | end 30 | 31 | drill "has fingerprint", :empty?, false do 32 | k = Rudy::AWS::EC2::Keypairs.get keypair_name 33 | k.fingerprint 34 | end 35 | 36 | drill "private key is not available later", nil do 37 | k = Rudy::AWS::EC2::Keypairs.get keypair_name 38 | k.private_key 39 | end 40 | 41 | dream :class, Array 42 | dream :empty?, false 43 | drill "list keypairs" do 44 | Rudy::AWS::EC2::Keypairs.list 45 | end 46 | 47 | xdrill "destroy keypairs", nil do 48 | keypairs = Rudy::AWS::EC2::Keypairs.list 49 | keypairs.each do |kp| 50 | Rudy::AWS::EC2::Keypairs.destroy kp.name 51 | end 52 | Rudy::AWS::EC2::Keypairs.list 53 | end 54 | 55 | end 56 | 57 | -------------------------------------------------------------------------------- /tryouts/25_ec2/20_groups_tryouts.rb: -------------------------------------------------------------------------------- 1 | group "EC2" 2 | library :rudy, 'lib' 3 | 4 | tryouts "Groups" do 5 | set :global, Rudy::Huxtable.global 6 | set :group_name, 'grp-' << Rudy::Utils.strand 7 | set :group_desc, 'desc-' << group_name 8 | setup do 9 | Rudy::Huxtable.update_config 10 | Rudy::AWS::EC2.connect global.accesskey, global.secretkey, global.region 11 | end 12 | 13 | dream :class, Rudy::AWS::EC2::Group 14 | dream :name, group_name 15 | dream :description, "Security Group #{group_name}" 16 | drill "create group with name" do 17 | Rudy::AWS::EC2::Groups.create group_name 18 | end 19 | 20 | dream :class, Rudy::AWS::EC2::Group 21 | dream :name, "#{group_name}2" 22 | dream :description, group_desc 23 | drill "create group with name and description" do 24 | Rudy::AWS::EC2::Groups.create "#{group_name}2", group_desc 25 | end 26 | 27 | drill "list as Array", :class, Array do 28 | Rudy::AWS::EC2::Groups.list 29 | end 30 | 31 | drill "list as Hash", :class, Hash do 32 | Rudy::AWS::EC2::Groups.list_as_hash 33 | end 34 | 35 | dream :size, 1 # will equal 2 if test fails 36 | drill "list returns Group objects" do 37 | list = Rudy::AWS::EC2::Groups.list 38 | list.collect! do |group| 39 | group.is_a?(Rudy::AWS::EC2::Group) 40 | end 41 | list.uniq 42 | end 43 | 44 | dream :class, Array 45 | dream :size, 1 46 | drill "destroy groups" do 47 | list = Rudy::AWS::EC2::Groups.list 48 | list.collect! do |group| 49 | next if group.name == "default" # can't delete this default group 50 | Rudy::AWS::EC2::Groups.destroy group.name 51 | end 52 | Rudy::AWS::EC2::Groups.list 53 | end 54 | end 55 | 56 | 57 | -------------------------------------------------------------------------------- /tryouts/25_ec2/21_groups_authorize_address_tryouts.rb: -------------------------------------------------------------------------------- 1 | group "EC2" 2 | library :rudy, 'lib' 3 | 4 | tryouts "Groups Authorize Address" do 5 | set :global, Rudy::Huxtable.global 6 | set :group_name, 'grp-' << Rudy::Utils.strand 7 | set :protocols, ['tcp', 'udp'] 8 | set :external_ip, Rudy::Utils::external_ip_address || '192.168.0.1/32' 9 | set :addresses, [external_ip] 10 | set :ports, [[3100,3150],[3200,3250]] 11 | setup do 12 | Rudy::Huxtable.update_config 13 | Rudy::AWS::EC2.connect global.accesskey, global.secretkey, global.region 14 | Rudy::AWS::EC2::Groups.create group_name 15 | end 16 | clean do 17 | Rudy::AWS::EC2::Groups.destroy group_name 18 | end 19 | 20 | drill "authorize group rules returns true", true do 21 | Rudy::AWS::EC2::Groups.authorize group_name, addresses, ports, protocols 22 | end 23 | 24 | dream :proc, lambda { |group| 25 | group.addresses.each_pair do |address,rules| 26 | return false unless rules.is_a? Array 27 | return false unless rules.size == 7 28 | end 29 | true 30 | } 31 | drill "group (#{group_name}) contains new rules" do 32 | stash :group, Rudy::AWS::EC2::Groups.get(group_name) 33 | end 34 | 35 | drill "revoke group rules returns true", true do 36 | Rudy::AWS::EC2::Groups.revoke(group_name, addresses, ports, protocols) 37 | end 38 | 39 | dream :proc, lambda { |group| 40 | group.addresses.each_pair do |address,rules| 41 | return false unless rules.is_a? Array 42 | return false unless rules.size == 3 43 | end 44 | true 45 | } 46 | drill "group does not contain new rules" do 47 | group = Rudy::AWS::EC2::Groups.get(group_name) 48 | stash :group, group 49 | group 50 | end 51 | 52 | 53 | end -------------------------------------------------------------------------------- /tryouts/25_ec2/22_groups_authorize_account_tryouts.rb: -------------------------------------------------------------------------------- 1 | group "EC2" 2 | library :rudy, 'lib' 3 | 4 | tryouts "Groups Authorize/Revoke Account" do 5 | set :global, Rudy::Huxtable.global 6 | set :group_name, 'grp-' << Rudy::Utils.strand 7 | setup do 8 | Rudy::Huxtable.update_config 9 | Rudy::AWS::EC2.connect global.accesskey, global.secretkey, global.region 10 | Rudy::AWS::EC2::Groups.create group_name 11 | end 12 | clean do 13 | Rudy::AWS::EC2::Groups.destroy group_name 14 | end 15 | 16 | dream :class, String 17 | dream :empty?, false 18 | drill "has account num" do 19 | Rudy::Huxtable.config.accounts.aws.accountnum 20 | end 21 | 22 | drill "authorize group rules returns true", true do 23 | accountnum = Rudy::Huxtable.config.accounts.aws.accountnum 24 | Rudy::AWS::EC2::Groups.authorize_group group_name, group_name, accountnum 25 | end 26 | 27 | dream :class, Rudy::AWS::EC2::Group 28 | dream :proc, lambda { |group| 29 | accountnum = Rudy::Huxtable.config.accounts.aws.accountnum 30 | should_have = "#{accountnum}:#{group_name}" 31 | return false unless group.groups.is_a?(Hash) 32 | group.groups.has_key?(should_have) == true 33 | } 34 | drill "group (#{group_name}) contains new rules" do 35 | stash :group, Rudy::AWS::EC2::Groups.get(group_name) 36 | end 37 | 38 | drill "revoke group rules returns true", true do 39 | accountnum = Rudy::Huxtable.config.accounts.aws.accountnum 40 | Rudy::AWS::EC2::Groups.revoke_group group_name, group_name, accountnum 41 | end 42 | 43 | dream :proc, lambda { |group| 44 | accountnum = Rudy::Huxtable.config.accounts.aws.accountnum 45 | should_have = "#{accountnum}:#{group_name}" 46 | return false unless group.groups.is_a?(Hash) 47 | group.groups.has_key?(should_have) == false 48 | } 49 | drill "group (#{group_name}) does not contain new rules" do 50 | stash :group, Rudy::AWS::EC2::Groups.get(group_name) 51 | end 52 | 53 | 54 | end -------------------------------------------------------------------------------- /tryouts/25_ec2/30_addresses_tryouts.rb: -------------------------------------------------------------------------------- 1 | group "EC2" 2 | library :rudy, 'lib' 3 | 4 | tryouts "Addresses" do 5 | set :global, Rudy::Huxtable.global 6 | set :group_name, 'grp-' << Rudy::Utils.strand 7 | set :group_desc, 'desc-' << group_name 8 | setup do 9 | Rudy::Huxtable.update_config 10 | Rudy::AWS::EC2.connect global.accesskey, global.secretkey, global.region 11 | end 12 | 13 | drill "should not be existing addresses", false do 14 | Rudy::AWS::EC2::Addresses.any? 15 | end 16 | 17 | dream :class, Rudy::AWS::EC2::Address 18 | dream :proc, lambda { |a| a.ipaddress.size > 0 } 19 | drill "create address" do 20 | Rudy::AWS::EC2::Addresses.create 21 | end 22 | 23 | dream :class, Array 24 | dream :empty?, false 25 | drill "list addresses" do 26 | stash :address, Rudy::AWS::EC2::Addresses.list 27 | end 28 | 29 | dream :class, Hash 30 | dream :empty?, false 31 | drill "list addresses as Hash" do 32 | Rudy::AWS::EC2::Addresses.list_as_hash 33 | end 34 | 35 | drill "destroy all addresses", true do 36 | Rudy::AWS::EC2::Addresses.list.each do |a| 37 | Rudy::AWS::EC2::Addresses.destroy a 38 | end 39 | Rudy::AWS::EC2::Addresses.list.nil? 40 | end 41 | 42 | end -------------------------------------------------------------------------------- /tryouts/25_ec2/40_volumes_tryouts.rb: -------------------------------------------------------------------------------- 1 | group "EC2" 2 | library :rudy, 'lib' 3 | 4 | tryouts "Volumes" do 5 | set :global, Rudy::Huxtable.global 6 | setup do 7 | Rudy::Huxtable.update_config 8 | Rudy::AWS::EC2.connect global.accesskey, global.secretkey, global.region 9 | end 10 | 11 | drill "no existing volumes", false do 12 | Rudy::AWS::EC2::Volumes.any? :available 13 | end 14 | 15 | dream :class, Rudy::AWS::EC2::Volume 16 | dream :size, 2 17 | dream(:zone) { Rudy::Huxtable.global.zone } 18 | dream :proc, lambda { |v| !v.awsid.nil? } 19 | drill "create a 2GB volume" do 20 | Rudy::AWS::EC2::Volumes.create 2, Rudy::Huxtable.global.zone 21 | end 22 | 23 | dream :class, Array 24 | dream :empty?, false 25 | drill "list available volumes" do 26 | Rudy::AWS::EC2::Volumes.list :available 27 | end 28 | 29 | dream :class, Hash 30 | dream :empty?, false 31 | drill "list available volumes as hash" do 32 | Rudy::AWS::EC2::Volumes.list_as_hash :available 33 | end 34 | 35 | dream :class, Rudy::AWS::EC2::Volume 36 | dream :size, 2 37 | dream :available?, true 38 | drill "get a specific volume" do 39 | volid = Rudy::AWS::EC2::Volumes.list(:available).first.awsid 40 | Rudy::AWS::EC2::Volumes.get volid 41 | end 42 | 43 | drill "destroy all volumes", false do 44 | volumes = Rudy::AWS::EC2::Volumes.list 45 | volumes.each do |vol| 46 | next unless vol.available? 47 | Rudy::AWS::EC2::Volumes.destroy(vol.awsid) 48 | end 49 | Rudy::AWS::EC2::Volumes.any? :available 50 | end 51 | 52 | 53 | end 54 | -------------------------------------------------------------------------------- /tryouts/25_ec2/50_snapshots_tryouts.rb: -------------------------------------------------------------------------------- 1 | group "EC2" 2 | library :rudy, 'lib' 3 | 4 | tryouts "Snapshots" do 5 | set :global, Rudy::Huxtable.global 6 | setup do 7 | Rudy::Huxtable.update_config 8 | Rudy::AWS::EC2.connect global.accesskey, global.secretkey, global.region 9 | Rudy::AWS::EC2::Volumes.create 3, global.zone 10 | end 11 | clean do 12 | Rudy::AWS::EC2::Volumes.list(:available) do |v| 13 | Rudy::AWS::EC2::Volumes.destroy v 14 | end 15 | end 16 | 17 | drill "no existing snapshots", false do 18 | Rudy::AWS::EC2::Snapshots.any? 19 | end 20 | 21 | dream :class, Rudy::AWS::EC2::Snapshot 22 | drill "create snapshot" do 23 | vol = Rudy::AWS::EC2::Volumes.list(:available).first 24 | Rudy::AWS::EC2::Snapshots.create vol.awsid 25 | end 26 | 27 | dream :class, Array 28 | dream :empty?, false 29 | drill "list snapshots as Array" do 30 | Rudy::AWS::EC2::Snapshots.list 31 | end 32 | 33 | dream :class, Hash 34 | dream :empty?, false 35 | drill "list snapshots as Hash" do 36 | Rudy::AWS::EC2::Snapshots.list_as_hash 37 | end 38 | 39 | dream :class, Rudy::AWS::EC2::Snapshot 40 | drill "get snapshot from id" do 41 | snap = Rudy::AWS::EC2::Snapshots.list.first 42 | Rudy::AWS::EC2::Snapshots.get snap.awsid 43 | end 44 | 45 | dream :class, Rudy::AWS::EC2::Volume 46 | dream :size, 3 47 | dream :proc, lambda { |v| v.creating? || v.available? } 48 | dream :proc, lambda { |v| 49 | snap = Rudy::AWS::EC2::Snapshots.list.first 50 | v.snapid == snap.awsid 51 | } 52 | drill "create volume from snapshot" do 53 | snap = Rudy::AWS::EC2::Snapshots.list.first 54 | Rudy::AWS::EC2::Volumes.create 3, global.zone, snap.awsid 55 | end 56 | 57 | drill "destroy snapshots", false do 58 | Rudy::AWS::EC2::Snapshots.list.each do |snap| 59 | Rudy::AWS::EC2::Snapshots.destroy snap.awsid 60 | end 61 | Rudy::AWS::EC2::Snapshots.any? 62 | end 63 | end 64 | 65 | __END__ 66 | 67 | 68 | should "(90) destroy snapshots" do 69 | assert @ec2snap.any?, "No snapshots" 70 | snap_list = @ec2snap.list 71 | snap_list.each do |snap| 72 | next unless snap.completed? 73 | assert @ec2snap.destroy(snap.awsid), "Not destroyed (#{snap.awsid})" 74 | end 75 | end -------------------------------------------------------------------------------- /tryouts/26_ec2_instances/10_instance_tryouts.rb: -------------------------------------------------------------------------------- 1 | group "EC2 Instances" 2 | library :rudy, 'lib' 3 | 4 | tryouts "Instances" do 5 | set :global, Rudy::Huxtable.global 6 | set :config, Rudy::Huxtable.config 7 | setup do 8 | Rudy::Huxtable.update_config 9 | Rudy::AWS::EC2.connect global.accesskey, global.secretkey, global.region 10 | @ami = config.machines.find(global.region, :ami) 11 | end 12 | clean do 13 | Rudy::AWS::EC2::Addresses.list.each do |add| 14 | Rudy::AWS::EC2::Addresses.destroy add.ipaddress 15 | end 16 | end 17 | 18 | dream :class, String 19 | dream :empty?, false 20 | drill("has ami") { @ami } 21 | 22 | drill "no machines running", false do 23 | Rudy::AWS::EC2::Instances.any? :running 24 | end 25 | 26 | dream :class, Rudy::AWS::EC2::Instance 27 | dream :running?, true 28 | drill "create instance" do 29 | list = Rudy::AWS::EC2::Instances.create :ami => @ami, :group => "default" 30 | list.each do |instance| 31 | Rudy::Utils.waiter { 32 | Rudy::AWS::EC2::Instances.running?(instance) 33 | } 34 | end 35 | Rudy::AWS::EC2::Instances.get list.first.awsid 36 | end 37 | 38 | dream :class, Array 39 | dream [:running, :pending, :shutting_down, :terminated, :degraded] 40 | drill "have known states" do 41 | Rudy::AWS::EC2::Instances::KNOWN_STATES 42 | end 43 | 44 | dream :class, Array 45 | dream :empty?, false 46 | drill "list instances as Array" do 47 | Rudy::AWS::EC2::Instances.list :running 48 | end 49 | 50 | dream :class, Hash 51 | dream :empty?, false 52 | drill "list instances as Hash" do 53 | Rudy::AWS::EC2::Instances.list_as_hash :running 54 | end 55 | 56 | # NOTE: This drill will probably fail b/c console output takes 57 | # a few minutes to become available. 58 | dream :class, String 59 | dream :empty?, false 60 | xdrill "have console output" do 61 | inst = Rudy::AWS::EC2::Instances.list(:running).first 62 | Rudy::AWS::EC2::Instances.console inst.awsid 63 | end 64 | 65 | dream true 66 | 67 | drill "assign IP address to instance", true do 68 | address = Rudy::AWS::EC2::Addresses.create 69 | instance = Rudy::AWS::EC2::Instances.list(:running).first 70 | Rudy::AWS::EC2::Addresses.associate(address, instance) 71 | instance = Rudy::AWS::EC2::Instances.get instance.awsid 72 | stash :address, address.ipaddress 73 | stash :instance_ip, instance.dns_public 74 | address.ipaddress == IPSocket.getaddress(instance.dns_public) 75 | end 76 | 77 | xdrill "can unassign an IP address (TODO)" do 78 | 79 | end 80 | 81 | # NOTE: Restart is generally disabled until it checks that it's 82 | # unavailable and then comes available (there's no restart status) 83 | xdrill "restart instance (TODO)", true do 84 | instance = Rudy::AWS::EC2::Instances.list(:running).first 85 | Rudy::AWS::EC2::Instances.restart instance 86 | end 87 | 88 | dream :class, Rudy::AWS::EC2::Instance 89 | dream :shutting_down?, true 90 | drill "destroy instance" do 91 | instance = Rudy::AWS::EC2::Instances.list(:running).first 92 | Rudy::AWS::EC2::Instances.destroy instance 93 | instance = Rudy::AWS::EC2::Instances.get instance.awsid 94 | end 95 | 96 | end 97 | 98 | __END__ 99 | 100 | should "(99) destroy instance" do 101 | assert @ec2inst.any?(:running), "No instances running" 102 | instances = @ec2inst.list(:running) 103 | return skip("No running instances") unless instances 104 | instances.each do |instance| 105 | assert @ec2inst.destroy(instance), "Did not destroy" 106 | end 107 | end -------------------------------------------------------------------------------- /tryouts/26_ec2_instances/50_images_tryouts.rb: -------------------------------------------------------------------------------- 1 | 2 | group "EC2 Instances" 3 | library :rudy, 'lib' 4 | 5 | tryouts "Images" do 6 | xdrill "TODO" 7 | end -------------------------------------------------------------------------------- /tryouts/30_metadata/10_include_tryouts.rb: -------------------------------------------------------------------------------- 1 | 2 | group "Metadata" 3 | library :rudy, 'lib' 4 | tryouts "include Rudy::Metadata" do 5 | set :test_domain, 'test_' #<< Rudy::Utils.strand 6 | 7 | setup do 8 | Rudy::Huxtable.global.offline = true 9 | Rudy::Huxtable.update_config # Read config files 10 | end 11 | clean do 12 | Rudy::Huxtable.global.offline = false 13 | end 14 | 15 | drill "has default domain", Rudy::DEFAULT_DOMAIN do 16 | Rudy::Metadata.domain 17 | end 18 | 19 | drill "can set domain", test_domain do 20 | Rudy::Metadata.domain = test_domain 21 | end 22 | 23 | drill "can open simpledb connection", true do 24 | global = Rudy::Huxtable.global 25 | akey, skey, region = global.accesskey, global.secretkey, global.region 26 | Rudy::Metadata.connect akey, skey, region 27 | end 28 | 29 | dream test_domain 30 | drill "can create test domain (automatically sets new internal domain)" do 31 | Rudy::Metadata.domain = Rudy::DEFAULT_DOMAIN 32 | Rudy::Metadata.create_domain test_domain 33 | end 34 | 35 | dream [:environment, :region, :role, :rtype, :zone] 36 | drill "can build a default criteria" do 37 | Rudy::Metadata.build_criteria(Rudy::Machines::RTYPE).keys.sort 38 | end 39 | 40 | dream true 41 | drill "can destroy domain (automatically returns to default)" do 42 | Rudy::Metadata.destroy_domain test_domain 43 | end 44 | 45 | end 46 | -------------------------------------------------------------------------------- /tryouts/30_metadata/13_object_tryouts.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | group "Metadata" 5 | library :rudy, 'lib' 6 | tryouts "Rudy::Metadata objects" do 7 | set :test_domain, 'test_' #<< Rudy::Utils.strand 8 | 9 | setup do 10 | Rudy::Huxtable.update_config 11 | global = Rudy::Huxtable.global 12 | akey, skey, region = global.accesskey, global.secretkey, global.region 13 | class Anything < Storable 14 | include Rudy::Metadata 15 | end 16 | end 17 | 18 | dream [:region, :zone, :environment, :role, :position] 19 | drill "sets up common fields on include" do 20 | Anything.field_names 21 | end 22 | 23 | end -------------------------------------------------------------------------------- /tryouts/30_metadata/50_disk_tryouts.rb: -------------------------------------------------------------------------------- 1 | 2 | group "Metadata" 3 | library :rudy, 'lib' 4 | 5 | tryout "Disk API" do 6 | 7 | set :test_domain, Rudy::DEFAULT_DOMAIN #'test_' << Rudy::Utils.strand(4) 8 | set :test_env, 'stage' #'env_' << Rudy::Utils.strand(4) 9 | 10 | setup do 11 | #Rudy.enable_debug 12 | Rudy::Huxtable.global.offline = true 13 | Rudy::Huxtable.update_config # Read config files 14 | global = Rudy::Huxtable.global 15 | global.environment = test_env 16 | akey, skey, region = global.accesskey, global.secretkey, global.region 17 | Rudy::Metadata.connect akey, skey, region 18 | Rudy::AWS::EC2.connect akey, skey, region 19 | end 20 | 21 | clean do 22 | if Rudy.debug? 23 | puts $/, "Rudy Debugging:" 24 | Rudy::Huxtable.logger.rewind 25 | puts Rudy::Huxtable.logger.read unless Rudy::Huxtable.logger.closed_read? 26 | end 27 | end 28 | 29 | xdrill "can create test domain", test_domain do 30 | Rudy::Metadata.create_domain test_domain 31 | end 32 | 33 | 34 | dream :class, Rudy::Disk 35 | dream :name do 36 | tmp = [Rudy::Huxtable.global.zone, Rudy::Huxtable.global.environment] 37 | tmp += [Rudy::Huxtable.global.role, '01'] 38 | # disk-us-east-1b-env_xxxxxx-app-01-rudy-disk 39 | ['disk', tmp].join(Rudy::DELIM) 40 | end 41 | drill "can create disk object for root path" do 42 | Rudy::Disk.new('/') 43 | end 44 | 45 | dream :name do 46 | tmp = [Rudy::Huxtable.global.zone, Rudy::Huxtable.global.environment] 47 | tmp += [Rudy::Huxtable.global.role, '01'] 48 | # disk-us-east-1b-env_xxxxxx-app-01-any-path 49 | ['disk', tmp, 'any', 'path'].join(Rudy::DELIM) 50 | end 51 | drill "can create disk object for an arbitrary path" do 52 | Rudy::Disk.new('/any/path') 53 | end 54 | 55 | dream :size, 1 56 | dream :device, '/dev/sdh' 57 | dream :path, '/' 58 | drill "has a default size and device" do 59 | Rudy::Disk.new('/') 60 | end 61 | 62 | dream :path, '/anything' 63 | dream :position, '09' 64 | drill "can specify a position" do 65 | Rudy::Disk.new '09', '/anything' 66 | end 67 | 68 | drill "save disk metadata", true do 69 | ret = Rudy::Disk.new('/any/path').save 70 | sleep 1 # eventual consistency 71 | ret 72 | end 73 | 74 | drill "knows when an object exists", true do 75 | Rudy::Disk.new('/any/path').exists? 76 | end 77 | 78 | drill "knows when an object doesn't exist", false do 79 | Rudy::Disk.new('/no/such/disk').exists? 80 | end 81 | 82 | dream :exception, Rudy::Metadata::DuplicateRecord 83 | drill "won't save over a disk with the same name" do 84 | Rudy::Disk.new('/any/path').save 85 | end 86 | 87 | drill "will save over a disk with the same name if forced", true do 88 | Rudy::Disk.new('/any/path').save(:replace) 89 | end 90 | 91 | dream :class, Rudy::Disk 92 | drill "get disk metadata" do 93 | Rudy::Disks.get '/any/path' 94 | end 95 | 96 | dream :class, Rudy::Disk 97 | dream :mounted, false 98 | drill "refresh disk metadata" do 99 | d = Rudy::Disk.new('/any/path') 100 | d.mounted = true 101 | d.refresh! 102 | d 103 | end 104 | 105 | dream true 106 | drill "destroy disk metadata" do 107 | d = Rudy::Disk.new('/any/path') 108 | d.destroy 109 | end 110 | 111 | xdrill "destroy a domain (#{test_domain})" do 112 | Rudy::Metadata.destroy_domain test_domain 113 | end 114 | 115 | end 116 | 117 | 118 | -------------------------------------------------------------------------------- /tryouts/30_metadata/51_disk_digest_tryouts.rb: -------------------------------------------------------------------------------- 1 | 2 | group "Metadata" 3 | library :rudy, 'lib' 4 | 5 | Gibbler.enable_debug if Tryouts.verbose > 3 6 | 7 | tryout "Disk API" do 8 | 9 | setup do 10 | #Rudy.enable_debug 11 | Rudy::Huxtable.update_config # Read config files 12 | global = Rudy::Huxtable.global 13 | akey, skey, region = global.accesskey, global.secretkey, global.region 14 | Rudy::Metadata.connect akey, skey, region 15 | Rudy::AWS::EC2.connect akey, skey, region 16 | end 17 | 18 | dream :class, Gibbler::Digest 19 | drill "has gibbler digest" do 20 | Rudy::Disk.new('/any/path').gibbler 21 | end 22 | 23 | 24 | end 25 | -------------------------------------------------------------------------------- /tryouts/30_metadata/53_disk_list_tryouts.rb: -------------------------------------------------------------------------------- 1 | 2 | group "Metadata" 3 | library :rudy, 'lib' 4 | 5 | Gibbler.enable_debug if Tryouts.verbose > 3 6 | 7 | tryout "List Disks" do 8 | 9 | setup do 10 | #Rudy.enable_debug 11 | Rudy::Huxtable.update_config # Read config files 12 | global = Rudy::Huxtable.global 13 | akey, skey, region = global.accesskey, global.secretkey, global.region 14 | Rudy::Metadata.connect akey, skey, region 15 | Rudy::AWS::EC2.connect akey, skey, region 16 | Rudy::Disk.new('/any/path1').save :replace 17 | Rudy::Disk.new('/any/path2').save :replace 18 | sleep 1 19 | end 20 | 21 | clean do 22 | Rudy::Disk.new('/any/path1').destroy 23 | Rudy::Disk.new('/any/path2').destroy 24 | end 25 | 26 | dream :class, Array 27 | dream :empty?, false 28 | drill "can list available disks" do 29 | Rudy::Disks.list 30 | end 31 | 32 | 33 | end 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /tryouts/30_metadata/56_disk_volume_tryouts.rb: -------------------------------------------------------------------------------- 1 | group "Metadata" 2 | library :rudy, 'lib' 3 | 4 | tryout "Disk Volume API" do 5 | 6 | set :test_domain, 'test_' #<< Rudy::Utils.strand(4) 7 | set :test_env, 'stage' << Rudy::Utils.strand(4) 8 | 9 | setup do 10 | #Rudy.enable_debug 11 | Rudy::Huxtable.global.offline = true 12 | Rudy::Huxtable.update_config # Read config files 13 | global = Rudy::Huxtable.global 14 | global.environment = test_env 15 | akey, skey, region = global.accesskey, global.secretkey, global.region 16 | Rudy::Metadata.connect akey, skey, region 17 | Rudy::AWS::EC2.connect akey, skey, region 18 | end 19 | 20 | clean do 21 | if Rudy.debug? 22 | puts $/, "Rudy Debugging:" 23 | Rudy::Huxtable.logger.rewind 24 | puts Rudy::Huxtable.logger.read unless Rudy::Huxtable.logger.closed_read? 25 | end 26 | end 27 | 28 | dream :volid, nil 29 | drill "disk volid is nil by default" do 30 | Rudy::Disk.new '/any/path' 31 | end 32 | 33 | dream :nil?, false 34 | dream :class, String 35 | drill "create disk instance with volume" do 36 | disk = Rudy::Disk.new '/sergeant/disk' 37 | disk.create 38 | disk.volid 39 | end 40 | 41 | dream :nil?, false 42 | drill "refresh disk" do 43 | disk = Rudy::Disk.new '/sergeant/disk' 44 | disk.refresh! 45 | disk.volid 46 | end 47 | 48 | xdrill "can attach volume to instance" 49 | xdrill "can mount volume" 50 | xdrill "can detach volume from instance" 51 | 52 | dream [true, false, false] 53 | drill "knows about the state of the volume" do 54 | disk = Rudy::Disk.new '/sergeant/disk' 55 | disk.refresh! 56 | [disk.volume_exists?, disk.volume_attached?, disk.volume_in_use?] 57 | end 58 | 59 | dream true 60 | drill "destroy disk with volume" do 61 | disk = Rudy::Disk.new '/sergeant/disk' 62 | disk.refresh! 63 | disk.destroy 64 | end 65 | 66 | 67 | 68 | end -------------------------------------------------------------------------------- /tryouts/30_metadata/60_backup_tryouts.rb: -------------------------------------------------------------------------------- 1 | 2 | group "Metadata" 3 | library :rudy, 'lib' 4 | 5 | tryout "Backup API" do 6 | 7 | set :sample_time, Time.now.utc 8 | set :test_domain, Rudy::DEFAULT_DOMAIN #'test_' << Rudy::Utils.strand(4) 9 | set :test_env, :stage #'env_' << Rudy::Utils.strand(4) 10 | 11 | setup do 12 | #Rudy.enable_debug 13 | Rudy::Huxtable.global.offline = true 14 | Rudy::Huxtable.update_config # Read config files 15 | global = Rudy::Huxtable.global 16 | global.environment = test_env 17 | akey, skey, region = global.accesskey, global.secretkey, global.region 18 | Rudy::Metadata.connect akey, skey, region 19 | Rudy::AWS::EC2.connect akey, skey, region 20 | end 21 | 22 | clean do 23 | if Rudy.debug? 24 | puts $/, "Rudy Debugging:" 25 | Rudy::Huxtable.logger.rewind 26 | puts Rudy::Huxtable.logger.read unless Rudy::Huxtable.logger.closed_read? 27 | end 28 | end 29 | 30 | xdrill "can create test domain", test_domain do 31 | Rudy::Metadata.create_domain test_domain 32 | end 33 | 34 | dream :class, Rudy::Backup 35 | drill "can create an instance" do 36 | Rudy::Backup.new 37 | end 38 | 39 | dream :class, Rudy::Backup 40 | dream :name do 41 | global = Rudy::Huxtable.global 42 | tmp = [global.zone, global.environment, global.role, '01'] 43 | tmp += ['20090201', '0000', '00'] 44 | ['back', tmp].join(Rudy::DELIM) 45 | end 46 | drill "can create backup object for root path" do 47 | Rudy::Backup.new(1, '/', :created => Time.parse('2009-02-01')) 48 | end 49 | 50 | dream :class, Rudy::Backup 51 | dream :name do 52 | global = Rudy::Huxtable.global 53 | tmp = [global.zone, global.environment, global.role, '01'] 54 | tmp += ['any', 'path', '20090201', '0000', '00'] 55 | ['back', tmp].join(Rudy::DELIM) 56 | end 57 | drill "can create backup object for an arbitrary path" do 58 | Rudy::Backup.new(1, '/any/path', :created => Time.parse('2009-02-01')) 59 | end 60 | 61 | dream :user, Rudy.sysinfo.user 62 | drill "has a default user" do 63 | Rudy::Backup.new(1, '/') 64 | end 65 | 66 | dream :class, Time 67 | drill "has a default created time" do 68 | Rudy::Backup.new(1, '/').created 69 | end 70 | 71 | drill "save metadata", true do 72 | ret = Rudy::Backup.new(1, '/any/path', :created => sample_time).save 73 | sleep 1 74 | ret 75 | end 76 | 77 | drill "knows when an object exists", true do 78 | Rudy::Backup.new(1, '/any/path', :created => sample_time).exists? 79 | end 80 | 81 | drill "knows when an object doesn't exist", false do 82 | Rudy::Backup.new(1, '/no/such/disk', :created => sample_time).exists? 83 | end 84 | 85 | dream :class, Rudy::Disk 86 | drill "creates associated disk object" do 87 | Rudy::Backup.new(1, '/any/path', :created => sample_time).disk 88 | end 89 | 90 | dream :exception, Rudy::Backups::NoDisk 91 | drill "raises exception when disk doesn't exist" do 92 | Rudy::Backup.new(1, '/no/such/disk').create 93 | end 94 | 95 | drill "destroy all backups", false do 96 | Rudy::Backups.list.each { |b| b.destroy } 97 | Rudy::Backups.any? 98 | end 99 | 100 | xdrill "destroy a domain (#{test_domain})", true do 101 | Rudy::Metadata.destroy_domain test_domain 102 | end 103 | end -------------------------------------------------------------------------------- /tryouts/30_metadata/63_backup_list_tryouts.rb: -------------------------------------------------------------------------------- 1 | 2 | group "Metadata" 3 | library :rudy, 'lib' 4 | 5 | Gibbler.enable_debug if Tryouts.verbose > 3 6 | 7 | tryout "List Backups" do 8 | 9 | set :sample_time, Time.now.utc 10 | 11 | setup do 12 | #Rudy.enable_debug 13 | Rudy::Huxtable.update_config # Read config files 14 | global = Rudy::Huxtable.global 15 | akey, skey, region = global.accesskey, global.secretkey, global.region 16 | Rudy::Metadata.connect akey, skey, region 17 | Rudy::AWS::EC2.connect akey, skey, region 18 | Rudy::Backup.new(1, '/any/path1', :created => sample_time).save :replace 19 | Rudy::Backup.new(2, '/any/path2', :created => sample_time).save :replace 20 | end 21 | 22 | clean do 23 | Rudy::Backup.new(1, '/any/path1', :created => sample_time).destroy 24 | Rudy::Backup.new(2, '/any/path2', :created => sample_time).destroy 25 | end 26 | 27 | dream :class, Array 28 | dream :empty?, false 29 | dream :size, 2 30 | drill "can list available backups" do 31 | Rudy::Backups.list 32 | end 33 | 34 | 35 | end 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /tryouts/30_metadata/64_backup_disk_tryouts.rb: -------------------------------------------------------------------------------- 1 | 2 | group "Metadata" 3 | library :rudy, 'lib' 4 | 5 | Gibbler.enable_debug if Tryouts.verbose > 3 6 | 7 | tryout "Disk Backups" do 8 | 9 | setup do 10 | #Rudy.enable_debug 11 | Rudy::Huxtable.update_config # Read config files 12 | global = Rudy::Huxtable.global 13 | akey, skey, region = global.accesskey, global.secretkey, global.region 14 | Rudy::Metadata.connect akey, skey, region 15 | Rudy::AWS::EC2.connect akey, skey, region 16 | Rudy::Disk.new( 1, '/any/path').save 17 | end 18 | 19 | clean do 20 | Rudy::Disk.new( 1, '/any/path').destroy 21 | if Rudy.debug? 22 | puts $/, "Rudy Debugging:" 23 | Rudy::Huxtable.logger.rewind 24 | puts Rudy::Huxtable.logger.read unless Rudy::Huxtable.logger.closed_read? 25 | end 26 | end 27 | 28 | drill "no previous backups", false do 29 | Rudy::Backups.any? 30 | end 31 | 32 | dream :class, Array 33 | dream :size, 10 34 | drill "create 10 backups" do 35 | 10.times do |i| 36 | seconds = i.to_s.rjust(2, '0') 37 | now = Time.parse("2009-01-01 00:00:#{seconds}") 38 | Rudy::Backup.new(1, '/any/path', :created => now).save 39 | end 40 | sleep 1 # eventual consistency 41 | Rudy::Backups.list 42 | end 43 | 44 | dream true 45 | drill "listed backups are in chronological order" do 46 | backups = Rudy::Backups.list 47 | stash :backups, backups 48 | prev = backups.shift 49 | success = false 50 | Rudy::Backups.list.each do |back| 51 | success = (prev.created <= back.created) 52 | break unless success 53 | end 54 | sleep 1 55 | success 56 | end 57 | 58 | drill "destroy all backups", false do 59 | Rudy::Backups.list.each { |b| b.destroy } 60 | sleep 1 61 | Rudy::Backups.any? 62 | end 63 | 64 | end 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /tryouts/30_metadata/66_backup_snapshot_tryouts.rb: -------------------------------------------------------------------------------- 1 | 2 | group "Metadata" 3 | library :rudy, 'lib' 4 | 5 | tryout "Backup Snapshot API" do 6 | 7 | set :sample_time, Time.now.utc 8 | set :test_domain, Rudy::DEFAULT_DOMAIN #'test_' << Rudy::Utils.strand(4) 9 | set :test_env, :stage #'env_' << Rudy::Utils.strand(4) 10 | 11 | setup do 12 | #Rudy.enable_debug 13 | Rudy::Huxtable.global.offline = true 14 | Rudy::Huxtable.update_config # Read config files 15 | global = Rudy::Huxtable.global 16 | global.environment = test_env 17 | akey, skey, region = global.accesskey, global.secretkey, global.region 18 | Rudy::Metadata.connect akey, skey, region 19 | Rudy::AWS::EC2.connect akey, skey, region 20 | end 21 | 22 | clean do 23 | if Rudy.debug? 24 | puts $/, "Rudy Debugging:" 25 | Rudy::Huxtable.logger.rewind 26 | puts Rudy::Huxtable.logger.read unless Rudy::Huxtable.logger.closed_read? 27 | end 28 | end 29 | 30 | dream :class, Rudy::Disk 31 | drill "Creates disk object with volume" do 32 | b = Rudy::Backup.new(1, '/any/path', :created => sample_time) 33 | b.disk.create 34 | end 35 | 36 | dream :class, String 37 | dream :empty?, false 38 | drill "refreshes associated disk object" do 39 | b = Rudy::Backup.new(1, '/any/path', :created => sample_time) 40 | b.disk.volid 41 | end 42 | 43 | dream :class, String 44 | dream :empty?, false 45 | drill "create backups with snapshot" do 46 | b = Rudy::Backup.new(1, '/any/path', :created => sample_time) 47 | b.create 48 | b.snapid 49 | end 50 | 51 | dream :any?, true 52 | drill "knows when there's at least one backup" do 53 | Rudy::Backup.new(1, '/any/path') 54 | end 55 | 56 | dream :any?, false 57 | drill "knows when there are no backups" do 58 | Rudy::Backup.new(1, '/no/such/path') 59 | end 60 | 61 | drill "destroy disk", true do 62 | back = Rudy::Backup.new(1, '/any/path', :created => sample_time) 63 | back.disk.destroy 64 | end 65 | 66 | xdrill "destroy backup", true do 67 | back = Rudy::Backup.new(1, '/any/path', :created => sample_time) 68 | back.destroy 69 | end 70 | 71 | drill "destroy all backups", false do 72 | Rudy::Backups.list.each { |b| b.destroy } 73 | Rudy::Backups.any? 74 | end 75 | 76 | end -------------------------------------------------------------------------------- /tryouts/30_metadata/70_machine_tryouts.rb: -------------------------------------------------------------------------------- 1 | 2 | library :rudy, 'lib' 3 | group "Metadata" 4 | 5 | tryout "Rudy::Machine API" do 6 | 7 | set :test_domain, Rudy::DEFAULT_DOMAIN #'test_' << Rudy::Utils.strand(4) 8 | set :test_env, 'stage' #'env_' << Rudy::Utils.strand(4) 9 | 10 | setup do 11 | #Rudy.enable_debug 12 | Rudy::Huxtable.global.offline = true 13 | Rudy::Huxtable.update_config # Read config files 14 | global = Rudy::Huxtable.global 15 | global.environment = test_env 16 | akey, skey, region = global.accesskey, global.secretkey, global.region 17 | Rudy::Metadata.connect akey, skey, region 18 | Rudy::AWS::EC2.connect akey, skey, region 19 | end 20 | 21 | clean do 22 | if Rudy.debug? 23 | puts $/, "Rudy Debugging:" 24 | Rudy::Huxtable.logger.rewind 25 | puts Rudy::Huxtable.logger.read unless Rudy::Huxtable.logger.closed_read? 26 | end 27 | end 28 | 29 | dream :class, Rudy::Machine 30 | dream :position, '02' 31 | drill "create new machine instance" do 32 | Rudy::Machine.new '02' 33 | end 34 | 35 | dream :class, Rudy::Machine 36 | dream :position, '04' 37 | drill "create new machine instance with integer position" do 38 | Rudy::Machine.new 4 39 | end 40 | 41 | drill "save machine metadata", true do 42 | Rudy::Machine.new.save 43 | end 44 | 45 | drill "knows when an object exists", true do 46 | sleep 1 # eventual consistency 47 | Rudy::Machine.new.exists? 48 | end 49 | 50 | drill "knows when an object doesn't exist", false do 51 | Rudy::Machine.new('99').exists? 52 | end 53 | 54 | dream :exception, Rudy::Metadata::DuplicateRecord 55 | drill "won't save over a machine with the same name" do 56 | ret = Rudy::Machine.new.save 57 | sleep 1 58 | ret 59 | end 60 | 61 | drill "will save over a disk with the same name if forced", true do 62 | Rudy::Machine.new.save :replace 63 | end 64 | 65 | dream :class, Rudy::Machine 66 | dream :size, 'm1.small' 67 | drill "refresh machine metadata" do 68 | m = Rudy::Machine.new 69 | m.save :replace 70 | m.size = :nothing 71 | sleep 1 72 | m.refresh! 73 | m 74 | end 75 | 76 | ##dream :class, Rudy::Machine 77 | ##dream :zone, 'zone9000' 78 | ##xdrill "correctly saves zone" do 79 | ## Rudy::Machine.new(9, :zone => 'zone9000').save 80 | ## Rudy::Machine.new(9, :zone => 'zone9000').refresh! 81 | ##end 82 | 83 | drill "destroy machine metadata", true do 84 | Rudy::Machine.new.destroy 85 | end 86 | 87 | 88 | end 89 | -------------------------------------------------------------------------------- /tryouts/30_metadata/73_machine_list_tryouts.rb: -------------------------------------------------------------------------------- 1 | 2 | group "Metadata" 3 | library :rudy, 'lib' 4 | 5 | Gibbler.enable_debug if Tryouts.verbose > 3 6 | 7 | tryout "List Machines" do 8 | 9 | setup do 10 | #Rudy.enable_debug 11 | Rudy::Huxtable.update_config # Read config files 12 | global = Rudy::Huxtable.global 13 | akey, skey, region = global.accesskey, global.secretkey, global.region 14 | Rudy::Metadata.connect akey, skey, region 15 | Rudy::AWS::EC2.connect akey, skey, region 16 | ('01'..'03').each { |i| Rudy::Machine.new(i).save } 17 | ('04'..'05').each { |i| Rudy::Machine.new(i, :environment => :test).save } 18 | sleep 1 # SimpleDB, eventual consistency 19 | end 20 | 21 | clean do 22 | ('01'..'03').each { |i| Rudy::Machine.new(i).destroy } 23 | ('04'..'05').each { |i| Rudy::Machine.new(i, :environment => :test).destroy } 24 | if Rudy.debug? 25 | puts $/, "Rudy Debugging:" 26 | Rudy::Huxtable.logger.rewind 27 | puts Rudy::Huxtable.logger.read unless Rudy::Huxtable.logger.closed_read? 28 | end 29 | end 30 | 31 | 32 | dream :class, Array 33 | dream :empty?, false 34 | dream :size, 3 35 | drill "list available disks in default environment" do 36 | ret = Rudy::Machines.list 37 | #puts ret.to_json 38 | ret 39 | end 40 | 41 | dream :size, 2 42 | drill "list available disks in 'test' environment" do 43 | ret = Rudy::Machines.list({:environment => :test}) 44 | #puts ret.to_json 45 | ret 46 | end 47 | 48 | dream :size, 5 49 | drill "list all available disks" do 50 | ret = Rudy::Machines.list({}, [:environment]) 51 | #puts ret.to_json 52 | ret 53 | end 54 | 55 | end 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /tryouts/30_metadata/76_machine_instance_tryouts.rb: -------------------------------------------------------------------------------- 1 | 2 | library :rudy, 'lib' 3 | group "Metadata" 4 | 5 | tryout "Rudy::Machine Instance API" do 6 | 7 | set :test_domain, Rudy::DEFAULT_DOMAIN #'test_' << Rudy::Utils.strand(4) 8 | set :test_env, 'stage' #'env_' << Rudy::Utils.strand(4) 9 | 10 | setup do 11 | #Rudy.enable_debug 12 | Rudy::Huxtable.global.offline = true 13 | Rudy::Huxtable.update_config # Read config files 14 | global = Rudy::Huxtable.global 15 | global.environment = test_env 16 | akey, skey, region = global.accesskey, global.secretkey, global.region 17 | Rudy::Metadata.connect akey, skey, region 18 | Rudy::AWS::EC2.connect akey, skey, region 19 | Rudy::Routines::Handlers::Keypair.create 20 | Rudy::Routines::Handlers::Group.create 21 | end 22 | 23 | clean do 24 | Rudy::Routines::Handlers::Keypair.unregister 25 | Rudy::Routines::Handlers::Keypair.delete_pkey 26 | Rudy::Routines::Handlers::Group.destroy 27 | if Rudy.debug? 28 | puts $/, "Rudy Debugging:" 29 | Rudy::Huxtable.logger.rewind 30 | puts Rudy::Huxtable.logger.read unless Rudy::Huxtable.logger.closed_read? 31 | end 32 | end 33 | 34 | dream :instid, nil 35 | drill "machine instid is nil by default" do 36 | Rudy::Machine.new '02' 37 | end 38 | 39 | dream :nil?, false 40 | dream :class, String 41 | drill "create machine with instance" do 42 | mach = Rudy::Machine.new '02' 43 | mach.create 44 | Rudy::Utils.waiter { 45 | mach.instance_running? 46 | } 47 | mach.instid 48 | end 49 | 50 | dream :nil?, false 51 | drill "refresh machine" do 52 | mach = Rudy::Machine.new '02' 53 | mach.refresh! 54 | mach.dns_public 55 | end 56 | 57 | dream [true, true] 58 | drill "knows about the state of the instance" do 59 | mach = Rudy::Machine.new '02' 60 | mach.refresh! 61 | [mach.instance_exists?, mach.instance_running?] 62 | end 63 | 64 | dream true 65 | drill "destroy machine with instance" do 66 | mach = Rudy::Machine.new '02' 67 | mach.refresh! 68 | ret = mach.destroy 69 | Rudy::Utils.waiter { 70 | !mach.instance_running? 71 | } 72 | ret 73 | end 74 | 75 | 76 | end -------------------------------------------------------------------------------- /tryouts/30_metadata/77_machines_tryouts.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | library :rudy, 'lib' 4 | group "Metadata" 5 | 6 | tryout "Rudy::Machines API" do 7 | 8 | set :test_domain, Rudy::DEFAULT_DOMAIN #'test_' << Rudy::Utils.strand(4) 9 | set :test_env, 'stage' #'env_' << Rudy::Utils.strand(4) 10 | 11 | setup do 12 | #Rudy.enable_debug 13 | Rudy::Huxtable.global.offline = true 14 | Rudy::Huxtable.update_config # Read config files 15 | global = Rudy::Huxtable.global 16 | global.environment = test_env 17 | akey, skey, region = global.accesskey, global.secretkey, global.region 18 | Rudy::Metadata.connect akey, skey, region 19 | Rudy::AWS::EC2.connect akey, skey, region 20 | Rudy::Machine.new('07').save 21 | Rudy::Machine.new('08').save 22 | Rudy::Machine.new('09').save 23 | end 24 | 25 | clean do 26 | Rudy::Machine.new('07').destroy 27 | Rudy::Machine.new('08').destroy 28 | Rudy::Machine.new('09').destroy 29 | if Rudy.debug? 30 | puts $/, "Rudy Debugging:" 31 | Rudy::Huxtable.logger.rewind 32 | puts Rudy::Huxtable.logger.read unless Rudy::Huxtable.logger.closed_read? 33 | end 34 | end 35 | 36 | dream :class, Rudy::Machine 37 | drill "get machine metadata" do 38 | Rudy::Machines.get '07' 39 | end 40 | 41 | drill "knows when the current group is not running", false do 42 | Rudy::Machines.running? 43 | end 44 | 45 | end -------------------------------------------------------------------------------- /tryouts/40_routines/10_keypair_handler_tryouts.rb: -------------------------------------------------------------------------------- 1 | group "Routines" 2 | library :rudy, 'lib' 3 | 4 | tryout "Keypair Handler" do 5 | Rudy::Huxtable.update_config # Read config files 6 | 7 | set :user, Rudy::Utils.strand(4) 8 | set :keydir, '/tmp' 9 | set :global, Rudy::Huxtable.global 10 | set :config, Rudy::Huxtable.config 11 | set :test_env, 'env_' << Rudy::Utils.strand 12 | setup do 13 | #Rudy.enable_debug 14 | akey, skey, region = global.accesskey, global.secretkey, global.region 15 | Rudy::Metadata.connect akey, skey, region 16 | Rudy::AWS::EC2.connect akey, skey, region 17 | global.environment = test_env 18 | config.defaults[:keydir] = keydir 19 | end 20 | 21 | drill "has new temporary ssh key directory", keydir do 22 | config.defaults[:keydir] 23 | end 24 | 25 | drill "knows when a keypair isn't registered", false do 26 | Rudy::Routines::Handlers::Keypair.registered? user 27 | end 28 | 29 | drill "knows when a private key file doesn't exist", false do 30 | Rudy::Routines::Handlers::Keypair.pkey? '/path/2/' << user 31 | end 32 | 33 | dream "#{keydir}/key-#{Rudy::Huxtable.global.zone}-#{test_env}-app-#{user}" 34 | drill "determine keypair path (#{user})" do 35 | ret = Rudy::Routines::Handlers::Keypair.pkey user 36 | end 37 | 38 | dream "#{keydir}/key-#{Rudy::Huxtable.global.zone}-#{test_env}-app" 39 | drill "determine root keypair path" do 40 | Rudy::Routines::Handlers::Keypair.pkey :root 41 | end 42 | 43 | dream :class, Rudy::AWS::EC2::Keypair 44 | drill "create a new keypair" do 45 | Rudy::Routines::Handlers::Keypair.create user 46 | end 47 | 48 | dream :class, Rudy::AWS::EC2::Keypair 49 | drill "create a new root keypair" do 50 | Rudy::Routines::Handlers::Keypair.create :root 51 | end 52 | 53 | end -------------------------------------------------------------------------------- /tryouts/40_routines/11_group_handler_tryouts.rb: -------------------------------------------------------------------------------- 1 | group "Routines" 2 | library :rudy, 'lib' 3 | 4 | tryout "Group Handler" do 5 | set :group, 'grp-' << Rudy::Utils.strand(4) 6 | set :global, Rudy::Huxtable.global 7 | set :config, Rudy::Huxtable.config 8 | set :test_env, 'env_' << Rudy::Utils.strand 9 | setup do 10 | #Rudy.enable_debug 11 | Rudy::Huxtable.update_config # Read config files 12 | akey, skey, region = global.accesskey, global.secretkey, global.region 13 | Rudy::Metadata.connect akey, skey, region 14 | Rudy::AWS::EC2.connect akey, skey, region 15 | global.environment = test_env 16 | end 17 | 18 | drill "knows when a group doesn't exist", false do 19 | Rudy::Routines::Handlers::Group.exists? group 20 | end 21 | 22 | dream :class, Rudy::AWS::EC2::Group 23 | drill "create a group (#{group})" do 24 | Rudy::Routines::Handlers::Group.create group 25 | end 26 | 27 | drill "knows when a group exists", true do 28 | Rudy::Routines::Handlers::Group.exists? group 29 | end 30 | 31 | dream true 32 | drill "destroy group" do 33 | Rudy::Routines::Handlers::Group.destroy group 34 | end 35 | 36 | end -------------------------------------------------------------------------------- /tryouts/80_cli/10_rudyec2_tryouts.rb: -------------------------------------------------------------------------------- 1 | 2 | group "rudy-ec2" 3 | command :rudy, File.expand_path(File.join(GYMNASIUM_HOME, '..', 'bin', 'rudy')) 4 | xtryouts "Zones" do 5 | dream :stdout do 6 | end 7 | drill "list zones", :zones 8 | end 9 | -------------------------------------------------------------------------------- /tryouts/80_cli/60_rudy_tryouts.rb: -------------------------------------------------------------------------------- 1 | 2 | RUDY_HOME = File.expand_path(File.join(File.dirname(__FILE__), '..', '..')) 3 | 4 | group "Rudy CLI" 5 | command :rudy, File.join(RUDY_HOME, 'bin', 'rudy') 6 | 7 | dreams File.join(GYMNASIUM_HOME, '80_cli', '60_rudy_dreams.rb') 8 | xtryout "rudy machines" do 9 | drill "no machines, no args", :machines 10 | ##drill "startup", :startup 11 | end 12 | 13 | xtryout "rudy myaddress" do 14 | drill 'no args', :myaddress 15 | drill 'internal only', :myaddress, :i 16 | drill 'external only', :myaddress, :e 17 | drill 'quiet', :q, :myaddress 18 | end 19 | 20 | 21 | ##dreams 'rudy myaddress' do 22 | ## dream 'no args' do 23 | ## output inline(%Q{ 24 | ## Internal: #{Rudy::Utils::internal_ip_address} 25 | ## External: #{Rudy::Utils::external_ip_address} 26 | ## }) 27 | ## end 28 | ## dream 'internal only', " Internal: #{Rudy::Utils::internal_ip_address}" 29 | ## dream 'external only', " External: #{Rudy::Utils::external_ip_address}" 30 | ## dream 'quiet', [Rudy::Utils::internal_ip_address, Rudy::Utils::external_ip_address] 31 | ##end 32 | ## 33 | ## 34 | ##dreams 'rudy machines' do 35 | ## dream 'no machines, no args' do 36 | ## output inline(%Q{ 37 | ##No machines running in stage-app 38 | ##Try: rudy machines --all 39 | ## }) 40 | ## end 41 | ##end -------------------------------------------------------------------------------- /tryouts/exploration/console.rb: -------------------------------------------------------------------------------- 1 | RUDY_HOME = File.join(File.dirname(__FILE__), '..') 2 | RUDY_LIB = File.join(RUDY_HOME, 'lib') 3 | $:.unshift RUDY_LIB # Put our local lib in first place 4 | 5 | require 'yaml' 6 | require 'date' 7 | 8 | require 'tryouts' 9 | require 'console' 10 | 11 | raise "Sorry Ruby 1.9 only!" unless RUBY_VERSION =~ /1.9/ 12 | 13 | before do 14 | @title = "RUDY v0.3" 15 | @now_utc = Time.now.utc.strftime("%Y-%m-%d %H:%M:%S") 16 | @props = { 17 | :zone => "us-east-1b", 18 | :environment => "stage", 19 | :role =>"app", 20 | :position => "01" 21 | } 22 | # PROMPT_COMMAND 23 | end 24 | 25 | after do 26 | #Console.clear 27 | end 28 | 29 | 30 | tryout :positioned do 31 | Console.print_at(@title, {:y => Cursor.y, :x => Cursor.x }) 32 | sleep 1 33 | Console.print_at(@now_utc, {:y => Cursor.y, :x => Console.width, :minus => true}) 34 | puts 35 | sleep 1 36 | Console.print_left(@title) 37 | sleep 1 38 | Console.print_right(@now_utc) 39 | puts 40 | sleep 1 41 | Console.print_spaced('1'*25, 2, 3, '4'*30, 5, 6) 42 | puts 43 | sleep 1 44 | Console.print_center(Window.bar(50)) 45 | 46 | end 47 | 48 | tryout :u_r_d_l do 49 | puts 50 | Cursor.up && print('.') 51 | sleep 1 52 | Cursor.right && print('.') 53 | sleep 1 54 | Cursor.left && Cursor.down && print('.') 55 | sleep 1 56 | Cursor.left(3) && print('.') 57 | end 58 | 59 | tryout :update_inplace do 60 | [(0..11).to_a, (90..110).to_a].flatten.each do |i| 61 | Console.print_at(i, {:y => Cursor.y, :x => 4 }) 62 | sleep 0.05 63 | end 64 | 65 | end 66 | 67 | 68 | tryout :danger! do 69 | win = Window.new(:width => 100, :height => 100) 70 | 71 | # DEBUGGING: There is a threading bug where the values of props and the 72 | # string to print are being shared. Make Console and class and give an instance 73 | # to each thread. However, that could fuck up shit like Cursor.position. 74 | 75 | 76 | win.static(:right, 0.2, {:y => 0}) do 77 | Time.now.utc.strftime("%Y-%m-%d %H:%M:%S").colour(:blue, :white, :underline) 78 | end 79 | win.static(:left, 0.2) do 80 | rand 81 | end 82 | 83 | win.join_threads 84 | 85 | puts $/, "Done!" 86 | 87 | end 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /tryouts/exploration/machine.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # Tryout - A basic use-case 4 | # 5 | 6 | $:.unshift File.join(File.dirname(__FILE__), '..', 'lib') 7 | %w{drydock caesars rye}.each { |dir| $: << File.join(File.dirname(__FILE__), '..', '..', dir, 'lib') } 8 | 9 | require 'rudy' 10 | 11 | 12 | machine1 = Rudy::Machine.new 13 | Rudy::Huxtable.change_environment(:prod) 14 | machine2 = Rudy::Machine.new 15 | 16 | puts machine1.name 17 | puts machine2.name 18 | puts machine2.s 19 | 20 | #rmach = Rudy::Machines.new 21 | #machine = rmach.create(:position => 1) 22 | #machine.upload('/some/file') 23 | 24 | -------------------------------------------------------------------------------- /tryouts/failer: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # For some reason when this echo is commented out 4 | # Net::SSH always reports the exit code as 0. 5 | echo "Failing with $1" 6 | exit $1 --------------------------------------------------------------------------------