@capsulos/provider-lambda 0.1.1 → 0.1.2
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/dist/index.d.ts +8 -0
- package/dist/index.js +29 -3
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/create.test.ts +60 -0
- package/src/index.ts +28 -3
package/dist/index.d.ts
CHANGED
|
@@ -32,6 +32,14 @@ export declare class LambdaProvider implements Provider {
|
|
|
32
32
|
list(template?: string): Promise<CapsuleInfo[]>;
|
|
33
33
|
terminate(id: string): Promise<void>;
|
|
34
34
|
connect(id: string): Promise<Connection>;
|
|
35
|
+
/**
|
|
36
|
+
* Did this call's RunMicrovm launch the VM, or did its clientToken hand
|
|
37
|
+
* back one launched earlier (idempotency/recovery)? The API does not say,
|
|
38
|
+
* so: no clientToken → ours; otherwise ours when the VM started after we
|
|
39
|
+
* asked (with a minute of slack for clock skew). Unknown → not ours, which
|
|
40
|
+
* errs on the side of leaving a VM alive.
|
|
41
|
+
*/
|
|
42
|
+
private launchedBy;
|
|
35
43
|
/** A Connection from an endpoint already in hand (get() returns it): no extra GetMicrovm. */
|
|
36
44
|
connectionFor(id: string, endpoint: string): Connection;
|
|
37
45
|
/** Headers to reach another port of the capsule through the same endpoint. */
|
package/dist/index.js
CHANGED
|
@@ -48,6 +48,7 @@ export class LambdaProvider {
|
|
|
48
48
|
return `arn:aws:lambda:${this.opts.region}:${this.opts.accountId}:microvm-image:${template}`;
|
|
49
49
|
}
|
|
50
50
|
async create(spec, opts = {}) {
|
|
51
|
+
const t0 = Date.now();
|
|
51
52
|
const ingress = this.opts.ingressConnectorArn ??
|
|
52
53
|
`arn:aws:lambda:${this.opts.region}:aws:network-connector:aws-network-connector:ALL_INGRESS`;
|
|
53
54
|
const res = await this.client.send(new RunMicrovmCommand({
|
|
@@ -78,10 +79,17 @@ export class LambdaProvider {
|
|
|
78
79
|
}
|
|
79
80
|
catch (err) {
|
|
80
81
|
// Never leave a VM running without an owner: the caller gets an error,
|
|
81
|
-
// not a capsule, so the VM goes too (the error still names it).
|
|
82
|
+
// not a capsule, so the VM goes too (the error still names it). Unless
|
|
83
|
+
// the clientToken recovered a VM someone else launched: that one has
|
|
84
|
+
// an owner, so it stays and the error says so.
|
|
82
85
|
if (err instanceof CapsuleError && err.capsuleId === id) {
|
|
83
|
-
await this.
|
|
84
|
-
|
|
86
|
+
if (await this.launchedBy(id, t0, spec.clientToken)) {
|
|
87
|
+
await this.terminate(id).catch(() => undefined);
|
|
88
|
+
throw new CapsuleError(`${err.message}; terminated it`, err.status, { capsuleId: id });
|
|
89
|
+
}
|
|
90
|
+
throw new CapsuleError(`${err.message}; left it running (recovered through clientToken, it has another owner)`, err.status, {
|
|
91
|
+
capsuleId: id,
|
|
92
|
+
});
|
|
85
93
|
}
|
|
86
94
|
throw err;
|
|
87
95
|
}
|
|
@@ -144,6 +152,24 @@ export class LambdaProvider {
|
|
|
144
152
|
throw new CapsuleError(`microVM ${id} has no endpoint yet (state ${vm.state})`, undefined, { capsuleId: id });
|
|
145
153
|
return this.connectionFor(id, vm.endpoint);
|
|
146
154
|
}
|
|
155
|
+
/**
|
|
156
|
+
* Did this call's RunMicrovm launch the VM, or did its clientToken hand
|
|
157
|
+
* back one launched earlier (idempotency/recovery)? The API does not say,
|
|
158
|
+
* so: no clientToken → ours; otherwise ours when the VM started after we
|
|
159
|
+
* asked (with a minute of slack for clock skew). Unknown → not ours, which
|
|
160
|
+
* errs on the side of leaving a VM alive.
|
|
161
|
+
*/
|
|
162
|
+
async launchedBy(id, t0, clientToken) {
|
|
163
|
+
if (!clientToken)
|
|
164
|
+
return true;
|
|
165
|
+
try {
|
|
166
|
+
const vm = await this.client.send(new GetMicrovmCommand({ microvmIdentifier: id }));
|
|
167
|
+
return vm.startedAt !== undefined && vm.startedAt.getTime() >= t0 - 60_000;
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
147
173
|
/** A Connection from an endpoint already in hand (get() returns it): no extra GetMicrovm. */
|
|
148
174
|
connectionFor(id, endpoint) {
|
|
149
175
|
return {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capsulos/provider-lambda",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "capsulos provider for AWS Lambda MicroVMs: capsules on your own AWS account.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"keywords": [
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@aws-sdk/client-lambda-microvms": "^3.1158.0",
|
|
47
|
-
"@capsulos/sdk": "0.1.
|
|
47
|
+
"@capsulos/sdk": "0.1.2"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@types/node": "^22.20.1",
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// create(): what happens to the VM when the wait for RUNNING fails. The AWS
|
|
2
|
+
// client is stubbed: these tests are about ownership, not about the service.
|
|
3
|
+
import { describe, expect, it } from 'vitest';
|
|
4
|
+
import { CapsuleError } from '@capsulos/sdk';
|
|
5
|
+
import { LambdaProvider } from './index.js';
|
|
6
|
+
|
|
7
|
+
type Sent = { name: string; input: Record<string, unknown> };
|
|
8
|
+
|
|
9
|
+
function provider(vm: { state: string; startedAt?: Date }) {
|
|
10
|
+
const sent: Sent[] = [];
|
|
11
|
+
const p = new LambdaProvider({ region: 'us-east-1', accountId: '123' });
|
|
12
|
+
(p as unknown as { client: { send: (cmd: { constructor: { name: string }; input: Record<string, unknown> }) => Promise<unknown> } }).client = {
|
|
13
|
+
async send(cmd) {
|
|
14
|
+
sent.push({ name: cmd.constructor.name, input: cmd.input });
|
|
15
|
+
switch (cmd.constructor.name) {
|
|
16
|
+
case 'RunMicrovmCommand':
|
|
17
|
+
return { microvmId: 'mvm-1' };
|
|
18
|
+
case 'GetMicrovmCommand':
|
|
19
|
+
return { microvmId: 'mvm-1', state: vm.state, startedAt: vm.startedAt, endpoint: 'x.on.aws' };
|
|
20
|
+
case 'TerminateMicrovmCommand':
|
|
21
|
+
return {};
|
|
22
|
+
default:
|
|
23
|
+
throw new Error(`unexpected ${cmd.constructor.name}`);
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
return { p, sent, terminated: () => sent.filter((s) => s.name === 'TerminateMicrovmCommand').length };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
describe('LambdaProvider.create on timeout', () => {
|
|
31
|
+
it('terminates a VM it launched itself and names it in the error', async () => {
|
|
32
|
+
const { p, terminated } = provider({ state: 'PENDING', startedAt: new Date() });
|
|
33
|
+
const err = await p.create({ template: 't' }, { timeoutMs: 1 }).catch((e: unknown) => e);
|
|
34
|
+
expect(err).toBeInstanceOf(CapsuleError);
|
|
35
|
+
expect((err as CapsuleError).capsuleId).toBe('mvm-1');
|
|
36
|
+
expect((err as CapsuleError).message).toMatch(/terminated it/);
|
|
37
|
+
expect(terminated()).toBe(1);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('leaves alone a VM that the clientToken recovered from an earlier launch', async () => {
|
|
41
|
+
const { p, terminated } = provider({ state: 'PENDING', startedAt: new Date(Date.now() - 10 * 60_000) });
|
|
42
|
+
const err = await p.create({ template: 't', clientToken: 'row-1' }, { timeoutMs: 1 }).catch((e: unknown) => e);
|
|
43
|
+
expect((err as CapsuleError).message).toMatch(/left it running/);
|
|
44
|
+
expect((err as CapsuleError).capsuleId).toBe('mvm-1');
|
|
45
|
+
expect(terminated()).toBe(0);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('with a clientToken, a VM that started after the call is still ours', async () => {
|
|
49
|
+
const { p, terminated } = provider({ state: 'PENDING', startedAt: new Date(Date.now() + 1000) });
|
|
50
|
+
await p.create({ template: 't', clientToken: 'row-1' }, { timeoutMs: 1 }).catch(() => undefined);
|
|
51
|
+
expect(terminated()).toBe(1);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('accepts a SUSPENDED VM as alive', async () => {
|
|
55
|
+
const { p } = provider({ state: 'SUSPENDED', startedAt: new Date() });
|
|
56
|
+
const handle = await p.create({ template: 't', clientToken: 'row-1' });
|
|
57
|
+
expect(handle.state).toBe('suspended');
|
|
58
|
+
expect(handle.connection.baseUrl).toBe('https://x.on.aws');
|
|
59
|
+
});
|
|
60
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -88,6 +88,7 @@ export class LambdaProvider implements Provider {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
async create(spec: CapsuleSpec, opts: CreateOptions = {}): Promise<CapsuleHandle> {
|
|
91
|
+
const t0 = Date.now();
|
|
91
92
|
const ingress =
|
|
92
93
|
this.opts.ingressConnectorArn ??
|
|
93
94
|
`arn:aws:lambda:${this.opts.region}:aws:network-connector:aws-network-connector:ALL_INGRESS`;
|
|
@@ -119,10 +120,17 @@ export class LambdaProvider implements Provider {
|
|
|
119
120
|
info = await this.waitForRunning(id, opts);
|
|
120
121
|
} catch (err) {
|
|
121
122
|
// Never leave a VM running without an owner: the caller gets an error,
|
|
122
|
-
// not a capsule, so the VM goes too (the error still names it).
|
|
123
|
+
// not a capsule, so the VM goes too (the error still names it). Unless
|
|
124
|
+
// the clientToken recovered a VM someone else launched: that one has
|
|
125
|
+
// an owner, so it stays and the error says so.
|
|
123
126
|
if (err instanceof CapsuleError && err.capsuleId === id) {
|
|
124
|
-
await this.
|
|
125
|
-
|
|
127
|
+
if (await this.launchedBy(id, t0, spec.clientToken)) {
|
|
128
|
+
await this.terminate(id).catch(() => undefined);
|
|
129
|
+
throw new CapsuleError(`${err.message}; terminated it`, err.status, { capsuleId: id });
|
|
130
|
+
}
|
|
131
|
+
throw new CapsuleError(`${err.message}; left it running (recovered through clientToken, it has another owner)`, err.status, {
|
|
132
|
+
capsuleId: id,
|
|
133
|
+
});
|
|
126
134
|
}
|
|
127
135
|
throw err;
|
|
128
136
|
}
|
|
@@ -183,6 +191,23 @@ export class LambdaProvider implements Provider {
|
|
|
183
191
|
return this.connectionFor(id, vm.endpoint);
|
|
184
192
|
}
|
|
185
193
|
|
|
194
|
+
/**
|
|
195
|
+
* Did this call's RunMicrovm launch the VM, or did its clientToken hand
|
|
196
|
+
* back one launched earlier (idempotency/recovery)? The API does not say,
|
|
197
|
+
* so: no clientToken → ours; otherwise ours when the VM started after we
|
|
198
|
+
* asked (with a minute of slack for clock skew). Unknown → not ours, which
|
|
199
|
+
* errs on the side of leaving a VM alive.
|
|
200
|
+
*/
|
|
201
|
+
private async launchedBy(id: string, t0: number, clientToken?: string): Promise<boolean> {
|
|
202
|
+
if (!clientToken) return true;
|
|
203
|
+
try {
|
|
204
|
+
const vm = await this.client.send(new GetMicrovmCommand({ microvmIdentifier: id }));
|
|
205
|
+
return vm.startedAt !== undefined && vm.startedAt.getTime() >= t0 - 60_000;
|
|
206
|
+
} catch {
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
186
211
|
/** A Connection from an endpoint already in hand (get() returns it): no extra GetMicrovm. */
|
|
187
212
|
connectionFor(id: string, endpoint: string): Connection {
|
|
188
213
|
return {
|