@calltelemetry/ct-lab-mcp 0.4.2 → 0.4.4
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 +16 -0
- package/dist/bin/ct-lab-mcp.js +118 -3
- package/dist/src/crd/reconciler.d.ts +41 -0
- package/dist/src/crd/reconciler.d.ts.map +1 -0
- package/dist/src/crd/reconciler.js +302 -0
- package/dist/src/crd/reconciler.js.map +1 -0
- package/dist/src/crd/types.d.ts +101 -0
- package/dist/src/crd/types.d.ts.map +1 -0
- package/dist/src/crd/types.js +7 -0
- package/dist/src/crd/types.js.map +1 -0
- package/dist/src/hypervisors/esxi/client.d.ts.map +1 -1
- package/dist/src/hypervisors/esxi/client.js +6 -7
- package/dist/src/hypervisors/esxi/client.js.map +1 -1
- package/dist/src/hypervisors/proxmox/client.d.ts +2 -0
- package/dist/src/hypervisors/proxmox/client.d.ts.map +1 -1
- package/dist/src/hypervisors/proxmox/client.js +90 -14
- package/dist/src/hypervisors/proxmox/client.js.map +1 -1
- package/dist/src/proxmox/mock.d.ts.map +1 -1
- package/dist/src/proxmox/mock.js +46 -0
- package/dist/src/proxmox/mock.js.map +1 -1
- package/dist/src/server.js +2 -2
- package/dist/src/server.js.map +1 -1
- package/dist/src/slot/manager.d.ts.map +1 -1
- package/dist/src/slot/manager.js +16 -13
- package/dist/src/slot/manager.js.map +1 -1
- package/dist/src/slot/purge-controller.d.ts +63 -0
- package/dist/src/slot/purge-controller.d.ts.map +1 -0
- package/dist/src/slot/purge-controller.js +119 -0
- package/dist/src/slot/purge-controller.js.map +1 -0
- package/dist/src/slot/types.d.ts +6 -0
- package/dist/src/slot/types.d.ts.map +1 -1
- package/dist/src/slot/types.js +11 -0
- package/dist/src/slot/types.js.map +1 -1
- package/dist/src/tools/index.d.ts +1 -0
- package/dist/src/tools/index.d.ts.map +1 -1
- package/dist/src/tools/index.js +26 -1
- package/dist/src/tools/index.js.map +1 -1
- package/dist/src/tools/list-hosts.d.ts +2 -0
- package/dist/src/tools/list-hosts.d.ts.map +1 -1
- package/dist/src/tools/list-hosts.js.map +1 -1
- package/dist/src/tools/manifest-tools.d.ts +38 -0
- package/dist/src/tools/manifest-tools.d.ts.map +1 -0
- package/dist/src/tools/manifest-tools.js +148 -0
- package/dist/src/tools/manifest-tools.js.map +1 -0
- package/dist/src/tools/provision-tools.d.ts +28 -8
- package/dist/src/tools/provision-tools.d.ts.map +1 -1
- package/dist/src/tools/provision-tools.js +153 -25
- package/dist/src/tools/provision-tools.js.map +1 -1
- package/dist/src/tools/slot-tools.d.ts.map +1 -1
- package/dist/src/tools/slot-tools.js +9 -3
- package/dist/src/tools/slot-tools.js.map +1 -1
- package/dist/src/tools/vm-cleanup.d.ts +79 -0
- package/dist/src/tools/vm-cleanup.d.ts.map +1 -0
- package/dist/src/tools/vm-cleanup.js +102 -0
- package/dist/src/tools/vm-cleanup.js.map +1 -0
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -89,6 +89,17 @@ npm install -g @calltelemetry/ct-lab-mcp
|
|
|
89
89
|
npx @calltelemetry/ct-lab-mcp
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
+
Merges to `main` publish automatically. If `package.json` version is not on npm,
|
|
93
|
+
that version is published, tagged, and released. If it is already on npm, CI
|
|
94
|
+
bumps the patch version, publishes, tags, and creates the GitHub Release.
|
|
95
|
+
`--dry-run` / docs-only work that should not ship must not land on `main`, or
|
|
96
|
+
bump the version only when the package should ship.
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# Current published line
|
|
100
|
+
npm view @calltelemetry/ct-lab-mcp version
|
|
101
|
+
```
|
|
102
|
+
|
|
92
103
|
---
|
|
93
104
|
|
|
94
105
|
## CLI Usage
|
|
@@ -168,6 +179,11 @@ Canonical mutation parameters are deliberately explicit:
|
|
|
168
179
|
- `release_slot` requires the exact `leaseId` and an `artifactReceipt`.
|
|
169
180
|
- `purge_slots` delegates only to `claims gc`; age, force, orphan, and VM-name
|
|
170
181
|
shortcuts are rejected.
|
|
182
|
+
- `list_expired_vms` classifies live VMs as legal, expiring, expired, dead
|
|
183
|
+
leftover, or protected. It never deletes.
|
|
184
|
+
- `cleanup_expired_vms` deletes stopped leftover disposables and purges disks
|
|
185
|
+
by default. Pass `dryRun: true` to classify only. Protected fixtures are
|
|
186
|
+
never candidates. Running leftovers stay report-only.
|
|
171
187
|
- `vm_renew` and `vm_transfer` preserve the exact asset and lease identity.
|
|
172
188
|
Running-guest renewal and expired-running transfer are accepted only when
|
|
173
189
|
the canonical CLI proves their narrow recovery conditions.
|
package/dist/bin/ct-lab-mcp.js
CHANGED
|
@@ -19,6 +19,7 @@ import { CredentialResolutionError } from "../src/credentials/errors.js";
|
|
|
19
19
|
import { startStdioServer } from "../src/transports/stdio.js";
|
|
20
20
|
import { createSseServer } from "../src/transports/sse.js";
|
|
21
21
|
import { CanonicalLabBridge, runCanonicalPreflight } from "../src/canonical/index.js";
|
|
22
|
+
import { PurgeController } from "../src/slot/purge-controller.js";
|
|
22
23
|
// Load environment variables from .env if present
|
|
23
24
|
dotenv.config();
|
|
24
25
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -40,14 +41,114 @@ export function buildPreflightCommand() {
|
|
|
40
41
|
}
|
|
41
42
|
export async function runPreflightCli(argv = process.argv, bridge = new CanonicalLabBridge({ required: true })) {
|
|
42
43
|
const program = buildPreflightCommand();
|
|
43
|
-
// `buildPreflightCommand` is the subcommand itself, so remove the root
|
|
44
|
-
// `preflight` token before Commander parses its options.
|
|
45
44
|
program.parse([argv[0] || "node", argv[1] || "ct-lab-mcp", ...argv.slice(3)]);
|
|
46
45
|
const options = program.opts();
|
|
47
46
|
const response = await runCanonicalPreflight({ assetId: options.assetId, linearIssue: options.linearIssue }, bridge);
|
|
48
47
|
process.stdout.write(`${JSON.stringify(response.result)}\n`);
|
|
49
48
|
return response.exitCode;
|
|
50
49
|
}
|
|
50
|
+
export function buildPurgeDaemonCommand() {
|
|
51
|
+
return new Command("purge-daemon")
|
|
52
|
+
.description("Runs the continuous autonomous purge controller daemon across all Proxmox VE hypervisors")
|
|
53
|
+
.option("--interval <seconds>", "Reconciliation interval in seconds", "30")
|
|
54
|
+
.option("--no-destroy", "Skip destroying VMs (reclaim slot lease only)");
|
|
55
|
+
}
|
|
56
|
+
export async function runPurgeDaemonCli(argv = process.argv) {
|
|
57
|
+
const program = buildPurgeDaemonCommand();
|
|
58
|
+
program.parse([argv[0] || "node", argv[1] || "ct-lab-mcp", ...argv.slice(3)]);
|
|
59
|
+
const options = program.opts();
|
|
60
|
+
const intervalMs = Math.max(5000, parseInt(options.interval || "30", 10) * 1000);
|
|
61
|
+
const destroyVms = options.destroy !== false;
|
|
62
|
+
console.log(`[ct-lab-mcp] Starting autonomous purge daemon (interval: ${intervalMs / 1000}s, destroyVms: ${destroyVms})...`);
|
|
63
|
+
const controller = new PurgeController({
|
|
64
|
+
intervalMs,
|
|
65
|
+
destroyVms,
|
|
66
|
+
autoStart: true,
|
|
67
|
+
});
|
|
68
|
+
return new Promise((resolve) => {
|
|
69
|
+
const handleSig = () => {
|
|
70
|
+
console.log("[ct-lab-mcp] Stopping purge daemon...");
|
|
71
|
+
controller.stop();
|
|
72
|
+
resolve(0);
|
|
73
|
+
};
|
|
74
|
+
process.once("SIGINT", handleSig);
|
|
75
|
+
process.once("SIGTERM", handleSig);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
export function buildBenchmarkCommand() {
|
|
79
|
+
return new Command("benchmark")
|
|
80
|
+
.description("Run high-resolution VM lifecycle performance benchmark (sub-45ms declarative or physical Proxmox)")
|
|
81
|
+
.option("--physical", "Run live physical hardware provisioning, boot, ping, and SSH benchmark against Proxmox Mini")
|
|
82
|
+
.option("--json", "Output results as structured JSON", false);
|
|
83
|
+
}
|
|
84
|
+
export async function runBenchmarkCli(argv = process.argv) {
|
|
85
|
+
const program = buildBenchmarkCommand();
|
|
86
|
+
program.parse([argv[0] || "node", argv[1] || "ct-lab-mcp", ...argv.slice(3)]);
|
|
87
|
+
const options = program.opts();
|
|
88
|
+
if (options.physical) {
|
|
89
|
+
console.log("[ct-lab-mcp] Executing live physical hardware benchmark against Proxmox Mini...");
|
|
90
|
+
const scriptPath = path.resolve(__dirname, "../../scripts/live-physical-vm-benchmark.mjs");
|
|
91
|
+
const { spawn } = await import("node:child_process");
|
|
92
|
+
return new Promise((resolve) => {
|
|
93
|
+
const child = spawn(process.execPath, [scriptPath], { stdio: "inherit" });
|
|
94
|
+
child.on("close", (code) => resolve(code ?? 0));
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
console.log("[ct-lab-mcp] Executing declarative VM lifecycle benchmark...");
|
|
99
|
+
const { ApplianceSlotReconciler } = await import("../src/crd/reconciler.js");
|
|
100
|
+
const { SlotManager } = await import("../src/slot/manager.js");
|
|
101
|
+
const t0 = performance.now();
|
|
102
|
+
const slotManager = new SlotManager();
|
|
103
|
+
const reconciler = new ApplianceSlotReconciler(slotManager);
|
|
104
|
+
const manifest = {
|
|
105
|
+
apiVersion: "lab.calltelemetry.com/v1alpha1",
|
|
106
|
+
kind: "ApplianceSlot",
|
|
107
|
+
metadata: { name: "cli-benchmark-slot", labels: { linearIssue: "BENCHMARK-CLI" } },
|
|
108
|
+
spec: { host: "proxmox-mini", resources: { memoryMb: 8192, cores: 4, thinDisk: true } }
|
|
109
|
+
};
|
|
110
|
+
const tAllocStart = performance.now();
|
|
111
|
+
const applied = await reconciler.reconcile(manifest);
|
|
112
|
+
const tAlloc = performance.now() - tAllocStart;
|
|
113
|
+
const tGetStart = performance.now();
|
|
114
|
+
await reconciler.getManifest("cli-benchmark-slot");
|
|
115
|
+
const tGet = performance.now() - tGetStart;
|
|
116
|
+
const tRenewStart = performance.now();
|
|
117
|
+
await reconciler.renewManifest("cli-benchmark-slot", 60);
|
|
118
|
+
const tRenew = performance.now() - tRenewStart;
|
|
119
|
+
const tDelStart = performance.now();
|
|
120
|
+
await reconciler.deleteManifest("cli-benchmark-slot");
|
|
121
|
+
const tDel = performance.now() - tDelStart;
|
|
122
|
+
const tTotal = performance.now() - t0;
|
|
123
|
+
if (options.json) {
|
|
124
|
+
console.log(JSON.stringify({
|
|
125
|
+
success: true,
|
|
126
|
+
metrics: {
|
|
127
|
+
allocationMs: Number(tAlloc.toFixed(3)),
|
|
128
|
+
getMs: Number(tGet.toFixed(3)),
|
|
129
|
+
renewMs: Number(tRenew.toFixed(3)),
|
|
130
|
+
deletionMs: Number(tDel.toFixed(3)),
|
|
131
|
+
totalMs: Number(tTotal.toFixed(3)),
|
|
132
|
+
defaultTtlMinutes: applied.status?.remainingTtlMinutes || 120
|
|
133
|
+
}
|
|
134
|
+
}, null, 2));
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
console.log("=================================================");
|
|
138
|
+
console.log("🚀 CALL TELEMETRY VM LIFECYCLE BENCHMARK");
|
|
139
|
+
console.log("=================================================");
|
|
140
|
+
console.log(`• Allocation & Credential Linkage: ${tAlloc.toFixed(2)}ms`);
|
|
141
|
+
console.log(`• Status Inspection & Reconcile: ${tGet.toFixed(2)}ms`);
|
|
142
|
+
console.log(`• Lease Heartbeat Renewal (+60m): ${tRenew.toFixed(2)}ms`);
|
|
143
|
+
console.log(`• Teardown & Capacity Restore: ${tDel.toFixed(2)}ms`);
|
|
144
|
+
console.log(`• Default Claim Duration: ${applied.status?.remainingTtlMinutes || 120} minutes (2 Hours)`);
|
|
145
|
+
console.log("-------------------------------------------------");
|
|
146
|
+
console.log(`• TOTAL LIFECYCLE ROUNDTRIP: ${tTotal.toFixed(2)}ms`);
|
|
147
|
+
console.log("=================================================\n");
|
|
148
|
+
}
|
|
149
|
+
return 0;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
51
152
|
/**
|
|
52
153
|
* Builds the Commander CLI program definition.
|
|
53
154
|
*/
|
|
@@ -76,6 +177,8 @@ export function buildCli(exitOverride = false) {
|
|
|
76
177
|
program.exitOverride();
|
|
77
178
|
}
|
|
78
179
|
program.addCommand(buildPreflightCommand());
|
|
180
|
+
program.addCommand(buildPurgeDaemonCommand());
|
|
181
|
+
program.addCommand(buildBenchmarkCommand());
|
|
79
182
|
return program;
|
|
80
183
|
}
|
|
81
184
|
/**
|
|
@@ -296,7 +399,19 @@ export function isMainModule(metaUrl) {
|
|
|
296
399
|
}
|
|
297
400
|
// Auto-run if executed as CLI entrypoint
|
|
298
401
|
if (isMainModule(import.meta.url)) {
|
|
299
|
-
|
|
402
|
+
let command;
|
|
403
|
+
if (process.argv[2] === "preflight") {
|
|
404
|
+
command = runPreflightCli();
|
|
405
|
+
}
|
|
406
|
+
else if (process.argv[2] === "purge-daemon") {
|
|
407
|
+
command = runPurgeDaemonCli();
|
|
408
|
+
}
|
|
409
|
+
else if (process.argv[2] === "benchmark") {
|
|
410
|
+
command = runBenchmarkCli();
|
|
411
|
+
}
|
|
412
|
+
else {
|
|
413
|
+
command = runCli();
|
|
414
|
+
}
|
|
300
415
|
Promise.resolve(command)
|
|
301
416
|
.then((result) => {
|
|
302
417
|
if (typeof result === "number" && result !== 0)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kubernetes ApplianceSlot Reconciler Engine
|
|
3
|
+
* Parses, validates, and reconciles declarative K8s ApplianceSlot manifests against Proxmox VE,
|
|
4
|
+
* SlotManager, and Doppler Credentials.
|
|
5
|
+
*/
|
|
6
|
+
import { ApplianceSlotManifest } from "./types.js";
|
|
7
|
+
import { SlotManager } from "../slot/manager.js";
|
|
8
|
+
import { MultiHypervisorManager } from "../hypervisors/manager.js";
|
|
9
|
+
import { CredentialResolver } from "../credentials/resolver.js";
|
|
10
|
+
export declare class ApplianceSlotReconciler {
|
|
11
|
+
private slotManager;
|
|
12
|
+
private hypervisorManager;
|
|
13
|
+
private resolver;
|
|
14
|
+
private activeManifests;
|
|
15
|
+
constructor(slotManager?: SlotManager, hypervisorManager?: MultiHypervisorManager, resolver?: CredentialResolver);
|
|
16
|
+
/**
|
|
17
|
+
* Parse a raw YAML or JSON manifest string into a strongly-typed ApplianceSlotManifest
|
|
18
|
+
*/
|
|
19
|
+
parseManifest(rawContent: string): ApplianceSlotManifest;
|
|
20
|
+
/**
|
|
21
|
+
* Reconcile a declarative ApplianceSlot manifest against hypervisors and slot scheduler
|
|
22
|
+
*/
|
|
23
|
+
reconcile(manifestOrRaw: ApplianceSlotManifest | string): Promise<ApplianceSlotManifest>;
|
|
24
|
+
/**
|
|
25
|
+
* Get the current status of an applied manifest
|
|
26
|
+
*/
|
|
27
|
+
getManifest(name: string): Promise<ApplianceSlotManifest | null>;
|
|
28
|
+
/**
|
|
29
|
+
* List all actively tracked manifests
|
|
30
|
+
*/
|
|
31
|
+
listManifests(): ApplianceSlotManifest[];
|
|
32
|
+
/**
|
|
33
|
+
* Renew / extend the lease duration of an active declarative manifest (heartbeat)
|
|
34
|
+
*/
|
|
35
|
+
renewManifest(name: string, additionalMinutes?: number): Promise<ApplianceSlotManifest>;
|
|
36
|
+
/**
|
|
37
|
+
* Delete / release an applied manifest and reclaim its slot
|
|
38
|
+
*/
|
|
39
|
+
deleteManifest(name: string): Promise<boolean>;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=reconciler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reconciler.d.ts","sourceRoot":"","sources":["../../../src/crd/reconciler.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,qBAAqB,EAKtB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAsB,MAAM,4BAA4B,CAAC;AAGpF,qBAAa,uBAAuB;IAClC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,iBAAiB,CAAyB;IAClD,OAAO,CAAC,QAAQ,CAAqB;IACrC,OAAO,CAAC,eAAe,CAAiD;gBAGtE,WAAW,CAAC,EAAE,WAAW,EACzB,iBAAiB,CAAC,EAAE,sBAAsB,EAC1C,QAAQ,CAAC,EAAE,kBAAkB;IAO/B;;OAEG;IACH,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,qBAAqB;IA+BxD;;OAEG;IACG,SAAS,CAAC,aAAa,EAAE,qBAAqB,GAAG,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAmK9F;;OAEG;IACG,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC;IAItE;;OAEG;IACH,aAAa,IAAI,qBAAqB,EAAE;IAIxC;;OAEG;IACG,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,iBAAiB,GAAE,MAAW,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAwDjG;;OAEG;IACG,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAgCrD"}
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kubernetes ApplianceSlot Reconciler Engine
|
|
3
|
+
* Parses, validates, and reconciles declarative K8s ApplianceSlot manifests against Proxmox VE,
|
|
4
|
+
* SlotManager, and Doppler Credentials.
|
|
5
|
+
*/
|
|
6
|
+
import { load as yamlLoad } from "js-yaml";
|
|
7
|
+
import { SlotManager } from "../slot/manager.js";
|
|
8
|
+
import { MultiHypervisorManager } from "../hypervisors/manager.js";
|
|
9
|
+
import { credentialResolver } from "../credentials/resolver.js";
|
|
10
|
+
export class ApplianceSlotReconciler {
|
|
11
|
+
slotManager;
|
|
12
|
+
hypervisorManager;
|
|
13
|
+
resolver;
|
|
14
|
+
activeManifests = new Map();
|
|
15
|
+
constructor(slotManager, hypervisorManager, resolver) {
|
|
16
|
+
this.slotManager = slotManager || new SlotManager();
|
|
17
|
+
this.hypervisorManager = hypervisorManager || new MultiHypervisorManager();
|
|
18
|
+
this.resolver = resolver || credentialResolver;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Parse a raw YAML or JSON manifest string into a strongly-typed ApplianceSlotManifest
|
|
22
|
+
*/
|
|
23
|
+
parseManifest(rawContent) {
|
|
24
|
+
let parsed;
|
|
25
|
+
try {
|
|
26
|
+
parsed = yamlLoad(rawContent);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
try {
|
|
30
|
+
parsed = JSON.parse(rawContent);
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
throw new Error(`Invalid manifest format (must be valid YAML or JSON): ${err.message}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (!parsed || typeof parsed !== "object") {
|
|
37
|
+
throw new Error("Invalid manifest: must be an object");
|
|
38
|
+
}
|
|
39
|
+
if (parsed.apiVersion !== "lab.calltelemetry.com/v1alpha1" && !parsed.apiVersion?.startsWith("lab.calltelemetry.com/")) {
|
|
40
|
+
throw new Error(`Unsupported apiVersion: ${parsed.apiVersion} (expected lab.calltelemetry.com/v1alpha1)`);
|
|
41
|
+
}
|
|
42
|
+
if (parsed.kind !== "ApplianceSlot") {
|
|
43
|
+
throw new Error(`Unsupported kind: ${parsed.kind} (expected ApplianceSlot)`);
|
|
44
|
+
}
|
|
45
|
+
if (!parsed.metadata?.name) {
|
|
46
|
+
throw new Error("Invalid manifest: metadata.name is required");
|
|
47
|
+
}
|
|
48
|
+
return parsed;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Reconcile a declarative ApplianceSlot manifest against hypervisors and slot scheduler
|
|
52
|
+
*/
|
|
53
|
+
async reconcile(manifestOrRaw) {
|
|
54
|
+
const manifest = typeof manifestOrRaw === "string" ? this.parseManifest(manifestOrRaw) : manifestOrRaw;
|
|
55
|
+
const name = manifest.metadata.name;
|
|
56
|
+
const now = new Date().toISOString();
|
|
57
|
+
const targetHost = manifest.spec.host === "auto" || !manifest.spec.host ? undefined : manifest.spec.host;
|
|
58
|
+
const linearIssue = manifest.spec.lease?.linearIssue || manifest.metadata.labels?.linearIssue || name;
|
|
59
|
+
const requester = manifest.spec.lease?.requester || manifest.metadata.labels?.requester || "k8s-controller";
|
|
60
|
+
const ttlMinutes = manifest.spec.lease?.ttlMinutes || 120;
|
|
61
|
+
const targetVersion = manifest.spec.appliance?.targetVersion || "0.8.7-rc";
|
|
62
|
+
const branch = manifest.spec.appliance?.branch || "0.8.7-stable";
|
|
63
|
+
const workloadType = manifest.spec.appliance?.workloadType || "UAT_CERTIFICATION";
|
|
64
|
+
const conditions = [];
|
|
65
|
+
// 1. Resolve Doppler credentials linkage
|
|
66
|
+
const dopplerProject = manifest.spec.credentials?.dopplerProject || process.env.DOPPLER_PROJECT || "ai-workspace";
|
|
67
|
+
const dopplerConfig = manifest.spec.credentials?.dopplerConfig || process.env.DOPPLER_CONFIG || "dev";
|
|
68
|
+
const sshUser = manifest.spec.credentials?.sshUser || "root";
|
|
69
|
+
let credStatus = {
|
|
70
|
+
dopplerProject,
|
|
71
|
+
dopplerConfig,
|
|
72
|
+
credentialTier: "Tier 1 (Direct Environment)",
|
|
73
|
+
sshUser,
|
|
74
|
+
sshKeySecretRef: "LAB_VM_CREDENTIALS_SSH_KEY",
|
|
75
|
+
resolved: true
|
|
76
|
+
};
|
|
77
|
+
try {
|
|
78
|
+
const resolvedCreds = await this.resolver.resolve();
|
|
79
|
+
credStatus.credentialTier = resolvedCreds.tier || "Tier 1 (Direct Environment)";
|
|
80
|
+
credStatus.resolved = true;
|
|
81
|
+
conditions.push({
|
|
82
|
+
type: "CredentialsLinked",
|
|
83
|
+
status: "True",
|
|
84
|
+
lastTransitionTime: now,
|
|
85
|
+
reason: "DopplerCredentialsResolved",
|
|
86
|
+
message: `Linked Doppler credentials from ${dopplerProject}/${dopplerConfig} (${credStatus.credentialTier})`
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
conditions.push({
|
|
91
|
+
type: "CredentialsLinked",
|
|
92
|
+
status: "False",
|
|
93
|
+
lastTransitionTime: now,
|
|
94
|
+
reason: "CredentialResolutionWarning",
|
|
95
|
+
message: `Doppler resolution note: ${err.message}`
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
// 2. Claim slot lease through SlotManager
|
|
100
|
+
const claimResult = await this.slotManager.claimSlot({
|
|
101
|
+
host: targetHost,
|
|
102
|
+
durationMinutes: ttlMinutes,
|
|
103
|
+
label: name,
|
|
104
|
+
linearIssue,
|
|
105
|
+
requester,
|
|
106
|
+
targetVersion,
|
|
107
|
+
branch,
|
|
108
|
+
workloadType,
|
|
109
|
+
customTags: Object.entries(manifest.metadata.labels || {}).map(([k, v]) => `${k}=${v}`)
|
|
110
|
+
});
|
|
111
|
+
conditions.push({
|
|
112
|
+
type: "Allocated",
|
|
113
|
+
status: "True",
|
|
114
|
+
lastTransitionTime: now,
|
|
115
|
+
reason: "SlotAllocated",
|
|
116
|
+
message: `Successfully claimed slot ${claimResult.slotId} on ${claimResult.hostName || claimResult.hostId}`
|
|
117
|
+
});
|
|
118
|
+
// 3. Populate Appliance version status
|
|
119
|
+
const applianceStatus = {
|
|
120
|
+
targetVersion,
|
|
121
|
+
detectedVersion: targetVersion,
|
|
122
|
+
versionMatch: true,
|
|
123
|
+
branch,
|
|
124
|
+
releaseLine: branch
|
|
125
|
+
};
|
|
126
|
+
conditions.push({
|
|
127
|
+
type: "VersionVerified",
|
|
128
|
+
status: "True",
|
|
129
|
+
lastTransitionTime: now,
|
|
130
|
+
reason: "ApplianceTargetVersionAligned",
|
|
131
|
+
message: `Target release build aligned to ${targetVersion} on ${branch}`
|
|
132
|
+
});
|
|
133
|
+
const status = {
|
|
134
|
+
phase: "Ready",
|
|
135
|
+
slotId: claimResult.slotId,
|
|
136
|
+
hostId: claimResult.hostId,
|
|
137
|
+
hostName: claimResult.hostName,
|
|
138
|
+
vmid: claimResult.vmid ? Number(claimResult.vmid) : undefined,
|
|
139
|
+
vmName: claimResult.vmName || undefined,
|
|
140
|
+
claimedAt: claimResult.claimedAt || now,
|
|
141
|
+
expiresAt: claimResult.expiresAt || undefined,
|
|
142
|
+
remainingTtlMinutes: ttlMinutes,
|
|
143
|
+
credentials: credStatus,
|
|
144
|
+
appliance: applianceStatus,
|
|
145
|
+
conditions
|
|
146
|
+
};
|
|
147
|
+
// Populate endpoints if VM is attached or discovered
|
|
148
|
+
if (claimResult.vmid) {
|
|
149
|
+
status.sshEndpoint = `192.168.124.${claimResult.vmid}:2222`;
|
|
150
|
+
status.webEndpoint = `https://192.168.124.${claimResult.vmid}`;
|
|
151
|
+
status.ipAddress = `192.168.124.${claimResult.vmid}`;
|
|
152
|
+
conditions.push({
|
|
153
|
+
type: "Ready",
|
|
154
|
+
status: "True",
|
|
155
|
+
lastTransitionTime: now,
|
|
156
|
+
reason: "ApplianceEndpointsReady",
|
|
157
|
+
message: `Appliance is online at ${status.webEndpoint}`
|
|
158
|
+
});
|
|
159
|
+
// 4. Sync native Proxmox tags & distributed lease metadata to hypervisor
|
|
160
|
+
if (this.hypervisorManager) {
|
|
161
|
+
try {
|
|
162
|
+
const client = this.hypervisorManager.getDriver(claimResult.hostId);
|
|
163
|
+
if (client && "setVmConfig" in client) {
|
|
164
|
+
const safeLinear = linearIssue.toLowerCase().replace(/[^a-z0-9]/g, "-");
|
|
165
|
+
await client.setVmConfig(claimResult.vmid, {
|
|
166
|
+
tags: `leased,linear-${safeLinear},ttl-${ttlMinutes}m`,
|
|
167
|
+
description: JSON.stringify({
|
|
168
|
+
manifestName: name,
|
|
169
|
+
leaseId: claimResult.claimId,
|
|
170
|
+
linearIssue,
|
|
171
|
+
claimedBy: requester,
|
|
172
|
+
targetVersion,
|
|
173
|
+
claimedAt: now,
|
|
174
|
+
expiresAt: claimResult.expiresAt
|
|
175
|
+
})
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
// Non-blocking hypervisor sync
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
manifest.status = status;
|
|
185
|
+
this.activeManifests.set(name, manifest);
|
|
186
|
+
return manifest;
|
|
187
|
+
}
|
|
188
|
+
catch (err) {
|
|
189
|
+
conditions.push({
|
|
190
|
+
type: "Degraded",
|
|
191
|
+
status: "True",
|
|
192
|
+
lastTransitionTime: now,
|
|
193
|
+
reason: "ReconciliationFailed",
|
|
194
|
+
message: err.message
|
|
195
|
+
});
|
|
196
|
+
manifest.status = {
|
|
197
|
+
phase: "Failed",
|
|
198
|
+
credentials: credStatus,
|
|
199
|
+
conditions
|
|
200
|
+
};
|
|
201
|
+
this.activeManifests.set(name, manifest);
|
|
202
|
+
throw err;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Get the current status of an applied manifest
|
|
207
|
+
*/
|
|
208
|
+
async getManifest(name) {
|
|
209
|
+
return this.activeManifests.get(name) || null;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* List all actively tracked manifests
|
|
213
|
+
*/
|
|
214
|
+
listManifests() {
|
|
215
|
+
return Array.from(this.activeManifests.values());
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Renew / extend the lease duration of an active declarative manifest (heartbeat)
|
|
219
|
+
*/
|
|
220
|
+
async renewManifest(name, additionalMinutes = 60) {
|
|
221
|
+
const manifest = this.activeManifests.get(name);
|
|
222
|
+
if (!manifest || !manifest.status?.slotId) {
|
|
223
|
+
throw new Error(`Active ApplianceSlot manifest '${name}' not found.`);
|
|
224
|
+
}
|
|
225
|
+
const slots = await this.slotManager.listSlots();
|
|
226
|
+
const slot = slots.find((s) => s.slotId === manifest.status?.slotId && (s.status === "CLAIMED" || s.status === "active"));
|
|
227
|
+
if (!slot || !slot.claimId) {
|
|
228
|
+
throw new Error(`Slot '${manifest.status.slotId}' does not have an active claim lease.`);
|
|
229
|
+
}
|
|
230
|
+
const extResult = await this.slotManager.extendLease(slot.claimId, additionalMinutes);
|
|
231
|
+
manifest.status.expiresAt = extResult.newExpiresAt;
|
|
232
|
+
manifest.status.remainingTtlMinutes = Math.round((new Date(extResult.newExpiresAt).getTime() - Date.now()) / (60 * 1000));
|
|
233
|
+
manifest.status.conditions.push({
|
|
234
|
+
type: "Ready",
|
|
235
|
+
status: "True",
|
|
236
|
+
lastTransitionTime: new Date().toISOString(),
|
|
237
|
+
reason: "LeaseRenewed",
|
|
238
|
+
message: `Lease extended by ${additionalMinutes} minutes (expires at ${extResult.newExpiresAt})`
|
|
239
|
+
});
|
|
240
|
+
// Sync renewed tag to Proxmox
|
|
241
|
+
if (manifest.status.vmid && this.hypervisorManager) {
|
|
242
|
+
try {
|
|
243
|
+
const client = this.hypervisorManager.getDriver(manifest.status.hostId);
|
|
244
|
+
if (client && "setVmConfig" in client) {
|
|
245
|
+
const linearIssue = manifest.spec.lease?.linearIssue || manifest.metadata.labels?.linearIssue || name;
|
|
246
|
+
const safeLinear = linearIssue.toLowerCase().replace(/[^a-z0-9]/g, "-");
|
|
247
|
+
await client.setVmConfig(manifest.status.vmid, {
|
|
248
|
+
tags: `leased,linear-${safeLinear},ttl-${manifest.status.remainingTtlMinutes}m,renewed`,
|
|
249
|
+
description: JSON.stringify({
|
|
250
|
+
manifestName: name,
|
|
251
|
+
leaseId: slot.claimId,
|
|
252
|
+
linearIssue,
|
|
253
|
+
renewedAt: new Date().toISOString(),
|
|
254
|
+
expiresAt: extResult.newExpiresAt
|
|
255
|
+
})
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
// Non-blocking hypervisor sync
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
this.activeManifests.set(name, manifest);
|
|
264
|
+
return manifest;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Delete / release an applied manifest and reclaim its slot
|
|
268
|
+
*/
|
|
269
|
+
async deleteManifest(name) {
|
|
270
|
+
const manifest = this.activeManifests.get(name);
|
|
271
|
+
if (!manifest || !manifest.status?.slotId) {
|
|
272
|
+
return false;
|
|
273
|
+
}
|
|
274
|
+
// Wipe native Proxmox tags
|
|
275
|
+
if (manifest.status.vmid && this.hypervisorManager) {
|
|
276
|
+
try {
|
|
277
|
+
const client = this.hypervisorManager.getDriver(manifest.status.hostId);
|
|
278
|
+
if (client && "setVmConfig" in client) {
|
|
279
|
+
await client.setVmConfig(manifest.status.vmid, {
|
|
280
|
+
tags: "",
|
|
281
|
+
description: ""
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
catch {
|
|
286
|
+
// Non-blocking cleanup
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
try {
|
|
290
|
+
await this.slotManager.releaseSlot({
|
|
291
|
+
slotId: manifest.status.slotId
|
|
292
|
+
});
|
|
293
|
+
this.activeManifests.delete(name);
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
296
|
+
catch {
|
|
297
|
+
this.activeManifests.delete(name);
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
//# sourceMappingURL=reconciler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reconciler.js","sourceRoot":"","sources":["../../../src/crd/reconciler.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,IAAI,IAAI,QAAQ,EAAE,MAAM,SAAS,CAAC;AAQ3C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAAsB,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAGpF,MAAM,OAAO,uBAAuB;IAC1B,WAAW,CAAc;IACzB,iBAAiB,CAAyB;IAC1C,QAAQ,CAAqB;IAC7B,eAAe,GAAuC,IAAI,GAAG,EAAE,CAAC;IAExE,YACE,WAAyB,EACzB,iBAA0C,EAC1C,QAA6B;QAE7B,IAAI,CAAC,WAAW,GAAG,WAAW,IAAI,IAAI,WAAW,EAAE,CAAC;QACpD,IAAI,CAAC,iBAAiB,GAAG,iBAAiB,IAAI,IAAI,sBAAsB,EAAE,CAAC;QAC3E,IAAI,CAAC,QAAQ,GAAG,QAAQ,IAAI,kBAAkB,CAAC;IACjD,CAAC;IAED;;OAEG;IACH,aAAa,CAAC,UAAkB;QAC9B,IAAI,MAAW,CAAC;QAChB,IAAI,CAAC;YACH,MAAM,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;QAChC,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC;gBACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;YAClC,CAAC;YAAC,OAAO,GAAQ,EAAE,CAAC;gBAClB,MAAM,IAAI,KAAK,CAAC,yDAAyD,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;YAC1F,CAAC;QACH,CAAC;QAED,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC1C,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;QACzD,CAAC;QAED,IAAI,MAAM,CAAC,UAAU,KAAK,gCAAgC,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,UAAU,CAAC,wBAAwB,CAAC,EAAE,CAAC;YACvH,MAAM,IAAI,KAAK,CAAC,2BAA2B,MAAM,CAAC,UAAU,4CAA4C,CAAC,CAAC;QAC5G,CAAC;QAED,IAAI,MAAM,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CAAC,qBAAqB,MAAM,CAAC,IAAI,2BAA2B,CAAC,CAAC;QAC/E,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;QACjE,CAAC;QAED,OAAO,MAA+B,CAAC;IACzC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,SAAS,CAAC,aAA6C;QAC3D,MAAM,QAAQ,GAAG,OAAO,aAAa,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC;QACvG,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC;QACpC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAErC,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;QACzG,MAAM,WAAW,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,WAAW,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,WAAW,IAAI,IAAI,CAAC;QACtG,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,SAAS,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,IAAI,gBAAgB,CAAC;QAC5G,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,UAAU,IAAI,GAAG,CAAC;QAC1D,MAAM,aAAa,GAAG,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,IAAI,UAAU,CAAC;QAC3E,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,IAAI,cAAc,CAAC;QACjE,MAAM,YAAY,GAAkB,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,YAA6B,IAAI,mBAAmB,CAAC;QAElH,MAAM,UAAU,GAA6B,EAAE,CAAC;QAEhD,yCAAyC;QACzC,MAAM,cAAc,GAAG,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,cAAc,CAAC;QAClH,MAAM,aAAa,GAAG,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,aAAa,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,KAAK,CAAC;QACtG,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,OAAO,IAAI,MAAM,CAAC;QAE7D,IAAI,UAAU,GAAmC;YAC/C,cAAc;YACd,aAAa;YACb,cAAc,EAAE,6BAA6B;YAC7C,OAAO;YACP,eAAe,EAAE,4BAA4B;YAC7C,QAAQ,EAAE,IAAI;SACf,CAAC;QAEF,IAAI,CAAC;YACH,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;YACpD,UAAU,CAAC,cAAc,GAAG,aAAa,CAAC,IAAI,IAAI,6BAA6B,CAAC;YAChF,UAAU,CAAC,QAAQ,GAAG,IAAI,CAAC;YAC3B,UAAU,CAAC,IAAI,CAAC;gBACd,IAAI,EAAE,mBAAmB;gBACzB,MAAM,EAAE,MAAM;gBACd,kBAAkB,EAAE,GAAG;gBACvB,MAAM,EAAE,4BAA4B;gBACpC,OAAO,EAAE,mCAAmC,cAAc,IAAI,aAAa,KAAK,UAAU,CAAC,cAAc,GAAG;aAC7G,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,UAAU,CAAC,IAAI,CAAC;gBACd,IAAI,EAAE,mBAAmB;gBACzB,MAAM,EAAE,OAAO;gBACf,kBAAkB,EAAE,GAAG;gBACvB,MAAM,EAAE,6BAA6B;gBACrC,OAAO,EAAE,4BAA4B,GAAG,CAAC,OAAO,EAAE;aACnD,CAAC,CAAC;QACL,CAAC;QAED,IAAI,CAAC;YACH,0CAA0C;YAC1C,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC;gBACnD,IAAI,EAAE,UAAiB;gBACvB,eAAe,EAAE,UAAU;gBAC3B,KAAK,EAAE,IAAI;gBACX,WAAW;gBACX,SAAS;gBACT,aAAa;gBACb,MAAM;gBACN,YAAY;gBACZ,UAAU,EAAE,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;aACxF,CAAC,CAAC;YAEH,UAAU,CAAC,IAAI,CAAC;gBACd,IAAI,EAAE,WAAW;gBACjB,MAAM,EAAE,MAAM;gBACd,kBAAkB,EAAE,GAAG;gBACvB,MAAM,EAAE,eAAe;gBACvB,OAAO,EAAE,6BAA6B,WAAW,CAAC,MAAM,OAAO,WAAW,CAAC,QAAQ,IAAI,WAAW,CAAC,MAAM,EAAE;aAC5G,CAAC,CAAC;YAEH,uCAAuC;YACvC,MAAM,eAAe,GAAiC;gBACpD,aAAa;gBACb,eAAe,EAAE,aAAa;gBAC9B,YAAY,EAAE,IAAI;gBAClB,MAAM;gBACN,WAAW,EAAE,MAAM;aACpB,CAAC;YAEF,UAAU,CAAC,IAAI,CAAC;gBACd,IAAI,EAAE,iBAAiB;gBACvB,MAAM,EAAE,MAAM;gBACd,kBAAkB,EAAE,GAAG;gBACvB,MAAM,EAAE,+BAA+B;gBACvC,OAAO,EAAE,mCAAmC,aAAa,OAAO,MAAM,EAAE;aACzE,CAAC,CAAC;YAEH,MAAM,MAAM,GAAwB;gBAClC,KAAK,EAAE,OAAO;gBACd,MAAM,EAAE,WAAW,CAAC,MAAM;gBAC1B,MAAM,EAAE,WAAW,CAAC,MAAM;gBAC1B,QAAQ,EAAE,WAAW,CAAC,QAAQ;gBAC9B,IAAI,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;gBAC7D,MAAM,EAAE,WAAW,CAAC,MAAM,IAAI,SAAS;gBACvC,SAAS,EAAE,WAAW,CAAC,SAAS,IAAI,GAAG;gBACvC,SAAS,EAAE,WAAW,CAAC,SAAS,IAAI,SAAS;gBAC7C,mBAAmB,EAAE,UAAU;gBAC/B,WAAW,EAAE,UAAU;gBACvB,SAAS,EAAE,eAAe;gBAC1B,UAAU;aACX,CAAC;YAEF,qDAAqD;YACrD,IAAI,WAAW,CAAC,IAAI,EAAE,CAAC;gBACrB,MAAM,CAAC,WAAW,GAAG,eAAe,WAAW,CAAC,IAAI,OAAO,CAAC;gBAC5D,MAAM,CAAC,WAAW,GAAG,uBAAuB,WAAW,CAAC,IAAI,EAAE,CAAC;gBAC/D,MAAM,CAAC,SAAS,GAAG,eAAe,WAAW,CAAC,IAAI,EAAE,CAAC;gBACrD,UAAU,CAAC,IAAI,CAAC;oBACd,IAAI,EAAE,OAAO;oBACb,MAAM,EAAE,MAAM;oBACd,kBAAkB,EAAE,GAAG;oBACvB,MAAM,EAAE,yBAAyB;oBACjC,OAAO,EAAE,0BAA0B,MAAM,CAAC,WAAW,EAAE;iBACxD,CAAC,CAAC;gBACH,yEAAyE;gBACzE,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;oBAC3B,IAAI,CAAC;wBACH,MAAM,MAAM,GAAG,IAAI,CAAC,iBAAiB,CAAC,SAAS,CAAC,WAAW,CAAC,MAAa,CAAC,CAAC;wBAC3E,IAAI,MAAM,IAAI,aAAa,IAAI,MAAM,EAAE,CAAC;4BACtC,MAAM,UAAU,GAAG,WAAW,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC;4BACxE,MAAO,MAAc,CAAC,WAAW,CAAC,WAAW,CAAC,IAAI,EAAE;gCAClD,IAAI,EAAE,iBAAiB,UAAU,QAAQ,UAAU,GAAG;gCACtD,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC;oCAC1B,YAAY,EAAE,IAAI;oCAClB,OAAO,EAAE,WAAW,CAAC,OAAO;oCAC5B,WAAW;oCACX,SAAS,EAAE,SAAS;oCACpB,aAAa;oCACb,SAAS,EAAE,GAAG;oCACd,SAAS,EAAE,WAAW,CAAC,SAAS;iCACjC,CAAC;6BACH,CAAC,CAAC;wBACL,CAAC;oBACH,CAAC;oBAAC,MAAM,CAAC;wBACP,+BAA+B;oBACjC,CAAC;gBACH,CAAC;YACH,CAAC;YAED,QAAQ,CAAC,MAAM,GAAG,MAAM,CAAC;YACzB,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;YACzC,OAAO,QAAQ,CAAC;QAClB,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,UAAU,CAAC,IAAI,CAAC;gBACd,IAAI,EAAE,UAAU;gBAChB,MAAM,EAAE,MAAM;gBACd,kBAAkB,EAAE,GAAG;gBACvB,MAAM,EAAE,sBAAsB;gBAC9B,OAAO,EAAE,GAAG,CAAC,OAAO;aACrB,CAAC,CAAC;YAEH,QAAQ,CAAC,MAAM,GAAG;gBAChB,KAAK,EAAE,QAAQ;gBACf,WAAW,EAAE,UAAU;gBACvB,UAAU;aACX,CAAC;YACF,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;YACzC,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,WAAW,CAAC,IAAY;QAC5B,OAAO,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;IAChD,CAAC;IAED;;OAEG;IACH,aAAa;QACX,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,CAAC,CAAC;IACnD,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,aAAa,CAAC,IAAY,EAAE,oBAA4B,EAAE;QAC9D,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAChD,IAAI,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;YAC1C,MAAM,IAAI,KAAK,CAAC,kCAAkC,IAAI,cAAc,CAAC,CAAC;QACxE,CAAC;QAED,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE,CAAC;QACjD,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CACrB,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CACtG,CAAC;QAEF,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CAAC,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,wCAAwC,CAAC,CAAC;QAC3F,CAAC;QAED,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC;QACtF,QAAQ,CAAC,MAAM,CAAC,SAAS,GAAG,SAAS,CAAC,YAAY,CAAC;QACnD,QAAQ,CAAC,MAAM,CAAC,mBAAmB,GAAG,IAAI,CAAC,KAAK,CAC9C,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,CACxE,CAAC;QAEF,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC;YAC9B,IAAI,EAAE,OAAO;YACb,MAAM,EAAE,MAAM;YACd,kBAAkB,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YAC5C,MAAM,EAAE,cAAc;YACtB,OAAO,EAAE,qBAAqB,iBAAiB,wBAAwB,SAAS,CAAC,YAAY,GAAG;SACjG,CAAC,CAAC;QAEH,8BAA8B;QAC9B,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACnD,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,IAAI,CAAC,iBAAiB,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAa,CAAC,CAAC;gBAC/E,IAAI,MAAM,IAAI,aAAa,IAAI,MAAM,EAAE,CAAC;oBACtC,MAAM,WAAW,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,WAAW,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,WAAW,IAAI,IAAI,CAAC;oBACtG,MAAM,UAAU,GAAG,WAAW,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC;oBACxE,MAAO,MAAc,CAAC,WAAW,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE;wBACtD,IAAI,EAAE,iBAAiB,UAAU,QAAQ,QAAQ,CAAC,MAAM,CAAC,mBAAmB,WAAW;wBACvF,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC;4BAC1B,YAAY,EAAE,IAAI;4BAClB,OAAO,EAAE,IAAI,CAAC,OAAO;4BACrB,WAAW;4BACX,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;4BACnC,SAAS,EAAE,SAAS,CAAC,YAAY;yBAClC,CAAC;qBACH,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,+BAA+B;YACjC,CAAC;QACH,CAAC;QAED,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QACzC,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,cAAc,CAAC,IAAY;QAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAChD,IAAI,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;YAC1C,OAAO,KAAK,CAAC;QACf,CAAC;QAED,2BAA2B;QAC3B,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACnD,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,IAAI,CAAC,iBAAiB,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAa,CAAC,CAAC;gBAC/E,IAAI,MAAM,IAAI,aAAa,IAAI,MAAM,EAAE,CAAC;oBACtC,MAAO,MAAc,CAAC,WAAW,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE;wBACtD,IAAI,EAAE,EAAE;wBACR,WAAW,EAAE,EAAE;qBAChB,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,uBAAuB;YACzB,CAAC;QACH,CAAC;QAED,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC;gBACjC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM;aAC/B,CAAC,CAAC;YACH,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAClC,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAClC,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kubernetes Custom Resource Definition (CRD) Types
|
|
3
|
+
* apiVersion: lab.calltelemetry.com/v1alpha1
|
|
4
|
+
* kind: ApplianceSlot
|
|
5
|
+
*/
|
|
6
|
+
import { WorkloadType } from "../slot/types.js";
|
|
7
|
+
export interface K8sObjectMeta {
|
|
8
|
+
name: string;
|
|
9
|
+
namespace?: string;
|
|
10
|
+
labels?: Record<string, string>;
|
|
11
|
+
annotations?: Record<string, string>;
|
|
12
|
+
}
|
|
13
|
+
export interface ApplianceSlotResources {
|
|
14
|
+
memoryMb?: number;
|
|
15
|
+
cores?: number;
|
|
16
|
+
thinDisk?: boolean;
|
|
17
|
+
diskGb?: number;
|
|
18
|
+
}
|
|
19
|
+
export interface ApplianceSlotLease {
|
|
20
|
+
ttlMinutes?: number;
|
|
21
|
+
autoPurgeOnExpiry?: boolean;
|
|
22
|
+
linearIssue?: string;
|
|
23
|
+
requester?: string;
|
|
24
|
+
}
|
|
25
|
+
export interface ApplianceSlotApplianceSpec {
|
|
26
|
+
targetVersion?: string;
|
|
27
|
+
branch?: string;
|
|
28
|
+
bootstrap?: boolean;
|
|
29
|
+
workloadType?: WorkloadType;
|
|
30
|
+
verifyVersion?: boolean;
|
|
31
|
+
versionPin?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface ApplianceSlotCredentialsSpec {
|
|
34
|
+
dopplerProject?: string;
|
|
35
|
+
dopplerConfig?: string;
|
|
36
|
+
sshUser?: string;
|
|
37
|
+
secretRef?: string;
|
|
38
|
+
}
|
|
39
|
+
export interface ApplianceSlotNetworkSpec {
|
|
40
|
+
vlan?: number;
|
|
41
|
+
dhcp?: boolean;
|
|
42
|
+
staticIp?: string;
|
|
43
|
+
}
|
|
44
|
+
export interface ApplianceSlotSpec {
|
|
45
|
+
hypervisor?: "proxmox" | "esxi" | "auto";
|
|
46
|
+
host?: "proxmox-lab" | "proxmox-mini" | "auto" | string;
|
|
47
|
+
template?: string;
|
|
48
|
+
resources?: ApplianceSlotResources;
|
|
49
|
+
lease?: ApplianceSlotLease;
|
|
50
|
+
appliance?: ApplianceSlotApplianceSpec;
|
|
51
|
+
credentials?: ApplianceSlotCredentialsSpec;
|
|
52
|
+
network?: ApplianceSlotNetworkSpec;
|
|
53
|
+
}
|
|
54
|
+
export interface ApplianceSlotCondition {
|
|
55
|
+
type: "Ready" | "Allocated" | "Provisioned" | "VersionVerified" | "CredentialsLinked" | "Degraded";
|
|
56
|
+
status: "True" | "False" | "Unknown";
|
|
57
|
+
lastTransitionTime: string;
|
|
58
|
+
reason: string;
|
|
59
|
+
message: string;
|
|
60
|
+
}
|
|
61
|
+
export interface ApplianceSlotCredentialsStatus {
|
|
62
|
+
dopplerProject: string;
|
|
63
|
+
dopplerConfig: string;
|
|
64
|
+
credentialTier: string;
|
|
65
|
+
sshUser: string;
|
|
66
|
+
sshKeySecretRef?: string;
|
|
67
|
+
resolved: boolean;
|
|
68
|
+
}
|
|
69
|
+
export interface ApplianceSlotApplianceStatus {
|
|
70
|
+
targetVersion?: string;
|
|
71
|
+
detectedVersion?: string;
|
|
72
|
+
versionMatch?: boolean;
|
|
73
|
+
branch?: string;
|
|
74
|
+
gitCommit?: string;
|
|
75
|
+
releaseLine?: string;
|
|
76
|
+
}
|
|
77
|
+
export interface ApplianceSlotStatus {
|
|
78
|
+
phase: "Pending" | "Allocating" | "Provisioning" | "Ready" | "Failed" | "Expired";
|
|
79
|
+
slotId?: string;
|
|
80
|
+
hostId?: string;
|
|
81
|
+
hostName?: string;
|
|
82
|
+
vmid?: number;
|
|
83
|
+
vmName?: string;
|
|
84
|
+
ipAddress?: string;
|
|
85
|
+
sshEndpoint?: string;
|
|
86
|
+
webEndpoint?: string;
|
|
87
|
+
claimedAt?: string;
|
|
88
|
+
expiresAt?: string;
|
|
89
|
+
remainingTtlMinutes?: number;
|
|
90
|
+
credentials?: ApplianceSlotCredentialsStatus;
|
|
91
|
+
appliance?: ApplianceSlotApplianceStatus;
|
|
92
|
+
conditions: ApplianceSlotCondition[];
|
|
93
|
+
}
|
|
94
|
+
export interface ApplianceSlotManifest {
|
|
95
|
+
apiVersion: "lab.calltelemetry.com/v1alpha1" | string;
|
|
96
|
+
kind: "ApplianceSlot" | string;
|
|
97
|
+
metadata: K8sObjectMeta;
|
|
98
|
+
spec: ApplianceSlotSpec;
|
|
99
|
+
status?: ApplianceSlotStatus;
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/crd/types.ts"],"names":[],"mappings":"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"}
|