judo 0.0.9 → 0.1.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/Rakefile +1 -2
- data/VERSION +1 -1
- data/bin/judo +102 -212
- data/lib/judo.rb +18 -0
- data/lib/judo/base.rb +199 -0
- data/lib/judo/commandline_helpers.rb +151 -0
- data/lib/judo/config.rb +133 -0
- data/lib/judo/group.rb +221 -0
- data/lib/judo/server.rb +513 -0
- data/lib/judo/setup.rb +114 -0
- data/spec/base.rb +1 -1
- data/spec/base_spec.rb +9 -0
- metadata +16 -26
- data/lib/all.rb +0 -12
- data/lib/config.rb +0 -105
- data/lib/group.rb +0 -185
- data/lib/server.rb +0 -491
- data/lib/setup.rb +0 -114
@@ -0,0 +1,151 @@
|
|
1
|
+
module JudoCommandLineHelpers
|
2
|
+
def judo_yield(arg, blk)
|
3
|
+
begin
|
4
|
+
blk.call(arg)
|
5
|
+
rescue JudoInvalid => e
|
6
|
+
puts "#{arg} - #{e.message}"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def split(string)
|
11
|
+
if string =~ /([^:]*):(.*)/
|
12
|
+
[ $1, $2 ]
|
13
|
+
else
|
14
|
+
[ string, nil]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def find_groups(judo, args, &blk)
|
19
|
+
raise JudoError, "No groups #{specified}" if args.empty? and judo.group.nil?
|
20
|
+
|
21
|
+
args << ":#{judo.group}" if args.empty? ## use default group if none specified
|
22
|
+
|
23
|
+
groups = args.map do |arg|
|
24
|
+
name,group = split(arg)
|
25
|
+
raise JudoError, "specify a group with ':GROUP'" unless name == "" and group
|
26
|
+
judo.get_group(group)
|
27
|
+
end
|
28
|
+
|
29
|
+
groups.each { |group| judo_yield(group, blk) if blk }
|
30
|
+
end
|
31
|
+
|
32
|
+
def mk_servers(judo, args, &blk)
|
33
|
+
servers = args.map do |arg|
|
34
|
+
name,group = split(arg)
|
35
|
+
group ||= judo.group
|
36
|
+
raise JudoError, "Cannot must specify a server, not a group, on create and launch" unless name
|
37
|
+
if name =~ /^\+(\d+)/
|
38
|
+
count = $1.to_i
|
39
|
+
raise JudoError, "You can batch-create between 1 and 5 servers" if count < 1 or count > 5
|
40
|
+
(1..count).map { judo.new_server(nil, group) }
|
41
|
+
else
|
42
|
+
judo.new_server(name, group)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
servers.flatten.each { |s| judo_yield(s, blk) if blk }
|
46
|
+
end
|
47
|
+
|
48
|
+
def find_either(judo, args, &blk)
|
49
|
+
results = []
|
50
|
+
args.each do |arg|
|
51
|
+
server,group = split(arg)
|
52
|
+
if server != ""
|
53
|
+
results << judo.servers.select { |s| s.name == server }
|
54
|
+
else
|
55
|
+
results << judo.groups.select { |g| g.name == group }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
results.flatten.each { |i| judo_yield(i, blk) if blk }
|
59
|
+
end
|
60
|
+
|
61
|
+
def find_servers(judo, args, use_default = true, &blk)
|
62
|
+
servers = judo.servers if args.empty?
|
63
|
+
servers ||= args.map { |a| find_server(judo, a, use_default) }.flatten
|
64
|
+
|
65
|
+
raise JudoError, "No servers" if servers.empty?
|
66
|
+
|
67
|
+
servers.each { |s| judo_yield(s,blk) if blk }
|
68
|
+
end
|
69
|
+
|
70
|
+
def find_server(judo, arg, use_default)
|
71
|
+
## this assumes names are unique no matter the group
|
72
|
+
name,group = split(arg)
|
73
|
+
if name != ""
|
74
|
+
server = judo.servers.detect { |s| s.name == name }
|
75
|
+
raise JudoError, "No such server #{name}" unless server
|
76
|
+
raise JudoError, "Server #{name} not in group #{group}" if group and server.group.name != group
|
77
|
+
server
|
78
|
+
else
|
79
|
+
group ||= judo.group if use_default
|
80
|
+
g = judo.groups.detect { |g| g.name == group }
|
81
|
+
raise JudoError, "No such group #{group}" unless g
|
82
|
+
g.servers
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def do_groups(judo)
|
87
|
+
printf " SERVER GROUPS\n"
|
88
|
+
judo.groups.each do |g|
|
89
|
+
printf "%-18s %s servers\n", g.name, g.servers.size
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def do_volumes(judo)
|
94
|
+
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 ] }
|
95
|
+
|
96
|
+
format = "%13s %6s %12s %-10s %-16s %-16s\n"
|
97
|
+
printf format, "AWS_ID", "SIZE", "AWS_STATUS", "AWS_DEVICE", "ATTACHED TO", "CONFIGURED FOR"
|
98
|
+
printf "%s\n", ("-" * 80)
|
99
|
+
|
100
|
+
vols.each do |v|
|
101
|
+
attached = v[:attached_to] ? v[:attached_to].name : v[:instance_id]
|
102
|
+
assigned = v[:assigned_to] ? v[:assigned_to].name : ""
|
103
|
+
printf format, v[:id], v[:size], v[:status], v[:device], attached, assigned
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def do_ips(judo)
|
108
|
+
ips = judo.ips.sort { |a,b| a[:assigned_to].to_s <=> b[:assigned_to].to_s }
|
109
|
+
|
110
|
+
format = "%15s %20s %20s\n"
|
111
|
+
printf format, "IP", "ATTACHED TO", "CONFIGURED FOR"
|
112
|
+
printf "%s\n", ("-"*57)
|
113
|
+
|
114
|
+
ips.each do |ip|
|
115
|
+
attached = ip[:attached_to] ? ip[:attached_to].name : ip[:instance_id]
|
116
|
+
assigned = ip[:assigned_to] ? ip[:assigned_to].name : ""
|
117
|
+
printf format, ip[:ip], attached, assigned
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def do_list(judo, args)
|
122
|
+
servers = find_servers(judo, args)
|
123
|
+
printf " SERVERS\n"
|
124
|
+
printf "%s\n", ("-" * 80)
|
125
|
+
servers.sort.each do |s|
|
126
|
+
printf "%-18s %-12s %-7s %-11s %-11s %-13s %-10s %-10s %s\n", s.name, s.group.name, s.version_desc, s.state["instance_id"], s.size_desc, s.ami, s.ec2_state, "#{s.volumes.keys.size} volumes", s.ip
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def do_info(judo, server)
|
131
|
+
puts "#{server}"
|
132
|
+
if server.ec2_instance and not server.ec2_instance.empty?
|
133
|
+
puts "\t[ EC2 ]"
|
134
|
+
[: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|
|
135
|
+
printf "\t %-24s: %s\n",k, server.ec2_instance[k]
|
136
|
+
end
|
137
|
+
end
|
138
|
+
puts "\t[ VOLUMES ]"
|
139
|
+
server.ec2_volumes.each do |v|
|
140
|
+
printf "\t %-13s %-10s %-10s %4d %-10s %-8s\n",
|
141
|
+
v[:aws_id],
|
142
|
+
v[:aws_status],
|
143
|
+
v[:zone],
|
144
|
+
v[:aws_size],
|
145
|
+
v[:aws_attachment_status],
|
146
|
+
v[:aws_device]
|
147
|
+
end
|
148
|
+
puts "\t[ CONFIG ]"
|
149
|
+
pp server.config
|
150
|
+
end
|
151
|
+
end
|
data/lib/judo/config.rb
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
module Judo
|
2
|
+
module Config
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def db_version
|
6
|
+
1
|
7
|
+
end
|
8
|
+
|
9
|
+
def get_db_version
|
10
|
+
version = @sdb.get_attributes("judo_config", "judo")[:attributes]["dbversion"]
|
11
|
+
version and version.first.to_i or db_version
|
12
|
+
end
|
13
|
+
|
14
|
+
def check_version
|
15
|
+
abort "judo db is newer than the current gem - upgrade judo and try again" if get_db_version > db_version
|
16
|
+
end
|
17
|
+
|
18
|
+
def repo_dir
|
19
|
+
judo_config["repo"] || File.dirname(judo_dir)
|
20
|
+
end
|
21
|
+
|
22
|
+
def access_id
|
23
|
+
judo_config["access_id"]
|
24
|
+
end
|
25
|
+
|
26
|
+
def access_secret
|
27
|
+
judo_config["access_secret"]
|
28
|
+
end
|
29
|
+
|
30
|
+
def get_ec2(aws_id, aws_key)
|
31
|
+
Aws::Ec2.new(aws_id, aws_key, :logger => Logger.new(nil))
|
32
|
+
end
|
33
|
+
|
34
|
+
def ec2
|
35
|
+
@ec2 ||= get_ec2(access_id, access_secret)
|
36
|
+
end
|
37
|
+
|
38
|
+
# def create_security_group
|
39
|
+
# ## EC2 create_security_group
|
40
|
+
# ec2.create_security_group('judo', 'Judo')
|
41
|
+
# ## EC2 authorize_security_group
|
42
|
+
# ec2.authorize_security_group_IP_ingress("judo", 22, 22,'tcp','0.0.0.0/0')
|
43
|
+
# rescue Aws::AwsError
|
44
|
+
# end
|
45
|
+
|
46
|
+
def judo_config
|
47
|
+
@config ||= read_judo_config
|
48
|
+
end
|
49
|
+
|
50
|
+
def judo_config_file
|
51
|
+
"#{judo_dir}/config.yml"
|
52
|
+
end
|
53
|
+
|
54
|
+
def judo_dir
|
55
|
+
@judo_dir ||= find_judo_dir(Dir.pwd) || abort("fatal: Not a judo repository (or any of the parent directories): .judo\nrun commands from the judo repository or type 'judo init' to setup the current directory as a new judo repository")
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.dirs
|
59
|
+
Dir["#{Judo::Config.repo_dir}/*/config.json"].map { |d| File.dirname(d) }
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.all
|
63
|
+
@@all ||= (dirs.map { |d| new(d) })
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.current
|
67
|
+
all.detect { |d| Dir.pwd == d.dir or Dir.pwd =~ /^#{d.dir}\// }
|
68
|
+
end
|
69
|
+
|
70
|
+
def default_options(pwd, dir = find_judo_dir(pwd))
|
71
|
+
puts "PWD: #{pwd}"
|
72
|
+
puts "DIR: #{dir}"
|
73
|
+
config = YAML.load File.read("#{dir}/config.yml")
|
74
|
+
repo_dir = config["repo"] || File.dirname(dir)
|
75
|
+
group_config = Dir["#{repo_dir}/*/config.json"].detect { |d| File.dirname(d) == pwd }
|
76
|
+
{
|
77
|
+
:judo_dir => dir,
|
78
|
+
:group => group_config ? File.basename(File.dirname(group_config)) : nil,
|
79
|
+
:repo => repo_dir,
|
80
|
+
:bucket => config["s3_bucket"],
|
81
|
+
:access_id => config["access_id"],
|
82
|
+
:access_secret => config["access_secret"]
|
83
|
+
}.delete_if { |key,value| value.nil? }
|
84
|
+
rescue Object => e
|
85
|
+
puts e.inspect
|
86
|
+
{}
|
87
|
+
end
|
88
|
+
|
89
|
+
def find_judo_dir(check)
|
90
|
+
if check == "/"
|
91
|
+
if File.exists?("#{ENV['HOME']}/.judo")
|
92
|
+
"#{ENV['HOME']}/.judo"
|
93
|
+
else
|
94
|
+
nil
|
95
|
+
end
|
96
|
+
else
|
97
|
+
File.exists?(check + "/.judo") ? check + "/.judo" : find_judo_dir(File.dirname(check))
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def read_judo_config
|
102
|
+
YAML.load File.read(judo_config_file)
|
103
|
+
rescue Errno::ENOENT
|
104
|
+
{}
|
105
|
+
end
|
106
|
+
|
107
|
+
def get_sdb(aws_id, aws_key)
|
108
|
+
Aws::SdbInterface.new(aws_id, aws_key, :logger => Logger.new(nil))
|
109
|
+
end
|
110
|
+
|
111
|
+
def sdb
|
112
|
+
@sdb ||= get_sdb(access_id, access_secret)
|
113
|
+
# @version_ok ||= check_version
|
114
|
+
@sdb
|
115
|
+
end
|
116
|
+
|
117
|
+
def s3
|
118
|
+
@s3 ||= Aws::S3.new(access_id, access_secret, :logger => Logger.new(nil))
|
119
|
+
end
|
120
|
+
|
121
|
+
def s3_bucket
|
122
|
+
@s3_bucket ||= s3.bucket(judo_config["s3_bucket"])
|
123
|
+
end
|
124
|
+
|
125
|
+
def s3_url(k)
|
126
|
+
Aws::S3Generator::Key.new(s3_bucket, k).get
|
127
|
+
end
|
128
|
+
|
129
|
+
def s3_put(k, file)
|
130
|
+
s3_bucket.put(k, file)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
data/lib/judo/group.rb
ADDED
@@ -0,0 +1,221 @@
|
|
1
|
+
module Judo
|
2
|
+
class Group
|
3
|
+
attr_accessor :name, :version
|
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
|
+
def initialize(base, name, version)
|
27
|
+
@base = base
|
28
|
+
@name = name
|
29
|
+
@version = version
|
30
|
+
# @dir = dir
|
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
|
46
|
+
|
47
|
+
def config
|
48
|
+
@config ||= load_config
|
49
|
+
end
|
50
|
+
|
51
|
+
def build_config
|
52
|
+
@build_config ||= all_configs.reverse.inject({}) { |sum,conf| sum.merge(conf) }
|
53
|
+
end
|
54
|
+
|
55
|
+
def server_names
|
56
|
+
@server_names ||= (@base.groups_config[@name] || [])
|
57
|
+
end
|
58
|
+
|
59
|
+
def servers
|
60
|
+
@base.servers.select { |s| server_names.include?(s.name) }
|
61
|
+
end
|
62
|
+
|
63
|
+
# def version
|
64
|
+
# @version ||= (@base.group_versions[@name] || ["0"]).first.to_i
|
65
|
+
# end
|
66
|
+
|
67
|
+
def load_config
|
68
|
+
JSON.load @base.s3_get(version_config_file)
|
69
|
+
rescue Aws::AwsError
|
70
|
+
raise JudoError, "No config stored: try 'judo commit #{to_s}'"
|
71
|
+
end
|
72
|
+
|
73
|
+
def set_version(new_version)
|
74
|
+
@version = new_version
|
75
|
+
@base.sdb.put_attributes("judo_config", "group_versions", { name => new_version }, :replace)
|
76
|
+
end
|
77
|
+
|
78
|
+
def compile
|
79
|
+
tmpdir = "/tmp/kuzushi/#{name}"
|
80
|
+
set_version(version + 1)
|
81
|
+
puts "Compiling #{self} version #{version}"
|
82
|
+
FileUtils.rm_rf(tmpdir)
|
83
|
+
FileUtils.mkdir_p(tmpdir)
|
84
|
+
new_config = build_config
|
85
|
+
Dir.chdir(tmpdir) do |d|
|
86
|
+
attachments(new_config).each do |to,from|
|
87
|
+
FileUtils.mkdir_p(File.dirname(to))
|
88
|
+
if from =~ /^http:\/\//
|
89
|
+
puts "curl '#{from}'"
|
90
|
+
system "curl '#{from}' > #{to}"
|
91
|
+
puts "#{to} is #{File.stat(to).size} bytes"
|
92
|
+
else
|
93
|
+
FileUtils.cp(from,to)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
File.open("config.json", "w") { |f| f.write(new_config.to_json) }
|
97
|
+
Dir.chdir("..") do
|
98
|
+
system "tar czvf #{tar_file} #{name}"
|
99
|
+
puts "Uploading to s3..."
|
100
|
+
@base.s3_put(tar_file, File.new(tar_file))
|
101
|
+
@base.s3_put(version_config_file, new_config.to_json)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def version_config_file
|
107
|
+
"#{name}.#{version}.json"
|
108
|
+
end
|
109
|
+
|
110
|
+
def tar_file
|
111
|
+
"#{name}.#{version}.tar.gz"
|
112
|
+
end
|
113
|
+
|
114
|
+
def s3_url
|
115
|
+
@url = @base.s3_url(tar_file)
|
116
|
+
end
|
117
|
+
|
118
|
+
def cp_file
|
119
|
+
FileUtil.mkdir_p(tmpdir)
|
120
|
+
end
|
121
|
+
|
122
|
+
def extract_file(type, name, files)
|
123
|
+
path = "#{dir}/#{type}s/#{name}"
|
124
|
+
puts "[#{name}] #{path}"
|
125
|
+
found = Dir[path]
|
126
|
+
if not found.empty?
|
127
|
+
found.each { |f| files["#{type}s/#{File.basename(f)}"] = f }
|
128
|
+
elsif parent
|
129
|
+
parent.extract_file(type, name, files)
|
130
|
+
else
|
131
|
+
raise "Cannot find file #{name} of type #{type}"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def extract(config, files)
|
136
|
+
config.each do |key,value|
|
137
|
+
[value].flatten.each do |v| ### cover "packages" : ["a","b"], "packages" : "a", "packages":[{ "file" : "foo.pkg"}]
|
138
|
+
if v.is_a? Hash
|
139
|
+
extract(v, files)
|
140
|
+
else
|
141
|
+
case key
|
142
|
+
when *[ "init", "before", "after" ]
|
143
|
+
extract_file(:script, v, files) unless v =~ /^#!/
|
144
|
+
when "package"
|
145
|
+
files["packages/#{v}_i386.deb"] = "#{config["source"]}#{v}_i386.deb"
|
146
|
+
files["packages/#{v}_amd64.deb"] = "#{config["source"]}#{v}_amd64.deb"
|
147
|
+
when "local_packages"
|
148
|
+
extract_file(:package, "#{v}_i386.deb", files)
|
149
|
+
extract_file(:package, "#{v}_amd64.deb", files)
|
150
|
+
when "template"
|
151
|
+
extract_file(:template, v, files)
|
152
|
+
when "source"
|
153
|
+
extract_file(:file, v, files) unless config["template"] or config["package"]
|
154
|
+
when "file"
|
155
|
+
extract_file(:file, File.basename(v), files) unless config["template"] or config["source"]
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
files
|
161
|
+
end
|
162
|
+
|
163
|
+
def keypair_file
|
164
|
+
extract_file(:keypair, config["key_name"] + ".pem" , {}).first
|
165
|
+
end
|
166
|
+
|
167
|
+
def attachments(c = config)
|
168
|
+
extract(c, {})
|
169
|
+
end
|
170
|
+
|
171
|
+
def raw_config
|
172
|
+
@raw_config ||= read_config
|
173
|
+
end
|
174
|
+
|
175
|
+
def parent
|
176
|
+
@parent ||= @base.groups.detect { |p| p.name == raw_config["import"] }
|
177
|
+
raise JudoError, "Parent group #{raw_config["import"]} must be commited" if raw_config["import"] and @parent.nil?
|
178
|
+
@parent
|
179
|
+
end
|
180
|
+
|
181
|
+
def destroy
|
182
|
+
servers.each { |s| s.destroy }
|
183
|
+
@base.task("Destring #{self}") do
|
184
|
+
@base.groups.delete(self)
|
185
|
+
@base.sdb.delete_attributes("judo_config", "groups", [ name ])
|
186
|
+
@base.sdb.delete_attributes("judo_config", "group_versions", [ name ])
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def all_configs
|
191
|
+
parent ? parent.all_configs.clone.unshift(raw_config) : [ raw_config ]
|
192
|
+
end
|
193
|
+
|
194
|
+
def dir
|
195
|
+
"#{@base.repo}/#{name}/"
|
196
|
+
end
|
197
|
+
|
198
|
+
def config_file
|
199
|
+
"#{dir}/config.json"
|
200
|
+
end
|
201
|
+
|
202
|
+
def read_config
|
203
|
+
JSON.parse(File.read(config_file))
|
204
|
+
rescue Errno::ENOENT
|
205
|
+
raise JudoError, "No config file #{config_file}"
|
206
|
+
end
|
207
|
+
|
208
|
+
def delete_server(server)
|
209
|
+
sdb.delete_attributes("judo_config", "groups", name => server.name)
|
210
|
+
end
|
211
|
+
|
212
|
+
def to_s
|
213
|
+
":#{name}"
|
214
|
+
end
|
215
|
+
|
216
|
+
def sdb
|
217
|
+
@base.sdb
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|