conan 0.2.1 → 0.3.5
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/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
|
-
|