Skip to content

Commit

Permalink
feat(aws-ecs): Support HTTPS in load balanced Fargate service (#1115)
Browse files Browse the repository at this point in the history
  • Loading branch information
clareliguori authored and rix0rrr committed Nov 14, 2018
1 parent fba780f commit 76a5cc7
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 2 deletions.
41 changes: 40 additions & 1 deletion packages/@aws-cdk/aws-ecs/lib/load-balanced-fargate-service.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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;
}

/**
Expand Down Expand Up @@ -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
});
}
}
}
4 changes: 3 additions & 1 deletion packages/@aws-cdk/aws-ecs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
"dependencies": {
"@aws-cdk/aws-applicationautoscaling": "^0.17.0",
"@aws-cdk/aws-autoscaling": "^0.17.0",
"@aws-cdk/aws-certificatemanager": "^0.17.0",
"@aws-cdk/aws-cloudformation": "^0.17.0",
"@aws-cdk/aws-cloudwatch": "^0.17.0",
"@aws-cdk/aws-ec2": "^0.17.0",
Expand All @@ -72,6 +73,7 @@
"@aws-cdk/aws-iam": "^0.17.0",
"@aws-cdk/aws-lambda": "^0.17.0",
"@aws-cdk/aws-logs": "^0.17.0",
"@aws-cdk/aws-route53": "^0.17.0",
"@aws-cdk/cdk": "^0.17.0",
"@aws-cdk/cx-api": "^0.17.0"
},
Expand All @@ -88,4 +90,4 @@
"@aws-cdk/aws-logs": "^0.17.0",
"@aws-cdk/cdk": "^0.17.0"
}
}
}
70 changes: 70 additions & 0 deletions packages/@aws-cdk/aws-ecs/test/test.l3s.ts
Original file line number Diff line number Diff line change
@@ -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');
Expand Down Expand Up @@ -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.ContainerImage.fromDockerHub('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.ContainerImage.fromDockerHub('test'),
domainName: 'api.example.com'
});
});

test.done();
},

Expand Down

0 comments on commit 76a5cc7

Please sign in to comment.