Skip to content

Commit

Permalink
feat(aws-cloudfront, nextjs-component): support cloudfront tags (#1350)
Browse files Browse the repository at this point in the history
  • Loading branch information
dphang committed Jul 2, 2021
1 parent 49b56a3 commit b5d03b1
Show file tree
Hide file tree
Showing 13 changed files with 158 additions and 38 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,9 @@ myNextApplication:
originAccessIdentityId: XYZEXAMPLE #optional
paths: ["/*"] # which paths should be invalidated on deploy, default matches everything, empty array skips invalidation completely
waitBeforeInvalidate: true # by default true, it waits for the CloudFront distribution to have completed before invalidating, to avoid possibly caching old page
tags: # Add any tags you want
tag1: val1
tag2: val2
```

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.
Expand Down Expand Up @@ -363,6 +366,9 @@ The exhaustive list of AWS actions required for a deployment:
"cloudfront:ListPublicKeys",
"cloudfront:ListStreamingDistributions",
"cloudfront:UpdateDistribution",
"cloudfront:TagResource", // for adding tags
"cloudfront:UntagResource", // for adding tags
"cloudfront:ListTagsForResource", // for adding tags
"iam:AttachRolePolicy",
"iam:CreateRole",
"iam:CreateServiceLinkedRole",
Expand Down
2 changes: 2 additions & 0 deletions packages/e2e-tests/next-app/serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ next-app:
api/*:
forward:
headers: [Authorization]
tags:
tag1: val1
tags:
defaultLambda:
tag1: val1
Expand Down
2 changes: 1 addition & 1 deletion packages/libs/cloudfront/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
},
"homepage": "https://github.com/serverless-nextjs/serverless-next.js#readme",
"dependencies": {
"aws-sdk": "2.937.0"
"aws-sdk": "^2.938.0"
},
"devDependencies": {
"@types/node": "^15.12.2",
Expand Down
8 changes: 4 additions & 4 deletions packages/libs/cloudfront/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
resolved "https://registry.npmjs.org/@types/node/-/node-15.14.0.tgz#74dbf254fb375551a9d2a71faf6b9dbc2178dc53"
integrity sha512-um/+/ip3QZmwLfIkWZSNtQIJNVAqrJ92OkLMeuZrjZMTAJniI7fh8N8OICyDhAJ2mzgk/fmYFo72jRr5HyZ1EQ==

aws-sdk@2.937.0:
version "2.937.0"
resolved "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.937.0.tgz#6dce5f72343c89bf06672403af3135397eba0fe7"
integrity sha512-Ko5fATHxfHWMVJjS5/7eNEeIZ0Sja3B5f7ZvdyGmyRdUv7JVeppkNmc6cK5jFt/qGxVOK2OZnY/vE6D/INwGiQ==
aws-sdk@^2.938.0:
version "2.938.0"
resolved "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.938.0.tgz#44fa4d95632f6a22c00d50955c2a4a42ade2c1d4"
integrity sha512-e+KWYRyJ4Tvlg+6kQTze9Hxmkn+4/H8m+D8AXlfgUbtzyc1OBDf7cMCcl8IvCD3xoqELsgEunPC014v2JTTfZg==
dependencies:
buffer "4.9.2"
events "1.1.1"
Expand Down
6 changes: 3 additions & 3 deletions packages/libs/lambda-at-edge/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@
"typescript": "^4.2.4"
},
"dependencies": {
"@aws-sdk/client-s3": "3.19.0",
"@aws-sdk/client-sqs": "3.19.0",
"@hapi/accept": "5.0.1",
"@aws-sdk/client-s3": "^3.19.0",
"@aws-sdk/client-sqs": "^3.19.0",
"@hapi/accept": "^5.0.2",
"@sls-next/core": "link:../core",
"@vercel/nft": "^0.13.1",
"execa": "^5.0.1",
Expand Down
23 changes: 5 additions & 18 deletions packages/libs/lambda-at-edge/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
dependencies:
tslib "^2.0.0"

"@aws-sdk/client-s3@3.19.0":
"@aws-sdk/client-s3@^3.19.0":
version "3.19.0"
resolved "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.19.0.tgz#ab6dc6ee56f3af3145269c66251ad7216569294b"
integrity sha512-in342ONWtXUNsh2H7Pe8jnB8ebEKQZTGQgCM5/V6Ue+f6wzgBqmb8blPMoFw+uFPJnxPfMugz6FvJzaOsB/sxA==
Expand Down Expand Up @@ -121,7 +121,7 @@
fast-xml-parser "3.19.0"
tslib "^2.0.0"

"@aws-sdk/client-sqs@3.19.0":
"@aws-sdk/client-sqs@^3.19.0":
version "3.19.0"
resolved "https://registry.npmjs.org/@aws-sdk/client-sqs/-/client-sqs-3.19.0.tgz#441e8a1b4b69c1fa5e26a345e12397aa4b9a2888"
integrity sha512-lixruihQ76nGU8ZNde4pJnALWDguS42p30gxkU4xQdk+G0TAmEh9wVL7d3myHJVMerqUwRqJgY8xjuuNdbVIdg==
Expand Down Expand Up @@ -1010,15 +1010,7 @@
"@babel/helper-validator-identifier" "^7.14.5"
to-fast-properties "^2.0.0"

"@hapi/accept@5.0.1":
version "5.0.1"
resolved "https://registry.npmjs.org/@hapi/accept/-/accept-5.0.1.tgz#068553e867f0f63225a506ed74e899441af53e10"
integrity sha512-fMr4d7zLzsAXo28PRRQPXR1o2Wmu+6z+VY1UzDp0iFo13Twj8WePakwXBiqn3E1aAlTpSNzCXdnnQXFhst8h8Q==
dependencies:
"@hapi/boom" "9.x.x"
"@hapi/hoek" "9.x.x"

"@hapi/accept@^5.0.1":
"@hapi/accept@^5.0.1", "@hapi/accept@^5.0.2":
version "5.0.2"
resolved "https://registry.npmjs.org/@hapi/accept/-/accept-5.0.2.tgz#ab7043b037e68b722f93f376afb05e85c0699523"
integrity sha512-CmzBx/bXUR8451fnZRuZAJRlzgm0Jgu5dltTX/bszmR2lheb9BpyN47Q1RbaGTsvFzn0PXAEs+lXDKfshccYZw==
Expand Down Expand Up @@ -1124,13 +1116,8 @@
picomatch "^2.2.2"

"@sls-next/core@link:../core":
version "3.2.0-alpha.12"
dependencies:
"@hapi/accept" "^5.0.1"
cookie "^0.4.1"
jsonwebtoken "^8.5.1"
path-to-regexp "^6.1.0"
regex-parser "^2.2.10"
version "0.0.0"
uid ""

"@tsconfig/node10@^1.0.7":
version "1.0.8"
Expand Down
2 changes: 1 addition & 1 deletion packages/libs/s3-static-assets/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
},
"homepage": "https://github.com/serverless-nextjs/serverless-next.js#readme",
"dependencies": {
"aws-sdk": "2.937.0",
"aws-sdk": "^2.938.0",
"fast-glob": "^3.2.5",
"fs-extra": "^9.1.0",
"mime-types": "^2.1.27",
Expand Down
8 changes: 4 additions & 4 deletions packages/libs/s3-static-assets/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ at-least-node@^1.0.0:
resolved "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2"
integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==

aws-sdk@2.937.0:
version "2.937.0"
resolved "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.937.0.tgz#6dce5f72343c89bf06672403af3135397eba0fe7"
integrity sha512-Ko5fATHxfHWMVJjS5/7eNEeIZ0Sja3B5f7ZvdyGmyRdUv7JVeppkNmc6cK5jFt/qGxVOK2OZnY/vE6D/INwGiQ==
aws-sdk@^2.938.0:
version "2.938.0"
resolved "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.938.0.tgz#44fa4d95632f6a22c00d50955c2a4a42ade2c1d4"
integrity sha512-e+KWYRyJ4Tvlg+6kQTze9Hxmkn+4/H8m+D8AXlfgUbtzyc1OBDf7cMCcl8IvCD3xoqELsgEunPC014v2JTTfZg==
dependencies:
buffer "4.9.2"
events "1.1.1"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,17 @@ export const mockGetCloudFrontOriginAccessIdentityPromise = promisifyMock(
export const mockPutBucketPolicy = jest.fn();
export const mockPutBucketPolicyPromise = promisifyMock(mockPutBucketPolicy);

export const mockUntagResource = jest.fn();
export const mockUntagResourcePromise = promisifyMock(mockUntagResource);

export const mockTagResource = jest.fn();
export const mockTagResourcePromise = promisifyMock(mockTagResource);

export const mockListTagsForResource = jest.fn();
export const mockListTagsForResourcePromise = promisifyMock(
mockListTagsForResource
);

export default {
CloudFront: jest.fn(() => ({
createDistribution: mockCreateDistribution,
Expand All @@ -48,7 +59,10 @@ export default {
deleteDistribution: mockDeleteDistribution,
createCloudFrontOriginAccessIdentity:
mockCreateCloudFrontOriginAccessIdentity,
getCloudFrontOriginAccessIdentity: mockGetCloudFrontOriginAccessIdentity
getCloudFrontOriginAccessIdentity: mockGetCloudFrontOriginAccessIdentity,
listTagsForResource: mockListTagsForResource,
untagResource: mockUntagResource,
tagResource: mockTagResource
})),

S3: jest.fn(() => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import {
mockUpdateDistribution,
mockCreateDistributionPromise,
mockGetDistributionConfigPromise,
mockUpdateDistributionPromise
mockUpdateDistributionPromise,
mockListTagsForResource,
mockListTagsForResourcePromise,
mockUntagResource,
mockTagResource
} from "../__mocks__/aws-sdk.mock";

jest.mock("aws-sdk", () => require("../__mocks__/aws-sdk.mock"));
Expand All @@ -19,7 +23,8 @@ describe("General options propagation", () => {
beforeEach(async () => {
mockCreateDistributionPromise.mockResolvedValueOnce({
Distribution: {
Id: "distribution123"
Id: "distribution123",
ARN: "distributionArn"
}
});

Expand Down Expand Up @@ -452,4 +457,49 @@ describe("General options propagation", () => {
})
);
});

it("create distribution with tags", async () => {
mockListTagsForResourcePromise.mockResolvedValueOnce({
Tags: {
Items: [{ Key: "existingTag", Tag: "existingValue" }]
}
});

await component.default({
tags: {
tag1: "val1",
tag2: "val2"
},
origins
});

expect(mockCreateDistribution).toBeCalled();

expect(mockListTagsForResource).toBeCalledWith({
Resource: "distributionArn"
});

expect(mockUntagResource).toBeCalledWith({
Resource: "distributionArn",
TagKeys: {
Items: ["existingTag"]
}
});

expect(mockTagResource).toBeCalledWith({
Resource: "distributionArn",
Tags: {
Items: [
{
Key: "tag1",
Value: "val1"
},
{
Key: "tag2",
Value: "val2"
}
]
}
});
});
});
14 changes: 12 additions & 2 deletions packages/serverless-components/aws-cloudfront/src/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import { Component } from "@serverless/core";
import {
createCloudFrontDistribution,
updateCloudFrontDistribution,
deleteCloudFrontDistribution
deleteCloudFrontDistribution,
setCloudFrontDistributionTags
} from "./";

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

inputs.region = inputs.region || "us-east-1";
inputs.enabled = inputs.enabled === false ? false : true;
inputs.enabled = inputs.enabled !== false;
inputs.comment =
inputs.comment === null || inputs.comment === undefined
? ""
Expand Down Expand Up @@ -95,6 +96,14 @@ class CloudFront extends Component {
this.state = await createCloudFrontDistribution(cf, s3, inputs);
}

// Set distribution tags if present
if (inputs.tags && !equals(this.state.tags, inputs.tags)) {
this.context.debug(
`Updating tags for CloudFront distribution of ID ${this.state.id}.`
);
await setCloudFrontDistributionTags(cf, this.state.arn, inputs.tags);
}

this.state.region = inputs.region;
this.state.enabled = inputs.enabled;
this.state.comment = inputs.comment;
Expand All @@ -103,6 +112,7 @@ class CloudFront extends Component {
this.state.origins = inputs.origins;
this.state.errorPages = inputs.errorPages;
this.state.defaults = inputs.defaults;
this.state.tags = inputs.tags;
await this.save();

this.context.debug(
Expand Down
51 changes: 50 additions & 1 deletion packages/serverless-components/aws-cloudfront/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -342,8 +342,57 @@ const deleteCloudFrontDistribution = async (cf, distributionId) => {
}
};

const setCloudFrontDistributionTags = async (
cf: AWS.CloudFront,
distributionArn: string,
tags: Record<string, string>
) => {
const listTagsResponse = await cf
.listTagsForResource({
Resource: distributionArn
})
.promise();

const existingTags = {};
if (listTagsResponse.Tags && listTagsResponse.Tags.Items) {
for (const tag of listTagsResponse.Tags.Items) {
existingTags[tag.Key] = tag.Value;
}
}

// Remove tags if there are any
if (Object.keys(existingTags).length > 0) {
await cf
.untagResource({
Resource: distributionArn,
TagKeys: {
Items: Object.keys(existingTags)
}
})
.promise();
}

// Add new tags if there are any
const newTags = [];
for (const [key, value] of Object.entries(tags)) {
newTags.push({ Key: key, Value: value });
}

if (newTags.length > 0) {
await cf
.tagResource({
Resource: distributionArn,
Tags: {
Items: newTags
}
})
.promise();
}
};

export {
createCloudFrontDistribution,
updateCloudFrontDistribution,
deleteCloudFrontDistribution
deleteCloudFrontDistribution,
setCloudFrontDistributionTags
};
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,7 @@ class NextjsComponent extends Component {
originAccessIdentityId: cloudFrontOriginAccessIdentityId,
paths: cloudFrontPaths,
waitBeforeInvalidate: cloudFrontWaitBeforeInvalidate = true,
tags: cloudFrontTags,
...cloudFrontOtherInputs
} = inputs.cloudfront || {};

Expand Down Expand Up @@ -867,7 +868,8 @@ class NextjsComponent extends Component {
webACLId: cloudFrontWebACLId,
restrictions: cloudFrontRestrictions,
certificate: cloudFrontCertificate,
originAccessIdentityId: cloudFrontOriginAccessIdentityId
originAccessIdentityId: cloudFrontOriginAccessIdentityId,
tags: cloudFrontTags
});

let appUrl = cloudFrontOutputs.url;
Expand Down

0 comments on commit b5d03b1

Please sign in to comment.