From 61e8a11612b1b94fa61542b757c5a5c09f630b36 Mon Sep 17 00:00:00 2001 From: Clare Liguori Date: Thu, 15 Nov 2018 11:31:52 -0800 Subject: [PATCH] feat(aws-ecs): TLS support for Fargate service applet --- .../load-balanced-fargate-service-applet.ts | 37 ++++++++++++-- .../lib/load-balanced-fargate-service.ts | 2 +- packages/@aws-cdk/aws-ecs/test/test.l3s.ts | 51 +++++++++++++++++++ 3 files changed, 86 insertions(+), 4 deletions(-) diff --git a/packages/@aws-cdk/aws-ecs/lib/load-balanced-fargate-service-applet.ts b/packages/@aws-cdk/aws-ecs/lib/load-balanced-fargate-service-applet.ts index 894977f9ed172..4fe86ab5e1d45 100644 --- a/packages/@aws-cdk/aws-ecs/lib/load-balanced-fargate-service-applet.ts +++ b/packages/@aws-cdk/aws-ecs/lib/load-balanced-fargate-service-applet.ts @@ -1,4 +1,6 @@ -import ec2 = require('@aws-cdk/aws-ec2'); +import { CertificateRef } from '@aws-cdk/aws-certificatemanager'; +import { VpcNetwork } from '@aws-cdk/aws-ec2'; +import { HostedZoneProvider } from '@aws-cdk/aws-route53'; import cdk = require('@aws-cdk/cdk'); import { Cluster } from './cluster'; import { ContainerImage } from './container-image'; @@ -77,18 +79,44 @@ export interface LoadBalancedFargateServiceAppletProps extends cdk.StackProps { * @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?: string; + + /** + * Certificate Manager certificate to associate with the load balancer. + * Setting this option will set the load balancer port to 443. + */ + certificate?: string; } /** - * An applet for a LoadBalancedFargateService + * An applet for a LoadBalancedFargateService. Sets up a Fargate service, Application + * load balancer, ECS cluster, VPC, and (optionally) Route53 alias record. */ export class LoadBalancedFargateServiceApplet extends cdk.Stack { constructor(parent: cdk.App, id: string, props: LoadBalancedFargateServiceAppletProps) { super(parent, id, props); - const vpc = new ec2.VpcNetwork(this, 'MyVpc', { maxAZs: 2 }); + const vpc = new VpcNetwork(this, 'MyVpc', { maxAZs: 2 }); const cluster = new Cluster(this, 'Cluster', { vpc }); + let domainZone; + if (props.domainZone) { + domainZone = new HostedZoneProvider(this, { domainName: props.domainZone }).findAndImport(this, 'Zone'); + } + let certificate; + if (props.certificate) { + certificate = CertificateRef.import(this, 'Cert', { certificateArn: props.certificate }); + } + // Instantiate Fargate Service with just cluster and image new LoadBalancedFargateService(this, "FargateService", { cluster, @@ -99,6 +127,9 @@ export class LoadBalancedFargateServiceApplet extends cdk.Stack { publicTasks: props.publicTasks, image: ContainerImage.fromDockerHub(props.image), desiredCount: props.desiredCount, + certificate, + domainName: props.domainName, + domainZone }); } } \ No newline at end of file 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 a2cfe02a583f7..7bed4822f8938 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 @@ -104,7 +104,7 @@ export interface LoadBalancedFargateServiceProps { } /** - * A single task running on an ECS cluster fronted by a load balancer + * A Fargate service running on an ECS cluster fronted by a load balancer */ export class LoadBalancedFargateService extends cdk.Construct { public readonly loadBalancer: elbv2.ApplicationLoadBalancer; diff --git a/packages/@aws-cdk/aws-ecs/test/test.l3s.ts b/packages/@aws-cdk/aws-ecs/test/test.l3s.ts index 048ef2593689d..b27c2c4877b4e 100644 --- a/packages/@aws-cdk/aws-ecs/test/test.l3s.ts +++ b/packages/@aws-cdk/aws-ecs/test/test.l3s.ts @@ -141,6 +141,57 @@ export = { LaunchType: "FARGATE", })); + test.done(); + }, + + 'test Fargateloadbalanced applet with TLS'(test: Test) { + // WHEN + const app = new cdk.App(); + const stack = new ecs.LoadBalancedFargateServiceApplet(app, 'Service', { + image: 'test', + desiredCount: 2, + domainName: 'api.example.com', + domainZone: 'example.com', + certificate: '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: 2, + LaunchType: "FARGATE", + })); + + expect(stack).to(haveResource('AWS::Route53::RecordSet', { + Name: 'api.example.com.', + HostedZoneId: "/hostedzone/DUMMY", + Type: 'A', + AliasTarget: { + HostedZoneId: { 'Fn::GetAtt': [ 'FargateServiceLBB353E155', 'CanonicalHostedZoneID' ] }, + DNSName: { 'Fn::GetAtt': [ 'FargateServiceLBB353E155', 'DNSName' ] }, + } + })); + + test.done(); + }, + + "errors when setting domainName but not domainZone on applet"(test: Test) { + // THEN + test.throws(() => { + new ecs.LoadBalancedFargateServiceApplet(new cdk.App(), 'Service', { + image: 'test', + domainName: 'api.example.com' + }); + }); + test.done(); } };