Skip to content

Commit

Permalink
feat(elbv2): support UDP and TCP_UDP protocols (#4390)
Browse files Browse the repository at this point in the history
* feat(elbv2): support udp, tcp_udp protocols

* fix: revert NetworkProtocol, JSDoc Protocol, fix tests

* chore: revert test order

* fix: health check protocol validation, docs

* chore: stricter tests
  • Loading branch information
Jimmy Gaussen authored and mergify[bot] committed Oct 10, 2019
1 parent adbc2e3 commit 1958f26
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 19 deletions.
Expand Up @@ -5,7 +5,7 @@ import {
BaseTargetGroupProps, ITargetGroup, loadBalancerNameFromListenerArn, LoadBalancerTargetProps,
TargetGroupBase, TargetGroupImportProps
} from '../shared/base-target-group';
import { ApplicationProtocol, TargetType } from '../shared/enums';
import { ApplicationProtocol, Protocol, TargetType } from '../shared/enums';
import { ImportedTargetGroupBase } from '../shared/imported';
import { determineProtocolAndPort } from '../shared/util';
import { IApplicationListener } from './application-listener';
Expand Down Expand Up @@ -309,6 +309,13 @@ export class ApplicationTargetGroup extends TargetGroupBase implements IApplicat
ret.push(`At least one of 'port' or 'protocol' is required for a non-Lambda TargetGroup`);
}

if (this.healthCheck && this.healthCheck.protocol && !ALB_HEALTH_CHECK_PROTOCOLS.includes(this.healthCheck.protocol)) {
ret.push([
`Health check protocol '${this.healthCheck.protocol}' is not supported. `,
`Must be one of [${ALB_HEALTH_CHECK_PROTOCOLS.join(', ')}]`
].join(''));
}

return ret;
}
}
Expand Down Expand Up @@ -373,3 +380,5 @@ export interface IApplicationLoadBalancerTarget {
*/
attachToApplicationTargetGroup(targetGroup: IApplicationTargetGroup): LoadBalancerTargetProps;
}

const ALB_HEALTH_CHECK_PROTOCOLS = [Protocol.HTTP, Protocol.HTTPS];
Expand Up @@ -89,8 +89,8 @@ export class NetworkListener extends BaseListener implements INetworkListener {
const certs = props.certificates || [];
const proto = props.protocol || (certs.length > 0 ? Protocol.TLS : Protocol.TCP);

if ([Protocol.TCP, Protocol.TLS].indexOf(proto) === -1) {
throw new Error(`The protocol must be either ${Protocol.TCP} or ${Protocol.TLS}. Found ${props.protocol}`);
if (NLB_PROTOCOLS.indexOf(proto) === -1) {
throw new Error(`The protocol must be one of ${NLB_PROTOCOLS.join(', ')}. Found ${props.protocol}`);
}

if (proto === Protocol.TLS && certs.filter(v => v != null).length === 0) {
Expand Down Expand Up @@ -220,3 +220,5 @@ export interface AddNetworkTargetsProps {
*/
readonly healthCheck?: HealthCheck;
}

const NLB_PROTOCOLS = [Protocol.TCP, Protocol.TLS, Protocol.UDP, Protocol.TCP_UDP];
Expand Up @@ -106,8 +106,8 @@ export class NetworkTargetGroup extends TargetGroupBase implements INetworkTarge
if (healthCheck.path) {
ret.push('Health check paths are not supported for Network Load Balancer health checks');
}
if (healthCheck.protocol && healthCheck.protocol !== Protocol.TCP && healthCheck.protocol !== Protocol.TLS) {
ret.push(`Health check protocol '${healthCheck.protocol}' is not supported. Must be one of [TCP, TLS]`);
if (healthCheck.protocol && !NLB_HEALTH_CHECK_PROTOCOLS.includes(healthCheck.protocol)) {
ret.push(`Health check protocol '${healthCheck.protocol}' is not supported. Must be one of [${NLB_HEALTH_CHECK_PROTOCOLS.join(', ')}]`);
}
if (healthCheck.timeout) {
ret.push('Custom health check timeouts are not supported for Network Load Balancer health checks');
Expand Down Expand Up @@ -151,3 +151,5 @@ export interface INetworkLoadBalancerTarget {
*/
attachToNetworkTargetGroup(targetGroup: INetworkTargetGroup): LoadBalancerTargetProps;
}

const NLB_HEALTH_CHECK_PROTOCOLS = [Protocol.HTTP, Protocol.HTTPS, Protocol.TCP];
Expand Up @@ -84,8 +84,8 @@ export interface HealthCheck {
/**
* The protocol the load balancer uses when performing health checks on targets.
*
* The TCP protocol is supported only if the protocol of the target group
* is TCP.
* The TCP protocol is supported for health checks only if the protocol of the target group is TCP, TLS, UDP, or TCP_UDP.
* The TLS, UDP, and TCP_UDP protocols are not supported for health checks.
*
* @default HTTP for ALBs, TCP for NLBs
*/
Expand Down
22 changes: 16 additions & 6 deletions packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/enums.ts
Expand Up @@ -14,28 +14,38 @@ export enum IpAddressType {
}

/**
* Backend protocol for health checks
* Backend protocol for network load balancers and health checks
*/
export enum Protocol {
/**
* HTTP
* HTTP (ALB health checks and NLB health checks)
*/
HTTP = 'HTTP',

/**
* HTTPS
* HTTPS (ALB health checks and NLB health checks)
*/
HTTPS = 'HTTPS',

/**
* TCP
* TCP (NLB, NLB health checks)
*/
TCP = 'TCP',

/**
* TLS
* TLS (NLB)
*/
TLS = 'TLS',

/**
* UDP (NLB)
*/
UDP = 'UDP',

/**
* Listen to both TCP and UDP on the same port (NLB)
*/
TLS = 'TLS'
TCP_UDP = 'TCP_UDP',
}

/**
Expand Down
Expand Up @@ -369,6 +369,34 @@ export = {
test.done();
},

'validation error if invalid health check protocol'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
const vpc = new ec2.Vpc(stack, 'Stack');
const lb = new elbv2.ApplicationLoadBalancer(stack, 'LB', { vpc });
const listener = lb.addListener('Listener', { port: 80 });

// WHEN
const group = listener.addTargets('Group', {
port: 80,
targets: [new FakeSelfRegisteringTarget(stack, 'Target', vpc)]
});

group.configureHealthCheck({
unhealthyThresholdCount: 3,
timeout: cdk.Duration.hours(1),
interval: cdk.Duration.seconds(30),
path: '/test',
protocol: elbv2.Protocol.TCP
});

// THEN
const validationErrors: string[] = (group as any).validate();
test.deepEqual(validationErrors, ["Health check protocol 'TCP' is not supported. Must be one of [HTTP, HTTPS]"]);

test.done();
},

'Can call addTargetGroups on imported listener'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
Expand Down
Expand Up @@ -179,10 +179,10 @@ export = {
const lb = new elbv2.NetworkLoadBalancer(stack, 'LB', { vpc });

test.throws(() => lb.addListener('Listener', {
port: 443,
protocol: elbv2.Protocol.HTTP,
defaultTargetGroups: [new elbv2.NetworkTargetGroup(stack, 'Group', { vpc, port: 80 })]
}), Error, '/The protocol must be either TCP or TLS. Found HTTP/');
port: 443,
protocol: elbv2.Protocol.HTTP,
defaultTargetGroups: [new elbv2.NetworkTargetGroup(stack, 'Group', { vpc, port: 80 })]
}), /The protocol must be one of TCP, TLS, UDP, TCP_UDP\. Found HTTP/);

test.done();
},
Expand All @@ -206,6 +206,30 @@ export = {
test.done();
},

'validation error if invalid health check protocol'(test: Test) {
const stack = new cdk.Stack();
const vpc = new ec2.Vpc(stack, 'Stack');
const lb = new elbv2.NetworkLoadBalancer(stack, 'LB', { vpc });
const listener = lb.addListener('PublicListener', { port: 80 });
const targetGroup = listener.addTargets('ECS', {
port: 80,
healthCheck: {
interval: cdk.Duration.seconds(60)
}
});

targetGroup.configureHealthCheck({
interval: cdk.Duration.seconds(30),
protocol: elbv2.Protocol.UDP
});

// THEN
const validationErrors: string[] = (targetGroup as any).validate();
test.deepEqual(validationErrors, ["Health check protocol 'UDP' is not supported. Must be one of [HTTP, HTTPS, TCP]"]);

test.done();
},

'Protocol & certs TLS listener'(test: Test) {
const stack = new cdk.Stack();
const vpc = new ec2.Vpc(stack, 'Stack');
Expand All @@ -215,7 +239,7 @@ export = {
port: 443,
protocol: elbv2.Protocol.TLS,
defaultTargetGroups: [new elbv2.NetworkTargetGroup(stack, 'Group', { vpc, port: 80 })]
}), Error, '/When the protocol is set to TLS, you must specify certificates/');
}), /When the protocol is set to TLS, you must specify certificates/);

test.done();
},
Expand All @@ -233,7 +257,7 @@ export = {
protocol: elbv2.Protocol.TCP,
certificates: [ { certificateArn: cert.certificateArn } ],
defaultTargetGroups: [new elbv2.NetworkTargetGroup(stack, 'Group', { vpc, port: 80 })]
}), Error, '/Protocol must be TLS when certificates have been specified/');
}), /Protocol must be TLS when certificates have been specified/);

test.done();
},
Expand Down

0 comments on commit 1958f26

Please sign in to comment.