ciinabox-ecs 0.1.6

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.
@@ -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
+ }