lambda-live-debugger
Version:
Debug Lambda functions locally like it is running in the cloud
762 lines (761 loc) • 29.8 kB
JavaScript
import { DeleteLayerVersionCommand, LambdaClient, ListLayerVersionsCommand, PublishLayerVersionCommand, UpdateFunctionConfigurationCommand, GetFunctionCommand, ListLayersCommand, } from '@aws-sdk/client-lambda';
import { IAMClient, GetRolePolicyCommand, PutRolePolicyCommand, DeleteRolePolicyCommand, } from '@aws-sdk/client-iam';
import { getVersion } from './version.mjs';
import fs from 'fs/promises';
import * as path from 'path';
import { Configuration } from './configuration.mjs';
import { AwsCredentials } from './awsCredentials.mjs';
import { getModuleDirname } from './getDirname.mjs';
import { Logger } from './logger.mjs';
import * as crypto from 'crypto';
let lambdaClient;
let iamClient;
const inlinePolicyName = 'LambdaLiveDebuggerPolicy';
const layerName = 'LambdaLiveDebugger';
const lldWrapperPath = '/opt/lld-wrapper';
let layerDescription;
/**
* Policy document to attach to the Lambda role
*/
const policyDocument = {
Version: '2012-10-17',
Statement: [
{
Effect: 'Allow',
Action: [
'iot:DescribeEndpoint',
'iot:Connect',
'iot:Publish',
'iot:Subscribe',
'iot:Receive',
],
Resource: '*',
},
],
};
/**
* Get the Lambda client
* @returns
*/
function getLambdaClient() {
if (!lambdaClient) {
lambdaClient = new LambdaClient({
region: Configuration.config.region,
credentials: AwsCredentials.getCredentialsProvider({
region: Configuration.config.region,
profile: Configuration.config.profile,
role: Configuration.config.role,
}),
});
}
return lambdaClient;
}
/**
* Get the IAM client
* @returns
*/
function getIAMClient() {
if (!iamClient) {
iamClient = new IAMClient({
region: Configuration.config.region,
credentials: AwsCredentials.getCredentialsProvider({
region: Configuration.config.region,
profile: Configuration.config.profile,
role: Configuration.config.role,
}),
});
}
return iamClient;
}
/**
* Find an existing layer
* @returns
*/
async function findExistingLayerVersion() {
let nextMarker;
const layerDescription = await getLayerDescription();
do {
const listLayerVersionsCommand = new ListLayerVersionsCommand({
LayerName: layerName,
Marker: nextMarker,
});
const response = await getLambdaClient().send(listLayerVersionsCommand);
if (response.LayerVersions && response.LayerVersions.length > 0) {
const matchingLayer = response.LayerVersions.find((layer) => layer.Description === layerDescription);
if (matchingLayer) {
Logger.verbose(`Matching layer version: ${matchingLayer.Version}, description: ${matchingLayer.Description}`);
return matchingLayer;
}
}
nextMarker = response.NextMarker;
} while (nextMarker);
Logger.verbose(`No matching layer version found with description ${layerDescription}`);
return undefined;
}
/**
* Get the description of the Lambda layer that is set to the layer
* @returns
*/
async function getLayerDescription() {
if (!layerDescription) {
layerDescription = `Lambda Live Debugger layer version ${await getVersion()}`;
}
if ((await getVersion()) === '0.0.1') {
// add a random string to the description to make it unique
layerDescription = `Lambda Live Debugger layer - development ${crypto.randomUUID()}`;
}
return layerDescription;
}
/**
* Deploy the Lambda Layer
* @returns
*/
async function deployLayer() {
const layerDescription = await getLayerDescription();
// check the ZIP
let layerZipPathFullPath = path.resolve(path.join(getModuleDirname(), './extension/extension.zip'));
// get the full path to the ZIP file
try {
await fs.access(layerZipPathFullPath);
}
catch {
// if I am debugging
const layerZipPathFullPath2 = path.join(getModuleDirname(), '../dist/extension/extension.zip');
try {
await fs.access(layerZipPathFullPath2);
layerZipPathFullPath = layerZipPathFullPath2;
}
catch {
throw new Error(`File for the layer not found: ${layerZipPathFullPath}.`);
}
}
Logger.verbose(`Layer ZIP path: ${layerZipPathFullPath}`);
// Read the ZIP file containing your layer code
const layerContent = await fs.readFile(layerZipPathFullPath);
Logger.verbose(`Deploying ${layerDescription}`);
// Create the command for publishing a new layer version
const publishLayerVersionCommand = new PublishLayerVersionCommand({
LayerName: layerName,
Description: layerDescription,
Content: {
ZipFile: layerContent,
},
//CompatibleArchitectures: ['x86_64', 'arm64'],
//CompatibleRuntimes: ['nodejs18.x', 'nodejs20.x', 'nodejs22.x', 'nodejs24.x'],
});
const response = await getLambdaClient().send(publishLayerVersionCommand);
if (!response.LayerVersionArn) {
throw new Error('Failed to retrieve the layer version ARN.');
}
Logger.verbose(`Deployed ${response.Description} ARN: ${response.LayerVersionArn}`);
return response.LayerVersionArn;
}
/**
* Delete the Lambda layer
*/
async function deleteLayer() {
let nextMarker;
do {
const layers = await getLambdaClient().send(new ListLayersCommand({
Marker: nextMarker,
}));
// Filter layers by name
const targetLayers = layers.Layers?.filter((layer) => layer.LayerName === layerName) || [];
for (const layer of targetLayers) {
await deleteAllVersionsOfLayer(layer.LayerArn);
}
nextMarker = layers.NextMarker;
} while (nextMarker);
}
/**
* Delete all versions of a layer
* @param layerArn
*/
async function deleteAllVersionsOfLayer(layerArn) {
let nextMarker;
do {
const versions = await getLambdaClient().send(new ListLayerVersionsCommand({
LayerName: layerArn,
Marker: nextMarker,
}));
for (const version of versions.LayerVersions || []) {
await deleteLayerVersion(layerArn, version.Version);
}
nextMarker = versions.NextMarker;
} while (nextMarker);
}
/**
* Delete a specific version of a layer
* @param layerArn
* @param versionNumber
*/
async function deleteLayerVersion(layerArn, versionNumber) {
try {
Logger.verbose(`Deleting version ${versionNumber} of layer ${layerArn}`);
await getLambdaClient().send(new DeleteLayerVersionCommand({
LayerName: layerArn,
VersionNumber: versionNumber,
}));
}
catch (error) {
Logger.error(`Error deleting version ${versionNumber} of layer ${layerArn}:`, error);
throw error;
}
}
/**
* Get the role name from a Lambda function
* @param functionName
* @returns role name
*/
async function getRoleNameFromFunction(functionName) {
try {
Logger.verbose(`[Function ${functionName}] Getting role from function`);
const getFunctionResponse = await getLambdaClient().send(new GetFunctionCommand({
FunctionName: functionName,
}));
const roleArn = getFunctionResponse.Configuration?.Role;
if (!roleArn) {
throw new Error(`Failed to retrieve the role ARN for lambda ${functionName}.`);
}
// Extract the role name from the role ARN
const roleName = roleArn.split('/').pop();
if (!roleName) {
throw new Error(`Failed to extract role name from role ARN: ${roleArn} for lambda ${functionName}.`);
}
Logger.verbose(`[Function ${functionName}] Found role: ${roleName}`);
return roleName;
}
catch (error) {
throw new Error(`Failed to get role name from function ${functionName}.`, {
cause: error,
});
}
}
/**
* Check if policy needs to be removed from the Lambda role
* @param roleName
* @returns
*/
async function analyzeRoleRemove(roleName) {
try {
Logger.verbose(`[Role ${roleName}] Analyzing policy removal from Lambda role`);
const existingPolicy = await createPolicyDocument(roleName);
const needToRemovePolicy = !!existingPolicy;
Logger.verbose(`[Role ${roleName}] Policy ${needToRemovePolicy ? 'needs to be removed' : 'not found to remove'} from role ${roleName}`);
return {
needToRemovePolicy,
roleName,
};
}
catch (error) {
throw new Error(`Failed to analyze removal policy from role ${roleName}.`, {
cause: error,
});
}
}
/**
* Remove the policy from the Lambda role
* @param roleData
* @returns
*/
async function removePolicyFromLambdaRole(roleName) {
Logger.verbose(`[Role ${roleName}] Removing policy from the role`);
try {
await getIAMClient().send(new DeleteRolePolicyCommand({
RoleName: roleName,
PolicyName: inlinePolicyName,
}));
}
catch (error) {
throw new Error(`Failed to remove policy from the role ${roleName}.`, {
cause: error,
});
}
}
/**
* Create policy document needed to attach to the Lambda role needed for the Lambda Live Debugger
* @param roleName
* @returns
*/
async function createPolicyDocument(roleName) {
try {
Logger.verbose(`[Role ${roleName}] Checking for existing policy document`);
const policy = await getIAMClient().send(new GetRolePolicyCommand({
RoleName: roleName,
PolicyName: inlinePolicyName,
}));
if (policy.PolicyDocument) {
Logger.verbose(`[Role ${roleName}] Found existing policy document`);
const policyDocument = JSON.parse(decodeURIComponent(policy.PolicyDocument));
return policyDocument;
}
else {
Logger.verbose(`[Role ${roleName}] No policy document found`);
return undefined;
}
}
catch (error) {
if (error.name === 'NoSuchEntityException') {
Logger.verbose(`[Role ${roleName}] Policy does not exist`);
return undefined;
}
else {
throw new Error(`Failed to create policy document for role ${roleName}.`, { cause: error });
}
}
}
/**
* Deploy the infrastructure
*/
async function applyAddingInfra(changes) {
Logger.verbose('Starting infrastructure deployment for adding Lambda Live Debugger');
let layerVersionArn;
if (changes.deployLayer) {
Logger.verbose('Deploying new layer version');
layerVersionArn = await deployLayer();
}
else {
if (!changes.existingLayerVersionArn) {
throw new Error('Expected existing layer ARN but none provided.');
}
Logger.verbose(`Using existing layer version: ${changes.existingLayerVersionArn}`);
layerVersionArn = changes.existingLayerVersionArn;
}
const promises = [];
// Add LLD to functions
for (const lambdaData of changes.lambdasToAdd) {
promises.push(addLayerToLambda({
...lambdaData,
layers: [
layerVersionArn,
// remove LLD layer if exist
...lambdaData.layers.filter((arn) => !arn.includes(`:layer:${layerName}:`)),
],
}));
}
// Remove LLD from filtered functions
for (const lambdaData of changes.lambdasToRemove) {
promises.push(removeLayerFromLambda(lambdaData));
}
// Add policies to roles
for (const roleName of changes.rolesToAdd) {
promises.push(addPolicyToRole(roleName));
}
// Remove policies from roles
for (const roleName of changes.rolesToRemove) {
promises.push(removePolicyFromLambdaRole(roleName));
}
await Promise.all(promises);
}
/**
* Get the planned infrastructure changes including removal from filtered functions
*/
async function getInfraChangesForAdding() {
Logger.verbose('Analyzing infrastructure changes for adding Lambda Live Debugger');
const existingLayer = await findExistingLayerVersion();
const configLambdasAll = Configuration.getLambdasAll();
const configLambdasUpdate = configLambdasAll.filter((l) => !(l.filteredOut === true));
const configLambdasRemove = configLambdasAll.filter((l) => l.filteredOut === true);
const lambdasToUpdatePromise = Promise.all(configLambdasUpdate.map(async (func) => {
const lambdaUpdate = await analyzeLambdaAdd(func.functionName, existingLayer?.LayerVersionArn);
return lambdaUpdate;
}));
const lambdasToRemovePromise = Promise.all(configLambdasRemove.map(async (func) => {
return analyzeLambdaRemove(func.functionName);
}));
// Get all role names for lambdas to update, ensure uniqueness, then analyze
const roleNamesToAddSet = new Set();
const roleNamesToAddPromise = Promise.all(configLambdasUpdate.map(async (func) => {
const roleName = await getRoleNameFromFunction(func.functionName);
roleNamesToAddSet.add(roleName);
}));
// Get all role names for lambdas to remove, ensure uniqueness, then analyze
const roleNamesToRemoveSet = new Set();
const roleNamesToRemovePromise = Promise.all(configLambdasRemove.map(async (func) => {
const roleName = await getRoleNameFromFunction(func.functionName);
roleNamesToRemoveSet.add(roleName);
}));
// Analyze roles to add
await roleNamesToAddPromise;
const roleNamesToAdd = Array.from(roleNamesToAddSet);
const rolesToAddPromise = Promise.all(roleNamesToAdd.map(async (roleName) => {
const roleUpdate = await analyzeRoleAdd(roleName);
return roleUpdate.addPolicy ? roleUpdate.roleName : undefined;
}));
// Analyze roles to remove
await roleNamesToRemovePromise;
let roleNamesToRemove = Array.from(roleNamesToRemoveSet);
// make sure that roles removed are not in the list to add
roleNamesToRemove = roleNamesToRemove.filter((role) => !roleNamesToAdd.includes(role));
const rolesToRemovePromise = Promise.all(roleNamesToRemove.map(async (roleName) => {
const roleRemoval = await analyzeRoleRemove(roleName);
return roleRemoval.needToRemovePolicy ? roleRemoval.roleName : undefined;
}));
const lambdasToUpdate = await lambdasToUpdatePromise;
const lambdasToAddFiltered = lambdasToUpdate.filter((l) => l);
const rolesToAdd = await rolesToAddPromise;
const rolesToAddFiltered = [
...new Set(rolesToAdd.filter((r) => r)),
];
const lambdasToRemove = await lambdasToRemovePromise;
const lambdasToRemoveFiltered = lambdasToRemove.filter((l) => l);
const rolesToRemove = await rolesToRemovePromise;
const rolesToRemoveFiltered = rolesToRemove.filter((r) => r);
return {
deployLayer: !existingLayer,
existingLayerVersionArn: existingLayer?.LayerVersionArn,
lambdasToAdd: lambdasToAddFiltered,
rolesToAdd: rolesToAddFiltered,
lambdasToRemove: lambdasToRemoveFiltered,
rolesToRemove: rolesToRemoveFiltered,
};
}
/**
* Check what needs to be removed from a Lambda function
* @param func - Lambda function properties
* @returns Lambda update configuration or undefined if no update needed
*/
async function analyzeLambdaRemove(functionName) {
try {
const { environmentVariables, ddlLayerArns, otherLayerArns, initialTimeout, } = await getLambdaConfiguration(functionName);
const needToRemoveLayer = ddlLayerArns.length > 0;
let needToRemoveEnvironmentVariables = false;
if (needToRemoveLayer) {
Logger.verbose(`[Function ${functionName}] Lambda Live Debugger layer(s) detected: ${ddlLayerArns.join(', ')}. Marked for removal.`);
}
else {
Logger.verbose(`[Function ${functionName}] No Lambda Live Debugger layer(s) to remove.`);
}
const ddlEnvironmentVariables = getEnvironmentVariablesForDebugger({
// set dummy data, so we just get the list of environment variables
functionName: 'xxx',
timeout: 0,
verbose: true,
initialExecWrapper: 'test',
});
// check if environment variables are set for each property
for (const [key] of Object.entries(ddlEnvironmentVariables)) {
if (environmentVariables && environmentVariables[key]) {
needToRemoveEnvironmentVariables = true;
break;
}
}
Logger.verbose(`[Function ${functionName}] ${needToRemoveEnvironmentVariables ? 'Environment variables needed to be removed' : 'No environment variables to remove'}. Existing environment variables: ` +
JSON.stringify(environmentVariables, null, 2));
const needToRemove = needToRemoveLayer || needToRemoveEnvironmentVariables;
if (needToRemove) {
const initialExecWrapper = environmentVariables.LLD_INITIAL_AWS_LAMBDA_EXEC_WRAPPER;
const ddlEnvironmentVariables = getEnvironmentVariablesForDebugger({
functionName: 'xxx',
timeout: 0,
verbose: true,
initialExecWrapper: 'test',
});
// Remove LLD environment variables
const cleanedEnvironmentVariables = { ...environmentVariables };
for (const [key] of Object.entries(ddlEnvironmentVariables)) {
if (key === 'AWS_LAMBDA_EXEC_WRAPPER') {
if (cleanedEnvironmentVariables[key] === lldWrapperPath) {
delete cleanedEnvironmentVariables[key];
}
}
else {
delete cleanedEnvironmentVariables[key];
}
}
if (initialExecWrapper) {
cleanedEnvironmentVariables.AWS_LAMBDA_EXEC_WRAPPER =
initialExecWrapper;
}
return {
functionName,
layers: otherLayerArns,
environmentVariables: cleanedEnvironmentVariables,
timeout: initialTimeout,
};
}
return undefined;
}
catch (error) {
throw new Error(`Failed to analyze removal from lambda ${functionName}.`, {
cause: error,
});
}
}
/**
* Get the planned removal changes
*/
async function getInfraChangesForRemoving() {
Logger.verbose('Analyzing infrastructure changes for removing Lambda Live Debugger');
const allLambdas = Configuration.getLambdasAll();
const lambdasToRemovePromise = Promise.all(allLambdas.map(async (func) => {
return analyzeLambdaRemove(func.functionName);
}));
// Get all role names for lambdas to remove, ensure uniqueness, then analyze
const roleNamesToRemoveSet = new Set();
await Promise.all(allLambdas.map(async (func) => {
const roleName = await getRoleNameFromFunction(func.functionName);
roleNamesToRemoveSet.add(roleName);
}));
const roleNamesToRemove = Array.from(roleNamesToRemoveSet);
const rolesToRemovePromise = Promise.all(roleNamesToRemove.map(async (roleName) => {
const roleRemoval = await analyzeRoleRemove(roleName);
return roleRemoval.needToRemovePolicy ? roleRemoval.roleName : undefined;
}));
const lambdasToRemove = await lambdasToRemovePromise;
const lambdasToRemoveFiltered = lambdasToRemove.filter((l) => l);
const rolesToRemove = await rolesToRemovePromise;
const rolesToRemoveFiltered = rolesToRemove.filter((r) => r);
return {
lambdasToRemove: lambdasToRemoveFiltered,
rolesToRemove: rolesToRemoveFiltered,
};
}
/**
* Remove the infrastructure
*/
async function applyRemoveInfra(changes) {
Logger.verbose('Starting infrastructure removal');
const promises = [];
for (const lambdaData of changes.lambdasToRemove) {
promises.push(removeLayerFromLambda(lambdaData));
}
for (const roleName of changes.rolesToRemove) {
promises.push(removePolicyFromLambdaRole(roleName));
}
await Promise.all(promises);
}
/**
* Get the Lambda function configuration including layers, environment variables, and timeout
* @param functionName - The name of the Lambda function
* @returns Lambda configuration details
*/
async function getLambdaConfiguration(functionName) {
try {
const getFunctionResponse = await getLambdaClient().send(new GetFunctionCommand({
FunctionName: functionName,
}));
const timeout = getFunctionResponse.Configuration?.Timeout;
// get all layers this function has by name
const layers = getFunctionResponse.Configuration?.Layers || [];
const layerArns = layers.map((l) => l.Arn).filter((arn) => arn);
const ddlLayerArns = layerArns.filter((arn) => arn?.includes(`:layer:${layerName}:`));
const otherLayerArns = layerArns.filter((arn) => !arn?.includes(`:layer:${layerName}:`));
const environmentVariables = getFunctionResponse.Configuration?.Environment?.Variables ?? {};
let initialTimeout;
const initialTimeoutStr = environmentVariables?.LLD_INITIAL_TIMEOUT;
if (!initialTimeoutStr || isNaN(Number(initialTimeoutStr))) {
initialTimeout = timeout;
}
else {
initialTimeout = Number(initialTimeoutStr);
}
return {
environmentVariables,
ddlLayerArns,
otherLayerArns,
initialTimeout,
};
}
catch (error) {
throw new Error(`Failed to get lambda configuration ${functionName}.`, {
cause: error,
});
}
}
/**
* Attach the layer to the Lambda function and update the environment variables
* @param lambdaData
*/
async function addLayerToLambda(lambdaData) {
Logger.verbose(`[Function ${lambdaData.functionName}] Adding layer and environment variables`);
try {
await updateLambda(lambdaData);
}
catch (error) {
throw new Error(`Failed to update add layer to lambda ${lambdaData.functionName}.`, { cause: error });
}
}
/**
* Remove the layer from the Lambda function and update the environment variables
* @param lambdaData
*/
async function removeLayerFromLambda(lambdaData) {
Logger.verbose(`[Function ${lambdaData.functionName}] Removing layer and environment variables`);
try {
await updateLambda(lambdaData);
}
catch (error) {
throw new Error(`Failed to remove layer from lambda ${lambdaData.functionName}.`, { cause: error });
}
}
/**
* General function to update the Lambda function configuration
*/
async function updateLambda(lambdaData) {
const updateFunctionConfigurationCommand = new UpdateFunctionConfigurationCommand({
FunctionName: lambdaData.functionName,
Layers: lambdaData.layers,
Environment: {
Variables: lambdaData.environmentVariables,
},
Timeout: lambdaData.timeout,
});
await getLambdaClient().send(updateFunctionConfigurationCommand);
}
/**
* Analyze the Lambda function to determine if it needs to be updated
* @param func - Lambda function properties
* @param existingLayerVersionArn - ARN of existing layer version if available
* @returns Lambda update configuration or undefined if no update needed
*/
async function analyzeLambdaAdd(functionName, existingLayerVersionArn) {
const { environmentVariables, ddlLayerArns, otherLayerArns, initialTimeout } = await getLambdaConfiguration(functionName);
if (!existingLayerVersionArn) {
const ddlEnvironmentVariables = getEnvironmentVariablesForDebugger({
functionName,
timeout: initialTimeout,
verbose: Configuration.config.verbose,
initialExecWrapper: environmentVariables.AWS_LAMBDA_EXEC_WRAPPER !== lldWrapperPath
? environmentVariables.AWS_LAMBDA_EXEC_WRAPPER
: undefined,
});
Logger.verbose(`[Function ${functionName}] The layer for this version does not exist in the account. We need to add it and attach it to the function`);
return {
functionName,
layers: otherLayerArns,
environmentVariables: {
...environmentVariables,
...ddlEnvironmentVariables,
},
timeout: Math.max(initialTimeout, 300),
};
}
else {
let needToUpdateLayer = false;
// check if layer is already attached
if (!ddlLayerArns?.find((arn) => arn === existingLayerVersionArn)) {
needToUpdateLayer = true;
Logger.verbose(`[Function ${functionName}] Layer not attached to the function`);
}
else {
Logger.verbose(`[Function ${functionName}] Layer already attached to the function`);
}
// check if layers with the wrong version are attached
if (!needToUpdateLayer &&
ddlLayerArns.find((arn) => arn !== existingLayerVersionArn)) {
needToUpdateLayer = true;
Logger.verbose(`[Function ${functionName}] Layer with the wrong version attached to the function`);
}
// support for multiple internal Lambda extensions
const initialExecWrapper = environmentVariables.AWS_LAMBDA_EXEC_WRAPPER !== lldWrapperPath
? environmentVariables.AWS_LAMBDA_EXEC_WRAPPER
: undefined;
if (initialExecWrapper) {
Logger.warn(`[Function ${functionName}] Another internal Lambda extension is already attached to the function, which might cause unpredictable behavior.`);
}
const ddlEnvironmentVariables = getEnvironmentVariablesForDebugger({
functionName,
timeout: initialTimeout,
verbose: Configuration.config.verbose,
initialExecWrapper,
});
let needToUpdateEnvironmentVariables = false;
// check if environment variables are already set for each property
for (const [key, value] of Object.entries(ddlEnvironmentVariables)) {
if (!environmentVariables || environmentVariables[key] !== value) {
needToUpdateEnvironmentVariables = true;
break;
}
}
Logger.verbose(`[Function ${functionName}] ${needToUpdateEnvironmentVariables ? 'Need to update environment variables' : 'No need to update environment variables'}. Existing environment variables: ` +
JSON.stringify(environmentVariables, null, 2));
return needToUpdateLayer || needToUpdateEnvironmentVariables
? {
functionName,
layers: [existingLayerVersionArn, ...otherLayerArns],
environmentVariables: {
...environmentVariables,
...ddlEnvironmentVariables,
},
timeout: Math.max(initialTimeout, 300),
}
: undefined;
}
}
/**
* Add the policy to the Lambda role
*/
async function addPolicyToRole(roleName) {
Logger.verbose(`[Role ${roleName}] Attaching policy to the role`);
try {
await getIAMClient().send(new PutRolePolicyCommand({
RoleName: roleName,
PolicyName: inlinePolicyName,
PolicyDocument: JSON.stringify(policyDocument),
}));
}
catch (error) {
throw new Error(`Failed to attach policy to role ${roleName}.`, {
cause: error,
});
}
}
/**
* Prepare the Lambda role for the update
* @param roleName
* @returns
*/
async function analyzeRoleAdd(roleName) {
try {
Logger.verbose(`[Role ${roleName}] Analyzing role for policy attachment`);
const existingPolicy = await createPolicyDocument(roleName);
let addPolicy = true;
// compare existing policy with the new one
if (existingPolicy) {
if (JSON.stringify(existingPolicy) === JSON.stringify(policyDocument)) {
Logger.verbose(`[Role ${roleName}] Policy already attached to the role`);
addPolicy = false;
}
else {
Logger.verbose(`[Role ${roleName}] Different policy found on role, will update`);
}
}
else {
Logger.verbose(`[Role ${roleName}] No policy found on role, will attach`);
}
return { addPolicy, roleName };
}
catch (error) {
throw new Error(`Failed to analyze role ${roleName} for policy attachment.`, {
cause: error,
});
}
}
/**
* Get the environment variables for the Lambda function
*/
function getEnvironmentVariablesForDebugger({ functionName, timeout, verbose, initialExecWrapper, }) {
const env = {
LLD_FUNCTION_ID: functionName,
AWS_LAMBDA_EXEC_WRAPPER: lldWrapperPath,
LLD_DEBUGGER_ID: Configuration.config.debuggerId,
LLD_INITIAL_TIMEOUT: timeout ? timeout.toString() : '-1', // should never be negative
LLD_OBSERVABLE_MODE: Configuration.config.observable ? 'true' : 'false',
LLD_OBSERVABLE_INTERVAL: Configuration.config.interval.toString(),
};
if (initialExecWrapper) {
env.LLD_INITIAL_AWS_LAMBDA_EXEC_WRAPPER = initialExecWrapper;
}
if (verbose) {
env.LLD_VERBOSE = 'true';
}
return env;
}
export const InfraDeploy = {
getInfraChangesForAdding,
getInfraChangesForRemoving,
applyAddingInfra,
applyRemoveInfra,
deleteLayer,
};