rudy 0.3.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGES.txt +53 -3
- data/README.rdoc +9 -5
- data/bin/rudy +115 -292
- data/bin/rudy-ec2 +107 -0
- data/lib/console.rb +322 -278
- data/lib/rudy.rb +78 -55
- data/lib/rudy/aws/ec2.rb +63 -5
- data/lib/rudy/command/addresses.rb +18 -13
- data/lib/rudy/command/backups.rb +175 -0
- data/lib/rudy/command/base.rb +664 -146
- data/lib/rudy/command/config.rb +77 -0
- data/lib/rudy/command/deploy.rb +12 -0
- data/lib/rudy/command/disks.rb +165 -195
- data/lib/rudy/command/environment.rb +42 -64
- data/lib/rudy/command/groups.rb +21 -19
- data/lib/rudy/command/images.rb +34 -19
- data/lib/rudy/command/instances.rb +46 -92
- data/lib/rudy/command/machines.rb +161 -0
- data/lib/rudy/command/metadata.rb +14 -30
- data/lib/rudy/command/release.rb +174 -0
- data/lib/rudy/command/volumes.rb +26 -10
- data/lib/rudy/config.rb +93 -0
- data/lib/rudy/metadata/backup.rb +1 -1
- data/lib/rudy/metadata/disk.rb +15 -50
- data/lib/rudy/scm/svn.rb +32 -21
- data/lib/rudy/utils.rb +2 -3
- data/lib/storable.rb +4 -0
- data/lib/tryouts.rb +40 -0
- data/rudy.gemspec +25 -9
- data/support/mailtest +40 -0
- data/support/rudy-ec2-startup +41 -15
- data/tryouts/console_tryout.rb +91 -0
- metadata +86 -11
- data/lib/drydock.rb +0 -524
- data/lib/rudy/command/stage.rb +0 -45
- data/lib/rudy/metadata/config.rb +0 -8
- data/lib/rudy/metadata/environment.rb +0 -0
@@ -0,0 +1,77 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Rudy
|
4
|
+
module Command
|
5
|
+
class Config < Rudy::Command::Base
|
6
|
+
|
7
|
+
# We force the Command::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
|
+
# i.e. if [prod][app][ami] is not available, it will check [prod][ami]
|
25
|
+
# and then [ami].
|
26
|
+
#
|
27
|
+
# # Display the value for a specific machine.
|
28
|
+
# $ rudy -e prod -r db config param-name
|
29
|
+
#
|
30
|
+
# # Display all configuration
|
31
|
+
# $ rudy config --all
|
32
|
+
#
|
33
|
+
def config
|
34
|
+
return if @config.nil?
|
35
|
+
puts "Config: #{@config.paths}" if @global.verbose > 0
|
36
|
+
|
37
|
+
which = @option.defaults ? @global.user : machine_name
|
38
|
+
puts "Machine: #{which}" if @global.verbose > 0
|
39
|
+
puts "User: #{@global.user}" if @global.verbose > 0
|
40
|
+
|
41
|
+
return if @config.empty?
|
42
|
+
|
43
|
+
# We need to check whether we're running on a human's computer
|
44
|
+
# or within EC2 (we call that running "in-situ"). The userdata
|
45
|
+
# available when running in-situ is in a different format.
|
46
|
+
if Rudy.in_situ?
|
47
|
+
|
48
|
+
|
49
|
+
else
|
50
|
+
|
51
|
+
if @option.all
|
52
|
+
y @config.machines.to_hash
|
53
|
+
y @config.routines.to_hash
|
54
|
+
elsif @option.defaults
|
55
|
+
y @config.defaults.to_hash
|
56
|
+
else
|
57
|
+
env, rol, usr, att = @global.environment, @global.role, @global.user, @argv.name
|
58
|
+
val = @config.machines.find_deferred(env, rol, usr, att) || ''
|
59
|
+
puts (val.is_a?(String)) ? val : val.to_hash.to_yaml
|
60
|
+
end
|
61
|
+
|
62
|
+
#name = @argv.first
|
63
|
+
#if name && @config.userdata.has_key?(which)
|
64
|
+
# value = @config.userdata[which][name.to_s]
|
65
|
+
# puts value if value
|
66
|
+
#elsif @option.all
|
67
|
+
# puts @config.to_yaml
|
68
|
+
#else
|
69
|
+
# value = @config.userdata[which]
|
70
|
+
# puts value.to_yaml if value
|
71
|
+
#end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/lib/rudy/command/disks.rb
CHANGED
@@ -4,240 +4,210 @@ module Rudy
|
|
4
4
|
module Command
|
5
5
|
class Disks < Rudy::Command::Base
|
6
6
|
|
7
|
-
def
|
8
|
-
criteria = [@zone]
|
9
|
-
criteria += [@environment, @role] unless @all
|
10
|
-
|
11
|
-
|
12
|
-
|
7
|
+
def disk
|
8
|
+
criteria = [@global.zone]
|
9
|
+
criteria += [@global.environment, @global.role] unless @option.all
|
10
|
+
Rudy::MetaData::Disk.list(@sdb, *criteria).each do |disk|
|
11
|
+
backups = Rudy::MetaData::Backup.for_disk(@sdb, disk, 2)
|
12
|
+
print_disk(disk, backups)
|
13
13
|
end
|
14
14
|
end
|
15
|
+
|
16
|
+
def create_disk_valid?
|
17
|
+
raise "No filesystem path specified" unless @option.path
|
18
|
+
raise "No size specified" unless @option.size
|
19
|
+
@instances = @ec2.instances.list(machine_group)
|
20
|
+
raise "There are no instances running in #{machine_group}" if !@instances || @instances.empty?
|
21
|
+
true
|
22
|
+
end
|
15
23
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
24
|
+
def create_disk
|
25
|
+
puts "Creating #{@option.path} for #{machine_group}"
|
26
|
+
switch_user("root")
|
27
|
+
exit unless are_you_sure?(2)
|
28
|
+
machine = @instances.values.first # NOTE: DANGER! Should handle position.
|
20
29
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
to_be_deleted[backup.awsid] = backup unless @ec2.snapshots.exists?(backup.awsid)
|
30
|
+
disk = Rudy::MetaData::Disk.new
|
31
|
+
[:region, :zone, :environment, :role, :position].each do |n|
|
32
|
+
disk.send("#{n}=", @global.send(n)) if @global.send(n)
|
25
33
|
end
|
26
|
-
|
27
|
-
|
28
|
-
puts "All backups are in-sync with snapshots. Nothing to do."
|
29
|
-
return
|
34
|
+
[:path, :device, :size].each do |n|
|
35
|
+
disk.send("#{n}=", @option.send(n)) if @option.send(n)
|
30
36
|
end
|
31
37
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
end
|
38
|
+
raise "Not enough info was provided to define a disk (#{disk.name})" unless disk.valid?
|
39
|
+
raise "The device #{disk.device} is already in use on that machine" if Rudy::MetaData::Disk.is_defined?(@sdb, disk)
|
40
|
+
# TODO: Check disk path
|
41
|
+
puts "Creating disk metadata for #{disk.name}"
|
37
42
|
|
38
|
-
puts
|
39
|
-
are_you_sure?
|
40
43
|
|
41
|
-
puts
|
42
|
-
puts "Deleting..."
|
43
|
-
to_be_deleted.each do |snap_id, backup|
|
44
|
-
print " -> #{backup.name}... "
|
45
|
-
@sdb.destroy(RUDY_DOMAIN, backup.name)
|
46
|
-
puts "done"
|
47
|
-
end
|
48
44
|
|
49
|
-
puts "
|
45
|
+
puts "Creating volume... (#{disk.size}GB in #{@global.zone})"
|
46
|
+
volume = @ec2.volumes.create(@global.zone, disk.size)
|
47
|
+
sleep 3
|
48
|
+
|
49
|
+
disk.awsid = volume[:aws_id]
|
50
|
+
disk.raw_volume = true # This value is not saved.
|
51
|
+
Rudy::MetaData::Disk.save(@sdb, disk)
|
52
|
+
|
53
|
+
execute_attach_disk(disk, machine)
|
54
|
+
|
55
|
+
print_disk(disk)
|
50
56
|
end
|
51
57
|
|
52
58
|
|
53
|
-
def
|
54
|
-
|
55
|
-
begin
|
56
|
-
backup = Rudy::MetaData::Backup.get(@sdb, name)
|
57
|
-
rescue => ex
|
58
|
-
puts "Error deleteing backup: #{ex.message}"
|
59
|
-
end
|
60
|
-
|
61
|
-
return unless backup
|
59
|
+
def destroy_disk_valid?
|
60
|
+
raise "No disk specified" if argv.empty?
|
62
61
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
@
|
62
|
+
if @argv.diskname =~ /^disk-/
|
63
|
+
@disk = Rudy::MetaData::Disk.get(@sdb, @argv.diskname)
|
64
|
+
else
|
65
|
+
disk = Rudy::MetaData::Disk.new
|
66
|
+
[:zone, :environment, :role, :position].each do |n|
|
67
|
+
disk.send("#{n}=", @global.send(n)) if @global.send(n)
|
68
|
+
end
|
69
|
+
disk.path = @argv.diskname
|
70
|
+
@disk = Rudy::MetaData::Disk.get(@sdb, disk.name)
|
72
71
|
end
|
73
|
-
|
72
|
+
|
73
|
+
raise "No such disk: #{@argv.diskname}" unless @disk
|
74
|
+
raise "The disk is in another machine environment" unless @global.environment.to_s == @disk.environment.to_s
|
75
|
+
raise "The disk is in another machine role" unless @global.role.to_s == @disk.role.to_s
|
76
|
+
@instances = @ec2.instances.list(machine_group)
|
77
|
+
true
|
74
78
|
end
|
75
79
|
|
76
|
-
def
|
77
|
-
|
80
|
+
def destroy_disk
|
81
|
+
puts "Destroying #{@disk.name} and #{@disk.awsid}"
|
82
|
+
switch_user("root")
|
83
|
+
exit unless are_you_sure?(5)
|
78
84
|
|
79
|
-
|
80
|
-
raise "The machine #{machine_name} does not have any disk metadata" if disks.empty?
|
85
|
+
machine = @instances.values.first # NOTE: DANGER! Should handle position.
|
81
86
|
|
82
|
-
|
87
|
+
execute_unattach_disk(@disk, machine)
|
88
|
+
execute_destroy_disk(@disk, machine)
|
83
89
|
|
84
|
-
|
85
|
-
|
86
|
-
raise "The snapshot #{@snapshot} does not exist" unless @ec2.snapshots.exists?(@snapshot)
|
87
|
-
disk = Rudy::MetaData::Disk.get(@sdb, diskname)
|
88
|
-
|
89
|
-
raise "The disk #{diskname} does not exist" unless disk
|
90
|
-
backup = Rudy::MetaData::Backup.new
|
91
|
-
backup.awsid = @snapshot
|
92
|
-
backup.time_stamp
|
93
|
-
|
94
|
-
# Populate machine infos
|
95
|
-
[:zone, :environment, :role, :position].each do |n|
|
96
|
-
val = instance_variable_get("@#{n}")
|
97
|
-
backup.send("#{n}=", val) if val
|
98
|
-
end
|
99
|
-
|
100
|
-
# Populate disk infos
|
101
|
-
[:path, :size].each do |n|
|
102
|
-
backup.send("#{n}=", disk.send(n)) if disk.send(n)
|
103
|
-
end
|
104
|
-
|
105
|
-
|
106
|
-
Rudy::MetaData::Backup.save(@sdb, backup)
|
107
|
-
|
108
|
-
puts backup.name
|
109
|
-
|
110
|
-
else
|
111
|
-
volumes = @ec2.instances.volumes(machine[:aws_instance_id])
|
112
|
-
raise "The machine #{machine_name} does not have any volumes attached." if volumes.empty?
|
113
|
-
|
114
|
-
puts "#{disks.size} Disk(s) defined with #{volumes.size} Volume(s) running"
|
115
|
-
|
116
|
-
volumes.each do |volume|
|
117
|
-
print "Volume #{volume[:aws_id]}... "
|
118
|
-
disk = Rudy::MetaData::Disk.from_volume(@sdb, volume[:aws_id])
|
119
|
-
backup = Rudy::MetaData::Backup.new
|
120
|
-
|
121
|
-
# TODO: Look for the disk based on the machine
|
122
|
-
raise "No disk associated to volume #{volume[:aws_id]}" unless disk
|
123
|
-
|
124
|
-
backup.volume = volume[:aws_id]
|
125
|
-
|
126
|
-
snap = @ec2.snapshots.create(volume[:aws_id])
|
127
|
-
|
128
|
-
backup.awsid = snap[:aws_id]
|
129
|
-
|
130
|
-
if !snap || !snap.is_a?(Hash)
|
131
|
-
puts "There was an unknown problem creating #{backup.name}. Continuing with the next volume..."
|
132
|
-
next
|
133
|
-
end
|
134
|
-
|
135
|
-
backup.time_stamp
|
90
|
+
puts "Done."
|
91
|
+
end
|
136
92
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
93
|
+
def attach_disk_valid?
|
94
|
+
destroy_disk_valid?
|
95
|
+
raise "There are no instances running in #{machine_group}" if !@instances || @instances.empty?
|
96
|
+
true
|
97
|
+
end
|
98
|
+
|
99
|
+
def attach_disk
|
100
|
+
puts "Attaching #{name}"
|
101
|
+
switch_user("root")
|
102
|
+
are_you_sure?(4)
|
103
|
+
|
104
|
+
machine = @instances.values.first # AK! Assumes single machine
|
105
|
+
|
106
|
+
execute_attach_disk(@disk, machine)
|
142
107
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
end
|
108
|
+
puts
|
109
|
+
ssh_command machine[:dns_name], keypairpath, @global.user, "df -h" # Display current mounts
|
110
|
+
puts
|
147
111
|
|
112
|
+
puts "Done!"
|
113
|
+
end
|
148
114
|
|
149
|
-
Rudy::MetaData::Backup.save(@sdb, backup)
|
150
115
|
|
151
|
-
puts backup.name
|
152
|
-
|
153
|
-
end
|
154
116
|
|
155
|
-
end
|
156
117
|
|
157
|
-
|
118
|
+
def unattach_disk_valid?
|
119
|
+
destroy_disk_valid?
|
120
|
+
true
|
121
|
+
end
|
158
122
|
|
123
|
+
def unattach_disk
|
124
|
+
puts "Unattaching #{@disk.name} from #{machine_group}"
|
125
|
+
switch_user("root")
|
126
|
+
are_you_sure?(4)
|
127
|
+
|
128
|
+
machine = @instances.values.first
|
129
|
+
|
130
|
+
execute_unattach_disk(@disk, machine)
|
131
|
+
|
132
|
+
puts "Done!"
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
|
137
|
+
def execute_unattach_disk(disk, machine)
|
138
|
+
begin
|
159
139
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
140
|
+
if machine
|
141
|
+
puts "Unmounting #{disk.path}...".att(:bright)
|
142
|
+
ssh_command machine[:dns_name], keypairpath, global.user, "umount #{disk.path}"
|
143
|
+
sleep 1
|
144
|
+
end
|
145
|
+
|
146
|
+
if @ec2.volumes.attached?(disk.awsid)
|
147
|
+
puts "Unattaching #{disk.awsid}".att(:bright)
|
148
|
+
@ec2.volumes.detach(disk.awsid)
|
149
|
+
sleep 5
|
150
|
+
end
|
151
|
+
|
152
|
+
rescue => ex
|
153
|
+
puts "Error while unattaching volume #{disk.awsid}: #{ex.message}"
|
154
|
+
puts ex.backtrace if Drydock.debug?
|
168
155
|
end
|
169
|
-
|
170
|
-
raise "Not enough info was provided to define a disk (#{disk.name})" unless disk.valid?
|
171
|
-
raise "The device #{disk.device} is already in use on that machine" if Rudy::MetaData::Disk.is_defined?(@sdb, disk)
|
172
|
-
puts "Creating disk metadata for #{disk.name}"
|
173
|
-
|
174
|
-
Rudy::MetaData::Disk.save(@sdb, disk)
|
175
|
-
|
176
|
-
print_disks
|
177
156
|
end
|
178
157
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
158
|
+
def execute_destroy_disk(disk, machine)
|
159
|
+
begin
|
160
|
+
|
161
|
+
if disk
|
162
|
+
|
163
|
+
if disk.awsid && @ec2.volumes.available?(disk.awsid)
|
164
|
+
puts "Destroying #{disk.path} (#{disk.awsid})".att(:bright)
|
165
|
+
@ec2.volumes.destroy(disk.awsid)
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
187
169
|
|
170
|
+
rescue => ex
|
171
|
+
puts "Error while destroying volume #{disk.awsid}: #{ex.message}"
|
172
|
+
puts ex.backtrace if Drydock.debug?
|
173
|
+
ensure
|
174
|
+
puts "Deleteing metadata for #{disk.name}".att(:bright)
|
175
|
+
Rudy::MetaData::Disk.destroy(@sdb, disk)
|
188
176
|
end
|
189
|
-
|
190
177
|
end
|
191
178
|
|
192
|
-
def
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
end
|
215
|
-
|
216
|
-
def attach_disk(name)
|
217
|
-
puts "Looking for #{name}"
|
218
|
-
disk = Rudy::MetaData::Disk.get(@sdb, name)
|
219
|
-
instances = @ec2.instances.list(machine_group)
|
220
|
-
raise "There are no instances running in #{machine_group}" if !instances || instances.empty?
|
221
|
-
instance_id = instances.keys.first # <--- TODO: This is bad!
|
222
|
-
machine = instances.values.first
|
223
|
-
|
224
|
-
do_dirty_disk_volume_deeds(disk, machine)
|
225
|
-
|
226
|
-
|
227
|
-
puts
|
228
|
-
ssh machine[:dns_name], keypairpath, user, "df -h" # Display current mounts
|
229
|
-
puts
|
230
|
-
|
231
|
-
puts "Done!"
|
179
|
+
def execute_attach_disk(disk, machine)
|
180
|
+
begin
|
181
|
+
unless @ec2.instances.attached_volume?(machine[:aws_instance_id], disk.device)
|
182
|
+
puts "Attaching #{disk.awsid} to #{machine[:aws_instance_id]}".att(:bright)
|
183
|
+
@ec2.volumes.attach(machine[:aws_instance_id], disk.awsid, disk.device)
|
184
|
+
sleep 3
|
185
|
+
end
|
186
|
+
|
187
|
+
if disk.raw_volume
|
188
|
+
puts "Creating the filesystem (mkfs.ext3 -F #{disk.device})".att(:bright)
|
189
|
+
ssh_command machine[:dns_name], keypairpath, @global.user, "mkfs.ext3 -F #{disk.device}"
|
190
|
+
sleep 1
|
191
|
+
end
|
192
|
+
|
193
|
+
puts "Mounting #{disk.path} to #{disk.device}".att(:bright)
|
194
|
+
ssh_command machine[:dns_name], keypairpath, @global.user, "mkdir -p #{disk.path} && mount -t ext3 #{disk.device} #{disk.path}"
|
195
|
+
|
196
|
+
sleep 1
|
197
|
+
rescue => ex
|
198
|
+
puts "There was an error attaching #{disk.name}: #{ex.message}"
|
199
|
+
puts ex.backtrace if Drydock.debug?
|
200
|
+
end
|
232
201
|
end
|
233
202
|
|
234
|
-
def destroy_disk(name)
|
235
|
-
puts "Destroying #{name}"
|
236
|
-
@sdb.destroy(RUDY_DOMAIN, name)
|
237
|
-
puts "Done."
|
238
|
-
end
|
239
|
-
|
240
203
|
end
|
241
204
|
end
|
242
205
|
end
|
243
206
|
|
207
|
+
|
208
|
+
__END__
|
209
|
+
|
210
|
+
|
211
|
+
|
212
|
+
|
213
|
+
|