@evident-ai/runner-cdk 3.5.2-dev.f258bd6 → 3.5.2-dev.f80111b
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 +15 -6
- package/dist/image-version-pruner-lambda/handler.js +28250 -0
- package/dist/microvm/construct.d.ts +6 -0
- package/dist/microvm/construct.js +18 -0
- package/dist/microvm/image-version-pruner/construct.d.ts +13 -0
- package/dist/microvm/image-version-pruner/construct.js +79 -0
- package/dist/microvm/image-version-pruner/handler.d.ts +20 -0
- package/dist/microvm/image-version-pruner/handler.js +116 -0
- package/package.json +1 -1
|
@@ -91,6 +91,12 @@ export interface EvidentMicrovmConstructProps {
|
|
|
91
91
|
* throws at construction rather than silently disabling the reporter.
|
|
92
92
|
*/
|
|
93
93
|
readonly evidentRunnerId?: string;
|
|
94
|
+
/**
|
|
95
|
+
* Opts into bounding the per-image MicroVM version quota (L-F8BECE9C) for
|
|
96
|
+
* this deployment's images only, using the upstream engine defaults. Omitting
|
|
97
|
+
* this leaves the synthesised template byte-for-byte identical.
|
|
98
|
+
*/
|
|
99
|
+
readonly imageVersionPruning?: boolean;
|
|
94
100
|
}
|
|
95
101
|
/**
|
|
96
102
|
* The customer-account half of the per-session runner (#558): the MicroVM
|
|
@@ -44,6 +44,7 @@ const lambda_microvm_cdk_1 = require("@evident-ai/lambda-microvm-cdk");
|
|
|
44
44
|
const constants_1 = require("./constants");
|
|
45
45
|
const shapes_1 = require("./shapes");
|
|
46
46
|
const construct_1 = require("./image-version-reporter/construct");
|
|
47
|
+
const construct_2 = require("./image-version-pruner/construct");
|
|
47
48
|
// Image hooks build the snapshot, so they get minutes where the runtime hooks
|
|
48
49
|
// get AWS's 60 s.
|
|
49
50
|
const IMAGE_HOOK_TIMEOUT_SECONDS = 600;
|
|
@@ -263,6 +264,23 @@ class EvidentMicrovmConstruct extends constructs_1.Construct {
|
|
|
263
264
|
buildTriggers: Object.fromEntries(shapes.map((shape) => [shape.name, images.get(shape.name).buildInputs])),
|
|
264
265
|
});
|
|
265
266
|
}
|
|
267
|
+
if (props.imageVersionPruning) {
|
|
268
|
+
const pruner = new construct_2.ImageVersionPruner(this, 'ImageVersionPruner', {
|
|
269
|
+
imageArns: shapes.map((shape) => (0, lambda_microvm_cdk_1.microvmImageArn)({
|
|
270
|
+
region: cdk.Stack.of(this).region,
|
|
271
|
+
accountId: cdk.Stack.of(this).account,
|
|
272
|
+
imageName: shape.imageName,
|
|
273
|
+
})),
|
|
274
|
+
buildTriggers: Object.fromEntries(shapes.map((shape) => {
|
|
275
|
+
const { buildRoleArn: _buildRoleArn, ...buildInputs } = images.get(shape.name).buildInputs;
|
|
276
|
+
return [shape.name, buildInputs];
|
|
277
|
+
})),
|
|
278
|
+
});
|
|
279
|
+
// Free a quota slot before the publish that consumes it.
|
|
280
|
+
for (const shape of shapes) {
|
|
281
|
+
images.get(shape.name).node.addDependency(pruner);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
266
284
|
this.functionUrl = functionUrl.url;
|
|
267
285
|
this.shapeCatalogueJson = shapeCatalogueJson;
|
|
268
286
|
this.durableStateBucket = durableState;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Construct } from 'constructs';
|
|
2
|
+
import type { MicrovmImageBuildInputs } from '@evident-ai/lambda-microvm-cdk';
|
|
3
|
+
export type ImageVersionPrunerProps = {
|
|
4
|
+
readonly imageArns: readonly string[];
|
|
5
|
+
/**
|
|
6
|
+
* Build inputs are a CloudFormation-visible invocation trigger. The build role
|
|
7
|
+
* ARN is excluded because it belongs to the image subtree and would create a cycle.
|
|
8
|
+
*/
|
|
9
|
+
readonly buildTriggers: Record<string, Omit<MicrovmImageBuildInputs, 'buildRoleArn'>>;
|
|
10
|
+
};
|
|
11
|
+
export declare class ImageVersionPruner extends Construct {
|
|
12
|
+
constructor(scope: Construct, id: string, props: ImageVersionPrunerProps);
|
|
13
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.ImageVersionPruner = void 0;
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
const cdk = __importStar(require("aws-cdk-lib"));
|
|
39
|
+
const iam = __importStar(require("aws-cdk-lib/aws-iam"));
|
|
40
|
+
const lambda = __importStar(require("aws-cdk-lib/aws-lambda"));
|
|
41
|
+
const cr = __importStar(require("aws-cdk-lib/custom-resources"));
|
|
42
|
+
const constructs_1 = require("constructs");
|
|
43
|
+
class ImageVersionPruner extends constructs_1.Construct {
|
|
44
|
+
constructor(scope, id, props) {
|
|
45
|
+
super(scope, id);
|
|
46
|
+
const handler = new lambda.Function(this, 'Function', {
|
|
47
|
+
runtime: lambda.Runtime.NODEJS_22_X,
|
|
48
|
+
code: lambda.Code.fromAsset(path.join(__dirname, '..', '..', '..', 'dist', 'image-version-pruner-lambda')),
|
|
49
|
+
handler: 'handler.handler',
|
|
50
|
+
memorySize: 256,
|
|
51
|
+
timeout: cdk.Duration.minutes(5),
|
|
52
|
+
});
|
|
53
|
+
handler.addToRolePolicy(new iam.PolicyStatement({
|
|
54
|
+
actions: [
|
|
55
|
+
'lambda:GetMicrovmImage',
|
|
56
|
+
'lambda:ListMicrovmImageVersions',
|
|
57
|
+
'lambda:UpdateMicrovmImageVersion',
|
|
58
|
+
'lambda:DeleteMicrovmImageVersion',
|
|
59
|
+
],
|
|
60
|
+
resources: [...props.imageArns],
|
|
61
|
+
}));
|
|
62
|
+
handler.addToRolePolicy(new iam.PolicyStatement({
|
|
63
|
+
// Evidence-only read: this account-wide permission cannot authorize a mutation.
|
|
64
|
+
actions: ['lambda:ListMicrovms'],
|
|
65
|
+
resources: ['*'],
|
|
66
|
+
}));
|
|
67
|
+
const provider = new cr.Provider(this, 'Provider', {
|
|
68
|
+
onEventHandler: handler,
|
|
69
|
+
});
|
|
70
|
+
new cdk.CustomResource(this, 'Resource', {
|
|
71
|
+
serviceToken: provider.serviceToken,
|
|
72
|
+
properties: {
|
|
73
|
+
imageArns: props.imageArns,
|
|
74
|
+
buildTriggers: props.buildTriggers,
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
exports.ImageVersionPruner = ImageVersionPruner;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { LambdaMicrovmsClient } from '@aws-sdk/client-lambda-microvms';
|
|
2
|
+
import { type PruneImageVersionsOptions, type PruneSummary } from '@evident-ai/lambda-microvm-cdk/prune';
|
|
3
|
+
export declare const PRUNE_DEADLINE_MS: number;
|
|
4
|
+
export type ImageVersionPrunerEvent = {
|
|
5
|
+
RequestType: 'Create' | 'Update' | 'Delete';
|
|
6
|
+
ResourceProperties?: {
|
|
7
|
+
imageArns?: unknown;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
export type ImageVersionPrunerDependencies = {
|
|
11
|
+
pruneImageVersions: (options: PruneImageVersionsOptions) => Promise<PruneSummary>;
|
|
12
|
+
createClient: () => LambdaMicrovmsClient;
|
|
13
|
+
now: () => number;
|
|
14
|
+
};
|
|
15
|
+
type ImageVersionPrunerResponse = {
|
|
16
|
+
PhysicalResourceId: string;
|
|
17
|
+
};
|
|
18
|
+
export declare function handleImageVersionPrune(event: ImageVersionPrunerEvent, dependencies?: ImageVersionPrunerDependencies): Promise<ImageVersionPrunerResponse>;
|
|
19
|
+
export declare const handler: (event: ImageVersionPrunerEvent) => Promise<ImageVersionPrunerResponse>;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handler = exports.PRUNE_DEADLINE_MS = void 0;
|
|
4
|
+
exports.handleImageVersionPrune = handleImageVersionPrune;
|
|
5
|
+
const client_lambda_microvms_1 = require("@aws-sdk/client-lambda-microvms");
|
|
6
|
+
const prune_1 = require("@evident-ai/lambda-microvm-cdk/prune");
|
|
7
|
+
const PHYSICAL_RESOURCE_ID = 'evident-image-version-pruner';
|
|
8
|
+
exports.PRUNE_DEADLINE_MS = 4 * 60 * 1_000;
|
|
9
|
+
const runtimeDependencies = {
|
|
10
|
+
pruneImageVersions: prune_1.pruneImageVersions,
|
|
11
|
+
createClient: () => new client_lambda_microvms_1.LambdaMicrovmsClient({}),
|
|
12
|
+
now: Date.now,
|
|
13
|
+
};
|
|
14
|
+
function response() {
|
|
15
|
+
return { PhysicalResourceId: PHYSICAL_RESOURCE_ID };
|
|
16
|
+
}
|
|
17
|
+
function log(message) {
|
|
18
|
+
console.log(`[image-version-pruner] ${message}`);
|
|
19
|
+
}
|
|
20
|
+
function logError(message) {
|
|
21
|
+
console.error(`[image-version-pruner] ${message}`);
|
|
22
|
+
}
|
|
23
|
+
function readImageArns(value) {
|
|
24
|
+
if (!Array.isArray(value) || !value.every((imageArn) => typeof imageArn === 'string')) {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
29
|
+
async function raceWithDeadline(operation, controller) {
|
|
30
|
+
let timer;
|
|
31
|
+
const deadline = new Promise((resolve) => {
|
|
32
|
+
timer = setTimeout(() => resolve({ timedOut: true }), exports.PRUNE_DEADLINE_MS);
|
|
33
|
+
});
|
|
34
|
+
try {
|
|
35
|
+
const result = await Promise.race([
|
|
36
|
+
operation.then((summary) => ({ timedOut: false, summary })),
|
|
37
|
+
deadline,
|
|
38
|
+
]);
|
|
39
|
+
if (result.timedOut) {
|
|
40
|
+
controller.abort();
|
|
41
|
+
try {
|
|
42
|
+
await operation;
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
console.warn('MicroVM image version pruning cancellation observed', {
|
|
46
|
+
error: error instanceof Error ? error.message : String(error),
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
finally {
|
|
53
|
+
if (timer !== undefined) {
|
|
54
|
+
clearTimeout(timer);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async function handleImageVersionPrune(event, dependencies = runtimeDependencies) {
|
|
59
|
+
if (event.RequestType === 'Delete') {
|
|
60
|
+
return response();
|
|
61
|
+
}
|
|
62
|
+
const imageArns = readImageArns(event.ResourceProperties?.imageArns);
|
|
63
|
+
if (imageArns === undefined) {
|
|
64
|
+
console.warn('MicroVM image version pruning skipped', {
|
|
65
|
+
operation: event.RequestType,
|
|
66
|
+
reason: 'imageArns must be an array of strings',
|
|
67
|
+
});
|
|
68
|
+
return response();
|
|
69
|
+
}
|
|
70
|
+
if (imageArns.length === 0) {
|
|
71
|
+
console.warn('MicroVM image version pruning skipped', {
|
|
72
|
+
operation: event.RequestType,
|
|
73
|
+
reason: 'imageArns is empty',
|
|
74
|
+
});
|
|
75
|
+
return response();
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
const startedAt = dependencies.now();
|
|
79
|
+
const controller = new AbortController();
|
|
80
|
+
const operation = dependencies.pruneImageVersions({
|
|
81
|
+
client: dependencies.createClient(),
|
|
82
|
+
imageArns,
|
|
83
|
+
abortSignal: controller.signal,
|
|
84
|
+
log,
|
|
85
|
+
logError,
|
|
86
|
+
});
|
|
87
|
+
const result = await raceWithDeadline(operation, controller);
|
|
88
|
+
if (result.timedOut) {
|
|
89
|
+
console.warn('MicroVM image version pruning deadline reached', {
|
|
90
|
+
operation: event.RequestType,
|
|
91
|
+
imageArns,
|
|
92
|
+
deadlineMs: exports.PRUNE_DEADLINE_MS,
|
|
93
|
+
elapsedMs: dependencies.now() - startedAt,
|
|
94
|
+
});
|
|
95
|
+
return response();
|
|
96
|
+
}
|
|
97
|
+
console.log('MicroVM image version pruning complete', {
|
|
98
|
+
operation: event.RequestType,
|
|
99
|
+
imageArns,
|
|
100
|
+
deactivated: result.summary.deactivated.length,
|
|
101
|
+
deleted: result.summary.deleted.length,
|
|
102
|
+
skipped: result.summary.skipped.length,
|
|
103
|
+
stoppedOnQuota: result.summary.stoppedOnQuota,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
console.warn('MicroVM image version pruning failed', {
|
|
108
|
+
operation: event.RequestType,
|
|
109
|
+
imageArns,
|
|
110
|
+
error: error instanceof Error ? error.message : String(error),
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
return response();
|
|
114
|
+
}
|
|
115
|
+
const handler = (event) => handleImageVersionPrune(event);
|
|
116
|
+
exports.handler = handler;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evident-ai/runner-cdk",
|
|
3
|
-
"version": "3.5.2-dev.
|
|
3
|
+
"version": "3.5.2-dev.f80111b",
|
|
4
4
|
"description": "Reusable CDK constructs for an Evident agent runner: a single scale-to-zero Fargate runner (task + service + per-agent self-stop role + waker Lambda), or a per-session AWS Lambda MicroVM that boots on demand and suspends between messages. Instantiate once per agent from your own stack.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|