Skip to content

Commit

Permalink
fix(ecs): Can't enable both Fargate and ASG capacity providers on ECS…
Browse files Browse the repository at this point in the history
… Cluster (#15012)

Previously, adding/enabling Fargate capacity providers would add the FARGATE and FARGATE_SPOT capacity providers a part of the [capacityProviders field](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecs-cluster.html#cfn-ecs-cluster-capacityproviders) on the ECS::Cluster resource. However, ASG Capacity Providers can only be specified as part of the [capacityProviders field on the ECS::ClusterCapacityProviderAssociation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecs-clustercapacityproviderassociations.html#cfn-ecs-clustercapacityproviderassociations-capacityproviders)
resource, due to circular dependency issues.

However, when a cluster is created, any capacity providers regardless of type must all be specified *either* on the cluster *or* as a ClusterCapacityProviderAssociation.  Therefore, in order to support both types on the same cluster, Fargate capacity providers must also be specified as a ClusterCapacityProviderAssociation.

This change removes the Fargate capacity providers from the Cluster resource and adds them as a ClusterCapacityProviderAssociation.

Fixes #14730.

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
SoManyHs authored Jun 9, 2021
1 parent 8625510 commit 6b2d0e0
Show file tree
Hide file tree
Showing 6 changed files with 349 additions and 28 deletions.
40 changes: 18 additions & 22 deletions packages/@aws-cdk/aws-ecs/lib/cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,14 +126,9 @@ export class Cluster extends Resource implements ICluster {
public readonly clusterName: string;

/**
* The cluster-level (FARGATE, FARGATE_SPOT) capacity providers.
* The names of both ASG and Fargate capacity providers associated with the cluster.
*/
private _fargateCapacityProviders: string[] = [];

/**
* The EC2 Auto Scaling Group capacity providers associated with the cluster.
*/
private _asgCapacityProviders: AsgCapacityProvider[] = [];
private _capacityProviderNames: string[] = [];

/**
* The AWS Cloud Map namespace to associate with the cluster.
Expand Down Expand Up @@ -173,7 +168,7 @@ export class Cluster extends Resource implements ICluster {
clusterSettings = [{ name: 'containerInsights', value: props.containerInsights ? ContainerInsights.ENABLED : ContainerInsights.DISABLED }];
}

this._fargateCapacityProviders = props.capacityProviders ?? [];
this._capacityProviderNames = props.capacityProviders ?? [];
if (props.enableFargateCapacityProviders) {
this.enableFargateCapacityProviders();
}
Expand All @@ -189,7 +184,6 @@ export class Cluster extends Resource implements ICluster {
const cluster = new CfnCluster(this, 'Resource', {
clusterName: this.physicalName,
clusterSettings,
capacityProviders: Lazy.list({ produce: () => this._fargateCapacityProviders }, { omitEmpty: true }),
configuration: this._executeCommandConfiguration && this.renderExecuteCommandConfiguration(),
});

Expand All @@ -216,16 +210,16 @@ export class Cluster extends Resource implements ICluster {
// since it's harmless, but we'd prefer not to add unexpected new
// resources to the stack which could surprise users working with
// brown-field CDK apps and stacks.
Aspects.of(this).add(new MaybeCreateCapacityProviderAssociations(this, id, this._asgCapacityProviders));
Aspects.of(this).add(new MaybeCreateCapacityProviderAssociations(this, id, this._capacityProviderNames));
}

/**
* Enable the Fargate capacity providers for this cluster.
*/
public enableFargateCapacityProviders() {
for (const provider of ['FARGATE', 'FARGATE_SPOT']) {
if (!this._fargateCapacityProviders.includes(provider)) {
this._fargateCapacityProviders.push(provider);
if (!this._capacityProviderNames.includes(provider)) {
this._capacityProviderNames.push(provider);
}
}
}
Expand Down Expand Up @@ -329,7 +323,7 @@ export class Cluster extends Resource implements ICluster {
*/
public addAsgCapacityProvider(provider: AsgCapacityProvider, options: AddAutoScalingGroupCapacityOptions = {}) {
// Don't add the same capacity provider more than once.
if (this._asgCapacityProviders.includes(provider)) {
if (this._capacityProviderNames.includes(provider.capacityProviderName)) {
return;
}

Expand All @@ -340,7 +334,7 @@ export class Cluster extends Resource implements ICluster {
taskDrainTime: provider.enableManagedTerminationProtection ? Duration.seconds(0) : options.taskDrainTime,
});

this._asgCapacityProviders.push(provider);
this._capacityProviderNames.push(provider.capacityProviderName);
}

/**
Expand Down Expand Up @@ -462,8 +456,8 @@ export class Cluster extends Resource implements ICluster {
throw new Error('CapacityProvider not supported');
}

if (!this._fargateCapacityProviders.includes(provider)) {
this._fargateCapacityProviders.push(provider);
if (!this._capacityProviderNames.includes(provider)) {
this._capacityProviderNames.push(provider);
}
}

Expand Down Expand Up @@ -1340,22 +1334,24 @@ export class AsgCapacityProvider extends CoreConstruct {
class MaybeCreateCapacityProviderAssociations implements IAspect {
private scope: CoreConstruct;
private id: string;
private capacityProviders: AsgCapacityProvider[]
private capacityProviders: string[]
private resource?: CfnClusterCapacityProviderAssociations

constructor(scope: CoreConstruct, id: string, capacityProviders: AsgCapacityProvider[]) {
constructor(scope: CoreConstruct, id: string, capacityProviders: string[] ) {
this.scope = scope;
this.id = id;
this.capacityProviders = capacityProviders;
}

public visit(node: IConstruct): void {
if (node instanceof Cluster) {
const providers = this.capacityProviders.map(p => p.capacityProviderName).filter(p => p !== 'FARGATE' && p !== 'FARGATE_SPOT');
if (providers.length > 0) {
new CfnClusterCapacityProviderAssociations(this.scope, this.id, {
if (this.capacityProviders.length > 0 && !this.resource) {
const resource = new CfnClusterCapacityProviderAssociations(this.scope, this.id, {
cluster: node.clusterName,
defaultCapacityProviderStrategy: [],
capacityProviders: Lazy.list({ produce: () => providers }),
capacityProviders: Lazy.list({ produce: () => this.capacityProviders }),
});
this.resource = resource;
}
}
}
Expand Down
23 changes: 22 additions & 1 deletion packages/@aws-cdk/aws-ecs/test/cluster.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1709,6 +1709,10 @@ nodeunitShim({

// THEN
expect(stack).to(haveResource('AWS::ECS::Cluster', {
CapacityProviders: ABSENT,
}));

expect(stack).to(haveResource('AWS::ECS::ClusterCapacityProviderAssociations', {
CapacityProviders: ['FARGATE_SPOT'],
}));

Expand All @@ -1727,6 +1731,10 @@ nodeunitShim({

// THEN
expect(stack).to(haveResource('AWS::ECS::Cluster', {
CapacityProviders: ABSENT,
}));

expect(stack).to(haveResource('AWS::ECS::ClusterCapacityProviderAssociations', {
CapacityProviders: ['FARGATE', 'FARGATE_SPOT'],
}));

Expand All @@ -1744,6 +1752,10 @@ nodeunitShim({

// THEN
expect(stack).to(haveResource('AWS::ECS::Cluster', {
CapacityProviders: ABSENT,
}));

expect(stack).to(haveResource('AWS::ECS::ClusterCapacityProviderAssociations', {
CapacityProviders: ['FARGATE', 'FARGATE_SPOT'],
}));

Expand All @@ -1762,6 +1774,10 @@ nodeunitShim({

// THEN
expect(stack).to(haveResource('AWS::ECS::Cluster', {
CapacityProviders: ABSENT,
}));

expect(stack).to(haveResource('AWS::ECS::ClusterCapacityProviderAssociations', {
CapacityProviders: ['FARGATE'],
}));

Expand All @@ -1780,6 +1796,10 @@ nodeunitShim({

// THEN
expect(stack).to(haveResource('AWS::ECS::Cluster', {
CapacityProviders: ABSENT,
}));

expect(stack).to(haveResource('AWS::ECS::ClusterCapacityProviderAssociations', {
CapacityProviders: ['FARGATE'],
}));

Expand Down Expand Up @@ -1928,7 +1948,6 @@ nodeunitShim({
enableManagedTerminationProtection: false,
});

// These should not be added at the association level
cluster.enableFargateCapacityProviders();

// Ensure not added twice
Expand All @@ -1941,6 +1960,8 @@ nodeunitShim({
Ref: 'EcsCluster97242B84',
},
CapacityProviders: [
'FARGATE',
'FARGATE_SPOT',
{
Ref: 'providerD3FF4D3A',
},
Expand Down
Loading

0 comments on commit 6b2d0e0

Please sign in to comment.