judo 0.1.0 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- data/TODO +45 -0
- data/VERSION +1 -1
- data/bin/judo +39 -6
- data/lib/judo.rb +3 -0
- data/lib/judo/base.rb +81 -9
- data/lib/judo/commandline_helpers.rb +15 -5
- data/lib/judo/group.rb +17 -47
- data/lib/judo/server.rb +84 -59
- data/lib/judo/setup.rb +2 -1
- data/lib/judo/snapshot.rb +108 -0
- metadata +6 -3
data/TODO
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
### NEEDED for new gem launch
|
2
|
+
|
3
|
+
### [ ] judo snapshots
|
4
|
+
### [ ] judo snapshot foo bar
|
5
|
+
### [ ] judo clone foo bar2
|
6
|
+
### [ ] snapshots/init/virgin
|
7
|
+
### [ ] judo stop --force
|
8
|
+
### [ ] judo swap (x) (y) -> swaps elastic IP's and name
|
9
|
+
### [ ] judo swap_ip (x) (y) -> swaps elastic IP's and name
|
10
|
+
|
11
|
+
### [ ] judo does not work with ruby 1.8.6 - :(
|
12
|
+
### [ ] saw a timeout on volume allocation - make sure we build in re-tries - need to allocate the server all together as much as possible
|
13
|
+
### [ ] there is a feature to allow for using that block_mapping feature - faster startup
|
14
|
+
|
15
|
+
### [ ] two phase delete (1 hr)
|
16
|
+
### [-] refactor availability_zone (2 hrs)
|
17
|
+
### [ ] pick availability zone from config "X":"Y" or "X":["Y","Z"]
|
18
|
+
### [ ] assign to state on creation ( could delay till volume creation )
|
19
|
+
### [ ] implement auto security_group creation and setup (6 hrs)
|
20
|
+
### [ ] bind kuzushi gem version version
|
21
|
+
|
22
|
+
### [ ] should be able to do ALL actions except commit without the repo!
|
23
|
+
### [ ] store git commit hash with commit to block a judo commit if there is newer material stored
|
24
|
+
### [ ] remove the tarball - store files a sha hashes in the bucket - makes for faster commits if the files have not changed
|
25
|
+
|
26
|
+
### [ ] use a logger service (1 hr)
|
27
|
+
### [ ] write specs (5 hr)
|
28
|
+
|
29
|
+
### Error Handling
|
30
|
+
### [ ] no availability zone before making disks
|
31
|
+
### [ ] security group does not exists
|
32
|
+
|
33
|
+
### Do Later
|
34
|
+
### [ ] use amazon's new conditional write tools so we never have problems from concurrent updates
|
35
|
+
### [ ] is thor really what we want to use here?
|
36
|
+
### [ ] need to be able to pin a config to a version of kuzushi - gem updates can/will break a lot of things
|
37
|
+
### [ ] I want a "judo monitor" command that will make start servers if they go down, and poke a listed port to make sure a service is listening, would be cool if it also detects wrong ami, wrong secuirity group, missing/extra volumes, missing/extra elastic_ip - might not want to force a reboot quite yet in these cases
|
38
|
+
### [ ] Implement "judo snapshot [NAME]" to take a snapshot of the ebs's blocks
|
39
|
+
### [ ] ruby 1.9.1 support
|
40
|
+
### [ ] find a good way to set the hostname or prompt to :name
|
41
|
+
### [ ] remove fog/s3 dependancy
|
42
|
+
### [ ] enforce template files end in .erb to make room for other possible templates as defined by the extensions
|
43
|
+
### [ ] zerigo integration for automatic DNS setup
|
44
|
+
### [ ] How cool would it be if this was all reimplemented in eventmachine and could start lots of boxes in parallel? Would need to evented AWS api calls... Never seen a library to do that - would have to write our own... "Fog Machine?"
|
45
|
+
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.4
|
data/bin/judo
CHANGED
@@ -30,6 +30,11 @@ Usage: judo launch [options] SERVER ...
|
|
30
30
|
|
31
31
|
judo commit [options] GROUP
|
32
32
|
|
33
|
+
judo snapshot [options] SERVER SNAPSHOT ## take an ebs snapshot of a server
|
34
|
+
judo snapshots [options] [SERVER ...] ## show current snapshots on servers
|
35
|
+
judo clone [options] SNAPSHOT SERVER ## create a new server from a snapshot
|
36
|
+
judo erase [options] SNAPSHOT ## erase an old snapshot
|
37
|
+
|
33
38
|
judo info [options] [SERVER ...]
|
34
39
|
judo console [options] [SERVER ...] ## shows AWS console output
|
35
40
|
judo ssh [options] [SERVER ...] ## ssh's into the server
|
@@ -53,6 +58,9 @@ banner
|
|
53
58
|
defaults[:judo_dir] = dir
|
54
59
|
defaults.merge(Judo::Base.default_options(Dir.pwd, dir))
|
55
60
|
end
|
61
|
+
opts.on( '-f', '--force', 'Force a stop or restart (immediately force detach volumes)' ) do
|
62
|
+
options[:force] = true
|
63
|
+
end
|
56
64
|
opts.on( '-r', '--repo DIR', 'Specify the location of the repo dir' ) do |dir|
|
57
65
|
options[:repo] = dir
|
58
66
|
end
|
@@ -68,6 +76,9 @@ banner
|
|
68
76
|
opts.on( '-g', '--group GROUP', 'Specify the default group of the repo dir' ) do |group|
|
69
77
|
options[:group] = group
|
70
78
|
end
|
79
|
+
opts.on( '-v', '--version VERSION', 'Update the servers config version on create/start/launch' ) do |version|
|
80
|
+
options[:version] = version
|
81
|
+
end
|
71
82
|
opts.on( '-h', '--help', 'Display this screen' ) do
|
72
83
|
puts opts
|
73
84
|
exit
|
@@ -77,6 +88,7 @@ end
|
|
77
88
|
optparse.parse!
|
78
89
|
|
79
90
|
judo = Judo::Base.new(defaults.merge(options))
|
91
|
+
judo.check_version
|
80
92
|
|
81
93
|
begin
|
82
94
|
case action
|
@@ -85,14 +97,35 @@ begin
|
|
85
97
|
when "list" then do_list(judo, ARGV)
|
86
98
|
when "groups" then do_groups(judo)
|
87
99
|
when "info" then find_servers(judo, ARGV) { |s| do_info(judo, s) }
|
88
|
-
when "console" then find_servers(judo, ARGV) { |s| puts
|
100
|
+
when "console" then find_servers(judo, ARGV) { |s| puts s.console_output }
|
89
101
|
when "commit" then find_groups(judo, ARGV) { |g| g.compile }
|
90
102
|
when "ssh" then find_servers(judo, ARGV) { |s| s.connect_ssh }
|
91
|
-
when "start" then find_servers(judo, ARGV) { |s| s.start }
|
92
|
-
when "restart" then find_servers(judo, ARGV) { |s| s.restart }
|
93
|
-
when "stop" then find_servers(judo, ARGV) { |s| s.stop }
|
94
|
-
when "create" then mk_servers(judo, ARGV) { |s| s.create }
|
95
|
-
when "launch" then mk_servers(judo, ARGV) { |s| s.create; s.start }
|
103
|
+
when "start" then find_servers(judo, ARGV) { |s| s.start(options[:version]) }
|
104
|
+
when "restart" then find_servers(judo, ARGV) { |s| s.restart(options[:force]) }
|
105
|
+
when "stop" then find_servers(judo, ARGV) { |s| s.stop(options[:force]) }
|
106
|
+
when "create" then mk_servers(judo, ARGV) { |s| s.create(options[:version]) }
|
107
|
+
when "launch" then mk_servers(judo, ARGV) { |s| s.create(options[:version]); s.start }
|
108
|
+
when "snapshots" then do_snapshots(judo, ARGV)
|
109
|
+
when "erase" then
|
110
|
+
raise JudoError, "usage: judo erase SNAPSHOT" unless ARGV.size == 1
|
111
|
+
snapshot_name = ARGV.shift
|
112
|
+
judo.snapshots.select { |s| s.name == snapshot_name }.each { |s| s.destroy }
|
113
|
+
when "snapshot" then
|
114
|
+
raise JudoError, "usage: judo snapshot SERVER SNAPSHOT" unless ARGV.size == 2
|
115
|
+
server_name = ARGV.shift
|
116
|
+
snapshot_name = ARGV.shift
|
117
|
+
servers = find_servers(judo, [server_name])
|
118
|
+
raise JudoError, "You must specify a valid server to snapshot" if servers.empty?
|
119
|
+
raise JudoError, "You can only snapshot one server at a time" if servers.size > 1
|
120
|
+
servers[0].snapshot(snapshot_name)
|
121
|
+
when "clone" then
|
122
|
+
snapshot_name = ARGV.shift
|
123
|
+
raise JudoError, "You must specify a snapshot name" unless snapshot_name
|
124
|
+
new_server = ARGV.shift
|
125
|
+
raise JudoError, "You must specify a new server name" unless new_server
|
126
|
+
snapshot = judo.snapshots.detect { |s| s.name == snapshot_name }
|
127
|
+
raise JudoError, "No such snapshot #{snapshot_name}" unless snapshot
|
128
|
+
snapshot.clone(new_server,options[:version])
|
96
129
|
when "destroy" then
|
97
130
|
raise JudoError, "You must specify what servers to destroy" if ARGV.empty?
|
98
131
|
find_either(judo, ARGV) do |i|
|
data/lib/judo.rb
CHANGED
@@ -7,6 +7,8 @@ require 'right_aws'
|
|
7
7
|
require 'json'
|
8
8
|
require 'pp'
|
9
9
|
|
10
|
+
raise "Judo Currently Requires Ruby 1.8.7" unless RUBY_VERSION == "1.8.7"
|
11
|
+
|
10
12
|
class JudoError < RuntimeError ; end
|
11
13
|
class JudoInvalid < RuntimeError ; end
|
12
14
|
|
@@ -14,5 +16,6 @@ require File.dirname(__FILE__) + '/judo/base'
|
|
14
16
|
require File.dirname(__FILE__) + '/judo/config'
|
15
17
|
require File.dirname(__FILE__) + '/judo/group'
|
16
18
|
require File.dirname(__FILE__) + '/judo/server'
|
19
|
+
require File.dirname(__FILE__) + '/judo/snapshot'
|
17
20
|
require File.dirname(__FILE__) + '/judo/setup'
|
18
21
|
|
data/lib/judo/base.rb
CHANGED
@@ -1,3 +1,27 @@
|
|
1
|
+
|
2
|
+
=begin
|
3
|
+
module Aws
|
4
|
+
class Ec2
|
5
|
+
API_VERSION = "2009-11-30" ## this didnt work
|
6
|
+
def start_instances(instance_id)
|
7
|
+
link = generate_request("StartInstances", { 'InstanceId' => instance_id } )
|
8
|
+
request_info(link, QEc2TerminateInstancesParser.new(:logger => @logger))
|
9
|
+
rescue Exception
|
10
|
+
on_exception
|
11
|
+
end
|
12
|
+
|
13
|
+
def stop_instances(instance_id)
|
14
|
+
link = generate_request("StopInstances", { 'InstanceId' => instance_id } )
|
15
|
+
puts link.inspect
|
16
|
+
result = request_info(link, QEc2TerminateInstancesParser.new(:logger => @logger))
|
17
|
+
puts result.inspect
|
18
|
+
rescue Exception
|
19
|
+
on_exception
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
=end
|
24
|
+
|
1
25
|
module Judo
|
2
26
|
class Base
|
3
27
|
attr_accessor :judo_dir, :repo, :group
|
@@ -12,7 +36,7 @@ module Judo
|
|
12
36
|
end
|
13
37
|
|
14
38
|
def volumes
|
15
|
-
@volumes ||=
|
39
|
+
@volumes ||= ec2_volumes.map do |v|
|
16
40
|
{
|
17
41
|
:id => v[:aws_id],
|
18
42
|
:size => v[:aws_size],
|
@@ -69,6 +93,16 @@ module Judo
|
|
69
93
|
@sdb ||= Aws::SdbInterface.new(access_id, access_secret, :logger => Logger.new(nil))
|
70
94
|
end
|
71
95
|
|
96
|
+
def fetch_snapshots_state
|
97
|
+
s = {}
|
98
|
+
sdb.select("select * from #{Judo::Snapshot.domain}")[:items].each do |group|
|
99
|
+
group.each do |key,val|
|
100
|
+
s[key] = val
|
101
|
+
end
|
102
|
+
end
|
103
|
+
s
|
104
|
+
end
|
105
|
+
|
72
106
|
def fetch_servers_state
|
73
107
|
s = {}
|
74
108
|
sdb.select("select * from #{Judo::Server.domain}")[:items].each do |group|
|
@@ -79,10 +113,18 @@ module Judo
|
|
79
113
|
s
|
80
114
|
end
|
81
115
|
|
116
|
+
def snapshots_state
|
117
|
+
@snapshots_state ||= fetch_snapshots_state
|
118
|
+
end
|
119
|
+
|
82
120
|
def servers_state
|
83
121
|
@servers_state ||= fetch_servers_state
|
84
122
|
end
|
85
123
|
|
124
|
+
def snapshots
|
125
|
+
@snapshots ||= snapshots_state.map { |name,data| Judo::Snapshot.new(self, name, data["server"].first) }
|
126
|
+
end
|
127
|
+
|
86
128
|
def servers
|
87
129
|
@servers ||= servers_state.map { |name,data| Judo::Server.new(self, name, data["group"].first) }
|
88
130
|
end
|
@@ -92,9 +134,15 @@ module Judo
|
|
92
134
|
servers << s
|
93
135
|
s
|
94
136
|
end
|
95
|
-
|
137
|
+
|
138
|
+
def new_snapshot(name, server)
|
139
|
+
s = Judo::Snapshot.new(self, name, server)
|
140
|
+
snapshots << s
|
141
|
+
s
|
142
|
+
end
|
143
|
+
|
96
144
|
def get_group(name)
|
97
|
-
group = groups.detect { |g| g.
|
145
|
+
group = groups.detect { |g| g.name == name }
|
98
146
|
group ||= Judo::Group.new(self, name, 0)
|
99
147
|
group
|
100
148
|
end
|
@@ -119,6 +167,14 @@ module Judo
|
|
119
167
|
@ec2_instance = nil
|
120
168
|
end
|
121
169
|
|
170
|
+
def ec2_volumes
|
171
|
+
@ec2_volumes ||= ec2.describe_volumes
|
172
|
+
end
|
173
|
+
|
174
|
+
def ec2_snapshots
|
175
|
+
@ec2_snapshots ||= ec2.describe_snapshots
|
176
|
+
end
|
177
|
+
|
122
178
|
def ec2_instances
|
123
179
|
@ec2_instance ||= ec2.describe_instances
|
124
180
|
end
|
@@ -182,18 +238,34 @@ module Judo
|
|
182
238
|
end
|
183
239
|
|
184
240
|
def db_version
|
185
|
-
|
241
|
+
2
|
242
|
+
end
|
243
|
+
|
244
|
+
def upgrade_db
|
245
|
+
case get_db_version
|
246
|
+
when 0
|
247
|
+
raise JudoError, "Your db appears to be unititialized. You will need to do a judo init"
|
248
|
+
when 1
|
249
|
+
task("Upgrading Judo: Creating Snapshots SDB Domain") do
|
250
|
+
sdb.create_domain(Judo::Snapshot.domain)
|
251
|
+
set_db_version(2)
|
252
|
+
end
|
253
|
+
else
|
254
|
+
raise JduoError, "judo db is newer than the current gem - upgrade judo and try again"
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
def set_db_version(new_version)
|
259
|
+
@db_version = new_version
|
260
|
+
sdb.put_attributes("judo_config", "judo", { "dbversion" => new_version }, :replace)
|
186
261
|
end
|
187
262
|
|
188
263
|
def get_db_version
|
189
|
-
|
190
|
-
version and version.first.to_i or db_version
|
264
|
+
@db_version ||= sdb.get_attributes("judo_config", "judo")[:attributes]["dbversion"].first.to_i
|
191
265
|
end
|
192
266
|
|
193
267
|
def check_version
|
194
|
-
|
195
|
-
raise JduoError, "judo db is newer than the current gem - upgrade judo and try again" if get_db_version > db_version
|
268
|
+
upgrade_db if get_db_version != db_version
|
196
269
|
end
|
197
|
-
|
198
270
|
end
|
199
271
|
end
|
@@ -16,7 +16,7 @@ module JudoCommandLineHelpers
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def find_groups(judo, args, &blk)
|
19
|
-
raise JudoError, "No groups
|
19
|
+
raise JudoError, "No groups specified" if args.empty? and judo.group.nil?
|
20
20
|
|
21
21
|
args << ":#{judo.group}" if args.empty? ## use default group if none specified
|
22
22
|
|
@@ -65,9 +65,10 @@ module JudoCommandLineHelpers
|
|
65
65
|
raise JudoError, "No servers" if servers.empty?
|
66
66
|
|
67
67
|
servers.each { |s| judo_yield(s,blk) if blk }
|
68
|
+
servers
|
68
69
|
end
|
69
70
|
|
70
|
-
def find_server(judo, arg, use_default)
|
71
|
+
def find_server(judo, arg, use_default = false)
|
71
72
|
## this assumes names are unique no matter the group
|
72
73
|
name,group = split(arg)
|
73
74
|
if name != ""
|
@@ -118,12 +119,23 @@ module JudoCommandLineHelpers
|
|
118
119
|
end
|
119
120
|
end
|
120
121
|
|
122
|
+
def do_snapshots(judo, args)
|
123
|
+
servers = find_servers(judo, args)
|
124
|
+
printf " SNAPSHOTS\n"
|
125
|
+
printf "%s\n", ("-" * 80)
|
126
|
+
## FIXME - listing snapshots for deleted servers?
|
127
|
+
judo.snapshots.each do |snapshot|
|
128
|
+
# next if args and not servers.map { |s| s.name }.include?(snapshot.server_name)
|
129
|
+
printf "%-15s %-25s %-15s %-10s %s\n", snapshot.name, snapshot.server_name, snapshot.group_name, snapshot.version_desc, "#{snapshot.num_ec2_snapshots}v"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
121
133
|
def do_list(judo, args)
|
122
134
|
servers = find_servers(judo, args)
|
123
135
|
printf " SERVERS\n"
|
124
136
|
printf "%s\n", ("-" * 80)
|
125
137
|
servers.sort.each do |s|
|
126
|
-
printf "%-
|
138
|
+
printf "%-32s %-12s %-7s %-11s %-11s %-10s %-3s %s\n", s.name, s.group.name, s.version_desc, s.state["instance_id"], s.size_desc, s.ec2_state, "#{s.volumes.keys.size}v", s.has_ip? ? "ip" : ""
|
127
139
|
end
|
128
140
|
end
|
129
141
|
|
@@ -145,7 +157,5 @@ module JudoCommandLineHelpers
|
|
145
157
|
v[:aws_attachment_status],
|
146
158
|
v[:aws_device]
|
147
159
|
end
|
148
|
-
puts "\t[ CONFIG ]"
|
149
|
-
pp server.config
|
150
160
|
end
|
151
161
|
end
|
data/lib/judo/group.rb
CHANGED
@@ -2,47 +2,11 @@ module Judo
|
|
2
2
|
class Group
|
3
3
|
attr_accessor :name, :version
|
4
4
|
|
5
|
-
# def self.dirs
|
6
|
-
# Dir["#{@base.repor}/*/config.json"].map { |d| File.dirname(d) }
|
7
|
-
# end
|
8
|
-
#
|
9
|
-
# def self.all
|
10
|
-
# @@all ||= (dirs.map { |d| new(d) })
|
11
|
-
# end
|
12
|
-
#
|
13
|
-
# def self.find(name)
|
14
|
-
# all.detect { |d| d.name == name }
|
15
|
-
# end
|
16
|
-
#
|
17
|
-
# def self.[](name)
|
18
|
-
# find(name)
|
19
|
-
# end
|
20
|
-
#
|
21
|
-
# def self.current
|
22
|
-
# all.detect { |d| Dir.pwd == d.dir or Dir.pwd =~ /^#{d.dir}\// }
|
23
|
-
# end
|
24
|
-
|
25
|
-
# def initialize(base, dir, name = File.basename(dir))
|
26
5
|
def initialize(base, name, version)
|
27
6
|
@base = base
|
28
7
|
@name = name
|
29
8
|
@version = version
|
30
|
-
|
31
|
-
end
|
32
|
-
|
33
|
-
# def create_server(server_name)
|
34
|
-
# abort("Server needs a name") if server_name.nil?
|
35
|
-
#
|
36
|
-
## abort("Already a server named #{server_name}") if Judo::Server.find_by_name(attrs[:name]) ## FIXME
|
37
|
-
## @base.read_config(attrs[:group]) ## make sure the config is valid ## FIXME
|
38
|
-
#
|
39
|
-
# server = Judo::Server.new base, server_name, name
|
40
|
-
# server.task("Creating server #{server_name}") do
|
41
|
-
# server.update "name" => server_name, "group" => name, "virgin" => true, "secret" => rand(2 ** 128).to_s(36)
|
42
|
-
# @base.sdb.put_attributes("judo_config", "groups", name => server_name)
|
43
|
-
# end
|
44
|
-
# server
|
45
|
-
# end
|
9
|
+
end
|
46
10
|
|
47
11
|
def config
|
48
12
|
@config ||= load_config
|
@@ -60,25 +24,20 @@ module Judo
|
|
60
24
|
@base.servers.select { |s| server_names.include?(s.name) }
|
61
25
|
end
|
62
26
|
|
63
|
-
# def version
|
64
|
-
# @version ||= (@base.group_versions[@name] || ["0"]).first.to_i
|
65
|
-
# end
|
66
|
-
|
67
27
|
def load_config
|
68
28
|
JSON.load @base.s3_get(version_config_file)
|
69
29
|
rescue Aws::AwsError
|
70
30
|
raise JudoError, "No config stored: try 'judo commit #{to_s}'"
|
71
31
|
end
|
72
32
|
|
73
|
-
def set_version
|
74
|
-
@version
|
75
|
-
@base.sdb.put_attributes("judo_config", "group_versions", { name => new_version }, :replace)
|
33
|
+
def set_version
|
34
|
+
@base.sdb.put_attributes("judo_config", "group_versions", { name => version }, :replace)
|
76
35
|
end
|
77
36
|
|
78
37
|
def compile
|
79
38
|
tmpdir = "/tmp/kuzushi/#{name}"
|
80
|
-
set_version(version + 1)
|
81
39
|
puts "Compiling #{self} version #{version}"
|
40
|
+
@version = @version + 1
|
82
41
|
FileUtils.rm_rf(tmpdir)
|
83
42
|
FileUtils.mkdir_p(tmpdir)
|
84
43
|
new_config = build_config
|
@@ -101,6 +60,7 @@ module Judo
|
|
101
60
|
@base.s3_put(version_config_file, new_config.to_json)
|
102
61
|
end
|
103
62
|
end
|
63
|
+
set_version
|
104
64
|
end
|
105
65
|
|
106
66
|
def version_config_file
|
@@ -121,7 +81,6 @@ module Judo
|
|
121
81
|
|
122
82
|
def extract_file(type, name, files)
|
123
83
|
path = "#{dir}/#{type}s/#{name}"
|
124
|
-
puts "[#{name}] #{path}"
|
125
84
|
found = Dir[path]
|
126
85
|
if not found.empty?
|
127
86
|
found.each { |f| files["#{type}s/#{File.basename(f)}"] = f }
|
@@ -147,12 +106,15 @@ module Judo
|
|
147
106
|
when "local_packages"
|
148
107
|
extract_file(:package, "#{v}_i386.deb", files)
|
149
108
|
extract_file(:package, "#{v}_amd64.deb", files)
|
109
|
+
when "git"
|
110
|
+
## do nothing - is a remote
|
150
111
|
when "template"
|
151
112
|
extract_file(:template, v, files)
|
152
113
|
when "source"
|
153
114
|
extract_file(:file, v, files) unless config["template"] or config["package"]
|
154
115
|
when "file"
|
155
|
-
|
116
|
+
## this is getting messy
|
117
|
+
extract_file(:file, File.basename(v), files) unless config["template"] or config["source"] or config["git"]
|
156
118
|
end
|
157
119
|
end
|
158
120
|
end
|
@@ -216,6 +178,14 @@ module Judo
|
|
216
178
|
def sdb
|
217
179
|
@base.sdb
|
218
180
|
end
|
181
|
+
|
182
|
+
def version_desc(v)
|
183
|
+
if v == version
|
184
|
+
"v#{v}"
|
185
|
+
else
|
186
|
+
"v#{v}/#{version}"
|
187
|
+
end
|
188
|
+
end
|
219
189
|
end
|
220
190
|
end
|
221
191
|
|
data/lib/judo/server.rb
CHANGED
@@ -1,51 +1,14 @@
|
|
1
|
-
### NEEDED for new gem launch
|
2
|
-
|
3
|
-
### [ ] return right away.. (1 hr)
|
4
|
-
### [ ] two phase delete (1 hr)
|
5
|
-
### [-] refactor availability_zone (2 hrs)
|
6
|
-
### [ ] pick availability zone from config "X":"Y" or "X":["Y","Z"]
|
7
|
-
### [ ] assign to state on creation ( could delay till volume creation )
|
8
|
-
### [ ] implement auto security_group creation and setup (6 hrs)
|
9
|
-
### [ ] write some examples - simple postgres/redis/couchdb server (5hrs)
|
10
|
-
### [ ] write new README (4 hrs)
|
11
|
-
### [ ] bind kuzushi gem version version
|
12
|
-
### [ ] realase new gem! (1 hr)
|
13
|
-
|
14
|
-
### [ ] should be able to do ALL actions except commit without the repo!
|
15
|
-
### [ ] store git commit hash with commit to block a judo commit if there is newer material stored
|
16
|
-
### [ ] remove the tarball - store files a sha hashes in the bucket - makes for faster commits if the files have not changed
|
17
|
-
|
18
|
-
### [ ] use a logger service (1 hr)
|
19
|
-
### [ ] write specs (5 hr)
|
20
|
-
|
21
|
-
### Error Handling
|
22
|
-
### [ ] no availability zone before making disks
|
23
|
-
### [ ] security group does not exists
|
24
|
-
|
25
|
-
### Do Later
|
26
|
-
### [ ] use amazon's new conditional write tools so we never have problems from concurrent updates
|
27
|
-
### [ ] is thor really what we want to use here?
|
28
|
-
### [ ] need to be able to pin a config to a version of kuzushi - gem updates can/will break a lot of things
|
29
|
-
### [ ] I want a "judo monitor" command that will make start servers if they go down, and poke a listed port to make sure a service is listening, would be cool if it also detects wrong ami, wrong secuirity group, missing/extra volumes, missing/extra elastic_ip - might not want to force a reboot quite yet in these cases
|
30
|
-
### [ ] Implement "judo snapshot [NAME]" to take a snapshot of the ebs's blocks
|
31
|
-
### [ ] ruby 1.9.1 support
|
32
|
-
### [ ] find a good way to set the hostname or prompt to :name
|
33
|
-
### [ ] remove fog/s3 dependancy
|
34
|
-
### [ ] enforce template files end in .erb to make room for other possible templates as defined by the extensions
|
35
|
-
### [ ] zerigo integration for automatic DNS setup
|
36
|
-
### [ ] How cool would it be if this was all reimplemented in eventmachine and could start lots of boxes in parallel? Would need to evented AWS api calls... Never seen a library to do that - would have to write our own... "Fog Machine?"
|
37
|
-
|
38
1
|
module Judo
|
39
2
|
class Server
|
40
3
|
attr_accessor :name
|
41
4
|
|
42
|
-
def initialize(base, name, group)
|
5
|
+
def initialize(base, name, group, version = nil)
|
43
6
|
@base = base
|
44
7
|
@name = name
|
45
8
|
@group_name = group
|
46
9
|
end
|
47
10
|
|
48
|
-
def create
|
11
|
+
def create(version = group.version, snapshots = nil)
|
49
12
|
raise JudoError, "no group specified" unless @group_name
|
50
13
|
|
51
14
|
if @name.nil?
|
@@ -56,11 +19,12 @@ module Judo
|
|
56
19
|
raise JudoError, "there is already a server named #{name}" if @base.servers.detect { |s| s.name == @name and s != self}
|
57
20
|
|
58
21
|
task("Creating server #{name}") do
|
59
|
-
update "name" => name, "group" => @group_name, "virgin" => true, "secret" => rand(2 ** 128).to_s(36)
|
22
|
+
update "name" => name, "group" => @group_name, "virgin" => true, "secret" => rand(2 ** 128).to_s(36), "version" => version
|
60
23
|
@base.sdb.put_attributes("judo_config", "groups", @group_name => name)
|
61
24
|
end
|
62
25
|
|
63
|
-
|
26
|
+
allocate_disk(snapshots)
|
27
|
+
allocate_ip
|
64
28
|
|
65
29
|
self
|
66
30
|
end
|
@@ -98,18 +62,37 @@ module Judo
|
|
98
62
|
end
|
99
63
|
|
100
64
|
def version_desc
|
101
|
-
|
102
|
-
if version == group.version
|
103
|
-
"v#{version}"
|
104
|
-
else
|
105
|
-
"v#{version}/#{group.version}"
|
106
|
-
end
|
65
|
+
group.version_desc(version)
|
107
66
|
end
|
108
67
|
|
109
68
|
def version
|
110
69
|
get("version").to_i
|
111
70
|
end
|
112
71
|
|
72
|
+
def update_version(new_version)
|
73
|
+
update "version" => new_version
|
74
|
+
end
|
75
|
+
|
76
|
+
def kuzushi_action
|
77
|
+
if virgin?
|
78
|
+
if cloned?
|
79
|
+
"start"
|
80
|
+
else
|
81
|
+
"init"
|
82
|
+
end
|
83
|
+
else
|
84
|
+
"start"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def clone
|
89
|
+
get("clone")
|
90
|
+
end
|
91
|
+
|
92
|
+
def cloned?
|
93
|
+
!!clone
|
94
|
+
end
|
95
|
+
|
113
96
|
def virgin?
|
114
97
|
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
|
115
98
|
end
|
@@ -118,6 +101,10 @@ module Judo
|
|
118
101
|
get "secret"
|
119
102
|
end
|
120
103
|
|
104
|
+
def snapshots
|
105
|
+
base.snapshots.select { |s| s.server == self }
|
106
|
+
end
|
107
|
+
|
121
108
|
def volumes
|
122
109
|
Hash[ (state["volumes"] || []).map { |a| a.split(":") } ]
|
123
110
|
end
|
@@ -147,7 +134,7 @@ module Judo
|
|
147
134
|
end
|
148
135
|
|
149
136
|
def delete
|
150
|
-
group.delete_server(self)
|
137
|
+
group.delete_server(self) if group
|
151
138
|
@base.sdb.delete_attributes(self.class.domain, name)
|
152
139
|
end
|
153
140
|
|
@@ -165,7 +152,24 @@ module Judo
|
|
165
152
|
"#{name}:#{@group_name}"
|
166
153
|
end
|
167
154
|
|
168
|
-
def
|
155
|
+
def allocate_disk(snapshots)
|
156
|
+
if snapshots
|
157
|
+
clone_snapshots(snapshots)
|
158
|
+
else
|
159
|
+
create_volumes
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def clone_snapshots(snapshots)
|
164
|
+
snapshots.each do |device,snap_id|
|
165
|
+
task("Creating EC2 Volume #{device} from #{snap_id}") do
|
166
|
+
volume_id = @base.ec2.create_volume(snap_id, nil, config["availability_zone"])[:aws_id]
|
167
|
+
add_volume(volume_id, device)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def create_volumes
|
169
173
|
if config["volumes"]
|
170
174
|
[config["volumes"]].flatten.each do |volume_config|
|
171
175
|
device = volume_config["device"]
|
@@ -185,7 +189,9 @@ module Judo
|
|
185
189
|
end
|
186
190
|
end
|
187
191
|
end
|
192
|
+
end
|
188
193
|
|
194
|
+
def allocate_ip
|
189
195
|
begin
|
190
196
|
if config["elastic_ip"] and not elastic_ip
|
191
197
|
### EC2 allocate_address
|
@@ -259,17 +265,18 @@ module Judo
|
|
259
265
|
["pending", "running", "shutting_down", "degraded"].include?(ec2_state)
|
260
266
|
end
|
261
267
|
|
262
|
-
def start
|
268
|
+
def start(new_version = nil)
|
263
269
|
invalid "Already running" if running?
|
264
270
|
invalid "No config has been commited yet, type 'judo commit'" unless group.version > 0
|
271
|
+
task("Updating server version") { update_version(new_version) } if new_version
|
265
272
|
task("Starting server #{name}") { launch_ec2 }
|
266
273
|
task("Wait for server") { wait_for_running } if elastic_ip or has_volumes?
|
267
274
|
task("Attaching ip") { attach_ip } if elastic_ip
|
268
275
|
task("Attaching volumes") { attach_volumes } if has_volumes?
|
269
276
|
end
|
270
277
|
|
271
|
-
def restart
|
272
|
-
stop if running?
|
278
|
+
def restart(force = false)
|
279
|
+
stop(force) if running?
|
273
280
|
start
|
274
281
|
end
|
275
282
|
|
@@ -285,10 +292,19 @@ module Judo
|
|
285
292
|
raise JudoInvalid, str
|
286
293
|
end
|
287
294
|
|
288
|
-
def
|
295
|
+
def force_detach_volumes
|
296
|
+
volumes.each do |device,volume_id|
|
297
|
+
task("Force detaching #{volume_id}") do
|
298
|
+
@base.ec2.detach_volume(volume_id, instance_id, device, true)
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
def stop(force = false)
|
289
304
|
invalid "not running" unless running?
|
290
305
|
## EC2 terminate_isntaces
|
291
306
|
task("Terminating instance") { @base.ec2.terminate_instances([ instance_id ]) }
|
307
|
+
force_detach_volumes if force
|
292
308
|
task("Wait for volumes to detach") { wait_for_volumes_detached } if volumes.size > 0
|
293
309
|
remove "instance_id"
|
294
310
|
end
|
@@ -305,8 +321,7 @@ module Judo
|
|
305
321
|
:key_name => config["key_name"],
|
306
322
|
:group_ids => security_groups,
|
307
323
|
:user_data => ud).first
|
308
|
-
|
309
|
-
update "instance_id" => result[:aws_instance_id], "virgin" => false, "version" => group.version
|
324
|
+
update "instance_id" => result[:aws_instance_id], "virgin" => false
|
310
325
|
end
|
311
326
|
|
312
327
|
def debug(str)
|
@@ -356,10 +371,15 @@ module Judo
|
|
356
371
|
end
|
357
372
|
|
358
373
|
def wait_for_volumes_detached
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
374
|
+
begin
|
375
|
+
Timeout::timeout(30) do
|
376
|
+
loop do
|
377
|
+
break if ec2_volumes.reject { |v| v[:aws_status] == "available" }.empty?
|
378
|
+
sleep 2
|
379
|
+
end
|
380
|
+
end
|
381
|
+
rescue Timeout::Error
|
382
|
+
force_detach_volumes
|
363
383
|
end
|
364
384
|
end
|
365
385
|
|
@@ -505,6 +525,11 @@ USER_DATA
|
|
505
525
|
end
|
506
526
|
end
|
507
527
|
|
528
|
+
def snapshot(name)
|
529
|
+
snap = @base.new_snapshot(name, self.name)
|
530
|
+
snap.create
|
531
|
+
end
|
532
|
+
|
508
533
|
def <=>(s)
|
509
534
|
[group.name, name] <=> [s.group.name, s.name]
|
510
535
|
end
|
data/lib/judo/setup.rb
CHANGED
@@ -63,7 +63,8 @@ DEFAULT
|
|
63
63
|
|
64
64
|
def setup_db
|
65
65
|
puts "Trying to connect to SimpleDB with #{@aws_access_id}"
|
66
|
-
sdb.create_domain(
|
66
|
+
sdb.create_domain(Judo::Server.domain)
|
67
|
+
sdb.create_domain(Judo::Snapshot.domain)
|
67
68
|
sdb.create_domain("judo_config")
|
68
69
|
olddb = sdb.get_attributes("judo_config", "judo")[:attributes]["dbversion"]
|
69
70
|
abort "There is an existing judo database of a newer version - upgrade judo and try again" if olddb and olddb.first.to_i > Judo::Config.db_version
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module Judo
|
2
|
+
class Snapshot
|
3
|
+
attr_accessor :name, :server_name
|
4
|
+
|
5
|
+
def self.domain
|
6
|
+
"judo_snapshots"
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(base, name, server_name)
|
10
|
+
@base = base
|
11
|
+
@name = name
|
12
|
+
@server_name = server_name
|
13
|
+
end
|
14
|
+
|
15
|
+
def server
|
16
|
+
@server ||= @base.servers.detect { |s| s.name == @server_name }
|
17
|
+
end
|
18
|
+
|
19
|
+
def fetch_state
|
20
|
+
@base.sdb.get_attributes(self.class.domain, name)[:attributes]
|
21
|
+
end
|
22
|
+
|
23
|
+
def state
|
24
|
+
@base.snapshots_state[name] ||= fetch_state
|
25
|
+
end
|
26
|
+
|
27
|
+
def group_name
|
28
|
+
get("group")
|
29
|
+
end
|
30
|
+
|
31
|
+
def version
|
32
|
+
get("version").to_i
|
33
|
+
end
|
34
|
+
|
35
|
+
def num_ec2_snapshots
|
36
|
+
devs.size
|
37
|
+
end
|
38
|
+
|
39
|
+
def devs
|
40
|
+
Hash[ (state["devs"] || []).map { |a| a.split(":") } ]
|
41
|
+
end
|
42
|
+
|
43
|
+
def create
|
44
|
+
raise JudoError,"snapshot already exists" unless state.empty?
|
45
|
+
raise JudoError,"server has no disks to clone: #{server.volumes}" if server.volumes.empty?
|
46
|
+
task("Snapshotting #{server.name}") do
|
47
|
+
devs = server.volumes.map do |dev,vol|
|
48
|
+
"#{dev}:#{@base.ec2.create_snapshot(vol)[:aws_id]}"
|
49
|
+
end
|
50
|
+
@base.sdb.put_attributes(self.class.domain, name, { "version" => server.version, "devs" => devs, "server" => server.name, "group" => server.group.name }, :replace)
|
51
|
+
server.add "snapshots", name
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def clone(new_server, version = self.version)
|
56
|
+
raise JudoError, "cannot clone, snapshotting not complete" unless completed?
|
57
|
+
server = @base.new_server(new_server, group_name)
|
58
|
+
server.create(version, devs)
|
59
|
+
server.update "clone" => name ##, "secret" => rand(2 ** 128).to_s(36) ## cant change this till kuzushi knows about a post-clone operation
|
60
|
+
end
|
61
|
+
|
62
|
+
def delete
|
63
|
+
@base.sdb.delete_attributes(self.class.domain, name)
|
64
|
+
server.remove "snapshots", name
|
65
|
+
end
|
66
|
+
|
67
|
+
def get(key)
|
68
|
+
state[key] && [state[key]].flatten.first
|
69
|
+
end
|
70
|
+
|
71
|
+
def destroy
|
72
|
+
devs.each do |dev,snapshot_id|
|
73
|
+
task("Deleting #{dev} #{snapshot_id}") do
|
74
|
+
begin
|
75
|
+
@base.ec2.delete_snapshot(snapshot_id)
|
76
|
+
rescue Object => e
|
77
|
+
puts "Error destrotying snapshot #{e.message}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
delete
|
82
|
+
end
|
83
|
+
|
84
|
+
def ec2_ids
|
85
|
+
devs.values
|
86
|
+
end
|
87
|
+
|
88
|
+
def completed?
|
89
|
+
not @base.ec2_snapshots.select { |s| ec2_ids.include? s[:aws_id] }.detect { |s| s[:aws_status] != "completed" }
|
90
|
+
end
|
91
|
+
|
92
|
+
def size(snap_id)
|
93
|
+
@base.ec2_snapshots.detect { |s| s[:aws_id] == snap_id }
|
94
|
+
end
|
95
|
+
|
96
|
+
def version_desc
|
97
|
+
group.version_desc(version)
|
98
|
+
end
|
99
|
+
|
100
|
+
def group
|
101
|
+
@group ||= @base.groups.detect { |g| g.name == group_name }
|
102
|
+
end
|
103
|
+
|
104
|
+
def fetch_state
|
105
|
+
@base.sdb.get_attributes(self.class.domain, name)[:attributes]
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
version: 0.1.
|
8
|
+
- 4
|
9
|
+
version: 0.1.4
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Orion Henry
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-05-06 00:00:00 -04:00
|
18
18
|
default_executable: judo
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -51,9 +51,11 @@ extensions: []
|
|
51
51
|
|
52
52
|
extra_rdoc_files:
|
53
53
|
- README.markdown
|
54
|
+
- TODO
|
54
55
|
files:
|
55
56
|
- README.markdown
|
56
57
|
- Rakefile
|
58
|
+
- TODO
|
57
59
|
- VERSION
|
58
60
|
- bin/judo
|
59
61
|
- lib/judo.rb
|
@@ -63,6 +65,7 @@ files:
|
|
63
65
|
- lib/judo/group.rb
|
64
66
|
- lib/judo/server.rb
|
65
67
|
- lib/judo/setup.rb
|
68
|
+
- lib/judo/snapshot.rb
|
66
69
|
- spec/base.rb
|
67
70
|
- spec/base_spec.rb
|
68
71
|
- spec/server_spec.rb
|