├── .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{#