judo 0.1.0 → 0.1.4
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/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
|