Skip to content

Commit 6446b78

Browse files
authored
refactor(lambda): Standardize Lambda API (#2876)
`Alias`es and `Version`s are now `IFunctions`, and other fixes. BREAKING CHANGE: - Renamed `Function.addLayer` to `addLayers` and made it variadic - Removed `IFunction.handler` property - Removed `IVersion.versionArn` property (the value is at `functionArn`) - Removed `SingletonLayerVersion` - Stopped exporting `LogRetention`
1 parent 118a716 commit 6446b78

File tree

15 files changed

+157
-174
lines changed

15 files changed

+157
-174
lines changed

packages/@aws-cdk/aws-cloudfront/lib/web_distribution.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -739,7 +739,7 @@ export class CloudFrontWebDistribution extends cdk.Construct implements IDistrib
739739
lambdaFunctionAssociations: input.lambdaFunctionAssociations
740740
.map(fna => ({
741741
eventType: fna.eventType,
742-
lambdaFunctionArn: fna.lambdaFunction && fna.lambdaFunction.versionArn,
742+
lambdaFunctionArn: fna.lambdaFunction && fna.lambdaFunction.functionArn,
743743
}))
744744
});
745745
}

packages/@aws-cdk/aws-lambda/lib/alias.ts

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,23 @@
11
import cloudwatch = require('@aws-cdk/aws-cloudwatch');
22
import { Construct, Stack } from '@aws-cdk/cdk';
3-
import { FunctionBase, IFunction } from './function-base';
3+
import { IFunction, QualifiedFunctionBase } from './function-base';
44
import { IVersion } from './lambda-version';
55
import { CfnAlias } from './lambda.generated';
66

7+
export interface IAlias extends IFunction {
8+
/**
9+
* Name of this alias.
10+
*
11+
* @attribute
12+
*/
13+
readonly aliasName: string;
14+
15+
/**
16+
* The underlying Lambda function version.
17+
*/
18+
readonly version: IVersion;
19+
}
20+
721
/**
822
* Properties for a new Lambda alias
923
*/
@@ -47,10 +61,30 @@ export interface AliasProps {
4761
readonly additionalVersions?: VersionWeight[];
4862
}
4963

64+
export interface AliasAttributes {
65+
readonly aliasName: string;
66+
readonly aliasVersion: IVersion;
67+
}
68+
5069
/**
5170
* A new alias to a particular version of a Lambda function.
5271
*/
53-
export class Alias extends FunctionBase {
72+
export class Alias extends QualifiedFunctionBase implements IAlias {
73+
public static fromAliasAttributes(scope: Construct, id: string, attrs: AliasAttributes): IAlias {
74+
class Imported extends QualifiedFunctionBase implements IAlias {
75+
public readonly aliasName = attrs.aliasName;
76+
public readonly version = attrs.aliasVersion;
77+
public readonly lambda = attrs.aliasVersion.lambda;
78+
public readonly functionArn = `${attrs.aliasVersion.lambda.functionArn}:${attrs.aliasName}`;
79+
public readonly functionName = `${attrs.aliasVersion.lambda.functionName}:${attrs.aliasName}`;
80+
public readonly grantPrincipal = attrs.aliasVersion.grantPrincipal;
81+
public readonly role = attrs.aliasVersion.role;
82+
83+
protected readonly canCreatePermissions = false;
84+
}
85+
return new Imported(scope, id);
86+
}
87+
5488
/**
5589
* Name of this alias.
5690
*
@@ -65,6 +99,10 @@ export class Alias extends FunctionBase {
6599
*/
66100
public readonly functionName: string;
67101

102+
public readonly lambda: IFunction;
103+
104+
public readonly version: IVersion;
105+
68106
/**
69107
* ARN of this alias
70108
*
@@ -75,21 +113,17 @@ export class Alias extends FunctionBase {
75113

76114
protected readonly canCreatePermissions: boolean = true;
77115

78-
/**
79-
* The actual Lambda function object that this Alias is pointing to
80-
*/
81-
private readonly underlyingLambda: IFunction;
82-
83116
constructor(scope: Construct, id: string, props: AliasProps) {
84117
super(scope, id);
85118

119+
this.lambda = props.version.lambda;
86120
this.aliasName = props.aliasName;
87-
this.underlyingLambda = props.version.lambda;
121+
this.version = props.version;
88122

89123
const alias = new CfnAlias(this, 'Resource', {
90124
name: props.aliasName,
91125
description: props.description,
92-
functionName: this.underlyingLambda.functionName,
126+
functionName: this.version.lambda.functionName,
93127
functionVersion: props.version.version,
94128
routingConfig: this.determineRoutingConfig(props)
95129
});
@@ -101,26 +135,23 @@ export class Alias extends FunctionBase {
101135
this.functionArn = alias.aliasArn;
102136
}
103137

104-
/**
105-
* Role associated with this alias
106-
*/
107-
public get role() {
108-
return this.underlyingLambda.role;
138+
public get grantPrincipal() {
139+
return this.version.grantPrincipal;
109140
}
110141

111-
public get grantPrincipal() {
112-
return this.underlyingLambda.grantPrincipal;
142+
public get role() {
143+
return this.version.role;
113144
}
114145

115146
public metric(metricName: string, props: cloudwatch.MetricOptions = {}): cloudwatch.Metric {
116147
// Metrics on Aliases need the "bare" function name, and the alias' ARN, this differes from the base behavior.
117148
return super.metric(metricName, {
118149
dimensions: {
119-
FunctionName: this.underlyingLambda.functionName,
150+
FunctionName: this.lambda.functionName,
120151
// construct the ARN from the underlying lambda so that alarms on an alias
121152
// don't cause a circular dependency with CodeDeploy
122153
// see: https://github.com/awslabs/aws-cdk/issues/2231
123-
Resource: `${this.underlyingLambda.functionArn}:${this.aliasName}`
154+
Resource: `${this.lambda.functionArn}:${this.aliasName}`
124155
},
125156
...props
126157
});

packages/@aws-cdk/aws-lambda/lib/function-base.ts

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,6 @@ import { Permission } from './permission';
1010

1111
export interface IFunction extends IResource, ec2.IConnectable, iam.IGrantable {
1212

13-
/**
14-
* Logical ID of this Function.
15-
*/
16-
readonly id: string;
17-
1813
/**
1914
* The name of the function.
2015
*
@@ -122,7 +117,7 @@ export interface FunctionAttributes {
122117
readonly securityGroupId?: string;
123118
}
124119

125-
export abstract class FunctionBase extends Resource implements IFunction {
120+
export abstract class FunctionBase extends Resource implements IFunction {
126121
/**
127122
* The principal this Lambda Function is running as
128123
*/
@@ -145,11 +140,6 @@ export abstract class FunctionBase extends Resource implements IFunction {
145140
*/
146141
public abstract readonly role?: iam.IRole;
147142

148-
/**
149-
* The $LATEST version of this function.
150-
*/
151-
public readonly latestVersion: IVersion = new LatestVersion(this);
152-
153143
/**
154144
* Whether the addPermission() call adds any permissions
155145
*
@@ -188,10 +178,6 @@ export abstract class FunctionBase extends Resource implements IFunction {
188178
});
189179
}
190180

191-
public get id() {
192-
return this.node.id;
193-
}
194-
195181
public addToRolePolicy(statement: iam.PolicyStatement) {
196182
if (!this.role) {
197183
return;
@@ -213,6 +199,11 @@ export abstract class FunctionBase extends Resource implements IFunction {
213199
return this._connections;
214200
}
215201

202+
public get latestVersion(): IVersion {
203+
// Dynamic to avoid invinite recursion when creating the LatestVersion instance...
204+
return new LatestVersion(this);
205+
}
206+
216207
/**
217208
* Whether or not this Lambda function was bound to a VPC
218209
*
@@ -289,15 +280,45 @@ export abstract class FunctionBase extends Resource implements IFunction {
289280
}
290281
}
291282

283+
export abstract class QualifiedFunctionBase extends FunctionBase {
284+
public abstract readonly lambda: IFunction;
285+
286+
public get latestVersion() {
287+
return this.lambda.latestVersion;
288+
}
289+
}
290+
292291
/**
293292
* The $LATEST version of a function, useful when attempting to create aliases.
294293
*/
295-
class LatestVersion extends Resource implements IVersion {
294+
class LatestVersion extends FunctionBase implements IVersion {
296295
public readonly lambda: IFunction;
297296
public readonly version = '$LATEST';
298297

298+
protected readonly canCreatePermissions = true;
299+
299300
constructor(lambda: FunctionBase) {
300301
super(lambda, '$LATEST');
301302
this.lambda = lambda;
302303
}
304+
305+
public get functionArn() {
306+
return `${this.lambda.functionArn}:${this.version}`;
307+
}
308+
309+
public get functionName() {
310+
return `${this.lambda.functionName}:${this.version}`;
311+
}
312+
313+
public get grantPrincipal() {
314+
return this.lambda.grantPrincipal;
315+
}
316+
317+
public get latestVersion() {
318+
return this;
319+
}
320+
321+
public get role() {
322+
return this.lambda.role;
323+
}
303324
}

packages/@aws-cdk/aws-lambda/lib/function.ts

Lines changed: 15 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -257,8 +257,8 @@ export class Function extends FunctionBase {
257257
class Import extends FunctionBase {
258258
public readonly functionName = functionName;
259259
public readonly functionArn = functionArn;
260-
public readonly role = role;
261260
public readonly grantPrincipal: iam.IPrincipal;
261+
public readonly role = role;
262262

263263
protected readonly canCreatePermissions = false;
264264

@@ -370,11 +370,6 @@ export class Function extends FunctionBase {
370370
*/
371371
public readonly runtime: Runtime;
372372

373-
/**
374-
* The name of the handler configured for this lambda.
375-
*/
376-
public readonly handler: string;
377-
378373
/**
379374
* The principal this Lambda Function is running as
380375
*/
@@ -442,14 +437,13 @@ export class Function extends FunctionBase {
442437

443438
this.functionName = resource.refAsString;
444439
this.functionArn = resource.functionArn;
445-
this.handler = props.handler;
446440
this.runtime = props.runtime;
447441

448442
// allow code to bind to stack.
449443
props.code.bind(this);
450444

451-
for (const layer of props.layers || []) {
452-
this.addLayer(layer);
445+
if (props.layers) {
446+
this.addLayers(...props.layers);
453447
}
454448

455449
for (const event of props.events || []) {
@@ -481,22 +475,23 @@ export class Function extends FunctionBase {
481475
}
482476

483477
/**
484-
* Adds a Lambda Layer to this Lambda function.
478+
* Adds one or more Lambda Layers to this Lambda function.
485479
*
486-
* @param layer the layer to be added.
480+
* @param layers the layers to be added.
487481
*
488482
* @throws if there are already 5 layers on this function, or the layer is incompatible with this function's runtime.
489483
*/
490-
public addLayer(layer: ILayerVersion): this {
491-
if (this.layers.length === 5) {
492-
throw new Error('Unable to add layer: this lambda function already uses 5 layers.');
493-
}
494-
if (layer.compatibleRuntimes && !layer.compatibleRuntimes.find(runtime => runtime.runtimeEquals(this.runtime))) {
495-
const runtimes = layer.compatibleRuntimes.map(runtime => runtime.name).join(', ');
496-
throw new Error(`This lambda function uses a runtime that is incompatible with this layer (${this.runtime.name} is not in [${runtimes}])`);
484+
public addLayers(...layers: ILayerVersion[]): void {
485+
for (const layer of layers) {
486+
if (this.layers.length === 5) {
487+
throw new Error('Unable to add layer: this lambda function already uses 5 layers.');
488+
}
489+
if (layer.compatibleRuntimes && !layer.compatibleRuntimes.find(runtime => runtime.runtimeEquals(this.runtime))) {
490+
const runtimes = layer.compatibleRuntimes.map(runtime => runtime.name).join(', ');
491+
throw new Error(`This lambda function uses a runtime that is incompatible with this layer (${this.runtime.name} is not in [${runtimes}])`);
492+
}
493+
this.layers.push(layer);
497494
}
498-
this.layers.push(layer);
499-
return this;
500495
}
501496

502497
/**
@@ -523,25 +518,6 @@ export class Function extends FunctionBase {
523518
});
524519
}
525520

526-
/**
527-
* Add a new version for this Lambda, always with a different name.
528-
*
529-
* This is similar to the {@link addVersion} method,
530-
* but useful when deploying this Lambda through CodePipeline with blue/green deployments.
531-
* When using {@link addVersion},
532-
* your Alias will not be updated until you change the name passed to {@link addVersion} in your CDK code.
533-
* When deploying through a Pipeline,
534-
* that might lead to a situation where a change to your Lambda application code will never be activated,
535-
* even though it traveled through the entire Pipeline,
536-
* because the Alias is still pointing to an old Version.
537-
* This method creates a new, unique Version every time the CDK code is executed,
538-
* and so prevents that from happening.
539-
*/
540-
public newVersion(): Version {
541-
const now = new Date();
542-
return this.addVersion(now.toISOString());
543-
}
544-
545521
private renderEnvironment() {
546522
if (!this.environment || Object.keys(this.environment).length === 0) {
547523
return undefined;

packages/@aws-cdk/aws-lambda/lib/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export * from './lambda-version';
99
export * from './singleton-lambda';
1010
export * from './event-source';
1111
export * from './event-source-mapping';
12+
1213
export * from './log-retention';
1314

1415
// AWS::Lambda CloudFormation Resources:

0 commit comments

Comments
 (0)