@kici-dev/orchestrator 0.13.0 → 0.14.0
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/agent/dispatcher.d.ts +17 -0
- package/dist/cancel/peer-job-cancel.d.ts +12 -0
- package/dist/cli/api-client.d.ts +73 -0
- package/dist/cli/commands/cluster-settings.d.ts +31 -1
- package/dist/cli/commands/peer.d.ts +27 -1
- package/dist/cli/commands/scaler.d.ts +40 -4
- package/dist/cli.js +2210 -933
- package/dist/cluster/cluster-settings-reader.d.ts +8 -2
- package/dist/cluster/coordinator.d.ts +34 -8
- package/dist/cluster/peer-client.d.ts +37 -4
- package/dist/cluster/peer-forget.d.ts +138 -0
- package/dist/cluster/peer-handler.d.ts +18 -1
- package/dist/cluster/scaler-orphans-peer.d.ts +40 -0
- package/dist/cluster/settings-propagation.d.ts +85 -0
- package/dist/cluster/worker-cluster-settings.d.ts +29 -0
- package/dist/config.d.ts +9 -0
- package/dist/db/migrations/154_cluster_settings_firecracker_api_socket_wait.d.ts +4 -0
- package/dist/db/migrations/155_org_settings_reroute_spawn_retry.d.ts +17 -0
- package/dist/db/migrations/156_kici_events_match_outcome.d.ts +17 -0
- package/dist/db/types.d.ts +16 -0
- package/dist/events/event-router.d.ts +7 -1
- package/dist/events/event-store.d.ts +20 -3
- package/dist/events/types.d.ts +45 -0
- package/dist/helpers/windows-batch.d.ts +59 -0
- package/dist/index.js +60 -0
- package/dist/metrics/prometheus.d.ts +2 -0
- package/dist/orchestrator-core.d.ts +7 -0
- package/dist/registration/registration-store.d.ts +10 -0
- package/dist/routes/admin-cluster-settings.d.ts +13 -1
- package/dist/routes/admin-event-inspect.d.ts +36 -0
- package/dist/routes/admin-peer-forget.d.ts +43 -0
- package/dist/routes/admin-scaler-orphans.d.ts +64 -0
- package/dist/routes/admin.d.ts +23 -0
- package/dist/scaler/backend-factory.d.ts +6 -1
- package/dist/scaler/bare-metal-backend.d.ts +17 -15
- package/dist/scaler/bare-metal-launch.d.ts +90 -0
- package/dist/scaler/firecracker-backend.d.ts +181 -7
- package/dist/scaler/live-vms.d.ts +57 -0
- package/dist/scaler/manager.d.ts +112 -3
- package/dist/scaler/orphan-requests.d.ts +23 -0
- package/dist/scaler/overlay-template.d.ts +38 -0
- package/dist/scaler/scaler-state-store.d.ts +7 -0
- package/dist/scaler/spawn-errors.d.ts +17 -0
- package/dist/scaler/startup-orphan-sweep.d.ts +15 -0
- package/dist/scaler/types.d.ts +21 -2
- package/dist/scaler/vm-job-bindings.d.ts +17 -0
- package/dist/secrets/rbac.d.ts +7 -1
- package/dist/server.js +5498 -2193
- package/dist/standalone.js +10754 -7451
- package/dist/worker/in-memory-execution-tracker.d.ts +5 -0
- package/dist/worker/in-memory-job-queue.d.ts +21 -1
- package/dist/worker/queued-reroute-release.d.ts +39 -0
- package/dist/worker/reroute-spawn-control.d.ts +58 -0
- package/dist/worker/spawn-retry-budget.d.ts +87 -0
- package/dist/worker-core.d.ts +11 -2
- package/installer-image-digests.json +3 -3
- package/package.json +4 -4
- package/sbom.spdx.json +53 -53
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Starting and ending a bare-metal agent process.
|
|
3
|
+
*
|
|
4
|
+
* Each decision about the launch is a pure function, so a test computes the
|
|
5
|
+
* exact command a host runs without starting anything:
|
|
6
|
+
* - Windows runs a `.cmd` / `.bat` launcher through cmd.exe. Node.js refuses
|
|
7
|
+
* to start a batch file without a shell, and every launcher a KiCI package
|
|
8
|
+
* ships on Windows is one.
|
|
9
|
+
* - Linux with `enforceCgroups` wraps the agent in a transient systemd scope.
|
|
10
|
+
* - Everything else runs the binary directly.
|
|
11
|
+
*/
|
|
12
|
+
import { type ChildProcess, type SpawnOptions } from 'node:child_process';
|
|
13
|
+
import type { EffectiveLimits } from './types.js';
|
|
14
|
+
export interface AgentLaunchInput {
|
|
15
|
+
binaryPath: string;
|
|
16
|
+
agentId: string;
|
|
17
|
+
effectiveLimits?: EffectiveLimits;
|
|
18
|
+
enforceCgroups: boolean;
|
|
19
|
+
platform: NodeJS.Platform;
|
|
20
|
+
/** The orchestrator's own environment; read for COMSPEC and SystemRoot on Windows. */
|
|
21
|
+
hostEnv: NodeJS.ProcessEnv;
|
|
22
|
+
}
|
|
23
|
+
/** A command and its arguments, as `spawn` / `execFile` take them. */
|
|
24
|
+
export interface AgentLaunch {
|
|
25
|
+
command: string;
|
|
26
|
+
args: string[];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Why a Windows host cannot run `binaryPath` through cmd.exe, or null. Only a
|
|
30
|
+
* batch file on a Windows host goes through cmd.exe, so any other path or host
|
|
31
|
+
* is accepted.
|
|
32
|
+
*/
|
|
33
|
+
export declare function agentLaunchRefusal(binaryPath: string, platform: NodeJS.Platform): string | null;
|
|
34
|
+
/**
|
|
35
|
+
* The command that starts the agent on this host.
|
|
36
|
+
*
|
|
37
|
+
* `CPUQuota` is a percent (1.0 cpus = 100%) and `MemoryMax` a raw byte count.
|
|
38
|
+
* The scope name embeds the agent id, so `systemctl --user status
|
|
39
|
+
* kici-agent-<agentId>.scope` finds it.
|
|
40
|
+
*
|
|
41
|
+
* @throws DeterministicSpawnError for a Windows batch path cmd.exe would read
|
|
42
|
+
* as syntax. Config validation refuses such a path first.
|
|
43
|
+
*/
|
|
44
|
+
export declare function buildAgentLaunch(input: AgentLaunchInput): AgentLaunch;
|
|
45
|
+
/**
|
|
46
|
+
* Spawn options for an agent process.
|
|
47
|
+
*
|
|
48
|
+
* On Linux and macOS the agent is detached: it leads its own process group,
|
|
49
|
+
* which `destroy()` signals.
|
|
50
|
+
*
|
|
51
|
+
* On Windows it is not. A detached process starts with no console, and the
|
|
52
|
+
* cmd.exe that runs a batch launcher then starts the agent's `node` in a new,
|
|
53
|
+
* hidden console of its own, so the agent's output never reaches the pipes the
|
|
54
|
+
* scaler reads. Not detached, cmd.exe gets a hidden console (`windowsHide`
|
|
55
|
+
* with no inherited stdio) that `node` shares, along with the pipes. Node.js
|
|
56
|
+
* puts a child that is not detached into a job object that ends it when the
|
|
57
|
+
* orchestrator exits; processes that child starts are not in that job, so the
|
|
58
|
+
* agent itself outlives an orchestrator restart, as it does on Linux and macOS.
|
|
59
|
+
*/
|
|
60
|
+
export declare function agentSpawnOptions(env: Record<string, string>, platform: NodeJS.Platform): SpawnOptions;
|
|
61
|
+
/**
|
|
62
|
+
* Start the agent process.
|
|
63
|
+
*
|
|
64
|
+
* Node.js reports a missing or unreadable binary later, through the child's
|
|
65
|
+
* `'error'` event. Any other launch failure throws here, synchronously: the
|
|
66
|
+
* host refused this exact invocation (a Windows batch file without cmd.exe, an
|
|
67
|
+
* environment string longer than the kernel accepts), and it will refuse the
|
|
68
|
+
* next one the same way.
|
|
69
|
+
*
|
|
70
|
+
* @throws DeterministicSpawnError for that synchronous refusal.
|
|
71
|
+
*/
|
|
72
|
+
export declare function startAgentProcess(launch: AgentLaunch, env: Record<string, string>, platform: NodeJS.Platform): ChildProcess;
|
|
73
|
+
/** The taskkill command that ends a Windows process and every process it started. */
|
|
74
|
+
export declare function windowsTreeKill(pid: number, hostEnv: NodeJS.ProcessEnv): AgentLaunch;
|
|
75
|
+
/** What a taskkill exit code means for the tree it was asked to end. */
|
|
76
|
+
export declare enum TreeKillOutcome {
|
|
77
|
+
Ended = "ended",
|
|
78
|
+
/** No process had the pid: the tree exited on its own first. */
|
|
79
|
+
AlreadyGone = "already-gone",
|
|
80
|
+
Failed = "failed"
|
|
81
|
+
}
|
|
82
|
+
/** taskkill exits 0 when it ended the tree and 128 when no process had the pid. */
|
|
83
|
+
export declare function treeKillOutcome(exitCode: number | null): TreeKillOutcome;
|
|
84
|
+
/**
|
|
85
|
+
* End a Windows process tree with `taskkill /T /F`. Resolves when the tree was
|
|
86
|
+
* ended or was already gone; rejects with taskkill's exit code and stderr
|
|
87
|
+
* otherwise.
|
|
88
|
+
*/
|
|
89
|
+
export declare function killWindowsTree(pid: number, hostEnv: NodeJS.ProcessEnv, timeoutMs: number): Promise<void>;
|
|
90
|
+
//# sourceMappingURL=bare-metal-launch.d.ts.map
|
|
@@ -16,11 +16,19 @@
|
|
|
16
16
|
*
|
|
17
17
|
* Follows the Docker backend pattern closely for consistency.
|
|
18
18
|
*/
|
|
19
|
+
import { ScalerVmTracker, type ScalerVmStopResult } from '@kici-dev/engine';
|
|
19
20
|
import { type ToolRequirement } from '@kici-dev/shared';
|
|
21
|
+
import type { LiveVmBackend, LiveVmProbe } from './live-vms.js';
|
|
20
22
|
import type { IpAllocator, IpAllocationResult } from './ip-allocator.js';
|
|
21
23
|
import type { AgentTokenStore } from '../agent/token-store.js';
|
|
22
24
|
import { type FirecrackerBridgeConfig, type BridgeHealth, type ExecOptions } from '../firecracker/host-network.js';
|
|
23
25
|
import type { ScalerBackend, ScalerDestroyContext, ScalerEntry, ManagedAgent, LabelSetConfig, ScalerEventCallback, ValidationResult, EffectiveLimits, SpawnContext } from './types.js';
|
|
26
|
+
/**
|
|
27
|
+
* How long a spawn waits for the VM's API socket when nothing configures it.
|
|
28
|
+
* Generous on purpose: under heavy disk write-back the jailer's copy of the
|
|
29
|
+
* firecracker binary into the chroot alone can take several seconds.
|
|
30
|
+
*/
|
|
31
|
+
export declare const DEFAULT_API_SOCKET_WAIT_MS = 30000;
|
|
24
32
|
/** Extended ManagedAgent that includes the allocated IP for cleanup */
|
|
25
33
|
export interface FirecrackerManagedAgent extends ManagedAgent {
|
|
26
34
|
/** Allocated IP address for TAP/IP release during destroy */
|
|
@@ -35,6 +43,12 @@ export interface FirecrackerManagedAgent extends ManagedAgent {
|
|
|
35
43
|
* default from.
|
|
36
44
|
*/
|
|
37
45
|
hostAccess?: string[];
|
|
46
|
+
/**
|
|
47
|
+
* Set once the VM's agent registered with the orchestrator. Until then a VM
|
|
48
|
+
* that stops on its own is a failed spawn, and nothing else will tear it
|
|
49
|
+
* down; afterwards the agent's disconnect does.
|
|
50
|
+
*/
|
|
51
|
+
registered?: boolean;
|
|
38
52
|
}
|
|
39
53
|
export interface FirecrackerScalerBackendOptions {
|
|
40
54
|
/** Human-readable name for this scaler */
|
|
@@ -92,6 +106,17 @@ export interface FirecrackerScalerBackendOptions {
|
|
|
92
106
|
* `cluster_settings.agent_token_ttl_ms` override; falls back to `tokenTtlMs`.
|
|
93
107
|
*/
|
|
94
108
|
tokenTtlProvider?: () => Promise<number>;
|
|
109
|
+
/**
|
|
110
|
+
* How long a spawn waits for the VM's API socket after the jailer starts, in
|
|
111
|
+
* ms. Default {@link DEFAULT_API_SOCKET_WAIT_MS}.
|
|
112
|
+
*/
|
|
113
|
+
apiSocketWaitMs?: number;
|
|
114
|
+
/**
|
|
115
|
+
* Live per-spawn resolver for the API-socket wait, so the fleet-wide
|
|
116
|
+
* `cluster_settings.firecracker_api_socket_wait_ms` applies without a
|
|
117
|
+
* restart. Falls back to `apiSocketWaitMs`.
|
|
118
|
+
*/
|
|
119
|
+
apiSocketWaitMsProvider?: () => Promise<number>;
|
|
95
120
|
/** Agent roles for this scaler. undefined = all, [] = execution only. */
|
|
96
121
|
roles?: string[];
|
|
97
122
|
/**
|
|
@@ -113,7 +138,7 @@ export interface FirecrackerScalerBackendOptions {
|
|
|
113
138
|
/** Injectable bridge provisioner (tests). @default host-network provisionBridge */
|
|
114
139
|
provisionBridgeFn?: (cfg: FirecrackerBridgeConfig, opts: ExecOptions) => Promise<void>;
|
|
115
140
|
}
|
|
116
|
-
export declare class FirecrackerScalerBackend implements ScalerBackend {
|
|
141
|
+
export declare class FirecrackerScalerBackend implements ScalerBackend, LiveVmBackend {
|
|
117
142
|
readonly type: "firecracker";
|
|
118
143
|
readonly spawnsOnLocalHost = true;
|
|
119
144
|
maxAgents: number;
|
|
@@ -129,6 +154,8 @@ export declare class FirecrackerScalerBackend implements ScalerBackend {
|
|
|
129
154
|
* the failure instead of leaving the reservation held until restart.
|
|
130
155
|
*/
|
|
131
156
|
private readonly spawnFailureHandlers;
|
|
157
|
+
/** Liveness probes of spawned VMs whose agent has not registered yet, by agent id. */
|
|
158
|
+
private readonly registrationWatches;
|
|
132
159
|
private _labelSets;
|
|
133
160
|
private readonly name;
|
|
134
161
|
private readonly ipAllocator;
|
|
@@ -136,6 +163,8 @@ export declare class FirecrackerScalerBackend implements ScalerBackend {
|
|
|
136
163
|
private readonly jailerPath;
|
|
137
164
|
private readonly kernelPath;
|
|
138
165
|
private readonly chrootBaseDir;
|
|
166
|
+
/** Pre-formatted overlay drive templates under {@link chrootBaseDir}. */
|
|
167
|
+
private readonly overlayTemplates;
|
|
139
168
|
private readonly uid;
|
|
140
169
|
private readonly gid;
|
|
141
170
|
private readonly vcpuCount;
|
|
@@ -152,6 +181,8 @@ export declare class FirecrackerScalerBackend implements ScalerBackend {
|
|
|
152
181
|
private readonly tokenStore?;
|
|
153
182
|
private readonly tokenTtlMs;
|
|
154
183
|
private readonly tokenTtlProvider?;
|
|
184
|
+
private readonly apiSocketWaitMs;
|
|
185
|
+
private readonly apiSocketWaitMsProvider?;
|
|
155
186
|
private readonly roles;
|
|
156
187
|
private readonly requireSudo;
|
|
157
188
|
private readonly autoProvisionHost;
|
|
@@ -163,6 +194,8 @@ export declare class FirecrackerScalerBackend implements ScalerBackend {
|
|
|
163
194
|
private orphanSweepTimer;
|
|
164
195
|
/** Guard against re-entrant sweeps if a single run takes longer than the interval. */
|
|
165
196
|
private orphanSweepInFlight;
|
|
197
|
+
/** Tail of the host reclaim chain; see {@link withHostReclaimLock}. */
|
|
198
|
+
private hostReclaimChain;
|
|
166
199
|
constructor(options: FirecrackerScalerBackendOptions);
|
|
167
200
|
/**
|
|
168
201
|
* Declare required tools for a firecracker scaler entry.
|
|
@@ -238,6 +271,56 @@ export declare class FirecrackerScalerBackend implements ScalerBackend {
|
|
|
238
271
|
* so a self-heal that fires twice does not duplicate the rules.
|
|
239
272
|
*/
|
|
240
273
|
private reapplyIsolationForTrackedVms;
|
|
274
|
+
markRegistered(managedId: string): void;
|
|
275
|
+
/**
|
|
276
|
+
* Wait for the VM's API socket for the live setting (else the configured
|
|
277
|
+
* default), and throw when it does not appear in that time.
|
|
278
|
+
*/
|
|
279
|
+
private waitForApiSocket;
|
|
280
|
+
/**
|
|
281
|
+
* Keep loosening a rootless VM's chroot permissions while firecracker starts.
|
|
282
|
+
*
|
|
283
|
+
* The jailer chmods the chroot root and every directory it creates (`/run`)
|
|
284
|
+
* to 0700 owned by its `--uid`, which locks out an orchestrator running as
|
|
285
|
+
* another user from `/run/firecracker.socket`. A `chmod -R go+rX` every
|
|
286
|
+
* 100 ms reopens each one as soon as it appears; it only changes the
|
|
287
|
+
* host-side view. One chmod runs at a time: on a slow disk a `sudo chmod -R`
|
|
288
|
+
* can outlast the tick, and overlapping ones would pile up for the whole
|
|
289
|
+
* wait.
|
|
290
|
+
*
|
|
291
|
+
* @returns a function that stops the loosening
|
|
292
|
+
*/
|
|
293
|
+
private loosenChrootPermissions;
|
|
294
|
+
/** Record a spawn that resolved, and watch its VM until its agent registers. */
|
|
295
|
+
private markSpawned;
|
|
296
|
+
/**
|
|
297
|
+
* Tear the VM down if its process stops before its agent registers.
|
|
298
|
+
*
|
|
299
|
+
* A guest whose init exits — the agent's fatal startup error, a kernel panic
|
|
300
|
+
* — reboots, and firecracker exits. Its agent will never register, and
|
|
301
|
+
* nothing else releases the VM's `maxAgents` slot, TAP, nft rules, IP and
|
|
302
|
+
* chroot until the manager's stale-spawn prune. The jailer child's own exit
|
|
303
|
+
* cannot tell this: under `--new-pid-ns` it exits 0 right after the start.
|
|
304
|
+
* So the VM's process is probed through its PID file, and the probe stops
|
|
305
|
+
* once the agent registers (its disconnect owns the teardown from then on),
|
|
306
|
+
* the VM is being destroyed, or it is no longer tracked.
|
|
307
|
+
*/
|
|
308
|
+
private watchUntilRegistered;
|
|
309
|
+
/**
|
|
310
|
+
* Whether there is positive evidence that a VM's firecracker process is
|
|
311
|
+
* gone: its PID file does not exist, the PID it names no longer exists, or
|
|
312
|
+
* that PID now belongs to a process that started after `sinceUptimeS`
|
|
313
|
+
* (seconds since boot, taken once firecracker was already running).
|
|
314
|
+
*
|
|
315
|
+
* Any other failure — an unreadable PID file, a `/proc` this user cannot
|
|
316
|
+
* see — is not evidence, so the answer is "not gone". The caller tears the
|
|
317
|
+
* VM down on a "gone", and tearing down a live VM leaves firecracker running
|
|
318
|
+
* without its TAP, IP and chroot.
|
|
319
|
+
*/
|
|
320
|
+
private isVmProcessGone;
|
|
321
|
+
/** Stop forwarding a VM's serial console and VMM logs. */
|
|
322
|
+
private stopLogTailing;
|
|
323
|
+
private stopRegistrationWatch;
|
|
241
324
|
destroy(managedId: string, _context?: ScalerDestroyContext): Promise<void>;
|
|
242
325
|
/**
|
|
243
326
|
* Force-reclaim a VM this backend no longer tracks in memory.
|
|
@@ -261,6 +344,84 @@ export declare class FirecrackerScalerBackend implements ScalerBackend {
|
|
|
261
344
|
* finish a reclaim the chroot already authorized.
|
|
262
345
|
*/
|
|
263
346
|
reapUnowned(managedId: string): Promise<boolean>;
|
|
347
|
+
private reapUnownedLocked;
|
|
348
|
+
/** Whether the jailer chroot for `vmId` exists on this host. */
|
|
349
|
+
private hasChrootOnHost;
|
|
350
|
+
/**
|
|
351
|
+
* The `ip_allocations` row for `vmId`, or null. Best effort: a failed read
|
|
352
|
+
* only costs the IP release, which the next sweep redoes.
|
|
353
|
+
*/
|
|
354
|
+
private lookupAllocation;
|
|
355
|
+
/** Whether this scaler's allocator holds the address of `vmId`. */
|
|
356
|
+
ownsAllocationFor(vmId: string): Promise<boolean>;
|
|
357
|
+
/**
|
|
358
|
+
* Reclaim the host resources of a VM nothing tracks, once its process is
|
|
359
|
+
* dead: forward its remaining logs, remove its isolation rules, TAP and IP,
|
|
360
|
+
* and delete its chroot.
|
|
361
|
+
*
|
|
362
|
+
* A second firecracker scaler on this host has its own IP space and its own
|
|
363
|
+
* teardown, so only a row naming THIS scaler is ours to release. With no such
|
|
364
|
+
* row (a worker's in-memory allocator forgets every row on restart) the TAP
|
|
365
|
+
* named after the VM's id is deleted: the name derives from this VM's id
|
|
366
|
+
* alone, so it cannot be another VM's.
|
|
367
|
+
*/
|
|
368
|
+
private teardownUntrackedVm;
|
|
369
|
+
/** Whether a live VM is one this backend spawned and still tracks. */
|
|
370
|
+
isTrackingVm(vmId: string): boolean;
|
|
371
|
+
/**
|
|
372
|
+
* Every VM under this scaler's chroot base whose firecracker process is
|
|
373
|
+
* running right now. A VM whose process is gone, or whose PID number was
|
|
374
|
+
* recycled, is dead and belongs to the orphan sweep, so it is not listed.
|
|
375
|
+
*/
|
|
376
|
+
listLiveVms(): Promise<LiveVmProbe[]>;
|
|
377
|
+
/**
|
|
378
|
+
* Stop one live VM nothing on this node tracks, on an operator's request.
|
|
379
|
+
*
|
|
380
|
+
* The tracked check that authorises the kill and the first `kill(2)` run in
|
|
381
|
+
* one synchronous tick: tracking only changes between ticks (a registration,
|
|
382
|
+
* a rehydrated spawn row, a new spawn), so nothing can claim the VM between
|
|
383
|
+
* the check and the signal. The kernel can still recycle the PID number in
|
|
384
|
+
* that window; `destroy` and `reapUnowned` carry the same window, and Node
|
|
385
|
+
* has no pidfd to close it.
|
|
386
|
+
*
|
|
387
|
+
* Runs under the host reclaim lock, so a sweep cannot release this VM's
|
|
388
|
+
* address to a new VM while the stop is still removing its rules.
|
|
389
|
+
*
|
|
390
|
+
* @param trackersOf what else on this node tracks the VM, read in the
|
|
391
|
+
* kill's tick; it must not await.
|
|
392
|
+
*/
|
|
393
|
+
stopUntrackedVm(vmId: string, trackersOf: () => ScalerVmTracker[]): Promise<ScalerVmStopResult>;
|
|
394
|
+
private stopUntrackedVmLocked;
|
|
395
|
+
/**
|
|
396
|
+
* SIGKILL a VM's firecracker process.
|
|
397
|
+
*
|
|
398
|
+
* The jailer drops firecracker to `--uid`, so an orchestrator running as
|
|
399
|
+
* another unprivileged user gets EPERM from `kill(2)`. On a rootless host
|
|
400
|
+
* (`requireSudo`) the signal goes through `sudo -n -u #<uid> kill`; anywhere
|
|
401
|
+
* else EPERM means the process lacks CAP_KILL. ESRCH means the process is
|
|
402
|
+
* already gone, which is the outcome wanted.
|
|
403
|
+
*
|
|
404
|
+
* The first statement is the synchronous `kill(2)`: `stopUntrackedVm` relies
|
|
405
|
+
* on the signal leaving in its caller's tick.
|
|
406
|
+
*/
|
|
407
|
+
private signalVmProcess;
|
|
408
|
+
/** Whether `/proc/<pid>` names a process right now. */
|
|
409
|
+
private procEntryExists;
|
|
410
|
+
/**
|
|
411
|
+
* {@link signalVmProcess} for the teardown paths: a failure is logged, never
|
|
412
|
+
* swallowed, and reported so the caller keeps the chroot of a process that
|
|
413
|
+
* still runs.
|
|
414
|
+
*
|
|
415
|
+
* @returns whether the process was signalled or was already gone
|
|
416
|
+
*/
|
|
417
|
+
private signalVmProcessOrLog;
|
|
418
|
+
/**
|
|
419
|
+
* Serialise host reclaims: the orphan sweep, the unowned-VM reclaim and the
|
|
420
|
+
* operator stop. Each frees addresses and deletes rules and TAPs by name, so
|
|
421
|
+
* two interleaved can hand an address one is still tearing down to a new VM
|
|
422
|
+
* and then delete the new VM's rules.
|
|
423
|
+
*/
|
|
424
|
+
private withHostReclaimLock;
|
|
264
425
|
shutdownAll(): Promise<void>;
|
|
265
426
|
/**
|
|
266
427
|
* Clear MMDS data for an agent after receiving config.ack via WS.
|
|
@@ -295,6 +456,7 @@ export declare class FirecrackerScalerBackend implements ScalerBackend {
|
|
|
295
456
|
* Returns the count of cleaned orphans.
|
|
296
457
|
*/
|
|
297
458
|
cleanupOrphans(): Promise<number>;
|
|
459
|
+
private cleanupOrphansLocked;
|
|
298
460
|
/**
|
|
299
461
|
* Delete isolation rules for every identifier no live VM owns.
|
|
300
462
|
*
|
|
@@ -360,13 +522,24 @@ export declare class FirecrackerScalerBackend implements ScalerBackend {
|
|
|
360
522
|
*/
|
|
361
523
|
buildVmConfig(alloc: IpAllocationResult, labelSet: LabelSetConfig, effectiveLimits?: EffectiveLimits): Record<string, unknown>;
|
|
362
524
|
/**
|
|
363
|
-
* Create
|
|
364
|
-
*
|
|
365
|
-
*
|
|
525
|
+
* Create the per-VM writable overlay drive: a sparse copy of this host's
|
|
526
|
+
* pre-formatted ext4 template for the size, so the guest can mount it
|
|
527
|
+
* immediately. See `overlay-template.ts` for why a spawn never formats.
|
|
366
528
|
*/
|
|
367
529
|
private createOverlayDrive;
|
|
368
530
|
/**
|
|
369
|
-
*
|
|
531
|
+
* Build the overlay template of every configured drive size in the
|
|
532
|
+
* background, so the first spawn after startup copies instead of formatting.
|
|
533
|
+
* A failure is logged; the first spawn of that size builds it instead.
|
|
534
|
+
*/
|
|
535
|
+
private prewarmOverlayTemplates;
|
|
536
|
+
/**
|
|
537
|
+
* Run a host command with a timeout (30s unless the caller passes one).
|
|
538
|
+
*
|
|
539
|
+
* A failure rejects with a `CommandError` whose message names the command,
|
|
540
|
+
* its exit code or signal, whether the timeout killed it, how long it ran,
|
|
541
|
+
* and the tail of its stderr. Spawn steps throw it as-is, so the same text
|
|
542
|
+
* reaches the `scaler.failed` event detail and the spawn-failure logs.
|
|
370
543
|
*/
|
|
371
544
|
private execAsync;
|
|
372
545
|
/**
|
|
@@ -406,8 +579,9 @@ export declare class FirecrackerScalerBackend implements ScalerBackend {
|
|
|
406
579
|
* naming it was written;
|
|
407
580
|
* 4. field 2 (`comm`) is `firecracker` (11 characters, so it is not
|
|
408
581
|
* truncated by the 15-character `comm` limit) and `/proc/<pid>/root` —
|
|
409
|
-
* where the jailer pivot_roots —
|
|
410
|
-
* readable at all. Those carry
|
|
582
|
+
* where the jailer pivot_roots — resolves to this VM's chroot (same
|
|
583
|
+
* device and inode) when it is readable at all. Those carry
|
|
584
|
+
* `identityConfirmed`, not liveness.
|
|
411
585
|
*/
|
|
412
586
|
private probeVmPid;
|
|
413
587
|
/**
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Live Firecracker VMs on this host, and how the orchestrator relates to each.
|
|
3
|
+
*
|
|
4
|
+
* A Firecracker backend inventories the VMs whose firecracker process is still
|
|
5
|
+
* running under its chroot base (`listLiveVms`); the manager classifies each
|
|
6
|
+
* one against everything on this node that tracks a VM.
|
|
7
|
+
*/
|
|
8
|
+
import { type ScalerLiveVm, type ScalerVmStopResult, type ScalerVmTracker } from '@kici-dev/engine';
|
|
9
|
+
/** One running VM a Firecracker backend found under its chroot base. */
|
|
10
|
+
export interface LiveVmProbe {
|
|
11
|
+
vmId: string;
|
|
12
|
+
/** The scaler whose chroot base holds the VM. */
|
|
13
|
+
scaler: string;
|
|
14
|
+
pid: number;
|
|
15
|
+
/** When the process started, from `/proc/<pid>/stat`. */
|
|
16
|
+
startedAtMs: number;
|
|
17
|
+
chrootDir: string;
|
|
18
|
+
/** Whether the process is provably this VM's own firecracker. */
|
|
19
|
+
identityConfirmed: boolean;
|
|
20
|
+
/** Why the identity is unconfirmed; set only when `identityConfirmed` is false. */
|
|
21
|
+
detail?: string;
|
|
22
|
+
}
|
|
23
|
+
/** Why an orphaned VM is listed as one. */
|
|
24
|
+
export declare const ORPHANED_REASON = "no in-memory VM, spawning entry, registered agent or active job binding";
|
|
25
|
+
/** The Firecracker backend surface the live-VM listing and stop rely on. */
|
|
26
|
+
export interface LiveVmBackend {
|
|
27
|
+
listLiveVms(): Promise<LiveVmProbe[]>;
|
|
28
|
+
/** Synchronous: the stop reads it in the same tick as its kill. */
|
|
29
|
+
isTrackingVm(vmId: string): boolean;
|
|
30
|
+
stopUntrackedVm(vmId: string, trackersOf: () => ScalerVmTracker[]): Promise<ScalerVmStopResult>;
|
|
31
|
+
/** Whether this scaler's allocator holds the VM's address. */
|
|
32
|
+
ownsAllocationFor(vmId: string): Promise<boolean>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Classify one live VM: tracked by anything wins, then a confirmed identity is
|
|
36
|
+
* an orphan, and a running process that is not provably the VM's firecracker
|
|
37
|
+
* is unverified — listed, but never stopped.
|
|
38
|
+
*/
|
|
39
|
+
export declare function classifyLiveVm(probe: LiveVmProbe, trackers: ScalerVmTracker[], nowMs?: number): ScalerLiveVm;
|
|
40
|
+
/** A local Firecracker backend under its scaler name. */
|
|
41
|
+
export type NamedLiveVmBackend = readonly [name: string, backend: LiveVmBackend];
|
|
42
|
+
/**
|
|
43
|
+
* Every live VM across `backends`, classified against `trackersOf`. Two
|
|
44
|
+
* scalers sharing a chroot base both list the same VM; it is reported once.
|
|
45
|
+
*/
|
|
46
|
+
export declare function listClassifiedLiveVms(backends: readonly NamedLiveVmBackend[], trackersOf: (vmId: string) => ScalerVmTracker[]): Promise<ScalerLiveVm[]>;
|
|
47
|
+
/**
|
|
48
|
+
* Stop each VM in turn through the backend whose chroot holds it.
|
|
49
|
+
*
|
|
50
|
+
* The backend is the one whose scan lists the VM, preferring the one whose
|
|
51
|
+
* allocator holds its address when two scalers share a chroot base. A VM no
|
|
52
|
+
* scan lists is not running: each backend is asked in turn, so the answer
|
|
53
|
+
* names why (`not-live` for a dead chroot), and `not-found` when no backend
|
|
54
|
+
* holds a chroot for it.
|
|
55
|
+
*/
|
|
56
|
+
export declare function stopLiveVms(backends: readonly NamedLiveVmBackend[], vmIds: readonly string[], trackersOf: (vmId: string) => ScalerVmTracker[]): Promise<ScalerVmStopResult[]>;
|
|
57
|
+
//# sourceMappingURL=live-vms.d.ts.map
|
package/dist/scaler/manager.d.ts
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
* enforces global limits, tracks spawning agents to prevent over-provisioning,
|
|
7
7
|
* and manages the agent lifecycle from spawn to destroy.
|
|
8
8
|
*/
|
|
9
|
-
import
|
|
9
|
+
import { ScalerVmTracker } from '@kici-dev/engine';
|
|
10
|
+
import type { ResourceRequest, ScalerCapacitySummary, ScalerLiveVm, ScalerVmStopResult } from '@kici-dev/engine';
|
|
10
11
|
import { ScaleDownReason } from './scaler-events.js';
|
|
11
12
|
import { type ScalerAgentView } from './agent-fit.js';
|
|
12
13
|
import type { ScalerEventEmitterLike } from './event-backend.js';
|
|
@@ -179,6 +180,11 @@ export interface ProvisionBackoffSettings {
|
|
|
179
180
|
/** Consecutive failures past which a refusal names repeated failure. */
|
|
180
181
|
maxConsecutiveFailures: number;
|
|
181
182
|
}
|
|
183
|
+
/** A job a pending provision was requested for. */
|
|
184
|
+
export interface BoundJobRef {
|
|
185
|
+
runId: string;
|
|
186
|
+
jobId: string;
|
|
187
|
+
}
|
|
182
188
|
/** Everything the {@link ScalerManager} is constructed from. */
|
|
183
189
|
export interface ScalerManagerDeps {
|
|
184
190
|
config: ScalerConfig;
|
|
@@ -273,6 +279,14 @@ export interface ScalerManagerDeps {
|
|
|
273
279
|
* {@link DEFAULT_PROVISION_BACKOFF}.
|
|
274
280
|
*/
|
|
275
281
|
resolveProvisionBackoff?: () => Promise<ProvisionBackoffSettings>;
|
|
282
|
+
/**
|
|
283
|
+
* Current `execution_jobs.status` of the jobs bound to pending event
|
|
284
|
+
* provisions, keyed by job id. A job with no row is absent from the map.
|
|
285
|
+
* Each job comes with its run so the read stays on the `(run_id, job_id)`
|
|
286
|
+
* index. Wired in orchestrator-core (has DB); omitted by the worker and most
|
|
287
|
+
* unit tests, which then withdraw nothing and report every pruned provision.
|
|
288
|
+
*/
|
|
289
|
+
readJobStatuses?: (jobs: readonly BoundJobRef[]) => Promise<ReadonlyMap<string, string>>;
|
|
276
290
|
/**
|
|
277
291
|
* Constructs a backend for a scaler added by a config reload. Supplied by
|
|
278
292
|
* both hosts from the shared backend factory, so a reload can only build
|
|
@@ -303,8 +317,10 @@ export declare class ScalerManager {
|
|
|
303
317
|
/** Recent scaler spawn failures, surfaced by `kici-admin diagnose`. */
|
|
304
318
|
private readonly failureTracker;
|
|
305
319
|
/**
|
|
306
|
-
* Consecutive
|
|
307
|
-
*
|
|
320
|
+
* Consecutive provisioning failures per scaler NAME, and the deferral they
|
|
321
|
+
* earned: an event scaler's external provision that never registered an
|
|
322
|
+
* agent, or a local launch that produced no agent (refused outright, or the
|
|
323
|
+
* agent stopped before it registered).
|
|
308
324
|
*
|
|
309
325
|
* Keyed by name rather than by backend type so one failing scaler never
|
|
310
326
|
* defers spawns for an unrelated one — two event scalers routinely drive two
|
|
@@ -316,6 +332,7 @@ export declare class ScalerManager {
|
|
|
316
332
|
*/
|
|
317
333
|
private readonly provisionFailures;
|
|
318
334
|
private readonly resolveProvisionBackoff;
|
|
335
|
+
private readonly readJobStatuses?;
|
|
319
336
|
private globalMaxAgents;
|
|
320
337
|
/** Per-scaler resource caps (`{ maxCpu, maxMemoryBytes }`), keyed by scaler name. */
|
|
321
338
|
private readonly resourceCaps;
|
|
@@ -524,6 +541,27 @@ export declare class ScalerManager {
|
|
|
524
541
|
* Whether this scaler pre-spawned (warm-filled) the agent.
|
|
525
542
|
*/
|
|
526
543
|
isPrespawnedAgent(agentId: string): boolean;
|
|
544
|
+
/** Every local Firecracker backend, under its scaler name. */
|
|
545
|
+
private firecrackerBackends;
|
|
546
|
+
/** The names of this node's Firecracker scalers. */
|
|
547
|
+
firecrackerScalerNames(): string[];
|
|
548
|
+
/**
|
|
549
|
+
* What on this node tracks `vmId`. Synchronous on purpose: the Firecracker
|
|
550
|
+
* stop calls it in the same tick as its kill, so it must not await.
|
|
551
|
+
*
|
|
552
|
+
* Every local Firecracker backend is asked, so two scalers sharing a chroot
|
|
553
|
+
* base never see each other's VMs as orphans. `spawningAgents` includes the
|
|
554
|
+
* entries `recoverState` rehydrated after a restart, which no backend tracks.
|
|
555
|
+
*/
|
|
556
|
+
trackersOf(vmId: string): ScalerVmTracker[];
|
|
557
|
+
/** Every live Firecracker VM on this host, classified against what tracks it. */
|
|
558
|
+
listLiveVms(): Promise<ScalerLiveVm[]>;
|
|
559
|
+
/**
|
|
560
|
+
* Stop the approved VMs one at a time. Each stop re-checks {@link trackersOf}
|
|
561
|
+
* in the tick of its kill, so a VM that became tracked since the listing is
|
|
562
|
+
* refused, never signalled.
|
|
563
|
+
*/
|
|
564
|
+
stopUntrackedVms(vmIds: readonly string[]): Promise<ScalerVmStopResult[]>;
|
|
527
565
|
/**
|
|
528
566
|
* What this scaler knows about an agent it started, for `canAgentRunJob`,
|
|
529
567
|
* or undefined for an agent it did not start.
|
|
@@ -1054,6 +1092,47 @@ export declare class ScalerManager {
|
|
|
1054
1092
|
* immediately.
|
|
1055
1093
|
*/
|
|
1056
1094
|
private reportProvisionFailure;
|
|
1095
|
+
/**
|
|
1096
|
+
* Defer a scaler whose host refused to start an agent, then free the spawn's
|
|
1097
|
+
* reservation.
|
|
1098
|
+
*
|
|
1099
|
+
* Arming comes first: the release notifies freed capacity, and the re-drive
|
|
1100
|
+
* that follows 250 ms later must meet the deferral rather than spawn straight
|
|
1101
|
+
* into the same refusal. The release still notifies, because a job waiting on
|
|
1102
|
+
* another scaler, or on the global cap, is legitimately unblocked by it.
|
|
1103
|
+
* Two failures come here: a `spawn()` that rejected with a
|
|
1104
|
+
* `DeterministicSpawnError`, and a settled spawn whose compute stopped before
|
|
1105
|
+
* its agent registered (`dropFailedSettledSpawn`). Every other spawn
|
|
1106
|
+
* rejection is released at once and retried.
|
|
1107
|
+
*/
|
|
1108
|
+
private deferRefusedLaunch;
|
|
1109
|
+
/**
|
|
1110
|
+
* Whether a bound job has ended. A read error, a missing row, an unknown
|
|
1111
|
+
* run, or no reader wired all read as "not ended", so the caller keeps
|
|
1112
|
+
* reporting the provision as it would without the check.
|
|
1113
|
+
*/
|
|
1114
|
+
private boundJobEnded;
|
|
1115
|
+
/**
|
|
1116
|
+
* Withdraw every settled, job-bound event provision on this coordinator
|
|
1117
|
+
* whose job already ended — cancelled, timed out, or run on another agent.
|
|
1118
|
+
*
|
|
1119
|
+
* The job's row is the shared truth: a cancel reaches whichever coordinator
|
|
1120
|
+
* the Platform picks, while the spawning entry lives on the one that asked.
|
|
1121
|
+
* A withdrawal is not a provisioning failure, so nothing is reported and no
|
|
1122
|
+
* backoff is armed. Returns how many provisions were withdrawn.
|
|
1123
|
+
*/
|
|
1124
|
+
withdrawEndedBoundProvisions(): Promise<number>;
|
|
1125
|
+
/**
|
|
1126
|
+
* Forget one pending provision and tear it down as `job-complete` — unless a
|
|
1127
|
+
* peer adopted its agent, in which case the adopter owns the teardown.
|
|
1128
|
+
*
|
|
1129
|
+
* The teardown goes through `emitScaleDownForSpec`, which drops the spawn
|
|
1130
|
+
* row only once the scale-down is out: a failed emit keeps the row for the
|
|
1131
|
+
* reaper, whose spawn-timeout verdict re-checks the job and sends
|
|
1132
|
+
* `job-complete` too. `backend.destroy` is not used — a rehydrated entry is
|
|
1133
|
+
* not tracked by the backend, so it would emit nothing.
|
|
1134
|
+
*/
|
|
1135
|
+
private withdrawProvision;
|
|
1057
1136
|
/**
|
|
1058
1137
|
* Report a pruned event spawn as a dead provision — unless a peer adopted it.
|
|
1059
1138
|
*
|
|
@@ -1085,6 +1164,8 @@ export declare class ScalerManager {
|
|
|
1085
1164
|
* existed, and they age out within one stale-prune window of the deploy.
|
|
1086
1165
|
*/
|
|
1087
1166
|
private reportPrunedProvisionFailure;
|
|
1167
|
+
/** The adoption half of {@link reportPrunedProvisionFailure}. */
|
|
1168
|
+
private reportPrunedUnlessAdopted;
|
|
1088
1169
|
/**
|
|
1089
1170
|
* Clear a scaler's consecutive-failure state after a successful provision.
|
|
1090
1171
|
*
|
|
@@ -1397,6 +1478,34 @@ export declare class ScalerManager {
|
|
|
1397
1478
|
* other instance already claimed is left exactly as it is.
|
|
1398
1479
|
*/
|
|
1399
1480
|
private markSpawningAgentAdopted;
|
|
1481
|
+
/**
|
|
1482
|
+
* Record that `backend.spawn()` resolved for a spawn this instance started.
|
|
1483
|
+
*
|
|
1484
|
+
* Returns false when the stale-spawn prune dropped the entry while
|
|
1485
|
+
* `spawn()` was still running (a per-org spawn deadline can outlast the
|
|
1486
|
+
* prune window). Nothing else would ever tear that compute down, so a local
|
|
1487
|
+
* backend's is destroyed here. An event backend's provision is left to the
|
|
1488
|
+
* reaper, as the prune leaves it.
|
|
1489
|
+
*/
|
|
1490
|
+
private onSpawnSettled;
|
|
1491
|
+
/**
|
|
1492
|
+
* Drop a settled local spawn whose backend reported it failed.
|
|
1493
|
+
*
|
|
1494
|
+
* After `spawn()` resolved, a backend that finds its compute gone before the
|
|
1495
|
+
* agent registered (a Firecracker guest that exited, a bare-metal agent
|
|
1496
|
+
* process that exited) tears the compute down itself and emits
|
|
1497
|
+
* `scaler.failed`. The agent will never register, so the spawning entry, its
|
|
1498
|
+
* durable row and its reservation go now — waiting for the stale-spawn prune
|
|
1499
|
+
* would hold the scaler's slot for minutes.
|
|
1500
|
+
*
|
|
1501
|
+
* A spawn that produced no agent is a refused launch, the same rule an event
|
|
1502
|
+
* scaler's dead provision follows: the scaler is deferred before the
|
|
1503
|
+
* reservation is freed, so an agent that dies at startup every time is not
|
|
1504
|
+
* respawned at its own startup cadence. Only a spawn that has not registered
|
|
1505
|
+
* reaches here — registration deletes the spawning entry — so an agent that
|
|
1506
|
+
* registered and exits later never arms the backoff.
|
|
1507
|
+
*/
|
|
1508
|
+
private dropFailedSettledSpawn;
|
|
1400
1509
|
/** Store-side half of the stale prune: drops the row only if nobody adopted it. */
|
|
1401
1510
|
private deleteUnadoptedSpawningAgentFromStore;
|
|
1402
1511
|
private persistReservation;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ScalerOrphansAction, type PeerScalerOrphansResponse } from '@kici-dev/engine';
|
|
2
|
+
import type { ScalerManager } from './manager.js';
|
|
3
|
+
/**
|
|
4
|
+
* The longest a scaler orphan request waits for its node. The `kici-admin`
|
|
5
|
+
* HTTP client stops waiting for response headers after 300 s, so a longer
|
|
6
|
+
* wait could never reach the operator.
|
|
7
|
+
*/
|
|
8
|
+
export declare const MAX_SCALER_ORPHANS_TIMEOUT_MS = 240000;
|
|
9
|
+
/** A scaler orphan answer without its peer-message envelope. */
|
|
10
|
+
export type ScalerOrphansAnswer = Omit<PeerScalerOrphansResponse, 'type' | 'messageId'>;
|
|
11
|
+
/** What one scaler orphan request asks of the node. */
|
|
12
|
+
export interface ScalerOrphansRequest {
|
|
13
|
+
action: ScalerOrphansAction;
|
|
14
|
+
/** `stop` only: the VM ids the operator approved. */
|
|
15
|
+
vmIds?: string[];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Answer a scaler orphan request on this node. A node with no scaler manager
|
|
19
|
+
* runs no Firecracker scaler: it lists nothing and holds none of the VMs a stop
|
|
20
|
+
* names. A failure is answered `ok: false` with its message, never thrown.
|
|
21
|
+
*/
|
|
22
|
+
export declare function answerScalerOrphansRequest(manager: Pick<ScalerManager, 'listLiveVms' | 'stopUntrackedVms' | 'firecrackerScalerNames'> | null, req: ScalerOrphansRequest): Promise<ScalerOrphansAnswer>;
|
|
23
|
+
//# sourceMappingURL=orphan-requests.d.ts.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Directory that holds the templates, relative to the jailer chroot base dir.
|
|
3
|
+
* It sits beside the per-VM chroots because the orchestrator must already be
|
|
4
|
+
* able to create directories there; the leading dot keeps it out of the
|
|
5
|
+
* orphan sweep, which reads every other entry as a VM id.
|
|
6
|
+
*/
|
|
7
|
+
export declare const OVERLAY_TEMPLATE_DIR: string;
|
|
8
|
+
/**
|
|
9
|
+
* Timeout for formatting a template. It runs once per size per host, mostly
|
|
10
|
+
* off a spawn's critical path, and its `fsync` may wait behind a disk backlog,
|
|
11
|
+
* so it gets far more room than a per-spawn step.
|
|
12
|
+
*/
|
|
13
|
+
export declare const OVERLAY_TEMPLATE_MKFS_TIMEOUT_MS = 300000;
|
|
14
|
+
/** Timeout for the per-spawn copy. */
|
|
15
|
+
export declare const OVERLAY_COPY_TIMEOUT_MS = 30000;
|
|
16
|
+
/** Runs a host command, rejecting on failure. The backend passes its `execAsync`. */
|
|
17
|
+
export type OverlayCommandRunner = (cmd: string, args: string[], timeoutMs: number) => Promise<unknown>;
|
|
18
|
+
/** Where the template for a drive size lives. */
|
|
19
|
+
export declare function overlayTemplatePath(chrootBaseDir: string, sizeMib: number): string;
|
|
20
|
+
/**
|
|
21
|
+
* Whether `path` is a usable template for `sizeMib`: it exists, its size is
|
|
22
|
+
* exactly `sizeMib` MiB, and it carries the ext4 superblock magic.
|
|
23
|
+
*/
|
|
24
|
+
export declare function isOverlayTemplate(path: string, sizeMib: number): Promise<boolean>;
|
|
25
|
+
/** Builds and hands out overlay templates for one jailer chroot base dir. */
|
|
26
|
+
export declare class OverlayTemplates {
|
|
27
|
+
private readonly chrootBaseDir;
|
|
28
|
+
private readonly run;
|
|
29
|
+
/** In-flight builds by size, so concurrent first uses share one build. */
|
|
30
|
+
private readonly builds;
|
|
31
|
+
constructor(chrootBaseDir: string, run: OverlayCommandRunner);
|
|
32
|
+
/** Return the path of a valid template for `sizeMib`, building it if needed. */
|
|
33
|
+
ensure(sizeMib: number): Promise<string>;
|
|
34
|
+
/** Create the overlay drive at `dest` as a sparse copy of the template. */
|
|
35
|
+
createOverlay(dest: string, sizeMib: number): Promise<void>;
|
|
36
|
+
private build;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=overlay-template.d.ts.map
|