Skip to content

Commit 65e84c5

Browse files
committed
fix(toolkit-lib): transform ProxyConfigurationProperties to properties for ECS
CloudFormation uses ProxyConfigurationProperties but the ECS SDK expects properties. This renames the property before transformation to ensure hotswap deployments work correctly with App Mesh proxy configurations.
1 parent a9d5b84 commit 65e84c5

File tree

2 files changed

+105
-0
lines changed

2 files changed

+105
-0
lines changed

packages/@aws-cdk/toolkit-lib/lib/api/hotswap/ecs-services.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ export async function isHotswappableEcsServiceChange(
106106

107107
// The SDK requires more properties here than its worth doing explicit typing for
108108
// instead, just use all the old values in the diff to fill them in implicitly
109+
renameCfnPropertiesToSdkProperties(taskDefinitionResource);
109110
const lowercasedTaskDef = transformObjectKeys(taskDefinitionResource, lowerCaseFirstCharacter, {
110111
// All the properties that take arbitrary string as keys i.e. { "string" : "string" }
111112
// https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_RegisterTaskDefinition.html#API_RegisterTaskDefinition_RequestSyntax
@@ -169,6 +170,14 @@ interface EcsService {
169170
readonly serviceArn: string;
170171
}
171172

173+
function renameCfnPropertiesToSdkProperties(object: any): void {
174+
// CloudFormation uses ProxyConfigurationProperties but SDK expects properties
175+
if (object.ProxyConfiguration?.ProxyConfigurationProperties !== undefined) {
176+
object.ProxyConfiguration.properties = object.ProxyConfiguration.ProxyConfigurationProperties;
177+
delete object.ProxyConfiguration.ProxyConfigurationProperties;
178+
}
179+
}
180+
172181
async function prepareTaskDefinitionChange(
173182
evaluateCfnTemplate: EvaluateCloudFormationTemplate,
174183
logicalId: string,

packages/@aws-cdk/toolkit-lib/test/api/hotswap/ecs-services-hotswap-deployments.test.ts

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,102 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot
638638
forceNewDeployment: true,
639639
});
640640
});
641+
642+
test(
643+
'should correctly transform ProxyConfiguration.ProxyConfigurationProperties to proxyConfiguration.properties',
644+
async () => {
645+
// GIVEN
646+
setup.setCurrentCfnStackTemplate({
647+
Resources: {
648+
TaskDef: {
649+
Type: 'AWS::ECS::TaskDefinition',
650+
Properties: {
651+
Family: 'my-task-def',
652+
ContainerDefinitions: [{ Image: 'image1' }],
653+
ProxyConfiguration: {
654+
ContainerName: 'FargateApplication',
655+
ProxyConfigurationProperties: [
656+
{ Name: 'AppPorts', Value: '8080' },
657+
{ Name: 'IgnoredUID', Value: '1337' },
658+
],
659+
Type: 'APPMESH',
660+
},
661+
},
662+
},
663+
Service: {
664+
Type: 'AWS::ECS::Service',
665+
Properties: {
666+
TaskDefinition: { Ref: 'TaskDef' },
667+
},
668+
},
669+
},
670+
});
671+
setup.pushStackResourceSummaries(
672+
setup.stackSummaryOf(
673+
'Service',
674+
'AWS::ECS::Service',
675+
'arn:aws:ecs:region:account:service/my-cluster/my-service',
676+
),
677+
);
678+
mockECSClient.on(RegisterTaskDefinitionCommand).resolves({
679+
taskDefinition: {
680+
taskDefinitionArn: 'arn:aws:ecs:region:account:task-definition/my-task-def:3',
681+
},
682+
});
683+
const cdkStackArtifact = setup.cdkStackArtifactOf({
684+
template: {
685+
Resources: {
686+
TaskDef: {
687+
Type: 'AWS::ECS::TaskDefinition',
688+
Properties: {
689+
Family: 'my-task-def',
690+
ContainerDefinitions: [{ Image: 'image2' }],
691+
ProxyConfiguration: {
692+
ContainerName: 'FargateApplication',
693+
ProxyConfigurationProperties: [
694+
{ Name: 'AppPorts', Value: '8080' },
695+
{ Name: 'IgnoredUID', Value: '1337' },
696+
],
697+
Type: 'APPMESH',
698+
},
699+
},
700+
},
701+
Service: {
702+
Type: 'AWS::ECS::Service',
703+
Properties: {
704+
TaskDefinition: { Ref: 'TaskDef' },
705+
},
706+
},
707+
},
708+
},
709+
});
710+
// WHEN
711+
const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
712+
// THEN
713+
expect(deployStackResult).not.toBeUndefined();
714+
expect(mockECSClient).toHaveReceivedCommandWith(RegisterTaskDefinitionCommand, {
715+
family: 'my-task-def',
716+
containerDefinitions: [{ image: 'image2' }],
717+
proxyConfiguration: {
718+
containerName: 'FargateApplication',
719+
properties: [
720+
{ name: 'AppPorts', value: '8080' },
721+
{ name: 'IgnoredUID', value: '1337' },
722+
],
723+
type: 'APPMESH',
724+
},
725+
});
726+
expect(mockECSClient).toHaveReceivedCommandWith(UpdateServiceCommand, {
727+
service: 'arn:aws:ecs:region:account:service/my-cluster/my-service',
728+
cluster: 'my-cluster',
729+
taskDefinition: 'arn:aws:ecs:region:account:task-definition/my-task-def:3',
730+
deploymentConfiguration: {
731+
minimumHealthyPercent: 0,
732+
},
733+
forceNewDeployment: true,
734+
});
735+
},
736+
);
641737
});
642738

643739
describe.each([

0 commit comments

Comments
 (0)