Skip to content

Commit 5a92637

Browse files
committed
Infer the ARN from the parts of the invoked lambda function arn
1 parent 12ce408 commit 5a92637

4 files changed

Lines changed: 105 additions & 22 deletions

File tree

packages/cdk-blue-green-container-deployment/src/__tests__/lambdas/__fixtures__/defaultContext.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ export const defaultContext = {
22
callbackWaitsForEmptyEventLoop: true,
33
functionName: 'foo',
44
functionVersion: 'foo',
5-
invokedFunctionArn: 'foo',
5+
invokedFunctionArn: 'arn:aws:lambda:eu-west-1:012345678910:function:MyCustomResourceHandler',
66
memoryLimitInMB: 'foo',
77
awsRequestId: 'foo',
88
logGroupName: 'foo',

packages/cdk-blue-green-container-deployment/src/__tests__/lambdas/ecs-deployment-group/index.test.ts

Lines changed: 68 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ import { defaultEvent } from '../__fixtures__/defaultEvent';
4040
import { defaultLogger } from '../__fixtures__/defaultLogger';
4141

4242
const defaultEcsDeploymentGroupProperties = {
43-
ApplicationName: 'Foo',
44-
DeploymentGroupName: 'Foo',
43+
ApplicationName: 'TestApplicationName',
44+
DeploymentGroupName: 'TestDeploymentGroupName',
4545
ServiceRoleArn: 'arn:aws:iam::012345678910:role/MyRole',
4646
EcsServices: [
4747
{
@@ -52,8 +52,7 @@ const defaultEcsDeploymentGroupProperties = {
5252
TargetGroupNames: ['Foo'],
5353
ProdTrafficListenerArn: 'arn:aws:elasticloadbalancing::012345678910:listener/app/MyApp/foo/prod',
5454
TestTrafficListenerArn: 'arn:aws:elasticloadbalancing::012345678910:listener/app/MyApp/foo/test',
55-
TerminationWaitTimeInMinutes: 5,
56-
ArnForDeploymentGroup: 'arn:aws:ecs:us-east-1:012345678910:service/MyCluster/MyService',
55+
TerminationWaitTimeInMinutes: 5
5756
};
5857

5958
describe('createHandler', () => {
@@ -84,10 +83,41 @@ describe('createHandler', () => {
8483
}),
8584
);
8685
});
86+
87+
it('returns the physical id and arn of the deployment group', async () => {
88+
const response = await handleCreate(
89+
{
90+
...defaultEvent,
91+
RequestType: 'Create',
92+
ResourceProperties: {
93+
ServiceToken: 'foo',
94+
...defaultEcsDeploymentGroupProperties,
95+
Tags: [
96+
{ Key: 'foo', Value: 'bar' },
97+
{ Key: 'k', Value: 'west' },
98+
],
99+
},
100+
},
101+
{
102+
...defaultContext,
103+
invokedFunctionArn: 'arn:aws:lambda:eu-west-1:012345678910:function:MyCustomResourceHandler'
104+
},
105+
defaultLogger,
106+
);
107+
108+
expect(response).toEqual(
109+
expect.objectContaining({
110+
physicalResourceId: 'TestDeploymentGroupName',
111+
responseData: {
112+
Arn: 'arn:aws:codedeploy:eu-west-1:012345678910:deploymentgroup:TestApplicationName/TestDeploymentGroupName'
113+
},
114+
}),
115+
);
116+
})
87117
});
88118

89119
describe('updateHandler', () => {
90-
it('sends data update request', async () => {
120+
it('sends data update requests', async () => {
91121
await handleUpdate(
92122
{
93123
...defaultEvent,
@@ -119,13 +149,13 @@ describe('updateHandler', () => {
119149
expect(mockUpdateRequest).toHaveBeenCalled();
120150

121151
expect(mockUntagResourceRequest).toHaveBeenCalledWith({
122-
ResourceArn: 'arn:aws:ecs:us-east-1:012345678910:service/MyCluster/MyService',
152+
ResourceArn: 'arn:aws:codedeploy:eu-west-1:012345678910:deploymentgroup:TestApplicationName/TestDeploymentGroupName',
123153
TagKeys: ['foo'],
124154
});
125155

126156
expect(mockTagResourceRequest).toHaveBeenCalledWith(
127157
expect.objectContaining({
128-
ResourceArn: 'arn:aws:ecs:us-east-1:012345678910:service/MyCluster/MyService',
158+
ResourceArn: 'arn:aws:codedeploy:eu-west-1:012345678910:deploymentgroup:TestApplicationName/TestDeploymentGroupName',
129159
Tags: [
130160
{ Key: 'dis', Value: 'dat' },
131161
{ Key: 'k', Value: 'west' },
@@ -134,4 +164,35 @@ describe('updateHandler', () => {
134164
}),
135165
);
136166
});
167+
168+
it('returns the physical id and arn of the deployment group', async () => {
169+
const response = await handleUpdate(
170+
{
171+
...defaultEvent,
172+
RequestType: 'Update',
173+
PhysicalResourceId: 'foo',
174+
ResourceProperties: {
175+
...defaultEcsDeploymentGroupProperties,
176+
ServiceToken: 'foo',
177+
},
178+
OldResourceProperties: {
179+
...defaultEcsDeploymentGroupProperties,
180+
},
181+
},
182+
{
183+
...defaultContext,
184+
invokedFunctionArn: 'arn:aws:lambda:us-east-1:012345678910:function:MyCustomResourceHandler',
185+
},
186+
defaultLogger,
187+
);
188+
189+
expect(response).toEqual(
190+
expect.objectContaining({
191+
physicalResourceId: 'TestDeploymentGroupName',
192+
responseData: {
193+
Arn: 'arn:aws:codedeploy:us-east-1:012345678910:deploymentgroup:TestApplicationName/TestDeploymentGroupName'
194+
},
195+
}),
196+
);
197+
})
137198
});

packages/cdk-blue-green-container-deployment/src/ecs-deployment-group.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -146,10 +146,7 @@ export class EcsDeploymentGroup extends Resource implements IEcsDeploymentGroup,
146146
}
147147
this.node.addDependency(...ecsServices);
148148

149-
this.deploymentGroupName = deploymentGroupName;
150-
this.deploymentGroupArn = this.arnForDeploymentGroup(this.application.applicationName, deploymentGroupName);
151-
152-
new CustomResource(this, 'CustomResource', {
149+
const ecsDeploymentGroup = new CustomResource(this, 'CustomResource', {
153150
serviceToken: serviceToken.functionArn,
154151
resourceType: 'Custom::EcsDeploymentGroup',
155152
properties: {
@@ -167,13 +164,11 @@ export class EcsDeploymentGroup extends Resource implements IEcsDeploymentGroup,
167164
AutoRollbackOnEvents: autoRollbackOnEvents,
168165
DeploymentConfigName: this.deploymentConfig.deploymentConfigName,
169166
Tags: Lazy.any({ produce: () => this.tags.renderTags() }),
170-
ArnForDeploymentGroup: this.arnForDeploymentGroup(this.application.applicationName, deploymentGroupName),
171167
},
172168
});
173-
}
174169

175-
private arnForDeploymentGroup(applicationName: string, deploymentGroupName: string): string {
176-
return `arn:${Aws.PARTITION}:codedeploy:${Aws.REGION}:${Aws.ACCOUNT_ID}:deploymentgroup:${applicationName}/${deploymentGroupName}`;
170+
this.deploymentGroupName = ecsDeploymentGroup.ref;
171+
this.deploymentGroupArn = ecsDeploymentGroup.getAttString('Arn');
177172
}
178173
}
179174

packages/cdk-blue-green-container-deployment/src/lambdas/ecs-deployment-group/index.ts

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,17 @@ export interface EcsDeploymentGroupProps {
2424
prodTrafficListenerArn: string;
2525
testTrafficListenerArn: string;
2626
terminationWaitTimeInMinutes: number;
27-
arnForDeploymentGroup: string;
2827
tags: CodeDeploy.Tag[];
2928
autoRollbackOnEvents?: RollbackEvent[];
3029
deploymentConfigName?: string;
3130
}
3231

32+
interface ArnParts {
33+
awsPartition: string;
34+
awsRegion: string;
35+
awsAccountId: string;
36+
}
37+
3338
const codeDeploy = new CodeDeploy();
3439

3540
const getProperties = (
@@ -48,11 +53,10 @@ const getProperties = (
4853
terminationWaitTimeInMinutes: props.TerminationWaitTimeInMinutes,
4954
autoRollbackOnEvents: props.AutoRollbackOnEvents,
5055
deploymentConfigName: props.DeploymentConfigName,
51-
arnForDeploymentGroup: props.ArnForDeploymentGroup,
5256
tags: props.Tags ?? [],
5357
});
5458

55-
export const handleCreate: OnCreateHandler = async (event): Promise<ResourceHandlerReturn> => {
59+
export const handleCreate: OnCreateHandler = async (event, context): Promise<ResourceHandlerReturn> => {
5660
const {
5761
applicationName,
5862
deploymentGroupName,
@@ -112,12 +116,16 @@ export const handleCreate: OnCreateHandler = async (event): Promise<ResourceHand
112116

113117
return {
114118
physicalResourceId: deploymentGroupName,
119+
responseData: {
120+
Arn: arnForDeploymentGroup(applicationName, deploymentGroupName, extractArnParts(context.invokedFunctionArn))
121+
},
115122
};
116123
};
117124

118-
export const handleUpdate: OnUpdateHandler = async (event): Promise<ResourceHandlerReturn> => {
125+
export const handleUpdate: OnUpdateHandler = async (event, context): Promise<ResourceHandlerReturn> => {
119126
const newProps = getProperties(event.ResourceProperties);
120127
const oldProps = getProperties(event.OldResourceProperties);
128+
const deploymentGroupArn = arnForDeploymentGroup(newProps.applicationName, newProps.deploymentGroupName, extractArnParts(context.invokedFunctionArn));
121129

122130
await codeDeploy
123131
.updateDeploymentGroup({
@@ -163,7 +171,7 @@ export const handleUpdate: OnUpdateHandler = async (event): Promise<ResourceHand
163171
if (removableTagKeys.length > 0) {
164172
await codeDeploy
165173
.untagResource({
166-
ResourceArn: newProps.arnForDeploymentGroup,
174+
ResourceArn: deploymentGroupArn,
167175
TagKeys: removableTagKeys,
168176
})
169177
.promise();
@@ -172,14 +180,17 @@ export const handleUpdate: OnUpdateHandler = async (event): Promise<ResourceHand
172180
if (newProps.tags.length > 0) {
173181
await codeDeploy
174182
.tagResource({
175-
ResourceArn: newProps.arnForDeploymentGroup,
183+
ResourceArn: deploymentGroupArn,
176184
Tags: newProps.tags,
177185
})
178186
.promise();
179187
}
180188

181189
return {
182190
physicalResourceId: newProps.deploymentGroupName,
191+
responseData: {
192+
Arn: deploymentGroupArn
193+
},
183194
};
184195
};
185196

@@ -194,6 +205,22 @@ const handleDelete: OnDeleteHandler = async (event): Promise<void> => {
194205
.promise();
195206
};
196207

208+
const extractArnParts = (invokedFunctionArn: string): ArnParts => {
209+
const matcher = invokedFunctionArn.match(/arn:(?<partition>\w+):lambda:(?<region>[\w\-]+):(?<accountId>\d+):.*/);
210+
if(!matcher || !matcher.groups || !matcher.groups.partition || !matcher.groups.region || !matcher.groups.accountId) {
211+
throw new Error(`Unable to extract necessary parts from function name. (${invokedFunctionArn}).`)
212+
}
213+
return {
214+
awsPartition: matcher.groups.partition,
215+
awsRegion: matcher.groups.region,
216+
awsAccountId: matcher.groups.accountId
217+
}
218+
}
219+
220+
const arnForDeploymentGroup = (applicationName: string, deploymentGroupName: string, arnParts: ArnParts): string => {
221+
return `arn:${arnParts.awsPartition}:codedeploy:${arnParts.awsRegion}:${arnParts.awsAccountId}:deploymentgroup:${applicationName}/${deploymentGroupName}`
222+
}
223+
197224
export const handler = customResourceHelper(
198225
(): ResourceHandler => ({
199226
onCreate: handleCreate,

0 commit comments

Comments
 (0)