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