sst 2.0.18 → 2.0.19

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.
@@ -10,6 +10,7 @@ import { Duration } from "./util/duration.js";
10
10
  import { Permissions } from "./util/permission.js";
11
11
  import * as functionUrlCors from "./util/functionUrlCors.js";
12
12
  declare const supportedRuntimes: {
13
+ rust: cdk.aws_lambda.Runtime;
13
14
  nodejs: cdk.aws_lambda.Runtime;
14
15
  "nodejs4.3": cdk.aws_lambda.Runtime;
15
16
  "nodejs6.10": cdk.aws_lambda.Runtime;
@@ -21,6 +21,7 @@ import { useRuntimeHandlers } from "../runtime/handlers.js";
21
21
  import { createAppContext } from "./context.js";
22
22
  const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
23
23
  const supportedRuntimes = {
24
+ rust: lambda.Runtime.PROVIDED_AL2,
24
25
  nodejs: lambda.Runtime.NODEJS,
25
26
  "nodejs4.3": lambda.Runtime.NODEJS_4_3,
26
27
  "nodejs6.10": lambda.Runtime.NODEJS_6_10,
@@ -354,6 +354,7 @@ export declare class WebSocketApi extends Construct implements SSTConstruct {
354
354
  };
355
355
  private createWebSocketApi;
356
356
  private createWebSocketStage;
357
+ private createCloudWatchRole;
357
358
  private addAuthorizer;
358
359
  private addRoute;
359
360
  private buildRouteAuth;
@@ -1,8 +1,10 @@
1
1
  import { Construct } from "constructs";
2
+ import { CustomResource } from "aws-cdk-lib";
2
3
  import * as iam from "aws-cdk-lib/aws-iam";
3
4
  import * as apig from "@aws-cdk/aws-apigatewayv2-alpha";
4
5
  import * as apigAuthorizers from "@aws-cdk/aws-apigatewayv2-authorizers-alpha";
5
6
  import * as apigIntegrations from "@aws-cdk/aws-apigatewayv2-integrations-alpha";
7
+ import { Effect, Policy, PolicyStatement } from "aws-cdk-lib/aws-iam";
6
8
  import { Stack } from "./Stack.js";
7
9
  import { getFunctionRef, isCDKConstruct } from "./Construct.js";
8
10
  import { Function as Fn, } from "./Function.js";
@@ -258,7 +260,10 @@ export class WebSocketApi extends Construct {
258
260
  };
259
261
  this._customDomainUrl = `wss://${customDomainData.url}`;
260
262
  }
263
+ // Create CloudWatch Role
264
+ const customResource = this.createCloudWatchRole();
261
265
  // Create stage
266
+ // note: create the CloudWatch role before creating the Api
262
267
  this.cdk.webSocketStage = new apig.WebSocketStage(this, "Stage", {
263
268
  webSocketApi: this.cdk.webSocketApi,
264
269
  stageName: this.node.root.stage,
@@ -266,10 +271,43 @@ export class WebSocketApi extends Construct {
266
271
  domainMapping,
267
272
  ...webSocketStageProps,
268
273
  });
274
+ this.cdk.webSocketStage.node.addDependency(customResource);
269
275
  // Configure Access Log
270
276
  this.cdk.accessLogGroup = apigV2AccessLog.buildAccessLogData(this, accessLog, this.cdk.webSocketStage, true);
271
277
  }
272
278
  }
279
+ createCloudWatchRole() {
280
+ const stack = Stack.of(this);
281
+ const roleName = "apigateway-cloudwatch-logs-role";
282
+ const roleArn = `arn:${stack.partition}:iam::${stack.account}:role/${roleName}`;
283
+ const policy = new Policy(this, "APIGatewayCloudWatchRolePolicy", {
284
+ statements: [
285
+ new PolicyStatement({
286
+ effect: Effect.ALLOW,
287
+ actions: ["apigateway:GET", "apigateway:PATCH"],
288
+ resources: [
289
+ `arn:${stack.partition}:apigateway:${stack.region}::/account`,
290
+ ],
291
+ }),
292
+ new PolicyStatement({
293
+ effect: Effect.ALLOW,
294
+ actions: ["iam:CreateRole", "iam:PassRole", "iam:AttachRolePolicy"],
295
+ resources: [roleArn],
296
+ }),
297
+ ],
298
+ });
299
+ stack.customResourceHandler.role?.attachInlinePolicy(policy);
300
+ const resource = new CustomResource(this, "APIGatewayCloudWatchRole", {
301
+ serviceToken: stack.customResourceHandler.functionArn,
302
+ resourceType: "Custom::APIGatewayCloudWatchRole",
303
+ properties: {
304
+ roleName,
305
+ roleArn,
306
+ },
307
+ });
308
+ resource.node.addDependency(policy);
309
+ return resource;
310
+ }
273
311
  addAuthorizer() {
274
312
  const { authorizer } = this.props;
275
313
  if (!authorizer || authorizer === "none") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sst",
3
- "version": "2.0.18",
3
+ "version": "2.0.19",
4
4
  "bin": {
5
5
  "sst": "cli/sst.js"
6
6
  },
@@ -29,8 +29,10 @@
29
29
  "@aws-cdk/cloudformation-diff": "2.62.2",
30
30
  "@aws-cdk/cx-api": "2.62.2",
31
31
  "@aws-cdk/region-info": "2.62.2",
32
+ "@aws-sdk/client-api-gateway": "3.208.0",
32
33
  "@aws-sdk/client-cloudformation": "3.208.0",
33
34
  "@aws-sdk/client-cloudfront": "3.208.0",
35
+ "@aws-sdk/client-iam": "3.208.0",
34
36
  "@aws-sdk/client-iot": "3.208.0",
35
37
  "@aws-sdk/client-iot-data-plane": "3.208.0",
36
38
  "@aws-sdk/client-lambda": "3.208.0",
@@ -0,0 +1 @@
1
+ export declare const useRustHandler: () => Promise<void>;
@@ -0,0 +1,96 @@
1
+ import path from "path";
2
+ import fs from "fs/promises";
3
+ import { useRuntimeHandlers } from "../handlers.js";
4
+ import { useRuntimeWorkers } from "../workers.js";
5
+ import { Context } from "../../context/context.js";
6
+ import { VisibleError } from "../../error.js";
7
+ import { exec, spawn } from "child_process";
8
+ import { promisify } from "util";
9
+ import { useRuntimeServerConfig } from "../server.js";
10
+ import { findAbove, isChild } from "../../util/fs.js";
11
+ const execAsync = promisify(exec);
12
+ export const useRustHandler = Context.memo(async () => {
13
+ const workers = await useRuntimeWorkers();
14
+ const server = await useRuntimeServerConfig();
15
+ const handlers = useRuntimeHandlers();
16
+ const processes = new Map();
17
+ const sources = new Map();
18
+ const handlerName = process.platform === "win32" ? `handler.exe` : `handler`;
19
+ handlers.register({
20
+ shouldBuild: (input) => {
21
+ if (!input.file.endsWith(".rs"))
22
+ return false;
23
+ const parent = sources.get(input.functionID);
24
+ if (!parent)
25
+ return false;
26
+ const result = isChild(parent, input.file);
27
+ return result;
28
+ },
29
+ canHandle: (input) => input.startsWith("rust"),
30
+ startWorker: async (input) => {
31
+ const proc = spawn(path.join(input.out, handlerName), {
32
+ env: {
33
+ ...process.env,
34
+ ...input.environment,
35
+ IS_LOCAL: "true",
36
+ RUST_BACKTRACE: "1",
37
+ AWS_LAMBDA_RUNTIME_API: `http://localhost:${server.port}/${input.workerID}`,
38
+ AWS_LAMBDA_FUNCTION_MEMORY_SIZE: "1024",
39
+ },
40
+ cwd: input.out,
41
+ });
42
+ proc.on("exit", () => workers.exited(input.workerID));
43
+ proc.stdout.on("data", (data) => {
44
+ workers.stdout(input.workerID, data.toString());
45
+ });
46
+ proc.stderr.on("data", (data) => {
47
+ workers.stdout(input.workerID, data.toString());
48
+ });
49
+ processes.set(input.workerID, proc);
50
+ },
51
+ stopWorker: async (workerID) => {
52
+ const proc = processes.get(workerID);
53
+ if (proc) {
54
+ proc.kill();
55
+ processes.delete(workerID);
56
+ }
57
+ },
58
+ build: async (input) => {
59
+ const parsed = path.parse(input.props.handler);
60
+ const project = await findAbove(parsed.dir, "Cargo.toml");
61
+ sources.set(input.functionID, project);
62
+ if (input.mode === "start") {
63
+ try {
64
+ await execAsync(`cargo build --bin ${parsed.name}`, {
65
+ cwd: project,
66
+ env: {
67
+ ...process.env,
68
+ },
69
+ });
70
+ await fs.cp(path.join(project, `target/debug`, parsed.name), path.join(input.out, "handler"));
71
+ }
72
+ catch (ex) {
73
+ throw new VisibleError("Failed to build");
74
+ }
75
+ }
76
+ if (input.mode === "deploy") {
77
+ try {
78
+ await execAsync(`cargo lambda build --release --bin ${parsed.name}`, {
79
+ cwd: project,
80
+ env: {
81
+ ...process.env,
82
+ },
83
+ });
84
+ await fs.cp(path.join(project, `target/lambda/`, parsed.name, "bootstrap"), path.join(input.out, "bootstrap"));
85
+ }
86
+ catch (ex) {
87
+ throw new VisibleError("Failed to build");
88
+ }
89
+ }
90
+ return {
91
+ type: "success",
92
+ handler: "handler",
93
+ };
94
+ },
95
+ });
96
+ });
package/runtime/server.js CHANGED
@@ -74,15 +74,22 @@ export const useRuntimeServer = Context.memo(async () => {
74
74
  "Lambda-Runtime-Deadline-Ms": payload.deadline,
75
75
  "Lambda-Runtime-Invoked-Function-Arn": payload.context.invokedFunctionArn,
76
76
  "Lambda-Runtime-Client-Context": JSON.stringify(payload.context.clientContext || {}),
77
- "Lambda-Runtime-Cognito-Identity": JSON.stringify(payload.context.identity || {}),
77
+ ...(payload.context.identity
78
+ ? {
79
+ "Lambda-Runtime-Cognito-Identity": JSON.stringify(payload.context.identity),
80
+ }
81
+ : {}),
78
82
  });
79
83
  res.json(payload.event);
80
84
  });
81
85
  app.post(`/:workerID/${cfg.API_VERSION}/runtime/invocation/:awsRequestId/response`, express.json({
82
86
  strict: false,
83
- type: ["application/json", "application/*+json"],
87
+ type() {
88
+ return true;
89
+ },
84
90
  limit: "10mb",
85
91
  }), (req, res) => {
92
+ console.log(JSON.stringify(req.body, null, 4));
86
93
  Logger.debug("Worker", req.params.workerID, "got response", req.body);
87
94
  const worker = workers.fromID(req.params.workerID);
88
95
  bus.publish("function.success", {