Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(lambda): create a node runtime aspect #29989

Closed
wants to merge 18 commits into from
1 change: 1 addition & 0 deletions packages/aws-cdk-lib/aws-lambda/lib/index.ts
Expand Up @@ -26,6 +26,7 @@ export * from './function-url';
export * from './runtime-management';
export * from './params-and-secrets-layers';
export * from './snapstart-config';
export * from './runtime-aspect';

// AWS::Lambda CloudFormation Resources:
export * from './lambda.generated';
Expand Down
84 changes: 84 additions & 0 deletions packages/aws-cdk-lib/aws-lambda/lib/runtime-aspect.ts
@@ -0,0 +1,84 @@
import { IConstruct } from 'constructs';
import { Function } from './function';
import { CfnFunction } from './lambda.generated';
import { Runtime } from './runtime';
import { CfnResource, IAspect } from '../../core';

/**
* Options used to configure a RuntimeAspect.
*/
export interface RuntimeAspectOptions {
/**
* Lambda functions that should be excluded by the RuntimeAspect
*
* @default - the RuntimeAspect will include all Lambda functions in the same runtime family.
*/
readonly functionsToIgnore?: Function[];
}

/**
* A class used to walk the construct tree and update all runtimes.
*/
export class NodeRuntimeAspect implements IAspect {
/**
* Walks the construct tree and updates all node runtimes with 'nodejs20.x'.
*/
public static nodeJs20(options: RuntimeAspectOptions = {}) {
return NodeRuntimeAspect.of(Runtime.NODEJS_20_X.toString(), options);
}

/**
* Use any runtime name to update all runtimes with the specified runtime family.
*/
public static of(runtimeName: string, options: RuntimeAspectOptions = {}) {
if (!runtimeName.includes('nodejs')) {
throw new Error('You must only use node runtimes');
}
return new NodeRuntimeAspect(runtimeName, options);
}

private readonly runtimeName: string;
private readonly functionPathsToIgnore: string[];

private constructor(runtimeName: string, options: RuntimeAspectOptions = {}) {
this.runtimeName = runtimeName;
this.functionPathsToIgnore = this.generateFunctionPathsToIgnore(options.functionsToIgnore ?? []);
}

public visit(node: IConstruct) {
if (this.functionPathsToIgnore.includes(node.node.path)) {
return;
}

// override runtimes for Function and SingletonFunction
if (node instanceof CfnFunction && this.isRuntimeFamily(node.runtime)) {
node.runtime = this.runtimeName;
return;
}

// override runtimes for CfnResource of type AWS::Lambda::Function
if (CfnResource.isCfnResource(node) && node.cfnResourceType === 'AWS::Lambda::Function') {
const runtime = node.getResourceProperty('Runtime');
if (this.isRuntimeFamily(runtime)) {
node.addPropertyOverride('Runtime', this.runtimeName);
}
return;
}
}

private generateFunctionPathsToIgnore(functionsToIgnore: Function[]) {
const functionPathsToIgnore = [];
for (const functionToIgnore of functionsToIgnore) {
functionPathsToIgnore.push(`${functionToIgnore.node.path}/Resource`);
}
return functionPathsToIgnore;
}

private isRuntimeFamily(runtime?: string) {
// this shouldn't happen, but in case it does throw an error
if (runtime === undefined) {
throw new Error('No runtime was configured for the visited node');
}
return runtime.includes('nodejs');
}
}
4 changes: 4 additions & 0 deletions packages/aws-cdk-lib/core/lib/cfn-resource.ts
Expand Up @@ -375,6 +375,10 @@ export class CfnResource extends CfnRefElement {
return this.cfnOptions.metadata?.[key];
}

public getResourceProperty(key: string): any {
return this._cfnProperties[key];
}

/**
* @returns a string representation of this resource
*/
Expand Down