-
Notifications
You must be signed in to change notification settings - Fork 161
/
index.ts
127 lines (120 loc) · 3.81 KB
/
index.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
// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT-0
import {
CloudFormationCustomResourceHandler,
CloudFormationCustomResourceDeleteEvent,
CloudFormationCustomResourceUpdateEvent,
} from "aws-lambda";
import Lambda from "aws-sdk/clients/lambda";
import Zip from "adm-zip";
import { writeFileSync, mkdtempSync } from "fs";
import { resolve } from "path";
import { sendCfnResponse, Status } from "./cfn-response";
import { fetch } from "./https";
async function updateLambdaCode(
action: "Create" | "Update" | "Delete",
lambdaFunction: string,
stringifiedConfig: string,
physicalResourceId?: string
) {
if (action === "Delete") {
// Deletes aren't executed; the Lambda Resource should just be deleted
return { physicalResourceId: physicalResourceId!, Data: {} };
}
console.log(
`Adding configuration to Lambda function ${lambdaFunction}:\n${stringifiedConfig}`
);
const region = lambdaFunction.split(":")[3];
const lambdaClient = new Lambda({ region });
// Parse the JSON to ensure it's validity (and avoid ugly errors at runtime)
const config = JSON.parse(stringifiedConfig);
// Fetch and extract Lambda zip contents to temporary folder, add configuration.json, and rezip
const { Code } = await lambdaClient
.getFunction({
FunctionName: lambdaFunction,
})
.promise();
const data = await fetch(Code!.Location!);
const lambdaZip = new Zip(data);
console.log(
"Lambda zip contents:",
lambdaZip.getEntries().map((entry) => entry.name)
);
console.log("Adding (fresh) configuration.json ...");
const tempDir = mkdtempSync("/tmp/lambda-package");
lambdaZip.extractAllTo(tempDir, true);
writeFileSync(
resolve(tempDir, "configuration.json"),
Buffer.from(JSON.stringify(config, null, 2))
);
const newLambdaZip = new Zip();
newLambdaZip.addLocalFolder(tempDir);
console.log(
"New Lambda zip contents:",
newLambdaZip.getEntries().map((entry) => entry.name)
);
const { CodeSha256, Version, FunctionArn } = await lambdaClient
.updateFunctionCode({
FunctionName: lambdaFunction,
ZipFile: newLambdaZip.toBuffer(),
Publish: true,
})
.promise();
console.log({ CodeSha256, Version, FunctionArn });
let attempts = 0;
while (++attempts <= 30) {
const { State } = await lambdaClient
.getFunctionConfiguration({
FunctionName: FunctionArn!,
})
.promise();
if (!State || State === "Pending") {
console.log(
`Waiting for updated Lambda function to become Active, is: ${State} (attempts: ${attempts})`
);
await new Promise((resolve) =>
setTimeout(resolve, Math.min(5000, 1000 * attempts))
);
continue;
}
if (State === "Active") {
console.log("Function is now Active!");
break;
}
throw new Error(`Lambda function state is: ${State}`);
}
return {
physicalResourceId: lambdaFunction,
Data: { CodeSha256, Version, FunctionArn },
};
}
export const handler: CloudFormationCustomResourceHandler = async (event) => {
const { ResourceProperties, RequestType } = event;
const { PhysicalResourceId } = event as
| CloudFormationCustomResourceDeleteEvent
| CloudFormationCustomResourceUpdateEvent;
const { LambdaFunction, Configuration } = ResourceProperties;
let status = Status.SUCCESS;
let physicalResourceId: string | undefined;
let data: { [key: string]: any } | undefined;
let reason: string | undefined;
try {
({ physicalResourceId, Data: data } = await updateLambdaCode(
RequestType,
LambdaFunction,
Configuration,
PhysicalResourceId
));
} catch (err) {
console.error(err);
status = Status.FAILED;
reason = `${err}`;
}
await sendCfnResponse({
event,
status,
data,
physicalResourceId,
reason,
});
};