conan 0.3.5 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/conan/capistrano.rb +2 -0
- data/lib/conan/cloud/aws/autoscale.rb +106 -0
- data/lib/conan/cloud/aws/default_amis.json +28 -28
- data/lib/conan/cloud/aws/provision.rb +133 -76
- data/lib/conan/cloud/aws/security_group.rb +37 -0
- data/lib/conan/cloud/aws/utils.rb +81 -0
- data/lib/conan/cloud/tasks.rb +32 -3
- data/lib/conan/deployment.rb +1 -2
- data/lib/conan/deployment/chef.rb +0 -1
- data/lib/conan/deployment/deploy.rb +10 -1
- data/lib/conan/template/config/autoscale.json +15 -0
- data/lib/conan/template/config/aws.json +35 -2
- data/lib/conan/version.rb +1 -1
- metadata +12 -8
data/lib/conan/capistrano.rb
CHANGED
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'fog'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
require_relative "./utils"
|
6
|
+
|
7
|
+
module AWS
|
8
|
+
class Autoscale
|
9
|
+
include Utils
|
10
|
+
|
11
|
+
attr_accessor :autoscale_config, :stage, :application
|
12
|
+
|
13
|
+
def initialize(stage = 'production', autoscale_config = {}, application = nil)
|
14
|
+
@autoscale_config = autoscale_config
|
15
|
+
@stage = stage
|
16
|
+
@application = application
|
17
|
+
end
|
18
|
+
|
19
|
+
def create_ami_from_server(server_name, region, image_description = nil)
|
20
|
+
image_name = "#{server_name}-image-#{Time.now.strftime('%Y%m%d%H%M')}" if image_name.nil?
|
21
|
+
image_description = "Image of #{server_name} created at #{DateTime.now} by conan"
|
22
|
+
server_to_image = find_server_by_name(server_name, region)
|
23
|
+
|
24
|
+
raise "Server #{server_name} in #{region} does not exist" if server_to_image.nil?
|
25
|
+
|
26
|
+
compute = Fog::Compute.new(:provider => :aws, :region => region)
|
27
|
+
puts "Creating image #{image_name} from server #{server_name}"
|
28
|
+
ami_request = compute.create_image(server_to_image.id, image_name, image_description)
|
29
|
+
image_id = ami_request.body["imageId"]
|
30
|
+
|
31
|
+
pending = true
|
32
|
+
image = nil
|
33
|
+
|
34
|
+
puts "Waiting for #{image_id} to become available"
|
35
|
+
while pending
|
36
|
+
sleep 10
|
37
|
+
image = compute.images.get(image_id)
|
38
|
+
pending = image.state == "pending"
|
39
|
+
end
|
40
|
+
|
41
|
+
raise "Image creation failed" if image.state == 'failed'
|
42
|
+
image_id
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
def create_launch_config(name, config = {})
|
47
|
+
new_conf = config.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
|
48
|
+
region = new_conf[:region] || "us-east-1"
|
49
|
+
|
50
|
+
compute = Fog::Compute.new(:provider => :aws, :region => region)
|
51
|
+
default_key_name = compute.key_pairs.all.first.name
|
52
|
+
|
53
|
+
default_params = { :instance_monitoring => true,
|
54
|
+
:instance_type => "m1.small",
|
55
|
+
:key_name => default_key_name
|
56
|
+
}
|
57
|
+
|
58
|
+
params = default_params.merge(new_conf)
|
59
|
+
|
60
|
+
if new_conf[:security_groups] and new_conf[:security_groups].size > 0
|
61
|
+
params[:security_groups] = new_conf[:security_groups].collect { |g| "#{stage}-#{g}" }
|
62
|
+
else
|
63
|
+
params[:security_groups] = ["#{stage}-default"]
|
64
|
+
end
|
65
|
+
|
66
|
+
if params[:server_to_image].nil?
|
67
|
+
params[:image_id] = default_image_id(region, params[:flavor_id], params[:root_device_type]) if params[:image_id].nil?
|
68
|
+
else
|
69
|
+
params[:image_id] = create_ami_from_server(params[:server_to_image], region) end
|
70
|
+
|
71
|
+
autoscale = Fog::AWS::AutoScaling.new(:region => region)
|
72
|
+
|
73
|
+
#need to create new launchconfiugrations with unique names
|
74
|
+
#because you can't modify them and you can't delete them if they are attached
|
75
|
+
#to an autoscale group
|
76
|
+
params[:id] = "#{ec2_name_tag(name)}-#{Time.now.strftime('%Y%m%d%H%M')}"
|
77
|
+
|
78
|
+
puts "Creating Autoscale Launch Configuration #{params[:id]}"
|
79
|
+
lc = autoscale.configurations.create(params)
|
80
|
+
|
81
|
+
params[:autoscale_groups].each do |group_name|
|
82
|
+
asg = autoscale.groups.get(group_name)
|
83
|
+
"Setting Autoscale Group #{group_name} to use Launch Configration #{params[:id]}"
|
84
|
+
asg.connection.update_auto_scaling_group(asg.id, {"LaunchConfigurationName" => params[:id]})
|
85
|
+
end unless params[:autoscale_groups].nil?
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
def configure_autoscale
|
90
|
+
end
|
91
|
+
|
92
|
+
def update_autoscale
|
93
|
+
autoscale_config.each do |type, resources|
|
94
|
+
case type
|
95
|
+
when "launch-config"
|
96
|
+
resources.each do |name, conf|
|
97
|
+
create_launch_config(name, conf)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
|
@@ -2,72 +2,72 @@
|
|
2
2
|
"ubuntu 10.04": {
|
3
3
|
"ap-northeast-1": {
|
4
4
|
"64-bit": {
|
5
|
-
"ebs": "ami-
|
6
|
-
"instance-store": "ami-
|
5
|
+
"ebs": "ami-942f9995",
|
6
|
+
"instance-store": "ami-902f9991"
|
7
7
|
},
|
8
8
|
"32-bit": {
|
9
|
-
"ebs": "ami-
|
10
|
-
"instance-store": "ami-
|
9
|
+
"ebs": "ami-922f9993",
|
10
|
+
"instance-store": "ami-842f9985"
|
11
11
|
}
|
12
12
|
},
|
13
13
|
"ap-southeast-1": {
|
14
14
|
"64-bit": {
|
15
|
-
"ebs": "ami-
|
16
|
-
"instance-store": "ami-
|
15
|
+
"ebs": "ami-7089cd22",
|
16
|
+
"instance-store": "ami-4e89cd1c"
|
17
17
|
},
|
18
18
|
"32-bit": {
|
19
|
-
"ebs": "ami-
|
20
|
-
"instance-store": "ami-
|
19
|
+
"ebs": "ami-7289cd20",
|
20
|
+
"instance-store": "ami-4489cd16"
|
21
21
|
}
|
22
22
|
},
|
23
23
|
"eu-west-1": {
|
24
24
|
"64-bit": {
|
25
|
-
"ebs": "ami-
|
26
|
-
"instance-store": "ami-
|
25
|
+
"ebs": "ami-fb665f8f",
|
26
|
+
"instance-store": "ami-1b665f6f"
|
27
27
|
},
|
28
28
|
"32-bit": {
|
29
|
-
"ebs": "ami-
|
30
|
-
"instance-store": "ami-
|
29
|
+
"ebs": "ami-f3665f87",
|
30
|
+
"instance-store": "ami-39665f4d"
|
31
31
|
}
|
32
32
|
},
|
33
33
|
"sa-east-1": {
|
34
34
|
"64-bit": {
|
35
|
-
"ebs": "ami-
|
36
|
-
"instance-store": "ami-
|
35
|
+
"ebs": "ami-7874ab65",
|
36
|
+
"instance-store": "ami-7c74ab61"
|
37
37
|
},
|
38
38
|
"32-bit": {
|
39
|
-
"ebs": "ami-
|
40
|
-
"instance-store": "ami-
|
39
|
+
"ebs": "ami-7e74ab63",
|
40
|
+
"instance-store": "ami-4674ab5b"
|
41
41
|
}
|
42
42
|
},
|
43
43
|
"us-east-1": {
|
44
44
|
"64-bit": {
|
45
|
-
"ebs": "ami-
|
46
|
-
"instance-store": "ami-
|
45
|
+
"ebs": "ami-349b495d",
|
46
|
+
"instance-store": "ami-5c9b4935"
|
47
47
|
},
|
48
48
|
"32-bit": {
|
49
|
-
"ebs": "ami-
|
50
|
-
"instance-store": "ami-
|
49
|
+
"ebs": "ami-3e9b4957",
|
50
|
+
"instance-store": "ami-809a48e9"
|
51
51
|
}
|
52
52
|
},
|
53
53
|
"us-west-1": {
|
54
54
|
"64-bit": {
|
55
|
-
"ebs": "ami-
|
56
|
-
"instance-store": "ami-
|
55
|
+
"ebs": "ami-7fb0e93a",
|
56
|
+
"instance-store": "ami-6bb0e92e"
|
57
57
|
},
|
58
58
|
"32-bit": {
|
59
|
-
"ebs": "ami-
|
60
|
-
"instance-store": "ami-
|
59
|
+
"ebs": "ami-7db0e938",
|
60
|
+
"instance-store": "ami-63b0e926"
|
61
61
|
}
|
62
62
|
},
|
63
63
|
"us-west-2": {
|
64
64
|
"64-bit": {
|
65
|
-
"ebs": "ami-
|
66
|
-
"instance-store": "ami-
|
65
|
+
"ebs": "ami-ec0b86dc",
|
66
|
+
"instance-store": "ami-e60b86d6"
|
67
67
|
},
|
68
68
|
"32-bit": {
|
69
|
-
"ebs": "ami-
|
70
|
-
"instance-store": "ami-
|
69
|
+
"ebs": "ami-ea0b86da",
|
70
|
+
"instance-store": "ami-e00b86d0"
|
71
71
|
}
|
72
72
|
}
|
73
73
|
}
|
@@ -2,52 +2,60 @@ require "fileutils"
|
|
2
2
|
require "fog"
|
3
3
|
require "json"
|
4
4
|
|
5
|
+
require_relative "./utils"
|
6
|
+
require_relative "./security_group"
|
7
|
+
|
5
8
|
module AWS
|
9
|
+
|
6
10
|
class Provision
|
11
|
+
include Utils
|
12
|
+
|
13
|
+
attr_accessor :aws_config, :stage, :application
|
7
14
|
|
8
|
-
def initialize(aws_config = {},
|
15
|
+
def initialize(stage = 'production', aws_config = {}, application = nil)
|
9
16
|
@aws_config = aws_config
|
10
17
|
@stage = stage
|
18
|
+
@application = application
|
11
19
|
end
|
12
20
|
|
13
|
-
def
|
14
|
-
|
21
|
+
def describe_env(filter_role = nil)
|
22
|
+
server_config = {}
|
15
23
|
servers = []
|
16
|
-
|
24
|
+
all_regions.each do |region|
|
17
25
|
compute = Fog::Compute.new(:provider => :aws, :region => region)
|
18
26
|
compute.servers.all.each { |s| servers << s if s.state == 'running' }
|
19
27
|
end
|
20
28
|
stages = servers.map {|s| s.tags["stage"] }.uniq
|
21
29
|
|
22
|
-
stages.each do |
|
23
|
-
|
30
|
+
stages.each do | st|
|
31
|
+
server_config[st] = {}
|
24
32
|
servers.each do |server|
|
25
|
-
if server.tags["stage"] ==
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
33
|
+
if server.tags["stage"] == st
|
34
|
+
config = {}
|
35
|
+
config["roles"] = server.tags["roles"].split(", ") unless server.tags["roles"].nil?
|
36
|
+
config["alias"] = server.tags["name"]
|
37
|
+
if filter_role.nil? || config["roles"].include?(filter_role)
|
38
|
+
server_config[st][server.dns_name] = config
|
39
|
+
end
|
30
40
|
end
|
31
41
|
end
|
32
42
|
end
|
43
|
+
server_config
|
33
44
|
|
34
|
-
File.open(file_path, "w") do |io|
|
35
|
-
io << JSON.pretty_generate(servers_json)
|
36
|
-
end
|
37
45
|
end
|
38
46
|
|
39
|
-
def build_env
|
47
|
+
def build_env
|
40
48
|
|
41
49
|
#we need to check the existance of ~/.fog
|
42
50
|
|
43
51
|
#first key_pairs
|
44
|
-
key_pairs =
|
52
|
+
key_pairs = aws_config["key_pairs"]
|
45
53
|
|
46
54
|
key_pairs.each do |name, conf|
|
47
55
|
create_key_pair(name, conf)
|
48
56
|
end unless key_pairs.nil?
|
49
57
|
|
50
|
-
security_groups =
|
58
|
+
security_groups = aws_config["security"]
|
51
59
|
|
52
60
|
if security_groups.nil? or security_groups.size == 0
|
53
61
|
create_default_security_groups()
|
@@ -59,9 +67,13 @@ module AWS
|
|
59
67
|
security_groups["rds"].each do |name, conf|
|
60
68
|
create_rds_security_group(name, conf)
|
61
69
|
end unless security_groups["rds"].nil?
|
70
|
+
|
71
|
+
security_groups["elasticache"].each do |name, conf|
|
72
|
+
create_elasticache_security_group(name, conf)
|
73
|
+
end unless security_groups["elasticache"].nil?
|
62
74
|
end
|
63
75
|
|
64
|
-
|
76
|
+
aws_config.each do |type, resources|
|
65
77
|
case type
|
66
78
|
when "ec2"
|
67
79
|
resources.each do |name, conf|
|
@@ -79,6 +91,10 @@ module AWS
|
|
79
91
|
resources.each do |name,conf|
|
80
92
|
create_elb(name, conf)
|
81
93
|
end
|
94
|
+
when "elasticache"
|
95
|
+
resources.each do |name,conf|
|
96
|
+
create_elasticache_cluster(name,conf)
|
97
|
+
end
|
82
98
|
end
|
83
99
|
end
|
84
100
|
end
|
@@ -116,15 +132,22 @@ module AWS
|
|
116
132
|
|
117
133
|
compute = Fog::Compute.new(:provider => :aws, :region => region)
|
118
134
|
|
119
|
-
sg_name =
|
135
|
+
sg_name = ec2_name_tag(name)
|
120
136
|
|
121
137
|
sg = compute.security_groups.get(sg_name)
|
122
138
|
if sg.nil?
|
123
139
|
puts "Creating EC2 Secruity Group #{sg_name}"
|
124
|
-
sg = compute.security_groups.create(:name => sg_name, :description => "#{name} Security Group for #{
|
125
|
-
new_conf[:
|
126
|
-
|
127
|
-
|
140
|
+
sg = compute.security_groups.create(:name => sg_name, :description => "#{name} Security Group for #{stage}")
|
141
|
+
new_conf[:ingress].each do |ingress|
|
142
|
+
ingress = ingress.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
|
143
|
+
port = ingress.delete(:port)
|
144
|
+
if ingress[:group_name]
|
145
|
+
ingress[:group_name] = ec2_name_tag(ingress[:group_name])
|
146
|
+
sg.authorize_ip_permission(Range.new(port.to_i,port.to_i),ingress)
|
147
|
+
else
|
148
|
+
sg.authorize_port_range(Range.new(port.to_i,port.to_i),ingress)
|
149
|
+
end
|
150
|
+
end unless new_conf[:ingress].nil?
|
128
151
|
else
|
129
152
|
puts "EC2 Security Group #{sg_name} already exists. Skipping"
|
130
153
|
end
|
@@ -137,15 +160,15 @@ module AWS
|
|
137
160
|
region = new_conf[:region] || "us-east-1"
|
138
161
|
rds = Fog::AWS::RDS.new(:region => region)
|
139
162
|
|
140
|
-
rdssg_name =
|
163
|
+
rdssg_name = rds_name_tag(name)
|
141
164
|
|
142
165
|
rdssg = rds.security_groups.get(rdssg_name)
|
143
166
|
|
144
167
|
if rdssg.nil?
|
145
168
|
puts "Creating RDS Security Group #{rdssg_name}"
|
146
|
-
rdssg = rds.security_groups.create(:id => rdssg_name, :description => "#{name} DB Security Group For #{
|
169
|
+
rdssg = rds.security_groups.create(:id => rdssg_name, :description => "#{name} DB Security Group For #{stage}")
|
147
170
|
new_conf[:ec2_security_groups].each do |ec2_group|
|
148
|
-
rdssg.authorize_ec2_security_group("#{
|
171
|
+
rdssg.authorize_ec2_security_group("#{stage}-#{ec2_group}")
|
149
172
|
end
|
150
173
|
else
|
151
174
|
puts "RDS Security Group #{rdssg_name} already exists. Skipping"
|
@@ -154,18 +177,51 @@ module AWS
|
|
154
177
|
end
|
155
178
|
end
|
156
179
|
|
180
|
+
def create_elasticache_security_group(name, config = {})
|
181
|
+
unless Fog.mocking?
|
182
|
+
new_conf = config.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
|
183
|
+
region = new_conf[:region] || "us-east-1"
|
184
|
+
elasticache = Fog::AWS::Elasticache.new(:region => region)
|
185
|
+
|
186
|
+
cache_sg_name = elasticache_name_tag(name)
|
187
|
+
|
188
|
+
sg_exists = false
|
189
|
+
body = elasticache.describe_cache_security_groups.body
|
190
|
+
sg_exists = body['CacheSecurityGroups'].any? do |group|
|
191
|
+
group['CacheSecurityGroupName'] == cache_sg_name
|
192
|
+
end
|
193
|
+
|
194
|
+
unless sg_exists
|
195
|
+
puts "Creating Elasticache Security Group #{cache_sg_name}"
|
196
|
+
body = elasticache.create_cache_security_group(cache_sg_name, "#{cache_sg_name} ElastiCache Security Group for #{stage}").body
|
197
|
+
new_conf[:ec2_security_groups].each do |ec2_group|
|
198
|
+
compute = Fog::Compute.new(:provider => :aws, :region => region)
|
199
|
+
sg_name = ec2_name_tag(ec2_group)
|
200
|
+
sg = compute.security_groups.get(sg_name)
|
201
|
+
body = elasticache.authorize_cache_security_group_ingress(
|
202
|
+
cache_sg_name, sg.name, sg.owner_id).body
|
203
|
+
end
|
204
|
+
else
|
205
|
+
puts "Elasticache Security Group #{cache_sg_name} already exists. Skipping"
|
206
|
+
end
|
207
|
+
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
157
211
|
def create_s3_bucket(name, config = {})
|
158
212
|
new_conf = config.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
|
159
213
|
region = new_conf[:region] || "us-east-1"
|
160
214
|
|
161
215
|
storage = Fog::Storage.new(:provider => :aws, :region => region)
|
162
216
|
|
217
|
+
new_conf['LocationConstraint'] = new_conf.delete(:location_constraint) if new_conf[:location_constraint]
|
218
|
+
|
163
219
|
begin
|
164
220
|
bucket = storage.get_bucket(name)
|
165
221
|
puts "S3 Bucket #{name} already exists. Skipping"
|
166
222
|
rescue Excon::Errors::NotFound
|
167
223
|
puts "Creating S3 Bucket #{name}"
|
168
|
-
storage.put_bucket(name)
|
224
|
+
storage.put_bucket(name, new_conf)
|
169
225
|
|
170
226
|
acl = new_conf[:acl] || 'public-read'
|
171
227
|
|
@@ -181,7 +237,7 @@ module AWS
|
|
181
237
|
|
182
238
|
zones = new_conf[:availability_zones] || all_availability_zones(region)
|
183
239
|
|
184
|
-
elb_name =
|
240
|
+
elb_name = ec2_name_tag(name)
|
185
241
|
|
186
242
|
lb = elb.load_balancers.get(elb_name)
|
187
243
|
if lb.nil?
|
@@ -193,9 +249,9 @@ module AWS
|
|
193
249
|
|
194
250
|
compute = Fog::Compute.new(:provider => :aws, :region => region)
|
195
251
|
|
196
|
-
inst_servers = new_conf[:servers].map { |s|
|
252
|
+
inst_servers = new_conf[:servers].map { |s| ec2_name_tag(s) } unless new_conf[:servers].nil?
|
197
253
|
|
198
|
-
|
254
|
+
list_stage_servers(region).each do |server|
|
199
255
|
#the mocking seems wrong
|
200
256
|
unless Fog.mocking?
|
201
257
|
name = server.tags["name"]
|
@@ -213,7 +269,6 @@ module AWS
|
|
213
269
|
region = new_conf[:region] || "us-east-1"
|
214
270
|
|
215
271
|
compute = Fog::Compute.new(:provider => :aws, :region => region)
|
216
|
-
|
217
272
|
default_key_name = compute.key_pairs.all.first.name
|
218
273
|
|
219
274
|
default_params = { :availability_zone => default_availability_zone(region),
|
@@ -228,32 +283,31 @@ module AWS
|
|
228
283
|
|
229
284
|
tags = {}
|
230
285
|
|
231
|
-
#need to bork if no name in config file
|
232
|
-
ec2_name = "#{@stage}-#{name}"
|
233
|
-
|
234
286
|
#need to parse all servers to see if this one exists
|
235
|
-
|
236
|
-
server_exists = false
|
237
|
-
servers.each { |server| server_exists = true if server.tags["name"] == ec2_name and server.state == 'running' }
|
287
|
+
server = find_server_by_name(name, region)
|
238
288
|
|
239
|
-
|
289
|
+
#need to bork if no name in config file
|
290
|
+
ec2_name = ec2_name_tag(name)
|
291
|
+
|
292
|
+
unless server
|
240
293
|
puts "Creating EC2 Server named #{ec2_name}"
|
241
294
|
tags[:name] = ec2_name
|
242
|
-
tags[:
|
295
|
+
tags[:Name] = ec2_name
|
296
|
+
tags[:stage] = stage
|
243
297
|
|
244
298
|
if new_conf[:roles]
|
245
|
-
new_conf[:roles] <<
|
299
|
+
new_conf[:roles] << stage unless new_conf[:roles].include? stage
|
246
300
|
tags[:roles] = new_conf[:roles].join(', ')
|
247
301
|
else
|
248
|
-
tags[:roles] = "app, db, #{
|
302
|
+
tags[:roles] = "app, db, #{stage}"
|
249
303
|
end
|
250
304
|
|
251
305
|
params[:tags] = tags
|
252
306
|
|
253
307
|
if new_conf[:groups] and new_conf[:groups].size > 0
|
254
|
-
params[:groups] = new_conf[:groups].collect { |g| "#{
|
308
|
+
params[:groups] = new_conf[:groups].collect { |g| "#{stage}-#{g}" }
|
255
309
|
else
|
256
|
-
params[:groups] = ["#{
|
310
|
+
params[:groups] = ["#{stage}-default"]
|
257
311
|
end
|
258
312
|
|
259
313
|
server = compute.servers.create(params)
|
@@ -281,12 +335,14 @@ module AWS
|
|
281
335
|
}
|
282
336
|
params = default_params.merge(new_conf)
|
283
337
|
|
284
|
-
params[:id] =
|
338
|
+
params[:id] = ec2_name_tag(name)
|
339
|
+
|
340
|
+
params.delete(:availability_zone) if params[:multi_az]
|
285
341
|
|
286
342
|
if new_conf[:db_security_groups] and new_conf[:db_security_groups].size > 0
|
287
|
-
params[:
|
343
|
+
params[:security_group_names] = new_conf[:db_security_groups].collect { |g| rds_name_tag(g) }
|
288
344
|
else
|
289
|
-
params[:
|
345
|
+
params[:security_group_names] = [rds_name_tag('default')]
|
290
346
|
end
|
291
347
|
|
292
348
|
server = rds.servers.get(params[:id])
|
@@ -295,49 +351,50 @@ module AWS
|
|
295
351
|
puts "Creating RDS Server #{params[:id]}"
|
296
352
|
server = rds.servers.create(params)
|
297
353
|
else
|
298
|
-
puts "RDS Server #{params[:id]}
|
354
|
+
puts "RDS Server #{params[:id]} already exists. Skipping"
|
299
355
|
end
|
300
356
|
else
|
301
357
|
puts "Fog is mocking. Can't test with RDS Servers. Skipping"
|
302
358
|
end
|
303
359
|
end
|
304
360
|
|
305
|
-
|
361
|
+
def create_elasticache_cluster(name, config = {})
|
362
|
+
unless Fog.mocking?
|
363
|
+
new_conf = config.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
|
364
|
+
region = new_conf[:region] || "us-east-1"
|
306
365
|
|
307
|
-
|
308
|
-
#using availability zone b by default as a is often unavailable in us-east-1
|
309
|
-
"#{region}b"
|
310
|
-
end
|
366
|
+
elasticache = Fog::AWS::Elasticache.new(:region => region)
|
311
367
|
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
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
|
368
|
+
default_params = { :node_type => "cache.m1.large",
|
369
|
+
:num_nodes => 1,
|
370
|
+
:port => 11211,
|
371
|
+
:preferred_availability_zone => default_availability_zone(region)
|
372
|
+
}
|
327
373
|
|
328
|
-
|
329
|
-
arch = ["m1.small", "t1.micro", "c1.medium"].include?(flavor_id) ? "32-bit" : "64-bit"
|
374
|
+
params = default_params.merge(new_conf)
|
330
375
|
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
376
|
+
if new_conf[:security_groups] and new_conf[:security_groups].size > 0
|
377
|
+
params[:security_group_names] = new_conf[:security_groups].collect { |g| elasticache_name_tag(g) }
|
378
|
+
else
|
379
|
+
params[:security_group_names] = [elasticache_name_tag('default')]
|
380
|
+
end
|
381
|
+
|
382
|
+
cluster_name = ec2_name_tag(name)
|
336
383
|
|
337
|
-
|
338
|
-
default_ami
|
384
|
+
cl = elasticache.clusters.get(cluster_name)
|
339
385
|
|
386
|
+
unless cl
|
387
|
+
puts "Creating ElastiCache cluster #{cluster_name}"
|
388
|
+
params[:id] = cluster_name
|
389
|
+
cl = elasticache.clusters.new(params)
|
390
|
+
cl.save
|
391
|
+
else
|
392
|
+
puts "Elasticache cluster #{cluster_name} already exists. Skipping"
|
393
|
+
end
|
394
|
+
|
395
|
+
end
|
340
396
|
end
|
397
|
+
|
341
398
|
end
|
342
399
|
end
|
343
400
|
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'fog/aws/models/compute/security_group'
|
3
|
+
|
4
|
+
module Fog
|
5
|
+
module Compute
|
6
|
+
class AWS
|
7
|
+
class SecurityGroup
|
8
|
+
def authorize_ip_permission(port_range, options = {})
|
9
|
+
requires :name
|
10
|
+
|
11
|
+
permission = {
|
12
|
+
'FromPort' => port_range.min,
|
13
|
+
'ToPort' => port_range.max,
|
14
|
+
'IpProtocol' => options[:ip_protocol] || 'tcp'
|
15
|
+
}
|
16
|
+
|
17
|
+
if options[:group_name]
|
18
|
+
grp_permitted = {'GroupName' => options[:group_name]}
|
19
|
+
grp_permitted['UserId'] = options[:user_id] if options[:user_id]
|
20
|
+
permission['Groups'] = [grp_permitted]
|
21
|
+
else
|
22
|
+
ip_permitted = {'CidrIp' => "0.0.0.0/0"}
|
23
|
+
if options[:cidr_ip]
|
24
|
+
ip_permitted = {'CidrIp' => options[:cidr_ip]}
|
25
|
+
end
|
26
|
+
permission['IpRanges'] = [ip_permitted]
|
27
|
+
end
|
28
|
+
|
29
|
+
connection.authorize_security_group_ingress(
|
30
|
+
name,
|
31
|
+
'IpPermissions' => [permission]
|
32
|
+
)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
require "fog"
|
3
|
+
require "json"
|
4
|
+
|
5
|
+
module AWS
|
6
|
+
module Utils
|
7
|
+
|
8
|
+
def ec2_name_tag(server_name)
|
9
|
+
"#{stage}-#{server_name}"
|
10
|
+
end
|
11
|
+
|
12
|
+
def rds_name_tag(server_name)
|
13
|
+
"#{stage}-db-#{server_name}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def elasticache_name_tag(server_name)
|
17
|
+
"#{stage}-elasticache-#{server_name}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def find_server_by_name(name, region = nil)
|
21
|
+
regions_to_search = region.nil? ? all_regions : [region]
|
22
|
+
found_servers = []
|
23
|
+
regions_to_search.each do |r|
|
24
|
+
compute = Fog::Compute.new(:provider => :aws, :region => r)
|
25
|
+
servers = compute.servers.all
|
26
|
+
found_servers = found_servers + servers.select { |server| server.tags["name"] == ec2_name_tag(name) and server.state == 'running' }
|
27
|
+
end
|
28
|
+
found_servers.empty? ? nil : found_servers[0]
|
29
|
+
end
|
30
|
+
|
31
|
+
def list_stage_servers(region = nil)
|
32
|
+
regions_to_search = region.nil? ? all_regions : [region]
|
33
|
+
found_servers = []
|
34
|
+
regions_to_search.each do |r|
|
35
|
+
compute = Fog::Compute.new(:provider => :aws, :region => r)
|
36
|
+
servers = compute.servers.all
|
37
|
+
found_servers = found_servers + servers.select { |server| server.tags["stage"] == stage and server.state == 'running' }
|
38
|
+
end
|
39
|
+
found_servers
|
40
|
+
end
|
41
|
+
|
42
|
+
def default_availability_zone(region)
|
43
|
+
#using availability zone b by default as a is often unavailable in us-east-1
|
44
|
+
"#{region}b"
|
45
|
+
end
|
46
|
+
|
47
|
+
def all_regions
|
48
|
+
['us-east-1', 'us-west-1', 'eu-west-1', 'ap-northeast-1', 'ap-southeast-1']
|
49
|
+
end
|
50
|
+
|
51
|
+
def all_availability_zones(region)
|
52
|
+
case region
|
53
|
+
when "us-east-1"
|
54
|
+
#not using availability zone us-east-1a as it often fails
|
55
|
+
["us-east-1b", "us-east-1c"]
|
56
|
+
when "us-west-1"
|
57
|
+
["us-west-1a", "us-west-1b", "us-west-1c"]
|
58
|
+
when "eu-west-1"
|
59
|
+
["eu-west-1a", "eu-west-1b"]
|
60
|
+
when "ap-northeast-1"
|
61
|
+
["ap-northeast-1a", "ap-northeast-1a"]
|
62
|
+
when "ap-southeast-1"
|
63
|
+
["ap-southeast-1a", "ap-southeast-1b"]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def default_image_id(region, flavor_id, root_device_type)
|
68
|
+
arch = ["m1.small", "t1.micro", "c1.medium"].include?(flavor_id) ? "32-bit" : "64-bit"
|
69
|
+
|
70
|
+
defaults = JSON.parse(File.read(File.expand_path(File.join(File.dirname(__FILE__), 'default_amis.json'))))
|
71
|
+
|
72
|
+
region_defaults = defaults["ubuntu 10.04"][region]
|
73
|
+
raise "Invalid Region" if region_defaults.nil?
|
74
|
+
default_ami = region_defaults[arch][root_device_type]
|
75
|
+
|
76
|
+
raise "Default AMI not found for #{region} #{flavor_id} #{root_device_type}" if default_ami.nil?
|
77
|
+
default_ami
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
data/lib/conan/cloud/tasks.rb
CHANGED
@@ -1,16 +1,45 @@
|
|
1
1
|
require "fileutils"
|
2
2
|
require "json"
|
3
|
-
require File.expand_path(File.join(File.dirname(__FILE__), 'aws/provision'))
|
4
3
|
|
5
4
|
namespace :aws do
|
6
5
|
|
7
6
|
task :provision do
|
8
7
|
aws_config = JSON.parse(File.read("config/aws.json"))[stage] || {}
|
9
|
-
AWS::Provision.new(aws_config,
|
8
|
+
AWS::Provision.new(stage, aws_config, application).build_env
|
10
9
|
end
|
11
10
|
|
12
11
|
task :write_config do
|
13
|
-
AWS::Provision.new.
|
12
|
+
server_config = AWS::Provision.new.describe_env
|
13
|
+
File.open("config/servers.json", "w") do |io|
|
14
|
+
io << JSON.pretty_generate(server_config)
|
15
|
+
end
|
14
16
|
end
|
17
|
+
|
18
|
+
desc "Allows ssh to instance by name. cap ssh <NAME>"
|
19
|
+
task :ssh do
|
20
|
+
server = variables[:logger].instance_variable_get("@options")[:actions][2]
|
21
|
+
instance = AWS::Provision.new(stage).find_server_by_name(server)
|
22
|
+
unless instance.nil?
|
23
|
+
port = ssh_options[:port] || 22
|
24
|
+
command = "ssh -p #{port} ubuntu@#{instance.dns_name}"
|
25
|
+
puts "Running `#{command}`"
|
26
|
+
exec(command)
|
27
|
+
else
|
28
|
+
puts "Server #{server} not found"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
desc "create autoscale setup from config/autoscale.json"
|
33
|
+
task :create_autoscale do
|
34
|
+
autoscale_config = JSON.parse(File.read("config/autoscale.json"))[stage] || {}
|
35
|
+
AWS::Autoscale.new(stage, autoscale_config, application).configure_autoscale
|
36
|
+
end
|
37
|
+
|
38
|
+
desc "update autoscale from config/autoscale.json after a deployment"
|
39
|
+
task :update_autoscale do
|
40
|
+
autoscale_config = JSON.parse(File.read("config/autoscale.json"))[stage] || {}
|
41
|
+
AWS::Autoscale.new(stage, autoscale_config, application).update_autoscale
|
42
|
+
end
|
43
|
+
|
15
44
|
end
|
16
45
|
|
data/lib/conan/deployment.rb
CHANGED
@@ -31,12 +31,11 @@ module Conan
|
|
31
31
|
|
32
32
|
def add_role(*roles)
|
33
33
|
roles = Hash.new{ |h,k| h[k] = [] }
|
34
|
-
|
35
34
|
server_config.each do |s, c|
|
36
35
|
c["roles"].each do |r|
|
37
36
|
roles[r.to_sym] << s
|
38
37
|
end
|
39
|
-
end
|
38
|
+
end unless server_config.nil?
|
40
39
|
|
41
40
|
roles.each do |r, ss|
|
42
41
|
next unless roles.include?(r)
|
@@ -1,5 +1,14 @@
|
|
1
1
|
require "json"
|
2
|
-
|
2
|
+
|
3
|
+
if File.exists?("config/servers.json")
|
4
|
+
set :server_config, JSON.parse(File.read("config/servers.json"))[stage] || {}
|
5
|
+
else
|
6
|
+
if File.exists?("config/aws.json")
|
7
|
+
set :server_config, AWS::Provision.new.describe_env(application)[stage]
|
8
|
+
else
|
9
|
+
set :server_config, {}
|
10
|
+
end
|
11
|
+
end
|
3
12
|
|
4
13
|
add_role :app, :db
|
5
14
|
|
@@ -0,0 +1,15 @@
|
|
1
|
+
{
|
2
|
+
"staging": {
|
3
|
+
"launch-config": {
|
4
|
+
"lcname": {
|
5
|
+
"server_to_image": "app1",
|
6
|
+
"image_id": "ORTHIS",
|
7
|
+
"flavor_id": "c1.xlarge",
|
8
|
+
"instance_monitoring": true
|
9
|
+
"key_name": "keyname",
|
10
|
+
"autoscale_groups": [""],
|
11
|
+
"security_groups": ["default"]
|
12
|
+
}
|
13
|
+
}
|
14
|
+
}
|
15
|
+
}
|
@@ -99,12 +99,45 @@
|
|
99
99
|
"region": "us-east-1",
|
100
100
|
"ec2_security_groups": ["default"]
|
101
101
|
}
|
102
|
-
}
|
102
|
+
},
|
103
|
+
"elasticache": {
|
104
|
+
"default": {
|
105
|
+
"region": "us-east-1",
|
106
|
+
"ec2_security_groups": ["default"]
|
107
|
+
}
|
108
|
+
}
|
103
109
|
},
|
104
110
|
"s3": {
|
105
111
|
"test": {
|
106
112
|
"region": "us-east-1"
|
107
113
|
}
|
108
|
-
}
|
114
|
+
},
|
115
|
+
"elasticache": {
|
116
|
+
"cachename": {
|
117
|
+
"node_type": "cache.m1.large",
|
118
|
+
"num_nodes": 2,
|
119
|
+
"port": 11211,
|
120
|
+
"preferred_availablility_zone": "us-east-1b",
|
121
|
+
"security_groups": ["default"]
|
122
|
+
}
|
123
|
+
},
|
124
|
+
"cloud_watch": {
|
125
|
+
"metric_alarms": {
|
126
|
+
"alarmname": {
|
127
|
+
"region": "us-east-1",
|
128
|
+
"description": "CPU over 70%",
|
129
|
+
"comparison": "GreaterThanOrEqualToThreshold",
|
130
|
+
"evaluation_period": 1,
|
131
|
+
"metric_name": "CPUUtilization",
|
132
|
+
"namespace": "AWS/EC2",
|
133
|
+
"period": 60,
|
134
|
+
"statistic": "Average",
|
135
|
+
"threshold": 70,
|
136
|
+
"unit": "Percent",
|
137
|
+
"dimensions": {
|
138
|
+
"ec2": ["app-1"]
|
139
|
+
}
|
140
|
+
}
|
141
|
+
}
|
109
142
|
}
|
110
143
|
}
|
data/lib/conan/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: conan
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,11 +10,11 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2012-02-
|
13
|
+
date: 2012-02-27 00:00:00.000000000Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: json
|
17
|
-
requirement: &
|
17
|
+
requirement: &70281889180960 !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ~>
|
@@ -22,10 +22,10 @@ dependencies:
|
|
22
22
|
version: 1.6.1
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
|
-
version_requirements: *
|
25
|
+
version_requirements: *70281889180960
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: capistrano
|
28
|
-
requirement: &
|
28
|
+
requirement: &70281889178900 !ruby/object:Gem::Requirement
|
29
29
|
none: false
|
30
30
|
requirements:
|
31
31
|
- - ! '>='
|
@@ -33,10 +33,10 @@ dependencies:
|
|
33
33
|
version: '0'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
|
-
version_requirements: *
|
36
|
+
version_requirements: *70281889178900
|
37
37
|
- !ruby/object:Gem::Dependency
|
38
38
|
name: fog
|
39
|
-
requirement: &
|
39
|
+
requirement: &70281889177220 !ruby/object:Gem::Requirement
|
40
40
|
none: false
|
41
41
|
requirements:
|
42
42
|
- - ! '>='
|
@@ -44,7 +44,7 @@ dependencies:
|
|
44
44
|
version: '0'
|
45
45
|
type: :runtime
|
46
46
|
prerelease: false
|
47
|
-
version_requirements: *
|
47
|
+
version_requirements: *70281889177220
|
48
48
|
description: Set up a project to enable the provision of infrastructure through AWS
|
49
49
|
and the configuration of servers using Chef via Capistrano.
|
50
50
|
email:
|
@@ -56,8 +56,11 @@ extra_rdoc_files: []
|
|
56
56
|
files:
|
57
57
|
- bin/conan
|
58
58
|
- lib/conan/capistrano.rb
|
59
|
+
- lib/conan/cloud/aws/autoscale.rb
|
59
60
|
- lib/conan/cloud/aws/default_amis.json
|
60
61
|
- lib/conan/cloud/aws/provision.rb
|
62
|
+
- lib/conan/cloud/aws/security_group.rb
|
63
|
+
- lib/conan/cloud/aws/utils.rb
|
61
64
|
- lib/conan/cloud/tasks.rb
|
62
65
|
- lib/conan/common.rb
|
63
66
|
- lib/conan/deployment/assets.rb
|
@@ -70,6 +73,7 @@ files:
|
|
70
73
|
- lib/conan/smart_hash_merge.rb
|
71
74
|
- lib/conan/template/Capfile
|
72
75
|
- lib/conan/template/CONAN_TODO
|
76
|
+
- lib/conan/template/config/autoscale.json
|
73
77
|
- lib/conan/template/config/aws.json
|
74
78
|
- lib/conan/template/config/deploy.rb
|
75
79
|
- lib/conan/template/config/servers.json
|