conan 0.2.1 → 0.3.5
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +1 -0
- data/lib/conan/cloud/aws/default_amis.json +74 -0
- data/lib/conan/cloud/aws/provision.rb +343 -0
- data/lib/conan/cloud/tasks.rb +16 -0
- data/lib/conan/common.rb +5 -0
- data/lib/conan/deployment.rb +24 -4
- data/lib/conan/deployment/assets.rb +61 -0
- data/lib/conan/deployment/git.rb +2 -2
- data/lib/conan/initializer.rb +5 -2
- data/lib/conan/settings.rb +3 -3
- data/lib/conan/template/CONAN_TODO +19 -1
- data/lib/conan/template/config/aws.json +110 -0
- data/lib/conan/template/config/deploy.rb +2 -0
- data/lib/conan/template/deploy/chef/dna/base.json +1 -1
- data/lib/conan/version.rb +1 -1
- metadata +60 -53
data/README.md
CHANGED
@@ -104,6 +104,7 @@ In this scenario, the configuration for the staging server will be produced by m
|
|
104
104
|
* `db.json`
|
105
105
|
* `staging.json`
|
106
106
|
|
107
|
+
|
107
108
|
Furthermore, this server will have the `app` and `db` roles in Capistrano, with
|
108
109
|
the usual meanings: the application will be deployed on this server, and the
|
109
110
|
database migrations will be run.
|
@@ -0,0 +1,74 @@
|
|
1
|
+
{
|
2
|
+
"ubuntu 10.04": {
|
3
|
+
"ap-northeast-1": {
|
4
|
+
"64-bit": {
|
5
|
+
"ebs": "ami-36e65037",
|
6
|
+
"instance-store": "ami-baf94fbb"
|
7
|
+
},
|
8
|
+
"32-bit": {
|
9
|
+
"ebs": "ami-1ae6501b",
|
10
|
+
"instance-store": "ami-46f94f47"
|
11
|
+
}
|
12
|
+
},
|
13
|
+
"ap-southeast-1": {
|
14
|
+
"64-bit": {
|
15
|
+
"ebs": "ami-c0c98c92",
|
16
|
+
"instance-store": "ami-3cc98c6e"
|
17
|
+
},
|
18
|
+
"32-bit": {
|
19
|
+
"ebs": "ami-d8c98c8a",
|
20
|
+
"instance-store": "ami-76c98c24"
|
21
|
+
}
|
22
|
+
},
|
23
|
+
"eu-west-1": {
|
24
|
+
"64-bit": {
|
25
|
+
"ebs": "ami-81dde2f5",
|
26
|
+
"instance-store": "ami-3fdde24b"
|
27
|
+
},
|
28
|
+
"32-bit": {
|
29
|
+
"ebs": "ami-95dde2e1",
|
30
|
+
"instance-store": "ami-fddee189"
|
31
|
+
}
|
32
|
+
},
|
33
|
+
"sa-east-1": {
|
34
|
+
"64-bit": {
|
35
|
+
"ebs": "ami-645f8079",
|
36
|
+
"instance-store": "ami-7a5f8067"
|
37
|
+
},
|
38
|
+
"32-bit": {
|
39
|
+
"ebs": "ami-6a5f8077",
|
40
|
+
"instance-store": "ami-4a5f8057"
|
41
|
+
}
|
42
|
+
},
|
43
|
+
"us-east-1": {
|
44
|
+
"64-bit": {
|
45
|
+
"ebs": "ami-55dc0b3c",
|
46
|
+
"instance-store": "ami-35de095c"
|
47
|
+
},
|
48
|
+
"32-bit": {
|
49
|
+
"ebs": "ami-71dc0b18",
|
50
|
+
"instance-store": "ami-4fd00726"
|
51
|
+
}
|
52
|
+
},
|
53
|
+
"us-west-1": {
|
54
|
+
"64-bit": {
|
55
|
+
"ebs": "ami-a191cfe4",
|
56
|
+
"instance-store": "ami-3991cf7c"
|
57
|
+
},
|
58
|
+
"32-bit": {
|
59
|
+
"ebs": "ami-9991cfdc",
|
60
|
+
"instance-store": "ami-b390cef6"
|
61
|
+
}
|
62
|
+
},
|
63
|
+
"us-west-2": {
|
64
|
+
"64-bit": {
|
65
|
+
"ebs": "ami-8eb33ebe",
|
66
|
+
"instance-store": "ami-94b33ea4"
|
67
|
+
},
|
68
|
+
"32-bit": {
|
69
|
+
"ebs": "ami-8cb33ebc",
|
70
|
+
"instance-store": "ami-bcb33e8c"
|
71
|
+
}
|
72
|
+
}
|
73
|
+
}
|
74
|
+
}
|
@@ -0,0 +1,343 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
require "fog"
|
3
|
+
require "json"
|
4
|
+
|
5
|
+
module AWS
|
6
|
+
class Provision
|
7
|
+
|
8
|
+
def initialize(aws_config = {}, stage = 'production')
|
9
|
+
@aws_config = aws_config
|
10
|
+
@stage = stage
|
11
|
+
end
|
12
|
+
|
13
|
+
def describe_env_to_json(file_path)
|
14
|
+
servers_json = {}
|
15
|
+
servers = []
|
16
|
+
["us-east-1", "us-west-1", "eu-west-1", "ap-southeast-1", "ap-northeast-1"].each do |region|
|
17
|
+
compute = Fog::Compute.new(:provider => :aws, :region => region)
|
18
|
+
compute.servers.all.each { |s| servers << s if s.state == 'running' }
|
19
|
+
end
|
20
|
+
stages = servers.map {|s| s.tags["stage"] }.uniq
|
21
|
+
|
22
|
+
stages.each do | stage|
|
23
|
+
servers_json[stage] = {}
|
24
|
+
servers.each do |server|
|
25
|
+
if server.tags["stage"] == stage
|
26
|
+
server_json = {}
|
27
|
+
server_json[:roles] = server.tags["roles"].split(", ") unless server.tags["roles"].nil?
|
28
|
+
server_json[:alias] = server.tags["name"]
|
29
|
+
servers_json[stage][server.dns_name] = server_json
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
File.open(file_path, "w") do |io|
|
35
|
+
io << JSON.pretty_generate(servers_json)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def build_env()
|
40
|
+
|
41
|
+
#we need to check the existance of ~/.fog
|
42
|
+
|
43
|
+
#first key_pairs
|
44
|
+
key_pairs = @aws_config["key_pairs"]
|
45
|
+
|
46
|
+
key_pairs.each do |name, conf|
|
47
|
+
create_key_pair(name, conf)
|
48
|
+
end unless key_pairs.nil?
|
49
|
+
|
50
|
+
security_groups = @aws_config["security"]
|
51
|
+
|
52
|
+
if security_groups.nil? or security_groups.size == 0
|
53
|
+
create_default_security_groups()
|
54
|
+
else
|
55
|
+
security_groups["ec2"].each do |name, conf|
|
56
|
+
create_ec2_security_group(name, conf)
|
57
|
+
end unless security_groups["ec2"].nil?
|
58
|
+
|
59
|
+
security_groups["rds"].each do |name, conf|
|
60
|
+
create_rds_security_group(name, conf)
|
61
|
+
end unless security_groups["rds"].nil?
|
62
|
+
end
|
63
|
+
|
64
|
+
@aws_config.each do |type, resources|
|
65
|
+
case type
|
66
|
+
when "ec2"
|
67
|
+
resources.each do |name, conf|
|
68
|
+
create_ec2_server(name, conf)
|
69
|
+
end
|
70
|
+
when "rds"
|
71
|
+
resources.each do |name, conf|
|
72
|
+
create_rds_server(name, conf)
|
73
|
+
end
|
74
|
+
when "s3"
|
75
|
+
resources.each do |name, conf|
|
76
|
+
create_s3_bucket(name, conf)
|
77
|
+
end
|
78
|
+
when "elb"
|
79
|
+
resources.each do |name,conf|
|
80
|
+
create_elb(name, conf)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def create_key_pair(name, config = {})
|
87
|
+
new_conf = config.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
|
88
|
+
region = new_conf[:region] || "us-east-1"
|
89
|
+
compute = Fog::Compute.new(:provider => :aws, :region => region)
|
90
|
+
|
91
|
+
kp = compute.key_pairs.get(name)
|
92
|
+
if kp.nil?
|
93
|
+
#no key pair create and write it out
|
94
|
+
puts "Creating key-pair #{name}"
|
95
|
+
kp = compute.key_pairs.create(:name => name)
|
96
|
+
|
97
|
+
puts "Writing key into your .ssh directory and adding it to ssh agent"
|
98
|
+
File.open("#{ENV['HOME']}/.ssh/#{kp.name}.pem", "w") do |io|
|
99
|
+
io << kp.private_key
|
100
|
+
end
|
101
|
+
system("chmod 600 #{ENV['HOME']}/.ssh/#{kp.name}.pem")
|
102
|
+
system("ssh-add #{ENV['HOME']}/.ssh/#{kp.name}.pem")
|
103
|
+
else
|
104
|
+
puts "Key-pair #{name} already exists. Skipping"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def create_default_security_groups()
|
109
|
+
create_ec2_security_group('default', {:ports => ["22", "80"]})
|
110
|
+
create_rds_security_group('default', {:ec2_security_groups => ["default"]})
|
111
|
+
end
|
112
|
+
|
113
|
+
def create_ec2_security_group(name, config = {})
|
114
|
+
new_conf = config.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
|
115
|
+
region = new_conf[:region] || "us-east-1"
|
116
|
+
|
117
|
+
compute = Fog::Compute.new(:provider => :aws, :region => region)
|
118
|
+
|
119
|
+
sg_name = "#{@stage}-#{name}"
|
120
|
+
|
121
|
+
sg = compute.security_groups.get(sg_name)
|
122
|
+
if sg.nil?
|
123
|
+
puts "Creating EC2 Secruity Group #{sg_name}"
|
124
|
+
sg = compute.security_groups.create(:name => sg_name, :description => "#{name} Security Group for #{@stage}")
|
125
|
+
new_conf[:ports].each do |port|
|
126
|
+
sg.authorize_port_range(Range.new(port.to_i,port.to_i))
|
127
|
+
end unless new_conf[:ports].nil?
|
128
|
+
else
|
129
|
+
puts "EC2 Security Group #{sg_name} already exists. Skipping"
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
|
134
|
+
def create_rds_security_group(name, config = {})
|
135
|
+
unless Fog.mocking?
|
136
|
+
new_conf = config.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
|
137
|
+
region = new_conf[:region] || "us-east-1"
|
138
|
+
rds = Fog::AWS::RDS.new(:region => region)
|
139
|
+
|
140
|
+
rdssg_name = "#{@stage}-db-#{name}"
|
141
|
+
|
142
|
+
rdssg = rds.security_groups.get(rdssg_name)
|
143
|
+
|
144
|
+
if rdssg.nil?
|
145
|
+
puts "Creating RDS Security Group #{rdssg_name}"
|
146
|
+
rdssg = rds.security_groups.create(:id => rdssg_name, :description => "#{name} DB Security Group For #{@stage}")
|
147
|
+
new_conf[:ec2_security_groups].each do |ec2_group|
|
148
|
+
rdssg.authorize_ec2_security_group("#{@stage}-#{ec2_group}")
|
149
|
+
end
|
150
|
+
else
|
151
|
+
puts "RDS Security Group #{rdssg_name} already exists. Skipping"
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def create_s3_bucket(name, config = {})
|
158
|
+
new_conf = config.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
|
159
|
+
region = new_conf[:region] || "us-east-1"
|
160
|
+
|
161
|
+
storage = Fog::Storage.new(:provider => :aws, :region => region)
|
162
|
+
|
163
|
+
begin
|
164
|
+
bucket = storage.get_bucket(name)
|
165
|
+
puts "S3 Bucket #{name} already exists. Skipping"
|
166
|
+
rescue Excon::Errors::NotFound
|
167
|
+
puts "Creating S3 Bucket #{name}"
|
168
|
+
storage.put_bucket(name)
|
169
|
+
|
170
|
+
acl = new_conf[:acl] || 'public-read'
|
171
|
+
|
172
|
+
storage.put_bucket_acl(name, acl)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def create_elb(name, config = {})
|
177
|
+
new_conf = config.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
|
178
|
+
region = new_conf[:region] || "us-east-1"
|
179
|
+
|
180
|
+
elb = Fog::AWS::ELB.new(:region => region)
|
181
|
+
|
182
|
+
zones = new_conf[:availability_zones] || all_availability_zones(region)
|
183
|
+
|
184
|
+
elb_name = "#{@stage}-#{name}"
|
185
|
+
|
186
|
+
lb = elb.load_balancers.get(elb_name)
|
187
|
+
if lb.nil?
|
188
|
+
puts "Creating Elastic Load Balancer #{elb_name}"
|
189
|
+
|
190
|
+
lb = elb.load_balancers.create(:id => elb_name, :availability_zones => zones)
|
191
|
+
|
192
|
+
#need to do listeners but for now stick with the default
|
193
|
+
|
194
|
+
compute = Fog::Compute.new(:provider => :aws, :region => region)
|
195
|
+
|
196
|
+
inst_servers = new_conf[:servers].map { |s| "#{@stage}-#{s}" } unless new_conf[:servers].nil?
|
197
|
+
|
198
|
+
compute.servers.all.each do |server|
|
199
|
+
#the mocking seems wrong
|
200
|
+
unless Fog.mocking?
|
201
|
+
name = server.tags["name"]
|
202
|
+
lb.register_instances(server.id) if (inst_servers.nil? or inst_servers.include?(name))
|
203
|
+
end
|
204
|
+
end
|
205
|
+
else
|
206
|
+
puts "Elastic Load Balancer #{elb_name} already exists. Skipping"
|
207
|
+
end
|
208
|
+
|
209
|
+
end
|
210
|
+
|
211
|
+
def create_ec2_server(name, config = {})
|
212
|
+
new_conf = config.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
|
213
|
+
region = new_conf[:region] || "us-east-1"
|
214
|
+
|
215
|
+
compute = Fog::Compute.new(:provider => :aws, :region => region)
|
216
|
+
|
217
|
+
default_key_name = compute.key_pairs.all.first.name
|
218
|
+
|
219
|
+
default_params = { :availability_zone => default_availability_zone(region),
|
220
|
+
:flavor_id => "m1.small",
|
221
|
+
:monitoring => true,
|
222
|
+
:key_name => default_key_name,
|
223
|
+
:root_device_type => "instance-store"
|
224
|
+
}
|
225
|
+
|
226
|
+
params = default_params.merge(new_conf)
|
227
|
+
params[:image_id] = default_image_id(region, params[:flavor_id], params[:root_device_type]) if params[:image_id].nil?
|
228
|
+
|
229
|
+
tags = {}
|
230
|
+
|
231
|
+
#need to bork if no name in config file
|
232
|
+
ec2_name = "#{@stage}-#{name}"
|
233
|
+
|
234
|
+
#need to parse all servers to see if this one exists
|
235
|
+
servers = compute.servers.all
|
236
|
+
server_exists = false
|
237
|
+
servers.each { |server| server_exists = true if server.tags["name"] == ec2_name and server.state == 'running' }
|
238
|
+
|
239
|
+
unless server_exists
|
240
|
+
puts "Creating EC2 Server named #{ec2_name}"
|
241
|
+
tags[:name] = ec2_name
|
242
|
+
tags[:stage] = @stage
|
243
|
+
|
244
|
+
if new_conf[:roles]
|
245
|
+
new_conf[:roles] << @stage unless new_conf[:roles].include? @stage
|
246
|
+
tags[:roles] = new_conf[:roles].join(', ')
|
247
|
+
else
|
248
|
+
tags[:roles] = "app, db, #{@stage}"
|
249
|
+
end
|
250
|
+
|
251
|
+
params[:tags] = tags
|
252
|
+
|
253
|
+
if new_conf[:groups] and new_conf[:groups].size > 0
|
254
|
+
params[:groups] = new_conf[:groups].collect { |g| "#{@stage}-#{g}" }
|
255
|
+
else
|
256
|
+
params[:groups] = ["#{@stage}-default"]
|
257
|
+
end
|
258
|
+
|
259
|
+
server = compute.servers.create(params)
|
260
|
+
server.wait_for { ready? }
|
261
|
+
else
|
262
|
+
puts "EC2 Server named #{ec2_name} already exists. Skipping"
|
263
|
+
end
|
264
|
+
|
265
|
+
end
|
266
|
+
|
267
|
+
def create_rds_server(name, config = {})
|
268
|
+
unless Fog.mocking?
|
269
|
+
new_conf = config.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
|
270
|
+
region = new_conf[:region] || "us-east-1"
|
271
|
+
rds = Fog::AWS::RDS.new(:region => region)
|
272
|
+
default_params = { :allocated_storage => 20,
|
273
|
+
:engine => 'mysql',
|
274
|
+
:master_username => 'root',
|
275
|
+
:password => 'password',
|
276
|
+
:backup_retention_period => 8,
|
277
|
+
:multi_az => false,
|
278
|
+
:db_name =>"production",
|
279
|
+
:availability_zone => default_availability_zone(region),
|
280
|
+
:flavor_id => 'db.m1.small'
|
281
|
+
}
|
282
|
+
params = default_params.merge(new_conf)
|
283
|
+
|
284
|
+
params[:id] = "#{@stage}-#{name}"
|
285
|
+
|
286
|
+
if new_conf[:db_security_groups] and new_conf[:db_security_groups].size > 0
|
287
|
+
params[:db_security_groups] = new_conf[:db_security_groups].collect { |g| "#{@stage}-db-#{g}" }
|
288
|
+
else
|
289
|
+
params[:db_security_groups] = ["#{@stage}-db-default"]
|
290
|
+
end
|
291
|
+
|
292
|
+
server = rds.servers.get(params[:id])
|
293
|
+
|
294
|
+
if server.nil?
|
295
|
+
puts "Creating RDS Server #{params[:id]}"
|
296
|
+
server = rds.servers.create(params)
|
297
|
+
else
|
298
|
+
puts "RDS Server #{params[:id]} aready exists. Skipping"
|
299
|
+
end
|
300
|
+
else
|
301
|
+
puts "Fog is mocking. Can't test with RDS Servers. Skipping"
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
private
|
306
|
+
|
307
|
+
def default_availability_zone(region)
|
308
|
+
#using availability zone b by default as a is often unavailable in us-east-1
|
309
|
+
"#{region}b"
|
310
|
+
end
|
311
|
+
|
312
|
+
def all_availability_zones(region)
|
313
|
+
case region
|
314
|
+
when "us-east-1"
|
315
|
+
#not using availability zone us-east-1a as it often fails
|
316
|
+
["us-east-1b", "us-east-1c"]
|
317
|
+
when "us-west-1"
|
318
|
+
["us-west-1a", "us-west-1b", "us-west-1c"]
|
319
|
+
when "eu-west-1"
|
320
|
+
["eu-west-1a", "eu-west-1b"]
|
321
|
+
when "ap-northeast-1"
|
322
|
+
["ap-northeast-1a", "ap-northeast-1a"]
|
323
|
+
when "ap-southeast-1"
|
324
|
+
["ap-southeast-1a", "ap-southeast-1b"]
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
def default_image_id(region, flavor_id, root_device_type)
|
329
|
+
arch = ["m1.small", "t1.micro", "c1.medium"].include?(flavor_id) ? "32-bit" : "64-bit"
|
330
|
+
|
331
|
+
defaults = JSON.parse(File.read(File.expand_path(File.join(File.dirname(__FILE__), 'default_amis.json'))))
|
332
|
+
|
333
|
+
region_defaults = defaults["ubuntu 10.04"][region]
|
334
|
+
raise "Invalid Region" if region_defaults.nil?
|
335
|
+
default_ami = region_defaults[arch][root_device_type]
|
336
|
+
|
337
|
+
raise "Default AMI not found for #{region} #{flavor_id} #{root_device_type}" if default_ami.nil?
|
338
|
+
default_ami
|
339
|
+
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
require "json"
|
3
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'aws/provision'))
|
4
|
+
|
5
|
+
namespace :aws do
|
6
|
+
|
7
|
+
task :provision do
|
8
|
+
aws_config = JSON.parse(File.read("config/aws.json"))[stage] || {}
|
9
|
+
AWS::Provision.new(aws_config, stage).build_env
|
10
|
+
end
|
11
|
+
|
12
|
+
task :write_config do
|
13
|
+
AWS::Provision.new.describe_env_to_json("config/servers.json")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
data/lib/conan/common.rb
ADDED
data/lib/conan/deployment.rb
CHANGED
@@ -1,6 +1,12 @@
|
|
1
1
|
module Conan
|
2
2
|
class Deployment
|
3
3
|
module Helpers
|
4
|
+
def _cset(name, *args, &block)
|
5
|
+
unless exists?(name)
|
6
|
+
set(name, *args, &block)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
4
10
|
def with_user(new_user, &blk)
|
5
11
|
old_user = user
|
6
12
|
return if old_user == new_user
|
@@ -43,13 +49,27 @@ module Conan
|
|
43
49
|
|
44
50
|
class <<self
|
45
51
|
def define_tasks(context)
|
46
|
-
load_script(context, "deploy")
|
47
|
-
load_script(context, "chef")
|
48
|
-
|
52
|
+
load_script(context, "deployment/deploy")
|
53
|
+
load_script(context, "deployment/chef")
|
54
|
+
|
55
|
+
load_script(context, "deployment/git")
|
56
|
+
|
57
|
+
load_script(context, "cloud/tasks")
|
58
|
+
|
59
|
+
#need to change to only compile for rails 3.1
|
60
|
+
begin
|
61
|
+
rails_v = `bundle exec rails -v`.chomp.split(' ').last
|
62
|
+
if Gem::Version.new(rails_v) > Gem::Version.new('3.1.0')
|
63
|
+
load_script(context, "deployment/assets")
|
64
|
+
end
|
65
|
+
rescue
|
66
|
+
#not a rails or 3.1 app or bundle failed
|
67
|
+
end
|
68
|
+
|
49
69
|
end
|
50
70
|
|
51
71
|
def load_script(context, fragment)
|
52
|
-
path = File.expand_path("
|
72
|
+
path = File.expand_path("../#{fragment}.rb", __FILE__)
|
53
73
|
code = File.read(path)
|
54
74
|
context.instance_eval(code, path)
|
55
75
|
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
def _cset(name, *args, &block)
|
2
|
+
unless exists?(name)
|
3
|
+
set(name, *args, &block)
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
_cset :asset_env, "RAILS_GROUPS=assets"
|
8
|
+
_cset :assets_prefix, "assets"
|
9
|
+
|
10
|
+
before 'deploy:finalize_update', 'deploy:assets:symlink'
|
11
|
+
after 'deploy:update_code', 'deploy:assets:precompile'
|
12
|
+
|
13
|
+
namespace :deploy do
|
14
|
+
namespace :assets do
|
15
|
+
desc <<-DESC
|
16
|
+
[internal] This task will set up a symlink to the shared directory \
|
17
|
+
for the assets directory. Assets are shared across deploys to avoid \
|
18
|
+
mid-deploy mismatches between old application html asking for assets \
|
19
|
+
and getting a 404 file not found error. The assets cache is shared \
|
20
|
+
for efficiency. If you cutomize the assets path prefix, override the \
|
21
|
+
:assets_prefix variable to match.
|
22
|
+
DESC
|
23
|
+
task :symlink, :roles => :app, :except => { :no_release => true } do
|
24
|
+
run <<-CMD
|
25
|
+
rm -rf #{latest_release}/public/#{assets_prefix} &&
|
26
|
+
mkdir -p #{latest_release}/public &&
|
27
|
+
mkdir -p #{shared_path}/assets &&
|
28
|
+
ln -s #{shared_path}/assets #{latest_release}/public/#{assets_prefix}
|
29
|
+
CMD
|
30
|
+
end
|
31
|
+
|
32
|
+
desc <<-DESC
|
33
|
+
Run the asset precompilation rake task. You can specify the full path \
|
34
|
+
to the rake executable by setting the rake variable. You can also \
|
35
|
+
specify additional environment variables to pass to rake via the \
|
36
|
+
asset_env variable. The defaults are:
|
37
|
+
|
38
|
+
set :rake, "rake"
|
39
|
+
set :rails_env, "production"
|
40
|
+
set :asset_env, "RAILS_GROUPS=assets"
|
41
|
+
DESC
|
42
|
+
task :precompile, :roles => :app, :except => { :no_release => true } do
|
43
|
+
run "cd #{latest_release} && #{rake} RAILS_ENV=#{rails_env} #{asset_env} assets:precompile"
|
44
|
+
end
|
45
|
+
|
46
|
+
desc <<-DESC
|
47
|
+
Run the asset clean rake task. Use with caution, this will delete \
|
48
|
+
all of your compiled assets. You can specify the full path \
|
49
|
+
to the rake executable by setting the rake variable. You can also \
|
50
|
+
specify additional environment variables to pass to rake via the \
|
51
|
+
asset_env variable. The defaults are:
|
52
|
+
|
53
|
+
set :rake, "rake"
|
54
|
+
set :rails_env, "production"
|
55
|
+
set :asset_env, "RAILS_GROUPS=assets"
|
56
|
+
DESC
|
57
|
+
task :clean, :roles => :app, :except => { :no_release => true } do
|
58
|
+
run "cd #{latest_release} && #{rake} RAILS_ENV=#{rails_env} #{asset_env} assets:clean"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/conan/deployment/git.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
namespace :git do
|
2
|
-
before "deploy:update_code", "git:tag_attempted_deploy"
|
2
|
+
before "deploy:update_code", "git:tag_attempted_deploy" unless deploy_via == :copy
|
3
3
|
task :tag_attempted_deploy do
|
4
4
|
git_tag branch, "#{stage}.last-deploy"
|
5
5
|
end
|
@@ -7,5 +7,5 @@ namespace :git do
|
|
7
7
|
task :tag_successful_deploy do
|
8
8
|
git_tag branch, "#{stage}.last-successful-deploy"
|
9
9
|
end
|
10
|
-
after "deploy:smoke_test", "git:tag_successful_deploy"
|
10
|
+
after "deploy:smoke_test", "git:tag_successful_deploy" unless deploy_via == :copy
|
11
11
|
end
|
data/lib/conan/initializer.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
require "fileutils"
|
2
|
+
require "rubygems"
|
3
|
+
require "bundler/setup"
|
2
4
|
|
3
5
|
module Conan
|
4
6
|
class Initializer
|
@@ -11,7 +13,7 @@ module Conan
|
|
11
13
|
|
12
14
|
def initialize(where, settings)
|
13
15
|
@destination = File.expand_path(where)
|
14
|
-
@settings = settings
|
16
|
+
@settings = Conan::Settings.new(settings)
|
15
17
|
end
|
16
18
|
|
17
19
|
def run
|
@@ -32,7 +34,8 @@ module Conan
|
|
32
34
|
|
33
35
|
def add_git_submodule
|
34
36
|
return unless File.directory?(".git")
|
35
|
-
|
37
|
+
return if File.directory?("deploy/chef/recipes/cookbooks")
|
38
|
+
sh "git submodule add #{@settings["COOKBOOK_REPOSITORY"]} deploy/chef/recipes/cookbooks #{@settings["COOKBOOK_BRANCH"]} >/dev/null 2>&1"
|
36
39
|
end
|
37
40
|
|
38
41
|
def copy_template
|
data/lib/conan/settings.rb
CHANGED
@@ -2,8 +2,8 @@ require "conan/version"
|
|
2
2
|
|
3
3
|
module Conan
|
4
4
|
class Settings
|
5
|
-
def initialize
|
6
|
-
@settings = defaults
|
5
|
+
def initialize(settings = {})
|
6
|
+
@settings = defaults.merge(settings)
|
7
7
|
end
|
8
8
|
|
9
9
|
def []=(k, v)
|
@@ -19,7 +19,7 @@ module Conan
|
|
19
19
|
{
|
20
20
|
"APPLICATION" => application_from_git_remote,
|
21
21
|
"COOKBOOK_REPOSITORY" => "git://github.com/madebymany/cookbooks.git",
|
22
|
-
"COOKBOOK_BRANCH" => "
|
22
|
+
"COOKBOOK_BRANCH" => "master",
|
23
23
|
"VERSION" => Conan::VERSION,
|
24
24
|
}
|
25
25
|
end
|
@@ -1,6 +1,24 @@
|
|
1
1
|
Conan the Deployer has set up your project.
|
2
2
|
|
3
|
-
|
3
|
+
Optional: if you are use AWS you can provision instances from a json file
|
4
|
+
|
5
|
+
* Add or edit ~/.fog to include your AWS access and secret key: It should look something like:
|
6
|
+
:default:
|
7
|
+
:aws_access_key_id: AithisismykeyYxDr
|
8
|
+
:aws_secret_access_key: PeT/thisismysecretaccesskey9HyE
|
9
|
+
|
10
|
+
* Edit config/aws.json to describe your AWS environments
|
11
|
+
|
12
|
+
Now you can use capistrano to provision your environment
|
13
|
+
|
14
|
+
* cap staging aws:provision
|
15
|
+
|
16
|
+
Then you can write out the config/servers.json which will be used in the next steps
|
17
|
+
|
18
|
+
* cap staging aws:write_config
|
19
|
+
(this will actually write out all environments so you may need to re-run it if you provision a new environment)
|
20
|
+
|
21
|
+
Next, set up your deployment or if you are not using AWS start her:
|
4
22
|
|
5
23
|
* Edit config/servers.json to specify your stages, servers, and roles
|
6
24
|
|
@@ -0,0 +1,110 @@
|
|
1
|
+
{
|
2
|
+
"staging": {
|
3
|
+
"key_pairs": {
|
4
|
+
"keypair-name": {
|
5
|
+
"region": "us-east-1"
|
6
|
+
}
|
7
|
+
},
|
8
|
+
"ec2": {
|
9
|
+
"app1": {
|
10
|
+
"region": "us-east-1",
|
11
|
+
"monitoring": false,
|
12
|
+
"roles": ["app", "db", "staging"],
|
13
|
+
"flavor_id": "m1.small",
|
14
|
+
"availability_zone": "us-east-1b",
|
15
|
+
"groups": ["default"]
|
16
|
+
}
|
17
|
+
},
|
18
|
+
"rds": {
|
19
|
+
"db1": {
|
20
|
+
"region": "us-east-1",
|
21
|
+
"flavor_id": "db.m1.small",
|
22
|
+
"backup_retention_period": "1",
|
23
|
+
"multi_az": false,
|
24
|
+
"availability_zone": "us-east-1b",
|
25
|
+
"db_security_groups": ["default"],
|
26
|
+
"db_name": "app_production",
|
27
|
+
"master_username": "root",
|
28
|
+
"password": "TODO"
|
29
|
+
}
|
30
|
+
},
|
31
|
+
"security": {
|
32
|
+
"ec2": {
|
33
|
+
"default": {
|
34
|
+
"region": "us-east-1",
|
35
|
+
"ports": ["22", "80"]
|
36
|
+
}
|
37
|
+
},
|
38
|
+
"rds": {
|
39
|
+
"default": {
|
40
|
+
"region": "us-east-1",
|
41
|
+
"ec2_security_groups": ["default"]
|
42
|
+
}
|
43
|
+
}
|
44
|
+
},
|
45
|
+
"s3": {
|
46
|
+
"bucket-name": {
|
47
|
+
"region": "us-east-1"
|
48
|
+
}
|
49
|
+
}
|
50
|
+
},
|
51
|
+
"production": {
|
52
|
+
"ec2": {
|
53
|
+
"app1": {
|
54
|
+
"region": "us-east-1",
|
55
|
+
"monitoring": true,
|
56
|
+
"roles": ["app", "db", "production"],
|
57
|
+
"flavor_id": "m1.small",
|
58
|
+
"availability_zone": "us-east-1b",
|
59
|
+
"groups": ["default"]
|
60
|
+
},
|
61
|
+
"app2": {
|
62
|
+
"region": "us-east-1",
|
63
|
+
"roles": ["app", "db", "production"],
|
64
|
+
"flavor_id": "m1.small",
|
65
|
+
"availability_zone": "us-east-1b",
|
66
|
+
"groups": ["default"]
|
67
|
+
}
|
68
|
+
},
|
69
|
+
"rds": {
|
70
|
+
"db1": {
|
71
|
+
"region": "us-east-1",
|
72
|
+
"multi_az": true,
|
73
|
+
"flavor_id": "db.m1.small",
|
74
|
+
"allocated_storage": 100,
|
75
|
+
"availability_zone": "us-east-1b",
|
76
|
+
"db_security_groups": ["default"],
|
77
|
+
"backup_retention_period": "8",
|
78
|
+
"db_name": "app_production",
|
79
|
+
"master_username": "root",
|
80
|
+
"password": "TODO"
|
81
|
+
}
|
82
|
+
},
|
83
|
+
"elb": {
|
84
|
+
"lb1": {
|
85
|
+
"region": "us-east-1",
|
86
|
+
"servers": ["app1", "app2"],
|
87
|
+
"https": "false"
|
88
|
+
}
|
89
|
+
},
|
90
|
+
"security": {
|
91
|
+
"ec2": {
|
92
|
+
"default": {
|
93
|
+
"region": "us-east-1",
|
94
|
+
"ports": ["22", "80"]
|
95
|
+
}
|
96
|
+
},
|
97
|
+
"rds": {
|
98
|
+
"default": {
|
99
|
+
"region": "us-east-1",
|
100
|
+
"ec2_security_groups": ["default"]
|
101
|
+
}
|
102
|
+
}
|
103
|
+
},
|
104
|
+
"s3": {
|
105
|
+
"test": {
|
106
|
+
"region": "us-east-1"
|
107
|
+
}
|
108
|
+
}
|
109
|
+
}
|
110
|
+
}
|
data/lib/conan/version.rb
CHANGED
metadata
CHANGED
@@ -1,49 +1,66 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: conan
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.5
|
5
5
|
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 2
|
9
|
-
- 1
|
10
|
-
version: 0.2.1
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Paul Battley
|
9
|
+
- Stuart Eccles
|
14
10
|
autorequire:
|
15
11
|
bindir: bin
|
16
12
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
dependencies:
|
21
|
-
- !ruby/object:Gem::Dependency
|
13
|
+
date: 2012-02-15 00:00:00.000000000Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
22
16
|
name: json
|
17
|
+
requirement: &70097716738400 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ~>
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.6.1
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *70097716738400
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: capistrano
|
28
|
+
requirement: &70097716737480 !ruby/object:Gem::Requirement
|
29
|
+
none: false
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
23
35
|
prerelease: false
|
24
|
-
|
36
|
+
version_requirements: *70097716737480
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: fog
|
39
|
+
requirement: &70097716735820 !ruby/object:Gem::Requirement
|
25
40
|
none: false
|
26
|
-
requirements:
|
27
|
-
- -
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
|
30
|
-
segments:
|
31
|
-
- 0
|
32
|
-
version: "0"
|
41
|
+
requirements:
|
42
|
+
- - ! '>='
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
33
45
|
type: :runtime
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
46
|
+
prerelease: false
|
47
|
+
version_requirements: *70097716735820
|
48
|
+
description: Set up a project to enable the provision of infrastructure through AWS
|
49
|
+
and the configuration of servers using Chef via Capistrano.
|
50
|
+
email:
|
51
|
+
- stuart@madebymany.co.uk
|
52
|
+
executables:
|
39
53
|
- conan
|
40
54
|
extensions: []
|
41
|
-
|
42
55
|
extra_rdoc_files: []
|
43
|
-
|
44
|
-
files:
|
56
|
+
files:
|
45
57
|
- bin/conan
|
46
58
|
- lib/conan/capistrano.rb
|
59
|
+
- lib/conan/cloud/aws/default_amis.json
|
60
|
+
- lib/conan/cloud/aws/provision.rb
|
61
|
+
- lib/conan/cloud/tasks.rb
|
62
|
+
- lib/conan/common.rb
|
63
|
+
- lib/conan/deployment/assets.rb
|
47
64
|
- lib/conan/deployment/chef.rb
|
48
65
|
- lib/conan/deployment/deploy.rb
|
49
66
|
- lib/conan/deployment/git.rb
|
@@ -53,6 +70,7 @@ files:
|
|
53
70
|
- lib/conan/smart_hash_merge.rb
|
54
71
|
- lib/conan/template/Capfile
|
55
72
|
- lib/conan/template/CONAN_TODO
|
73
|
+
- lib/conan/template/config/aws.json
|
56
74
|
- lib/conan/template/config/deploy.rb
|
57
75
|
- lib/conan/template/config/servers.json
|
58
76
|
- lib/conan/template/deploy/chef/dna/app.json
|
@@ -69,39 +87,28 @@ files:
|
|
69
87
|
- lib/conan/template/deploy/chef/solo.rb
|
70
88
|
- lib/conan/version.rb
|
71
89
|
- README.md
|
72
|
-
has_rdoc: true
|
73
90
|
homepage: http://github.com/madebymany/conan
|
74
91
|
licenses: []
|
75
|
-
|
76
92
|
post_install_message:
|
77
93
|
rdoc_options: []
|
78
|
-
|
79
|
-
require_paths:
|
94
|
+
require_paths:
|
80
95
|
- lib
|
81
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
96
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
82
97
|
none: false
|
83
|
-
requirements:
|
84
|
-
- -
|
85
|
-
- !ruby/object:Gem::Version
|
86
|
-
|
87
|
-
|
88
|
-
- 0
|
89
|
-
version: "0"
|
90
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
103
|
none: false
|
92
|
-
requirements:
|
93
|
-
- -
|
94
|
-
- !ruby/object:Gem::Version
|
95
|
-
|
96
|
-
segments:
|
97
|
-
- 0
|
98
|
-
version: "0"
|
104
|
+
requirements:
|
105
|
+
- - ! '>='
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: '0'
|
99
108
|
requirements: []
|
100
|
-
|
101
109
|
rubyforge_project:
|
102
|
-
rubygems_version: 1.
|
110
|
+
rubygems_version: 1.8.10
|
103
111
|
signing_key:
|
104
112
|
specification_version: 3
|
105
113
|
summary: Conan The Deployer
|
106
114
|
test_files: []
|
107
|
-
|