Skip to content

Commit

Permalink
Normalize bootstrap bucket name
Browse files Browse the repository at this point in the history
  • Loading branch information
fwang committed Dec 22, 2023
1 parent 5399532 commit a442ba3
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 47 deletions.
7 changes: 5 additions & 2 deletions internal/components/src/components/bucket.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Input, ComponentResourceOptions, output } from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
import { RandomId } from "@pulumi/random";
import { prefixName, randomDecToSuffix } from "./helpers/naming";
import { prefixName, hashNumberToString } from "./helpers/naming";
import { Component } from "./component";

/**
Expand Down Expand Up @@ -36,7 +36,10 @@ export class Bucket extends Component {
`${name}Bucket`,
{
bucket: randomId.dec.apply((dec) =>
prefixName(name.toLowerCase(), `-${randomDecToSuffix(dec)}`)
prefixName(
name.toLowerCase(),
`-${hashNumberToString(parseInt(dec), 8)}`
)
),
forceDestroy: true,
...args?.nodes?.bucket,
Expand Down
13 changes: 10 additions & 3 deletions internal/components/src/components/helpers/aws.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import crypto from "crypto";
const bootstrapBuckets: Record<string, Promise<string>> = {};
import {
SSMClient,
Expand All @@ -11,7 +10,7 @@ import { StandardRetryStrategy } from "@aws-sdk/middleware-retry";
export type {} from "@smithy/types";
import { output } from "@pulumi/pulumi";
import { lazy } from "../../util/lazy";
import { sanitizeToPascalCase } from "./naming";
import { HASH_CHARS, hashNumberToString, sanitizeToPascalCase } from "./naming";

const useProviderCache = lazy(() => new Map<string, aws.Provider>());
const useClientCache = lazy(() => new Map<string, any>());
Expand Down Expand Up @@ -49,7 +48,15 @@ export const AWS = {

if (result?.Parameter?.Value) return result.Parameter.Value;

const name = `sst-bootstrap-${crypto.randomUUID()}`;
// Generate a bootstrap bucket suffix number
const suffixLength = 12;
const minNumber = Math.pow(HASH_CHARS.length, suffixLength);
const numberSuffix =
Math.floor(Math.random() * minNumber) + minNumber;
const name = `sst-bootstrap-${hashNumberToString(
numberSuffix,
suffixLength
)}`;
await s3.send(
new CreateBucketCommand({
Bucket: name,
Expand Down
27 changes: 16 additions & 11 deletions internal/components/src/components/helpers/naming.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export function prefixName(name: string, suffix?: string) {

const stageTruncated = $app.stage.substring(
0,
Math.max(8, L - nameLen - 1),
Math.max(8, L - nameLen - 1)
);
const nameTruncated = name.substring(0, L - stageTruncated.length - 1);
return `${nameTruncated}-${stageTruncated}`;
Expand All @@ -34,16 +34,21 @@ export function prefixName(name: string, suffix?: string) {
return `${prefixedName}${suffixStr}`;
}

export function randomDecToSuffix(dec: string) {
return numberToLowercaseAlphaString(parseInt(dec), 8).padStart(8, "s");
}
export function hashNumberToString(number: number, length: number) {
const charLength = HASH_CHARS.length;
let hash = "";
while (number > 0) {
hash = HASH_CHARS[number % charLength] + hash;
number = Math.floor(number / charLength);
}

function numberToLowercaseAlphaString(num: number, len: number) {
let result = "";
while (num > 0 && result.length < len) {
let remainder = (num - 1) % 26;
result = String.fromCharCode(97 + remainder) + result;
num = Math.floor((num - 1) / 26);
// Padding with 's'
hash = hash.slice(0, length);
while (hash.length < length) {
hash = "s" + hash;
}
return result;

return hash;
}

export const HASH_CHARS = "abcdefhkmnorstuvwxz";
60 changes: 30 additions & 30 deletions internal/components/src/components/nextjs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ export class Nextjs extends Component {
constructor(
name: string,
args?: NextjsArgs,
opts?: ComponentResourceOptions,
opts?: ComponentResourceOptions
) {
super("sst:sst:Nextjs", name, args, opts);

Expand Down Expand Up @@ -198,7 +198,7 @@ export class Nextjs extends Component {
outputPath,
access,
bucket,
plan,
plan
);
const serverFunction = ssrFunctions[0] ?? Object.values(edgeFunctions)[0];

Expand Down Expand Up @@ -249,7 +249,7 @@ export class Nextjs extends Component {
...(experimental.disableIncrementalCache
? ["--dangerously-disable-incremental-cache"]
: []),
].join(" "),
].join(" ")
);
}

Expand Down Expand Up @@ -351,7 +351,7 @@ export class Nextjs extends Component {
bundle: path.join(
outputPath,
".open-next",
"image-optimization-function",
"image-optimization-function"
),
runtime: "nodejs18.x",
architectures: ["arm64"],
Expand Down Expand Up @@ -438,13 +438,13 @@ export class Nextjs extends Component {
cacheType: "static",
pattern: fs
.statSync(
path.join(outputPath, ".open-next/assets", item),
path.join(outputPath, ".open-next/assets", item)
)
.isDirectory()
? `${item}/*`
: item,
origin: "s3",
}) as const,
}) as const
),
],
cachePolicyAllowedHeaders: DEFAULT_CACHE_POLICY_ALLOWED_HEADERS,
Expand All @@ -455,7 +455,7 @@ export class Nextjs extends Component {
function: path.join(
outputPath,
".open-next",
"warmer-function",
"warmer-function"
),
},
});
Expand All @@ -467,7 +467,7 @@ if (event.rawPath) {
routes.map(({ regex, logGroupPath }) => ({
regex,
logGroupPath,
})),
}))
)}.find(({ regex }) => event.rawPath.match(new RegExp(regex)));
if (routeData) {
console.log("::sst::" + JSON.stringify({
Expand All @@ -479,8 +479,8 @@ if (event.rawPath) {
}
}`;
}
},
),
}
)
);
}

Expand All @@ -495,15 +495,15 @@ if (event.rawPath) {
fifoQueue: true,
receiveWaitTimeSeconds: 20,
},
{ parent },
{ parent }
);
const consumer = new Function(
`${name}Revalidator`,
{
description: "Next.js revalidator",
handler: "index.handler",
bundle: outputPath.apply((outputPath) =>
path.join(outputPath, ".open-next", "revalidation-function"),
path.join(outputPath, ".open-next", "revalidation-function")
),
runtime: "nodejs18.x",
timeout: "30 seconds",
Expand All @@ -526,12 +526,12 @@ if (event.rawPath) {
},
],
})
.then((doc) => doc.json),
.then((doc) => doc.json)
),
},
],
},
{ parent },
{ parent }
);
new aws.lambda.EventSourceMapping(
`${name}RevalidatorEventSource`,
Expand All @@ -540,7 +540,7 @@ if (event.rawPath) {
eventSourceArn: queue.arn,
batchSize: 5,
},
{ parent },
{ parent }
);
return queue;
});
Expand Down Expand Up @@ -638,7 +638,7 @@ if (event.rawPath) {
});
for (const file of files) {
fs.rmSync(
path.join(outputPath, ".open-next", "server-function", file),
path.join(outputPath, ".open-next", "server-function", file)
);
}
});
Expand Down Expand Up @@ -703,7 +703,7 @@ if (event.rawPath) {
// "/favicon.ico/route": "/favicon.ico"
// }
const appPathRoute = Object.keys(appPathRoutesManifest).find(
(key) => appPathRoutesManifest[key] === page,
(key) => appPathRoutesManifest[key] === page
);
if (!appPathRoute) return;

Expand All @@ -724,7 +724,7 @@ if (event.rawPath) {
outputPath,
".next",
"server",
`${filePath}.map`,
`${filePath}.map`
);
if (!fs.existsSync(sourcemapPath)) return;

Expand Down Expand Up @@ -753,13 +753,13 @@ if (event.rawPath) {
outputPath,
".next",
"server",
`${filePath}.map`,
`${filePath}.map`
);
if (!fs.existsSync(sourcemapPath)) return;

return sourcemapPath;
}
},
}
);

return _routes;
Expand All @@ -778,7 +778,7 @@ if (event.rawPath) {
} catch (e) {
console.error(e);
throw new Error(
`Failed to read routes data from ".next/routes-manifest.json" for the "${name}" site.`,
`Failed to read routes data from ".next/routes-manifest.json" for the "${name}" site.`
);
}
});
Expand All @@ -791,7 +791,7 @@ if (event.rawPath) {
try {
const content = fs
.readFileSync(
path.join(outputPath, ".next/app-path-routes-manifest.json"),
path.join(outputPath, ".next/app-path-routes-manifest.json")
)
.toString();
return JSON.parse(content) as Record<string, string>;
Expand All @@ -809,7 +809,7 @@ if (event.rawPath) {
try {
const content = fs
.readFileSync(
path.join(outputPath, ".next/server/app-paths-manifest.json"),
path.join(outputPath, ".next/server/app-paths-manifest.json")
)
.toString();
return JSON.parse(content) as Record<string, string>;
Expand All @@ -827,7 +827,7 @@ if (event.rawPath) {
try {
const content = fs
.readFileSync(
path.join(outputPath, ".next/server/pages-manifest.json"),
path.join(outputPath, ".next/server/pages-manifest.json")
)
.toString();
return JSON.parse(content) as Record<string, string>;
Expand All @@ -845,7 +845,7 @@ if (event.rawPath) {
try {
const content = fs
.readFileSync(
path.join(outputPath, ".next/prerender-manifest.json"),
path.join(outputPath, ".next/prerender-manifest.json")
)
.toString();
prerenderManifest = JSON.parse(content);
Expand Down Expand Up @@ -899,15 +899,15 @@ if (event.rawPath) {
]
}`,
},
{ parent },
{ parent }
);
new aws.iam.RolePolicyAttachment(
`${name}DisableLoggingPolicyAttachment`,
{
policyArn: policy.arn,
role: serverFunction.nodes.function.role,
},
{ parent },
{ parent }
);
}

Expand All @@ -922,14 +922,14 @@ if (event.rawPath) {
`${name}Sourcemap${sanitizeToPascalCase(sourcemapKey)}`,
{
bucket: output($app.providers?.aws?.region!).apply((region) =>
AWS.bootstrap.forRegion(region),
AWS.bootstrap.forRegion(region)
),
source: new asset.FileAsset(sourcemapPath),
key: serverFunction!.nodes.function.arn.apply((arn) =>
path.posix.join("sourcemaps", arn, sourcemapKey),
path.posix.join("sourcemaps", arn, sourcemapKey)
),
},
{ parent, retainOnDelete: true },
{ parent, retainOnDelete: true }
);
});
});
Expand Down
2 changes: 1 addition & 1 deletion pkg/project/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ func (a *AwsProvider) resolveBucket() (string, error) {
var pnf *ssmTypes.ParameterNotFound
if errors.As(err, &pnf) {
region := a.config.Region
bucketName := fmt.Sprintf("sst-bootstrap-%v", util.RandomString(12))
bucketName := fmt.Sprintf("sst--%v", util.RandomString(12))
slog.Info("creating bootstrap bucket", "name", bucketName)
s3Client := s3.NewFromConfig(a.config)

Expand Down

0 comments on commit a442ba3

Please sign in to comment.