From 486a577b5b83f0f2f3644a2201572f5e55d987c1 Mon Sep 17 00:00:00 2001 From: Clare Liguori Date: Wed, 7 Nov 2018 22:51:16 -0800 Subject: [PATCH 1/3] feat(aws-ecs): Support HTTPS in load balanced Fargate service --- .../lib/load-balanced-fargate-service.ts | 41 ++++++++++- packages/@aws-cdk/aws-ecs/package.json | 2 + packages/@aws-cdk/aws-ecs/test/test.l3s.ts | 70 +++++++++++++++++++ 3 files changed, 112 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-ecs/lib/load-balanced-fargate-service.ts b/packages/@aws-cdk/aws-ecs/lib/load-balanced-fargate-service.ts index 5ee7bba7769b3..722a016793def 100644 --- a/packages/@aws-cdk/aws-ecs/lib/load-balanced-fargate-service.ts +++ b/packages/@aws-cdk/aws-ecs/lib/load-balanced-fargate-service.ts @@ -1,4 +1,6 @@ +import { CertificateRef } from '@aws-cdk/aws-certificatemanager'; import elbv2 = require('@aws-cdk/aws-elasticloadbalancingv2'); +import { AliasRecord, HostedZoneRef } from '@aws-cdk/aws-route53'; import cdk = require('@aws-cdk/cdk'); import { ICluster } from './cluster'; import { IContainerImage } from './container-image'; @@ -83,6 +85,22 @@ export interface LoadBalancedFargateServiceProps { * @default 1 */ desiredCount?: number; + + /* + * Domain name for the service, e.g. api.example.com + */ + domainName?: string; + + /** + * Route53 hosted zone for the domain, e.g. "example.com." + */ + domainZone?: HostedZoneRef; + + /** + * Certificate Manager certificate to associate with the load balancer. + * Setting this option will set the load balancer port to 443. + */ + certificate?: CertificateRef; } /** @@ -123,12 +141,33 @@ export class LoadBalancedFargateService extends cdk.Construct { this.loadBalancer = lb; - const listener = lb.addListener('PublicListener', { port: 80, open: true }); + let listener; + if (typeof props.certificate !== 'undefined') { + listener = lb.addListener('PublicListener', { + port: 443, + open: true, + certificateArns: [props.certificate.certificateArn] + }); + } else { + listener = lb.addListener('PublicListener', { port: 80, open: true }); + } + listener.addTargets('ECS', { port: 80, targets: [service] }); new cdk.Output(this, 'LoadBalancerDNS', { value: lb.dnsName }); + + if (typeof props.domainName !== 'undefined') { + if (typeof props.domainZone === 'undefined') { + throw new Error('A Route53 hosted domain zone name is required to configure the specified domain name'); + } + + new AliasRecord(props.domainZone, "DNS", { + recordName: props.domainName, + target: lb + }); + } } } diff --git a/packages/@aws-cdk/aws-ecs/package.json b/packages/@aws-cdk/aws-ecs/package.json index bde03911bc472..69ec75eafde1a 100644 --- a/packages/@aws-cdk/aws-ecs/package.json +++ b/packages/@aws-cdk/aws-ecs/package.json @@ -63,6 +63,7 @@ "dependencies": { "@aws-cdk/aws-applicationautoscaling": "^0.15.2", "@aws-cdk/aws-autoscaling": "^0.15.2", + "@aws-cdk/aws-certificatemanager": "^0.15.2", "@aws-cdk/aws-cloudformation": "^0.15.2", "@aws-cdk/aws-cloudwatch": "^0.15.2", "@aws-cdk/aws-ec2": "^0.15.2", @@ -72,6 +73,7 @@ "@aws-cdk/aws-iam": "^0.15.2", "@aws-cdk/aws-lambda": "^0.15.2", "@aws-cdk/aws-logs": "^0.15.2", + "@aws-cdk/aws-route53": "^0.15.2", "@aws-cdk/cdk": "^0.15.2", "@aws-cdk/cx-api": "^0.15.2" }, diff --git a/packages/@aws-cdk/aws-ecs/test/test.l3s.ts b/packages/@aws-cdk/aws-ecs/test/test.l3s.ts index 9ef60be7cba3d..17b7a0b4aa0a0 100644 --- a/packages/@aws-cdk/aws-ecs/test/test.l3s.ts +++ b/packages/@aws-cdk/aws-ecs/test/test.l3s.ts @@ -1,5 +1,7 @@ import { expect, haveResource } from '@aws-cdk/assert'; +import { CertificateRef } from '@aws-cdk/aws-certificatemanager'; import ec2 = require('@aws-cdk/aws-ec2'); +import { PublicHostedZone } from '@aws-cdk/aws-route53'; import cdk = require('@aws-cdk/cdk'); import { Test } from 'nodeunit'; import ecs = require('../lib'); @@ -52,6 +54,74 @@ export = { LaunchType: "FARGATE", })); + expect(stack).to(haveResource('AWS::ElasticLoadBalancingV2::Listener', { + Port: 80 + })); + + test.done(); + }, + + 'test Fargateloadbalanced construct with TLS'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.VpcNetwork(stack, 'VPC'); + const cluster = new ecs.Cluster(stack, 'Cluster', { vpc }); + const zone = new PublicHostedZone(stack, 'HostedZone', { zoneName: 'example.com' }); + + // WHEN + new ecs.LoadBalancedFargateService(stack, 'Service', { + cluster, + image: ecs.DockerHub.image('test'), + domainName: 'api.example.com', + domainZone: zone, + certificate: CertificateRef.import(stack, 'Cert', { certificateArn: 'helloworld' }) + }); + + // THEN - stack contains a load balancer and a service + expect(stack).to(haveResource('AWS::ElasticLoadBalancingV2::LoadBalancer')); + + expect(stack).to(haveResource('AWS::ElasticLoadBalancingV2::Listener', { + Port: 443, + Certificates: [{ + CertificateArn: "helloworld" + }] + })); + + expect(stack).to(haveResource("AWS::ECS::Service", { + DesiredCount: 1, + LaunchType: "FARGATE", + })); + + expect(stack).to(haveResource('AWS::Route53::RecordSet', { + Name: 'api.example.com.', + HostedZoneId: { + Ref: "HostedZoneDB99F866" + }, + Type: 'A', + AliasTarget: { + HostedZoneId: { 'Fn::GetAtt': [ 'ServiceLBE9A1ADBC', 'CanonicalHostedZoneID' ] }, + DNSName: { 'Fn::GetAtt': [ 'ServiceLBE9A1ADBC', 'DNSName' ] }, + } + })); + + test.done(); + }, + + "errors when setting domainName but not domainZone"(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.VpcNetwork(stack, 'VPC'); + const cluster = new ecs.Cluster(stack, 'Cluster', { vpc }); + + // THEN + test.throws(() => { + new ecs.LoadBalancedFargateService(stack, 'Service', { + cluster, + image: ecs.DockerHub.image('test'), + domainName: 'api.example.com' + }); + }); + test.done(); }, From 7c4c58e4c0d39fc8f7aab4e87ee65ff06eb0cfd6 Mon Sep 17 00:00:00 2001 From: Clare Liguori Date: Tue, 13 Nov 2018 08:56:00 -0800 Subject: [PATCH 2/3] fix merge typo --- packages/@aws-cdk/aws-ecs/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/@aws-cdk/aws-ecs/package.json b/packages/@aws-cdk/aws-ecs/package.json index b84d5b267287f..44158b7816ebc 100644 --- a/packages/@aws-cdk/aws-ecs/package.json +++ b/packages/@aws-cdk/aws-ecs/package.json @@ -76,7 +76,6 @@ "@aws-cdk/aws-route53": "^0.16.0", "@aws-cdk/cdk": "^0.16.0", "@aws-cdk/cx-api": "^0.16.0" ->>>>>>> upstream/master }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { From dac782467321fcabea342911f2ce0f4e67dd744a Mon Sep 17 00:00:00 2001 From: Clare Liguori Date: Tue, 13 Nov 2018 10:56:54 -0800 Subject: [PATCH 3/3] Remove ecs.DockerHub from newer tests --- packages/@aws-cdk/aws-ecs/test/test.l3s.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-ecs/test/test.l3s.ts b/packages/@aws-cdk/aws-ecs/test/test.l3s.ts index 81a6a2a580e43..048ef2593689d 100644 --- a/packages/@aws-cdk/aws-ecs/test/test.l3s.ts +++ b/packages/@aws-cdk/aws-ecs/test/test.l3s.ts @@ -71,7 +71,7 @@ export = { // WHEN new ecs.LoadBalancedFargateService(stack, 'Service', { cluster, - image: ecs.DockerHub.image('test'), + image: ecs.ContainerImage.fromDockerHub('test'), domainName: 'api.example.com', domainZone: zone, certificate: CertificateRef.import(stack, 'Cert', { certificateArn: 'helloworld' }) @@ -117,7 +117,7 @@ export = { test.throws(() => { new ecs.LoadBalancedFargateService(stack, 'Service', { cluster, - image: ecs.DockerHub.image('test'), + image: ecs.ContainerImage.fromDockerHub('test'), domainName: 'api.example.com' }); });