Skip to content
This repository was archived by the owner on Jan 28, 2025. It is now read-only.

Commit b5d03b1

Browse files
authored
feat(aws-cloudfront, nextjs-component): support cloudfront tags (#1350)
1 parent 49b56a3 commit b5d03b1

File tree

13 files changed

+158
-38
lines changed

13 files changed

+158
-38
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,9 @@ myNextApplication:
245245
originAccessIdentityId: XYZEXAMPLE #optional
246246
paths: ["/*"] # which paths should be invalidated on deploy, default matches everything, empty array skips invalidation completely
247247
waitBeforeInvalidate: true # by default true, it waits for the CloudFront distribution to have completed before invalidating, to avoid possibly caching old page
248+
tags: # Add any tags you want
249+
tag1: val1
250+
tag2: val2
248251
```
249252

250253
This is particularly useful for caching any of your Next.js pages at CloudFront's edge locations. See [this](https://github.com/serverless-nextjs/serverless-next.js/tree/master/packages/serverless-components/nextjs-component/examples/app-with-custom-caching-config) for an example application with custom cache configuration.
@@ -363,6 +366,9 @@ The exhaustive list of AWS actions required for a deployment:
363366
"cloudfront:ListPublicKeys",
364367
"cloudfront:ListStreamingDistributions",
365368
"cloudfront:UpdateDistribution",
369+
"cloudfront:TagResource", // for adding tags
370+
"cloudfront:UntagResource", // for adding tags
371+
"cloudfront:ListTagsForResource", // for adding tags
366372
"iam:AttachRolePolicy",
367373
"iam:CreateRole",
368374
"iam:CreateServiceLinkedRole",

packages/e2e-tests/next-app/serverless.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ next-app:
1010
api/*:
1111
forward:
1212
headers: [Authorization]
13+
tags:
14+
tag1: val1
1315
tags:
1416
defaultLambda:
1517
tag1: val1

packages/libs/cloudfront/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
},
3434
"homepage": "https://github.com/serverless-nextjs/serverless-next.js#readme",
3535
"dependencies": {
36-
"aws-sdk": "2.937.0"
36+
"aws-sdk": "^2.938.0"
3737
},
3838
"devDependencies": {
3939
"@types/node": "^15.12.2",

packages/libs/cloudfront/yarn.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
resolved "https://registry.npmjs.org/@types/node/-/node-15.14.0.tgz#74dbf254fb375551a9d2a71faf6b9dbc2178dc53"
88
integrity sha512-um/+/ip3QZmwLfIkWZSNtQIJNVAqrJ92OkLMeuZrjZMTAJniI7fh8N8OICyDhAJ2mzgk/fmYFo72jRr5HyZ1EQ==
99

10-
aws-sdk@2.937.0:
11-
version "2.937.0"
12-
resolved "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.937.0.tgz#6dce5f72343c89bf06672403af3135397eba0fe7"
13-
integrity sha512-Ko5fATHxfHWMVJjS5/7eNEeIZ0Sja3B5f7ZvdyGmyRdUv7JVeppkNmc6cK5jFt/qGxVOK2OZnY/vE6D/INwGiQ==
10+
aws-sdk@^2.938.0:
11+
version "2.938.0"
12+
resolved "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.938.0.tgz#44fa4d95632f6a22c00d50955c2a4a42ade2c1d4"
13+
integrity sha512-e+KWYRyJ4Tvlg+6kQTze9Hxmkn+4/H8m+D8AXlfgUbtzyc1OBDf7cMCcl8IvCD3xoqELsgEunPC014v2JTTfZg==
1414
dependencies:
1515
buffer "4.9.2"
1616
events "1.1.1"

packages/libs/lambda-at-edge/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,9 @@
6161
"typescript": "^4.2.4"
6262
},
6363
"dependencies": {
64-
"@aws-sdk/client-s3": "3.19.0",
65-
"@aws-sdk/client-sqs": "3.19.0",
66-
"@hapi/accept": "5.0.1",
64+
"@aws-sdk/client-s3": "^3.19.0",
65+
"@aws-sdk/client-sqs": "^3.19.0",
66+
"@hapi/accept": "^5.0.2",
6767
"@sls-next/core": "link:../core",
6868
"@vercel/nft": "^0.13.1",
6969
"execa": "^5.0.1",

packages/libs/lambda-at-edge/yarn.lock

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@
6868
dependencies:
6969
tslib "^2.0.0"
7070

71-
"@aws-sdk/[email protected]":
71+
"@aws-sdk/client-s3@^3.19.0":
7272
version "3.19.0"
7373
resolved "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.19.0.tgz#ab6dc6ee56f3af3145269c66251ad7216569294b"
7474
integrity sha512-in342ONWtXUNsh2H7Pe8jnB8ebEKQZTGQgCM5/V6Ue+f6wzgBqmb8blPMoFw+uFPJnxPfMugz6FvJzaOsB/sxA==
@@ -121,7 +121,7 @@
121121
fast-xml-parser "3.19.0"
122122
tslib "^2.0.0"
123123

124-
"@aws-sdk/[email protected]":
124+
"@aws-sdk/client-sqs@^3.19.0":
125125
version "3.19.0"
126126
resolved "https://registry.npmjs.org/@aws-sdk/client-sqs/-/client-sqs-3.19.0.tgz#441e8a1b4b69c1fa5e26a345e12397aa4b9a2888"
127127
integrity sha512-lixruihQ76nGU8ZNde4pJnALWDguS42p30gxkU4xQdk+G0TAmEh9wVL7d3myHJVMerqUwRqJgY8xjuuNdbVIdg==
@@ -1010,15 +1010,7 @@
10101010
"@babel/helper-validator-identifier" "^7.14.5"
10111011
to-fast-properties "^2.0.0"
10121012

1013-
1014-
version "5.0.1"
1015-
resolved "https://registry.npmjs.org/@hapi/accept/-/accept-5.0.1.tgz#068553e867f0f63225a506ed74e899441af53e10"
1016-
integrity sha512-fMr4d7zLzsAXo28PRRQPXR1o2Wmu+6z+VY1UzDp0iFo13Twj8WePakwXBiqn3E1aAlTpSNzCXdnnQXFhst8h8Q==
1017-
dependencies:
1018-
"@hapi/boom" "9.x.x"
1019-
"@hapi/hoek" "9.x.x"
1020-
1021-
"@hapi/accept@^5.0.1":
1013+
"@hapi/accept@^5.0.1", "@hapi/accept@^5.0.2":
10221014
version "5.0.2"
10231015
resolved "https://registry.npmjs.org/@hapi/accept/-/accept-5.0.2.tgz#ab7043b037e68b722f93f376afb05e85c0699523"
10241016
integrity sha512-CmzBx/bXUR8451fnZRuZAJRlzgm0Jgu5dltTX/bszmR2lheb9BpyN47Q1RbaGTsvFzn0PXAEs+lXDKfshccYZw==
@@ -1124,13 +1116,8 @@
11241116
picomatch "^2.2.2"
11251117

11261118
"@sls-next/core@link:../core":
1127-
version "3.2.0-alpha.12"
1128-
dependencies:
1129-
"@hapi/accept" "^5.0.1"
1130-
cookie "^0.4.1"
1131-
jsonwebtoken "^8.5.1"
1132-
path-to-regexp "^6.1.0"
1133-
regex-parser "^2.2.10"
1119+
version "0.0.0"
1120+
uid ""
11341121

11351122
"@tsconfig/node10@^1.0.7":
11361123
version "1.0.8"

packages/libs/s3-static-assets/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
},
3434
"homepage": "https://github.com/serverless-nextjs/serverless-next.js#readme",
3535
"dependencies": {
36-
"aws-sdk": "2.937.0",
36+
"aws-sdk": "^2.938.0",
3737
"fast-glob": "^3.2.5",
3838
"fs-extra": "^9.1.0",
3939
"mime-types": "^2.1.27",

packages/libs/s3-static-assets/yarn.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ at-least-node@^1.0.0:
3333
resolved "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2"
3434
integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==
3535

36-
aws-sdk@2.937.0:
37-
version "2.937.0"
38-
resolved "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.937.0.tgz#6dce5f72343c89bf06672403af3135397eba0fe7"
39-
integrity sha512-Ko5fATHxfHWMVJjS5/7eNEeIZ0Sja3B5f7ZvdyGmyRdUv7JVeppkNmc6cK5jFt/qGxVOK2OZnY/vE6D/INwGiQ==
36+
aws-sdk@^2.938.0:
37+
version "2.938.0"
38+
resolved "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.938.0.tgz#44fa4d95632f6a22c00d50955c2a4a42ade2c1d4"
39+
integrity sha512-e+KWYRyJ4Tvlg+6kQTze9Hxmkn+4/H8m+D8AXlfgUbtzyc1OBDf7cMCcl8IvCD3xoqELsgEunPC014v2JTTfZg==
4040
dependencies:
4141
buffer "4.9.2"
4242
events "1.1.1"

packages/serverless-components/aws-cloudfront/__mocks__/aws-sdk.mock.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,17 @@ export const mockGetCloudFrontOriginAccessIdentityPromise = promisifyMock(
4040
export const mockPutBucketPolicy = jest.fn();
4141
export const mockPutBucketPolicyPromise = promisifyMock(mockPutBucketPolicy);
4242

43+
export const mockUntagResource = jest.fn();
44+
export const mockUntagResourcePromise = promisifyMock(mockUntagResource);
45+
46+
export const mockTagResource = jest.fn();
47+
export const mockTagResourcePromise = promisifyMock(mockTagResource);
48+
49+
export const mockListTagsForResource = jest.fn();
50+
export const mockListTagsForResourcePromise = promisifyMock(
51+
mockListTagsForResource
52+
);
53+
4354
export default {
4455
CloudFront: jest.fn(() => ({
4556
createDistribution: mockCreateDistribution,
@@ -48,7 +59,10 @@ export default {
4859
deleteDistribution: mockDeleteDistribution,
4960
createCloudFrontOriginAccessIdentity:
5061
mockCreateCloudFrontOriginAccessIdentity,
51-
getCloudFrontOriginAccessIdentity: mockGetCloudFrontOriginAccessIdentity
62+
getCloudFrontOriginAccessIdentity: mockGetCloudFrontOriginAccessIdentity,
63+
listTagsForResource: mockListTagsForResource,
64+
untagResource: mockUntagResource,
65+
tagResource: mockTagResource
5266
})),
5367

5468
S3: jest.fn(() => ({

packages/serverless-components/aws-cloudfront/__tests__/general-options.test.ts

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ import {
55
mockUpdateDistribution,
66
mockCreateDistributionPromise,
77
mockGetDistributionConfigPromise,
8-
mockUpdateDistributionPromise
8+
mockUpdateDistributionPromise,
9+
mockListTagsForResource,
10+
mockListTagsForResourcePromise,
11+
mockUntagResource,
12+
mockTagResource
913
} from "../__mocks__/aws-sdk.mock";
1014

1115
jest.mock("aws-sdk", () => require("../__mocks__/aws-sdk.mock"));
@@ -19,7 +23,8 @@ describe("General options propagation", () => {
1923
beforeEach(async () => {
2024
mockCreateDistributionPromise.mockResolvedValueOnce({
2125
Distribution: {
22-
Id: "distribution123"
26+
Id: "distribution123",
27+
ARN: "distributionArn"
2328
}
2429
});
2530

@@ -452,4 +457,49 @@ describe("General options propagation", () => {
452457
})
453458
);
454459
});
460+
461+
it("create distribution with tags", async () => {
462+
mockListTagsForResourcePromise.mockResolvedValueOnce({
463+
Tags: {
464+
Items: [{ Key: "existingTag", Tag: "existingValue" }]
465+
}
466+
});
467+
468+
await component.default({
469+
tags: {
470+
tag1: "val1",
471+
tag2: "val2"
472+
},
473+
origins
474+
});
475+
476+
expect(mockCreateDistribution).toBeCalled();
477+
478+
expect(mockListTagsForResource).toBeCalledWith({
479+
Resource: "distributionArn"
480+
});
481+
482+
expect(mockUntagResource).toBeCalledWith({
483+
Resource: "distributionArn",
484+
TagKeys: {
485+
Items: ["existingTag"]
486+
}
487+
});
488+
489+
expect(mockTagResource).toBeCalledWith({
490+
Resource: "distributionArn",
491+
Tags: {
492+
Items: [
493+
{
494+
Key: "tag1",
495+
Value: "val1"
496+
},
497+
{
498+
Key: "tag2",
499+
Value: "val2"
500+
}
501+
]
502+
}
503+
});
504+
});
455505
});

packages/serverless-components/aws-cloudfront/src/component.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import { Component } from "@serverless/core";
44
import {
55
createCloudFrontDistribution,
66
updateCloudFrontDistribution,
7-
deleteCloudFrontDistribution
7+
deleteCloudFrontDistribution,
8+
setCloudFrontDistributionTags
89
} from "./";
910

1011
/*
@@ -23,7 +24,7 @@ class CloudFront extends Component {
2324
this.context.status("Deploying");
2425

2526
inputs.region = inputs.region || "us-east-1";
26-
inputs.enabled = inputs.enabled === false ? false : true;
27+
inputs.enabled = inputs.enabled !== false;
2728
inputs.comment =
2829
inputs.comment === null || inputs.comment === undefined
2930
? ""
@@ -95,6 +96,14 @@ class CloudFront extends Component {
9596
this.state = await createCloudFrontDistribution(cf, s3, inputs);
9697
}
9798

99+
// Set distribution tags if present
100+
if (inputs.tags && !equals(this.state.tags, inputs.tags)) {
101+
this.context.debug(
102+
`Updating tags for CloudFront distribution of ID ${this.state.id}.`
103+
);
104+
await setCloudFrontDistributionTags(cf, this.state.arn, inputs.tags);
105+
}
106+
98107
this.state.region = inputs.region;
99108
this.state.enabled = inputs.enabled;
100109
this.state.comment = inputs.comment;
@@ -103,6 +112,7 @@ class CloudFront extends Component {
103112
this.state.origins = inputs.origins;
104113
this.state.errorPages = inputs.errorPages;
105114
this.state.defaults = inputs.defaults;
115+
this.state.tags = inputs.tags;
106116
await this.save();
107117

108118
this.context.debug(

packages/serverless-components/aws-cloudfront/src/index.ts

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,8 +342,57 @@ const deleteCloudFrontDistribution = async (cf, distributionId) => {
342342
}
343343
};
344344

345+
const setCloudFrontDistributionTags = async (
346+
cf: AWS.CloudFront,
347+
distributionArn: string,
348+
tags: Record<string, string>
349+
) => {
350+
const listTagsResponse = await cf
351+
.listTagsForResource({
352+
Resource: distributionArn
353+
})
354+
.promise();
355+
356+
const existingTags = {};
357+
if (listTagsResponse.Tags && listTagsResponse.Tags.Items) {
358+
for (const tag of listTagsResponse.Tags.Items) {
359+
existingTags[tag.Key] = tag.Value;
360+
}
361+
}
362+
363+
// Remove tags if there are any
364+
if (Object.keys(existingTags).length > 0) {
365+
await cf
366+
.untagResource({
367+
Resource: distributionArn,
368+
TagKeys: {
369+
Items: Object.keys(existingTags)
370+
}
371+
})
372+
.promise();
373+
}
374+
375+
// Add new tags if there are any
376+
const newTags = [];
377+
for (const [key, value] of Object.entries(tags)) {
378+
newTags.push({ Key: key, Value: value });
379+
}
380+
381+
if (newTags.length > 0) {
382+
await cf
383+
.tagResource({
384+
Resource: distributionArn,
385+
Tags: {
386+
Items: newTags
387+
}
388+
})
389+
.promise();
390+
}
391+
};
392+
345393
export {
346394
createCloudFrontDistribution,
347395
updateCloudFrontDistribution,
348-
deleteCloudFrontDistribution
396+
deleteCloudFrontDistribution,
397+
setCloudFrontDistributionTags
349398
};

packages/serverless-components/nextjs-component/src/component.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ class NextjsComponent extends Component {
314314
originAccessIdentityId: cloudFrontOriginAccessIdentityId,
315315
paths: cloudFrontPaths,
316316
waitBeforeInvalidate: cloudFrontWaitBeforeInvalidate = true,
317+
tags: cloudFrontTags,
317318
...cloudFrontOtherInputs
318319
} = inputs.cloudfront || {};
319320

@@ -867,7 +868,8 @@ class NextjsComponent extends Component {
867868
webACLId: cloudFrontWebACLId,
868869
restrictions: cloudFrontRestrictions,
869870
certificate: cloudFrontCertificate,
870-
originAccessIdentityId: cloudFrontOriginAccessIdentityId
871+
originAccessIdentityId: cloudFrontOriginAccessIdentityId,
872+
tags: cloudFrontTags
871873
});
872874

873875
let appUrl = cloudFrontOutputs.url;

0 commit comments

Comments
 (0)