-
Notifications
You must be signed in to change notification settings - Fork 3.9k
/
singleton-lambda.ts
193 lines (169 loc) · 6.5 KB
/
singleton-lambda.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
import { Construct, IConstruct, IDependable, Node } from 'constructs';
import { Architecture } from './architecture';
import { Function as LambdaFunction, FunctionProps, EnvironmentOptions } from './function';
import { FunctionBase } from './function-base';
import { Version } from './lambda-version';
import { ILayerVersion } from './layers';
import { Permission } from './permission';
import { Runtime } from './runtime';
import * as ec2 from '../../aws-ec2';
import * as iam from '../../aws-iam';
import * as logs from '../../aws-logs';
import * as cdk from '../../core';
/**
* Properties for a newly created singleton Lambda
*/
export interface SingletonFunctionProps extends FunctionProps {
/**
* A unique identifier to identify this lambda
*
* The identifier should be unique across all custom resource providers.
* We recommend generating a UUID per provider.
*/
readonly uuid: string;
/**
* A descriptive name for the purpose of this Lambda.
*
* If the Lambda does not have a physical name, this string will be
* reflected its generated name. The combination of lambdaPurpose
* and uuid must be unique.
*
* @default SingletonLambda
*/
readonly lambdaPurpose?: string;
}
/**
* A Lambda that will only ever be added to a stack once.
*
* This construct is a way to guarantee that the lambda function will be guaranteed to be part of the stack,
* once and only once, irrespective of how many times the construct is declared to be part of the stack.
* This is guaranteed as long as the `uuid` property and the optional `lambdaPurpose` property stay the same
* whenever they're declared into the stack.
*
* @resource AWS::Lambda::Function
*/
export class SingletonFunction extends FunctionBase {
public readonly grantPrincipal: iam.IPrincipal;
public readonly functionName: string;
public readonly functionArn: string;
public readonly role?: iam.IRole;
public readonly permissionsNode: Node;
public readonly architecture: Architecture;
/**
* The runtime environment for the Lambda function.
*/
public readonly runtime: Runtime;
protected readonly canCreatePermissions: boolean;
private lambdaFunction: LambdaFunction;
constructor(scope: Construct, id: string, props: SingletonFunctionProps) {
super(scope, id);
this.lambdaFunction = this.ensureLambda(props);
this.permissionsNode = this.lambdaFunction.node;
this.architecture = this.lambdaFunction.architecture;
this.functionArn = this.lambdaFunction.functionArn;
this.functionName = this.lambdaFunction.functionName;
this.role = this.lambdaFunction.role;
this.runtime = this.lambdaFunction.runtime;
this.grantPrincipal = this.lambdaFunction.grantPrincipal;
this.canCreatePermissions = true; // Doesn't matter, addPermission is overriden anyway
}
/**
* @inheritdoc
*/
public get isBoundToVpc(): boolean {
return this.lambdaFunction.isBoundToVpc;
}
/**
* @inheritdoc
*/
public get connections(): ec2.Connections {
return this.lambdaFunction.connections;
}
/**
* The LogGroup where the Lambda function's logs are made available.
*
* If either `logRetention` is set or this property is called, a CloudFormation custom resource is added to the stack that
* pre-creates the log group as part of the stack deployment, if it already doesn't exist, and sets the correct log retention
* period (never expire, by default).
*
* Further, if the log group already exists and the `logRetention` is not set, the custom resource will reset the log retention
* to never expire even if it was configured with a different value.
*/
public get logGroup(): logs.ILogGroup {
return this.lambdaFunction.logGroup;
}
/**
* Returns a `lambda.Version` which represents the current version of this
* singleton Lambda function. A new version will be created every time the
* function's configuration changes.
*
* You can specify options for this version using the `currentVersionOptions`
* prop when initializing the `lambda.SingletonFunction`.
*/
public get currentVersion(): Version {
return this.lambdaFunction.currentVersion;
}
public get resourceArnsForGrantInvoke() {
return [this.functionArn, `${this.functionArn}:*`];
};
/**
* Adds an environment variable to this Lambda function.
* If this is a ref to a Lambda function, this operation results in a no-op.
* @param key The environment variable key.
* @param value The environment variable's value.
* @param options Environment variable options.
*/
public addEnvironment(key: string, value: string, options?: EnvironmentOptions) {
return this.lambdaFunction.addEnvironment(key, value, options);
}
/**
* Adds one or more Lambda Layers to this Lambda function.
*
* @param layers the layers to be added.
*
* @throws if there are already 5 layers on this function, or the layer is incompatible with this function's runtime.
*/
public addLayers(...layers: ILayerVersion[]) {
return this.lambdaFunction.addLayers(...layers);
}
public addPermission(name: string, permission: Permission) {
return this.lambdaFunction.addPermission(name, permission);
}
/**
* Using node.addDependency() does not work on this method as the underlying lambda function is modeled
* as a singleton across the stack. Use this method instead to declare dependencies.
*/
public addDependency(...up: IDependable[]) {
this.lambdaFunction.node.addDependency(...up);
}
/**
* The SingletonFunction construct cannot be added as a dependency of another construct using
* node.addDependency(). Use this method instead to declare this as a dependency of another construct.
*/
public dependOn(down: IConstruct) {
down.node.addDependency(this.lambdaFunction);
}
/** @internal */
public _checkEdgeCompatibility() {
return this.lambdaFunction._checkEdgeCompatibility();
}
/**
* Returns the construct tree node that corresponds to the lambda function.
* @internal
*/
protected _functionNode(): Node {
return this.lambdaFunction.node;
}
private ensureLambda(props: SingletonFunctionProps): LambdaFunction {
const constructName = (props.lambdaPurpose || 'SingletonLambda') + slugify(props.uuid);
const existing = cdk.Stack.of(this).node.tryFindChild(constructName);
if (existing) {
// Just assume this is true
return existing as LambdaFunction;
}
return new LambdaFunction(cdk.Stack.of(this), constructName, props);
}
}
function slugify(x: string): string {
return x.replace(/[^a-zA-Z0-9]/g, '');
}