diff --git a/src/infraDeploy.ts b/src/infraDeploy.ts index 74ee338..57f0e07 100755 --- a/src/infraDeploy.ts +++ b/src/infraDeploy.ts @@ -483,7 +483,7 @@ export async function deployInfrastructure() { await Promise.all(promises); } -export async function destroyInfrastructure() { +export async function removeInfrastructure() { const promises: Promise[] = []; promises.push(deleteLayer()); diff --git a/src/lldebugger.ts b/src/lldebugger.ts index 3981ae4..f705bf7 100755 --- a/src/lldebugger.ts +++ b/src/lldebugger.ts @@ -29,7 +29,7 @@ export let resources: ResourcesToDebug; let debuggerId: string; export let argOptions: { - destroy?: boolean; + remove?: boolean; verbose?: boolean; context?: string; profile?: string; @@ -43,7 +43,7 @@ async function run() { program.name("lld").description("Lambda Live Debugger").version(version); - program.option("-d, --destroy", "Destroy the infrastructure"); + program.option("-r, --remove", "Remove Lambda Live Debugger infrastructure"); program.option("-v, --verbose", "Verbose logs"); program.option("-c, --context ", "AWS CDK context"); program.option("--profile ", "AWS profile to use"); @@ -72,9 +72,9 @@ async function run() { ); console.log("ARGUMENTS", argOptions); - if (argOptions.destroy) { - console.log("Destroying infrastructure..."); - await InfraDeploy.destroyInfrastructure(); + if (argOptions.remove) { + console.log("Removing Lambda Live Debugger infrastructure..."); + await InfraDeploy.removeInfrastructure(); return; } diff --git a/test/cdk-simple.test.ts b/test/cdk-simple.test.ts index d1e59a7..b9f9e69 100644 --- a/test/cdk-simple.test.ts +++ b/test/cdk-simple.test.ts @@ -1,69 +1,32 @@ import { expect, test, describe, beforeAll, afterAll } from "vitest"; -import { ChildProcess, exec, execSync, spawn } from "child_process"; -import { IAMClient, GetRolePolicyCommand } from "@aws-sdk/client-iam"; -import { promisify } from "util"; +import { ChildProcess } from "child_process"; import fs from "fs/promises"; -import { - LambdaClient, - GetFunctionCommand, - InvokeCommand, -} from "@aws-sdk/client-lambda"; -import { setTimeout } from "timers/promises"; +import { LambdaClient, InvokeCommand } from "@aws-sdk/client-lambda"; +import { startDebugger } from "./utils/startDebugger"; +import { expectInfraIsRemoved } from "./utils/expectInfraIsRemoved"; +import { expectInfraIsDeployed } from "./utils/expectInfraIsDeployed"; +import { removeInfra } from "./utils/removeInfra"; +import { exec } from "child_process"; +import { promisify } from "util"; -const execAsync = promisify(exec); +export const execAsync = promisify(exec); describe("cdk-simple", () => { - const sampleFolder = "test/cdk-simple"; + const folder = "test/cdk-simple"; let lldProcess: ChildProcess | undefined; beforeAll(async () => { if (process.env.CI === "true" || process.env.RUN_TEST_FROM_CLI === "true") { if (process.env.CI === "true") { await execAsync("npm run destroy", { - cwd: sampleFolder, + cwd: folder, }); } await execAsync("npm run deploy", { - cwd: sampleFolder, - }); - - console.log("Starting LLD..."); - - lldProcess = spawn("node ../../dist/lldebugger.js -c=environment=test", { - cwd: sampleFolder, - shell: true, + cwd: folder, }); - - // wait for the debugger to start - - await new Promise((resolve, reject) => { - let errorWhileRunning = false; - if (!lldProcess) { - throw new Error("Failed to start LLD"); - } - - lldProcess.stdout?.on("data", (data) => { - console.log("LLD: " + data.toString()); - const line = data.toString(); - if (line.includes("IoT connected")) { - resolve(true); - } - }); - lldProcess.stderr?.on("data", (data) => { - console.log("LLD ERROR: " + data.toString()); - errorWhileRunning = true; - }); - lldProcess.on("close", (error) => { - console.log(`LLD CLOSED: error=${errorWhileRunning}`); - if (error) { - reject(error); - } else { - resolve(true); - } - }); - }); - await setTimeout(5000); + lldProcess = await startDebugger(folder); } }); @@ -73,46 +36,18 @@ describe("cdk-simple", () => { }); test("check infra", async () => { - if (process.env.CI === "true" || process.env.RUN_TEST_FROM_CLI === "true") { - const lambdaName = await getTestFunctionName(sampleFolder); - const lambdaConfiguration = await getFuntion(lambdaName); - const roleArn = lambdaConfiguration.Configuration?.Role; - const policyDocument = await getPolicyDocument(roleArn); - - expect( - lambdaConfiguration.Configuration?.Environment?.Variables - ).toMatchObject({ - AWS_LAMBDA_EXEC_WRAPPER: "/opt/lld-wrapper", - LLD_DEBUGGER_ID: expect.any(String), - LLD_FUNCTION_ID: expect.any(String), - LLD_INITIAL_TIMEOUT: "3", - NODE_OPTIONS: "--enable-source-maps", - }); - - expect(lambdaConfiguration.Configuration?.Layers?.length).toEqual(1); - - expect(lambdaConfiguration.Configuration?.Layers![0].Arn).toContain( - ":layer:LambdaLiveDebugger:" - ); - expect(policyDocument).toEqual({ - Statement: [ - { - Action: "iot:*", - Effect: "Allow", - Resource: "*", - }, - ], - Version: "2012-10-17", - }); - } + const lambdaName = await getCdkFunctionName( + folder, + "FunctionNameTestTsCommonJs" + ); + await expectInfraIsDeployed(lambdaName); }); test("call Lambda - testTsCommon", async () => { - const cdkOutputs = JSON.parse( - await fs.readFile(`${sampleFolder}/cdk-outputs.json`, "utf-8") + const lambdaName = await getCdkFunctionName( + folder, + "FunctionNameTestTsCommonJs" ); - const lambdaName = - cdkOutputs["test-lld-cdk-simple"].FunctionNameTestTsCommonJs; const now = new Date().toISOString(); @@ -136,11 +71,10 @@ describe("cdk-simple", () => { }); test("call Lambda - testTsEsModule", async () => { - const cdkOutputs = JSON.parse( - await fs.readFile(`${sampleFolder}/cdk-outputs.json`, "utf-8") + const lambdaName = await getCdkFunctionName( + folder, + "FunctionNameTestTsEsModule" ); - const lambdaName = - cdkOutputs["test-lld-cdk-simple"].FunctionNameTestTsEsModule; const now = new Date().toISOString(); @@ -164,11 +98,10 @@ describe("cdk-simple", () => { }); test("call Lambda - testJsCommonJs", async () => { - const cdkOutputs = JSON.parse( - await fs.readFile(`${sampleFolder}/cdk-outputs.json`, "utf-8") + const lambdaName = await getCdkFunctionName( + folder, + "FunctionNameTestJsCommonJs" ); - const lambdaName = - cdkOutputs["test-lld-cdk-simple"].FunctionNameTestJsCommonJs; const now = new Date().toISOString(); @@ -192,11 +125,10 @@ describe("cdk-simple", () => { }); test("call Lambda - testJsEsModule", async () => { - const cdkOutputs = JSON.parse( - await fs.readFile(`${sampleFolder}/cdk-outputs.json`, "utf-8") + const lambdaName = await getCdkFunctionName( + folder, + "FunctionNameTestJsEsModule" ); - const lambdaName = - cdkOutputs["test-lld-cdk-simple"].FunctionNameTestJsEsModule; const now = new Date().toISOString(); @@ -220,11 +152,10 @@ describe("cdk-simple", () => { }); test("call Lambda - testJsCommonJsBase", async () => { - const cdkOutputs = JSON.parse( - await fs.readFile(`${sampleFolder}/cdk-outputs.json`, "utf-8") + const lambdaName = await getCdkFunctionName( + folder, + "FunctionNameTestJsCommonJsBase" ); - const lambdaName = - cdkOutputs["test-lld-cdk-simple"].FunctionNameTestJsCommonJsBase; const now = new Date().toISOString(); @@ -247,13 +178,11 @@ describe("cdk-simple", () => { expect(response.runningLocaly).toEqual("true"); }); - //testJsEsModuleBase test("call Lambda - testJsEsModuleBase", async () => { - const cdkOutputs = JSON.parse( - await fs.readFile(`${sampleFolder}/cdk-outputs.json`, "utf-8") + const lambdaName = await getCdkFunctionName( + folder, + "FunctionNameTestJsEsModuleBase" ); - const lambdaName = - cdkOutputs["test-lld-cdk-simple"].FunctionNameTestJsEsModuleBase; const now = new Date().toISOString(); @@ -276,82 +205,23 @@ describe("cdk-simple", () => { expect(response.runningLocaly).toEqual("true"); }); - test("destroy infra", async () => { + test("remove infra", async () => { if (process.env.CI === "true" || process.env.RUN_TEST_FROM_CLI === "true") { - lldProcess?.kill(); - - await execSync("node ../../dist/lldebugger.js --destroy", { - cwd: sampleFolder, - }); - - await setTimeout(6000); - - const lambdaName = await getTestFunctionName(sampleFolder); - const lambdaConfiguration = await getFuntion(lambdaName); - const roleArn = lambdaConfiguration.Configuration?.Role; - const policyDocument = await getPolicyDocument(roleArn); - - expect(lambdaConfiguration.Configuration?.Environment) - .toMatchInlineSnapshot(` - { - "Variables": { - "AWS_NODEJS_CONNECTION_REUSE_ENABLED": "1", - }, - } - `); - expect(lambdaConfiguration.Configuration?.Layers).toBeUndefined(); - expect(policyDocument).toBeUndefined(); + await removeInfra(lldProcess, folder); + const lambdaName = await getCdkFunctionName( + folder, + "FunctionNameTestTsCommonJs" + ); + await expectInfraIsRemoved(lambdaName); } }); }); -async function getTestFunctionName(sampleFolder: string) { +export async function getCdkFunctionName(folder: string, functionName: string) { // read lambda configuration using LambdaClient const cdkOutputs = JSON.parse( - await fs.readFile(`${sampleFolder}/cdk-outputs.json`, "utf-8") + await fs.readFile(`${folder}/cdk-outputs.json`, "utf-8") ); - const lambdaName = - cdkOutputs["test-lld-cdk-simple"].FunctionNameTestTsCommonJs; + const lambdaName = cdkOutputs["test-lld-cdk-simple"][functionName]; return lambdaName; } - -const iamClient = new IAMClient({}); - -async function getPolicyDocument(roleArn: string | undefined) { - try { - const roleName = roleArn!.split("/").pop(); - - const policy = await iamClient.send( - new GetRolePolicyCommand({ - RoleName: roleName, - PolicyName: "LambdaLiveDebuggerPolicy", - }) - ); - - if (policy.PolicyDocument) { - const policyDocument = JSON.parse( - decodeURIComponent(policy.PolicyDocument) - ); - return policyDocument; - } else { - return undefined; - } - } catch (error: any) { - if (error.name === "NoSuchEntityException") { - return undefined; - } else { - throw error; - } - } -} - -const lambdaClient = new LambdaClient({}); - -async function getFuntion(lambdaName: any) { - const lambdaConfiguration = await lambdaClient.send( - new GetFunctionCommand({ - FunctionName: lambdaName, - }) - ); - return lambdaConfiguration; -} diff --git a/test/utils/expectInfraIsDeployed.ts b/test/utils/expectInfraIsDeployed.ts new file mode 100644 index 0000000..1d1cf7c --- /dev/null +++ b/test/utils/expectInfraIsDeployed.ts @@ -0,0 +1,37 @@ +import { expect } from "vitest"; +import { getFuntionConfiguration } from "./getFuntionConfiguration"; +import { getPolicyDocument } from "./getPolicyDocument"; + +export async function expectInfraIsDeployed(lambdaName: any) { + if (process.env.CI === "true" || process.env.RUN_TEST_FROM_CLI === "true") { + const lambdaConfiguration = await getFuntionConfiguration(lambdaName); + const roleArn = lambdaConfiguration.Configuration?.Role; + const policyDocument = await getPolicyDocument(roleArn); + + expect( + lambdaConfiguration.Configuration?.Environment?.Variables + ).toMatchObject({ + AWS_LAMBDA_EXEC_WRAPPER: "/opt/lld-wrapper", + LLD_DEBUGGER_ID: expect.any(String), + LLD_FUNCTION_ID: expect.any(String), + LLD_INITIAL_TIMEOUT: "3", + NODE_OPTIONS: "--enable-source-maps", + }); + + expect(lambdaConfiguration.Configuration?.Layers?.length).toEqual(1); + + expect(lambdaConfiguration.Configuration?.Layers![0].Arn).toContain( + ":layer:LambdaLiveDebugger:" + ); + expect(policyDocument).toEqual({ + Statement: [ + { + Action: "iot:*", + Effect: "Allow", + Resource: "*", + }, + ], + Version: "2012-10-17", + }); + } +} diff --git a/test/utils/expectInfraIsRemoved.ts b/test/utils/expectInfraIsRemoved.ts new file mode 100644 index 0000000..3e6efc6 --- /dev/null +++ b/test/utils/expectInfraIsRemoved.ts @@ -0,0 +1,19 @@ +import { expect } from "vitest"; +import { getFuntionConfiguration } from "./getFuntionConfiguration"; +import { getPolicyDocument } from "./getPolicyDocument"; + +export async function expectInfraIsRemoved(fuctionName: string) { + const lambdaConfiguration = await getFuntionConfiguration(fuctionName); + const roleArn = lambdaConfiguration.Configuration?.Role; + const policyDocument = await getPolicyDocument(roleArn); + + expect(lambdaConfiguration.Configuration?.Environment).toMatchInlineSnapshot(` + { + "Variables": { + "AWS_NODEJS_CONNECTION_REUSE_ENABLED": "1", + }, + } + `); + expect(lambdaConfiguration.Configuration?.Layers).toBeUndefined(); + expect(policyDocument).toBeUndefined(); +} diff --git a/test/utils/getFuntionConfiguration.ts b/test/utils/getFuntionConfiguration.ts new file mode 100644 index 0000000..4eb851f --- /dev/null +++ b/test/utils/getFuntionConfiguration.ts @@ -0,0 +1,12 @@ +import { GetFunctionCommand, LambdaClient } from "@aws-sdk/client-lambda"; + +export const lambdaClient = new LambdaClient({}); + +export async function getFuntionConfiguration(lambdaName: string) { + const lambdaConfiguration = await lambdaClient.send( + new GetFunctionCommand({ + FunctionName: lambdaName, + }) + ); + return lambdaConfiguration; +} diff --git a/test/utils/getPolicyDocument.ts b/test/utils/getPolicyDocument.ts new file mode 100644 index 0000000..95aaa23 --- /dev/null +++ b/test/utils/getPolicyDocument.ts @@ -0,0 +1,31 @@ +import { GetRolePolicyCommand, IAMClient } from "@aws-sdk/client-iam"; + +export const iamClient = new IAMClient({}); + +export async function getPolicyDocument(roleArn: string | undefined) { + try { + const roleName = roleArn!.split("/").pop(); + + const policy = await iamClient.send( + new GetRolePolicyCommand({ + RoleName: roleName, + PolicyName: "LambdaLiveDebuggerPolicy", + }) + ); + + if (policy.PolicyDocument) { + const policyDocument = JSON.parse( + decodeURIComponent(policy.PolicyDocument) + ); + return policyDocument; + } else { + return undefined; + } + } catch (error: any) { + if (error.name === "NoSuchEntityException") { + return undefined; + } else { + throw error; + } + } +} diff --git a/test/utils/removeInfra.ts b/test/utils/removeInfra.ts new file mode 100644 index 0000000..7d8bc8b --- /dev/null +++ b/test/utils/removeInfra.ts @@ -0,0 +1,15 @@ +import { ChildProcess, execSync } from "child_process"; +import { setTimeout } from "timers/promises"; + +export async function removeInfra( + lldProcess: ChildProcess | undefined, + folder: string +) { + lldProcess?.kill(); + + await execSync("node ../../dist/lldebugger.js --remove", { + cwd: folder, + }); + + await setTimeout(5000); +} diff --git a/test/utils/startDebugger.ts b/test/utils/startDebugger.ts new file mode 100644 index 0000000..4a9b6aa --- /dev/null +++ b/test/utils/startDebugger.ts @@ -0,0 +1,48 @@ +import { spawn } from "child_process"; +import { setTimeout } from "timers/promises"; +import { exec } from "child_process"; +import { promisify } from "util"; + +export const execAsync = promisify(exec); + +export async function startDebugger(folder: string) { + console.log("Starting LLD..."); + + const lldProcess = spawn( + "node ../../dist/lldebugger.js -c=environment=test", + { + cwd: folder, + shell: true, + } + ); + + // wait for the debugger to start + await new Promise((resolve, reject) => { + let errorWhileRunning = false; + if (!lldProcess) { + throw new Error("Failed to start LLD"); + } + + lldProcess.stdout?.on("data", (data) => { + console.log("LLD: " + data.toString()); + const line = data.toString(); + if (line.includes("IoT connected")) { + resolve(true); + } + }); + lldProcess.stderr?.on("data", (data) => { + console.log("LLD ERROR: " + data.toString()); + errorWhileRunning = true; + }); + lldProcess.on("close", (error) => { + console.log(`LLD CLOSED: error=${errorWhileRunning}`); + if (error) { + reject(error); + } else { + resolve(true); + } + }); + }); + await setTimeout(5000); + return lldProcess; +}