Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ecs): add a new API for registering ECS targets #4212

Merged
merged 9 commits into from
Oct 4, 2019
23 changes: 23 additions & 0 deletions packages/@aws-cdk/aws-ecs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,29 @@ const target = listener.addTargets('ECS', {
});
```

Alternatively, you can also create all load balancer targets to be registered in this service, add them to target groups, and attach target groups to listeners accordingly.

```ts
import elbv2 = require('@aws-cdk/aws-elasticloadbalancingv2');

const service = new ecs.FargateService(this, 'Service', { /* ... */ });

const lb = new elbv2.ApplicationLoadBalancer(this, 'LB', { vpc, internetFacing: true });
const listener = lb.addListener('Listener', { port: 80 });
service.registerLoadBalancerTargets(
{
containerTarget: {
containerName: 'web',
containerPort: 80,
},
targetGroupId: 'ECS',
listener: ecs.ListenerConfig.applicationListener(listener, {
protocol: elbv2.ApplicationProtocol.HTTPS
}),
},
);
```

### Include a classic load balancer
`Services` can also be directly attached to a classic load balancer as targets:

Expand Down
138 changes: 138 additions & 0 deletions packages/@aws-cdk/aws-ecs/lib/base/base-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import cloudmap = require('@aws-cdk/aws-servicediscovery');
import { Construct, Duration, IResolvable, IResource, Lazy, Resource, Stack } from '@aws-cdk/core';
import { LoadBalancerTargetOptions, NetworkMode, TaskDefinition } from '../base/task-definition';
import { ICluster } from '../cluster';
import { Protocol } from '../container-definition';
import { CfnService } from '../ecs.generated';
import { ScalableTaskCount } from './scalable-task-count';

Expand All @@ -23,6 +24,37 @@ export interface IService extends IResource {
readonly serviceArn: string;
}

export interface EcsTarget {
/**
* The name of the container.
*/
readonly containerName: string;

/**
* The port number of the container. Only applicable when using application/network load balancers.
*
* @default - Container port of the first added port mapping.
*/
readonly containerPort?: number;

/**
* The protocol used for the port mapping. Only applicable when using application load balancers.
*
* @default Protocol.TCP
*/
readonly protocol?: Protocol;

/**
* ID for a target group to be created.
*/
readonly newTargetGroupId: string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we rename this to targetGroupId (new is relative)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ha, I just made him change it from targetGroupId -> newTargetGroupId.

The thing is, I'm not sure that targetGroupId conveys accurately enough that it will be created, rather than one that already exists that happens to have that ID.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough, just find newTargetGroup to be ambiguous.


/**
* Listener and properties for adding target group to the listener.
*/
readonly listener: ListenerConfig;
}

/**
* Interface for ECS load balancer target.
*/
Expand Down Expand Up @@ -116,6 +148,83 @@ export interface BaseServiceProps extends BaseServiceOptions {
readonly launchType: LaunchType;
}

/**
* Base class for configuring listener when registering targets.
*/
export abstract class ListenerConfig {
/**
* Create a config for adding target group to ALB listener.
*/
public static applicationListener(listener: elbv2.ApplicationListener, props?: elbv2.AddApplicationTargetsProps): ListenerConfig {
return new ApplicationListenerConfig(listener, props);
}

/**
* Create a config for adding target group to NLB listener.
*/
public static networkListener(listener: elbv2.NetworkListener, props?: elbv2.AddNetworkTargetsProps): ListenerConfig {
return new NetworkListenerConfig(listener, props);
}

/**
* Create and attach a target group to listener.
*/
public abstract addTargets(id: string, target: LoadBalancerTargetOptions, service: BaseService): void;
}

/**
* Class for configuring application load balancer listener when registering targets.
*/
class ApplicationListenerConfig extends ListenerConfig {
SoManyHs marked this conversation as resolved.
Show resolved Hide resolved
constructor(private readonly listener: elbv2.ApplicationListener, private readonly props?: elbv2.AddApplicationTargetsProps) {
super();
}

/**
* Create and attach a target group to listener.
*/
public addTargets(id: string, target: LoadBalancerTargetOptions, service: BaseService) {
const props = this.props || {};
const protocol = props.protocol;
const port = props.port !== undefined ? props.port : (protocol === undefined ? 80 :
(protocol === elbv2.ApplicationProtocol.HTTPS ? 443 : 80));
this.listener.addTargets(id, {
... props,
targets: [
service.loadBalancerTarget({
...target
})
],
port
});
}
}

/**
* Class for configuring network load balancer listener when registering targets.
*/
class NetworkListenerConfig extends ListenerConfig {
SoManyHs marked this conversation as resolved.
Show resolved Hide resolved
constructor(private readonly listener: elbv2.NetworkListener, private readonly props?: elbv2.AddNetworkTargetsProps) {
super();
}

/**
* Create and attach a target group to listener.
*/
public addTargets(id: string, target: LoadBalancerTargetOptions, service: BaseService) {
const port = this.props !== undefined ? this.props.port : 80;
this.listener.addTargets(id, {
... this.props,
targets: [
service.loadBalancerTarget({
...target
})
],
port
});
}
}

/**
* The base class for Ec2Service and FargateService services.
*/
Expand Down Expand Up @@ -283,6 +392,35 @@ export abstract class BaseService extends Resource
};
}

/**
* Use this function to create all load balancer targets to be registered in this service, add them to
* target groups, and attach target groups to listeners accordingly.
*
* @example
*
* service.registerLoadBalancerTargets(
* {
* containerTarget: {
* containerName: 'web',
* containerPort: 80,
* },
* targetGroupId: 'ECS',
* listener: ecs.ListenerConfig.applicationListener(listener, {
* protocol: elbv2.ApplicationProtocol.HTTPS
* }),
* },
* )
Comment on lines +399 to +412
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should add this to the README, not sure if it makes sense to add it here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have a precedent for having examples in jsdoc strings versus in README?

*/
public registerLoadBalancerTargets(...targets: EcsTarget[]) {
for (const target of targets) {
target.listener.addTargets(target.newTargetGroupId, {
containerName: target.containerName,
containerPort: target.containerPort,
protocol: target.protocol
}, this);
}
}

/**
* This method is called to attach this service to a Network Load Balancer.
*
Expand Down
Loading