Skip to content

Commit

Permalink
feat(ecs): make cluster and vpc optional for higher level constructs (#…
Browse files Browse the repository at this point in the history
…2773)

* feat(ecs): make cluster and vpc optional for higher level constructs

* chore(ecs): review feedback

* chore(ecs): improve commenst after review

* fix(ecs-patterns): refactor tests after merge

* fix(ecs-patterns): whitespace

* fix(ecs-patterns): memory in MB

* fix(ecs): merge conflicts

* feat(ecs-patterns): use default cluster per stack

* fix(ecs-patterns): tests

* feat(ecs-patterns): add magic id

* chore: PR review

* chore: add README
  • Loading branch information
hoegertn authored and Elad Ben-Israel committed Jul 29, 2019
1 parent bc233fa commit 979f6fd
Show file tree
Hide file tree
Showing 13 changed files with 3,056 additions and 12 deletions.
5 changes: 5 additions & 0 deletions packages/@aws-cdk/aws-ecs-patterns/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ const loadBalancedFargateService = new ecsPatterns.LoadBalancedFargateService(st
});
```

Instead of providing a cluster you can specify a VPC and CDK will create a new ECS cluster.
If you deploy multiple services CDK will only create on cluster per VPC.

You can omit `cluster` and `vpc` to let CDK create a new VPC with two AZs and create a cluster inside this VPC.

## Queue Processing Services

To define a service that creates a queue and reads from that queue, instantiate one of the following:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ICertificate } from '@aws-cdk/aws-certificatemanager';
import ec2 = require('@aws-cdk/aws-ec2');
import ecs = require('@aws-cdk/aws-ecs');
import elbv2 = require('@aws-cdk/aws-elasticloadbalancingv2');
import { AddressRecordTarget, ARecord, IHostedZone } from '@aws-cdk/aws-route53';
Expand All @@ -16,8 +17,19 @@ export enum LoadBalancerType {
export interface LoadBalancedServiceBaseProps {
/**
* The cluster where your service will be deployed
* You can only specify either vpc or cluster. Alternatively, you can leave both blank
*
* @default - create a new cluster; if you do not specify a cluster nor a vpc, a new VPC will be created for you as well
*/
readonly cluster?: ecs.ICluster;

/**
* VPC that the cluster instances or tasks are running in
* You can only specify either vpc or cluster. Alternatively, you can leave both blank
*
* @default - use vpc of cluster or create a new one
*/
readonly cluster: ecs.ICluster;
readonly vpc?: ec2.IVpc;

/**
* The image to start.
Expand Down Expand Up @@ -115,11 +127,18 @@ export abstract class LoadBalancedServiceBase extends cdk.Construct {

public readonly targetGroup: elbv2.ApplicationTargetGroup | elbv2.NetworkTargetGroup;

public readonly cluster: ecs.ICluster;

public readonly logDriver?: ecs.LogDriver;

constructor(scope: cdk.Construct, id: string, props: LoadBalancedServiceBaseProps) {
super(scope, id);

if (props.cluster && props.vpc) {
throw new Error(`You can only specify either vpc or cluster. Alternatively, you can leave both blank`);
}
this.cluster = props.cluster || this.getDefaultCluster(this, props.vpc);

// Create log driver if logging is enabled
const enableLogging = props.enableLogging !== undefined ? props.enableLogging : true;
this.logDriver = enableLogging ? this.createAWSLogDriver(this.node.id) : undefined;
Expand All @@ -134,7 +153,7 @@ export abstract class LoadBalancedServiceBase extends cdk.Construct {
const internetFacing = props.publicLoadBalancer !== undefined ? props.publicLoadBalancer : true;

const lbProps = {
vpc: props.cluster.vpc,
vpc: this.cluster.vpc,
internetFacing
};

Expand Down Expand Up @@ -183,6 +202,13 @@ export abstract class LoadBalancedServiceBase extends cdk.Construct {
new cdk.CfnOutput(this, 'LoadBalancerDNS', { value: this.loadBalancer.loadBalancerDnsName });
}

protected getDefaultCluster(scope: cdk.Construct, vpc?: ec2.IVpc): ecs.Cluster {
// magic string to avoid collision with user-defined constructs
const DEFAULT_CLUSTER_ID = `EcsDefaultClusterMnL3mNNYN${vpc ? vpc.node.id : ''}`;
const stack = cdk.Stack.of(scope);
return stack.node.tryFindChild(DEFAULT_CLUSTER_ID) as ecs.Cluster || new ecs.Cluster(stack, DEFAULT_CLUSTER_ID, { vpc });
}

protected addServiceAsTarget(service: ecs.BaseService) {
if (this.loadBalancerType === LoadBalancerType.APPLICATION) {
(this.targetGroup as elbv2.ApplicationTargetGroup).addTarget(service);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export class LoadBalancedEc2Service extends LoadBalancedServiceBase {

const assignPublicIp = props.publicTasks !== undefined ? props.publicTasks : false;
const service = new ecs.Ec2Service(this, "Service", {
cluster: props.cluster,
cluster: this.cluster,
desiredCount: props.desiredCount || 1,
taskDefinition,
assignPublicIp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export class LoadBalancedFargateService extends LoadBalancedServiceBase {
});
const assignPublicIp = props.publicTasks !== undefined ? props.publicTasks : false;
const service = new ecs.FargateService(this, "Service", {
cluster: props.cluster,
cluster: this.cluster,
desiredCount: props.desiredCount || 1,
taskDefinition,
assignPublicIp,
Expand Down
42 changes: 42 additions & 0 deletions packages/@aws-cdk/aws-ecs-patterns/test/ec2/test.l3s.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,48 @@ export = {
test.done();
},

'set vpc instead of cluster'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
const vpc = new ec2.Vpc(stack, 'VPC');

// WHEN
new ecsPatterns.LoadBalancedEc2Service(stack, 'Service', {
vpc,
memoryLimitMiB: 1024,
image: ecs.ContainerImage.fromRegistry('test'),
desiredCount: 2,
environment: {
TEST_ENVIRONMENT_VARIABLE1: "test environment variable 1 value",
TEST_ENVIRONMENT_VARIABLE2: "test environment variable 2 value"
}
});

// THEN - stack does not contain a LaunchConfiguration
expect(stack, true).notTo(haveResource("AWS::AutoScaling::LaunchConfiguration"));

test.throws(() => expect(stack));

test.done();
},

'setting vpc and cluster throws error'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
const vpc = new ec2.Vpc(stack, 'VPC');
const cluster = new ecs.Cluster(stack, 'Cluster', { vpc });

// WHEN
test.throws(() => new ecsPatterns.LoadBalancedEc2Service(stack, 'Service', {
cluster,
vpc,
loadBalancerType: ecsPatterns.LoadBalancerType.NETWORK,
image: ecs.ContainerImage.fromRegistry("/aws/aws-example-app")
}));

test.done();
},

'test ECS loadbalanced construct with memoryReservationMiB'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
Expand Down
Loading

0 comments on commit 979f6fd

Please sign in to comment.