sst 2.18.4 → 2.19.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,17 +12,20 @@ import * as functionUrlCors from "./util/functionUrlCors.js";
12
12
  import url from "url";
13
13
  import { useDeferredTasks } from "./deferred_task.js";
14
14
  import { useProject } from "../project.js";
15
+ import { VisibleError } from "../error.js";
15
16
  import { useRuntimeHandlers } from "../runtime/handlers.js";
16
17
  import { createAppContext } from "./context.js";
17
18
  import { useWarning } from "./util/warning.js";
18
- import { Architecture, AssetCode, Code, Function as CDKFunction, FunctionUrlAuthType, LayerVersion, Runtime as CDKRuntime, Tracing, } from "aws-cdk-lib/aws-lambda";
19
+ import { Architecture, AssetCode, Code, Function as CDKFunction, FunctionUrlAuthType, Handler as CDKHandler, LayerVersion, Runtime as CDKRuntime, Tracing, } from "aws-cdk-lib/aws-lambda";
19
20
  import { RetentionDays } from "aws-cdk-lib/aws-logs";
20
21
  import { Token, Size as CDKSize, Duration as CDKDuration, } from "aws-cdk-lib/core";
21
22
  import { Effect, PolicyStatement } from "aws-cdk-lib/aws-iam";
22
23
  import { StringParameter } from "aws-cdk-lib/aws-ssm";
24
+ import { Platform } from "aws-cdk-lib/aws-ecr-assets";
23
25
  import { useBootstrap } from "../bootstrap.js";
24
26
  const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
25
27
  const supportedRuntimes = {
28
+ container: CDKRuntime.FROM_IMAGE,
26
29
  rust: CDKRuntime.PROVIDED_AL2,
27
30
  nodejs: CDKRuntime.NODEJS,
28
31
  "nodejs4.3": CDKRuntime.NODEJS_4_3,
@@ -96,7 +99,7 @@ export class Function extends CDKFunction {
96
99
  return Architecture.ARM_64;
97
100
  if (props.architecture === "x86_64")
98
101
  return Architecture.X86_64;
99
- return undefined;
102
+ return Architecture.X86_64;
100
103
  })();
101
104
  const memorySize = Function.normalizeMemorySize(props.memorySize);
102
105
  const diskSize = Function.normalizeDiskSize(props.diskSize);
@@ -147,17 +150,30 @@ export class Function extends CDKFunction {
147
150
  }
148
151
  super(scope, id, {
149
152
  ...props,
153
+ ...(props.runtime === "container"
154
+ ? {
155
+ code: Code.fromAssetImage(path.resolve(__dirname, "../support/bridge"), {
156
+ ...(architecture?.dockerPlatform
157
+ ? { platform: Platform.custom(architecture.dockerPlatform) }
158
+ : {}),
159
+ }),
160
+ handler: CDKHandler.FROM_IMAGE,
161
+ runtime: CDKRuntime.FROM_IMAGE,
162
+ layers: undefined,
163
+ }
164
+ : {
165
+ runtime: CDKRuntime.NODEJS_16_X,
166
+ code: Code.fromAsset(path.resolve(__dirname, "../support/bridge")),
167
+ handler: "bridge.handler",
168
+ layers: [],
169
+ }),
150
170
  architecture,
151
- code: Code.fromAsset(path.resolve(__dirname, "../support/bridge")),
152
- handler: "bridge.handler",
153
171
  functionName,
154
- runtime: CDKRuntime.NODEJS_16_X,
155
172
  memorySize,
156
173
  ephemeralStorageSize: diskSize,
157
174
  timeout,
158
175
  tracing,
159
176
  environment: props.environment,
160
- layers: [],
161
177
  logRetention,
162
178
  logRetentionRetryOptions: logRetention && { maxRetries: 100 },
163
179
  retryAttempts: 0,
@@ -186,17 +202,30 @@ export class Function extends CDKFunction {
186
202
  else {
187
203
  super(scope, id, {
188
204
  ...props,
205
+ ...(props.runtime === "container"
206
+ ? {
207
+ code: Code.fromAssetImage(props.handler, {
208
+ ...(architecture?.dockerPlatform
209
+ ? { platform: Platform.custom(architecture.dockerPlatform) }
210
+ : {}),
211
+ }),
212
+ handler: CDKHandler.FROM_IMAGE,
213
+ runtime: CDKRuntime.FROM_IMAGE,
214
+ layers: undefined,
215
+ }
216
+ : {
217
+ code: Code.fromInline("export function placeholder() {}"),
218
+ handler: "index.placeholder",
219
+ runtime: CDKRuntime.NODEJS_16_X,
220
+ layers: Function.buildLayers(scope, id, props),
221
+ }),
189
222
  architecture,
190
- code: Code.fromInline("export function placeholder() {}"),
191
- handler: "index.placeholder",
192
223
  functionName,
193
- runtime: CDKRuntime.NODEJS_16_X,
194
224
  memorySize,
195
225
  ephemeralStorageSize: diskSize,
196
226
  timeout,
197
227
  tracing,
198
228
  environment: props.environment,
199
- layers: Function.buildLayers(scope, id, props),
200
229
  logRetention,
201
230
  logRetentionRetryOptions: logRetention && { maxRetries: 100 },
202
231
  });
@@ -204,11 +233,14 @@ export class Function extends CDKFunction {
204
233
  // Build function
205
234
  const result = await useRuntimeHandlers().build(this.node.addr, "deploy");
206
235
  if (result.type === "error") {
207
- throw new Error([
236
+ throw new VisibleError([
208
237
  `Failed to build function "${props.handler}"`,
209
238
  ...result.errors,
210
239
  ].join("\n"));
211
240
  }
241
+ // No need to update code if runtime is container
242
+ if (props.runtime === "container")
243
+ return;
212
244
  // Update code
213
245
  const cfnFunction = this.node.defaultChild;
214
246
  const code = AssetCode.fromAsset(result.out);
@@ -877,9 +877,7 @@ function handler(event) {
877
877
  generateBuildId() {
878
878
  // We will generate a hash based on the contents of the "public" folder
879
879
  // which will be used to indicate if we need to invalidate our CloudFront
880
- // cache. As the browser build files are always uniquely hash in their
881
- // filenames according to their content we can ignore the browser build
882
- // files.
880
+ // cache.
883
881
  // The below options are needed to support following symlinks when building zip files:
884
882
  // - nodir: This will prevent symlinks themselves from being copied into the zip.
885
883
  // - follow: This will follow symlinks and copy the files within.
@@ -887,7 +885,6 @@ function handler(event) {
887
885
  dot: true,
888
886
  nodir: true,
889
887
  follow: true,
890
- ignore: [`${this.buildConfig.clientBuildVersionedSubDir}/**`],
891
888
  cwd: path.resolve(this.props.path, this.buildConfig.clientBuildOutputDir),
892
889
  };
893
890
  const files = glob.sync("**", globOptions);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "sideEffects": false,
3
3
  "name": "sst",
4
- "version": "2.18.4",
4
+ "version": "2.19.1",
5
5
  "bin": {
6
6
  "sst": "cli/sst.js"
7
7
  },
package/project.d.ts CHANGED
@@ -29,6 +29,7 @@ export interface ConfigOptions {
29
29
  imageAssetPublishingRoleArn?: string;
30
30
  cloudFormationExecutionRole?: string;
31
31
  lookupRoleArn?: string;
32
+ pathMetadata?: boolean;
32
33
  };
33
34
  }
34
35
  declare const DEFAULTS: {
@@ -0,0 +1 @@
1
+ export declare const useContainerHandler: () => Promise<void>;
@@ -0,0 +1,124 @@
1
+ import { useRuntimeHandlers } from "../handlers.js";
2
+ import { useRuntimeWorkers } from "../workers.js";
3
+ import { Context } from "../../context/context.js";
4
+ import { VisibleError } from "../../error.js";
5
+ import { spawn } from "child_process";
6
+ import { useRuntimeServerConfig } from "../server.js";
7
+ import { isChild } from "../../util/fs.js";
8
+ import { execAsync } from "../../util/process.js";
9
+ export const useContainerHandler = Context.memo(async () => {
10
+ const workers = await useRuntimeWorkers();
11
+ const server = await useRuntimeServerConfig();
12
+ const handlers = useRuntimeHandlers();
13
+ const containers = new Map();
14
+ const sources = new Map();
15
+ handlers.register({
16
+ shouldBuild: (input) => {
17
+ const parent = sources.get(input.functionID);
18
+ if (!parent)
19
+ return false;
20
+ return isChild(parent, input.file);
21
+ },
22
+ canHandle: (input) => input.startsWith("container"),
23
+ startWorker: async (input) => {
24
+ const name = `sst-workerID-${input.workerID}-${Date.now()}`;
25
+ const proc = spawn("docker", [
26
+ "run",
27
+ "--rm",
28
+ "--network=host",
29
+ `--name=${name}`,
30
+ ...Object.entries({
31
+ ...input.environment,
32
+ IS_LOCAL: "true",
33
+ AWS_LAMBDA_RUNTIME_API: `host.docker.internal:${server.port}/${input.workerID}`,
34
+ })
35
+ .map(([key, value]) => ["-e", `${key}=${value}`])
36
+ .flat(),
37
+ `sst-dev:${input.functionID}`,
38
+ ], {
39
+ env: {
40
+ ...process.env,
41
+ },
42
+ cwd: input.out,
43
+ });
44
+ proc.on("exit", () => {
45
+ workers.exited(input.workerID);
46
+ });
47
+ proc.stdout.on("data", (data) => {
48
+ workers.stdout(input.workerID, data.toString());
49
+ });
50
+ proc.stderr.on("data", (data) => {
51
+ workers.stdout(input.workerID, data.toString());
52
+ });
53
+ containers.set(input.workerID, name);
54
+ },
55
+ stopWorker: async (workerID) => {
56
+ const name = containers.get(workerID);
57
+ if (name) {
58
+ try {
59
+ // note:
60
+ // - calling `docker kill` kills the docker process much faster than `docker stop`
61
+ // - process.kill() does not work on docker processes
62
+ await execAsync(`docker kill ${name}`, {
63
+ env: {
64
+ ...process.env,
65
+ },
66
+ });
67
+ }
68
+ catch (ex) {
69
+ console.error(ex);
70
+ throw new VisibleError(`Could not stop docker container ${name}`);
71
+ }
72
+ containers.delete(workerID);
73
+ }
74
+ },
75
+ build: async (input) => {
76
+ const project = input.props.handler;
77
+ sources.set(input.functionID, project);
78
+ if (input.mode === "start") {
79
+ try {
80
+ const result = await execAsync(`docker build -t sst-dev:${input.functionID} .`, {
81
+ cwd: project,
82
+ env: {
83
+ ...process.env,
84
+ },
85
+ });
86
+ }
87
+ catch (ex) {
88
+ return {
89
+ type: "error",
90
+ errors: [String(ex)],
91
+ };
92
+ }
93
+ }
94
+ if (input.mode === "deploy") {
95
+ try {
96
+ const platform = input.props.architecture === "arm_64"
97
+ ? "linux/arm64"
98
+ : "linux/amd64";
99
+ await execAsync([
100
+ `docker build`,
101
+ `-t sst-build:${input.functionID}`,
102
+ `--platform ${platform}`,
103
+ `.`,
104
+ ].join(" "), {
105
+ cwd: project,
106
+ env: {
107
+ ...process.env,
108
+ },
109
+ });
110
+ }
111
+ catch (ex) {
112
+ return {
113
+ type: "error",
114
+ errors: [String(ex)],
115
+ };
116
+ }
117
+ }
118
+ return {
119
+ type: "success",
120
+ handler: "not required for container",
121
+ };
122
+ },
123
+ });
124
+ });
@@ -1,6 +1,9 @@
1
1
  import { FunctionProps } from "../constructs/Function.js";
2
2
  declare module "../bus.js" {
3
3
  interface Events {
4
+ "function.build.started": {
5
+ functionID: string;
6
+ };
4
7
  "function.build.success": {
5
8
  functionID: string;
6
9
  };
@@ -19,6 +22,7 @@ interface BuildInput {
19
22
  interface StartWorkerInput {
20
23
  url: string;
21
24
  workerID: string;
25
+ functionID: string;
22
26
  environment: Record<string, string>;
23
27
  out: string;
24
28
  handler: string;
@@ -34,6 +34,7 @@ export const useRuntimeHandlers = Context.memo(() => {
34
34
  const out = path.join(project.paths.artifacts, functionID);
35
35
  await fs.rm(out, { recursive: true, force: true });
36
36
  await fs.mkdir(out, { recursive: true });
37
+ bus.publish("function.build.started", { functionID });
37
38
  if (func.hooks?.beforeBuild)
38
39
  await func.hooks.beforeBuild(func, out);
39
40
  const built = await handler.build({
@@ -43,6 +43,7 @@ export const useRuntimeWorkers = Context.memo(async () => {
43
43
  await handler.startWorker({
44
44
  ...build,
45
45
  workerID: evt.properties.workerID,
46
+ functionID: evt.properties.functionID,
46
47
  environment: evt.properties.env,
47
48
  url: `${server.url}/${evt.properties.workerID}/${server.API_VERSION}`,
48
49
  runtime: props.runtime,