judo 0.4.4 → 0.5.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.
@@ -0,0 +1,157 @@
1
+ module Judo
2
+ module CLIHelpers
3
+ def default_group_dir
4
+ File.expand_path(File.dirname(__FILE__) + "/../../default")
5
+ end
6
+
7
+ def do_generate(judo, args)
8
+ name = args.first
9
+ if File.exists?(name)
10
+ puts "group already exists at /#{name}"
11
+ else
12
+ FileUtils.cp_r(default_group_dir, name)
13
+ end
14
+ end
15
+
16
+ def each_server(judo, args, &blk)
17
+ raise JudoError, "No servers specified - use :all for all servers" if args.empty?
18
+ servers = judo.find_servers_by_name_or_groups_with_not(args)
19
+ servers.each do |server|
20
+ begin
21
+ blk.call(server)
22
+ rescue JudoInvalid => e
23
+ puts "#{server} - #{e.message}"
24
+ end
25
+ end
26
+ end
27
+
28
+ def mk_server_names(judo, args, &blk)
29
+ args.each do |arg|
30
+ name,group = arg.split(":")
31
+ raise JudoError, "You must specify a group on create and launch" unless group
32
+ names = if name =~ /^[12345]$/
33
+ (1..(name.to_i)).each do
34
+ blk.call(judo.mk_server_name(group), group)
35
+ end
36
+ elsif name == ""
37
+ blk.call(judo.mk_server_name(group), group)
38
+ elsif name =~ /^\d+$/
39
+ raise JudoError, "You can batch-create between 1 and 5 servers" if count < 1 or count > 5
40
+ else
41
+ blk.call(name, group)
42
+ end
43
+ end
44
+ end
45
+
46
+ def do_commit(judo, args)
47
+ name = args.first
48
+ if name =~ /:(.+)$/
49
+ group = judo.get_group($1)
50
+ else
51
+ raise JudoError, "Invalid group name '#{name}'"
52
+ end
53
+ group.commit
54
+ end
55
+
56
+ def mk_servers(judo, options, args, start)
57
+ mk_server_names(judo, args) do |name, group|
58
+ begin
59
+ server = judo.create_server(name, group, options)
60
+ server.start(options) if start
61
+ rescue JudoInvalid => e
62
+ puts "#{server} - #{e.message}"
63
+ end
64
+ end
65
+ end
66
+
67
+ def do_groups(judo)
68
+ printf " SERVER GROUPS\n"
69
+ judo.groups.each do |g|
70
+ printf "%-18s %s servers\n", g.name, g.servers.size
71
+ end
72
+ end
73
+
74
+ def do_volumes(judo)
75
+ vols = judo.volumes.sort { |a,b| [ a[:assigned_to].to_s, a[:instance_id].to_s ] <=> [ b[:assigned_to].to_s, b[:instance_id].to_s ] }
76
+
77
+ format = "%13s %6s %12s %-10s %-16s %-16s\n"
78
+ printf format, "AWS_ID", "SIZE", "AWS_STATUS", "AWS_DEVICE", "ATTACHED TO", "CONFIGURED FOR"
79
+ printf "%s\n", ("-" * 80)
80
+
81
+ vols.each do |v|
82
+ attached = v[:attached_to] ? v[:attached_to].name : v[:instance_id]
83
+ assigned = v[:assigned_to] ? v[:assigned_to].name : ""
84
+ printf format, v[:id], v[:size], v[:status], v[:device], attached, assigned
85
+ end
86
+ end
87
+
88
+ def do_ips(judo)
89
+ ips = judo.ips.sort { |a,b| a[:assigned_to].to_s <=> b[:assigned_to].to_s }
90
+
91
+ format = "%15s %20s %20s\n"
92
+ printf format, "IP", "ATTACHED TO", "CONFIGURED FOR"
93
+ printf "%s\n", ("-"*57)
94
+
95
+ ips.each do |ip|
96
+ attached = ip[:attached_to] ? ip[:attached_to].name : ip[:instance_id]
97
+ assigned = ip[:assigned_to] ? ip[:assigned_to].name : ""
98
+ printf format, ip[:ip], attached, assigned
99
+ end
100
+ end
101
+
102
+ def do_snapshots(judo, args)
103
+ printf " SNAPSHOTS\n"
104
+ printf "%s\n", ("-" * 80)
105
+ judo.snapshots.each do |snapshot|
106
+ printf "%-15s %-25s %-15s %-10s %s\n", snapshot.name, snapshot.server_name, snapshot.group_name, snapshot.version_desc, "ebs:#{snapshot.ec2_ids.size}"
107
+ end
108
+ end
109
+
110
+ def do_list(judo, args)
111
+ printf " SERVERS\n"
112
+ printf "%s\n", ("-" * 80)
113
+ args << ":all" if args.empty?
114
+ each_server(judo,args) do |s|
115
+ printf "%-32s %-12s %-7s %-11s %-11s %-10s %-3s %s\n", s.name, s.group.name, s.version_desc, s.get("instance_id"), s.size_desc, s.ec2_state, "ebs:#{s.volumes.keys.size}", s.has_ip? ? "ip" : ""
116
+ end
117
+ end
118
+
119
+ def sub_info(header, data, &block)
120
+ return if data == []
121
+ return if data == {}
122
+ return if data.nil?
123
+ puts " [ #{header} ]"
124
+ [ data ].flatten.each do |d|
125
+ block.call(d)
126
+ end
127
+ end
128
+
129
+ def do_info(judo, server)
130
+ puts "[ #{server} ]"
131
+ printf " %-24s: %s\n", "ID", server.id
132
+ printf " %-24s: %s\n", "Group", server.group.name
133
+ printf " %-24s: %s\n", "Animated From", server.clone if server.clone
134
+ printf " %-24s: %s\n", "Elastic Ip", server.elastic_ip if server.elastic_ip
135
+ sub_info("EC2", server.ec2_instance) do |i|
136
+ [:aws_instance_id, :ssh_key_name, :aws_availability_zone, :aws_state, :aws_image_id, :dns_name, :aws_instance_type, :private_dns_name, :aws_launch_time, :aws_groups ].each do |k|
137
+ printf " %-24s: %s\n",k, i[k]
138
+ end
139
+ end
140
+ sub_info("METADATA", server.metadata.keys) do |key|
141
+ printf(" %-24s: %s\n", key, server.metadata[key] )
142
+ end
143
+ sub_info("VOLUMES", server.ec2_volumes) do |v|
144
+ printf " %-13s %-10s %-10s %4d %-10s %-8s\n",
145
+ v[:aws_id],
146
+ v[:aws_status],
147
+ v[:zone],
148
+ v[:aws_size],
149
+ v[:aws_attachment_status],
150
+ v[:aws_device]
151
+ end
152
+ sub_info("SNAPSHOTS", server.snapshots) do |s|
153
+ printf " %-10s %-15s %-8s %-5s\n", s.name, s.group_name, s.version_desc, "#{s.ec2_ids.size}v"
154
+ end
155
+ end
156
+ end
157
+ end
@@ -19,12 +19,8 @@ module Judo
19
19
  (@config ||= {})[version] ||= load_config(version)
20
20
  end
21
21
 
22
- def server_ids
23
- @server_ids ||= (@base.groups_config[@name] || [])
24
- end
25
-
26
22
  def servers
27
- @base.servers.select { |s| server_ids.include?(s.id) }
23
+ @base.servers.select { |s| s.group_name == name }
28
24
  end
29
25
 
30
26
  def load_userdata(version)
@@ -40,28 +36,32 @@ module Judo
40
36
  end
41
37
 
42
38
  def set_version
43
- @base.sdb.put_attributes(@base.base_domain, "group_versions", { name => version }, :replace)
39
+ @base.sdb.put_attributes(@base.base_domain,
40
+ "group_versions", {name => version}, :replace)
41
+ end
42
+
43
+ def check_conf(conf)
44
+ if conf["import"]
45
+ raise JudoError, "config option 'import' no longer supported"
46
+ end
44
47
  end
45
48
 
46
- def compile
47
- raise JudoError, "Group name :all is reserved" if name == "all"
49
+ def commit
50
+ raise JudoError, "Group name all is reserved" if name == "all"
48
51
  @base.task("Compiling #{self} version #{version + 1}") do
49
52
  @version = version + 1
50
- raise JudoError, "can not find group folder #{dir}" unless File.exists?(dir)
51
- conf = JSON.parse(read_file('config.json'))
52
- raise JudoError, "config option 'import' no longer supported" if conf["import"]
53
+ conf = JSON.parse(File.read("#{name}/config.json"))
54
+ userdata = File.read("#{name}/userdata.erb")
53
55
  tar = tar_file(@version)
54
- Dir.chdir(@base.repo) do |d|
55
- puts ""
56
- system "tar czvf #{tar} #{name}"
57
- puts "Uploading config to s3..."
58
- @base.s3_put(version_config_file(@version), conf.to_json)
59
- puts "Uploading userdata.erb to s3..."
60
- @base.s3_put(version_userdata_file(@version), read_file('userdata.erb'))
61
- puts "Uploading tar file to s3..."
62
- @base.s3_put(tar, File.new(tar).read(File.stat(tar).size))
63
- File.delete(tar)
64
- end
56
+ puts ""
57
+ system "tar czvf #{tar} #{name}"
58
+ puts "Uploading config to s3..."
59
+ @base.s3_put(version_config_file(@version), conf.to_json)
60
+ puts "Uploading userdata.erb to s3..."
61
+ @base.s3_put(version_userdata_file(@version), userdata)
62
+ puts "Uploading tar file to s3..."
63
+ @base.s3_put(tar, File.new(tar).read(File.stat(tar).size))
64
+ File.delete(tar)
65
65
  set_version
66
66
  end
67
67
  end
@@ -82,37 +82,17 @@ module Judo
82
82
  @url = @base.s3_url(tar_file(version))
83
83
  end
84
84
 
85
-
86
85
  def destroy
87
- servers.each { |s| s.destroy }
88
- @base.task("Destring #{self}") do
86
+ @base.task("Destroying servers in group #{self}") do
87
+ servers.each { |s| s.destroy }
88
+ end
89
+ @base.task("Destroying group #{self}") do
89
90
  @base.groups.delete(self)
90
- @base.sdb.delete_attributes(@base.base_domain, "groups", [ name ])
91
91
  @base.sdb.delete_attributes(@base.base_domain, "group_versions", [ name ])
92
92
  end
93
93
  end
94
94
 
95
- def dir
96
- "#{@base.repo}/#{name}/"
97
- end
98
-
99
- def default_userdata_file
100
- File.expand_path(File.dirname(__FILE__) + "/../../default/userdata.erb")
101
- end
102
-
103
- def read_file(name)
104
- File.read("#{dir}/#{name}")
105
- rescue Errno::ENOENT
106
- default = @base.default_file(name)
107
- puts "File #{name} not found: using #{default} instead"
108
- File.read default
109
- end
110
-
111
- def delete_server(server)
112
- sdb.delete_attributes(@base.base_domain, "groups", name => server.id)
113
- end
114
-
115
- def displayname
95
+ def displayname
116
96
  ":#{name}"
117
97
  end
118
98
 
@@ -120,16 +100,8 @@ module Judo
120
100
  displayname
121
101
  end
122
102
 
123
- def sdb
124
- @base.sdb
125
- end
126
-
127
103
  def version_desc(v)
128
- if v == version
129
- "v#{v}"
130
- else
131
- "v#{v}/#{version}"
132
- end
104
+ v == version ? "v#{v}" : "v#{v}/#{version}"
133
105
  end
134
106
  end
135
107
  end
@@ -0,0 +1,13 @@
1
+ module Aws
2
+ class Ec2
3
+ def describe_snapshots(list=[], opts={})
4
+ params = {}
5
+ params.merge!(hash_params('SnapshotId',list.to_a))
6
+ params.merge!(hash_params('Owner', [opts[:owner]])) if opts[:owner]
7
+ link = generate_request("DescribeSnapshots", params)
8
+ request_cache_or_info :describe_snapshots, link, QEc2DescribeSnapshotsParser, @@bench, list.blank?
9
+ rescue Exception
10
+ on_exception
11
+ end
12
+ end
13
+ end
@@ -1,38 +1,33 @@
1
1
  module Judo
2
2
  class Server
3
- attr_accessor :id
3
+ attr_accessor :base, :id, :group_name
4
4
 
5
- def initialize(base, id, group, version = nil)
5
+ def initialize(base, id, group_name, version = nil)
6
6
  @base = base
7
7
  @id = id
8
- @group = group
9
- @judo_domain = base.domain
8
+ @group_name = group_name
10
9
  end
11
10
 
12
11
  def create(name, options)
13
12
  raise JudoError, "group '#{group_name}' does not exists" unless group
14
-
15
- options[:virgin] = true if options[:virgin].nil?
16
-
17
- snapshots = options[:snapshots]
18
- metadata = options[:metadata]
19
- ip = options[:elastic_ip] ## if the ip was allocated beforehand
20
- virgin = options[:virgin] ## should the server init?
21
- clone = options[:clone] ## if it was cloned from a snapshot
22
-
23
- version = options[:version]
24
- version ||= group.version
25
-
26
13
  raise JudoError, "there is already a server named #{name}" if @base.servers.detect { |s| s.name == name and s != self}
27
14
  raise JudoError, "there is already a server with id #{id}" if @base.servers.detect { |s| s.id == id and s != self}
28
15
 
16
+ virgin = !(options[:virgin] == false)
17
+ snapshots = options[:snapshots]
18
+ metadata = options[:metadata]
19
+ ip = options[:elastic_ip]
20
+ clone = options[:clone]
21
+ instance_type = options[:instance_type]
22
+ version = options[:version] || group.version
23
+
29
24
  task("Creating server #{name}") do
30
25
  update("name" => name, "group" => group_name,
31
26
  "virgin" => virgin, "elastic_ip" => ip,
32
- "secret" => new_secret, "version" => version,
33
- "clone" => clone, "created_at" => Time.now.to_i)
27
+ "version" => version, "clone" => clone,
28
+ "instance_type" => instance_type,
29
+ "created_at" => Time.now.to_i)
34
30
  set_metadata(metadata) if metadata
35
- @base.sdb.put_attributes(@base.base_domain, "groups", group_name => id)
36
31
  end
37
32
 
38
33
  allocate_disk(snapshots)
@@ -42,19 +37,13 @@ module Judo
42
37
  end
43
38
 
44
39
  def set_metadata(new_metadata)
45
- puts new_metadata.inspect
46
- clean_metadata = new_metadata.inject({}) { |buf,(k,v)|
47
- puts [ buf, k , v ].inspect
48
- buf[k.to_s] = v.to_s; buf }
49
- puts clean_metadata.inspect
50
- keys_to_remove = clean_metadata.keys - clean_metadata.keys
51
- @metadata = clean_metadata
52
- update encode_metadata(clean_metadata) unless clean_metadata.empty?
53
- keys_to_remove.each { |key| remove key }
54
- end
55
-
56
- def encode_metadata(data)
57
- data.inject({}) { |buf,(key,value)| buf["metadata_#{key.to_s}"] = [value].to_json; buf }
40
+ new_metadata_json = new_metadata.to_json
41
+ if (new_metadata_json.size > 1000)
42
+ raise "metadata json too big: '#{new_metadata_json}'"
43
+ else
44
+ @metadata = new_metadata
45
+ update("metadata_json" => new_metadata_json)
46
+ end
58
47
  end
59
48
 
60
49
  def metadata
@@ -62,16 +51,12 @@ module Judo
62
51
  end
63
52
 
64
53
  def get_metadata
65
- state.inject({}) do |buf,(key,value)|
66
- if key =~ /^metadata_(.*)/
67
- buf[$1] = JSON.load(value.first)
68
- end
69
- buf
70
- end
54
+ metadata_json = get("metadata_json")
55
+ JSON.load(metadata_json) if metadata_json
71
56
  end
72
57
 
73
58
  def group
74
- @group_obj ||= @base.groups.detect { |g| g.name == @group }
59
+ @group ||= @base.groups.detect { |g| g.name == @group_name }
75
60
  end
76
61
 
77
62
  def fetch_state
@@ -98,10 +83,6 @@ module Judo
98
83
  get "name"
99
84
  end
100
85
 
101
- def data
102
- get "data"
103
- end
104
-
105
86
  def instance_id
106
87
  get "instance_id"
107
88
  end
@@ -138,10 +119,6 @@ module Judo
138
119
  end
139
120
  end
140
121
 
141
- def note
142
- get("note")
143
- end
144
-
145
122
  def clone
146
123
  get("clone")
147
124
  end
@@ -158,10 +135,6 @@ module Judo
158
135
  get("virgin").to_s == "true" ## I'm going to set it to true and it will come back from the db as "true" -> could be "false" or false or nil also
159
136
  end
160
137
 
161
- def secret
162
- get "secret"
163
- end
164
-
165
138
  def snapshots
166
139
  @base.snapshots.select { |s| s.server == self }
167
140
  end
@@ -197,15 +170,13 @@ module Judo
197
170
  end
198
171
 
199
172
  def delete
200
- group.delete_server(self) if group
201
173
  @base.sdb.delete_attributes(@base.server_domain, id)
202
174
  end
203
175
 
204
176
  ######## end simple DB access #######
205
177
 
206
178
  def instance_type
207
- ## need instance_size to be backward compatible - needed for configs made before v 0.3.0
208
- get("instance_type") || config["instance_type"] || config["instance_size"] || "m1.small"
179
+ get("instance_type")
209
180
  end
210
181
 
211
182
  def config
@@ -245,7 +216,6 @@ module Judo
245
216
  size = volume_config["size"]
246
217
  if not volumes[device]
247
218
  task("Creating EC2 Volume #{device} #{size}") do
248
- ### EC2 create_volume
249
219
  volume_id = @base.ec2.create_volume(nil, size, config["availability_zone"])[:aws_id]
250
220
  add_volume(volume_id, device)
251
221
  end
@@ -262,7 +232,6 @@ module Judo
262
232
  def allocate_ip
263
233
  begin
264
234
  if config["elastic_ip"] and not elastic_ip
265
- ### EC2 allocate_address
266
235
  task("Adding an elastic ip") do
267
236
  ip = @base.ec2.allocate_address
268
237
  add_ip(ip)
@@ -313,18 +282,16 @@ module Judo
313
282
 
314
283
  def destroy
315
284
  stop if running?
316
- ### EC2 release_address
317
285
  task("Deleting Elastic Ip") { remove_ip } if has_ip?
318
286
  volumes.each { |dev,v| remove_volume(v,dev) }
319
287
  task("Destroying server #{name}") { delete }
320
288
  end
321
289
 
322
290
  def ec2_state
323
- ec2_instance[:aws_state] rescue "offline"
291
+ ec2_instance[:aws_state]
324
292
  end
325
293
 
326
294
  def ec2_instance
327
- ### EC2 describe_instances
328
295
  @base.ec2_instances.detect { |e| e[:aws_instance_id] == instance_id } or {}
329
296
  end
330
297
 
@@ -334,7 +301,7 @@ module Judo
334
301
  end
335
302
 
336
303
  def start(options = {})
337
- @boot= options[:boot]
304
+ @boot = options[:boot]
338
305
  new_version = options[:version]
339
306
  set_instance_type(options[:instance_type]) if options[:instance_type]
340
307
 
@@ -385,15 +352,12 @@ module Judo
385
352
  end
386
353
 
387
354
  def launch_ec2
388
- # validate
389
-
390
- ## EC2 launch_instances
391
355
  ud = user_data
392
356
  debug(ud)
393
357
  result = @base.ec2.launch_instances(ami,
394
358
  :instance_type => instance_type,
395
359
  :availability_zone => config["availability_zone"],
396
- :key_name => @base.key_name,
360
+ :key_name => "judo",
397
361
  :group_ids => security_groups,
398
362
  :user_data => ud).first
399
363
  update "instance_id" => result[:aws_instance_id], "virgin" => false, "started_at" => Time.now.to_i
@@ -472,17 +436,21 @@ module Judo
472
436
  end
473
437
  end
474
438
 
439
+ def ssh?
440
+ begin
441
+ Timeout::timeout(2) do
442
+ TCPSocket.new(hostname, 22)
443
+ true
444
+ end
445
+ rescue SocketError, Timeout::Error, Errno::ECONNREFUSED, Errno::EHOSTUNREACH
446
+ false
447
+ end
448
+ end
449
+
475
450
  def wait_for_ssh
476
451
  invalid "not running" unless running?
477
452
  loop do
478
- begin
479
- reload
480
- Timeout::timeout(4) do
481
- TCPSocket.new(hostname, 22)
482
- return
483
- end
484
- rescue SocketError, Timeout::Error, Errno::ECONNREFUSED, Errno::EHOSTUNREACH
485
- end
453
+ ssh? ? return : sleep(1)
486
454
  end
487
455
  end
488
456
 
@@ -493,7 +461,6 @@ module Judo
493
461
 
494
462
  def attach_ip
495
463
  return unless running? and elastic_ip
496
- ### EC2 associate_address
497
464
  @base.ec2.associate_address(instance_id, elastic_ip)
498
465
  end
499
466
 
@@ -508,14 +475,12 @@ module Judo
508
475
  def attach_volumes
509
476
  return unless running?
510
477
  volumes.each do |device,volume_id|
511
- ### EC2 attach_volume
512
478
  @base.ec2.attach_volume(volume_id, instance_id, device)
513
479
  end
514
480
  end
515
481
 
516
482
  def remove_volume(volume_id, device)
517
483
  task("Deleting #{device} #{volume_id}") do
518
- ### EC2 delete_volume
519
484
  @base.ec2.delete_volume(volume_id)
520
485
  remove "volumes", "#{device}:#{volume_id}"
521
486
  end
@@ -534,14 +499,14 @@ module Judo
534
499
  def ssh_command(cmd)
535
500
  wait_for_ssh
536
501
  @base.keypair_file do |file|
537
- system "ssh -i #{file} #{config["user"]}@#{hostname} '#{cmd}'"
502
+ system "ssh -q -i #{file} #{config["user"]}@#{hostname} '#{cmd}'"
538
503
  end
539
504
  end
540
505
 
541
506
  def connect_ssh
542
507
  wait_for_ssh
543
508
  @base.keypair_file do |file|
544
- system "ssh -i #{file} #{config["user"]}@#{hostname}"
509
+ system "ssh -q -i #{file} #{config["user"]}@#{hostname}"
545
510
  end
546
511
  end
547
512
 
@@ -568,26 +533,6 @@ module Judo
568
533
  group.s3_url(version)
569
534
  end
570
535
 
571
- def domain
572
- @base.domain
573
- end
574
-
575
- def validate
576
- ### EC2 create_security_group
577
- @base.create_security_group
578
-
579
- ### EC2 desctibe_key_pairs
580
- k = @base.ec2.describe_key_pairs.detect { |kp| kp[:aws_key_name] == config["key_name"] }
581
-
582
- if k.nil?
583
- if config["key_name"] == "judo"
584
- @base.create_keypair
585
- else
586
- raise "cannot use key_pair #{config["key_name"]} b/c it does not exist"
587
- end
588
- end
589
- end
590
-
591
536
  def snapshot(name)
592
537
  snap = @base.new_snapshot(name, id)
593
538
  snap.create
@@ -630,14 +575,6 @@ module Judo
630
575
  [group.to_s, name.to_s] <=> [s.group.to_s, s.name.to_s]
631
576
  end
632
577
 
633
- def new_secret
634
- rand(2 ** 128).to_s(36)
635
- end
636
-
637
- def group_name
638
- @group
639
- end
640
-
641
578
  def boot
642
579
  @boot || {}
643
580
  end