ciinabox-ecs 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,81 @@
1
+ require 'cfndsl'
2
+ require_relative '../../ext/helper'
3
+
4
+ if !defined? timezone
5
+ timezone = 'GMT'
6
+ end
7
+
8
+ image = 'atlassian/bitbucket-server'
9
+ memory = 2048
10
+ cpu = 300
11
+ container_port = 7999
12
+ service = lookup_service('jenkins', services)
13
+ if service
14
+ image = service['ContainerImage'] || 'atlassian/bitbucket-server'
15
+ memory = service['ContainerMemory'] || 2048
16
+ cpu = service['ContainerCPU'] || 300
17
+ container_port = service['InstancePort'] || 7999
18
+ end
19
+
20
+ CloudFormation {
21
+
22
+ AWSTemplateFormatVersion "2010-09-09"
23
+ Description "ciinabox - ECS Service Bitbucket v#{ciinabox_version}"
24
+
25
+ Parameter("ECSCluster"){ Type 'String' }
26
+ Parameter("ECSRole"){ Type 'String' }
27
+ Parameter("ServiceELB"){ Type 'String' }
28
+
29
+ Resource('BitbucketTask') {
30
+ Type "AWS::ECS::TaskDefinition"
31
+ Property('ContainerDefinitions', [
32
+ {
33
+ Name: 'bitbucket',
34
+ Memory: memory,
35
+ Cpu: cpu,
36
+ Image: image,
37
+ PortMappings: [{
38
+ HostPort: container_port,
39
+ ContainerPort: container_port
40
+ }],
41
+ Environment: [
42
+ {
43
+ Name: 'VIRTUAL_HOST',
44
+ Value: "bitbucket.#{dns_domain}"
45
+ },
46
+ {
47
+ Name: 'VIRTUAL_PORT',
48
+ Value: '7990'
49
+ }
50
+ ],
51
+ Essential: true,
52
+ MountPoints: [
53
+ {
54
+ ContainerPath: '/var/atlassian/application-data/bitbucket',
55
+ SourceVolume: 'bitbucket_data',
56
+ ReadOnly: false
57
+ }
58
+ ]
59
+ }
60
+ ])
61
+ Property('Volumes', [
62
+ {
63
+ Name: 'bitbucket_data',
64
+ Host: {
65
+ SourcePath: '/data/bitbucket'
66
+ }
67
+ }
68
+ ])
69
+ }
70
+
71
+ Resource('BitbucketService') {
72
+ Type 'AWS::ECS::Service'
73
+ Property('Cluster', Ref('ECSCluster'))
74
+ Property('DesiredCount', 1)
75
+ Property('TaskDefinition', Ref('BitbucketTask'))
76
+ Property('Role', Ref('ECSRole'))
77
+ Property('LoadBalancers', [
78
+ { ContainerName: 'bitbucket', ContainerPort: '7999', LoadBalancerName: Ref('ServiceELB') }
79
+ ])
80
+ }
81
+ }
@@ -0,0 +1,394 @@
1
+ require 'cfndsl'
2
+ require 'securerandom'
3
+ require 'deep_merge'
4
+
5
+ # default values
6
+ shared_envs = {
7
+ TZ: "Australia/Melbourne",
8
+ DRONE_SECRET: SecureRandom.hex
9
+ }
10
+ drone_server_envs = shared_envs.clone
11
+ drone_server_envs.deep_merge!({
12
+ DRONE_HOST: "https://drone.#{dns_domain}"
13
+ })
14
+ drone_agent_envs = shared_envs.clone
15
+ drone_agent_envs.deep_merge!({
16
+ DRONE_SERVER: {
17
+ "Fn::Join" => [
18
+ ":",
19
+ [
20
+ { Ref: "ECSENIPrivateIpAddress" },
21
+ "9000"
22
+ ]
23
+ ]
24
+ }
25
+ })
26
+ internal_drone_elb = true
27
+ drone_server_image = 'drone/drone'
28
+ drone_agent_image = 'drone/agent'
29
+ drone_server_ext_ports = [ '8000' ]
30
+ drone_server_int_ports = [ '9000' ]
31
+ drone_server_mappings = []
32
+ drone_agent_mappings = []
33
+ drone_params = [
34
+ { VPC: { Ref: "VPC" } },
35
+ { SubnetPublicA: { Ref: "SubnetPublicA" } },
36
+ { SubnetPublicB: { Ref: "SubnetPublicB" } },
37
+ { ECSSubnetPrivateA: { Ref: "ECSSubnetPrivateA" } },
38
+ { ECSSubnetPrivateB: { Ref: "ECSSubnetPrivateB"} },
39
+ { SecurityGroupBackplane: { Ref: "SecurityGroupBackplane" } },
40
+ { SecurityGroupOps: { Ref: "SecurityGroupOps" } },
41
+ { SecurityGroupDev: { Ref: "SecurityGroupDev" } },
42
+ { SecurityGroupNatGateway: { Ref: "SecurityGroupNatGateway" } },
43
+ { SecurityGroupWebHooks: { Ref: "SecurityGroupWebHooks" } },
44
+ { ECSENIPrivateIpAddress: { Ref: "ECSENIPrivateIpAddress" } }
45
+ ]
46
+
47
+ # resource allocations
48
+ memory = 512
49
+ cpu = 256
50
+
51
+ # look up service
52
+ service = lookup_service('drone', services)
53
+
54
+ if service and service['params'].kind_of?(Array)
55
+ drone_params = drone_params | service['params']
56
+ end
57
+
58
+ if service and service['tasks']
59
+ tasks = service['tasks']
60
+ if not tasks['drone-server'].nil?
61
+ # drone-server envs
62
+ drone_server_envs.deep_merge!(tasks['drone-server']['env'])
63
+ # drone-server docker images
64
+ drone_server_image = tasks['drone-server']['image'] || drone_server_image
65
+ # drone-server ports
66
+ drone_server_ext_ports = tasks['drone-server']['ext-ports'] || drone_server_ext_ports
67
+ drone_server_int_ports = tasks['drone-server']['int-ports'] || drone_server_int_ports
68
+ # drone-server mappings
69
+ drone_server_mappings = tasks['drone-server']['mappings'] || drone_server_mappings
70
+ end
71
+
72
+ if not tasks['drone-agent'].nil?
73
+ # drone-agent envs
74
+ drone_agent_envs.deep_merge!(tasks['drone-agent']['env'])
75
+ # drone-agent docker images
76
+ drone_agent_image = tasks['drone-agent']['image'] || drone_agent_image
77
+ # drone-agent mappings
78
+ drone_agent_mappings = tasks['drone-agent']['mappings'] || drone_agent_mappings
79
+ end
80
+
81
+ # internal elb
82
+ internal_drone_elb = tasks['internal_drone_elb'] || internal_drone_elb
83
+ end
84
+
85
+ # dictionaries
86
+ envs = {
87
+ 'drone-server' => drone_server_envs,
88
+ 'drone-agent' => drone_agent_envs
89
+ }
90
+
91
+ images = {
92
+ 'drone-server' => drone_server_image,
93
+ 'drone-agent' => drone_agent_image
94
+ }
95
+
96
+ ext_ports = {
97
+ 'drone-server' => drone_server_ext_ports
98
+ }
99
+
100
+ int_ports = {
101
+ 'drone-server' => drone_server_int_ports
102
+ }
103
+
104
+ mappings = {
105
+ 'drone-server' => drone_server_mappings,
106
+ 'drone-agent' => drone_agent_mappings
107
+ }
108
+
109
+ # defined ecs volumes
110
+ volumes = [
111
+ {
112
+ Name: 'timezone',
113
+ Host: {
114
+ SourcePath: '/etc/localtime'
115
+ }
116
+ },
117
+ {
118
+ Name: 'drone_data',
119
+ Host: {
120
+ SourcePath: '/data/drone'
121
+ }
122
+ },
123
+ {
124
+ Name: 'docker_socket',
125
+ Host: {
126
+ SourcePath: '/var/run/docker.sock'
127
+ }
128
+ }
129
+ ]
130
+
131
+ # mounts
132
+ mounts = {
133
+ 'drone-server' => [
134
+ {
135
+ ContainerPath: '/etc/localtime',
136
+ SourceVolume: 'timezone',
137
+ ReadOnly: true
138
+ },
139
+ {
140
+ ContainerPath: '/var/lib/drone',
141
+ SourceVolume: 'drone_data',
142
+ ReadOnly: false
143
+ }
144
+ ],
145
+ 'drone-agent' => [
146
+ {
147
+ ContainerPath: '/etc/localtime',
148
+ SourceVolume: 'timezone',
149
+ ReadOnly: true
150
+ },
151
+ {
152
+ ContainerPath: '/var/run/docker.sock',
153
+ SourceVolume: 'docker_socket',
154
+ ReadOnly: false
155
+ }
156
+ ]
157
+ }
158
+
159
+ # generate container definition
160
+ container_definitions = {}
161
+
162
+ ['drone-server', 'drone-agent'].each do | task |
163
+ definition = (service && service['tasks']) ? (service['tasks'][task] || {}) : {}
164
+ container_definition = [{
165
+ Name: task,
166
+ Links: [],
167
+ Memory: definition['memory'] || memory,
168
+ Cpu: definition['cpu'] || cpu,
169
+ Image: images[task],
170
+ PortMappings: ((ext_ports[task] || []) | (int_ports[task] || [])).map { |mapping|
171
+ t = mapping.split(':')
172
+ if t.length >= 2
173
+ {
174
+ ContainerPort: t[1].to_i,
175
+ HostPort: t[0].to_i
176
+ }.merge(t[2] ? {Protocol: t[2]} : {})
177
+ elsif t.length == 1
178
+ {
179
+ ContainerPort: t[0].to_i
180
+ }
181
+ end
182
+ }.compact,
183
+ Essential: true,
184
+ MountPoints: mounts[task],
185
+ Environment: envs[task].map { |key, value|
186
+ {
187
+ Name: key,
188
+ Value: value
189
+ }
190
+ }
191
+ }]
192
+ container_definitions.merge!(task => container_definition)
193
+ end
194
+
195
+ # Cloudformation
196
+ CloudFormation {
197
+
198
+ AWSTemplateFormatVersion "2010-09-09"
199
+ Description "ciinabox - ECS Service Drone v#{ciinabox_version}"
200
+
201
+ Parameter("ECSCluster") {Type 'String'}
202
+ Parameter("ECSRole") {Type 'String'}
203
+ Parameter("ServiceELB") {Type 'String'}
204
+ Parameter('InternalELB') {Type 'String'} if internal_elb
205
+
206
+ drone_params.each do |param|
207
+ param.keys.each do |key|
208
+ Parameter(key) {Type 'String'}
209
+ end
210
+ end
211
+
212
+ # Mapping
213
+ Mapping('EnvironmentType', Mappings['EnvironmentType'])
214
+
215
+ drone_alb_sg = [Ref("SecurityGroupBackplane"), Ref("SecurityGroupOps"), Ref("SecurityGroupDev"), Ref("SecurityGroupNatGateway"), Ref("SecurityGroupWebHooks")]
216
+
217
+ Resource('DroneServerALB') {
218
+ Type "AWS::ElasticLoadBalancingV2::LoadBalancer"
219
+ Property("SecurityGroups", drone_alb_sg)
220
+ Property("Subnets", [Ref("SubnetPublicA"), Ref("SubnetPublicB")])
221
+ Property("Tags", [
222
+ { Key: "Name", Value: "DroneServerALB Application LoadBalancer" }
223
+ ])
224
+ }
225
+
226
+ Resource('DroneServerALBTargetGroup') {
227
+ DependsOn("DroneServerALB")
228
+ Type "AWS::ElasticLoadBalancingV2::TargetGroup"
229
+ Property("HealthCheckPath", '/')
230
+ Property("HealthCheckProtocol", 'HTTP')
231
+ Property("HealthCheckIntervalSeconds", 30)
232
+ Property("HealthCheckTimeoutSeconds", 10)
233
+ Property("HealthyThresholdCount", 3)
234
+ Property("UnhealthyThresholdCount", 2)
235
+ Property("Matcher", {
236
+ HttpCode: "200,302,301"
237
+ })
238
+ Property("Port", 8000)
239
+ Property("Protocol", 'HTTP')
240
+ Property("VpcId", Ref("VPC"))
241
+ Property("Tags",[
242
+ { Key: "Name", Value: "DroneServerALB Target Group" },
243
+ ])
244
+ }
245
+
246
+ Resource("DroneServerALBHTTPListener") {
247
+ DependsOn("DroneServerALB")
248
+ DependsOn("DroneServerALBTargetGroup")
249
+ Type "AWS::ElasticLoadBalancingV2::Listener"
250
+ Property("Protocol", "HTTP")
251
+ Property("Port", 80)
252
+ Property("DefaultActions", [
253
+ TargetGroupArn: Ref("DroneServerALBTargetGroup"),
254
+ Type: "forward"
255
+ ])
256
+ Property("LoadBalancerArn", Ref("DroneServerALB"))
257
+ }
258
+
259
+ Resource("DroneServerALBHTTPSListener") {
260
+ DependsOn("DroneServerALB")
261
+ DependsOn("DroneServerALBTargetGroup")
262
+ Type "AWS::ElasticLoadBalancingV2::Listener"
263
+ Property("Certificates", [{CertificateArn: default_ssl_cert_id}])
264
+ Property("Protocol", "HTTPS")
265
+ Property("Port", 443)
266
+ Property("DefaultActions", [
267
+ TargetGroupArn: Ref("DroneServerALBTargetGroup"),
268
+ Type: "forward"
269
+ ])
270
+ Property("LoadBalancerArn", Ref("DroneServerALB"))
271
+ }
272
+
273
+ # Resource("SecurityGroupDroneNLB") {
274
+ # Type 'AWS::EC2::SecurityGroup'
275
+ # Property('VpcId', Ref('VPC'))
276
+ # Property('GroupDescription', 'DRONE NLB SG')
277
+ # Property('SecurityGroupIngress', [
278
+ # { IpProtocol: 'tcp', FromPort: '9000', ToPort: '9000', CidrIp: FnJoin( "", [ FnFindInMap('EnvironmentType','ciinabox','NetworkPrefix'),".", FnFindInMap('EnvironmentType','ciinabox','StackOctet'), ".0.0/",FnFindInMap('EnvironmentType','ciinabox','StackMask') ] ) }
279
+ # ])
280
+ # }
281
+ #
282
+ # drone_nlb_sg = [Ref("SecurityGroupDroneNLB")]
283
+ #
284
+ # Resource('DroneServerNLB') {
285
+ # Type "AWS::ElasticLoadBalancingV2::LoadBalancer"
286
+ # Property("Scheme", "internal")
287
+ # Property("Type", "network")
288
+ # # Property("SecurityGroups", drone_nlb_sg)
289
+ # Property("Subnets", [
290
+ # Ref("ECSSubnetPrivateA"),
291
+ # Ref("ECSSubnetPrivateB")
292
+ # ])
293
+ # Property("Tags", [
294
+ # { Key: "Name", Value: "DroneServerNLB Network LoadBalancer" }
295
+ # ])
296
+ # }
297
+ #
298
+ # Resource('DroneServerNLBTargetGroup') {
299
+ # DependsOn("DroneServerNLB")
300
+ # Type "AWS::ElasticLoadBalancingV2::TargetGroup"
301
+ # Property("HealthCheckProtocol", 'TCP')
302
+ # Property("HealthCheckPort", 9000)
303
+ # Property("HealthCheckIntervalSeconds", 30)
304
+ # Property("HealthCheckTimeoutSeconds", 10)
305
+ # Property("HealthyThresholdCount", 3)
306
+ # Property("UnhealthyThresholdCount", 3)
307
+ # Property("Port", 9000)
308
+ # Property("Protocol", 'TCP')
309
+ # Property("VpcId", Ref("VPC"))
310
+ # Property("Tags",[
311
+ # { Key: "Name", Value: "DroneServerNLB Target Group" },
312
+ # ])
313
+ # }
314
+ #
315
+ # Resource("DroneServerNLBListener") {
316
+ # DependsOn("DroneServerNLB")
317
+ # DependsOn("DroneServerNLBTargetGroup")
318
+ # Type "AWS::ElasticLoadBalancingV2::Listener"
319
+ # Property("Protocol", "TCP")
320
+ # Property("Port", 9000)
321
+ # Property("DefaultActions", [
322
+ # TargetGroupArn: Ref("DroneServerNLBTargetGroup"),
323
+ # Type: "forward"
324
+ # ])
325
+ # Property("LoadBalancerArn", Ref("DroneServerNLB"))
326
+ # }
327
+
328
+ Resource('DroneServerTask') {
329
+ Type "AWS::ECS::TaskDefinition"
330
+ Property('ContainerDefinitions', container_definitions['drone-server'])
331
+ Property('NetworkMode', 'host')
332
+ Property('Volumes', volumes)
333
+ }
334
+
335
+ Resource('DroneAgentTask') {
336
+ Type "AWS::ECS::TaskDefinition"
337
+ Property('ContainerDefinitions', container_definitions['drone-agent'])
338
+ Property('NetworkMode', 'host')
339
+ Property('Volumes', volumes)
340
+ }
341
+
342
+ drone_server_lbs = []
343
+
344
+ Resource('DroneServerService') {
345
+ Type 'AWS::ECS::Service'
346
+ Property('Cluster', Ref('ECSCluster'))
347
+ Property('DeploymentConfiguration', {
348
+ MaximumPercent: 100,
349
+ MinimumHealthyPercent: 0
350
+ })
351
+ Property('DesiredCount', 1)
352
+ Property('TaskDefinition', Ref('DroneServerTask'))
353
+ Property('Role', Ref('ECSRole'))
354
+ Property('LoadBalancers', (ext_ports['drone-server'] || []).map { |mapping|
355
+ t = mapping.split(':')
356
+ if t.length >= 2
357
+ {
358
+ ContainerName: 'drone-server',
359
+ ContainerPort: t[1].to_i,
360
+ TargetGroupArn: Ref('DroneServerALBTargetGroup')
361
+ }
362
+ else
363
+ {
364
+ ContainerName: 'drone-server',
365
+ ContainerPort: t[0].to_i,
366
+ TargetGroupArn: Ref('DroneServerALBTargetGroup')
367
+ }
368
+ end
369
+ })
370
+ }
371
+
372
+ Resource('DroneAgentService') {
373
+ Type 'AWS::ECS::Service'
374
+ DependsOn('DroneServerService')
375
+ Property('Cluster', Ref('ECSCluster'))
376
+ Property('DeploymentConfiguration', {
377
+ MaximumPercent: 100,
378
+ MinimumHealthyPercent: 0
379
+ })
380
+ Property('DesiredCount', 1)
381
+ Property('TaskDefinition', Ref('DroneAgentTask'))
382
+ }
383
+
384
+ Resource("DroneServerDNS") {
385
+ Type 'AWS::Route53::RecordSet'
386
+ Property('HostedZoneName', FnJoin('', [ dns_domain, '.']))
387
+ Property('Name', FnJoin('', ['drone.', dns_domain, '.']))
388
+ Property('Type','A')
389
+ Property('AliasTarget', {
390
+ 'DNSName' => FnGetAtt('DroneServerALB','DNSName'),
391
+ 'HostedZoneId' => FnGetAtt('DroneServerALB','CanonicalHostedZoneID')
392
+ })
393
+ }
394
+ }