@evident-ai/runner-cdk 0.1.1-dev.d96ef8a → 0.1.1-dev.da88f8e
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -3
- package/dist/controller-lambda/handler.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +7 -1
- package/dist/microvm/constants.d.ts +3 -0
- package/dist/microvm/constants.js +20 -9
- package/dist/microvm/construct.js +4 -1
- package/dist/microvm/controller/microvm-client.d.ts +1 -1
- package/dist/microvm/image/stage-context.d.ts +33 -0
- package/dist/microvm/image/stage-context.js +148 -0
- package/dist/microvm-image-context/Dockerfile +224 -0
- package/dist/microvm-image-context/hook-server.js +290 -0
- package/dist/microvm-image-context/hooks/common.sh +1255 -0
- package/dist/microvm-image-context/hooks/resume +79 -0
- package/dist/microvm-image-context/hooks/run +111 -0
- package/dist/microvm-image-context/hooks/suspend +19 -0
- package/dist/microvm-image-context/hooks/terminate +34 -0
- package/package.json +5 -2
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
9
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
27
|
+
|
|
28
|
+
// ../lambda-microvm-runtime/dist/runtime.js
|
|
29
|
+
var require_runtime = __commonJS({
|
|
30
|
+
"../lambda-microvm-runtime/dist/runtime.js"(exports2) {
|
|
31
|
+
"use strict";
|
|
32
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
33
|
+
exports2.HOOK_SIGTERM_SECONDS = exports2.HOOK_PATHS = exports2.HOOK_NAMES = exports2.HOOK_PATH_PREFIX = void 0;
|
|
34
|
+
exports2.parseRunEnvelope = parseRunEnvelope;
|
|
35
|
+
exports2.createMicrovmRuntime = createMicrovmRuntime;
|
|
36
|
+
var node_http_1 = require("node:http");
|
|
37
|
+
exports2.HOOK_PATH_PREFIX = "/aws/lambda-microvms/runtime/v1";
|
|
38
|
+
exports2.HOOK_NAMES = ["ready", "validate", "run", "resume", "suspend", "terminate"];
|
|
39
|
+
exports2.HOOK_PATHS = Object.fromEntries(exports2.HOOK_NAMES.map((name) => [name, `${exports2.HOOK_PATH_PREFIX}/${name}`]));
|
|
40
|
+
var RUN_HOOK_PAYLOAD_MAX_BYTES = 16384;
|
|
41
|
+
var MAX_BODY_BYTES = RUN_HOOK_PAYLOAD_MAX_BYTES * 8;
|
|
42
|
+
exports2.HOOK_SIGTERM_SECONDS = 55;
|
|
43
|
+
var DEFAULT_TIMEOUT_SECONDS = exports2.HOOK_SIGTERM_SECONDS;
|
|
44
|
+
var DEFAULT_KILL_GRACE_SECONDS = 10;
|
|
45
|
+
function readBody(request) {
|
|
46
|
+
return new Promise((resolve, reject) => {
|
|
47
|
+
const chunks = [];
|
|
48
|
+
let size = 0;
|
|
49
|
+
let oversize = false;
|
|
50
|
+
request.on("data", (chunk) => {
|
|
51
|
+
if (oversize) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
size += chunk.length;
|
|
55
|
+
if (size > MAX_BODY_BYTES) {
|
|
56
|
+
oversize = true;
|
|
57
|
+
resolve(null);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
chunks.push(chunk);
|
|
61
|
+
});
|
|
62
|
+
request.on("end", () => {
|
|
63
|
+
if (!oversize) {
|
|
64
|
+
resolve(Buffer.concat(chunks).toString("utf8"));
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
request.on("error", reject);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
function parseRunEnvelope(rawBody) {
|
|
71
|
+
let parsed;
|
|
72
|
+
try {
|
|
73
|
+
parsed = JSON.parse(rawBody);
|
|
74
|
+
} catch {
|
|
75
|
+
throw new Error("run hook body is not valid JSON");
|
|
76
|
+
}
|
|
77
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
78
|
+
throw new Error("run hook body is not a JSON object");
|
|
79
|
+
}
|
|
80
|
+
const envelope = parsed;
|
|
81
|
+
const microvmId = envelope.microvmId;
|
|
82
|
+
const runHookPayload = envelope.runHookPayload;
|
|
83
|
+
if (typeof microvmId !== "string" || microvmId === "") {
|
|
84
|
+
throw new Error("run hook body is missing microvmId");
|
|
85
|
+
}
|
|
86
|
+
if (typeof runHookPayload !== "string") {
|
|
87
|
+
throw new Error("run hook body is missing runHookPayload");
|
|
88
|
+
}
|
|
89
|
+
return { microvmId, runHookPayload };
|
|
90
|
+
}
|
|
91
|
+
function createMicrovmRuntime({ hooksDir, spawn, hasScript, timeoutSeconds = DEFAULT_TIMEOUT_SECONDS, killGraceSeconds = DEFAULT_KILL_GRACE_SECONDS }) {
|
|
92
|
+
function runScript(hook, script, env, payload) {
|
|
93
|
+
return new Promise((resolve) => {
|
|
94
|
+
const child = spawn(script, { env });
|
|
95
|
+
let settled = false;
|
|
96
|
+
let escalation;
|
|
97
|
+
const settle = (outcome) => {
|
|
98
|
+
if (settled) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
settled = true;
|
|
102
|
+
clearTimeout(timer);
|
|
103
|
+
resolve(outcome);
|
|
104
|
+
};
|
|
105
|
+
const timer = setTimeout(() => {
|
|
106
|
+
console.error(`[hook] hook=${hook} status=timeout limit_seconds=${timeoutSeconds}`);
|
|
107
|
+
child.kill("SIGTERM");
|
|
108
|
+
escalation = setTimeout(() => {
|
|
109
|
+
console.error(`[hook] hook=${hook} status=kill_escalated grace_seconds=${killGraceSeconds}`);
|
|
110
|
+
child.kill("SIGKILL");
|
|
111
|
+
}, killGraceSeconds * 1e3);
|
|
112
|
+
escalation.unref();
|
|
113
|
+
settle({ statusCode: 500, message: "hook timed out" });
|
|
114
|
+
}, timeoutSeconds * 1e3);
|
|
115
|
+
timer.unref();
|
|
116
|
+
child.once("error", (error) => {
|
|
117
|
+
console.error(`[hook] hook=${hook} status=spawn_failed error=${error.message}`);
|
|
118
|
+
settle({ statusCode: 500, message: "hook could not be started" });
|
|
119
|
+
});
|
|
120
|
+
child.once("exit", (code) => {
|
|
121
|
+
clearTimeout(escalation);
|
|
122
|
+
if (code === 0) {
|
|
123
|
+
settle({ statusCode: 200, message: hook });
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
console.error(`[hook] hook=${hook} status=failed exit_code=${code}`);
|
|
127
|
+
settle({ statusCode: 500, message: "hook failed" });
|
|
128
|
+
});
|
|
129
|
+
if (payload !== void 0 && child.stdin) {
|
|
130
|
+
child.stdin.write(payload);
|
|
131
|
+
child.stdin.end();
|
|
132
|
+
} else {
|
|
133
|
+
child.stdin?.end();
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
async function dispatch(hook, rawBody) {
|
|
138
|
+
const env = { ...process.env };
|
|
139
|
+
let payload;
|
|
140
|
+
if (hook === "run") {
|
|
141
|
+
const envelope = parseRunEnvelope(rawBody);
|
|
142
|
+
env.MICROVM_ID = envelope.microvmId;
|
|
143
|
+
payload = envelope.runHookPayload;
|
|
144
|
+
}
|
|
145
|
+
const script = `${hooksDir}/${hook}`;
|
|
146
|
+
if (!hasScript(script)) {
|
|
147
|
+
return { statusCode: 200, message: `${hook} (no script)` };
|
|
148
|
+
}
|
|
149
|
+
const outcome = await runScript(hook, script, env, payload);
|
|
150
|
+
if (hook === "terminate" && outcome.statusCode !== 200) {
|
|
151
|
+
console.error("[hook] hook=terminate status=reported_ok_despite_failure");
|
|
152
|
+
return { statusCode: 200, message: "terminate (best effort)" };
|
|
153
|
+
}
|
|
154
|
+
return outcome;
|
|
155
|
+
}
|
|
156
|
+
return (0, node_http_1.createServer)((request, response) => {
|
|
157
|
+
const send = (statusCode, message) => {
|
|
158
|
+
response.writeHead(statusCode, { "content-type": "application/json" });
|
|
159
|
+
response.end(JSON.stringify({ message }));
|
|
160
|
+
};
|
|
161
|
+
const path = (request.url ?? "").split("?")[0];
|
|
162
|
+
const hook = exports2.HOOK_NAMES.find((name) => exports2.HOOK_PATHS[name] === path);
|
|
163
|
+
if (!hook) {
|
|
164
|
+
request.resume();
|
|
165
|
+
send(404, "unknown hook path");
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (request.method !== "POST") {
|
|
169
|
+
request.resume();
|
|
170
|
+
send(405, "hook requires POST");
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
void readBody(request).then(async (body) => {
|
|
174
|
+
if (body === null) {
|
|
175
|
+
send(413, "hook body too large");
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
try {
|
|
179
|
+
const outcome = await dispatch(hook, body);
|
|
180
|
+
send(outcome.statusCode, outcome.message);
|
|
181
|
+
} catch (error) {
|
|
182
|
+
send(400, error instanceof Error ? error.message : "bad request");
|
|
183
|
+
}
|
|
184
|
+
}).catch((error) => {
|
|
185
|
+
console.error(`[hook] hook=${hook} status=unhandled_error error=${error instanceof Error ? error.message : String(error)}`);
|
|
186
|
+
send(500, "hook failed");
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// ../lambda-microvm-runtime/dist/node-runtime.js
|
|
194
|
+
var require_node_runtime = __commonJS({
|
|
195
|
+
"../lambda-microvm-runtime/dist/node-runtime.js"(exports2) {
|
|
196
|
+
"use strict";
|
|
197
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
198
|
+
exports2.createNodeRuntime = createNodeRuntime2;
|
|
199
|
+
var node_child_process_1 = require("node:child_process");
|
|
200
|
+
var node_fs_1 = require("node:fs");
|
|
201
|
+
var runtime_1 = require_runtime();
|
|
202
|
+
function createNodeRuntime2({ hooksDir, timeoutSeconds, killGraceSeconds }) {
|
|
203
|
+
return (0, runtime_1.createMicrovmRuntime)({
|
|
204
|
+
hooksDir,
|
|
205
|
+
timeoutSeconds,
|
|
206
|
+
killGraceSeconds,
|
|
207
|
+
hasScript: (script) => {
|
|
208
|
+
try {
|
|
209
|
+
(0, node_fs_1.accessSync)(script, node_fs_1.constants.X_OK);
|
|
210
|
+
return true;
|
|
211
|
+
} catch {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
spawn: (script, options) => {
|
|
216
|
+
const child = (0, node_child_process_1.spawn)(script, {
|
|
217
|
+
env: options.env,
|
|
218
|
+
// stdin is a pipe so the run payload never has to travel in argv.
|
|
219
|
+
stdio: ["pipe", "inherit", "inherit"],
|
|
220
|
+
// Its own process group, so a signal can reach the tree the script
|
|
221
|
+
// built — a hook shells out, and stopping only the shell orphans the
|
|
222
|
+
// synchroniser or probe it was waiting on.
|
|
223
|
+
detached: true
|
|
224
|
+
});
|
|
225
|
+
child.kill = (signal) => {
|
|
226
|
+
try {
|
|
227
|
+
process.kill(-child.pid, signal);
|
|
228
|
+
return true;
|
|
229
|
+
} catch (error) {
|
|
230
|
+
if (error.code !== "ESRCH") {
|
|
231
|
+
console.error(`[hook] script=${script} status=signal_failed signal=${String(signal)} error=${error instanceof Error ? error.message : String(error)}`);
|
|
232
|
+
}
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
child.stdin?.on("error", (error) => {
|
|
237
|
+
console.error(`[hook] script=${script} status=stdin_write_failed error=${error.message}`);
|
|
238
|
+
});
|
|
239
|
+
return child;
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// ../lambda-microvm-runtime/dist/index.js
|
|
247
|
+
var require_dist = __commonJS({
|
|
248
|
+
"../lambda-microvm-runtime/dist/index.js"(exports2) {
|
|
249
|
+
"use strict";
|
|
250
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
251
|
+
exports2.createNodeRuntime = exports2.HOOK_SIGTERM_SECONDS = exports2.HOOK_PATH_PREFIX = exports2.HOOK_PATHS = exports2.HOOK_NAMES = exports2.parseRunEnvelope = exports2.createMicrovmRuntime = void 0;
|
|
252
|
+
var runtime_1 = require_runtime();
|
|
253
|
+
Object.defineProperty(exports2, "createMicrovmRuntime", { enumerable: true, get: function() {
|
|
254
|
+
return runtime_1.createMicrovmRuntime;
|
|
255
|
+
} });
|
|
256
|
+
Object.defineProperty(exports2, "parseRunEnvelope", { enumerable: true, get: function() {
|
|
257
|
+
return runtime_1.parseRunEnvelope;
|
|
258
|
+
} });
|
|
259
|
+
Object.defineProperty(exports2, "HOOK_NAMES", { enumerable: true, get: function() {
|
|
260
|
+
return runtime_1.HOOK_NAMES;
|
|
261
|
+
} });
|
|
262
|
+
Object.defineProperty(exports2, "HOOK_PATHS", { enumerable: true, get: function() {
|
|
263
|
+
return runtime_1.HOOK_PATHS;
|
|
264
|
+
} });
|
|
265
|
+
Object.defineProperty(exports2, "HOOK_PATH_PREFIX", { enumerable: true, get: function() {
|
|
266
|
+
return runtime_1.HOOK_PATH_PREFIX;
|
|
267
|
+
} });
|
|
268
|
+
Object.defineProperty(exports2, "HOOK_SIGTERM_SECONDS", { enumerable: true, get: function() {
|
|
269
|
+
return runtime_1.HOOK_SIGTERM_SECONDS;
|
|
270
|
+
} });
|
|
271
|
+
var node_runtime_1 = require_node_runtime();
|
|
272
|
+
Object.defineProperty(exports2, "createNodeRuntime", { enumerable: true, get: function() {
|
|
273
|
+
return node_runtime_1.createNodeRuntime;
|
|
274
|
+
} });
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
// src/microvm/image/hook-server.ts
|
|
279
|
+
var import_lambda_microvm_runtime = __toESM(require_dist());
|
|
280
|
+
|
|
281
|
+
// src/microvm/constants.ts
|
|
282
|
+
var HOOKS_PORT = 8080;
|
|
283
|
+
var HOOKS_DIR = "/etc/evident/hooks";
|
|
284
|
+
|
|
285
|
+
// src/microvm/image/hook-server.ts
|
|
286
|
+
var server = (0, import_lambda_microvm_runtime.createNodeRuntime)({ hooksDir: HOOKS_DIR });
|
|
287
|
+
var port = Number(process.env.HOOKS_PORT ?? HOOKS_PORT);
|
|
288
|
+
server.listen(port, "0.0.0.0", () => {
|
|
289
|
+
console.log(`[hook] server listening on 0.0.0.0:${port}`);
|
|
290
|
+
});
|