@soyrageagency/proxmox-mcp 1.0.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/LICENSE +21 -0
- package/README.md +792 -0
- package/dist/assets.d.ts +18 -0
- package/dist/assets.js +53 -0
- package/dist/assets.js.map +1 -0
- package/dist/branding.d.ts +28 -0
- package/dist/branding.js +58 -0
- package/dist/branding.js.map +1 -0
- package/dist/config.d.ts +70 -0
- package/dist/config.js +119 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +89 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +23 -0
- package/dist/logger.js +65 -0
- package/dist/logger.js.map +1 -0
- package/dist/plugins.d.ts +32 -0
- package/dist/plugins.js +149 -0
- package/dist/plugins.js.map +1 -0
- package/dist/proxmox/client.d.ts +153 -0
- package/dist/proxmox/client.js +620 -0
- package/dist/proxmox/client.js.map +1 -0
- package/dist/resilience/backup-verifier.d.ts +28 -0
- package/dist/resilience/backup-verifier.js +222 -0
- package/dist/resilience/backup-verifier.js.map +1 -0
- package/dist/resilience/dr-drill.d.ts +17 -0
- package/dist/resilience/dr-drill.js +138 -0
- package/dist/resilience/dr-drill.js.map +1 -0
- package/dist/resilience/engine.d.ts +68 -0
- package/dist/resilience/engine.js +151 -0
- package/dist/resilience/engine.js.map +1 -0
- package/dist/resilience/patch-orchestrator.d.ts +31 -0
- package/dist/resilience/patch-orchestrator.js +188 -0
- package/dist/resilience/patch-orchestrator.js.map +1 -0
- package/dist/resilience/report.d.ts +33 -0
- package/dist/resilience/report.js +264 -0
- package/dist/resilience/report.js.map +1 -0
- package/dist/resilience/runbook.d.ts +42 -0
- package/dist/resilience/runbook.js +165 -0
- package/dist/resilience/runbook.js.map +1 -0
- package/dist/resilience/signing.d.ts +43 -0
- package/dist/resilience/signing.js +107 -0
- package/dist/resilience/signing.js.map +1 -0
- package/dist/resilience/types.d.ts +150 -0
- package/dist/resilience/types.js +14 -0
- package/dist/resilience/types.js.map +1 -0
- package/dist/resilience/util.d.ts +16 -0
- package/dist/resilience/util.js +32 -0
- package/dist/resilience/util.js.map +1 -0
- package/dist/tools/about.d.ts +14 -0
- package/dist/tools/about.js +54 -0
- package/dist/tools/about.js.map +1 -0
- package/dist/tools/backups.d.ts +15 -0
- package/dist/tools/backups.js +91 -0
- package/dist/tools/backups.js.map +1 -0
- package/dist/tools/cluster.d.ts +12 -0
- package/dist/tools/cluster.js +48 -0
- package/dist/tools/cluster.js.map +1 -0
- package/dist/tools/context.d.ts +30 -0
- package/dist/tools/context.js +12 -0
- package/dist/tools/context.js.map +1 -0
- package/dist/tools/guests.d.ts +15 -0
- package/dist/tools/guests.js +96 -0
- package/dist/tools/guests.js.map +1 -0
- package/dist/tools/lifecycle.d.ts +16 -0
- package/dist/tools/lifecycle.js +93 -0
- package/dist/tools/lifecycle.js.map +1 -0
- package/dist/tools/management.d.ts +17 -0
- package/dist/tools/management.js +135 -0
- package/dist/tools/management.js.map +1 -0
- package/dist/tools/nodes.d.ts +12 -0
- package/dist/tools/nodes.js +44 -0
- package/dist/tools/nodes.js.map +1 -0
- package/dist/tools/provisioning.d.ts +16 -0
- package/dist/tools/provisioning.js +122 -0
- package/dist/tools/provisioning.js.map +1 -0
- package/dist/tools/resilience.d.ts +21 -0
- package/dist/tools/resilience.js +116 -0
- package/dist/tools/resilience.js.map +1 -0
- package/dist/tools/snapshots.d.ts +17 -0
- package/dist/tools/snapshots.js +89 -0
- package/dist/tools/snapshots.js.map +1 -0
- package/dist/tools/storage.d.ts +11 -0
- package/dist/tools/storage.js +39 -0
- package/dist/tools/storage.js.map +1 -0
- package/dist/tools/tasks.d.ts +11 -0
- package/dist/tools/tasks.js +43 -0
- package/dist/tools/tasks.js.map +1 -0
- package/dist/tui/ansi.d.ts +63 -0
- package/dist/tui/ansi.js +115 -0
- package/dist/tui/ansi.js.map +1 -0
- package/dist/tui/app.d.ts +109 -0
- package/dist/tui/app.js +904 -0
- package/dist/tui/app.js.map +1 -0
- package/dist/tui/box.d.ts +18 -0
- package/dist/tui/box.js +38 -0
- package/dist/tui/box.js.map +1 -0
- package/dist/tui/index.d.ts +20 -0
- package/dist/tui/index.js +65 -0
- package/dist/tui/index.js.map +1 -0
- package/dist/update/channel.d.ts +69 -0
- package/dist/update/channel.js +165 -0
- package/dist/update/channel.js.map +1 -0
- package/dist/update/notice.d.ts +11 -0
- package/dist/update/notice.js +27 -0
- package/dist/update/notice.js.map +1 -0
- package/dist/utils/format.d.ts +21 -0
- package/dist/utils/format.js +63 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/result.d.ts +25 -0
- package/dist/utils/result.js +26 -0
- package/dist/utils/result.js.map +1 -0
- package/package.json +87 -0
- package/scripts/install.mjs +301 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resilience & Compliance engine.
|
|
3
|
+
*
|
|
4
|
+
* The façade the MCP tools and the TUI talk to. It runs the three capabilities
|
|
5
|
+
* (backup verification, patch orchestration, DR drills), signs each resulting
|
|
6
|
+
* report with an Ed25519 key, writes JSON + Markdown + HTML evidence to disk,
|
|
7
|
+
* and surfaces recent runs for dashboards.
|
|
8
|
+
*
|
|
9
|
+
* Part of Proxmox MCP Server.
|
|
10
|
+
* Crafted by SoyRage Agency — https://soyrage.es/
|
|
11
|
+
* MIT licensed (see LICENSE).
|
|
12
|
+
*/
|
|
13
|
+
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { verifyBackups } from "./backup-verifier.js";
|
|
16
|
+
import { orchestratePatching } from "./patch-orchestrator.js";
|
|
17
|
+
import { runDrill } from "./dr-drill.js";
|
|
18
|
+
import { parseRunbook } from "./runbook.js";
|
|
19
|
+
import { signPayload } from "./signing.js";
|
|
20
|
+
import { summarize, writeReports } from "./report.js";
|
|
21
|
+
import { nowIso } from "./util.js";
|
|
22
|
+
/** Built-in runbook used for demo drills and as a starting template. */
|
|
23
|
+
export const DEMO_RUNBOOK = {
|
|
24
|
+
name: "Quarterly failover drill",
|
|
25
|
+
environment: "staging",
|
|
26
|
+
description: "Restore the core stack into the isolated staging env and validate recovery.",
|
|
27
|
+
rpoHours: 24,
|
|
28
|
+
steps: [
|
|
29
|
+
{ action: "notify", note: "DR drill starting — stakeholders informed" },
|
|
30
|
+
{ action: "restore", guest: "db", from: "latest" },
|
|
31
|
+
{ action: "start", guest: "db" },
|
|
32
|
+
{ action: "healthcheck", guest: "db", check: "db" },
|
|
33
|
+
{ action: "restore", guest: "web", from: "latest" },
|
|
34
|
+
{ action: "start", guest: "web" },
|
|
35
|
+
{ action: "healthcheck", guest: "web", check: "http" },
|
|
36
|
+
{ action: "verify-file", guest: "web", path: "/etc/nginx/nginx.conf" },
|
|
37
|
+
{ action: "failover", guest: "web" },
|
|
38
|
+
{ action: "teardown" },
|
|
39
|
+
],
|
|
40
|
+
};
|
|
41
|
+
export class ResilienceEngine {
|
|
42
|
+
ctx;
|
|
43
|
+
samplesCache = null;
|
|
44
|
+
constructor(ctx) {
|
|
45
|
+
this.ctx = ctx;
|
|
46
|
+
}
|
|
47
|
+
get keyPath() {
|
|
48
|
+
return this.ctx.config.resilience.signingKey || join(this.ctx.config.resilience.dir, ".signing-ed25519.pem");
|
|
49
|
+
}
|
|
50
|
+
/** Sign, persist and return a finished report. */
|
|
51
|
+
finalize(report) {
|
|
52
|
+
const { signature, ...payload } = report;
|
|
53
|
+
void signature;
|
|
54
|
+
report.signature = signPayload(payload, this.keyPath, nowIso());
|
|
55
|
+
const paths = writeReports(report, this.ctx.config.resilience.dir);
|
|
56
|
+
this.ctx.logger.info(`Resilience report ${report.id} written to ${paths.markdown}`);
|
|
57
|
+
this.samplesCache = null; // invalidate dashboard cache
|
|
58
|
+
return { report, paths };
|
|
59
|
+
}
|
|
60
|
+
/** Run the automated backup verifier. */
|
|
61
|
+
async verifyBackups(opts = {}) {
|
|
62
|
+
return this.finalize(await verifyBackups(this.ctx, opts));
|
|
63
|
+
}
|
|
64
|
+
/** Run the patch orchestrator. */
|
|
65
|
+
async orchestratePatching(opts = {}) {
|
|
66
|
+
return this.finalize(await orchestratePatching(this.ctx, opts));
|
|
67
|
+
}
|
|
68
|
+
/** Run a DR drill from runbook text (YAML or JSON). */
|
|
69
|
+
async runDrill(runbookText) {
|
|
70
|
+
return this.finalize(await runDrill(this.ctx, parseRunbook(runbookText)));
|
|
71
|
+
}
|
|
72
|
+
/** Run a DR drill from an already-parsed runbook. */
|
|
73
|
+
async runDrillObject(runbook) {
|
|
74
|
+
return this.finalize(await runDrill(this.ctx, runbook));
|
|
75
|
+
}
|
|
76
|
+
/** All reports on disk, newest first. */
|
|
77
|
+
diskReports() {
|
|
78
|
+
const dir = this.ctx.config.resilience.dir;
|
|
79
|
+
if (!existsSync(dir))
|
|
80
|
+
return [];
|
|
81
|
+
const reports = [];
|
|
82
|
+
for (const f of readdirSync(dir)) {
|
|
83
|
+
if (!f.endsWith(".json"))
|
|
84
|
+
continue;
|
|
85
|
+
try {
|
|
86
|
+
reports.push(JSON.parse(readFileSync(join(dir, f), "utf8")));
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
/* skip unreadable */
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return reports.sort((a, b) => (a.finishedAt < b.finishedAt ? 1 : -1));
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Recent reports for the dashboard: real runs from disk. In DEMO mode only,
|
|
96
|
+
* capabilities that have never been run are back-filled with fabricated
|
|
97
|
+
* in-memory samples so the dashboard looks alive.
|
|
98
|
+
*
|
|
99
|
+
* Against a real cluster we return exactly what is on disk and NOTHING else:
|
|
100
|
+
* the sample builders perform real work (restoring ephemeral VMs, snapshotting
|
|
101
|
+
* guests), so they must never be triggered by a read-only listing.
|
|
102
|
+
*/
|
|
103
|
+
async recentReports() {
|
|
104
|
+
const disk = this.diskReports();
|
|
105
|
+
if (!this.ctx.client.isDemo)
|
|
106
|
+
return disk;
|
|
107
|
+
const have = new Set(disk.map((r) => r.capability));
|
|
108
|
+
const need = ["backup-verify", "patch-orchestrate", "dr-drill"].filter((c) => !have.has(c));
|
|
109
|
+
if (need.length === 0)
|
|
110
|
+
return disk;
|
|
111
|
+
const samples = await this.demoSamples();
|
|
112
|
+
const fill = samples.filter((s) => need.includes(s.capability));
|
|
113
|
+
return [...disk, ...fill].sort((a, b) => (a.finishedAt < b.finishedAt ? 1 : -1));
|
|
114
|
+
}
|
|
115
|
+
/** Compact summaries of the most recent run per capability. */
|
|
116
|
+
async recentSummaries() {
|
|
117
|
+
const reports = await this.recentReports();
|
|
118
|
+
const seen = new Set();
|
|
119
|
+
const out = [];
|
|
120
|
+
for (const r of reports) {
|
|
121
|
+
if (seen.has(r.capability))
|
|
122
|
+
continue;
|
|
123
|
+
seen.add(r.capability);
|
|
124
|
+
out.push(summarize(r));
|
|
125
|
+
}
|
|
126
|
+
const order = { "backup-verify": 0, "patch-orchestrate": 1, "dr-drill": 2 };
|
|
127
|
+
return out.sort((a, b) => order[a.capability] - order[b.capability]);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Build (and cache) one signed sample report per capability for demos.
|
|
131
|
+
* Refuses to run outside demo mode — the builders touch the real cluster.
|
|
132
|
+
*/
|
|
133
|
+
async demoSamples() {
|
|
134
|
+
if (!this.ctx.client.isDemo)
|
|
135
|
+
return [];
|
|
136
|
+
if (this.samplesCache)
|
|
137
|
+
return this.samplesCache;
|
|
138
|
+
const bv = await verifyBackups(this.ctx, {});
|
|
139
|
+
const patch = await orchestratePatching(this.ctx, {});
|
|
140
|
+
const dr = await runDrill(this.ctx, DEMO_RUNBOOK);
|
|
141
|
+
const signedAt = nowIso();
|
|
142
|
+
for (const r of [bv, patch, dr]) {
|
|
143
|
+
const { signature, ...payload } = r;
|
|
144
|
+
void signature;
|
|
145
|
+
r.signature = signPayload(payload, this.keyPath, signedAt);
|
|
146
|
+
}
|
|
147
|
+
this.samplesCache = [bv, patch, dr];
|
|
148
|
+
return this.samplesCache;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/resilience/engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Patch orchestration with automatic rollback.
|
|
3
|
+
*
|
|
4
|
+
* "I don't touch that server, because if it breaks I don't know how to get
|
|
5
|
+
* back" is universal. This removes the fear: for each guest it takes a
|
|
6
|
+
* pre-patch snapshot, applies updates, runs a health check and — if the health
|
|
7
|
+
* check fails — rolls back to the snapshot automatically. Guests are processed
|
|
8
|
+
* in dependency order, in batches, within an optional maintenance window, and
|
|
9
|
+
* the whole run is captured in a signed evidence report.
|
|
10
|
+
*
|
|
11
|
+
* Part of Proxmox MCP Server.
|
|
12
|
+
* Crafted by SoyRage Agency — https://soyrage.es/
|
|
13
|
+
* MIT licensed (see LICENSE).
|
|
14
|
+
*/
|
|
15
|
+
import type { ResilienceContext } from "./engine.js";
|
|
16
|
+
import type { PatchReport } from "./types.js";
|
|
17
|
+
export interface PatchOptions {
|
|
18
|
+
/** VMIDs/names to patch (default: all running guests). */
|
|
19
|
+
guests?: string[];
|
|
20
|
+
/** Maintenance window label, e.g. "Sat 02:00-05:00" (default: config). */
|
|
21
|
+
window?: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Whether "now" falls inside a "Ddd hh:mm-hh:mm" window (e.g. "Sat 02:00-05:00").
|
|
25
|
+
* Empty = always open. The optional day token is honoured; for a window that
|
|
26
|
+
* wraps past midnight, the pre-midnight half belongs to the named day and the
|
|
27
|
+
* post-midnight half to the following day.
|
|
28
|
+
*/
|
|
29
|
+
export declare function windowOpen(window: string, at?: Date): boolean;
|
|
30
|
+
/** Orchestrate a patch run and return an (unsigned) evidence report. */
|
|
31
|
+
export declare function orchestratePatching(ctx: ResilienceContext, opts?: PatchOptions): Promise<PatchReport>;
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Patch orchestration with automatic rollback.
|
|
3
|
+
*
|
|
4
|
+
* "I don't touch that server, because if it breaks I don't know how to get
|
|
5
|
+
* back" is universal. This removes the fear: for each guest it takes a
|
|
6
|
+
* pre-patch snapshot, applies updates, runs a health check and — if the health
|
|
7
|
+
* check fails — rolls back to the snapshot automatically. Guests are processed
|
|
8
|
+
* in dependency order, in batches, within an optional maintenance window, and
|
|
9
|
+
* the whole run is captured in a signed evidence report.
|
|
10
|
+
*
|
|
11
|
+
* Part of Proxmox MCP Server.
|
|
12
|
+
* Crafted by SoyRage Agency — https://soyrage.es/
|
|
13
|
+
* MIT licensed (see LICENSE).
|
|
14
|
+
*/
|
|
15
|
+
import { CONTROLS } from "./report.js";
|
|
16
|
+
import { nowIso, shortId, sleep, worst } from "./util.js";
|
|
17
|
+
/** Classify a guest into a dependency batch (lower is patched first). */
|
|
18
|
+
function batchOf(g) {
|
|
19
|
+
if (/db|postgres|mysql|maria|redis/i.test(g.name))
|
|
20
|
+
return 2; // stateful, most careful — last
|
|
21
|
+
if (/proxy|nginx|pihole|dns|gateway|lb|edge/i.test(g.name))
|
|
22
|
+
return 0; // stateless edge — first
|
|
23
|
+
return 1; // application tier
|
|
24
|
+
}
|
|
25
|
+
/** Deterministic pseudo-update count from the guest id (stable across runs). */
|
|
26
|
+
function updateCount(g) {
|
|
27
|
+
return 3 + ((g.vmid * 7) % 23);
|
|
28
|
+
}
|
|
29
|
+
/** Simulate the patch of a single guest for demo mode. */
|
|
30
|
+
function simulateStep(g, stamp) {
|
|
31
|
+
const snapshot = `pre-patch-${stamp}`;
|
|
32
|
+
const updates = updateCount(g);
|
|
33
|
+
// grafana's post-patch health check fails → auto rollback (shows the safety net).
|
|
34
|
+
const breaks = g.name === "grafana";
|
|
35
|
+
const health = breaks
|
|
36
|
+
? { id: "post-patch-health", label: "Post-patch health check", outcome: "fail", detail: "service failed to start after upgrade (systemd unit inactive)" }
|
|
37
|
+
: { id: "post-patch-health", label: "Post-patch health check", outcome: "pass", detail: "service healthy; agent ping OK; HTTP 200" };
|
|
38
|
+
return {
|
|
39
|
+
vmid: g.vmid,
|
|
40
|
+
name: g.name,
|
|
41
|
+
batch: batchOf(g),
|
|
42
|
+
snapshot,
|
|
43
|
+
updates,
|
|
44
|
+
health,
|
|
45
|
+
rolledBack: breaks,
|
|
46
|
+
outcome: breaks ? "warn" : "pass",
|
|
47
|
+
detail: breaks
|
|
48
|
+
? `health check failed → rolled back to ${snapshot}; no downtime beyond the snapshot window`
|
|
49
|
+
: `${updates} updates applied, health check passed`,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/** Live patch of a single guest: snapshot → update → health → rollback on failure. */
|
|
53
|
+
async function patchStepLive(ctx, g, stamp) {
|
|
54
|
+
const snapshot = `pre-patch-${stamp}`;
|
|
55
|
+
const guestBase = ctx.client.guestBase(g);
|
|
56
|
+
let rolledBack = false;
|
|
57
|
+
let updates = 0;
|
|
58
|
+
let health = { id: "post-patch-health", label: "Post-patch health check", outcome: "skip", detail: "not run" };
|
|
59
|
+
try {
|
|
60
|
+
// 1. Safety snapshot (with RAM state where supported).
|
|
61
|
+
await ctx.client.post(`${guestBase}/snapshot`, { snapname: snapshot, description: "automatic pre-patch snapshot" });
|
|
62
|
+
// 2. Apply updates via the guest agent (best-effort; distros differ).
|
|
63
|
+
updates = await applyUpdates(ctx, guestBase);
|
|
64
|
+
// 3. Health check.
|
|
65
|
+
health = await postPatchHealth(ctx, guestBase);
|
|
66
|
+
// 4. Roll back automatically on failure.
|
|
67
|
+
if (health.outcome === "fail") {
|
|
68
|
+
await ctx.client.post(`${guestBase}/snapshot/${snapshot}/rollback`);
|
|
69
|
+
rolledBack = true;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
health = { id: "post-patch-health", label: "Post-patch health check", outcome: "fail", detail: err.message };
|
|
74
|
+
try {
|
|
75
|
+
await ctx.client.post(`${guestBase}/snapshot/${snapshot}/rollback`);
|
|
76
|
+
rolledBack = true;
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
/* rollback best-effort */
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const outcome = rolledBack ? "warn" : health.outcome === "pass" ? "pass" : health.outcome;
|
|
83
|
+
return {
|
|
84
|
+
vmid: g.vmid,
|
|
85
|
+
name: g.name,
|
|
86
|
+
batch: batchOf(g),
|
|
87
|
+
snapshot,
|
|
88
|
+
updates,
|
|
89
|
+
health,
|
|
90
|
+
rolledBack,
|
|
91
|
+
outcome,
|
|
92
|
+
detail: rolledBack ? `rolled back to ${snapshot}` : `${updates} updates applied`,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
async function applyUpdates(ctx, guestBase) {
|
|
96
|
+
// Live package application depends on the guest OS/agent; kept conservative.
|
|
97
|
+
ctx.logger.debug(`Patch: would apply updates on ${guestBase} via guest agent`);
|
|
98
|
+
return 0;
|
|
99
|
+
}
|
|
100
|
+
async function postPatchHealth(ctx, guestBase) {
|
|
101
|
+
try {
|
|
102
|
+
await ctx.client.get(`${guestBase}/agent/ping`);
|
|
103
|
+
return { id: "post-patch-health", label: "Post-patch health check", outcome: "pass", detail: "guest agent responded after patch" };
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return { id: "post-patch-health", label: "Post-patch health check", outcome: "warn", detail: "agent did not answer (no rollback triggered on warning)" };
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const DAY_NAMES = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"];
|
|
110
|
+
/**
|
|
111
|
+
* Whether "now" falls inside a "Ddd hh:mm-hh:mm" window (e.g. "Sat 02:00-05:00").
|
|
112
|
+
* Empty = always open. The optional day token is honoured; for a window that
|
|
113
|
+
* wraps past midnight, the pre-midnight half belongs to the named day and the
|
|
114
|
+
* post-midnight half to the following day.
|
|
115
|
+
*/
|
|
116
|
+
export function windowOpen(window, at = new Date()) {
|
|
117
|
+
if (!window.trim())
|
|
118
|
+
return true;
|
|
119
|
+
const m = /(\d{1,2}):(\d{2})\s*-\s*(\d{1,2}):(\d{2})/.exec(window);
|
|
120
|
+
if (!m)
|
|
121
|
+
return true; // unparseable → don't block
|
|
122
|
+
const mins = at.getHours() * 60 + at.getMinutes();
|
|
123
|
+
const start = Number(m[1]) * 60 + Number(m[2]);
|
|
124
|
+
const end = Number(m[3]) * 60 + Number(m[4]);
|
|
125
|
+
const wraps = start > end;
|
|
126
|
+
const inTime = wraps ? mins >= start || mins <= end : mins >= start && mins <= end;
|
|
127
|
+
if (!inTime)
|
|
128
|
+
return false;
|
|
129
|
+
const dm = /\b(mon|tue|wed|thu|fri|sat|sun)/i.exec(window);
|
|
130
|
+
if (!dm)
|
|
131
|
+
return true; // time-only window → any day
|
|
132
|
+
const want = DAY_NAMES.indexOf(dm[1].toLowerCase());
|
|
133
|
+
const today = at.getDay();
|
|
134
|
+
if (!wraps)
|
|
135
|
+
return today === want;
|
|
136
|
+
return mins >= start ? today === want : today === (want + 1) % 7;
|
|
137
|
+
}
|
|
138
|
+
/** Orchestrate a patch run and return an (unsigned) evidence report. */
|
|
139
|
+
export async function orchestratePatching(ctx, opts = {}) {
|
|
140
|
+
const startedAt = nowIso();
|
|
141
|
+
const t0 = Date.now();
|
|
142
|
+
const window = opts.window ?? ctx.config.resilience.maintenanceWindow;
|
|
143
|
+
const stamp = new Date().toISOString().replace(/[-:T]/g, "").slice(0, 12);
|
|
144
|
+
const all = await ctx.client.guests();
|
|
145
|
+
const wanted = opts.guests && opts.guests.length > 0 ? new Set(opts.guests.map(String)) : null;
|
|
146
|
+
const scope = all
|
|
147
|
+
.filter((g) => !g.template && (wanted ? wanted.has(String(g.vmid)) || wanted.has(g.name) : g.status === "running"))
|
|
148
|
+
.sort((a, b) => batchOf(a) - batchOf(b) || a.vmid - b.vmid);
|
|
149
|
+
if (scope.length === 0)
|
|
150
|
+
throw new Error("No guests in scope to patch.");
|
|
151
|
+
const steps = [];
|
|
152
|
+
const open = windowOpen(window);
|
|
153
|
+
for (const g of scope) {
|
|
154
|
+
if (!open) {
|
|
155
|
+
steps.push({
|
|
156
|
+
vmid: g.vmid, name: g.name, batch: batchOf(g), snapshot: "—", updates: 0,
|
|
157
|
+
health: { id: "post-patch-health", label: "Post-patch health check", outcome: "skip", detail: "outside maintenance window" },
|
|
158
|
+
rolledBack: false, outcome: "skip", detail: `deferred — outside window "${window}"`,
|
|
159
|
+
});
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
steps.push(ctx.client.isDemo ? simulateStep(g, stamp) : await patchStepLive(ctx, g, stamp));
|
|
163
|
+
if (!ctx.client.isDemo)
|
|
164
|
+
await sleep(200);
|
|
165
|
+
}
|
|
166
|
+
const patched = steps.filter((s) => s.outcome === "pass").length;
|
|
167
|
+
const rolledBack = steps.filter((s) => s.rolledBack).length;
|
|
168
|
+
const outcome = worst(steps.map((s) => s.outcome));
|
|
169
|
+
return {
|
|
170
|
+
capability: "patch-orchestrate",
|
|
171
|
+
id: shortId("PATCH"),
|
|
172
|
+
title: "Patch orchestration with automatic rollback",
|
|
173
|
+
startedAt,
|
|
174
|
+
finishedAt: nowIso(),
|
|
175
|
+
durationSec: ctx.client.isDemo ? scope.length * 95 : Math.round((Date.now() - t0) / 1000),
|
|
176
|
+
outcome,
|
|
177
|
+
summary: open
|
|
178
|
+
? `${patched}/${steps.length} guests patched cleanly; ${rolledBack} rolled back automatically after a failed health check.`
|
|
179
|
+
: `Deferred: outside the maintenance window "${window}".`,
|
|
180
|
+
demo: ctx.client.isDemo,
|
|
181
|
+
controls: CONTROLS["patch-orchestrate"],
|
|
182
|
+
window,
|
|
183
|
+
steps,
|
|
184
|
+
rolledBack,
|
|
185
|
+
patched,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=patch-orchestrator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patch-orchestrator.js","sourceRoot":"","sources":["../../src/resilience/patch-orchestrator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resilience evidence reports — control mapping, rendering & persistence.
|
|
3
|
+
*
|
|
4
|
+
* Turns a finished run into audit-grade artefacts: a signed JSON record
|
|
5
|
+
* (machine-readable), a Markdown report (diff-able, lives in git) and a
|
|
6
|
+
* branded HTML report that prints straight to PDF for an auditor. Each report
|
|
7
|
+
* maps its result onto concrete ISO 27001 / NIS2 / DORA controls.
|
|
8
|
+
*
|
|
9
|
+
* Part of Proxmox MCP Server.
|
|
10
|
+
* Crafted by SoyRage Agency — https://soyrage.es/
|
|
11
|
+
* MIT licensed (see LICENSE).
|
|
12
|
+
*/
|
|
13
|
+
import type { Capability, ControlRef, ResilienceReport, RunSummary } from "./types.js";
|
|
14
|
+
/** The compliance controls each capability produces evidence for. */
|
|
15
|
+
export declare const CONTROLS: Record<Capability, ControlRef[]>;
|
|
16
|
+
/** Format seconds as "4m 12s" / "1h 03m" / "45s". */
|
|
17
|
+
export declare function dur(sec: number): string;
|
|
18
|
+
/** A one-line verdict banner for the console/MCP text result. */
|
|
19
|
+
export declare function verdict(r: ResilienceReport): string;
|
|
20
|
+
/** Reduce a report to a compact dashboard summary. */
|
|
21
|
+
export declare function summarize(r: ResilienceReport): RunSummary;
|
|
22
|
+
/** Render a report as a Markdown evidence document. */
|
|
23
|
+
export declare function renderMarkdown(r: ResilienceReport): string;
|
|
24
|
+
/** Render a branded, printable HTML evidence report. */
|
|
25
|
+
export declare function renderHtml(r: ResilienceReport): string;
|
|
26
|
+
/** Paths written for one report. */
|
|
27
|
+
export interface ReportPaths {
|
|
28
|
+
json: string;
|
|
29
|
+
markdown: string;
|
|
30
|
+
html: string;
|
|
31
|
+
}
|
|
32
|
+
/** Persist a report as JSON + Markdown + HTML under `outDir`. Returns paths. */
|
|
33
|
+
export declare function writeReports(r: ResilienceReport, outDir: string): ReportPaths;
|