Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@
"test": "npm run build && RUN_TEST_FROM_CLI=true vitest run && RUN_TEST_FROM_CLI=true OBSERVABLE_MODE=true vitest run",
"test-cdk-basic": "npm run build && RUN_TEST_FROM_CLI=true vitest run test/cdk-basic.test.ts",
"test-cdk-basic-observable": "npm run build && RUN_TEST_FROM_CLI=true OBSERVABLE_MODE=true vitest run test/cdk-basic.test.ts",
"test-cdk-nested": "npm run build && RUN_TEST_FROM_CLI=true vitest run test/cdk-nested.test.ts",
"test-cdk-nested-observable": "npm run build && RUN_TEST_FROM_CLI=true OBSERVABLE_MODE=true vitest run test/cdk-nested.test.ts",
"test-cdk-esm": "npm run build && RUN_TEST_FROM_CLI=true vitest run test/cdk-esm.test.ts",
"test-cdk-esm-observable": "npm run build && RUN_TEST_FROM_CLI=true OBSERVABLE_MODE=true vitest run test/cdk-esm.test.ts",
"test-sls-basic": "npm run build && RUN_TEST_FROM_CLI=true vitest run test/sls-basic.test.ts",
Expand Down Expand Up @@ -148,6 +150,7 @@
"src/extension/*",
"test",
"test/cdk-basic",
"test/cdk-nested",
"test/cdk-esm",
"test/cdk-config",
"test/sls-basic",
Expand Down
47 changes: 47 additions & 0 deletions src/cloudFormation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,54 @@ async function getLambdasInStack(
);
}

/**
* Get CloudFormation stack template
* @param stackName
* @param awsConfiguration
* @returns
*/
async function getStackResourcePhysicalResourceId(
stackName: string,
logicalResourceId: string,
awsConfiguration: AwsConfiguration,
) {
const { DescribeStackResourceCommand } = await import(
'@aws-sdk/client-cloudformation'
);
const command = new DescribeStackResourceCommand({
StackName: stackName,
LogicalResourceId: logicalResourceId,
});
const cloudFormationClient = await getCloudFormationClient(awsConfiguration);

try {
const response = await cloudFormationClient.send(command);

if (!response.StackResourceDetail) {
throw new Error(
`No resource details found in stack ${stackName} for ${logicalResourceId}`,
);
}

const physicalResourceId = response.StackResourceDetail.PhysicalResourceId;
if (!physicalResourceId)
throw new Error(
`No physicalResourceId found in stack ${stackName} for ${logicalResourceId}`,
);
return physicalResourceId;
} catch (error: any) {
if (error.name === 'ValidationError') {
Logger.error(
`Stack ${stackName} and/or ${logicalResourceId} not found. Try specifying a region. Error: ${error.message}`,
error,
);
}
throw error;
}
}

export const CloudFormation = {
getCloudFormationStackTemplate,
getLambdasInStack,
getStackResourcePhysicalResourceId: getStackResourcePhysicalResourceId,
};
109 changes: 105 additions & 4 deletions src/frameworks/cdkFramework.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,48 @@ export class CdkFramework implements IFramework {
JSON.stringify(lambdasInCdk, null, 2),
);

//get all stack names
const stackNames = [
...new Set( // unique
const cdkTokenRegex = /^\${Token\[TOKEN\.\d+\]}$/;
const stackTokensCdkPathMappings = Object.fromEntries(
lambdasInCdk
.filter((lambda) => cdkTokenRegex.test(lambda.stackName))
.map((lambda) => [lambda.stackName, lambda.stackCdkPath]),
);
const realStackNames = [
...new Set(
lambdasInCdk.map((lambda) => {
return lambda.stackName;
return lambda.rootStackName;
}),
),
];

const unresolvedTokens = new Set(Object.keys(stackTokensCdkPathMappings));
const resolvedTokenizedStackNames = new Set<string>();

if (Object.keys(stackTokensCdkPathMappings).length) {
Logger.verbose(
`[CDK] Found tokenized stackNames: ${[...unresolvedTokens].join(', ')}`,
);
Logger.verbose(
`[CDK] Will look for tokenized stackNames in ${realStackNames.join(
', ',
)}`,
);
for (const realStackName of realStackNames) {
if (!unresolvedTokens.size) break;
await this.resolveTokenizedStackNames(
unresolvedTokens,
resolvedTokenizedStackNames,
stackTokensCdkPathMappings,
awsConfiguration,
realStackName,
);
}
}

//get all stack names
const stackNames = [
...new Set(realStackNames.concat([...resolvedTokenizedStackNames])), // unique
];
Logger.verbose(
`[CDK] Found the following stacks in CDK: ${stackNames.join(', ')}`,
);
Expand Down Expand Up @@ -170,6 +204,61 @@ export class CdkFramework implements IFramework {
return lambdasDiscovered;
}

protected async resolveTokenizedStackNames(
unresolvedTokens: Set<string>,
resolvedTokenizedStackNames: Set<string>,
stackTokensCdkPathMappings: Record<string, string>,
awsConfiguration: AwsConfiguration,
stackName: string,
): Promise<void> {
if (!unresolvedTokens.size) {
return;
}

const cfTemplate = await CloudFormation.getCloudFormationStackTemplate(
stackName,
awsConfiguration,
);

if (cfTemplate) {
const nestedStacks = Object.entries(cfTemplate.Resources)
.filter(
([, resource]: [string, any]) =>
resource.Type === 'AWS::CloudFormation::Stack',
)
.map(([key, resource]: [string, any]) => {
return {
logicalId: key,
cdkPath: resource.Metadata['aws:cdk:path'],
};
});

for (const nestedStack of nestedStacks) {
const mapping = Object.entries(stackTokensCdkPathMappings).find(
(f) => f[1] === nestedStack.cdkPath,
);

if (mapping) {
unresolvedTokens.delete(mapping[0]);
const physicalResourceId =
await CloudFormation.getStackResourcePhysicalResourceId(
stackName,
nestedStack.logicalId,
awsConfiguration,
);
resolvedTokenizedStackNames.add(physicalResourceId);
await this.resolveTokenizedStackNames(
unresolvedTokens,
resolvedTokenizedStackNames,
stackTokensCdkPathMappings,
awsConfiguration,
physicalResourceId,
);
}
}
}
}

/**
* Getz Lambda functions from the CloudFormation template metadata
* @param stackName
Expand Down Expand Up @@ -308,9 +397,15 @@ export class CdkFramework implements IFramework {
`;
global.lambdas = global.lambdas ?? [];

let rootStack = this.stack;
while (rootStack.nestedStackParent) {
rootStack = rootStack.nestedStackParent;
}
const lambdaInfo = {
//cdkPath: this.node.defaultChild?.node.path ?? this.node.path,
stackCdkPath: this.stack.node.defaultChild?.node.path ?? this.stack.node.path,
stackName: this.stack.stackName,
rootStackName: rootStack.stackName,
codePath: props.entry,
code: props.code,
node: this.node,
Expand All @@ -320,6 +415,8 @@ export class CdkFramework implements IFramework {

// console.log("CDK INFRA: ", {
// stackName: lambdaInfo.stackName,
// stackCdkPath: lambdaInfo.stackCdkPath,
// rootStackName: lambdaInfo.rootStackName,
// codePath: lambdaInfo.codePath,
// code: lambdaInfo.code,
// handler: lambdaInfo.handler,
Expand Down Expand Up @@ -490,6 +587,8 @@ export class CdkFramework implements IFramework {
return {
cdkPath: lambda.cdkPath,
stackName: lambda.stackName,
stackCdkPath: lambda.stackCdkPath,
rootStackName: lambda.rootStackName,
packageJsonPath,
codePath,
handler,
Expand Down Expand Up @@ -572,6 +671,8 @@ export class CdkFramework implements IFramework {
return lambdas as {
cdkPath: string;
stackName: string;
stackCdkPath: string;
rootStackName: string;
codePath?: string;
code: {
path?: string;
Expand Down
2 changes: 2 additions & 0 deletions src/frameworks/cdkFrameworkWorker.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ parentPort.on('message', async (data) => {
handler: lambda.handler,
stackName: lambda.stackName,
codePath: lambda.codePath,
stackCdkPath: lambda.stackCdkPath,
rootStackName: lambda.rootStackName,
code: {
path: lambda.code?.path,
},
Expand Down
Loading
Loading