rudy 0.2.4 → 0.3.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/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