rubycfn 0.4.10 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -1
  3. data/Gemfile.lock +1 -1
  4. data/README.md +43 -67
  5. data/bin/rubycfn +17 -73
  6. data/lib/cli_methods.rb +2 -2
  7. data/lib/rubycfn/version.rb +1 -1
  8. data/templates/.env +2 -0
  9. data/templates/.env.acceptance +1 -0
  10. data/templates/.env.dependencies.rspec +6 -0
  11. data/templates/.env.development +1 -0
  12. data/templates/.env.production +1 -0
  13. data/templates/.env.rspec +1 -0
  14. data/templates/.env.test +1 -0
  15. data/templates/{.gitignore.erb → .gitignore} +3 -0
  16. data/templates/{.rubocop.yml.erb → .rubocop.yml} +14 -1
  17. data/templates/{Gemfile.erb → Gemfile} +0 -1
  18. data/templates/README.md +57 -0
  19. data/templates/{Rakefile.erb → Rakefile} +15 -8
  20. data/templates/bootstrap/dependency_stack.rb +49 -0
  21. data/templates/config.yaml +65 -0
  22. data/templates/lib/aws_helper/aws_sdk.rb +30 -0
  23. data/templates/{compiler.rb.erb → lib/aws_helper/compiler.rb} +15 -9
  24. data/templates/{dependencies.rb.erb → lib/aws_helper/dependencies.rb} +5 -3
  25. data/templates/{deploy.rb.erb → lib/aws_helper/deploy.rb} +6 -4
  26. data/templates/lib/aws_helper/helpers.rb +3 -0
  27. data/templates/{main_aws_helper.rb.erb → lib/aws_helper/main.rb} +0 -0
  28. data/templates/{upload_stack.rb.erb → lib/aws_helper/upload_stack.rb} +15 -6
  29. data/templates/lib/core/applications.rb +479 -0
  30. data/templates/lib/core/assume_role.rb +40 -0
  31. data/templates/lib/core/classes.rb +25 -0
  32. data/templates/{core_compile.rb.erb → lib/core/compile.rb} +1 -0
  33. data/templates/lib/core/dependencies.rb +22 -0
  34. data/templates/{core_deploy.rb.erb → lib/core/deploy.rb} +20 -10
  35. data/templates/lib/core/git.rb +15 -0
  36. data/templates/lib/core/init.rb +173 -0
  37. data/templates/{core_upload.rb.erb → lib/core/upload.rb} +0 -0
  38. data/templates/{main.rb.erb → lib/main.rb} +8 -6
  39. data/templates/lib/shared_concerns/global_variables.rb +56 -0
  40. data/templates/{helper_methods.rb.erb → lib/shared_concerns/helper_functions.rb} +0 -0
  41. data/templates/lib/shared_concerns/helper_methods.rb +3 -0
  42. data/templates/{shared_methods.rb.erb → lib/shared_concerns/shared_methods.rb} +9 -0
  43. data/templates/lib/stacks/acm_stack/certificate_manager.rb +79 -0
  44. data/templates/{new_stack.rb.erb → lib/stacks/acm_stack/main.rb} +3 -4
  45. data/templates/lib/stacks/ecs_stack/ecs_cluster.rb +344 -0
  46. data/templates/lib/stacks/ecs_stack/lifecycle_hook.rb +188 -0
  47. data/templates/lib/stacks/ecs_stack/load_balancer.rb +68 -0
  48. data/templates/{ecs_stack.rb.erb → lib/stacks/ecs_stack/main.rb} +2 -1
  49. data/templates/{project_stack.rb.erb → lib/stacks/parent_stack/main.rb} +2 -2
  50. data/templates/lib/stacks/parent_stack/parent.rb +18 -0
  51. data/templates/lib/stacks/vpc_stack/infra_vpc.rb +193 -0
  52. data/templates/{vpc_stack.rb.erb → lib/stacks/vpc_stack/main.rb} +1 -2
  53. data/templates/{parent_stack_spec.rb.erb → spec/lib/parent_spec.rb} +2 -5
  54. data/templates/{spec_helper.rb.erb → spec/spec_helper.rb} +2 -2
  55. metadata +54 -44
  56. data/format.vim +0 -3
  57. data/templates/.env.erb +0 -4
  58. data/templates/.env.production.erb +0 -6
  59. data/templates/.env.rspec.erb +0 -6
  60. data/templates/.env.test.erb +0 -6
  61. data/templates/.gitlab-ci.yml.erb +0 -75
  62. data/templates/aws_sdk.rb.erb +0 -18
  63. data/templates/core_diff.rb.erb +0 -59
  64. data/templates/ecs_stack_concern.rb.erb +0 -20
  65. data/templates/global_variables.rb.erb +0 -16
  66. data/templates/helpers.rb.erb +0 -7
  67. data/templates/new_concern.rb.erb +0 -10
  68. data/templates/project_concern.rb.erb +0 -26
  69. data/templates/subnets.rb.erb +0 -18
  70. data/templates/vpc_concerns.rb.erb +0 -87
  71. data/templates/vpc_spec.rb.erb +0 -39
@@ -0,0 +1,344 @@
1
+ module EcsStack
2
+ module EcsCluster
3
+ extend ActiveSupport::Concern
4
+ included do
5
+ parameter :vpc,
6
+ description: "VPC ID"
7
+ parameter :subnets,
8
+ description: "Subnets for ECS"
9
+ parameter :ecs_ami,
10
+ description: "ECS-Optimized AMI ID",
11
+ type: "AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>",
12
+ default: "/aws/service/ecs/optimized-ami/amazon-linux/recommended/image_id"
13
+
14
+ variable :cluster_size,
15
+ default: "1",
16
+ value: infra_config["environments"][environment]["cluster_size"]
17
+
18
+ variable :cluster_instance_type,
19
+ default: "t2.micro",
20
+ value: infra_config["environments"][environment]["cluster_instance_type"]
21
+
22
+ resource :ecs_cluster,
23
+ type: "AWS::ECS::Cluster"
24
+
25
+ resource :ecs_auto_scaling_group,
26
+ type: "AWS::AutoScaling::AutoScalingGroup" do |r|
27
+ r.property(:vpc_zone_identifier) { :subnets.ref.fnsplit(",") }
28
+ r.property(:launch_configuration_name) { :ecs_launch_configuration.ref }
29
+ r.property(:min_size) { cluster_size.to_i }
30
+ r.property(:max_size) { cluster_size.to_i }
31
+ r.property(:desired_capacity) { cluster_size.to_i }
32
+ r.property(:tags) do
33
+ [
34
+ {
35
+ "Key": "Name",
36
+ "Value": "#{environment} ECS host",
37
+ "PropagateAtLaunch": true
38
+ }
39
+ ]
40
+ end
41
+ end
42
+
43
+ resource :ecs_launch_configuration,
44
+ metadata: {
45
+ "AWS::CloudFormation::Init": {
46
+ "config": {
47
+ "packages": {
48
+ "yum": {
49
+ "collectd": []
50
+ }
51
+ },
52
+ "commands": {
53
+ "01_add_instance_to_cluster": {
54
+ "command": "echo ECS_CLUSTER=${SfsEcsCluster} >> /etc/ecs/ecs.config".fnsub
55
+ },
56
+ "02_enable_cloudwatch_agent": {
57
+ "command": "/opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -c ssm:${EcsCloudWatchParameter} -s".fnsub
58
+ }
59
+ },
60
+ "files": {
61
+ "/etc/cfn/cfn-hup.conf": {
62
+ "mode": 256,
63
+ "owner": "root",
64
+ "group": "root",
65
+ "content": "[main]\nstack=${AWS::StackId}\nregion=${AWS::Region}\n".fnsub
66
+ },
67
+ "/etc/cfn/hooks.d/cfn-auto-reloader.conf": {
68
+ "content": "[cfn-auto-reloader-hook]\ntriggers=post.update\npath=Resources.EcsLaunchConfiguration.Metadata.AWS::CloudFormation::Init\naction=/opt/aws/bin/cfn-init -v --region ${AWS::Region} --stack ${AWS::StackName} --resource EcsLaunchConfiguration\n".fnsub
69
+ }
70
+ },
71
+ "services": {
72
+ "sysvinit": {
73
+ "cfn-hup": {
74
+ "enabled": true,
75
+ "ensureRunning": true,
76
+ "files": [
77
+ "/etc/cfn/cfn-hup.conf",
78
+ "/etc/cfn/hooks.d/cfn-auto-reloader.conf"
79
+ ]
80
+ }
81
+ }
82
+ }
83
+ }
84
+ }
85
+ },
86
+ type: "AWS::AutoScaling::LaunchConfiguration" do |r|
87
+ r.property(:image_id) { :ecs_ami.ref }
88
+ r.property(:key_name) { "sfs-keypair-#{environment}" }
89
+ r.property(:instance_type) { cluster_instance_type }
90
+ r.property(:security_groups) do
91
+ [
92
+ :ecs_host_security_group.ref,
93
+ :load_balancer_security_group.ref # Not sure if necessary
94
+ ]
95
+ end
96
+ r.property(:iam_instance_profile) { :ecs_instance_profile.ref }
97
+ r.property(:user_data) { "#!/bin/bash\nyum install -y https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm\nyum install -y https://s3.amazonaws.com/amazoncloudwatch-agent/amazon_linux/amd64/latest/amazon-cloudwatch-agent.rpm\nyum install -y aws-cfn-bootstrap hibagent \n/opt/aws/bin/cfn-init -v --region ${AWS::Region} --stack ${AWS::StackName} --resource EcsLaunchConfiguration\n/opt/aws/bin/cfn-signal -e $? --region ${AWS::Region} --stack ${AWS::StackName} --resource EcsAutoScalingGroup\n/usr/bin/enable-ec2-spot-hibernation\n".fnsub.fnbase64 }
98
+ end
99
+
100
+ ecs_ssm_parameter_value = {
101
+ "logs": {
102
+ "force_flush_interval": 5,
103
+ "logs_collected": {
104
+ "files": {
105
+ "collect_list": [
106
+ {
107
+ "file_path": "/var/log/messages",
108
+ "log_group_name": "${SfsEcsCluster}-/var/log/messages",
109
+ "log_stream_name": "{instance_id}",
110
+ "timestamp_format": "%b %d %H:%M:%S"
111
+ },
112
+ {
113
+ "file_path": "/var/log/dmesg",
114
+ "log_group_name": "${SfsEcsCluster}-/var/log/dmesg",
115
+ "log_stream_name": "{instance_id}"
116
+ },
117
+ {
118
+ "file_path": "/var/log/docker",
119
+ "log_group_name": "${SfsEcsCluster}-/var/log/docker",
120
+ "log_stream_name": "{instance_id}",
121
+ "timestamp_format": "%Y-%m-%dT%H:%M:%S.%f"
122
+ },
123
+ {
124
+ "file_path": "/var/log/ecs/ecs-init.log",
125
+ "log_group_name": "${SfsEcsCluster}-/var/log/ecs/ecs-init.log",
126
+ "log_stream_name": "{instance_id}",
127
+ "timestamp_format": "%Y-%m-%dT%H:%M:%SZ"
128
+ },
129
+ {
130
+ "file_path": "/var/log/ecs/ecs-agent.log.*",
131
+ "log_group_name": "${SfsEcsCluster}-/var/log/ecs/ecs-agent.log",
132
+ "log_stream_name": "{instance_id}",
133
+ "timestamp_format": "%Y-%m-%dT%H:%M:%SZ"
134
+ },
135
+ {
136
+ "file_path": "/var/log/ecs/audit.log",
137
+ "log_group_name": "${SfsEcsCluster}-/var/log/ecs/audit.log",
138
+ "log_stream_name": "{instance_id}",
139
+ "timestamp_format": "%Y-%m-%dT%H:%M:%SZ"
140
+ }
141
+ ]
142
+ }
143
+ }
144
+ },
145
+ "metrics": {
146
+ "append_dimensions": {
147
+ "AutoScalingGroupName": "${!aws:AutoScalingGroupName}",
148
+ "InstanceId": "${!aws:InstanceId}",
149
+ "InstanceType": "${!aws:InstanceType}"
150
+ },
151
+ "metrics_collected": {
152
+ "collectd": {
153
+ "metrics_aggregation_interval": 60
154
+ },
155
+ "disk": {
156
+ "measurement": [
157
+ "used_percent"
158
+ ],
159
+ "metrics_collection_interval": 60,
160
+ "resources": [
161
+ "/"
162
+ ]
163
+ },
164
+ "mem": {
165
+ "measurement": [
166
+ "mem_used_percent"
167
+ ],
168
+ "metrics_collection_interval": 60
169
+ },
170
+ "statsd": {
171
+ "metrics_aggregation_interval": 60,
172
+ "metrics_collection_interval": 10,
173
+ "service_address": ":8125"
174
+ }
175
+ }
176
+ }
177
+ }
178
+
179
+ resource :ecs_cloud_watch_parameter,
180
+ type: "AWS::SSM::Parameter" do |r|
181
+ r.property(:description) { "ECS" }
182
+ r.property(:name) { "AmazonCloudWatch-${SfsEcsCluster}-ECS".fnsub }
183
+ r.property(:type) { "String" }
184
+ r.property(:value) { JSON.pretty_generate(ecs_ssm_parameter_value).fnsub }
185
+ end
186
+
187
+ resource :ecs_host_security_group,
188
+ type: "AWS::EC2::SecurityGroup" do |r|
189
+ r.property(:vpc_id) { :vpc.ref }
190
+ r.property(:group_description) { "Access to the ECS hosts and the tasks/containers that run on them" }
191
+ r.property(:security_group_ingress) do
192
+ [
193
+ {
194
+ "SourceSecurityGroupId": :load_balancer_security_group.ref,
195
+ "IpProtocol": -1
196
+ }
197
+ ]
198
+ end
199
+ r.property(:tags) do
200
+ [
201
+ {
202
+ "Key": "Name",
203
+ "Value": "#{environment}-ECS-Hosts"
204
+ }
205
+ ]
206
+ end
207
+ end
208
+
209
+ resource :load_balancer_security_group,
210
+ type: "AWS::EC2::SecurityGroup" do |r|
211
+ r.property(:vpc_id) { :vpc.ref }
212
+ r.property(:group_description) { "Access to the load balancer that sits in front of ECS" }
213
+ r.property(:security_group_ingress) do
214
+ [
215
+ {
216
+ "CidrIp": "0.0.0.0/0",
217
+ "IpProtocol": -1
218
+ }
219
+ ]
220
+ end
221
+ r.property(:tags) do
222
+ [
223
+ {
224
+ "Key": "Name",
225
+ "Value": "#{environment}-ECS-LoadBalancers"
226
+ }
227
+ ]
228
+ end
229
+ end
230
+
231
+ ecs_role_assume_role_policy_document = {
232
+ "Statement": [
233
+ {
234
+ "Action": "sts:AssumeRole",
235
+ "Effect": "Allow",
236
+ "Principal": {
237
+ "Service": "ec2.amazonaws.com"
238
+ }
239
+ }
240
+ ]
241
+ }
242
+
243
+ ecs_role_policy_document = {
244
+ "Statement": [{
245
+ "Effect": "Allow",
246
+ "Action": [
247
+ "ecs:CreateCluster",
248
+ "ecs:DeregisterContainerInstance",
249
+ "ecs:DiscoverPollEndpoint",
250
+ "ecs:Poll",
251
+ "ecs:RegisterContainerInstance",
252
+ "ecs:StartTelemetrySession",
253
+ "ecs:Submit*",
254
+ "ecr:BatchCheckLayerAvailability",
255
+ "ecr:BatchGetImage",
256
+ "ecr:GetDownloadUrlForLayer",
257
+ "ecr:GetAuthorizationToken"
258
+ ],
259
+ "Resource": "*"
260
+ }]
261
+ }
262
+
263
+ resource :ecs_role,
264
+ type: "AWS::IAM::Role" do |r|
265
+ r.property(:path) { "/" }
266
+ r.property(:role_name) { "#{environment}-ECSRole-${AWS::Region}".fnsub }
267
+ r.property(:assume_role_policy_document) { JSON.pretty_generate(ecs_role_assume_role_policy_document) }
268
+ r.property(:managed_policy_arns) do
269
+ [
270
+ "arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM",
271
+ "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy",
272
+ "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role"
273
+ ]
274
+ end
275
+ r.property(:policies) do
276
+ [
277
+ {
278
+ "PolicyName": "ecs-service",
279
+ "PolicyDocument": JSON.pretty_generate(ecs_role_policy_document)
280
+ }
281
+ ]
282
+ end
283
+ end
284
+
285
+ resource :ecs_instance_profile,
286
+ type: "AWS::IAM::InstanceProfile" do |r|
287
+ r.property(:path) { "/" }
288
+ r.property(:roles) do
289
+ [
290
+ :ecs_role.ref
291
+ ]
292
+ end
293
+ end
294
+
295
+ resource :ecs_service_auto_scaling_role,
296
+ type: "AWS::IAM::Role" do |r|
297
+ r.property(:assume_role_policy_document) do
298
+ {
299
+ "Version": "2012-10-17",
300
+ "Statement": {
301
+ "Action": [
302
+ "sts:AssumeRole"
303
+ ],
304
+ "Effect": "Allow",
305
+ "Principal": {
306
+ "Service": [
307
+ "application-autoscaling.amazonaws.com"
308
+ ]
309
+ }
310
+ }
311
+ }
312
+ end
313
+ r.property(:path) { "/" }
314
+ r.property(:policies) do
315
+ [
316
+ {
317
+ "PolicyName": "ecs-service-autoscaling",
318
+ "PolicyDocument": {
319
+ "Statement": {
320
+ "Effect": "Allow",
321
+ "Action": [
322
+ "application-autoscaling:*",
323
+ "cloudwatch:DescribeAlarms",
324
+ "cloudwatch:PutMetricAlarm",
325
+ "ecs:DescribeServices",
326
+ "ecs:UpdateService"
327
+ ],
328
+ "Resource": "*"
329
+ }
330
+ }
331
+ }
332
+ ]
333
+ end
334
+ end
335
+
336
+ output :ecs_cluster,
337
+ value: :ecs_cluster.ref
338
+ output :ecs_cluster_arn,
339
+ value: :ecs_cluster.ref(:arn)
340
+ output :ecs_auto_scaling_role_arn,
341
+ value: :ecs_service_auto_scaling_role.ref(:arn)
342
+ end
343
+ end
344
+ end
@@ -0,0 +1,188 @@
1
+ module EcsStack
2
+ module LifecycleHook
3
+ extend ActiveSupport::Concern
4
+ ## Drains instances on termination
5
+ included do
6
+ resource :ecs_lifecycle_notification_topic,
7
+ depends_on: :lifecycle_handler_function,
8
+ type: "AWS::SNS::Topic" do |r|
9
+ r.property(:subscription) do
10
+ [
11
+ {
12
+ "Endpoint": :lifecycle_handler_function.ref(:arn),
13
+ "Protocol": "lambda"
14
+ }
15
+ ]
16
+ end
17
+ end
18
+
19
+ resource :instance_terminating_hook,
20
+ depends_on: :ecs_lifecycle_notification_topic,
21
+ type: "AWS::AutoScaling::LifecycleHook" do |r|
22
+ r.property(:auto_scaling_group_name) { :ecs_auto_scaling_group.ref }
23
+ r.property(:default_result) { "ABANDON" }
24
+ r.property(:heartbeat_timeout) { 900 }
25
+ r.property(:lifecycle_transition) { "autoscaling:EC2_INSTANCE_TERMINATING" }
26
+ r.property(:notification_target_arn) { :ecs_lifecycle_notification_topic.ref }
27
+ r.property(:role_arn) { :autoscaling_notification_role.ref(:arn) }
28
+ end
29
+
30
+ resource :autoscaling_notification_role,
31
+ type: "AWS::IAM::Role" do |r|
32
+ r.property(:assume_role_policy_document) do
33
+ {
34
+ "Version": "2012-10-17",
35
+ "Statement": [
36
+ {
37
+ "Effect": "Allow",
38
+ "Principal": {
39
+ "Service": [
40
+ "autoscaling.amazonaws.com"
41
+ ]
42
+ },
43
+ "Action": [
44
+ "sts:AssumeRole"
45
+ ]
46
+ }
47
+ ]
48
+ }
49
+ end
50
+ r.property(:managed_policy_arns) { ["arn:aws:iam::aws:policy/service-role/AutoScalingNotificationAccessRole"] }
51
+ end
52
+
53
+ resource :lambda_execution_role,
54
+ type: "AWS::IAM::Role" do |r|
55
+ r.property(:policies) do
56
+ [
57
+ {
58
+ "PolicyName": "lambda-inline",
59
+ "PolicyDocument": {
60
+ "Version": "2012-10-17",
61
+ "Statement": [
62
+ {
63
+ "Effect": "Allow",
64
+ "Action": [
65
+ "autoscaling:CompleteLifecycleAction",
66
+ "logs:CreateLogGroup",
67
+ "logs:CreateLogStream",
68
+ "logs:PutLogEvents",
69
+ "ec2:DescribeInstances",
70
+ "ec2:DescribeInstanceAttribute",
71
+ "ec2:DescribeInstanceStatus",
72
+ "ec2:DescribeHosts",
73
+ "ecs:ListContainerInstances",
74
+ "ecs:SubmitContainerStateChange",
75
+ "ecs:SubmitTaskStateChange",
76
+ "ecs:DescribeContainerInstances",
77
+ "ecs:UpdateContainerInstancesState",
78
+ "ecs:ListTasks",
79
+ "ecs:DescribeTasks",
80
+ "sns:Publish",
81
+ "sns:ListSubscriptions"
82
+ ],
83
+ "Resource": "*"
84
+ }
85
+ ]
86
+ }
87
+ }
88
+ ]
89
+ end
90
+ r.property(:assume_role_policy_document) do
91
+ {
92
+ "Version": "2012-10-17",
93
+ "Statement": [
94
+ {
95
+ "Effect": "Allow",
96
+ "Principal": {
97
+ "Service": [
98
+ "lambda.amazonaws.com"
99
+ ]
100
+ },
101
+ "Action": [
102
+ "sts:AssumeRole"
103
+ ]
104
+ }
105
+ ]
106
+ }
107
+ end
108
+ r.property(:managed_policy_arns) do
109
+ [
110
+ "arn:aws:iam::aws:policy/service-role/AutoScalingNotificationAccessRole"
111
+ ]
112
+ end
113
+ end
114
+
115
+ resource :lambda_invoke_permission,
116
+ type: "AWS::Lambda::Permission" do |r|
117
+ r.property(:function_name) { :lifecycle_handler_function.ref }
118
+ r.property(:action) { "lambda:InvokeFunction" }
119
+ r.property(:principal) { "sns.amazonaws.com" }
120
+ r.property(:source_arn) { :ecs_lifecycle_notification_topic.ref }
121
+ end
122
+
123
+ resource :lifecycle_handler_function,
124
+ type: "AWS::Lambda::Function" do |r|
125
+ r.property(:environment) do
126
+ {
127
+ "Variables": {
128
+ "CLUSTER": :sfs_ecs_cluster.ref
129
+ }
130
+ }
131
+ end
132
+ r.property(:code) do
133
+ {
134
+ "ZipFile": {
135
+ "Fn::Join": [
136
+ "\n",
137
+ [
138
+ "import boto3,json,os,time",
139
+ "ec2Client = boto3.client('ec2')",
140
+ "ecsClient = boto3.client('ecs')",
141
+ "autoscalingClient = boto3.client('autoscaling')",
142
+ "snsClient = boto3.client('sns')",
143
+ "lambdaClient = boto3.client('lambda')",
144
+ "def publishSNSMessage(snsMessage,snsTopicArn):",
145
+ " response = snsClient.publish(TopicArn=snsTopicArn,Message=json.dumps(snsMessage),Subject='reinvoking')",
146
+ "def setContainerInstanceStatusToDraining(ecsClusterName,containerInstanceArn):",
147
+ " response = ecsClient.update_container_instances_state(cluster=ecsClusterName,containerInstances=[containerInstanceArn],status='DRAINING')",
148
+ "def tasksRunning(ecsClusterName,ec2InstanceId):",
149
+ " ecsContainerInstances = ecsClient.describe_container_instances(cluster=ecsClusterName,containerInstances=ecsClient.list_container_instances(cluster=ecsClusterName)['containerInstanceArns'])['containerInstances']",
150
+ " for i in ecsContainerInstances:",
151
+ " if i['ec2InstanceId'] == ec2InstanceId:",
152
+ " if i['status'] == 'ACTIVE':",
153
+ " setContainerInstanceStatusToDraining(ecsClusterName,i['containerInstanceArn'])",
154
+ " return 1",
155
+ " if (i['runningTasksCount']>0) or (i['pendingTasksCount']>0):",
156
+ " return 1",
157
+ " return 0",
158
+ " return 2",
159
+ "def lambda_handler(event, context):",
160
+ " ecsClusterName=os.environ['CLUSTER']",
161
+ " snsTopicArn=event['Records'][0]['Sns']['TopicArn']",
162
+ " snsMessage=json.loads(event['Records'][0]['Sns']['Message'])",
163
+ " lifecycleHookName=snsMessage['LifecycleHookName']",
164
+ " lifecycleActionToken=snsMessage['LifecycleActionToken']",
165
+ " asgName=snsMessage['AutoScalingGroupName']",
166
+ " ec2InstanceId=snsMessage['EC2InstanceId']",
167
+ " checkTasks=tasksRunning(ecsClusterName,ec2InstanceId)",
168
+ " if checkTasks==0:",
169
+ " try:",
170
+ " response = autoscalingClient.complete_lifecycle_action(LifecycleHookName=lifecycleHookName,AutoScalingGroupName=asgName,LifecycleActionToken=lifecycleActionToken,LifecycleActionResult='CONTINUE')",
171
+ " except BaseException as e:",
172
+ " print(str(e))",
173
+ " elif checkTasks==1:",
174
+ " time.sleep(5)",
175
+ " publishSNSMessage(snsMessage,snsTopicArn)"
176
+ ]
177
+ ]
178
+ }
179
+ }
180
+ end
181
+ r.property(:handler) { "index.lambda_handler" }
182
+ r.property(:role) { :lambda_execution_role.ref(:arn) }
183
+ r.property(:runtime) { "python3.6" }
184
+ r.property(:timeout) { 10 }
185
+ end
186
+ end
187
+ end
188
+ end
@@ -0,0 +1,68 @@
1
+ module EcsStack
2
+ module LoadBalancer
3
+ extend ActiveSupport::Concern
4
+ included do
5
+ parameter :public_subnets,
6
+ description: "List of Public EC2 Subnets for the ALB"
7
+
8
+ resource :ecs_load_balancer,
9
+ type: "AWS::ElasticLoadBalancingV2::LoadBalancer" do |r|
10
+ r.property(:name) { environment }
11
+ r.property(:subnets) { :public_subnets.ref.fnsplit(",") }
12
+ r.property(:security_groups) do
13
+ [
14
+ :ecs_host_security_group.ref,
15
+ :load_balancer_security_group.ref # Not sure if necessary
16
+ ]
17
+ end
18
+ r.property(:tags) do
19
+ [
20
+ {
21
+ "Key": "Name",
22
+ "Value": "#{environment}_ecs_loadbalancer"
23
+ }
24
+ ]
25
+ end
26
+ end
27
+
28
+ resource :load_balancer_listener,
29
+ type: "AWS::ElasticLoadBalancingV2::Listener" do |r|
30
+ r.property(:load_balancer_arn) { :ecs_load_balancer.ref }
31
+ r.property(:port) { 80 }
32
+ r.property(:protocol) { "HTTP" }
33
+ r.property(:default_actions) do
34
+ [
35
+ {
36
+ "Type": "forward",
37
+ "TargetGroupArn": :default_target_group.ref
38
+ }
39
+ ]
40
+ end
41
+ end
42
+
43
+ resource :default_target_group,
44
+ type: "AWS::ElasticLoadBalancingV2::TargetGroup" do |r|
45
+ r.property(:name) { "#{environment}-default" }
46
+ r.property(:vpc_id) { :vpc.ref }
47
+ r.property(:port) { 80 }
48
+ r.property(:protocol) { "HTTP" }
49
+ end
50
+
51
+ output :ecs_load_balancer,
52
+ description: "ECS Application Load Balancer",
53
+ value: :ecs_load_balancer.ref
54
+
55
+ output :ecs_load_balancer_url,
56
+ description: "URL of the ECS ALB",
57
+ value: :ecs_load_balancer.ref("DNSName")
58
+
59
+ output :ecs_load_balancer_listener,
60
+ description: "ECS Port 80 listener",
61
+ value: :load_balancer_listener.ref
62
+
63
+ output :ecs_load_balancer_hosted_zone_id,
64
+ description: "Canonical Hosted Zone ID of the ALB",
65
+ value: :ecs_load_balancer.ref("CanonicalHostedZoneID")
66
+ end
67
+ end
68
+ end
@@ -1,11 +1,12 @@
1
1
  module EcsStack
2
2
  extend ActiveSupport::Concern
3
3
  include Rubycfn
4
-
5
4
  included do
6
5
  include Concerns::GlobalVariables
7
6
  include Concerns::SharedMethods
8
7
  include EcsStack::EcsCluster
8
+ include EcsStack::LifecycleHook
9
+ include EcsStack::LoadBalancer
9
10
 
10
11
  description generate_stack_description("EcsStack")
11
12
  end
@@ -1,11 +1,11 @@
1
- module <%= name %>Stack
1
+ module InfraStack
2
2
  extend ActiveSupport::Concern
3
3
  include Rubycfn
4
4
 
5
5
  included do
6
6
  include Concerns::GlobalVariables
7
7
  include Concerns::SharedMethods
8
- include <%= name %>Stack::Parent
8
+ include InfraStack::Parent
9
9
 
10
10
  description generate_stack_description("ParentStack")
11
11
  end
@@ -0,0 +1,18 @@
1
+ require_relative "../../core/applications"
2
+
3
+ module InfraStack
4
+ module Parent
5
+ extend ActiveSupport::Concern
6
+ included do
7
+ generate_bootstrap_parameters
8
+
9
+ resource :vpc_stack,
10
+ type: "AWS::CloudFormation::Stack" do |r|
11
+ r.property(:template_url) { "vpcstack" }
12
+ r.property(:tags) { default_tags }
13
+ end
14
+
15
+ create_applications
16
+ end
17
+ end
18
+ end