rudy 0.2.4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/console.rb ADDED
@@ -0,0 +1,341 @@
1
+
2
+ # Adapted from: http://github.com/oneup/ruby-console/tree/tput
3
+
4
+ if __FILE__ == $0
5
+ puts "This is a module, don't run it from the command line."
6
+ exit
7
+ end
8
+
9
+ class String
10
+ def color(*arg) # colorize a string
11
+ Console.color(arg) +
12
+ self +
13
+ Console.color(:default)
14
+ end
15
+ def printAt(*arg)
16
+ row = 0
17
+ col = 0
18
+ if not arg.is_a?(Array)
19
+ arg = [arg]
20
+ end
21
+ if arg.length > 0
22
+ row = arg[0]
23
+ end
24
+ if arg.length > 1
25
+ col = arg[1]
26
+ end
27
+ Cursor.save
28
+ Cursor.position = row, col
29
+ print self
30
+ Cursor.restore
31
+ end
32
+ end
33
+
34
+ class Console
35
+ def self.clear
36
+ system "tput clear"
37
+ end
38
+
39
+ def self.reset
40
+ system "tput reset"
41
+ end
42
+
43
+ def self.color(*arg) # colorize a string
44
+ if arg[0].is_a?(Array)
45
+ arg = arg[0]
46
+ end
47
+ if arg.length == 0
48
+ arg = :default, :red, :bg_default
49
+ end
50
+ attribute = { # mapper for the attributes
51
+ :normal => 0,
52
+ :bright => 1,
53
+ :dim => 2,
54
+ :underscore => 4,
55
+ :blink => 5,
56
+ :reverse => 7,
57
+ :hidden => 8,
58
+ :default => 0
59
+ }
60
+ fg_color = { # mapper for the foreground color
61
+ :black => 30,
62
+ :red => 31,
63
+ :green => 32,
64
+ :yellow => 33,
65
+ :blue => 34,
66
+ :magenta => 35,
67
+ :cyan => 36,
68
+ :white => 37,
69
+ :default => 39
70
+ }
71
+ bg_color = { # mapper for the background color
72
+ :bg_black => 40,
73
+ :bg_red => 41,
74
+ :bg_green => 42,
75
+ :bg_yellow => 43,
76
+ :bg_blue => 44,
77
+ :bg_magenta => 45,
78
+ :bg_cyan => 46,
79
+ :bg_white => 47,
80
+ :bg_default => 49
81
+ }
82
+ if arg.length > 0 # turn symbols into numbers
83
+ arg[0] = attribute[arg[0]] # attributes
84
+ end
85
+ if arg.length > 1
86
+ arg[1] = fg_color[arg[1]] # foreground color
87
+ end
88
+ if arg.length > 2
89
+ arg[2] = bg_color[arg[2]] # background color
90
+ end
91
+ "\e[#{arg.join(";")}m" # magic ansi escape sequence
92
+ end
93
+
94
+ def self.color=(arg)
95
+ if not arg.is_a?(Array)
96
+ arg = [arg]
97
+ end
98
+ if arg.length == 0
99
+ arg = :default, :red, :bg_default
100
+ end
101
+ print self.color(arg)
102
+ end
103
+
104
+ def self.width
105
+ `tput cols`.chomp.to_i
106
+ end
107
+
108
+ def self.height
109
+ `tput lines`.chomp.to_i
110
+ end
111
+ end
112
+
113
+ class Cursor
114
+ def self.position
115
+ row = ""
116
+ col = ""
117
+ c = ""
118
+
119
+ termsettings = `stty -g`
120
+ system("stty raw -echo")
121
+ print "\e[6n"
122
+ while (c = STDIN.getc.chr) != ";"
123
+ if c == "\e" or c == "["
124
+ next
125
+ else
126
+ row += c
127
+ end
128
+ end
129
+ while (c = STDIN.getc.chr) != "R"
130
+ col += c
131
+ end
132
+ system("stty #{termsettings}")
133
+
134
+ [row, col]
135
+ end
136
+
137
+ def self.position=(arg)
138
+ row = 0
139
+ col = 0
140
+ if not arg.is_a?(Array)
141
+ arg = [arg]
142
+ end
143
+ if arg.length > 0
144
+ row = arg[0]
145
+ end
146
+ if arg.length > 1
147
+ col = arg[1]
148
+ end
149
+ system "tput cup #{row} #{col}"
150
+ end
151
+
152
+ def self.up(*arg)
153
+ if arg.length == 0
154
+ arg[0] == 1
155
+ end
156
+ print "\e[#{arg[0]}A"
157
+ end
158
+
159
+ def self.down(*arg)
160
+ if arg.length == 0
161
+ arg[0] == 1
162
+ end
163
+ print "\e[#{arg[0]}B"
164
+ end
165
+
166
+ def self.right(*arg)
167
+ if arg.length == 0
168
+ arg[0] == 1
169
+ end
170
+ system "tput cuf #{arg[0]}"
171
+ end
172
+
173
+ def self.left(*arg)
174
+ if arg.length == 0
175
+ arg[0] == 1
176
+ end
177
+ system "tput cub #{arg[0]}"
178
+ end
179
+
180
+ def self.save
181
+ system "tput sc"
182
+ end
183
+
184
+ def self.restore
185
+ system "tput rc"
186
+ end
187
+ end
188
+
189
+ class Win
190
+ def initialize
191
+ @row = 1
192
+ @col = 1
193
+ @width = 10
194
+ @height = 5
195
+ @text = ""
196
+ @border = :single
197
+ @fg = :default
198
+ @bg = :bg_default
199
+ @bordercolor = :default
200
+ end
201
+
202
+ def width=(width)
203
+ @width = width
204
+ end
205
+
206
+ def width
207
+ @width
208
+ end
209
+
210
+ def height=(height)
211
+ @height = height
212
+ end
213
+
214
+ def height
215
+ @height
216
+ end
217
+
218
+ def text=(text)
219
+ @text = text
220
+ end
221
+
222
+ def text
223
+ @text
224
+ end
225
+
226
+ def border=(arg)
227
+ if not arg.is_a?(Array)
228
+ arg = [arg]
229
+ end
230
+ if arg.length > 0
231
+ @border = arg[0]
232
+ end
233
+ if arg.length > 1
234
+ @bordercolor = arg[1]
235
+ end
236
+ end
237
+
238
+ def border
239
+ [@border, @bordercolor]
240
+ end
241
+
242
+ def position=(arg)
243
+ if not arg.is_a?(Array)
244
+ arg = [arg]
245
+ end
246
+ if arg.length > 0
247
+ @row = arg[0]
248
+ end
249
+ if arg.length > 1
250
+ @col = arg[1]
251
+ end
252
+ end
253
+
254
+ def position
255
+ [@row, @col]
256
+ end
257
+
258
+ def fg=(fg)
259
+ @fg = fg
260
+ end
261
+
262
+ def fg
263
+ @fg
264
+ end
265
+
266
+ def bg=(bg)
267
+ @bg = bg
268
+ end
269
+
270
+ def bg
271
+ @bg
272
+ end
273
+
274
+ def drawBorder
275
+ single = {
276
+ :upper_left => "\u250C",
277
+ :upper_right => "\u2510",
278
+ :lower_left => "\u2514",
279
+ :lower_right => "\u2518",
280
+ :horizontal => "\u2500",
281
+ :vertical => "\u2502"
282
+ }
283
+ bold = {
284
+ :upper_left => "\u250D",
285
+ :upper_right => "\u2511",
286
+ :lower_left => "\u2515",
287
+ :lower_right => "\u2519",
288
+ :horizontal => "\u2501",
289
+ :vertical => "\u2503"
290
+ }
291
+ double = {
292
+ :upper_left => "\u2554",
293
+ :upper_right => "\u2557",
294
+ :lower_left => "\u255A",
295
+ :lower_right => "\u255D",
296
+ :horizontal => "\u2550",
297
+ :vertical => "\u2551"
298
+ }
299
+ round = {
300
+ :upper_left => "\u256D",
301
+ :upper_right => "\u256E",
302
+ :lower_left => "\u2570",
303
+ :lower_right => "\u256F",
304
+ :horizontal => "\u2500",
305
+ :vertical => "\u2502"
306
+ }
307
+ mapper = case @border
308
+ when :single then single
309
+ when :bold then bold
310
+ when :double then double
311
+ when :round then round
312
+ else single
313
+ end
314
+ (mapper[:upper_left] + mapper[:horizontal] * (@width - 2) + mapper[:upper_right]).color(:normal, @bordercolor, @bg).printAt @row, @col
315
+ (mapper[:lower_left] + mapper[:horizontal] * (@width - 2) + mapper[:lower_right]).color(:normal, @bordercolor, @bg).printAt @row + @height - 1, @col
316
+ 0.upto(@height - 3) do |i|
317
+ (mapper[:vertical] + " " * (@width - 2) + mapper[:vertical]).color(:normal, @bordercolor, @bg).printAt @row + i + 1, @col
318
+ end
319
+ end
320
+
321
+ def draw
322
+ text = @text.split("\n")
323
+ 0.upto(text.length - 1) do |i|
324
+ if @border != :none
325
+ text[i] = text[i][0, @width - 2]
326
+ else
327
+ text[i] = text[i][0, @width]
328
+ end
329
+ end
330
+ if @border == :none
331
+ 0.upto((text.length > @height ? @height : text.length) - 1) do |i|
332
+ text[i].color(:normal, @fg, @bg).printAt @row + i , @col
333
+ end
334
+ else
335
+ drawBorder
336
+ 0.upto(@text.length > @height - 3 ? @height - 3 : text.length) do |i|
337
+ text[i].to_s.color(:normal, @fg, @bg).printAt @row + i + 1, @col + 1
338
+ end
339
+ end
340
+ end
341
+ end
data/lib/rudy/aws/ec2.rb CHANGED
@@ -14,10 +14,46 @@ module Rudy::AWS
14
14
  @aws.describe_images_by_owner('self') || []
15
15
  end
16
16
 
17
+ # +id+ AMI ID to deregister (ami-XXXXXXX)
18
+ # Returns true when successful. Otherwise throws an exception.
19
+ def deregister(id)
20
+ @aws.deregister_image(id)
21
+ end
22
+
23
+ # +path+ the S3 path to the manifest (bucket/file.manifest.xml)
24
+ # Returns the AMI ID when successful, otherwise throws an exception.
25
+ def register(path)
26
+ @aws.register_image(path)
27
+ end
17
28
  end
29
+ class Snapshots
30
+ include Rudy::AWS::ObjectBase
31
+
32
+ def list
33
+ @aws.describe_snapshots || []
34
+ end
35
+
36
+ def create(vol_id)
37
+ @aws.create_snapshot(vol_id)
38
+ end
39
+
40
+ def destroy(snap_id)
41
+ @aws.delete_snapshot(snap_id)
42
+ end
43
+
44
+ def exists?(id)
45
+ list.each do |v|
46
+ return true if v[:aws_id] === id
47
+ end
48
+ false
49
+ end
50
+
51
+ end
52
+
18
53
  class Volumes
19
54
  include Rudy::AWS::ObjectBase
20
55
 
56
+
21
57
  def list
22
58
  list = @aws.describe_volumes() || []
23
59
  list.select { |v| v[:aws_status] != "deleting" }
@@ -27,10 +63,18 @@ module Rudy::AWS
27
63
  @aws.attach_volume(vol_id, inst_id, device)
28
64
  end
29
65
 
66
+ def detach(vol_id)
67
+ @aws.detach_volume(vol_id)
68
+ end
69
+
30
70
  def create(zone, size, snapshot=nil)
31
71
  @aws.create_volume(snapshot, size, zone)
32
72
  end
33
73
 
74
+ def destroy(vol_id)
75
+ @aws.delete_volume(vol_id)
76
+ end
77
+
34
78
  def exists?(id)
35
79
  list.each do |v|
36
80
  return true if v[:aws_id] === id
@@ -38,6 +82,7 @@ module Rudy::AWS
38
82
  false
39
83
  end
40
84
 
85
+
41
86
  end
42
87
 
43
88
  class Instances
@@ -41,6 +41,11 @@ module Rudy::AWS
41
41
  items
42
42
  end
43
43
 
44
+ def select(query)
45
+ list = @aws2.select(query) || []
46
+ list[0]
47
+ end
48
+
44
49
  def get_attributes(domain, item, attribute=nil)
45
50
  @aws.get_attributes(domain, item, attribute)
46
51
  end
data/lib/rudy/aws.rb CHANGED
@@ -20,6 +20,7 @@ module Rudy
20
20
  attr_reader :addresses
21
21
  attr_reader :groups
22
22
  attr_reader :volumes
23
+ attr_reader :snapshots
23
24
  attr_reader :aws
24
25
 
25
26
  def initialize(access_key, secret_key)
@@ -28,6 +29,7 @@ module Rudy
28
29
  @images = Rudy::AWS::EC2::Images.new(@aws)
29
30
  @groups = Rudy::AWS::EC2::Groups.new(@aws)
30
31
  @addresses = Rudy::AWS::EC2::Addresses.new(@aws)
32
+ @snapshots = Rudy::AWS::EC2::Snapshots.new(@aws)
31
33
  @volumes = Rudy::AWS::EC2::Volumes.new(@aws)
32
34
  end
33
35
 
@@ -51,6 +53,7 @@ module Rudy
51
53
 
52
54
  def initialize(access_key, secret_key)
53
55
  @aws = RightAws::SdbInterface.new(access_key, secret_key, {:logger => Logger.new(@@logger)})
56
+ @aws2 = AwsSdb::Service.new(:access_key_id => access_key, :secret_access_key => secret_key, :logger => Logger.new(@@logger))
54
57
  @domains = Rudy::AWS::SimpleDB::Domains.new(@aws)
55
58
  end
56
59
 
@@ -28,7 +28,7 @@ module Rudy
28
28
  attr_reader :scm
29
29
 
30
30
  attr_reader :rscripts
31
-
31
+ attr_reader :domains
32
32
  attr_reader :machine_images
33
33
 
34
34
  def init
@@ -81,8 +81,9 @@ module Rudy
81
81
  end
82
82
  end
83
83
 
84
- @sdb = Rudy::AWS::SimpleDB.new(@access_key, @secret_key)
85
84
  @ec2 = Rudy::AWS::EC2.new(@access_key, @secret_key)
85
+ # RightAws::SdbInterface is on thin ice. No select support! Or sort!
86
+ @sdb = Rudy::AWS::SimpleDB.new(@access_key, @secret_key)
86
87
  #@s3 = Rudy::AWS::SimpleDB.new(@access_key, @secret_key)
87
88
  end
88
89
 
@@ -90,7 +91,7 @@ module Rudy
90
91
  # not have a valid keypair configured. (See: EC2_KEYPAIR_*)
91
92
  def check_keys
92
93
  raise "No SSH key provided for #{keypairname}!" unless has_keypair?(keypairname)
93
- raise "SSH key provided but cannot be found! (#{keypairpath})" unless File.exists?(keypairpath)
94
+ raise "SSH key provided but cannot be found! (#{keypairname}: #{keypairpath})" unless File.exists?(keypairpath)
94
95
  end
95
96
 
96
97
  def has_pem_keys?
@@ -157,10 +158,8 @@ module Rudy
157
158
  raise "#{disk.device} is already in use on #{machine[:aws_instance_id]}! (Try umounting it)"
158
159
  end
159
160
 
160
- new_volume = false
161
161
  if !disk.awsid || (disk.awsid && !@ec2.volumes.exists?(disk.awsid))
162
162
  disk = Rudy::MetaData::Disk.update_volume(@sdb, @ec2, disk, machine)
163
- new_volume = true
164
163
  end
165
164
 
166
165
  raise "Unknown error creating volume! #{disk.awsid}" unless disk && disk.awsid
@@ -171,11 +170,16 @@ module Rudy
171
170
 
172
171
  @user = "root"
173
172
 
174
- if new_volume
173
+ if disk.raw_volume
175
174
  puts "Creating the filesystem (mkfs.ext3 -F #{disk.device})"
176
175
  capture(:stdout) do
177
176
  ssh machine[:dns_name], keypairpath, user, "mkfs.ext3 -F #{disk.device}"
178
177
  end
178
+
179
+ puts "Saving disk metadata"
180
+ disk.raw_volume = false
181
+ Rudy::MetaData::Disk.save(@sdb, disk)
182
+
179
183
  sleep 2
180
184
  end
181
185
 
@@ -193,26 +197,25 @@ module Rudy
193
197
  # +cmd+ is the name of the command current running.
194
198
  def print_header(cmd=nil)
195
199
  title = "RUDY v#{Rudy::VERSION}"
196
- title << " -- #{@alias}" if @alias
197
- puts title, $/
198
-
200
+ now_utc = Time.now.utc
201
+ criteria = []
202
+ [:zone, :environment, :role, :position].each do |n|
203
+ val = instance_variable_get("@#{n}")
204
+ criteria << "#{n.to_s.color :normal}:#{val.color :bright}"
205
+ end
206
+ puts '%s -- %s' % [title, criteria.join(", ")]
207
+ puts 'UTC: %s' % now_utc.strftime("%Y-%m-%d %H:%M:%S")
208
+ puts
209
+
199
210
  if (@environment == "prod")
200
- puts %q(=======================================================
211
+ msg = %q(=======================================================
201
212
  =======================================================
202
213
  !!!!!!!!! YOU ARE PLAYING WITH PRODUCTION !!!!!!!!!
203
214
  =======================================================
204
- =======================================================
205
-
206
- )
207
- end
208
-
209
- criteria = []
210
- [:zone, :environment, :role, :position].each do |n|
211
- val = instance_variable_get("@#{n}")
212
- criteria << "[#{n} = #{val}]"
215
+ =======================================================)
216
+ puts msg.color(:bright, :red, :bg_white)
217
+ puts
213
218
  end
214
- puts criteria.join(" and ")
215
- puts
216
219
 
217
220
  end
218
221
 
@@ -220,6 +223,16 @@ module Rudy
220
223
 
221
224
  end
222
225
 
226
+ # Returns a hash of info for the requested machine. If the requested machine
227
+ # is not running, it will raise an exception.
228
+ def find_current_machine
229
+ machine_list = @ec2.instances.list(machine_group)
230
+ machine = machine_list.values.first # NOTE: Only one machine per group, for now...
231
+ raise "There's no machine running in #{machine_group}" unless machine
232
+ raise "The primary machine in #{machine_group} is not in a running state" unless machine[:aws_state] == 'running'
233
+
234
+ machine
235
+ end
223
236
 
224
237
 
225
238
  def group_metadata(env=@environment, role=@role)
@@ -242,17 +255,28 @@ module Rudy
242
255
 
243
256
  def print_image(img)
244
257
  puts '-'*60
245
- puts "Image: #{img[:aws_location]}"
258
+ puts "Image: #{img[:aws_id]}"
246
259
  img.each_pair do |key, value|
247
260
  printf(" %22s: %s#{$/}", key, value) if value
248
261
  end
249
262
  puts
250
263
  end
251
264
 
252
- def print_disk(disk)
265
+ def print_disk(disk, backups=[])
253
266
  puts '-'*60
254
267
  puts "Disk: #{disk.name}"
255
268
  puts disk.to_s
269
+ puts "#{backups.size} most recent backups:", backups.collect { |back| "#{back.nice_time} (#{back.awsid})" }
270
+ puts
271
+ end
272
+
273
+
274
+ def print_volume(vol, disk)
275
+ puts '-'*60
276
+ puts "Volume: #{vol[:aws_id]} (disk: #{disk.name})"
277
+ vol.each_pair do |key, value|
278
+ printf(" %22s: %s#{$/}", key, value) if value
279
+ end
256
280
  puts
257
281
  end
258
282