Skip to content

Commit 1ad29d1

Browse files
fix: Move running CDK code in worker
1 parent 024ec35 commit 1ad29d1

File tree

9 files changed

+139
-47
lines changed

9 files changed

+139
-47
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
"scripts": {
4040
"typecheck": "tsc --noEmit -p tsconfig.typecheck.json && npx tsc --noEmit -p src/extension/tsconfig.json",
4141
"add-bang": "sed -i '1s|^|#!/usr/bin/env node\\n|' ./dist/lldebugger.mjs",
42-
"build": "tsc -p tsconfig.build.json && cp src/nodeWorkerRunner.mjs dist && node fix-imports.js && npm run add-bang && npm run bundle-extension",
42+
"build": "tsc -p tsconfig.build.json && cp src/nodeWorkerRunner.mjs dist && cp src/frameworks/cdkFrameworkWorker.mjs dist/frameworks && node fix-imports.js && npm run add-bang && npm run bundle-extension",
4343
"bundle-extension": "cd src/extension && npm run build && cd ../../",
4444
"deploy-github-role": "aws cloudformation deploy --stack-name lld-deploy-role --template-file cloudformation/gitHubDeployRole.yaml --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM --profile lldebugger",
4545
"deploy-tests": "npm run deploy --workspaces",

src/configuration/getConfigFromCliArgs.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ export async function getConfigFromCliArgs(
2020
program.name("lld").description("Lambda Live Debugger").version(version);
2121
program.option(
2222
"-r, --remove [option]",
23-
"Remove Lambda Live Debugger infrastructure. Options: 'keep-layer' (default), 'remove-all'. The lates also remove the Lambda Layer"
23+
"Remove Lambda Live Debugger infrastructure. Options: 'keep-layer' (default), 'remove-all'. The latest also removes the Lambda Layer"
2424
//validateRemoveOption,
2525
//"keep-layer"
2626
);
2727
program.option(
2828
"-w, --wizard",
29-
"Program interactively asks for each parameter and save it to lldebugger.config.ts"
29+
"Program interactively asks for each parameter and saves it to lldebugger.config.ts"
3030
);
3131
program.option("-v, --verbose", "Verbose logs");
3232
program.option(
@@ -38,7 +38,7 @@ export async function getConfigFromCliArgs(
3838
program.option("-s, --stage <stage>", "Serverless Framework stage");
3939
program.option(
4040
"-f, --function <function name>",
41-
"Filter by function name. You can use * as wildcard"
41+
"Filter by function name. You can use * as a wildcard"
4242
);
4343
program.option("-m, --subfolder <subfolder>", "Monorepo subfolder");
4444
program.option("-o, --observable", "Observable mode");

src/frameworks/cdkFramework.ts

Lines changed: 39 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,15 @@ import * as fs from "fs/promises";
33
import * as path from "path";
44
import { BundlingType, LambdaResource } from "../types/resourcesDiscovery.js";
55
import { outputFolder } from "../constants.js";
6-
import type { BundlingOptions } from "aws-cdk-lib/aws-lambda-nodejs";
76
import { findPackageJson } from "../utils/findPackageJson.js";
87
import { IFramework } from "./iFrameworks.js";
98
import { CloudFormation } from "../cloudFormation.js";
109
import { AwsConfiguration } from "../types/awsConfiguration.js";
1110
import { LldConfigBase } from "../types/lldConfig.js";
1211
import { Logger } from "../logger.js";
13-
14-
// this is global variable to store the data from the CDK code once it is executed
15-
declare global {
16-
var lambdas: Array<{
17-
code: any;
18-
node: any;
19-
//cdkPath: string;
20-
stackName: string;
21-
codePath: string;
22-
handler: string;
23-
bundling: BundlingOptions;
24-
}>;
25-
}
12+
import { Worker } from "node:worker_threads";
13+
import { getModuleDirname } from "../getDirname.js";
14+
import { Configuration } from "../configuration.js";
2615

2716
/**
2817
* Support for AWS CDK framework
@@ -219,10 +208,6 @@ export class CdkFramework implements IFramework {
219208
config: LldConfigBase
220209
) {
221210
const entryFile = await this.getCdkEntryFile(cdkConfigPath);
222-
223-
// this is global variable to store the data from the CDK code once it is executed
224-
global.lambdas = [];
225-
226211
// Define a plugin to prepend custom code to .ts or .tsx files
227212
const injectCodePlugin: esbuild.Plugin = {
228213
name: "injectCode",
@@ -319,30 +304,49 @@ export class CdkFramework implements IFramework {
319304
process.env.CDK_CONTEXT_JSON = JSON.stringify(CDK_CONTEXT_JSON);
320305
Logger.verbose(`[CDK] context:`, JSON.stringify(CDK_CONTEXT_JSON, null, 2));
321306

322-
// execute code to get the data into global.lambdas
323-
const codeFile = await fs.readFile(compileOutput, "utf8");
324-
//const __dirname = path.resolve("x/"); // CDK needs this, pure magic
325-
const __dirname = path.resolve("./node_modules/aws-cdk-lib/x/x"); // CDK needs this, pure magic
326-
eval(codeFile);
307+
const lambdas: any[] = await new Promise((resolve, reject) => {
308+
const worker = new Worker(
309+
path.resolve(
310+
path.join(getModuleDirname(), "frameworks/cdkFrameworkWorker.mjs")
311+
),
312+
{
313+
workerData: {
314+
verbose: Configuration.config.verbose,
315+
},
316+
}
317+
);
327318

328-
if (global.lambdas.length === 0) {
329-
throw new Error("No Lambda functions found in the CDK code");
330-
}
319+
worker.on("message", (message) => {
320+
resolve(message);
321+
worker.terminate();
322+
});
323+
324+
worker.on("error", (error) => {
325+
reject(
326+
new Error(`Error running CDK code in worker: ${error.message}`, {
327+
cause: error,
328+
})
329+
);
330+
});
331331

332-
const lambdasPrettified = global.lambdas.map((lambda: any) => ({
333-
stackName: lambda.stackName,
334-
codePath: lambda.codePath,
335-
handler: lambda.handler,
336-
bundling: lambda.bundling,
337-
}));
332+
worker.on("exit", (code) => {
333+
if (code !== 0) {
334+
reject(new Error(`CDK worker stopped with exit code ${code}`));
335+
}
336+
});
337+
338+
worker.postMessage({
339+
compileOutput,
340+
});
341+
});
338342

339343
Logger.verbose(
340344
`[CDK] Found the following Lambda functions in the CDK code:`,
341-
JSON.stringify(lambdasPrettified, null, 2)
345+
JSON.stringify(lambdas, null, 2)
342346
);
343347

344348
const list = await Promise.all(
345-
global.lambdas.map(async (lambda: any) => {
349+
lambdas.map(async (lambda: any) => {
346350
// handler slit into file and file name
347351
const handlerSplit = lambda.handler.split(".");
348352

@@ -375,7 +379,7 @@ export class CdkFramework implements IFramework {
375379
Logger.verbose(`[CDK] package.json path: ${packageJsonPath}`);
376380

377381
return {
378-
cdkPath: lambda.node.defaultChild.node.path,
382+
cdkPath: lambda.cdkPath,
379383
stackName: lambda.stackName,
380384
packageJsonPath,
381385
codePath,

src/frameworks/cdkFrameworkWorker.mjs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { createRequire as topLevelCreateRequire } from "module";
2+
const require = topLevelCreateRequire(import.meta.url);
3+
4+
import { workerData, parentPort } from "node:worker_threads";
5+
import fs from "fs/promises";
6+
import path from "path";
7+
8+
import { Logger } from "../logger.mjs";
9+
10+
Logger.setVerbose(workerData.verbose);
11+
Logger.verbose(`[CDK] [Worker] Started`);
12+
13+
parentPort.on("message", async (data) => {
14+
// this is global variable to store the data from the CDK code once it is executed
15+
global.lambdas = [];
16+
17+
Logger.verbose(`[Worker ${workerData.workerId}] Received message`, data);
18+
19+
// execute code to get the data into global.lambdas
20+
const codeFile = await fs.readFile(data.compileOutput, "utf8");
21+
const __dirname = path.resolve("./node_modules/aws-cdk-lib/x/x"); // CDK needs this, pure magic
22+
eval(codeFile);
23+
24+
if (global.lambdas.length === 0) {
25+
throw new Error("No Lambda functions found in the CDK code");
26+
}
27+
28+
const lambdas = global.lambdas.map((lambda) => ({
29+
handler: lambda.handler,
30+
stackName: lambda.stackName,
31+
codePath: lambda.codePath,
32+
code: {
33+
path: lambda.code?.path,
34+
},
35+
cdkPath: lambda.node.defaultChild.node.path,
36+
bundling: lambda.bundling,
37+
}));
38+
39+
try {
40+
Logger.verbose(
41+
`[CDK] [Worker] Sending found lambdas`,
42+
JSON.stringify(lambdas, null, 2)
43+
);
44+
parentPort.postMessage(lambdas);
45+
} catch (error) {
46+
handleError(error);
47+
}
48+
});
49+
50+
process.on("unhandledRejection", (error) => {
51+
Logger.error(`[CDK] [Worker] Unhandled Rejection`, error);
52+
handleError(error);
53+
});
54+
55+
function handleError(error) {
56+
parentPort.postMessage({
57+
errorType: error.name ?? "Error",
58+
errorMessage: error.message,
59+
trace: error.stack,
60+
});
61+
}

src/ioTService.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type {
55
AwsCredentialIdentityProvider,
66
AwsCredentialIdentity,
77
} from "@smithy/types";
8+
import { Logger } from "./logger.js";
89

910
let device: iot.device;
1011

@@ -134,24 +135,24 @@ async function connect(props?: {
134135

135136
if (props?.topic) {
136137
device.subscribe(props.topic, { qos: 1 });
137-
console.debug("Subscribed to topic ", props.topic);
138+
Logger.verbose("[IoT] Subscribed to topic ", props.topic);
138139
}
139140

140141
device.on("connect", () => {
141-
console.debug("IoT connected");
142+
Logger.verbose("[IoT] Connected");
142143
connectedPromiseResolve();
143144
});
144145

145146
device.on("error", (err) => {
146-
console.debug("IoT error", err);
147+
Logger.error("[IoT] Error", err);
147148
});
148149

149150
device.on("close", () => {
150-
console.debug("IoT closed");
151+
Logger.verbose("[IoT] Closed");
151152
});
152153

153154
device.on("reconnect", () => {
154-
console.debug("IoT reconnecting...");
155+
Logger.verbose("[IoT] Reconnecting...");
155156
});
156157

157158
if (props?.onMessage) {
@@ -183,6 +184,8 @@ async function connect(props?: {
183184
device.on("message", messageReceived);
184185
}
185186

187+
await connectedPromise;
188+
186189
return {
187190
publish: async (payload, topic) => {
188191
await connectedPromise;

src/lldebugger.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ async function run() {
9797
const rootFolderForWarchingChanges = getRootFolder(folders);
9898
FileWatcher.watchForFileChanges(rootFolderForWarchingChanges);
9999

100-
LambdaConnection.connect();
100+
await LambdaConnection.connect();
101101
Logger.log("Debugger started!");
102102
}
103103

src/logger.mjs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
let verboseEnabled = false;
2+
/**
3+
* Log verbose message in verbose logging is enabled
4+
* @param args
5+
*/
6+
function verbose(...args) {
7+
if (verboseEnabled) {
8+
console.info(...args);
9+
}
10+
}
11+
/**
12+
*
13+
* @param enabled
14+
*/
15+
function setVerbose(enabled) {
16+
verboseEnabled = enabled;
17+
}
18+
export const Logger = {
19+
log: console.log,
20+
error: console.error,
21+
warn: console.warn,
22+
verbose,
23+
setVerbose,
24+
};

src/nodeWorkerRunner.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { createRequire as topLevelCreateRequire } from "module";
22
const require = topLevelCreateRequire(import.meta.url);
33

44
import { workerData, parentPort } from "node:worker_threads";
5-
import { Logger } from "../dist/logger.mjs";
5+
import { Logger } from "./logger.mjs";
66

77
Logger.setVerbose(workerData.verbose);
88
Logger.verbose(

test/utils/startDebugger.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ async function startDebuggerInternal(folder: string, args: string[] = []) {
4646
lldProcess.stdout?.on("data", (data) => {
4747
console.log("LLD: " + data.toString());
4848
const line = data.toString();
49-
if (line.includes("IoT connected")) {
49+
if (line.includes("Debugger started!")) {
5050
resolve(true);
5151
}
5252
});

0 commit comments

Comments
 (0)