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.
@@ -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
@@ -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
+