rubycfn 0.5.2 → 0.5.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,185 +3,187 @@ module EcsStack
3
3
  extend ActiveSupport::Concern
4
4
  ## Drains instances on termination
5
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": [
6
+ unless infra_config["environments"][environment]["cluster_size"].nil? || infra_config["environments"][environment]["cluster_size"].to_i.zero?
7
+ resource :ecs_lifecycle_notification_topic,
8
+ depends_on: :lifecycle_handler_function,
9
+ type: "AWS::SNS::Topic" do |r|
10
+ r.property(:subscription) do
11
+ [
36
12
  {
37
- "Effect": "Allow",
38
- "Principal": {
39
- "Service": [
40
- "autoscaling.amazonaws.com"
41
- ]
42
- },
43
- "Action": [
44
- "sts:AssumeRole"
45
- ]
13
+ "Endpoint": :lifecycle_handler_function.ref(:arn),
14
+ "Protocol": "lambda"
46
15
  }
47
16
  ]
48
- }
17
+ end
18
+ end
19
+
20
+ resource :instance_terminating_hook,
21
+ depends_on: :ecs_lifecycle_notification_topic,
22
+ type: "AWS::AutoScaling::LifecycleHook" do |r|
23
+ r.property(:auto_scaling_group_name) { :ecs_auto_scaling_group.ref }
24
+ r.property(:default_result) { "ABANDON" }
25
+ r.property(:heartbeat_timeout) { 900 }
26
+ r.property(:lifecycle_transition) { "autoscaling:EC2_INSTANCE_TERMINATING" }
27
+ r.property(:notification_target_arn) { :ecs_lifecycle_notification_topic.ref }
28
+ r.property(:role_arn) { :autoscaling_notification_role.ref(:arn) }
49
29
  end
50
- r.property(:managed_policy_arns) { ["arn:aws:iam::aws:policy/service-role/AutoScalingNotificationAccessRole"] }
51
- end
52
30
 
53
- resource :lambda_execution_role,
54
- type: "AWS::IAM::Role" do |r|
55
- r.property(:policies) do
56
- [
31
+ resource :autoscaling_notification_role,
32
+ type: "AWS::IAM::Role" do |r|
33
+ r.property(:assume_role_policy_document) do
57
34
  {
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
- }
35
+ "Version": "2012-10-17",
36
+ "Statement": [
37
+ {
38
+ "Effect": "Allow",
39
+ "Principal": {
40
+ "Service": [
41
+ "autoscaling.amazonaws.com"
42
+ ]
43
+ },
44
+ "Action": [
45
+ "sts:AssumeRole"
46
+ ]
47
+ }
48
+ ]
87
49
  }
88
- ]
50
+ end
51
+ r.property(:managed_policy_arns) { ["arn:aws:iam::aws:policy/service-role/AutoScalingNotificationAccessRole"] }
89
52
  end
90
- r.property(:assume_role_policy_document) do
91
- {
92
- "Version": "2012-10-17",
93
- "Statement": [
53
+
54
+ resource :lambda_execution_role,
55
+ type: "AWS::IAM::Role" do |r|
56
+ r.property(:policies) do
57
+ [
94
58
  {
95
- "Effect": "Allow",
96
- "Principal": {
97
- "Service": [
98
- "lambda.amazonaws.com"
59
+ "PolicyName": "lambda-inline",
60
+ "PolicyDocument": {
61
+ "Version": "2012-10-17",
62
+ "Statement": [
63
+ {
64
+ "Effect": "Allow",
65
+ "Action": [
66
+ "autoscaling:CompleteLifecycleAction",
67
+ "logs:CreateLogGroup",
68
+ "logs:CreateLogStream",
69
+ "logs:PutLogEvents",
70
+ "ec2:DescribeInstances",
71
+ "ec2:DescribeInstanceAttribute",
72
+ "ec2:DescribeInstanceStatus",
73
+ "ec2:DescribeHosts",
74
+ "ecs:ListContainerInstances",
75
+ "ecs:SubmitContainerStateChange",
76
+ "ecs:SubmitTaskStateChange",
77
+ "ecs:DescribeContainerInstances",
78
+ "ecs:UpdateContainerInstancesState",
79
+ "ecs:ListTasks",
80
+ "ecs:DescribeTasks",
81
+ "sns:Publish",
82
+ "sns:ListSubscriptions"
83
+ ],
84
+ "Resource": "*"
85
+ }
99
86
  ]
100
- },
101
- "Action": [
102
- "sts:AssumeRole"
103
- ]
87
+ }
104
88
  }
105
89
  ]
106
- }
107
- end
108
- r.property(:managed_policy_arns) do
109
- [
110
- "arn:aws:iam::aws:policy/service-role/AutoScalingNotificationAccessRole"
111
- ]
90
+ end
91
+ r.property(:assume_role_policy_document) do
92
+ {
93
+ "Version": "2012-10-17",
94
+ "Statement": [
95
+ {
96
+ "Effect": "Allow",
97
+ "Principal": {
98
+ "Service": [
99
+ "lambda.amazonaws.com"
100
+ ]
101
+ },
102
+ "Action": [
103
+ "sts:AssumeRole"
104
+ ]
105
+ }
106
+ ]
107
+ }
108
+ end
109
+ r.property(:managed_policy_arns) do
110
+ [
111
+ "arn:aws:iam::aws:policy/service-role/AutoScalingNotificationAccessRole"
112
+ ]
113
+ end
112
114
  end
113
- end
114
115
 
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
116
+ resource :lambda_invoke_permission,
117
+ type: "AWS::Lambda::Permission" do |r|
118
+ r.property(:function_name) { :lifecycle_handler_function.ref }
119
+ r.property(:action) { "lambda:InvokeFunction" }
120
+ r.property(:principal) { "sns.amazonaws.com" }
121
+ r.property(:source_arn) { :ecs_lifecycle_notification_topic.ref }
122
+ end
122
123
 
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
124
+ resource :lifecycle_handler_function,
125
+ type: "AWS::Lambda::Function" do |r|
126
+ r.property(:environment) do
127
+ {
128
+ "Variables": {
129
+ "CLUSTER": :ecs_cluster.ref
130
+ }
129
131
  }
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)"
132
+ end
133
+ r.property(:code) do
134
+ {
135
+ "ZipFile": {
136
+ "Fn::Join": [
137
+ "\n",
138
+ [
139
+ "import boto3,json,os,time",
140
+ "ec2Client = boto3.client('ec2')",
141
+ "ecsClient = boto3.client('ecs')",
142
+ "autoscalingClient = boto3.client('autoscaling')",
143
+ "snsClient = boto3.client('sns')",
144
+ "lambdaClient = boto3.client('lambda')",
145
+ "def publishSNSMessage(snsMessage,snsTopicArn):",
146
+ " response = snsClient.publish(TopicArn=snsTopicArn,Message=json.dumps(snsMessage),Subject='reinvoking')",
147
+ "def setContainerInstanceStatusToDraining(ecsClusterName,containerInstanceArn):",
148
+ " response = ecsClient.update_container_instances_state(cluster=ecsClusterName,containerInstances=[containerInstanceArn],status='DRAINING')",
149
+ "def tasksRunning(ecsClusterName,ec2InstanceId):",
150
+ " ecsContainerInstances = ecsClient.describe_container_instances(cluster=ecsClusterName,containerInstances=ecsClient.list_container_instances(cluster=ecsClusterName)['containerInstanceArns'])['containerInstances']",
151
+ " for i in ecsContainerInstances:",
152
+ " if i['ec2InstanceId'] == ec2InstanceId:",
153
+ " if i['status'] == 'ACTIVE':",
154
+ " setContainerInstanceStatusToDraining(ecsClusterName,i['containerInstanceArn'])",
155
+ " return 1",
156
+ " if (i['runningTasksCount']>0) or (i['pendingTasksCount']>0):",
157
+ " return 1",
158
+ " return 0",
159
+ " return 2",
160
+ "def lambda_handler(event, context):",
161
+ " ecsClusterName=os.environ['CLUSTER']",
162
+ " snsTopicArn=event['Records'][0]['Sns']['TopicArn']",
163
+ " snsMessage=json.loads(event['Records'][0]['Sns']['Message'])",
164
+ " lifecycleHookName=snsMessage['LifecycleHookName']",
165
+ " lifecycleActionToken=snsMessage['LifecycleActionToken']",
166
+ " asgName=snsMessage['AutoScalingGroupName']",
167
+ " ec2InstanceId=snsMessage['EC2InstanceId']",
168
+ " checkTasks=tasksRunning(ecsClusterName,ec2InstanceId)",
169
+ " if checkTasks==0:",
170
+ " try:",
171
+ " response = autoscalingClient.complete_lifecycle_action(LifecycleHookName=lifecycleHookName,AutoScalingGroupName=asgName,LifecycleActionToken=lifecycleActionToken,LifecycleActionResult='CONTINUE')",
172
+ " except BaseException as e:",
173
+ " print(str(e))",
174
+ " elif checkTasks==1:",
175
+ " time.sleep(5)",
176
+ " publishSNSMessage(snsMessage,snsTopicArn)"
177
+ ]
176
178
  ]
177
- ]
179
+ }
178
180
  }
179
- }
181
+ end
182
+ r.property(:handler) { "index.lambda_handler" }
183
+ r.property(:role) { :lambda_execution_role.ref(:arn) }
184
+ r.property(:runtime) { "python3.6" }
185
+ r.property(:timeout) { 10 }
180
186
  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
187
  end
186
188
  end
187
189
  end
@@ -5,64 +5,66 @@ module EcsStack
5
5
  parameter :public_subnets,
6
6
  description: "List of Public EC2 Subnets for the ALB"
7
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
- ]
8
+ unless infra_config["environments"][environment]["cluster_size"].nil? || infra_config["environments"][environment]["cluster_size"].to_i.zero?
9
+ resource :ecs_load_balancer,
10
+ type: "AWS::ElasticLoadBalancingV2::LoadBalancer" do |r|
11
+ r.property(:name) { environment }
12
+ r.property(:subnets) { :public_subnets.ref.fnsplit(",") }
13
+ r.property(:security_groups) do
14
+ [
15
+ :ecs_host_security_group.ref,
16
+ :load_balancer_security_group.ref # Not sure if necessary
17
+ ]
18
+ end
19
+ r.property(:tags) do
20
+ [
21
+ {
22
+ "Key": "Name",
23
+ "Value": "#{environment}_ecs_loadbalancer"
24
+ }
25
+ ]
26
+ end
17
27
  end
18
- r.property(:tags) do
19
- [
20
- {
21
- "Key": "Name",
22
- "Value": "#{environment}_ecs_loadbalancer"
23
- }
24
- ]
25
- end
26
- end
27
28
 
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
- ]
29
+ resource :load_balancer_listener,
30
+ type: "AWS::ElasticLoadBalancingV2::Listener" do |r|
31
+ r.property(:load_balancer_arn) { :ecs_load_balancer.ref }
32
+ r.property(:port) { 80 }
33
+ r.property(:protocol) { "HTTP" }
34
+ r.property(:default_actions) do
35
+ [
36
+ {
37
+ "Type": "forward",
38
+ "TargetGroupArn": :default_target_group.ref
39
+ }
40
+ ]
41
+ end
40
42
  end
41
- end
42
43
 
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
44
+ resource :default_target_group,
45
+ type: "AWS::ElasticLoadBalancingV2::TargetGroup" do |r|
46
+ r.property(:name) { "#{environment}-default" }
47
+ r.property(:vpc_id) { :vpc.ref }
48
+ r.property(:port) { 80 }
49
+ r.property(:protocol) { "HTTP" }
50
+ end
50
51
 
51
- output :ecs_load_balancer,
52
- description: "ECS Application Load Balancer",
53
- value: :ecs_load_balancer.ref
52
+ output :ecs_load_balancer,
53
+ description: "ECS Application Load Balancer",
54
+ value: :ecs_load_balancer.ref
54
55
 
55
- output :ecs_load_balancer_url,
56
- description: "URL of the ECS ALB",
57
- value: :ecs_load_balancer.ref("DNSName")
56
+ output :ecs_load_balancer_url,
57
+ description: "URL of the ECS ALB",
58
+ value: :ecs_load_balancer.ref("DNSName")
58
59
 
59
- output :ecs_load_balancer_listener,
60
- description: "ECS Port 80 listener",
61
- value: :load_balancer_listener.ref
60
+ output :ecs_load_balancer_listener,
61
+ description: "ECS Port 80 listener",
62
+ value: :load_balancer_listener.ref
62
63
 
63
- output :ecs_load_balancer_hosted_zone_id,
64
- description: "Canonical Hosted Zone ID of the ALB",
65
- value: :ecs_load_balancer.ref("CanonicalHostedZoneID")
64
+ output :ecs_load_balancer_hosted_zone_id,
65
+ description: "Canonical Hosted Zone ID of the ALB",
66
+ value: :ecs_load_balancer.ref("CanonicalHostedZoneID")
67
+ end
66
68
  end
67
69
  end
68
70
  end