solutious-rudy 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.txt +75 -0
- data/LICENSE.txt +19 -0
- data/README.rdoc +36 -0
- data/Rakefile +68 -0
- data/bin/rudy +175 -0
- data/bin/rudy-ec2 +108 -0
- data/lib/aws_sdb.rb +3 -0
- data/lib/aws_sdb/error.rb +42 -0
- data/lib/aws_sdb/service.rb +215 -0
- data/lib/console.rb +385 -0
- data/lib/rudy.rb +210 -0
- data/lib/rudy/aws.rb +68 -0
- data/lib/rudy/aws/ec2.rb +304 -0
- data/lib/rudy/aws/s3.rb +3 -0
- data/lib/rudy/aws/simpledb.rb +53 -0
- data/lib/rudy/command/addresses.rb +46 -0
- data/lib/rudy/command/backups.rb +175 -0
- data/lib/rudy/command/base.rb +839 -0
- data/lib/rudy/command/config.rb +77 -0
- data/lib/rudy/command/deploy.rb +12 -0
- data/lib/rudy/command/disks.rb +213 -0
- data/lib/rudy/command/environment.rb +74 -0
- data/lib/rudy/command/groups.rb +61 -0
- data/lib/rudy/command/images.rb +99 -0
- data/lib/rudy/command/instances.rb +85 -0
- data/lib/rudy/command/machines.rb +170 -0
- data/lib/rudy/command/metadata.rb +41 -0
- data/lib/rudy/command/release.rb +174 -0
- data/lib/rudy/command/volumes.rb +66 -0
- data/lib/rudy/config.rb +93 -0
- data/lib/rudy/metadata.rb +26 -0
- data/lib/rudy/metadata/backup.rb +160 -0
- data/lib/rudy/metadata/disk.rb +138 -0
- data/lib/rudy/scm/svn.rb +68 -0
- data/lib/rudy/utils.rb +64 -0
- data/lib/storable.rb +280 -0
- data/lib/tryouts.rb +40 -0
- data/rudy.gemspec +76 -0
- data/support/mailtest +40 -0
- data/support/rudy-ec2-startup +200 -0
- data/tryouts/console_tryout.rb +91 -0
- metadata +188 -0
data/lib/rudy/aws/s3.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
|
4
|
+
module Rudy::AWS
|
5
|
+
|
6
|
+
|
7
|
+
class SimpleDB
|
8
|
+
class Domains
|
9
|
+
include Rudy::AWS::ObjectBase
|
10
|
+
|
11
|
+
def create(name)
|
12
|
+
@aws.create_domain(name)
|
13
|
+
end
|
14
|
+
|
15
|
+
def destroy(name)
|
16
|
+
@aws.delete_domain(name)
|
17
|
+
end
|
18
|
+
|
19
|
+
def list
|
20
|
+
@aws.list_domains
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def destroy(domain, item, attributes={})
|
25
|
+
@aws.delete_attributes(domain, item, attributes)
|
26
|
+
end
|
27
|
+
|
28
|
+
def store(domain, item, attributes={}, replace=false)
|
29
|
+
@aws.put_attributes(domain, item, attributes, replace)
|
30
|
+
end
|
31
|
+
|
32
|
+
def query(domain, query=nil, max=nil)
|
33
|
+
@aws.query(domain, query, max)
|
34
|
+
end
|
35
|
+
|
36
|
+
def query_with_attributes(domain, query, max=nil)
|
37
|
+
items = {}
|
38
|
+
query(domain, query)[:items].each do |item|
|
39
|
+
items[item] = get_attributes(domain, item)[:attributes]
|
40
|
+
end
|
41
|
+
items
|
42
|
+
end
|
43
|
+
|
44
|
+
def select(query)
|
45
|
+
list = @aws2.select(query) || []
|
46
|
+
list[0]
|
47
|
+
end
|
48
|
+
|
49
|
+
def get_attributes(domain, item, attribute=nil)
|
50
|
+
@aws.get_attributes(domain, item, attribute)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
|
4
|
+
module Rudy
|
5
|
+
module Command
|
6
|
+
class Addresses < Rudy::Command::Base
|
7
|
+
|
8
|
+
|
9
|
+
def associate_addresses_valid?
|
10
|
+
raise "You have not supplied an IP addresses" unless @argv.address
|
11
|
+
raise "You did not supply an instance ID" unless @argv.instanceid
|
12
|
+
|
13
|
+
@inst = @ec2.instances.get(@argv.instanceid)
|
14
|
+
raise "Instance #{@inst[:aws_instance_id]} does not exist!" unless @inst
|
15
|
+
|
16
|
+
raise "That's not an elastic IP you own!" unless @ec2.addresses.valid?(@argv.address)
|
17
|
+
raise "#{@argv.address} is already associated!" if @ec2.addresses.associated?(@argv.address)
|
18
|
+
|
19
|
+
true
|
20
|
+
end
|
21
|
+
|
22
|
+
def associate_addresses
|
23
|
+
puts "Associating #{@argv.address} to #{@inst[:aws_groups]}: #{@inst[:dns_name]}"
|
24
|
+
@ec2.addresses.associate(@inst[:aws_instance_id], @argv.address)
|
25
|
+
puts "Done!"
|
26
|
+
puts
|
27
|
+
|
28
|
+
addresses
|
29
|
+
end
|
30
|
+
|
31
|
+
def addresses
|
32
|
+
puts "Elastic IP mappings:"
|
33
|
+
@ec2.addresses.list.each do |address|
|
34
|
+
print "IP: #{address[:public_ip]} "
|
35
|
+
if address[:instance_id]
|
36
|
+
inst = @ec2.instances.get(address[:instance_id])
|
37
|
+
puts "%s: %s %s" % [inst[:aws_groups], inst[:aws_instance_id], inst[:dns_name]]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
puts
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
@@ -0,0 +1,175 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
|
4
|
+
|
5
|
+
module Rudy
|
6
|
+
module Command
|
7
|
+
class Backups < Rudy::Command::Base
|
8
|
+
|
9
|
+
|
10
|
+
def backup
|
11
|
+
criteria = [@global.zone]
|
12
|
+
criteria += [@global.environment, @global.role] unless @option.all
|
13
|
+
|
14
|
+
Rudy::MetaData::Backup.list(@sdb, *criteria).each do |backup|
|
15
|
+
puts "%s (%s)" % [backup.name, backup.awsid]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Check for backups pointing to snapshots that don't exist.
|
20
|
+
def sync_backup
|
21
|
+
unless argv.empty?
|
22
|
+
puts "The disk you specified will be ignored."
|
23
|
+
argv.clear
|
24
|
+
end
|
25
|
+
|
26
|
+
criteria = [@global.zone]
|
27
|
+
criteria += [@global.environment, @global.role] unless @option.all
|
28
|
+
|
29
|
+
puts "Looking for backup metadata with delinquent snapshots..."
|
30
|
+
to_be_deleted = {} # snap-id => backup
|
31
|
+
Rudy::MetaData::Backup.list(@sdb, *criteria).each do |backup|
|
32
|
+
to_be_deleted[backup.awsid] = backup unless @ec2.snapshots.exists?(backup.awsid)
|
33
|
+
end
|
34
|
+
|
35
|
+
if to_be_deleted.empty?
|
36
|
+
puts "All backups are in-sync with snapshots. Nothing to do."
|
37
|
+
return
|
38
|
+
end
|
39
|
+
|
40
|
+
puts
|
41
|
+
puts "These backup metadata will be deleted:"
|
42
|
+
to_be_deleted.each do |snap_id, backup|
|
43
|
+
puts "%s: %s" % [snap_id, backup.name]
|
44
|
+
end
|
45
|
+
|
46
|
+
puts
|
47
|
+
are_you_sure?
|
48
|
+
|
49
|
+
puts
|
50
|
+
puts "Deleting..."
|
51
|
+
to_be_deleted.each do |snap_id, backup|
|
52
|
+
print " -> #{backup.name}... "
|
53
|
+
@sdb.destroy(RUDY_DOMAIN, backup.name)
|
54
|
+
puts "done"
|
55
|
+
end
|
56
|
+
|
57
|
+
puts "Done!"
|
58
|
+
end
|
59
|
+
|
60
|
+
def destroy_backup_valid?
|
61
|
+
raise "No backup specified" if argv.empty?
|
62
|
+
exit unless are_you_sure?(5)
|
63
|
+
true
|
64
|
+
end
|
65
|
+
|
66
|
+
def destroy_backup
|
67
|
+
name = @argv.first
|
68
|
+
puts "Destroying #{name}"
|
69
|
+
begin
|
70
|
+
backup = Rudy::MetaData::Backup.get(@sdb, name)
|
71
|
+
rescue => ex
|
72
|
+
puts "Error deleteing backup: #{ex.message}"
|
73
|
+
end
|
74
|
+
|
75
|
+
return unless backup
|
76
|
+
|
77
|
+
begin
|
78
|
+
puts " -> deleting snapshot..."
|
79
|
+
@ec2.snapshots.destroy(backup.awsid)
|
80
|
+
rescue => ex
|
81
|
+
puts "Error deleting snapshot: #{ex.message}."
|
82
|
+
puts "Continuing..."
|
83
|
+
ensure
|
84
|
+
puts " -> deleting metadata..."
|
85
|
+
@sdb.destroy(RUDY_DOMAIN, name)
|
86
|
+
end
|
87
|
+
puts "Done."
|
88
|
+
end
|
89
|
+
|
90
|
+
def create_backup
|
91
|
+
diskname = @argv.first
|
92
|
+
|
93
|
+
machine = find_current_machine
|
94
|
+
|
95
|
+
disks = Rudy::MetaData::Disk.list(@sdb, machine[:aws_availability_zone], @global.environment, @global.role, @global.position)
|
96
|
+
raise "The machine #{machine_name} does not have any disk metadata" if disks.empty?
|
97
|
+
|
98
|
+
puts "Machine: #{machine_name}"
|
99
|
+
|
100
|
+
if @option.snapshot
|
101
|
+
raise "You must supply a diskname when using an existing snapshot" unless diskname
|
102
|
+
raise "The snapshot #{@option.snapshot} does not exist" unless @ec2.snapshots.exists?(@option.snapshot)
|
103
|
+
disk = Rudy::MetaData::Disk.get(@sdb, diskname)
|
104
|
+
|
105
|
+
raise "The disk #{diskname} does not exist" unless disk
|
106
|
+
backup = Rudy::MetaData::Backup.new
|
107
|
+
backup.awsid = @option.snapshot
|
108
|
+
backup.time_stamp
|
109
|
+
|
110
|
+
# Populate machine infos
|
111
|
+
[:zone, :environment, :role, :position].each do |n|
|
112
|
+
backup.send("#{n}=", @global.send(n)) if @global.send(n)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Populate disk infos
|
116
|
+
[:path, :size].each do |n|
|
117
|
+
backup.send("#{n}=", disk.send(n)) if disk.send(n)
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
Rudy::MetaData::Backup.save(@sdb, backup)
|
122
|
+
|
123
|
+
puts backup.name
|
124
|
+
|
125
|
+
else
|
126
|
+
volumes = @ec2.instances.volumes(machine[:aws_instance_id])
|
127
|
+
raise "The machine #{machine_name} does not have any volumes attached." if volumes.empty?
|
128
|
+
|
129
|
+
puts "#{disks.size} Disk(s) defined with #{volumes.size} Volume(s) running"
|
130
|
+
|
131
|
+
volumes.each do |volume|
|
132
|
+
print "Volume #{volume[:aws_id]}... "
|
133
|
+
disk = Rudy::MetaData::Disk.find_from_volume(@sdb, volume[:aws_id])
|
134
|
+
backup = Rudy::MetaData::Backup.new
|
135
|
+
|
136
|
+
# TODO: Look for the disk based on the machine
|
137
|
+
raise "No disk associated to volume #{volume[:aws_id]}" unless disk
|
138
|
+
|
139
|
+
backup.volume = volume[:aws_id]
|
140
|
+
|
141
|
+
# Populate machine infos
|
142
|
+
[:zone, :environment, :role, :position].each do |n|
|
143
|
+
backup.send("#{n}=", @global.send(n)) if @global.send(n)
|
144
|
+
end
|
145
|
+
|
146
|
+
# Populate disk infos
|
147
|
+
[:path, :size].each do |n|
|
148
|
+
backup.send("#{n}=", disk.send(n)) if disk.send(n)
|
149
|
+
end
|
150
|
+
|
151
|
+
backup.time_stamp
|
152
|
+
|
153
|
+
raise "There was a problem creating the backup metadata" unless backup.valid?
|
154
|
+
|
155
|
+
snap = @ec2.snapshots.create(volume[:aws_id])
|
156
|
+
|
157
|
+
if !snap || !snap.is_a?(Hash)
|
158
|
+
puts "There was an unknown problem creating #{backup.name}. Continuing with the next volume..."
|
159
|
+
next
|
160
|
+
end
|
161
|
+
|
162
|
+
backup.awsid = snap[:aws_id]
|
163
|
+
|
164
|
+
Rudy::MetaData::Backup.save(@sdb, backup)
|
165
|
+
|
166
|
+
puts backup.name
|
167
|
+
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
@@ -0,0 +1,839 @@
|
|
1
|
+
|
2
|
+
module Rudy
|
3
|
+
class UnknownInstance < RuntimeError; end
|
4
|
+
end
|
5
|
+
|
6
|
+
module Rudy
|
7
|
+
module Command
|
8
|
+
class NoCred < RuntimeError; end;
|
9
|
+
|
10
|
+
class Base < Drydock::Command
|
11
|
+
|
12
|
+
attr_reader :scm
|
13
|
+
|
14
|
+
attr_reader :rscripts
|
15
|
+
attr_reader :domains
|
16
|
+
attr_reader :machine_images
|
17
|
+
|
18
|
+
attr_reader :config
|
19
|
+
|
20
|
+
|
21
|
+
def init
|
22
|
+
|
23
|
+
|
24
|
+
raise "PRODUCTION ACCESS IS DISABLED IN DEBUG MODE" if @global.environment == "prod" && Drydock.debug?
|
25
|
+
|
26
|
+
@global.config ||= RUDY_CONFIG_FILE
|
27
|
+
|
28
|
+
unless File.exists?(@global.config)
|
29
|
+
init_config_dir
|
30
|
+
end
|
31
|
+
|
32
|
+
@config = Rudy::Config.new(@global.config, {:verbose => (@global.verbose > 0)} )
|
33
|
+
@config.look_and_load
|
34
|
+
|
35
|
+
raise "There is no machine group configured" if @config.machines.nil?
|
36
|
+
raise "There is no AWS info configured" if @config.awsinfo.nil?
|
37
|
+
|
38
|
+
|
39
|
+
@global.accesskey ||= @config.awsinfo.accesskey || ENV['AWS_ACCESS_KEY']
|
40
|
+
@global.secretkey ||= @config.awsinfo.secretkey || ENV['AWS_SECRET_KEY'] || ENV['AWS_SECRET_ACCESS_KEY']
|
41
|
+
@global.account ||= @config.awsinfo.account || ENV['AWS_ACCOUNT_NUMBER']
|
42
|
+
|
43
|
+
@global.cert ||= @config.awsinfo.cert || ENV['EC2_CERT']
|
44
|
+
@global.privatekey ||= @config.awsinfo.privatekey || ENV['EC2_PRIVATE_KEY']
|
45
|
+
|
46
|
+
@global.cert = File.expand_path(@global.cert || '')
|
47
|
+
@global.privatekey = File.expand_path(@global.privatekey || '')
|
48
|
+
|
49
|
+
@global.region ||= @config.defaults.region || DEFAULT_REGION
|
50
|
+
@global.zone ||= @config.defaults.zone || DEFAULT_ZONE
|
51
|
+
@global.environment ||= @config.defaults.environment || DEFAULT_ENVIRONMENT
|
52
|
+
@global.role ||= @config.defaults.role || DEFAULT_ROLE
|
53
|
+
@global.position ||= @config.defaults.position || DEFAULT_POSITION
|
54
|
+
@global.user ||= @config.defaults.user || DEFAULT_USER
|
55
|
+
|
56
|
+
@global.local_user = ENV['USER'] || :user
|
57
|
+
@global.local_hostname = Socket.gethostname || :host
|
58
|
+
|
59
|
+
|
60
|
+
if @global.verbose > 1
|
61
|
+
puts "GLOBALS:"
|
62
|
+
@global.marshal_dump.each_pair do |n,v|
|
63
|
+
puts "#{n}: #{v}"
|
64
|
+
end
|
65
|
+
["machines", "routines"].each do |type|
|
66
|
+
puts "#{$/*2}#{type.upcase}:"
|
67
|
+
val = @config.send(type).find_deferred(@global.environment, @global.role)
|
68
|
+
puts val.to_hash.to_yaml
|
69
|
+
end
|
70
|
+
puts
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
# TODO: enforce home directory permissions
|
76
|
+
#if File.exists?(RUDY_CONFIG_DIR)
|
77
|
+
# puts "Checking #{check_environment} permissions..."
|
78
|
+
#end
|
79
|
+
|
80
|
+
if has_keys?
|
81
|
+
@ec2 = Rudy::AWS::EC2.new(@global.accesskey, @global.secretkey)
|
82
|
+
@sdb = Rudy::AWS::SimpleDB.new(@global.accesskey, @global.secretkey)
|
83
|
+
#@s3 = Rudy::AWS::SimpleDB.new(@global.accesskey, @global.secretkey)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
protected :init
|
87
|
+
|
88
|
+
def machine_data
|
89
|
+
machine_data = {
|
90
|
+
# Give the machine an identity
|
91
|
+
:zone => @global.zone,
|
92
|
+
:environment => @global.environment,
|
93
|
+
:role => @global.role,
|
94
|
+
:position => @global.position,
|
95
|
+
|
96
|
+
# Add hosts to the /etc/hosts file
|
97
|
+
:hosts => {
|
98
|
+
:dbmaster => "127.0.0.1",
|
99
|
+
}
|
100
|
+
}
|
101
|
+
|
102
|
+
machine_data.to_hash
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
# Raises exceptions if the requested user does
|
107
|
+
# not have a valid keypair configured. (See: EC2_KEYPAIR_*)
|
108
|
+
def check_keys
|
109
|
+
raise "No SSH key provided for #{@global.user}! (check #{RUDY_CONFIG_FILE})" unless has_keypair?
|
110
|
+
raise "SSH key provided but cannot be found! (check #{RUDY_CONFIG_FILE})" unless File.exists?(keypairpath)
|
111
|
+
end
|
112
|
+
|
113
|
+
def has_pem_keys?
|
114
|
+
(@global.cert && File.exists?(@global.cert) &&
|
115
|
+
@global.privatekey && File.exists?(@global.privatekey))
|
116
|
+
end
|
117
|
+
|
118
|
+
def has_keys?
|
119
|
+
(@global.accesskey && !@global.accesskey.empty? && @global.secretkey && !@global.secretkey.empty?)
|
120
|
+
end
|
121
|
+
|
122
|
+
def keypairpath(name=nil)
|
123
|
+
name ||= @global.user
|
124
|
+
raise "No default user configured" unless name
|
125
|
+
kp = @config.machines.find(@global.environment, @global.role, :users, name, :keypair2)
|
126
|
+
kp ||= @config.machines.find(@global.environment, :users, name, :keypair)
|
127
|
+
kp ||= @config.machines.find(:users, name, :keypair)
|
128
|
+
kp &&= File.expand_path(kp)
|
129
|
+
kp
|
130
|
+
end
|
131
|
+
def has_keypair?(name=nil)
|
132
|
+
kp = keypairpath(name)
|
133
|
+
(!kp.nil? && File.exists?(kp))
|
134
|
+
end
|
135
|
+
|
136
|
+
# Opens an SSH session.
|
137
|
+
# <li>+host+ the hostname to connect to. Defaults to the machine specified
|
138
|
+
# by @global.environment, @global.role, @global.position.</li>
|
139
|
+
# <li>+b+ a block to execute on the host. Receives |session|</li>
|
140
|
+
#
|
141
|
+
# ssh do |session|
|
142
|
+
# session.exec(cmd)
|
143
|
+
# end
|
144
|
+
#
|
145
|
+
# See Net::SSH
|
146
|
+
#
|
147
|
+
def ssh(host=nil, &b)
|
148
|
+
host ||= machine_hostname
|
149
|
+
raise "No host provided for SSH" unless host
|
150
|
+
raise "No block provided for SSH" unless b
|
151
|
+
|
152
|
+
Net::SSH.start(host, @global.user, :keys => [keypairpath]) do |session|
|
153
|
+
b.call(session)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# Secure copy.
|
158
|
+
#
|
159
|
+
# scp do |scp|
|
160
|
+
# # upload a file to a remote server
|
161
|
+
# scp.upload! "/local/path", "/remote/path"
|
162
|
+
#
|
163
|
+
# # upload from an in-memory buffer
|
164
|
+
# scp.upload! StringIO.new("some data to upload"), "/remote/path"
|
165
|
+
#
|
166
|
+
# # run multiple downloads in parallel
|
167
|
+
# d1 = scp.download("/remote/path", "/local/path")
|
168
|
+
# d2 = scp.download("/remote/path2", "/local/path2")
|
169
|
+
# [d1, d2].each { |d| d.wait }
|
170
|
+
# end
|
171
|
+
#
|
172
|
+
def scp(host=nil, &b)
|
173
|
+
host ||= machine_hostname
|
174
|
+
raise "No host provided for scp" unless host
|
175
|
+
raise "No block provided for scp" unless b
|
176
|
+
|
177
|
+
Net::SCP.start(host, @global.user, :keys => [keypairpath]) do |scp|
|
178
|
+
b.call(scp)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
# +name+ the name of the remote user to use for the remainder of the command
|
183
|
+
# (or until switched again). If no name is provided, the user will be revert
|
184
|
+
# to whatever it was before the previous switch.
|
185
|
+
def switch_user(name=nil)
|
186
|
+
if name == nil && @switch_user_previous
|
187
|
+
@global.user = @switch_user_previous
|
188
|
+
elsif @global.user != name
|
189
|
+
puts "Remote commands will be run as #{name} user"
|
190
|
+
@switch_user_previous = @global.user
|
191
|
+
@global.user = name
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# Returns a hash of info for the requested machine. If the requested machine
|
196
|
+
# is not running, it will raise an exception.
|
197
|
+
def find_current_machine
|
198
|
+
find_machine(machine_group)
|
199
|
+
end
|
200
|
+
|
201
|
+
def find_machine(group)
|
202
|
+
machine_list = @ec2.instances.list(group)
|
203
|
+
machine = machine_list.values.first # NOTE: Only one machine per group, for now...
|
204
|
+
raise "There's no machine running in #{group}" unless machine
|
205
|
+
raise "The primary machine in #{group} is not in a running state" unless machine[:aws_state] == 'running'
|
206
|
+
machine
|
207
|
+
end
|
208
|
+
|
209
|
+
def machine_hostname(group=nil)
|
210
|
+
group ||= machine_group
|
211
|
+
find_machine(group)[:dns_name]
|
212
|
+
end
|
213
|
+
|
214
|
+
def machine_group
|
215
|
+
[@global.environment, @global.role].join(RUDY_DELIM)
|
216
|
+
end
|
217
|
+
|
218
|
+
def machine_image
|
219
|
+
ami = @config.machines.find_deferred(@global.environment, @global.role, :ami)
|
220
|
+
raise "There is no AMI configured for #{machine_group}" unless ami
|
221
|
+
ami
|
222
|
+
end
|
223
|
+
|
224
|
+
def machine_address
|
225
|
+
@config.machines.find_deferred(@global.environment, @global.role, :address)
|
226
|
+
end
|
227
|
+
|
228
|
+
# TODO: fix machine_group to include zone
|
229
|
+
def machine_name
|
230
|
+
[@global.zone, machine_group, @global.position].join(RUDY_DELIM)
|
231
|
+
end
|
232
|
+
|
233
|
+
def instance_id?(id=nil)
|
234
|
+
(id && id[0,2] == "i-")
|
235
|
+
end
|
236
|
+
|
237
|
+
def image_id?(id=nil)
|
238
|
+
(id && id[0,4] == "ami-")
|
239
|
+
end
|
240
|
+
|
241
|
+
def volume_id?(id=nil)
|
242
|
+
(id && id[0,4] == "vol-")
|
243
|
+
end
|
244
|
+
|
245
|
+
def snapshot_id?(id=nil)
|
246
|
+
(id && id[0,5] == "snap-")
|
247
|
+
end
|
248
|
+
|
249
|
+
|
250
|
+
def wait_for_machine(id)
|
251
|
+
|
252
|
+
print "Waiting for #{id} to become available"
|
253
|
+
STDOUT.flush
|
254
|
+
|
255
|
+
while @ec2.instances.pending?(id)
|
256
|
+
sleep 2
|
257
|
+
print '.'
|
258
|
+
STDOUT.flush
|
259
|
+
end
|
260
|
+
|
261
|
+
machine = @ec2.instances.get(id)
|
262
|
+
|
263
|
+
puts " It's up!\a\a" # with bells
|
264
|
+
print "Waiting for SSH daemon at #{machine[:dns_name]}"
|
265
|
+
STDOUT.flush
|
266
|
+
|
267
|
+
while !Rudy::Utils.service_available?(machine[:dns_name], 22)
|
268
|
+
print '.'
|
269
|
+
STDOUT.flush
|
270
|
+
end
|
271
|
+
puts " It's up!\a\a\a"
|
272
|
+
|
273
|
+
end
|
274
|
+
|
275
|
+
|
276
|
+
def device_to_path(machine, device)
|
277
|
+
# /dev/sdr 10321208 154232 9642688 2% /rilli/app
|
278
|
+
dfoutput = ssh_command(machine[:dns_name], keypairpath, @global.user, "df #{device} | tail -1").chomp
|
279
|
+
dfvals = dfoutput.scan(/(#{device}).+\s(.+?)$/).flatten # ["/dev/sdr", "/rilli/app"]
|
280
|
+
dfvals.last
|
281
|
+
end
|
282
|
+
|
283
|
+
# +action+ is one of: :shutdown, :start, :deploy
|
284
|
+
# +machine+ is a right_aws machine instance hash
|
285
|
+
def execute_disk_routines(machines, action)
|
286
|
+
machines = [machines] unless machines.is_a?( Array)
|
287
|
+
|
288
|
+
puts "Running #{action.to_s.capitalize} DISK routines".att(:bright)
|
289
|
+
|
290
|
+
disks = @config.machines.find_deferred(@global.environment, @global.role, :disks)
|
291
|
+
routines = @config.routines.find(@global.environment, @global.role, action, :disks)
|
292
|
+
|
293
|
+
unless routines
|
294
|
+
puts "No #{action} disk routines."
|
295
|
+
return
|
296
|
+
end
|
297
|
+
|
298
|
+
switch_user("root")
|
299
|
+
|
300
|
+
machines.each do |machine|
|
301
|
+
|
302
|
+
unless machine[:aws_instance_id]
|
303
|
+
puts "Machine given has no instance ID. Skipping disks."
|
304
|
+
return
|
305
|
+
end
|
306
|
+
|
307
|
+
unless machine[:dns_name]
|
308
|
+
puts "Machine given has no DNS name. Skipping disks."
|
309
|
+
return
|
310
|
+
end
|
311
|
+
|
312
|
+
if routines.destroy
|
313
|
+
disk_paths = routines.destroy.keys
|
314
|
+
vols = @ec2.instances.volumes(machine[:aws_instance_id]) || []
|
315
|
+
puts "No volumes to destroy for (#{machine[:aws_instance_id]})" if vols.empty?
|
316
|
+
vols.each do |vol|
|
317
|
+
disk = Rudy::MetaData::Disk.find_from_volume(@sdb, vol[:aws_id])
|
318
|
+
if disk
|
319
|
+
this_path = disk.path
|
320
|
+
else
|
321
|
+
puts "No disk metadata for volume #{vol[:aws_id]}. Going old school..."
|
322
|
+
this_path = device_to_path(machine, vol[:aws_device])
|
323
|
+
end
|
324
|
+
|
325
|
+
dconf = disks[this_path]
|
326
|
+
|
327
|
+
unless dconf
|
328
|
+
puts "#{this_path} is not defined for this machine. Check your machines config."
|
329
|
+
next
|
330
|
+
end
|
331
|
+
|
332
|
+
if disk_paths.member?(this_path)
|
333
|
+
|
334
|
+
unless disks.has_key?(this_path)
|
335
|
+
puts "#{this_path} is not defined as a machine disk. Skipping..."
|
336
|
+
next
|
337
|
+
end
|
338
|
+
|
339
|
+
begin
|
340
|
+
puts "Unmounting #{this_path}..."
|
341
|
+
ssh_command machine[:dns_name], keypairpath, @global.user, "umount #{this_path}"
|
342
|
+
sleep 3
|
343
|
+
rescue => ex
|
344
|
+
puts "Error while unmounting #{this_path}: #{ex.message}"
|
345
|
+
puts ex.backtrace if Drydock.debug?
|
346
|
+
puts "We'll keep going..."
|
347
|
+
end
|
348
|
+
|
349
|
+
begin
|
350
|
+
|
351
|
+
if @ec2.volumes.attached?(disk.awsid)
|
352
|
+
puts "Detaching #{vol[:aws_id]}"
|
353
|
+
@ec2.volumes.detach(vol[:aws_id])
|
354
|
+
sleep 3 # TODO: replace with something like wait_for_machine
|
355
|
+
end
|
356
|
+
|
357
|
+
puts "Destroying #{this_path} (#{vol[:aws_id]})"
|
358
|
+
if @ec2.volumes.available?(disk.awsid)
|
359
|
+
@ec2.volumes.destroy(vol[:aws_id])
|
360
|
+
else
|
361
|
+
puts "Volume is still attached (maybe a web server of database is running?)"
|
362
|
+
end
|
363
|
+
|
364
|
+
if disk
|
365
|
+
puts "Deleteing metadata for #{disk.name}"
|
366
|
+
Rudy::MetaData::Disk.destroy(@sdb, disk)
|
367
|
+
end
|
368
|
+
|
369
|
+
rescue => ex
|
370
|
+
puts "Error while detaching volume #{vol[:aws_id]}: #{ex.message}"
|
371
|
+
puts ex.backtrace if Drydock.debug?
|
372
|
+
puts "Continuing..."
|
373
|
+
end
|
374
|
+
|
375
|
+
end
|
376
|
+
puts
|
377
|
+
|
378
|
+
end
|
379
|
+
|
380
|
+
end
|
381
|
+
|
382
|
+
|
383
|
+
if routines.mount
|
384
|
+
disk_paths = routines.mount.keys
|
385
|
+
vols = @ec2.instances.volumes(machine[:aws_instance_id]) || []
|
386
|
+
puts "No volumes to mount for (#{machine[:aws_instance_id]})" if vols.empty?
|
387
|
+
vols.each do |vol|
|
388
|
+
disk = Rudy::MetaData::Disk.find_from_volume(@sdb, vol[:aws_id])
|
389
|
+
if disk
|
390
|
+
this_path = disk.path
|
391
|
+
else
|
392
|
+
puts "No disk metadata for volume #{vol[:aws_id]}. Going old school..."
|
393
|
+
this_path = device_to_path(machine, vol[:aws_device])
|
394
|
+
end
|
395
|
+
|
396
|
+
next unless disk_paths.member?(this_path)
|
397
|
+
|
398
|
+
dconf = disks[this_path]
|
399
|
+
|
400
|
+
unless dconf
|
401
|
+
puts "#{this_path} is not defined for this machine. Check your machines config."
|
402
|
+
next
|
403
|
+
end
|
404
|
+
|
405
|
+
|
406
|
+
begin
|
407
|
+
unless @ec2.instances.attached_volume?(machine[:aws_instance_id], vol[:aws_device])
|
408
|
+
puts "Attaching #{vol[:aws_id]} to #{machine[:aws_instance_id]}".att(:bright)
|
409
|
+
@ec2.volumes.attach(machine[:aws_instance_id], vol[:aws_id],vol[:aws_device])
|
410
|
+
sleep 3
|
411
|
+
end
|
412
|
+
|
413
|
+
puts "Mounting #{this_path} to #{vol[:aws_device]}".att(:bright)
|
414
|
+
ssh_command machine[:dns_name], keypairpath, @global.user, "mkdir -p #{this_path} && mount -t ext3 #{vol[:aws_device]} #{this_path}"
|
415
|
+
|
416
|
+
sleep 1
|
417
|
+
rescue => ex
|
418
|
+
puts "There was an error mounting #{this_path}: #{ex.message}"
|
419
|
+
puts ex.backtrace if Drydock.debug?
|
420
|
+
end
|
421
|
+
puts
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
|
426
|
+
|
427
|
+
if routines.restore
|
428
|
+
|
429
|
+
routines.restore.each_pair do |path,props|
|
430
|
+
from = props[:from] || "unknown"
|
431
|
+
unless from.to_s == "backup"
|
432
|
+
puts "Sorry! You can currently only restore from backup. Check your routines config."
|
433
|
+
next
|
434
|
+
end
|
435
|
+
|
436
|
+
begin
|
437
|
+
puts "Restoring disk for #{path}"
|
438
|
+
|
439
|
+
dconf = disks[path]
|
440
|
+
|
441
|
+
unless dconf
|
442
|
+
puts "#{path} is not defined for this machine. Check your machines config."
|
443
|
+
next
|
444
|
+
end
|
445
|
+
|
446
|
+
zon = props[:zone] || @global.zone
|
447
|
+
env = props[:environment] || @global.environment
|
448
|
+
rol = props[:role] || @global.role
|
449
|
+
pos = props[:position] || @global.position
|
450
|
+
puts "Looking for backup from #{zon}-#{env}-#{rol}-#{pos}"
|
451
|
+
backup = find_most_recent_backup(zon, env, rol, pos, path)
|
452
|
+
|
453
|
+
unless backup
|
454
|
+
puts "No backups found"
|
455
|
+
next
|
456
|
+
end
|
457
|
+
|
458
|
+
puts "Found: #{backup.name}".att(:bright)
|
459
|
+
|
460
|
+
disk = Rudy::MetaData::Disk.new
|
461
|
+
disk.path = path
|
462
|
+
[:region, :zone, :environment, :role, :position].each do |n|
|
463
|
+
disk.send("#{n}=", @global.send(n)) if @global.send(n)
|
464
|
+
end
|
465
|
+
|
466
|
+
disk.device = dconf[:device]
|
467
|
+
size = (backup.size.to_i > dconf[:size].to_i) ? backup.size : dconf[:size]
|
468
|
+
disk.size = size.to_i
|
469
|
+
|
470
|
+
|
471
|
+
if Rudy::MetaData::Disk.is_defined?(@sdb, disk)
|
472
|
+
puts "The disk #{disk.name} already exists."
|
473
|
+
puts "You probably need to define when to destroy the disk."
|
474
|
+
puts "Skipping..."
|
475
|
+
next
|
476
|
+
end
|
477
|
+
|
478
|
+
if @ec2.instances.attached_volume?(machine[:aws_instance_id], disk.device)
|
479
|
+
puts "Skipping disk for #{disk.path} (device #{disk.device} is in use)"
|
480
|
+
next
|
481
|
+
end
|
482
|
+
|
483
|
+
# NOTE: It's important to use Caesars' hash syntax b/c the disk property
|
484
|
+
# "size" conflicts with Hash#size which is what we'll get if there's no
|
485
|
+
# size defined.
|
486
|
+
unless disk.size.kind_of?(Integer)
|
487
|
+
puts "Skipping disk for #{disk.path} (size not defined)"
|
488
|
+
next
|
489
|
+
end
|
490
|
+
|
491
|
+
if disk.path.nil?
|
492
|
+
puts "Skipping disk for #{disk.path} (no path defined)"
|
493
|
+
next
|
494
|
+
end
|
495
|
+
|
496
|
+
unless disk.valid?
|
497
|
+
puts "Skipping #{disk.name} (not enough info)"
|
498
|
+
next
|
499
|
+
end
|
500
|
+
|
501
|
+
puts "Creating volume... (from #{backup.awsid})".att(:bright)
|
502
|
+
volume = @ec2.volumes.create(@global.zone, disk.size, backup.awsid)
|
503
|
+
|
504
|
+
puts "Attaching #{volume[:aws_id]} to #{machine[:aws_instance_id]}".att(:bright)
|
505
|
+
@ec2.volumes.attach(machine[:aws_instance_id], volume[:aws_id], disk.device)
|
506
|
+
sleep 3
|
507
|
+
|
508
|
+
puts "Mounting #{disk.device} to #{disk.path}".att(:bright)
|
509
|
+
ssh_command machine[:dns_name], keypairpath, @global.user, "mkdir -p #{disk.path} && mount -t ext3 #{disk.device} #{disk.path}"
|
510
|
+
|
511
|
+
puts "Creating disk metadata for #{disk.name}"
|
512
|
+
disk.awsid = volume[:aws_id]
|
513
|
+
Rudy::MetaData::Disk.save(@sdb, disk)
|
514
|
+
|
515
|
+
sleep 1
|
516
|
+
rescue => ex
|
517
|
+
puts "There was an error creating #{path}: #{ex.message}"
|
518
|
+
puts ex.backtrace if Drydock.debug?
|
519
|
+
if disk
|
520
|
+
puts "Removing metadata for #{disk.name}"
|
521
|
+
Rudy::MetaData::Disk.destroy(@sdb, disk)
|
522
|
+
end
|
523
|
+
end
|
524
|
+
puts
|
525
|
+
end
|
526
|
+
end
|
527
|
+
|
528
|
+
|
529
|
+
|
530
|
+
if routines.create
|
531
|
+
routines.create.each_pair do |path,props|
|
532
|
+
|
533
|
+
begin
|
534
|
+
puts "Creating disk for #{path}"
|
535
|
+
|
536
|
+
dconf = disks[path]
|
537
|
+
|
538
|
+
unless dconf
|
539
|
+
puts "#{path} is not defined for this machine. Check your machines config."
|
540
|
+
next
|
541
|
+
end
|
542
|
+
|
543
|
+
disk = Rudy::MetaData::Disk.new
|
544
|
+
disk.path = path
|
545
|
+
[:region, :zone, :environment, :role, :position].each do |n|
|
546
|
+
disk.send("#{n}=", @global.send(n)) if @global.send(n)
|
547
|
+
end
|
548
|
+
[:device, :size].each do |n|
|
549
|
+
disk.send("#{n}=", dconf[n]) if dconf.has_key?(n)
|
550
|
+
end
|
551
|
+
|
552
|
+
if Rudy::MetaData::Disk.is_defined?(@sdb, disk)
|
553
|
+
puts "The disk #{disk.name} already exists."
|
554
|
+
puts "You probably need to define when to destroy the disk."
|
555
|
+
puts "Skipping..."
|
556
|
+
next
|
557
|
+
end
|
558
|
+
|
559
|
+
if @ec2.instances.attached_volume?(machine[:aws_instance_id], disk.device)
|
560
|
+
puts "Skipping disk for #{disk.path} (device #{disk.device} is in use)"
|
561
|
+
next
|
562
|
+
end
|
563
|
+
|
564
|
+
# NOTE: It's important to use Caesars' hash syntax b/c the disk property
|
565
|
+
# "size" conflicts with Hash#size which is what we'll get if there's no
|
566
|
+
# size defined.
|
567
|
+
unless disk.size.kind_of?(Integer)
|
568
|
+
puts "Skipping disk for #{disk.path} (size not defined)"
|
569
|
+
next
|
570
|
+
end
|
571
|
+
|
572
|
+
if disk.path.nil?
|
573
|
+
puts "Skipping disk for #{disk.path} (no path defined)"
|
574
|
+
next
|
575
|
+
end
|
576
|
+
|
577
|
+
unless disk.valid?
|
578
|
+
puts "Skipping #{disk.name} (not enough info)"
|
579
|
+
next
|
580
|
+
end
|
581
|
+
|
582
|
+
puts "Creating volume... (#{disk.size}GB in #{@global.zone})".att(:bright)
|
583
|
+
volume = @ec2.volumes.create(@global.zone, disk.size)
|
584
|
+
|
585
|
+
puts "Attaching #{volume[:aws_id]} to #{machine[:aws_instance_id]}".att(:bright)
|
586
|
+
@ec2.volumes.attach(machine[:aws_instance_id], volume[:aws_id], disk.device)
|
587
|
+
sleep 6
|
588
|
+
|
589
|
+
puts "Creating the filesystem (mkfs.ext3 -F #{disk.device})".att(:bright)
|
590
|
+
ssh_command machine[:dns_name], keypairpath, @global.user, "mkfs.ext3 -F #{disk.device}"
|
591
|
+
sleep 3
|
592
|
+
|
593
|
+
puts "Mounting #{disk.device} to #{disk.path}".att(:bright)
|
594
|
+
ssh_command machine[:dns_name], keypairpath, @global.user, "mkdir -p #{disk.path} && mount -t ext3 #{disk.device} #{disk.path}"
|
595
|
+
|
596
|
+
puts "Creating disk metadata for #{disk.name}"
|
597
|
+
disk.awsid = volume[:aws_id]
|
598
|
+
Rudy::MetaData::Disk.save(@sdb, disk)
|
599
|
+
|
600
|
+
sleep 1
|
601
|
+
rescue => ex
|
602
|
+
puts "There was an error creating #{path}: #{ex.message}"
|
603
|
+
if disk
|
604
|
+
puts "Removing metadata for #{disk.name}"
|
605
|
+
Rudy::MetaData::Disk.destroy(@sdb, disk)
|
606
|
+
end
|
607
|
+
end
|
608
|
+
puts
|
609
|
+
end
|
610
|
+
end
|
611
|
+
end
|
612
|
+
end
|
613
|
+
|
614
|
+
def find_most_recent_backup(zon, env, rol, pos, path)
|
615
|
+
criteria = [zon, env, rol, pos, path]
|
616
|
+
(Rudy::MetaData::Backup.list(@sdb, *criteria) || []).first
|
617
|
+
end
|
618
|
+
|
619
|
+
def execute_routines(machines, action, before_or_after)
|
620
|
+
machines = [machines] unless machines.is_a?( Array)
|
621
|
+
config = @config.routines.find_deferred(@global.environment, @global.role, :config) || {}
|
622
|
+
config[:global] = @global.marshal_dump
|
623
|
+
config[:global].reject! { |n,v| n == :cert || n == :privatekey }
|
624
|
+
|
625
|
+
# The config file contains settings from ~/.rudy/config
|
626
|
+
#
|
627
|
+
# routines do
|
628
|
+
# config do
|
629
|
+
# end
|
630
|
+
# end
|
631
|
+
#
|
632
|
+
config_file = "#{action}-config.yaml"
|
633
|
+
tf = Tempfile.new(config_file)
|
634
|
+
write_to_file(tf.path, config.to_hash.to_yaml, 'w')
|
635
|
+
puts "Running #{action.to_s.capitalize} #{before_or_after.to_s.upcase} routines".att(:bright)
|
636
|
+
machines.each do |machine|
|
637
|
+
|
638
|
+
rscripts = @config.routines.find_deferred(@global.environment, @global.role, action, before_or_after) || []
|
639
|
+
rscripts = [rscripts] unless rscripts.is_a?(Array)
|
640
|
+
|
641
|
+
puts "No scripts defined." if !rscripts || rscripts.empty?
|
642
|
+
|
643
|
+
rscripts.each do |rscript|
|
644
|
+
user, script = rscript.shift
|
645
|
+
|
646
|
+
switch_user(user) # scp and ssh will run as this user
|
647
|
+
|
648
|
+
puts "Transfering #{config_file}..."
|
649
|
+
scp do |scp|
|
650
|
+
scp.upload!(tf.path, "~/#{config_file}") do |ch, name, sent, total|
|
651
|
+
"#{name}: #{sent}/#{total}"
|
652
|
+
end
|
653
|
+
end
|
654
|
+
ssh do |session|
|
655
|
+
puts "Running #{script}...".att(:bright)
|
656
|
+
session.exec!("chmod 700 ~/#{config_file}")
|
657
|
+
session.exec!("chmod 700 #{script}")
|
658
|
+
puts session.exec!("#{script}")
|
659
|
+
|
660
|
+
puts "Removing remote copy of #{config_file}..."
|
661
|
+
session.exec!("rm ~/#{config_file}")
|
662
|
+
end
|
663
|
+
puts $/
|
664
|
+
end
|
665
|
+
end
|
666
|
+
|
667
|
+
tf.delete # remove local copy of config_file
|
668
|
+
#switch_user # return to the requested user
|
669
|
+
end
|
670
|
+
|
671
|
+
# Print a default header to the screen for every command.
|
672
|
+
# +cmd+ is the name of the command current running.
|
673
|
+
def print_header(cmd=nil)
|
674
|
+
title = "RUDY v#{Rudy::VERSION}" unless @global.quiet
|
675
|
+
now_utc = Time.now.utc.strftime("%Y-%m-%d %H:%M:%S")
|
676
|
+
criteria = []
|
677
|
+
[:zone, :environment, :role, :position].each do |n|
|
678
|
+
val = @global.send(n)
|
679
|
+
next unless val
|
680
|
+
criteria << "#{n.to_s.slice(0,1).att :normal}:#{val.att :bright}"
|
681
|
+
end
|
682
|
+
puts '%s -- %s UTC' % [title, now_utc] unless @global.quiet
|
683
|
+
puts '[%s]' % criteria.join(" ") unless @global.quiet
|
684
|
+
|
685
|
+
puts unless @global.quiet
|
686
|
+
|
687
|
+
if (@global.environment == "prod")
|
688
|
+
msg = without_indent %q(
|
689
|
+
=======================================================
|
690
|
+
=======================================================
|
691
|
+
!!!!!!!!! YOU ARE PLAYING WITH PRODUCTION !!!!!!!!!
|
692
|
+
=======================================================
|
693
|
+
=======================================================)
|
694
|
+
puts msg.colour(:red).bgcolour(:white).att(:bright), $/ unless @global.quiet
|
695
|
+
|
696
|
+
end
|
697
|
+
|
698
|
+
if Rudy.in_situ?
|
699
|
+
msg = %q(============ THIS IS EC2 ============)
|
700
|
+
puts msg.colour(:blue).bgcolour(:white).att(:bright), $/ unless @global.quiet
|
701
|
+
end
|
702
|
+
|
703
|
+
end
|
704
|
+
|
705
|
+
def print_footer
|
706
|
+
|
707
|
+
end
|
708
|
+
|
709
|
+
|
710
|
+
|
711
|
+
|
712
|
+
def group_metadata(env=@global.environment, role=@global.role)
|
713
|
+
query = "['environment' = '#{env}'] intersection ['role' = '#{role}']"
|
714
|
+
@sdb.query_with_attributes(RUDY_DOMAIN, query)
|
715
|
+
end
|
716
|
+
|
717
|
+
private
|
718
|
+
# Print info about a running instance
|
719
|
+
# +inst+ is a hash
|
720
|
+
def print_instance(inst)
|
721
|
+
puts '-'*60
|
722
|
+
puts "Instance: #{inst[:aws_instance_id].att(:bright)} (AMI: #{inst[:aws_image_id]})"
|
723
|
+
[:aws_state, :dns_name, :private_dns_name, :aws_availability_zone, :aws_launch_time, :ssh_key_name].each do |key|
|
724
|
+
printf(" %22s: %s#{$/}", key, inst[key]) if inst[key]
|
725
|
+
end
|
726
|
+
printf(" %22s: %s#{$/}", 'aws_groups', inst[:aws_groups].join(', '))
|
727
|
+
puts
|
728
|
+
end
|
729
|
+
|
730
|
+
def print_image(img)
|
731
|
+
puts '-'*60
|
732
|
+
puts "Image: #{img[:aws_id].att(:bright)}"
|
733
|
+
img.each_pair do |key, value|
|
734
|
+
printf(" %22s: %s#{$/}", key, value) if value
|
735
|
+
end
|
736
|
+
puts
|
737
|
+
end
|
738
|
+
|
739
|
+
def print_disk(disk, backups=[])
|
740
|
+
puts '-'*60
|
741
|
+
puts "Disk: #{disk.name.att(:bright)}"
|
742
|
+
puts disk.to_s
|
743
|
+
puts "#{backups.size} most recent backups:", backups.collect { |back| "#{back.nice_time} (#{back.awsid})" }
|
744
|
+
puts
|
745
|
+
end
|
746
|
+
|
747
|
+
|
748
|
+
def print_volume(vol, disk)
|
749
|
+
puts '-'*60
|
750
|
+
puts "Volume: #{vol[:aws_id].att(:bright)} (disk: #{disk.name if disk})"
|
751
|
+
vol.each_pair do |key, value|
|
752
|
+
printf(" %22s: %s#{$/}", key, value) if value
|
753
|
+
end
|
754
|
+
puts
|
755
|
+
end
|
756
|
+
|
757
|
+
# Print info about a a security group
|
758
|
+
# +group+ is an OpenStruct
|
759
|
+
def print_group(group)
|
760
|
+
puts '-'*60
|
761
|
+
puts "%12s: %s" % ['GROUP', group[:aws_group_name].att(:bright)]
|
762
|
+
puts
|
763
|
+
|
764
|
+
group_ip = {}
|
765
|
+
group[:aws_perms].each do |perm|
|
766
|
+
(group_ip[ perm[:cidr_ips] ] ||= []) << "#{perm[:protocol]}/#{perm[:from_port]}-#{perm[:to_port]}"
|
767
|
+
end
|
768
|
+
|
769
|
+
puts "%22s %s" % ["source address/mask", "protocol/ports (from, to)"]
|
770
|
+
|
771
|
+
|
772
|
+
group_ip.each_pair do |ip, perms|
|
773
|
+
puts "%22s %s" % [ip, perms.shift]
|
774
|
+
perms.each do |perm|
|
775
|
+
puts "%22s %s" % ['', perm]
|
776
|
+
end
|
777
|
+
puts
|
778
|
+
end
|
779
|
+
end
|
780
|
+
|
781
|
+
def init_config_dir
|
782
|
+
unless File.exists?(RUDY_CONFIG_DIR)
|
783
|
+
puts "Creating #{RUDY_CONFIG_DIR}"
|
784
|
+
Dir.mkdir(RUDY_CONFIG_DIR, 0700)
|
785
|
+
end
|
786
|
+
|
787
|
+
unless File.exists?(RUDY_CONFIG_FILE)
|
788
|
+
puts "Creating #{RUDY_CONFIG_FILE}"
|
789
|
+
rudy_config = without_indent %Q{
|
790
|
+
# Amazon Web Services
|
791
|
+
# Account access indentifiers.
|
792
|
+
awsinfo do
|
793
|
+
account ""
|
794
|
+
accesskey ""
|
795
|
+
secretkey ""
|
796
|
+
privatekey "~/path/2/pk-xxxx.pem"
|
797
|
+
cert "~/path/2/cert-xxxx.pem"
|
798
|
+
end
|
799
|
+
|
800
|
+
# Machine Configuration
|
801
|
+
# Specify your private keys here. These can be defined globally
|
802
|
+
# or by environment and role like in machines.rb.
|
803
|
+
machines do
|
804
|
+
ami "ami-0734d36e" # gentoo-m1.small-v5
|
805
|
+
users do
|
806
|
+
root :keypair => "path/2/root-private-key"
|
807
|
+
end
|
808
|
+
end
|
809
|
+
|
810
|
+
# Routine Configuration
|
811
|
+
# Define stuff here that you don't want to be stored in version control.
|
812
|
+
routines do
|
813
|
+
config do
|
814
|
+
# ...
|
815
|
+
end
|
816
|
+
end
|
817
|
+
|
818
|
+
# Global Defaults
|
819
|
+
# Define the values to use unless otherwise specified on the command-line.
|
820
|
+
defaults do
|
821
|
+
region "us-east-1"
|
822
|
+
zone "us-east-1b"
|
823
|
+
environment "stage"
|
824
|
+
role "app"
|
825
|
+
position "01"
|
826
|
+
user ENV['USER']
|
827
|
+
end
|
828
|
+
}
|
829
|
+
write_to_file(RUDY_CONFIG_FILE, rudy_config, 'w')
|
830
|
+
end
|
831
|
+
|
832
|
+
#puts "Creating SimpleDB domain called #{RUDY_DOMAIN}"
|
833
|
+
#@sdb.domains.create(RUDY_DOMAIN)
|
834
|
+
end
|
835
|
+
end
|
836
|
+
end
|
837
|
+
end
|
838
|
+
|
839
|
+
|