Skip to content

Commit dc7533c

Browse files
fix(ecs-patterns): feature flag missing for breaking change passing container port for target group port (#20284)
PR #18157 results in a new TargetGroup being created from NetworkLoadBalancedEc2Service, NetworkLoadBalancedFargateService, NetworkMultipleTargetGroupsEc2Service, and NetworkMultipleTargerGroupsFargateService even when no change is made because we are now passing through the containerPort props to TargetGroups's Port. For existing services, this is a breaking change so a feature flag is needed. This PR adds that feature flag. Closes #19411. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 1fcea37 commit dc7533c

File tree

4 files changed

+164
-12
lines changed

4 files changed

+164
-12
lines changed

Diff for: packages/@aws-cdk/aws-ecs-patterns/lib/base/network-load-balanced-service-base.ts

+6-5
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import { INetworkLoadBalancer, NetworkListener, NetworkLoadBalancer, NetworkTarg
77
import { IRole } from '@aws-cdk/aws-iam';
88
import { ARecord, CnameRecord, IHostedZone, RecordTarget } from '@aws-cdk/aws-route53';
99
import { LoadBalancerTarget } from '@aws-cdk/aws-route53-targets';
10-
import * as cdk from '@aws-cdk/core';
10+
import { CfnOutput, Duration, FeatureFlags, Stack } from '@aws-cdk/core';
11+
import { ECS_PATTERNS_TARGET_GROUP_PORT_FROM_CONTAINER_PORT } from '@aws-cdk/cx-api';
1112
import { Construct } from 'constructs';
1213

1314
// keep this import separate from other imports to reduce chance for merge conflicts with v2-main
@@ -103,7 +104,7 @@ export interface NetworkLoadBalancedServiceBaseProps {
103104
*
104105
* @default - defaults to 60 seconds if at least one load balancer is in-use and it is not already set
105106
*/
106-
readonly healthCheckGracePeriod?: cdk.Duration;
107+
readonly healthCheckGracePeriod?: Duration;
107108

108109
/**
109110
* The maximum number of tasks, specified as a percentage of the Amazon ECS
@@ -347,7 +348,7 @@ export abstract class NetworkLoadBalancedServiceBase extends CoreConstruct {
347348
const loadBalancer = props.loadBalancer ?? new NetworkLoadBalancer(this, 'LB', lbProps);
348349
const listenerPort = props.listenerPort ?? 80;
349350
const targetProps = {
350-
port: props.taskImageOptions?.containerPort ?? 80,
351+
port: FeatureFlags.of(this).isEnabled(ECS_PATTERNS_TARGET_GROUP_PORT_FROM_CONTAINER_PORT) ? props.taskImageOptions?.containerPort ?? 80 : 80,
351352
};
352353

353354
this.listener = loadBalancer.addListener('PublicListener', { port: listenerPort });
@@ -384,7 +385,7 @@ export abstract class NetworkLoadBalancedServiceBase extends CoreConstruct {
384385
}
385386

386387
if (props.loadBalancer === undefined) {
387-
new cdk.CfnOutput(this, 'LoadBalancerDNS', { value: this.loadBalancer.loadBalancerDnsName });
388+
new CfnOutput(this, 'LoadBalancerDNS', { value: this.loadBalancer.loadBalancerDnsName });
388389
}
389390
}
390391

@@ -394,7 +395,7 @@ export abstract class NetworkLoadBalancedServiceBase extends CoreConstruct {
394395
protected getDefaultCluster(scope: CoreConstruct, vpc?: IVpc): Cluster {
395396
// magic string to avoid collision with user-defined constructs
396397
const DEFAULT_CLUSTER_ID = `EcsDefaultClusterMnL3mNNYN${vpc ? vpc.node.id : ''}`;
397-
const stack = cdk.Stack.of(scope);
398+
const stack = Stack.of(scope);
398399
return stack.node.tryFindChild(DEFAULT_CLUSTER_ID) as Cluster || new Cluster(stack, DEFAULT_CLUSTER_ID, { vpc });
399400
}
400401

Diff for: packages/@aws-cdk/aws-ecs-patterns/lib/base/network-multiple-target-groups-service-base.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import { NetworkListener, NetworkLoadBalancer, NetworkTargetGroup } from '@aws-c
77
import { IRole } from '@aws-cdk/aws-iam';
88
import { ARecord, IHostedZone, RecordTarget } from '@aws-cdk/aws-route53';
99
import { LoadBalancerTarget } from '@aws-cdk/aws-route53-targets';
10-
import { CfnOutput, Duration, Stack } from '@aws-cdk/core';
10+
import { CfnOutput, Duration, FeatureFlags, Stack } from '@aws-cdk/core';
11+
import { ECS_PATTERNS_TARGET_GROUP_PORT_FROM_CONTAINER_PORT } from '@aws-cdk/cx-api';
1112
import { Construct } from 'constructs';
1213

1314
// v2 - keep this import as a separate section to reduce merge conflict when forward merging with the v2 branch.
@@ -374,7 +375,7 @@ export abstract class NetworkMultipleTargetGroupsServiceBase extends CoreConstru
374375
protected registerECSTargets(service: BaseService, container: ContainerDefinition, targets: NetworkTargetProps[]): NetworkTargetGroup {
375376
for (const targetProps of targets) {
376377
const targetGroup = this.findListener(targetProps.listener).addTargets(`ECSTargetGroup${container.containerName}${targetProps.containerPort}`, {
377-
port: targetProps.containerPort ?? 80,
378+
port: FeatureFlags.of(this).isEnabled(ECS_PATTERNS_TARGET_GROUP_PORT_FROM_CONTAINER_PORT) ? targetProps.containerPort ?? 80 : 80,
378379
targets: [
379380
service.loadBalancerTarget({
380381
containerName: container.containerName,

Diff for: packages/@aws-cdk/aws-ecs-patterns/test/fargate/load-balanced-fargate-service-v2.test.ts

+145-5
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ import { Vpc } from '@aws-cdk/aws-ec2';
33
import * as ecs from '@aws-cdk/aws-ecs';
44
import { ContainerImage } from '@aws-cdk/aws-ecs';
55
import { CompositePrincipal, Role, ServicePrincipal } from '@aws-cdk/aws-iam';
6-
import { Duration, Stack } from '@aws-cdk/core';
6+
import { testFutureBehavior, testLegacyBehavior } from '@aws-cdk/cdk-build-tools';
7+
import { App, Duration, Stack } from '@aws-cdk/core';
8+
import { ECS_PATTERNS_TARGET_GROUP_PORT_FROM_CONTAINER_PORT } from '@aws-cdk/cx-api';
79
import { ApplicationLoadBalancedFargateService, ApplicationMultipleTargetGroupsFargateService, NetworkLoadBalancedFargateService, NetworkMultipleTargetGroupsFargateService } from '../../lib';
810

911
describe('When Application Load Balancer', () => {
@@ -663,9 +665,36 @@ describe('When Network Load Balancer', () => {
663665
}).toThrow(/You must specify one of: taskDefinition or image/);
664666
});
665667

666-
test('test Fargate networkloadbalanced construct with custom Port', () => {
668+
testLegacyBehavior('Fargate neworkloadbalanced construct uses Port 80 for target group when feature flag is not enabled', App, (app) => {
667669
// GIVEN
668-
const stack = new Stack();
670+
const stack = new Stack(app);
671+
const vpc = new Vpc(stack, 'VPC');
672+
const cluster = new ecs.Cluster(stack, 'Cluster', { vpc });
673+
674+
new NetworkLoadBalancedFargateService(stack, 'NLBService', {
675+
cluster: cluster,
676+
memoryLimitMiB: 1024,
677+
cpu: 512,
678+
taskImageOptions: {
679+
image: ContainerImage.fromRegistry('amazon/amazon-ecs-sample'),
680+
containerPort: 81,
681+
},
682+
listenerPort: 8181,
683+
});
684+
685+
Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::TargetGroup', {
686+
Port: 80,
687+
Protocol: 'TCP',
688+
TargetType: 'ip',
689+
VpcId: {
690+
Ref: 'VPCB9E5F0B4',
691+
},
692+
});
693+
});
694+
695+
testFutureBehavior('Fargate networkloadbalanced construct uses custom Port for target group when feature flag is enabled', { [ECS_PATTERNS_TARGET_GROUP_PORT_FROM_CONTAINER_PORT]: true }, App, (app) => {
696+
// GIVEN
697+
const stack = new Stack(app);
669698
const vpc = new Vpc(stack, 'VPC');
670699
const cluster = new ecs.Cluster(stack, 'Cluster', { vpc });
671700

@@ -690,9 +719,79 @@ describe('When Network Load Balancer', () => {
690719
});
691720
});
692721

693-
test('test Fargate multinetworkloadbalanced construct with custom Port', () => {
722+
testFutureBehavior('Fargate networkloadbalanced construct uses 80 for target group when feature flag is enabled but container port is not provided', { [ECS_PATTERNS_TARGET_GROUP_PORT_FROM_CONTAINER_PORT]: true }, App, (app) => {
694723
// GIVEN
695-
const stack = new Stack();
724+
const stack = new Stack(app);
725+
const vpc = new Vpc(stack, 'VPC');
726+
const cluster = new ecs.Cluster(stack, 'Cluster', { vpc });
727+
728+
new NetworkLoadBalancedFargateService(stack, 'NLBService', {
729+
cluster: cluster,
730+
memoryLimitMiB: 1024,
731+
cpu: 512,
732+
taskImageOptions: {
733+
image: ContainerImage.fromRegistry('amazon/amazon-ecs-sample'),
734+
},
735+
listenerPort: 8181,
736+
});
737+
738+
Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::TargetGroup', {
739+
Port: 80,
740+
Protocol: 'TCP',
741+
TargetType: 'ip',
742+
VpcId: {
743+
Ref: 'VPCB9E5F0B4',
744+
},
745+
});
746+
});
747+
748+
testLegacyBehavior('Fargate multinetworkloadbalanced construct uses Port 80 for target group when feature flag is not enabled', App, (app) => {
749+
// GIVEN
750+
const stack = new Stack(app);
751+
const vpc = new Vpc(stack, 'VPC');
752+
const cluster = new ecs.Cluster(stack, 'Cluster', { vpc });
753+
754+
new NetworkMultipleTargetGroupsFargateService(stack, 'Service', {
755+
cluster,
756+
taskImageOptions: {
757+
image: ecs.ContainerImage.fromRegistry('test'),
758+
},
759+
});
760+
761+
762+
new NetworkMultipleTargetGroupsFargateService(stack, 'NLBService', {
763+
cluster: cluster,
764+
memoryLimitMiB: 1024,
765+
cpu: 512,
766+
taskImageOptions: {
767+
image: ContainerImage.fromRegistry('amazon/amazon-ecs-sample'),
768+
},
769+
loadBalancers: [
770+
{
771+
name: 'lb1',
772+
listeners: [
773+
{ name: 'listener1', port: 8181 },
774+
],
775+
},
776+
],
777+
targetGroups: [{
778+
containerPort: 81,
779+
}],
780+
});
781+
782+
Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::TargetGroup', {
783+
Port: 80,
784+
Protocol: 'TCP',
785+
TargetType: 'ip',
786+
VpcId: {
787+
Ref: 'VPCB9E5F0B4',
788+
},
789+
});
790+
});
791+
792+
testFutureBehavior('test Fargate multinetworkloadbalanced construct uses custom Port for target group when feature flag is enabled', { [ECS_PATTERNS_TARGET_GROUP_PORT_FROM_CONTAINER_PORT]: true }, App, (app) => {
793+
// GIVEN
794+
const stack = new Stack(app);
696795
const vpc = new Vpc(stack, 'VPC');
697796
const cluster = new ecs.Cluster(stack, 'Cluster', { vpc });
698797

@@ -733,4 +832,45 @@ describe('When Network Load Balancer', () => {
733832
},
734833
});
735834
});
835+
836+
testFutureBehavior('test Fargate multinetworkloadbalanced construct uses 80 for target group when feature flag is enabled but container port is not provided', { [ECS_PATTERNS_TARGET_GROUP_PORT_FROM_CONTAINER_PORT]: true }, App, (app) => {
837+
// GIVEN
838+
const stack = new Stack(app);
839+
const vpc = new Vpc(stack, 'VPC');
840+
const cluster = new ecs.Cluster(stack, 'Cluster', { vpc });
841+
842+
new NetworkMultipleTargetGroupsFargateService(stack, 'Service', {
843+
cluster,
844+
taskImageOptions: {
845+
image: ecs.ContainerImage.fromRegistry('test'),
846+
},
847+
});
848+
849+
850+
new NetworkMultipleTargetGroupsFargateService(stack, 'NLBService', {
851+
cluster: cluster,
852+
memoryLimitMiB: 1024,
853+
cpu: 512,
854+
taskImageOptions: {
855+
image: ContainerImage.fromRegistry('amazon/amazon-ecs-sample'),
856+
},
857+
loadBalancers: [
858+
{
859+
name: 'lb1',
860+
listeners: [
861+
{ name: 'listener1', port: 8181 },
862+
],
863+
},
864+
],
865+
});
866+
867+
Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::TargetGroup', {
868+
Port: 80,
869+
Protocol: 'TCP',
870+
TargetType: 'ip',
871+
VpcId: {
872+
Ref: 'VPCB9E5F0B4',
873+
},
874+
});
875+
});
736876
});

Diff for: packages/@aws-cdk/cx-api/lib/features.ts

+10
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,15 @@ export const IAM_MINIMIZE_POLICIES = '@aws-cdk/aws-iam:minimizePolicies';
245245
*/
246246
export const CODEPIPELINE_CROSS_ACCOUNT_KEY_ALIAS_STACK_SAFE_UNIQUE_ID = '@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeUniqueId';
247247

248+
/**
249+
* Enable this feature flag to pass through the `NetworkLoadBalanced<Ec2|Fargate>ServiceProps.taskImageOptions.containerPort`
250+
* and the `NetworkMultipleTargetGroups<Ec2|Fargate>ServiceProps.targetGroups[X].containerPort` to the generated
251+
* `ElasticLoadBalancingV2::TargetGroup`'s `Port` property.
252+
*
253+
* This is a feature flag because updating `Port` causes a replacement of the target groups, which is a breaking change.
254+
*/
255+
export const ECS_PATTERNS_TARGET_GROUP_PORT_FROM_CONTAINER_PORT = '@aws-cdk/aws-ecs-patterns:containerPortToTargetGroupPort';
256+
248257
/**
249258
* Flag values that should apply for new projects
250259
*
@@ -273,6 +282,7 @@ export const FUTURE_FLAGS: { [key: string]: boolean } = {
273282
[CHECK_SECRET_USAGE]: true,
274283
[IAM_MINIMIZE_POLICIES]: true,
275284
[CODEPIPELINE_CROSS_ACCOUNT_KEY_ALIAS_STACK_SAFE_UNIQUE_ID]: true,
285+
[ECS_PATTERNS_TARGET_GROUP_PORT_FROM_CONTAINER_PORT]: true,
276286
};
277287

278288
/**

0 commit comments

Comments
 (0)