Skip to content

Commit

Permalink
Merge pull request #249 from getlift/fix-cf-function-names
Browse files Browse the repository at this point in the history
  • Loading branch information
mnapoli authored Aug 5, 2022
2 parents dc0ce09 + 8f7ff29 commit 1635149
Show file tree
Hide file tree
Showing 9 changed files with 151 additions and 3 deletions.
7 changes: 7 additions & 0 deletions src/constructs/aws/ServerSideWebsite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import * as cloudfront from "aws-cdk-lib/aws-cloudfront";
import { AwsConstruct } from "@lift/constructs/abstracts";
import type { ConstructCommands } from "@lift/constructs";
import type { AwsProvider } from "@lift/providers";
import { ensureNameMaxLength } from "../../utils/naming";
import { s3Put, s3Sync } from "../../utils/s3-sync";
import { emptyBucket, invalidateCloudFrontCache } from "../../classes/aws";
import ServerlessError from "../../utils/error";
Expand Down Expand Up @@ -456,7 +457,13 @@ export class ServerSideWebsite extends AwsConstruct {
return request;
}`;

const functionName = ensureNameMaxLength(
`${this.provider.stackName}-${this.provider.region}-${this.id}-request`,
64
);

return new cloudfront.Function(this, "RequestFunction", {
functionName,
code: cloudfront.FunctionCode.fromInline(code),
});
}
Expand Down
8 changes: 7 additions & 1 deletion src/constructs/aws/SinglePageApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { Construct as CdkConstruct } from "constructs";
import type { AwsProvider } from "@lift/providers";
import { redirectToMainDomain } from "../../classes/cloudfrontFunctions";
import { getCfnFunctionAssociations } from "../../utils/getDefaultCfnFunctionAssociations";
import { ensureNameMaxLength } from "../../utils/naming";
import type { CommonStaticWebsiteConfiguration } from "./abstracts/StaticWebsiteAbstract";
import { COMMON_STATIC_WEBSITE_DEFINITION, StaticWebsiteAbstract } from "./abstracts/StaticWebsiteAbstract";

Expand Down Expand Up @@ -58,8 +59,13 @@ function handler(event) {
return event.request;
}`;

const functionName = ensureNameMaxLength(
`${this.provider.stackName}-${this.provider.region}-${this.id}-request`,
64
);

return new cloudfront.Function(this, "RequestFunction", {
functionName: `${this.provider.stackName}-${this.provider.region}-${this.id}-request`,
functionName,
code: cloudfront.FunctionCode.fromInline(code),
});
}
Expand Down
8 changes: 7 additions & 1 deletion src/constructs/aws/StaticWebsite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { BucketProps } from "aws-cdk-lib/aws-s3";
import { RemovalPolicy } from "aws-cdk-lib";
import { redirectToMainDomain } from "../../classes/cloudfrontFunctions";
import { getCfnFunctionAssociations } from "../../utils/getDefaultCfnFunctionAssociations";
import { ensureNameMaxLength } from "../../utils/naming";
import type { CommonStaticWebsiteConfiguration } from "./abstracts/StaticWebsiteAbstract";
import { COMMON_STATIC_WEBSITE_DEFINITION, StaticWebsiteAbstract } from "./abstracts/StaticWebsiteAbstract";

Expand Down Expand Up @@ -52,8 +53,13 @@ export class StaticWebsite extends StaticWebsiteAbstract {
return request;
}`;

const functionName = ensureNameMaxLength(
`${this.provider.stackName}-${this.provider.region}-${this.id}-request`,
64
);

return new cloudfront.Function(this, "RequestFunction", {
functionName: `${this.provider.stackName}-${this.provider.region}-${this.id}-request`,
functionName,
code: cloudfront.FunctionCode.fromInline(code),
});
}
Expand Down
8 changes: 7 additions & 1 deletion src/constructs/aws/abstracts/StaticWebsiteAbstract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { emptyBucket, invalidateCloudFrontCache } from "../../../classes/aws";
import ServerlessError from "../../../utils/error";
import type { Progress } from "../../../utils/logger";
import { getUtils } from "../../../utils/logger";
import { ensureNameMaxLength } from "../../../utils/naming";
import { s3Sync } from "../../../utils/s3-sync";

export const COMMON_STATIC_WEBSITE_DEFINITION = {
Expand Down Expand Up @@ -343,8 +344,13 @@ export abstract class StaticWebsiteAbstract extends AwsConstruct {
return response;
}`;

const functionName = ensureNameMaxLength(
`${this.provider.stackName}-${this.provider.region}-${this.id}-response`,
64
);

return new cloudfront.Function(this, "ResponseFunction", {
functionName: `${this.provider.stackName}-${this.provider.region}-${this.id}-response`,
functionName,
code: cloudfront.FunctionCode.fromInline(code),
});
}
Expand Down
11 changes: 11 additions & 0 deletions src/utils/naming.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import crypto from "crypto";

export function ensureNameMaxLength(name: string, maxLength: number): string {
if (name.length <= maxLength) {
return name;
}

const uniqueSuffix = crypto.createHash("md5").update(name).digest("hex").slice(0, 6);

return name.slice(0, maxLength - uniqueSuffix.length - 1) + "-" + uniqueSuffix;
}
33 changes: 33 additions & 0 deletions test/unit/serverSideWebsite.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,17 @@ describe("server-side website", () => {
},
},
});
expect(cfTemplate.Resources[requestFunction]).toMatchObject({
Type: "AWS::CloudFront::Function",
Properties: {
Name: "app-dev-us-east-1-backend-request",
FunctionConfig: {
Comment: "app-dev-us-east-1-backend-request",
Runtime: "cloudfront-js-1.0",
},
AutoPublish: true,
},
});
expect(cfTemplate.Outputs).toMatchObject({
[computeLogicalId("backend", "AssetsBucketName")]: {
Description: "Name of the bucket that stores the website assets.",
Expand Down Expand Up @@ -704,4 +715,26 @@ describe("server-side website", () => {
ObjectLockEnabled: true,
});
});

it("trims CloudFront function names to stay under the limit", async () => {
const { cfTemplate, computeLogicalId } = await runServerless({
command: "package",
options: {
stage: "super-long-stage-name",
},
config: Object.assign(baseConfig, {
constructs: {
"suuuper-long-construct-name": {
type: "server-side-website",
},
},
}),
});
expect(cfTemplate.Resources[computeLogicalId("suuuper-long-construct-name", "RequestFunction")]).toMatchObject({
Type: "AWS::CloudFront::Function",
Properties: {
Name: "app-super-long-stage-name-us-east-1-suuuper-long-construc-f3b7e1",
},
});
});
});
31 changes: 31 additions & 0 deletions test/unit/singlePageApp.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,4 +161,35 @@ describe("single page app", () => {
ObjectLockEnabled: true,
});
});

it("trims CloudFront function names to stay under the limit", async () => {
const { cfTemplate, computeLogicalId } = await runServerless({
command: "package",
options: {
stage: "super-long-stage-name",
},
config: Object.assign(baseConfig, {
constructs: {
"suuuper-long-construct-name": {
type: "single-page-app",
path: ".",
},
},
}),
});
expect(cfTemplate.Resources[computeLogicalId("suuuper-long-construct-name", "RequestFunction")]).toMatchObject({
Type: "AWS::CloudFront::Function",
Properties: {
Name: "app-super-long-stage-name-us-east-1-suuuper-long-construc-f3b7e1",
},
});
expect(cfTemplate.Resources[computeLogicalId("suuuper-long-construct-name", "ResponseFunction")]).toMatchObject(
{
Type: "AWS::CloudFront::Function",
Properties: {
Name: "app-super-long-stage-name-us-east-1-suuuper-long-construc-8c1f76",
},
}
);
});
});
34 changes: 34 additions & 0 deletions test/unit/staticWebsite.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -610,4 +610,38 @@ describe("static websites", () => {
ObjectLockEnabled: true,
});
});

it("trims CloudFront function names to stay under the limit", async () => {
const { cfTemplate, computeLogicalId } = await runServerless({
command: "package",
options: {
stage: "super-long-stage-name",
},
config: Object.assign(baseConfig, {
constructs: {
"suuuper-long-construct-name": {
type: "static-website",
path: ".",
domain: ["foo.com", "bar.com"],
certificate: "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234",
redirectToMainDomain: true,
},
},
}),
});
expect(cfTemplate.Resources[computeLogicalId("suuuper-long-construct-name", "RequestFunction")]).toMatchObject({
Type: "AWS::CloudFront::Function",
Properties: {
Name: "app-super-long-stage-name-us-east-1-suuuper-long-construc-f3b7e1",
},
});
expect(cfTemplate.Resources[computeLogicalId("suuuper-long-construct-name", "ResponseFunction")]).toMatchObject(
{
Type: "AWS::CloudFront::Function",
Properties: {
Name: "app-super-long-stage-name-us-east-1-suuuper-long-construc-8c1f76",
},
}
);
});
});
14 changes: 14 additions & 0 deletions test/unit/utils/naming.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { ensureNameMaxLength } from "../../../src/utils/naming";

describe("naming", () => {
it("should not change names shorter than the limit", () => {
expect(ensureNameMaxLength("foo", 3)).toEqual("foo");
});

it("should trim names with a unique suffix to stay under the limit", () => {
expect(ensureNameMaxLength("foobarfoobarfoobarfoobar", 15)).toEqual("foobarfo-7ca709");
expect(ensureNameMaxLength("foobarfoobarfoobarfoobar", 15)).toHaveLength(15);
// The suffix changes based on teh full string to avoid duplicates
expect(ensureNameMaxLength("foobarfoofoofoofoofoofoo", 15)).not.toEqual("foobarfo-7ca709");
});
});

0 comments on commit 1635149

Please sign in to comment.