Skip to content

Commit 74037c3

Browse files
committed
Fix service tagging
1 parent ff9f21e commit 74037c3

4 files changed

Lines changed: 128 additions & 22 deletions

File tree

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
[{"timestamp":1637260639127,"files":[{"filename":"ecs-deployment-group/index.js","previous":145091,"size":144953,"diff":-138},{"filename":"ecs-service/index.js","previous":144989,"size":144959,"diff":-30}]},{"timestamp":1637252676806,"files":[{"filename":"ecs-deployment-group/index.js","previous":144919,"size":145091,"diff":172},{"filename":"ecs-service/index.js","previous":144989,"size":144989,"diff":0}]},{"timestamp":1637251737832,"files":[{"filename":"ecs-deployment-group/index.js","previous":144885,"size":144919,"diff":34},{"filename":"ecs-service/index.js","previous":144989,"size":144989,"diff":0}]},{"timestamp":1637251652938,"files":[{"filename":"ecs-deployment-group/index.js","previous":144825,"size":144885,"diff":60},{"filename":"ecs-service/index.js","previous":144989,"size":144989,"diff":0}]},{"timestamp":1637188077152,"files":[{"filename":"ecs-deployment-group/index.js","previous":144825,"size":144825,"diff":0},{"filename":"ecs-service/index.js","previous":144969,"size":144989,"diff":20}]},{"timestamp":1637185265755,"files":[{"filename":"ecs-deployment-group/index.js","previous":144825,"size":144825,"diff":0},{"filename":"ecs-service/index.js","previous":144856,"size":144969,"diff":113}]},{"timestamp":1637169624155,"files":[{"filename":"ecs-deployment-group/index.js","previous":144825,"size":144825,"diff":0},{"filename":"ecs-service/index.js","previous":144857,"size":144856,"diff":-1}]},{"timestamp":1637169566420,"files":[{"filename":"ecs-deployment-group/index.js","previous":144825,"size":144825,"diff":0},{"filename":"ecs-service/index.js","previous":144819,"size":144857,"diff":38}]},{"timestamp":1636492609712,"files":[{"filename":"ecs-deployment-group/index.js","previous":144825,"size":144825,"diff":0},{"filename":"ecs-service/index.js","previous":144797,"size":144819,"diff":22}]},{"timestamp":1634467601943,"files":[{"filename":"ecs-deployment-group/index.js","previous":144792,"size":144825,"diff":33},{"filename":"ecs-service/index.js","previous":2133,"size":144797,"diff":142664}]},{"timestamp":1634411334117,"files":[{"filename":"dummy-task-definition/index.js","previous":1728,"size":0,"diff":-1728},{"filename":"ecs-deployment-group/index.js","previous":2182,"size":144792,"diff":142610},{"filename":"ecs-service/index.js","previous":2138,"size":2133,"diff":-5}]},{"timestamp":1631683651094,"files":[{"filename":"dummy-task-definition/index.js","previous":1728,"size":1728,"diff":0},{"filename":"ecs-deployment-group/index.js","previous":2182,"size":2182,"diff":0},{"filename":"ecs-service/index.js","previous":2152,"size":2138,"diff":-14}]},{"timestamp":1629825530770,"files":[{"filename":"dummy-task-definition/index.js","previous":1728,"size":1728,"diff":0},{"filename":"ecs-deployment-group/index.js","previous":2171,"size":2182,"diff":11},{"filename":"ecs-service/index.js","previous":2152,"size":2152,"diff":0}]},{"timestamp":1629823812165,"files":[{"filename":"dummy-task-definition/index.js","previous":1728,"size":1728,"diff":0},{"filename":"ecs-deployment-group/index.js","previous":2123,"size":2171,"diff":48},{"filename":"ecs-service/index.js","previous":2152,"size":2152,"diff":0}]},{"timestamp":1627652613714,"files":[{"filename":"dummy-task-definition/index.js","previous":1726,"size":1728,"diff":2},{"filename":"ecs-deployment-group/index.js","previous":2123,"size":2123,"diff":0},{"filename":"ecs-service/index.js","previous":2081,"size":2152,"diff":71}]},{"timestamp":1609276390870,"files":[{"filename":"dummy-task-definition/index.js","previous":1712,"size":1726,"diff":14},{"filename":"ecs-deployment-group/index.js","previous":2123,"size":2123,"diff":0},{"filename":"ecs-service/index.js","previous":2081,"size":2081,"diff":0}]},{"timestamp":1606329521054,"files":[{"filename":"dummy-task-definition/index.js","previous":1706,"size":1712,"diff":6},{"filename":"ecs-deployment-group/index.js","previous":2116,"size":2123,"diff":7},{"filename":"ecs-service/index.js","previous":2073,"size":2081,"diff":8}]},{"timestamp":1596457247342,"files":[{"filename":"dummy-task-definition/index.js","previous":1756,"size":1706,"diff":-50},{"filename":"ecs-deployment-group/index.js","previous":2116,"size":2116,"diff":0},{"filename":"ecs-service/index.js","previous":2073,"size":2073,"diff":0}]},{"timestamp":1596454924871,"files":[{"filename":"dummy-task-definition/index.js","previous":4964,"size":1756,"diff":-3208},{"filename":"ecs-deployment-group/index.js","previous":6103,"size":2116,"diff":-3987},{"filename":"ecs-service/index.js","previous":6141,"size":2073,"diff":-4068}]},{"timestamp":1596407637937,"files":[{"filename":"blue-green-service/__entrypoint__.js","previous":6760,"size":0,"diff":-6760},{"filename":"blue-green-service/index.js","previous":3368,"size":0,"diff":-3368},{"filename":"dummy-task-definition/__entrypoint__.js","previous":6760,"size":0,"diff":-6760},{"filename":"dummy-task-definition/index.js","previous":1963,"size":4964,"diff":3001},{"filename":"ecs-deployment-group/__entrypoint__.js","previous":6760,"size":0,"diff":-6760},{"filename":"ecs-deployment-group/index.js","previous":2292,"size":6103,"diff":3811},{"filename":"ecs-service/__entrypoint__.js","previous":6760,"size":0,"diff":-6760},{"filename":"ecs-service/index.js","previous":2312,"size":6141,"diff":3829}]}]
1+
[{"timestamp":1637606065897,"files":[{"filename":"ecs-deployment-group/index.js","previous":144953,"size":144989,"diff":36},{"filename":"ecs-service/index.js","previous":144959,"size":144996,"diff":37}]},{"timestamp":1637260639127,"files":[{"filename":"ecs-deployment-group/index.js","previous":145091,"size":144953,"diff":-138},{"filename":"ecs-service/index.js","previous":144989,"size":144959,"diff":-30}]},{"timestamp":1637252676806,"files":[{"filename":"ecs-deployment-group/index.js","previous":144919,"size":145091,"diff":172},{"filename":"ecs-service/index.js","previous":144989,"size":144989,"diff":0}]},{"timestamp":1637251737832,"files":[{"filename":"ecs-deployment-group/index.js","previous":144885,"size":144919,"diff":34},{"filename":"ecs-service/index.js","previous":144989,"size":144989,"diff":0}]},{"timestamp":1637251652938,"files":[{"filename":"ecs-deployment-group/index.js","previous":144825,"size":144885,"diff":60},{"filename":"ecs-service/index.js","previous":144989,"size":144989,"diff":0}]},{"timestamp":1637188077152,"files":[{"filename":"ecs-deployment-group/index.js","previous":144825,"size":144825,"diff":0},{"filename":"ecs-service/index.js","previous":144969,"size":144989,"diff":20}]},{"timestamp":1637185265755,"files":[{"filename":"ecs-deployment-group/index.js","previous":144825,"size":144825,"diff":0},{"filename":"ecs-service/index.js","previous":144856,"size":144969,"diff":113}]},{"timestamp":1637169624155,"files":[{"filename":"ecs-deployment-group/index.js","previous":144825,"size":144825,"diff":0},{"filename":"ecs-service/index.js","previous":144857,"size":144856,"diff":-1}]},{"timestamp":1637169566420,"files":[{"filename":"ecs-deployment-group/index.js","previous":144825,"size":144825,"diff":0},{"filename":"ecs-service/index.js","previous":144819,"size":144857,"diff":38}]},{"timestamp":1636492609712,"files":[{"filename":"ecs-deployment-group/index.js","previous":144825,"size":144825,"diff":0},{"filename":"ecs-service/index.js","previous":144797,"size":144819,"diff":22}]},{"timestamp":1634467601943,"files":[{"filename":"ecs-deployment-group/index.js","previous":144792,"size":144825,"diff":33},{"filename":"ecs-service/index.js","previous":2133,"size":144797,"diff":142664}]},{"timestamp":1634411334117,"files":[{"filename":"dummy-task-definition/index.js","previous":1728,"size":0,"diff":-1728},{"filename":"ecs-deployment-group/index.js","previous":2182,"size":144792,"diff":142610},{"filename":"ecs-service/index.js","previous":2138,"size":2133,"diff":-5}]},{"timestamp":1631683651094,"files":[{"filename":"dummy-task-definition/index.js","previous":1728,"size":1728,"diff":0},{"filename":"ecs-deployment-group/index.js","previous":2182,"size":2182,"diff":0},{"filename":"ecs-service/index.js","previous":2152,"size":2138,"diff":-14}]},{"timestamp":1629825530770,"files":[{"filename":"dummy-task-definition/index.js","previous":1728,"size":1728,"diff":0},{"filename":"ecs-deployment-group/index.js","previous":2171,"size":2182,"diff":11},{"filename":"ecs-service/index.js","previous":2152,"size":2152,"diff":0}]},{"timestamp":1629823812165,"files":[{"filename":"dummy-task-definition/index.js","previous":1728,"size":1728,"diff":0},{"filename":"ecs-deployment-group/index.js","previous":2123,"size":2171,"diff":48},{"filename":"ecs-service/index.js","previous":2152,"size":2152,"diff":0}]},{"timestamp":1627652613714,"files":[{"filename":"dummy-task-definition/index.js","previous":1726,"size":1728,"diff":2},{"filename":"ecs-deployment-group/index.js","previous":2123,"size":2123,"diff":0},{"filename":"ecs-service/index.js","previous":2081,"size":2152,"diff":71}]},{"timestamp":1609276390870,"files":[{"filename":"dummy-task-definition/index.js","previous":1712,"size":1726,"diff":14},{"filename":"ecs-deployment-group/index.js","previous":2123,"size":2123,"diff":0},{"filename":"ecs-service/index.js","previous":2081,"size":2081,"diff":0}]},{"timestamp":1606329521054,"files":[{"filename":"dummy-task-definition/index.js","previous":1706,"size":1712,"diff":6},{"filename":"ecs-deployment-group/index.js","previous":2116,"size":2123,"diff":7},{"filename":"ecs-service/index.js","previous":2073,"size":2081,"diff":8}]},{"timestamp":1596457247342,"files":[{"filename":"dummy-task-definition/index.js","previous":1756,"size":1706,"diff":-50},{"filename":"ecs-deployment-group/index.js","previous":2116,"size":2116,"diff":0},{"filename":"ecs-service/index.js","previous":2073,"size":2073,"diff":0}]},{"timestamp":1596454924871,"files":[{"filename":"dummy-task-definition/index.js","previous":4964,"size":1756,"diff":-3208},{"filename":"ecs-deployment-group/index.js","previous":6103,"size":2116,"diff":-3987},{"filename":"ecs-service/index.js","previous":6141,"size":2073,"diff":-4068}]},{"timestamp":1596407637937,"files":[{"filename":"blue-green-service/__entrypoint__.js","previous":6760,"size":0,"diff":-6760},{"filename":"blue-green-service/index.js","previous":3368,"size":0,"diff":-3368},{"filename":"dummy-task-definition/__entrypoint__.js","previous":6760,"size":0,"diff":-6760},{"filename":"dummy-task-definition/index.js","previous":1963,"size":4964,"diff":3001},{"filename":"ecs-deployment-group/__entrypoint__.js","previous":6760,"size":0,"diff":-6760},{"filename":"ecs-deployment-group/index.js","previous":2292,"size":6103,"diff":3811},{"filename":"ecs-service/__entrypoint__.js","previous":6760,"size":0,"diff":-6760},{"filename":"ecs-service/index.js","previous":2312,"size":6141,"diff":3829}]}]

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

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@ import { defaultEcsServiceResourceProperties } from '../__fixtures__/defaultEcsS
4040
import { defaultEvent } from '../__fixtures__/defaultEvent';
4141
import { defaultLogger } from '../__fixtures__/defaultLogger';
4242

43+
afterEach(() => {
44+
mockEcsCreate.mockClear();
45+
mockEcsUpdate.mockClear();
46+
mockEcsTagResource.mockClear();
47+
mockEcsUntagResource.mockClear();
48+
});
49+
4350
describe('createHandler', () => {
4451
it('sends tags with create request', async () => {
4552
const response = await handleCreate(
@@ -130,4 +137,89 @@ describe('updateHandler', () => {
130137
}),
131138
);
132139
});
140+
141+
it('does not delete keys if no old keys are deleted', async () => {
142+
await handleUpdate(
143+
{
144+
...defaultEvent,
145+
RequestType: 'Update',
146+
PhysicalResourceId: 'foo',
147+
ResourceProperties: {
148+
...defaultEcsServiceResourceProperties,
149+
Tags: [
150+
{ Key: 'dis', Value: 'dat' },
151+
{ Key: 'k', Value: 'west' },
152+
{ Key: 'ye', Value: 'west' },
153+
],
154+
},
155+
OldResourceProperties: {
156+
...defaultEcsServiceResourceProperties,
157+
Tags: [
158+
{ Key: 'dis', Value: 'dat' },
159+
{ Key: 'k', Value: 'west' },
160+
{ Key: 'ye', Value: 'west' },
161+
],
162+
},
163+
},
164+
defaultContext,
165+
defaultLogger,
166+
);
167+
168+
expect(mockEcsUpdate).toHaveBeenCalledWith(
169+
expect.objectContaining({
170+
cluster: 'foo',
171+
deploymentConfiguration: {},
172+
desiredCount: 1,
173+
healthCheckGracePeriodSeconds: 3,
174+
service: 'foo',
175+
}),
176+
);
177+
178+
expect(mockEcsUntagResource).not.toHaveBeenCalled();
179+
180+
expect(mockEcsTagResource).toHaveBeenCalledWith(
181+
expect.objectContaining({
182+
resourceArn: 'arn:aws:ecs:us-east-1:012345678910:service/MyCluster/MyService',
183+
tags: [
184+
{ key: 'dis', value: 'dat' },
185+
{ key: 'k', value: 'west' },
186+
{ key: 'ye', value: 'west' },
187+
],
188+
}),
189+
);
190+
});
191+
192+
it('does not delete or create keys if no old keys or new keys are present', async () => {
193+
await handleUpdate(
194+
{
195+
...defaultEvent,
196+
RequestType: 'Update',
197+
PhysicalResourceId: 'foo',
198+
ResourceProperties: {
199+
...defaultEcsServiceResourceProperties,
200+
Tags: [],
201+
},
202+
OldResourceProperties: {
203+
...defaultEcsServiceResourceProperties,
204+
Tags: [],
205+
},
206+
},
207+
defaultContext,
208+
defaultLogger,
209+
);
210+
211+
expect(mockEcsUpdate).toHaveBeenCalledWith(
212+
expect.objectContaining({
213+
cluster: 'foo',
214+
deploymentConfiguration: {},
215+
desiredCount: 1,
216+
healthCheckGracePeriodSeconds: 3,
217+
service: 'foo',
218+
}),
219+
);
220+
221+
expect(mockEcsUntagResource).not.toHaveBeenCalled();
222+
223+
expect(mockEcsTagResource).not.toHaveBeenCalled();
224+
});
133225
});

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export interface IEcsService {
1515

1616
export enum PropagateTags {
1717
SERVICE = 'SERVICE',
18-
TASK_DEFINITION = 'TASK_DEFINITION'
18+
TASK_DEFINITION = 'TASK_DEFINITION',
1919
}
2020

2121
export interface EcsServiceProps {
@@ -111,7 +111,14 @@ export class EcsService extends Construct implements IConnectable, IEcsService,
111111
serviceToken.addToRolePolicy(
112112
new PolicyStatement({
113113
effect: Effect.ALLOW,
114-
actions: ['ecs:CreateService', 'ecs:UpdateService', 'ecs:DeleteService', 'ecs:DescribeServices'],
114+
actions: [
115+
'ecs:CreateService',
116+
'ecs:UpdateService',
117+
'ecs:DeleteService',
118+
'ecs:DescribeServices',
119+
'ecs:TagResource',
120+
'ecs:UntagResource',
121+
],
115122
resources: ['*'],
116123
}),
117124
);

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

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,9 @@ export const handleCreate: OnCreateHandler = async (event): Promise<ResourceHand
127127
* For more information, see CreateDeployment in the AWS CodeDeploy API Reference.
128128
*/
129129
export const handleUpdate: OnUpdateHandler = async (event): Promise<ResourceHandlerReturn> => {
130-
const { cluster, serviceName, desiredCount, deploymentConfiguration, healthCheckGracePeriodSeconds, tags } =
131-
getProperties(event.ResourceProperties);
130+
const { cluster, serviceName, desiredCount, deploymentConfiguration, healthCheckGracePeriodSeconds, tags } = getProperties(
131+
event.ResourceProperties,
132+
);
132133

133134
const { service } = await ecs
134135
.updateService({
@@ -143,23 +144,29 @@ export const handleUpdate: OnUpdateHandler = async (event): Promise<ResourceHand
143144
if (!service) throw Error('Service could not be updated');
144145

145146
const newTagKeys: string[] = tags.map((t: Tag) => t.Key);
146-
const removableTagKeys: string[] = (event.OldResourceProperties.Tags || []).map((t: any) => t.Key).filter((t: string) => !newTagKeys.includes(t));
147-
148-
await ecs
149-
.untagResource({
150-
resourceArn: service.serviceArn as string,
151-
tagKeys: removableTagKeys,
152-
})
153-
.promise();
154-
155-
await ecs
156-
.tagResource({
157-
resourceArn: service.serviceArn as string,
158-
tags: tags.map((t) => {
159-
return { key: t.Key, value: t.Value };
160-
}),
161-
})
162-
.promise();
147+
const removableTagKeys: string[] = (event.OldResourceProperties.Tags || [])
148+
.map((t: any) => t.Key)
149+
.filter((t: string) => !newTagKeys.includes(t));
150+
151+
if (removableTagKeys.length > 0) {
152+
await ecs
153+
.untagResource({
154+
resourceArn: service.serviceArn as string,
155+
tagKeys: removableTagKeys,
156+
})
157+
.promise();
158+
}
159+
160+
if (tags.length > 0) {
161+
await ecs
162+
.tagResource({
163+
resourceArn: service.serviceArn as string,
164+
tags: tags.map((t) => {
165+
return { key: t.Key, value: t.Value };
166+
}),
167+
})
168+
.promise();
169+
}
163170

164171
return {
165172
physicalResourceId: service.serviceArn as string,

0 commit comments

Comments
 (0)