conan 0.3.5 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,7 @@
1
1
  require "bundler/capistrano"
2
2
  require "conan/deployment"
3
+ require "conan/cloud/aws/provision"
4
+ require "conan/cloud/aws/autoscale"
3
5
 
4
6
  Capistrano::Configuration.instance(:must_exist).load do
5
7
  Conan::Deployment.define_tasks(self)
@@ -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-36e65037",
6
- "instance-store": "ami-baf94fbb"
5
+ "ebs": "ami-942f9995",
6
+ "instance-store": "ami-902f9991"
7
7
  },
8
8
  "32-bit": {
9
- "ebs": "ami-1ae6501b",
10
- "instance-store": "ami-46f94f47"
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-c0c98c92",
16
- "instance-store": "ami-3cc98c6e"
15
+ "ebs": "ami-7089cd22",
16
+ "instance-store": "ami-4e89cd1c"
17
17
  },
18
18
  "32-bit": {
19
- "ebs": "ami-d8c98c8a",
20
- "instance-store": "ami-76c98c24"
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-81dde2f5",
26
- "instance-store": "ami-3fdde24b"
25
+ "ebs": "ami-fb665f8f",
26
+ "instance-store": "ami-1b665f6f"
27
27
  },
28
28
  "32-bit": {
29
- "ebs": "ami-95dde2e1",
30
- "instance-store": "ami-fddee189"
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-645f8079",
36
- "instance-store": "ami-7a5f8067"
35
+ "ebs": "ami-7874ab65",
36
+ "instance-store": "ami-7c74ab61"
37
37
  },
38
38
  "32-bit": {
39
- "ebs": "ami-6a5f8077",
40
- "instance-store": "ami-4a5f8057"
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-55dc0b3c",
46
- "instance-store": "ami-35de095c"
45
+ "ebs": "ami-349b495d",
46
+ "instance-store": "ami-5c9b4935"
47
47
  },
48
48
  "32-bit": {
49
- "ebs": "ami-71dc0b18",
50
- "instance-store": "ami-4fd00726"
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-a191cfe4",
56
- "instance-store": "ami-3991cf7c"
55
+ "ebs": "ami-7fb0e93a",
56
+ "instance-store": "ami-6bb0e92e"
57
57
  },
58
58
  "32-bit": {
59
- "ebs": "ami-9991cfdc",
60
- "instance-store": "ami-b390cef6"
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-8eb33ebe",
66
- "instance-store": "ami-94b33ea4"
65
+ "ebs": "ami-ec0b86dc",
66
+ "instance-store": "ami-e60b86d6"
67
67
  },
68
68
  "32-bit": {
69
- "ebs": "ami-8cb33ebc",
70
- "instance-store": "ami-bcb33e8c"
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 = {}, stage = 'production')
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 describe_env_to_json(file_path)
14
- servers_json = {}
21
+ def describe_env(filter_role = nil)
22
+ server_config = {}
15
23
  servers = []
16
- ["us-east-1", "us-west-1", "eu-west-1", "ap-southeast-1", "ap-northeast-1"].each do |region|
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 | stage|
23
- servers_json[stage] = {}
30
+ stages.each do | st|
31
+ server_config[st] = {}
24
32
  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
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 = @aws_config["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 = @aws_config["security"]
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
- @aws_config.each do |type, resources|
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 = "#{@stage}-#{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 #{@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?
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 = "#{@stage}-db-#{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 #{@stage}")
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("#{@stage}-#{ec2_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 = "#{@stage}-#{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| "#{@stage}-#{s}" } unless new_conf[:servers].nil?
252
+ inst_servers = new_conf[:servers].map { |s| ec2_name_tag(s) } unless new_conf[:servers].nil?
197
253
 
198
- compute.servers.all.each do |server|
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
- 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' }
287
+ server = find_server_by_name(name, region)
238
288
 
239
- unless server_exists
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[:stage] = @stage
295
+ tags[:Name] = ec2_name
296
+ tags[:stage] = stage
243
297
 
244
298
  if new_conf[:roles]
245
- new_conf[:roles] << @stage unless new_conf[:roles].include? @stage
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, #{@stage}"
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| "#{@stage}-#{g}" }
308
+ params[:groups] = new_conf[:groups].collect { |g| "#{stage}-#{g}" }
255
309
  else
256
- params[:groups] = ["#{@stage}-default"]
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] = "#{@stage}-#{name}"
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[:db_security_groups] = new_conf[:db_security_groups].collect { |g| "#{@stage}-db-#{g}" }
343
+ params[:security_group_names] = new_conf[:db_security_groups].collect { |g| rds_name_tag(g) }
288
344
  else
289
- params[:db_security_groups] = ["#{@stage}-db-default"]
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]} aready exists. Skipping"
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
- private
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
- 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
366
+ elasticache = Fog::AWS::Elasticache.new(:region => region)
311
367
 
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
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
- 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"
374
+ params = default_params.merge(new_conf)
330
375
 
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]
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
- raise "Default AMI not found for #{region} #{flavor_id} #{root_device_type}" if default_ami.nil?
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
@@ -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, stage).build_env
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.describe_env_to_json("config/servers.json")
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
 
@@ -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)
@@ -18,7 +18,6 @@ namespace :chef do
18
18
  cache[host] ||= `ssh #{host} ifconfig`[/inet addr:(10\.\d+\.\d+\.\d+)/, 1]
19
19
  aliases[config["alias"]] = cache[host]
20
20
  end
21
-
22
21
  FileUtils.mkdir_p File.dirname(cache_path)
23
22
  File.open(cache_path, "w") do |io|
24
23
  io << JSON.dump(cache)
@@ -1,5 +1,14 @@
1
1
  require "json"
2
- set :server_config, JSON.parse(File.read("config/servers.json"))[stage] || {}
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
@@ -1,3 +1,3 @@
1
1
  module Conan
2
- VERSION = "0.3.5"
2
+ VERSION = "0.4.1"
3
3
  end
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.3.5
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-15 00:00:00.000000000Z
13
+ date: 2012-02-27 00:00:00.000000000Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: json
17
- requirement: &70097716738400 !ruby/object:Gem::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: *70097716738400
25
+ version_requirements: *70281889180960
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: capistrano
28
- requirement: &70097716737480 !ruby/object:Gem::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: *70097716737480
36
+ version_requirements: *70281889178900
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: fog
39
- requirement: &70097716735820 !ruby/object:Gem::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: *70097716735820
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