@homeflare/alchemy 0.37.1 → 0.37.3
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/proxmox/backup-job-config.d.ts +95 -0
- package/dist/proxmox/backup-job-config.d.ts.map +1 -0
- package/dist/proxmox/backup-job-distilled.d.ts +46 -0
- package/dist/proxmox/backup-job-distilled.d.ts.map +1 -0
- package/dist/proxmox/backup-job.d.ts +9 -26
- package/dist/proxmox/backup-job.d.ts.map +1 -1
- package/dist/proxmox/ceph-fs-distilled.d.ts +17 -10
- package/dist/proxmox/ceph-fs-distilled.d.ts.map +1 -1
- package/dist/proxmox/ceph-fs-wire.d.ts +5 -22
- package/dist/proxmox/ceph-fs-wire.d.ts.map +1 -1
- package/dist/proxmox/ceph-fs.d.ts +3 -3
- package/dist/proxmox/distilled-pbs.d.ts +12 -0
- package/dist/proxmox/distilled-pbs.d.ts.map +1 -0
- package/dist/proxmox/fake-pbs-jobs.d.ts +23 -0
- package/dist/proxmox/fake-pbs-jobs.d.ts.map +1 -0
- package/dist/proxmox/fake-pbs-notify.d.ts.map +1 -1
- package/dist/proxmox/fake-pve.d.ts +1 -1
- package/dist/proxmox/fake-pve.d.ts.map +1 -1
- package/dist/proxmox/group-wire.d.ts +2 -2
- package/dist/proxmox/group-wire.d.ts.map +1 -1
- package/dist/proxmox/index.js +1382 -506
- package/dist/proxmox/index.js.map +51 -27
- package/dist/proxmox/live-core-fixtures.d.ts +40 -0
- package/dist/proxmox/live-core-fixtures.d.ts.map +1 -0
- package/dist/proxmox/metric-server-config.d.ts +49 -0
- package/dist/proxmox/metric-server-config.d.ts.map +1 -0
- package/dist/proxmox/metric-server-distilled.d.ts +43 -0
- package/dist/proxmox/metric-server-distilled.d.ts.map +1 -0
- package/dist/proxmox/metric-server.d.ts +2 -10
- package/dist/proxmox/metric-server.d.ts.map +1 -1
- package/dist/proxmox/node-network-wire.d.ts +1 -1
- package/dist/proxmox/node-network-wire.d.ts.map +1 -1
- package/dist/proxmox/notification-matcher-distilled.d.ts +9 -0
- package/dist/proxmox/notification-matcher-distilled.d.ts.map +1 -0
- package/dist/proxmox/notification-matcher-lifecycle.d.ts +27 -0
- package/dist/proxmox/notification-matcher-lifecycle.d.ts.map +1 -0
- package/dist/proxmox/notification-matcher.d.ts +5 -5
- package/dist/proxmox/notification-matcher.d.ts.map +1 -1
- package/dist/proxmox/notification-sdk-input.d.ts +22 -0
- package/dist/proxmox/notification-sdk-input.d.ts.map +1 -0
- package/dist/proxmox/notification-target-distilled.d.ts +12 -0
- package/dist/proxmox/notification-target-distilled.d.ts.map +1 -0
- package/dist/proxmox/notification-target-form.d.ts +2 -0
- package/dist/proxmox/notification-target-form.d.ts.map +1 -1
- package/dist/proxmox/notification-target-lifecycle.d.ts +29 -0
- package/dist/proxmox/notification-target-lifecycle.d.ts.map +1 -0
- package/dist/proxmox/notification-target-spec.d.ts +5 -0
- package/dist/proxmox/notification-target-spec.d.ts.map +1 -0
- package/dist/proxmox/notification-target.d.ts +4 -5
- package/dist/proxmox/notification-target.d.ts.map +1 -1
- package/dist/proxmox/pbs-datastore-endpoint.d.ts +1 -1
- package/dist/proxmox/pbs-datastore-form.d.ts +18 -42
- package/dist/proxmox/pbs-datastore-form.d.ts.map +1 -1
- package/dist/proxmox/pbs-datastore-guard.d.ts +7 -4
- package/dist/proxmox/pbs-datastore-guard.d.ts.map +1 -1
- package/dist/proxmox/pbs-datastore-lifecycle.d.ts +56 -0
- package/dist/proxmox/pbs-datastore-lifecycle.d.ts.map +1 -0
- package/dist/proxmox/pbs-datastore-wire.d.ts +21 -0
- package/dist/proxmox/pbs-datastore-wire.d.ts.map +1 -0
- package/dist/proxmox/pbs-datastore.d.ts +5 -6
- package/dist/proxmox/pbs-datastore.d.ts.map +1 -1
- package/dist/proxmox/pbs-notification-input.d.ts +18 -0
- package/dist/proxmox/pbs-notification-input.d.ts.map +1 -0
- package/dist/proxmox/pbs-notification-matcher-distilled.d.ts +11 -0
- package/dist/proxmox/pbs-notification-matcher-distilled.d.ts.map +1 -0
- package/dist/proxmox/pbs-notification-matcher.d.ts +4 -3
- package/dist/proxmox/pbs-notification-matcher.d.ts.map +1 -1
- package/dist/proxmox/pbs-notification-target-distilled.d.ts +20 -0
- package/dist/proxmox/pbs-notification-target-distilled.d.ts.map +1 -0
- package/dist/proxmox/pbs-notification-target-lifecycle.d.ts +4 -4
- package/dist/proxmox/pbs-notification-target-lifecycle.d.ts.map +1 -1
- package/dist/proxmox/pbs-notification-target.d.ts +1 -1
- package/dist/proxmox/pbs-prune-job-lifecycle.d.ts +22 -0
- package/dist/proxmox/pbs-prune-job-lifecycle.d.ts.map +1 -0
- package/dist/proxmox/pbs-prune-job-spec.d.ts +5 -0
- package/dist/proxmox/pbs-prune-job-spec.d.ts.map +1 -0
- package/dist/proxmox/pbs-prune-job-wire.d.ts +11 -0
- package/dist/proxmox/pbs-prune-job-wire.d.ts.map +1 -0
- package/dist/proxmox/pbs-prune-job.d.ts +9 -8
- package/dist/proxmox/pbs-prune-job.d.ts.map +1 -1
- package/dist/proxmox/pbs-sync-job-attributes.d.ts +34 -0
- package/dist/proxmox/pbs-sync-job-attributes.d.ts.map +1 -0
- package/dist/proxmox/pbs-sync-job-form.d.ts +21 -22
- package/dist/proxmox/pbs-sync-job-form.d.ts.map +1 -1
- package/dist/proxmox/pbs-sync-job-lifecycle.d.ts +22 -0
- package/dist/proxmox/pbs-sync-job-lifecycle.d.ts.map +1 -0
- package/dist/proxmox/pbs-sync-job-wire.d.ts +11 -0
- package/dist/proxmox/pbs-sync-job-wire.d.ts.map +1 -0
- package/dist/proxmox/pbs-sync-job.d.ts +9 -39
- package/dist/proxmox/pbs-sync-job.d.ts.map +1 -1
- package/dist/proxmox/pbs-verify-job-lifecycle.d.ts +22 -0
- package/dist/proxmox/pbs-verify-job-lifecycle.d.ts.map +1 -0
- package/dist/proxmox/pbs-verify-job-spec.d.ts +16 -0
- package/dist/proxmox/pbs-verify-job-spec.d.ts.map +1 -0
- package/dist/proxmox/pbs-verify-job-wire.d.ts +11 -0
- package/dist/proxmox/pbs-verify-job-wire.d.ts.map +1 -0
- package/dist/proxmox/pbs-verify-job.d.ts +12 -12
- package/dist/proxmox/pbs-verify-job.d.ts.map +1 -1
- package/dist/proxmox/pool.d.ts +10 -1
- package/dist/proxmox/pool.d.ts.map +1 -1
- package/dist/proxmox/storage-wire.d.ts +2 -2
- package/dist/proxmox/storage-wire.d.ts.map +1 -1
- package/dist/proxmox/user-wire.d.ts +2 -2
- package/dist/proxmox/user-wire.d.ts.map +1 -1
- package/dist/proxmox/zfs-pool-wire.d.ts +1 -1
- package/dist/proxmox/zfs-pool-wire.d.ts.map +1 -1
- package/docs/changelog/README.md +22 -0
- package/docs/changelog/archive-01.md +176 -0
- package/docs/changelog/archive-02.md +180 -0
- package/docs/changelog/archive-03.md +179 -0
- package/docs/changelog/archive-04.md +178 -0
- package/docs/changelog/archive-05.md +177 -0
- package/docs/changelog/archive-06.md +180 -0
- package/docs/changelog/archive-07.md +180 -0
- package/docs/changelog/archive-08.md +178 -0
- package/docs/changelog/archive-09.md +181 -0
- package/docs/changelog/archive-10.md +182 -0
- package/docs/changelog/archive-11.md +182 -0
- package/docs/changelog/archive-12.md +183 -0
- package/docs/changelog/archive-13.md +180 -0
- package/docs/changelog/archive-14.md +178 -0
- package/docs/changelog/archive-15.md +181 -0
- package/docs/changelog/archive-16.md +182 -0
- package/docs/changelog/archive-17.md +183 -0
- package/docs/changelog/archive-18.md +55 -0
- package/docs/pbs-distilled-jobs.md +31 -0
- package/docs/pbs-notifications.md +8 -2
- package/docs/proxmox-notification-sdk.md +28 -0
- package/docs/proxmox-sdk-migration.md +60 -0
- package/docs/proxmox.md +9 -3
- package/docs/upstream-conformance-families.md +6 -2
- package/package.json +3 -3
- package/src/proxmox/adopt-noop.test.ts +4 -5
- package/src/proxmox/backup-job-config.ts +95 -0
- package/src/proxmox/backup-job-distilled.ts +86 -0
- package/src/proxmox/backup-job.ts +78 -116
- package/src/proxmox/ceph-fs-delete.test.ts +5 -8
- package/src/proxmox/ceph-fs-destroy-sdk.test.ts +98 -0
- package/src/proxmox/ceph-fs-distilled.ts +41 -13
- package/src/proxmox/ceph-fs-wire.ts +5 -93
- package/src/proxmox/ceph-fs.ts +7 -7
- package/src/proxmox/constraints.test.ts +9 -8
- package/src/proxmox/distilled-pbs.test.ts +87 -0
- package/src/proxmox/distilled-pbs.ts +50 -0
- package/src/proxmox/fake-pbs-jobs.ts +99 -0
- package/src/proxmox/fake-pbs-notify.ts +4 -1
- package/src/proxmox/fake-pve.ts +3 -2
- package/src/proxmox/live-core-fixtures.ts +126 -0
- package/src/proxmox/live-core-lifecycle.test.ts +115 -0
- package/src/proxmox/live-core-wire.test.ts +190 -0
- package/src/proxmox/metric-server-config.ts +87 -0
- package/src/proxmox/metric-server-distilled.ts +81 -0
- package/src/proxmox/metric-server.ts +77 -88
- package/src/proxmox/notification-distilled.test.ts +162 -0
- package/src/proxmox/notification-matcher-distilled.ts +60 -0
- package/src/proxmox/notification-matcher-lifecycle.ts +71 -0
- package/src/proxmox/notification-matcher.ts +9 -26
- package/src/proxmox/notification-replacement.test.ts +190 -0
- package/src/proxmox/notification-sdk-input.ts +39 -0
- package/src/proxmox/notification-target-distilled.ts +160 -0
- package/src/proxmox/notification-target-form.ts +2 -0
- package/src/proxmox/notification-target-lifecycle.ts +75 -0
- package/src/proxmox/notification-target-spec.ts +68 -0
- package/src/proxmox/notification-target.ts +6 -68
- package/src/proxmox/pbs-datastore-endpoint.ts +1 -1
- package/src/proxmox/pbs-datastore-form.ts +18 -49
- package/src/proxmox/pbs-datastore-guard.ts +7 -4
- package/src/proxmox/pbs-datastore-lifecycle.ts +114 -0
- package/src/proxmox/pbs-datastore-wire.ts +94 -0
- package/src/proxmox/pbs-datastore.ts +7 -133
- package/src/proxmox/pbs-jobs-delete.test.ts +40 -0
- package/src/proxmox/pbs-jobs-sdk.test.ts +215 -0
- package/src/proxmox/pbs-notification-errors.test.ts +93 -0
- package/src/proxmox/pbs-notification-input.ts +25 -0
- package/src/proxmox/pbs-notification-mail.test.ts +64 -0
- package/src/proxmox/pbs-notification-matcher-distilled.ts +65 -0
- package/src/proxmox/pbs-notification-matcher.ts +70 -17
- package/src/proxmox/pbs-notification-target-distilled.ts +124 -0
- package/src/proxmox/pbs-notification-target-lifecycle.ts +26 -17
- package/src/proxmox/pbs-notification-target.ts +1 -1
- package/src/proxmox/pbs-notifications-stack.test.ts +25 -0
- package/src/proxmox/pbs-prune-job-lifecycle.ts +52 -0
- package/src/proxmox/pbs-prune-job-spec.ts +53 -0
- package/src/proxmox/pbs-prune-job-wire.ts +68 -0
- package/src/proxmox/pbs-prune-job.ts +10 -58
- package/src/proxmox/pbs-sync-job-attributes.ts +33 -0
- package/src/proxmox/pbs-sync-job-form.ts +7 -6
- package/src/proxmox/pbs-sync-job-lifecycle.ts +46 -0
- package/src/proxmox/pbs-sync-job-wire.ts +71 -0
- package/src/proxmox/pbs-sync-job.ts +10 -41
- package/src/proxmox/pbs-verify-job-lifecycle.ts +52 -0
- package/src/proxmox/pbs-verify-job-spec.ts +76 -0
- package/src/proxmox/pbs-verify-job-wire.ts +58 -0
- package/src/proxmox/pbs-verify-job.ts +13 -85
- package/src/proxmox/pool.ts +92 -10
package/dist/proxmox/index.js
CHANGED
|
@@ -1440,89 +1440,23 @@ var ProxmoxApiTokenProvider = () => Provider2.effect(ProxmoxApiToken, Effect8.su
|
|
|
1440
1440
|
})));
|
|
1441
1441
|
// src/proxmox/backup-job.ts
|
|
1442
1442
|
import { Resource as Resource3 } from "alchemy";
|
|
1443
|
-
import * as Provider3 from "alchemy/Provider";
|
|
1444
|
-
import * as Effect12 from "effect/Effect";
|
|
1445
|
-
|
|
1446
|
-
// src/proxmox/resource.ts
|
|
1447
1443
|
import { isResolved as isResolved3 } from "alchemy/Diff";
|
|
1448
|
-
import * as
|
|
1449
|
-
|
|
1450
|
-
// src/proxmox/client.ts
|
|
1451
|
-
import * as Effect9 from "effect/Effect";
|
|
1452
|
-
import * as HttpClient5 from "effect/unstable/http/HttpClient";
|
|
1453
|
-
import * as HttpClientRequest2 from "effect/unstable/http/HttpClientRequest";
|
|
1454
|
-
class PveError extends Error {
|
|
1455
|
-
status;
|
|
1456
|
-
method;
|
|
1457
|
-
path;
|
|
1458
|
-
constructor(status, method, path, detail) {
|
|
1459
|
-
super(`PVE ${method} ${path} -> ${String(status)}: ${detail}`);
|
|
1460
|
-
this.status = status;
|
|
1461
|
-
this.method = method;
|
|
1462
|
-
this.path = path;
|
|
1463
|
-
this.name = "PveError";
|
|
1464
|
-
}
|
|
1465
|
-
}
|
|
1466
|
-
var clusterExhausted = (method, path, attempts) => new PveError(0, method, path, `all members failed: ${attempts.map((a) => `${a.member} (${a.why})`).join("; ")}`);
|
|
1467
|
-
var encode = (form) => {
|
|
1468
|
-
const params = new URLSearchParams;
|
|
1469
|
-
for (const [key, value] of Object.entries(form)) {
|
|
1470
|
-
if (typeof value === "string")
|
|
1471
|
-
params.append(key, value);
|
|
1472
|
-
else
|
|
1473
|
-
for (const item of value)
|
|
1474
|
-
params.append(key, item);
|
|
1475
|
-
}
|
|
1476
|
-
return params.toString();
|
|
1477
|
-
};
|
|
1478
|
-
var buildRequest = (method, apiBase, path, credential, target, form) => {
|
|
1479
|
-
const url = `${apiBase}/${path}`;
|
|
1480
|
-
return HttpClientRequest2.make(method)(url).pipe(HttpClientRequest2.setHeaders({ Authorization: authorization(credential, target) }), form === undefined ? (self) => self : HttpClientRequest2.bodyText(encode(form), "application/x-www-form-urlencoded"));
|
|
1481
|
-
};
|
|
1482
|
-
var executeOne = (target, credential, method, path, form, apiBase) => Effect9.gen(function* () {
|
|
1483
|
-
const client = yield* HttpClient5.HttpClient;
|
|
1484
|
-
const request = buildRequest(method, apiBase, path, credential, target, form);
|
|
1485
|
-
return yield* client.execute(request).pipe(Effect9.mapError((cause) => new PveError(0, method, path, String(cause))));
|
|
1486
|
-
});
|
|
1487
|
-
var readResponse = (response, method, path) => Effect9.gen(function* () {
|
|
1488
|
-
if (response.status < 200 || response.status >= 300) {
|
|
1489
|
-
const detail = yield* response.text.pipe(Effect9.orElseSucceed(() => ""));
|
|
1490
|
-
return yield* Effect9.fail(new PveError(response.status, method, path, detail.slice(0, 300)));
|
|
1491
|
-
}
|
|
1492
|
-
const body = yield* response.json.pipe(Effect9.mapError((cause) => new PveError(0, method, path, String(cause))));
|
|
1493
|
-
return body.data;
|
|
1494
|
-
});
|
|
1495
|
-
var pveWith = (target, credential, method, path, form) => Effect9.gen(function* () {
|
|
1496
|
-
const response = target.scheme === "pve" ? yield* executeOnCluster(target, method, path, (apiBase) => buildRequest(method, apiBase, path, credential, target, form)).pipe(Effect9.mapError((failure) => failure.tag === "cluster" ? clusterExhausted(failure.method, failure.path, failure.attempts) : new PveError(0, method, path, String(failure)))) : yield* executeOne(target, credential, method, path, form, target.api);
|
|
1497
|
-
return yield* readResponse(response, method, path);
|
|
1498
|
-
});
|
|
1499
|
-
var pveEnvelopeWith = (target, credential, method, path) => Effect9.gen(function* () {
|
|
1500
|
-
const response = yield* executeOnCluster(target, method, path, (apiBase) => buildRequest(method, apiBase, path, credential, target)).pipe(Effect9.mapError((failure) => failure.tag === "cluster" ? clusterExhausted(failure.method, failure.path, failure.attempts) : new PveError(0, method, path, String(failure))));
|
|
1501
|
-
if (response.status < 200 || response.status >= 300) {
|
|
1502
|
-
const detail = yield* response.text.pipe(Effect9.orElseSucceed(() => ""));
|
|
1503
|
-
return yield* Effect9.fail(new PveError(response.status, method, path, detail.slice(0, 300)));
|
|
1504
|
-
}
|
|
1505
|
-
return yield* response.json.pipe(Effect9.mapError((cause) => new PveError(0, method, path, String(cause))));
|
|
1506
|
-
});
|
|
1507
|
-
var pve = (target, role, method, path, form) => Effect9.gen(function* () {
|
|
1508
|
-
const credential = yield* leased(target, role);
|
|
1509
|
-
return yield* pveWith(target, credential, method, path, form);
|
|
1510
|
-
});
|
|
1444
|
+
import * as cluster2 from "@distilled.cloud/proxmox/cluster";
|
|
1511
1445
|
|
|
1512
1446
|
// src/proxmox/resource-guard.ts
|
|
1513
|
-
import * as
|
|
1447
|
+
import * as Effect9 from "effect/Effect";
|
|
1514
1448
|
var specGuards = (spec) => {
|
|
1515
1449
|
const endpointsOf = (props) => typeof spec.endpoint === "function" ? spec.endpoint(props) : spec.endpoint;
|
|
1516
|
-
const guardCreate = (props, presence) =>
|
|
1450
|
+
const guardCreate = (props, presence) => Effect9.suspend(() => {
|
|
1517
1451
|
const create = endpointsOf(props)?.create;
|
|
1518
|
-
return create === undefined ?
|
|
1452
|
+
return create === undefined ? Effect9.void : guardForm(create, spec.createForm(props), presence);
|
|
1519
1453
|
});
|
|
1520
|
-
const guardUpdate = (props) =>
|
|
1454
|
+
const guardUpdate = (props) => Effect9.suspend(() => {
|
|
1521
1455
|
const update = endpointsOf(props)?.update;
|
|
1522
1456
|
if (update === undefined)
|
|
1523
|
-
return
|
|
1457
|
+
return Effect9.void;
|
|
1524
1458
|
const form = spec.updateForm?.(props);
|
|
1525
|
-
return form === undefined ?
|
|
1459
|
+
return form === undefined ? Effect9.void : guardForm(update, form, false);
|
|
1526
1460
|
});
|
|
1527
1461
|
return { guardCreate, guardUpdate };
|
|
1528
1462
|
};
|
|
@@ -1530,66 +1464,7 @@ var specGuards = (spec) => {
|
|
|
1530
1464
|
// src/proxmox/update-guard.ts
|
|
1531
1465
|
var formToSend = (matches3, live, props, form) => matches3(live, props) || Object.keys(form).length === 0 ? undefined : form;
|
|
1532
1466
|
|
|
1533
|
-
// src/proxmox/
|
|
1534
|
-
var pveOperations = (spec) => {
|
|
1535
|
-
const { guardCreate, guardUpdate } = specGuards(spec);
|
|
1536
|
-
const read = (props) => pve(props.target, spec.readRole ?? "read", "GET", spec.path(props)).pipe(Effect11.map((data) => data == null ? undefined : spec.attributes(data, props)), Effect11.orElseSucceed(() => {
|
|
1537
|
-
return;
|
|
1538
|
-
}));
|
|
1539
|
-
return {
|
|
1540
|
-
guardCreate,
|
|
1541
|
-
guardUpdate,
|
|
1542
|
-
read,
|
|
1543
|
-
diff: (news, output) => Effect11.gen(function* () {
|
|
1544
|
-
if (!isResolved3(news))
|
|
1545
|
-
return;
|
|
1546
|
-
yield* guardCreate(news, output === undefined);
|
|
1547
|
-
yield* guardUpdate(news);
|
|
1548
|
-
if (output === undefined)
|
|
1549
|
-
return;
|
|
1550
|
-
const live = yield* read(news);
|
|
1551
|
-
if (live === undefined) {
|
|
1552
|
-
yield* guardCreate(news, true);
|
|
1553
|
-
return { action: "update" };
|
|
1554
|
-
}
|
|
1555
|
-
if (spec.matches(live, news))
|
|
1556
|
-
return { action: "noop" };
|
|
1557
|
-
return spec.updateForm === undefined ? { action: "replace" } : { action: "update" };
|
|
1558
|
-
}),
|
|
1559
|
-
reconcile: (news) => Effect11.gen(function* () {
|
|
1560
|
-
const live = yield* read(news);
|
|
1561
|
-
yield* guardCreate(news, live === undefined);
|
|
1562
|
-
yield* guardUpdate(news);
|
|
1563
|
-
if (live === undefined) {
|
|
1564
|
-
yield* pve(news.target, "provision", "POST", spec.collection(news), spec.createForm(news));
|
|
1565
|
-
} else if (spec.updateForm !== undefined) {
|
|
1566
|
-
const form = formToSend(spec.matches, live, news, spec.updateForm(news));
|
|
1567
|
-
if (form !== undefined) {
|
|
1568
|
-
yield* pve(news.target, "provision", "PUT", spec.path(news), form);
|
|
1569
|
-
}
|
|
1570
|
-
}
|
|
1571
|
-
const after = yield* read(news);
|
|
1572
|
-
if (after === undefined) {
|
|
1573
|
-
return yield* Effect11.die(new Error(`${spec.path(news)}: the write returned no error but the object is still absent. ` + 'PVE wraps every answer in {"data":...} and can report success on a call that did ' + "nothing -- read back rather than trusting the status code."));
|
|
1574
|
-
}
|
|
1575
|
-
return after;
|
|
1576
|
-
}),
|
|
1577
|
-
destroy: (olds) => pve(olds.target, "provision", "DELETE", spec.path(olds))
|
|
1578
|
-
};
|
|
1579
|
-
};
|
|
1580
|
-
var pveHandlers = (spec) => {
|
|
1581
|
-
const ops = pveOperations(spec);
|
|
1582
|
-
return {
|
|
1583
|
-
list: () => Effect11.succeed([]),
|
|
1584
|
-
read: ({ olds }) => ops.read(olds),
|
|
1585
|
-
diff: ({ news, output }) => ops.diff(news, output),
|
|
1586
|
-
reconcile: ({ news }) => ops.reconcile(news),
|
|
1587
|
-
delete: ({ olds }) => ops.destroy(olds)
|
|
1588
|
-
};
|
|
1589
|
-
};
|
|
1590
|
-
|
|
1591
|
-
// src/proxmox/backup-job.ts
|
|
1592
|
-
var ProxmoxBackupJob = Resource3("Proxmox.BackupJob");
|
|
1467
|
+
// src/proxmox/backup-job-config.ts
|
|
1593
1468
|
var shape2 = (props) => ({
|
|
1594
1469
|
all: props.all === true ? "1" : "0",
|
|
1595
1470
|
enabled: props.enabled === false ? "0" : "1",
|
|
@@ -1606,7 +1481,7 @@ var shape2 = (props) => ({
|
|
|
1606
1481
|
...props.storage === undefined ? {} : { storage: props.storage },
|
|
1607
1482
|
...props.vmid === undefined ? {} : { vmid: guestList(props.vmid) }
|
|
1608
1483
|
});
|
|
1609
|
-
var
|
|
1484
|
+
var backupJobSpec = {
|
|
1610
1485
|
attributes: (live, props) => ({
|
|
1611
1486
|
all: bool(live["all"], false),
|
|
1612
1487
|
comment: text(live["comment"], ""),
|
|
@@ -1631,13 +1506,95 @@ var handlers = pveHandlers({
|
|
|
1631
1506
|
matches: (attributes, props) => attributes.schedule === props.schedule && attributes.enabled === (props.enabled !== false) && attributes.all === (props.all === true) && attributes.mode === (props.mode ?? "snapshot") && attributes["notification-mode"] === (props["notification-mode"] ?? "auto") && attributes["repeat-missed"] === (props["repeat-missed"] === true) && (props.storage === undefined || attributes.storage === props.storage) && (props.pool === undefined || attributes.pool === props.pool) && (props.comment === undefined || attributes.comment === props.comment) && (props.vmid === undefined || attributes.vmid === guestList(props.vmid)) && (props.exclude === undefined || attributes.exclude === guestList(props.exclude)) && (props["notes-template"] === undefined || attributes["notes-template"] === props["notes-template"]) && (props["prune-backups"] === undefined || attributes["prune-backups"] === propertyString(props["prune-backups"])) && (props.fleecing === undefined || attributes.fleecing === propertyString(props.fleecing, "enabled")),
|
|
1632
1507
|
path: (props) => `cluster/backup/${props.id}`,
|
|
1633
1508
|
updateForm: shape2
|
|
1634
|
-
}
|
|
1635
|
-
|
|
1509
|
+
};
|
|
1510
|
+
|
|
1511
|
+
// src/proxmox/backup-job-distilled.ts
|
|
1512
|
+
import * as cluster from "@distilled.cloud/proxmox/cluster";
|
|
1513
|
+
import * as Effect10 from "effect/Effect";
|
|
1514
|
+
import * as Schema from "effect/Schema";
|
|
1515
|
+
import { ProxmoxParseError } from "@distilled.cloud/proxmox/Errors";
|
|
1516
|
+
var backupJobRequest = (props, form = backupJobSpec.updateForm(props)) => {
|
|
1517
|
+
const {
|
|
1518
|
+
"notes-template": notes_template,
|
|
1519
|
+
"notification-mode": notification_mode,
|
|
1520
|
+
"prune-backups": prune_backups,
|
|
1521
|
+
"repeat-missed": repeat_missed,
|
|
1522
|
+
...fields
|
|
1523
|
+
} = form;
|
|
1524
|
+
return {
|
|
1525
|
+
...fields,
|
|
1526
|
+
id: props.id,
|
|
1527
|
+
notification_mode,
|
|
1528
|
+
repeat_missed,
|
|
1529
|
+
...notes_template === undefined ? {} : { notes_template },
|
|
1530
|
+
...prune_backups === undefined ? {} : { prune_backups }
|
|
1531
|
+
};
|
|
1532
|
+
};
|
|
1533
|
+
var readBackupJob = (props) => runPve(props.target, "read", false, cluster.getClusterBackup({ id: props.id })).pipe(Effect10.flatMap((live) => Schema.decodeUnknownEffect(Schema.toType(cluster.GetClusterBackupResponse))({
|
|
1534
|
+
...live,
|
|
1535
|
+
prune_backups: undefined,
|
|
1536
|
+
fleecing: undefined
|
|
1537
|
+
}).pipe(Effect10.mapError(() => new ProxmoxParseError({
|
|
1538
|
+
body: undefined,
|
|
1539
|
+
cause: "Backup job response does not match its vendor schema"
|
|
1540
|
+
})), Effect10.as(live))), Effect10.map((live) => backupJobSpec.attributes({
|
|
1541
|
+
...live,
|
|
1542
|
+
"next-run": live.next_run,
|
|
1543
|
+
"notes-template": live.notes_template,
|
|
1544
|
+
"notification-mode": live.notification_mode,
|
|
1545
|
+
"prune-backups": typeof live.prune_backups === "object" && live.prune_backups !== null ? Object.fromEntries(Object.entries(live.prune_backups).map(([key, value]) => [
|
|
1546
|
+
key.replaceAll("_", "-"),
|
|
1547
|
+
value
|
|
1548
|
+
])) : live.prune_backups,
|
|
1549
|
+
"repeat-missed": live.repeat_missed
|
|
1550
|
+
}, props)), Effect10.catchTag("BackupJobNotFound", () => Effect10.succeed(undefined)), Effect10.catchTag("NotFound", () => Effect10.succeed(undefined)));
|
|
1551
|
+
var deleteBackupJob = (props) => runPve(props.target, "provision", true, cluster.deleteClusterBackup({ id: props.id })).pipe(Effect10.catchTag("BackupJobNotFound", () => Effect10.void), Effect10.catchTag("NotFound", () => Effect10.void));
|
|
1552
|
+
|
|
1553
|
+
// src/proxmox/backup-job.ts
|
|
1554
|
+
import * as Provider3 from "alchemy/Provider";
|
|
1555
|
+
import * as Effect11 from "effect/Effect";
|
|
1556
|
+
var ProxmoxBackupJob = Resource3("Proxmox.BackupJob");
|
|
1557
|
+
var { guardCreate, guardUpdate } = specGuards(backupJobSpec);
|
|
1558
|
+
var ProxmoxBackupJobProvider = () => Provider3.effect(ProxmoxBackupJob, Effect11.succeed(ProxmoxBackupJob.Provider.of({
|
|
1559
|
+
list: () => Effect11.succeed([]),
|
|
1560
|
+
read: ({ olds }) => readBackupJob(olds),
|
|
1561
|
+
diff: Effect11.fn(function* ({ news, output }) {
|
|
1562
|
+
if (!isResolved3(news))
|
|
1563
|
+
return;
|
|
1564
|
+
yield* guardCreate(news, output === undefined);
|
|
1565
|
+
yield* guardUpdate(news);
|
|
1566
|
+
if (output === undefined)
|
|
1567
|
+
return;
|
|
1568
|
+
const live = yield* readBackupJob(news);
|
|
1569
|
+
if (live === undefined) {
|
|
1570
|
+
yield* guardCreate(news, true);
|
|
1571
|
+
return { action: "update" };
|
|
1572
|
+
}
|
|
1573
|
+
return { action: backupJobSpec.matches(live, news) ? "noop" : "update" };
|
|
1574
|
+
}),
|
|
1575
|
+
reconcile: Effect11.fn(function* ({ news }) {
|
|
1576
|
+
const live = yield* readBackupJob(news);
|
|
1577
|
+
yield* guardCreate(news, live === undefined);
|
|
1578
|
+
yield* guardUpdate(news);
|
|
1579
|
+
if (live === undefined) {
|
|
1580
|
+
yield* runPve(news.target, "provision", true, cluster2.createClusterBackup(backupJobRequest(news)));
|
|
1581
|
+
} else {
|
|
1582
|
+
const form = formToSend(backupJobSpec.matches, live, news, backupJobSpec.updateForm(news));
|
|
1583
|
+
if (form !== undefined)
|
|
1584
|
+
yield* runPve(news.target, "provision", true, cluster2.putClusterBackup(backupJobRequest(news, form)));
|
|
1585
|
+
}
|
|
1586
|
+
const after = yield* readBackupJob(news);
|
|
1587
|
+
if (after === undefined)
|
|
1588
|
+
return yield* Effect11.fail(new Error(`${backupJobSpec.path(news)}: write returned success but the resource is still absent`));
|
|
1589
|
+
return after;
|
|
1590
|
+
}),
|
|
1591
|
+
delete: ({ olds }) => deleteBackupJob(olds)
|
|
1592
|
+
})));
|
|
1636
1593
|
// src/proxmox/ceph-daemon.ts
|
|
1637
1594
|
import { Resource as Resource4 } from "alchemy";
|
|
1638
1595
|
import { isResolved as isResolved4 } from "alchemy/Diff";
|
|
1639
1596
|
import * as Provider4 from "alchemy/Provider";
|
|
1640
|
-
import * as
|
|
1597
|
+
import * as Effect13 from "effect/Effect";
|
|
1641
1598
|
|
|
1642
1599
|
// src/proxmox/ceph-daemon-form.ts
|
|
1643
1600
|
var DAEMON_ENDPOINTS = {
|
|
@@ -1678,19 +1635,19 @@ var daemonAttributes = (live, props) => {
|
|
|
1678
1635
|
|
|
1679
1636
|
// src/proxmox/ceph-daemon-wire.ts
|
|
1680
1637
|
import * as nodes from "@distilled.cloud/proxmox/nodes";
|
|
1681
|
-
import * as
|
|
1638
|
+
import * as Effect12 from "effect/Effect";
|
|
1682
1639
|
var listRows = (props) => {
|
|
1683
1640
|
const node = props.node;
|
|
1684
1641
|
switch (props.kind) {
|
|
1685
1642
|
case "mon":
|
|
1686
|
-
return runPve(props.target, "read", false, nodes.listNodeCephMon({ node })).pipe(
|
|
1643
|
+
return runPve(props.target, "read", false, nodes.listNodeCephMon({ node })).pipe(Effect12.map((rows) => rows));
|
|
1687
1644
|
case "mgr":
|
|
1688
|
-
return runPve(props.target, "read", false, nodes.listNodeCephMgr({ node })).pipe(
|
|
1645
|
+
return runPve(props.target, "read", false, nodes.listNodeCephMgr({ node })).pipe(Effect12.map((rows) => rows));
|
|
1689
1646
|
case "mds":
|
|
1690
|
-
return runPve(props.target, "read", false, nodes.listNodeCephMds({ node })).pipe(
|
|
1647
|
+
return runPve(props.target, "read", false, nodes.listNodeCephMds({ node })).pipe(Effect12.map((rows) => rows));
|
|
1691
1648
|
}
|
|
1692
1649
|
};
|
|
1693
|
-
var readDaemon = (props) => listRows(props).pipe(
|
|
1650
|
+
var readDaemon = (props) => listRows(props).pipe(Effect12.map((rows) => daemonAttributes(rows, props)));
|
|
1694
1651
|
var createDaemon = (props) => {
|
|
1695
1652
|
const node = props.node;
|
|
1696
1653
|
const id = daemonId(props);
|
|
@@ -1729,7 +1686,7 @@ var notCreated = (props) => new Error(`${daemonPath(props)}: no ${props.kind} na
|
|
|
1729
1686
|
var ProxmoxCephDaemon = Resource4("Proxmox.CephDaemon", {
|
|
1730
1687
|
defaultRemovalPolicy: "retain"
|
|
1731
1688
|
});
|
|
1732
|
-
var reconcileDaemon =
|
|
1689
|
+
var reconcileDaemon = Effect13.fn(function* ({ news }) {
|
|
1733
1690
|
const live = yield* readDaemon(news);
|
|
1734
1691
|
yield* guardWrite2(DAEMON_ENDPOINTS[news.kind], createForm(news), live === undefined);
|
|
1735
1692
|
if (live !== undefined)
|
|
@@ -1737,13 +1694,13 @@ var reconcileDaemon = Effect14.fn(function* ({ news }) {
|
|
|
1737
1694
|
yield* createDaemon(news);
|
|
1738
1695
|
const after = yield* readDaemon(news);
|
|
1739
1696
|
if (after === undefined)
|
|
1740
|
-
return yield*
|
|
1697
|
+
return yield* Effect13.die(notCreated(news));
|
|
1741
1698
|
return after;
|
|
1742
1699
|
});
|
|
1743
|
-
var ProxmoxCephDaemonProvider = () => Provider4.effect(ProxmoxCephDaemon,
|
|
1744
|
-
list: () =>
|
|
1700
|
+
var ProxmoxCephDaemonProvider = () => Provider4.effect(ProxmoxCephDaemon, Effect13.succeed(ProxmoxCephDaemon.Provider.of({
|
|
1701
|
+
list: () => Effect13.succeed([]),
|
|
1745
1702
|
read: ({ olds }) => readDaemon(olds),
|
|
1746
|
-
diff:
|
|
1703
|
+
diff: Effect13.fn(function* ({ news, output }) {
|
|
1747
1704
|
if (!isResolved4(news))
|
|
1748
1705
|
return;
|
|
1749
1706
|
yield* guardWrite2(DAEMON_ENDPOINTS[news.kind], createForm(news), output === undefined);
|
|
@@ -1762,9 +1719,135 @@ var ProxmoxCephDaemonProvider = () => Provider4.effect(ProxmoxCephDaemon, Effect
|
|
|
1762
1719
|
// src/proxmox/ceph-flag.ts
|
|
1763
1720
|
import { Resource as Resource5 } from "alchemy";
|
|
1764
1721
|
import * as Provider5 from "alchemy/Provider";
|
|
1722
|
+
import * as Effect16 from "effect/Effect";
|
|
1723
|
+
|
|
1724
|
+
// src/proxmox/resource.ts
|
|
1725
|
+
import { isResolved as isResolved5 } from "alchemy/Diff";
|
|
1765
1726
|
import * as Effect15 from "effect/Effect";
|
|
1727
|
+
|
|
1728
|
+
// src/proxmox/client.ts
|
|
1729
|
+
import * as Effect14 from "effect/Effect";
|
|
1730
|
+
import * as HttpClient5 from "effect/unstable/http/HttpClient";
|
|
1731
|
+
import * as HttpClientRequest2 from "effect/unstable/http/HttpClientRequest";
|
|
1732
|
+
class PveError extends Error {
|
|
1733
|
+
status;
|
|
1734
|
+
method;
|
|
1735
|
+
path;
|
|
1736
|
+
constructor(status, method, path, detail) {
|
|
1737
|
+
super(`PVE ${method} ${path} -> ${String(status)}: ${detail}`);
|
|
1738
|
+
this.status = status;
|
|
1739
|
+
this.method = method;
|
|
1740
|
+
this.path = path;
|
|
1741
|
+
this.name = "PveError";
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
var clusterExhausted = (method, path, attempts) => new PveError(0, method, path, `all members failed: ${attempts.map((a) => `${a.member} (${a.why})`).join("; ")}`);
|
|
1745
|
+
var encode = (form) => {
|
|
1746
|
+
const params = new URLSearchParams;
|
|
1747
|
+
for (const [key, value] of Object.entries(form)) {
|
|
1748
|
+
if (typeof value === "string")
|
|
1749
|
+
params.append(key, value);
|
|
1750
|
+
else
|
|
1751
|
+
for (const item of value)
|
|
1752
|
+
params.append(key, item);
|
|
1753
|
+
}
|
|
1754
|
+
return params.toString();
|
|
1755
|
+
};
|
|
1756
|
+
var buildRequest = (method, apiBase, path, credential, target, form) => {
|
|
1757
|
+
const url = `${apiBase}/${path}`;
|
|
1758
|
+
return HttpClientRequest2.make(method)(url).pipe(HttpClientRequest2.setHeaders({ Authorization: authorization(credential, target) }), form === undefined ? (self) => self : HttpClientRequest2.bodyText(encode(form), "application/x-www-form-urlencoded"));
|
|
1759
|
+
};
|
|
1760
|
+
var executeOne = (target, credential, method, path, form, apiBase) => Effect14.gen(function* () {
|
|
1761
|
+
const client = yield* HttpClient5.HttpClient;
|
|
1762
|
+
const request = buildRequest(method, apiBase, path, credential, target, form);
|
|
1763
|
+
return yield* client.execute(request).pipe(Effect14.mapError((cause) => new PveError(0, method, path, String(cause))));
|
|
1764
|
+
});
|
|
1765
|
+
var readResponse = (response, method, path) => Effect14.gen(function* () {
|
|
1766
|
+
if (response.status < 200 || response.status >= 300) {
|
|
1767
|
+
const detail = yield* response.text.pipe(Effect14.orElseSucceed(() => ""));
|
|
1768
|
+
return yield* Effect14.fail(new PveError(response.status, method, path, detail.slice(0, 300)));
|
|
1769
|
+
}
|
|
1770
|
+
const body = yield* response.json.pipe(Effect14.mapError((cause) => new PveError(0, method, path, String(cause))));
|
|
1771
|
+
return body.data;
|
|
1772
|
+
});
|
|
1773
|
+
var pveWith = (target, credential, method, path, form) => Effect14.gen(function* () {
|
|
1774
|
+
const response = target.scheme === "pve" ? yield* executeOnCluster(target, method, path, (apiBase) => buildRequest(method, apiBase, path, credential, target, form)).pipe(Effect14.mapError((failure) => failure.tag === "cluster" ? clusterExhausted(failure.method, failure.path, failure.attempts) : new PveError(0, method, path, String(failure)))) : yield* executeOne(target, credential, method, path, form, target.api);
|
|
1775
|
+
return yield* readResponse(response, method, path);
|
|
1776
|
+
});
|
|
1777
|
+
var pveEnvelopeWith = (target, credential, method, path) => Effect14.gen(function* () {
|
|
1778
|
+
const response = yield* executeOnCluster(target, method, path, (apiBase) => buildRequest(method, apiBase, path, credential, target)).pipe(Effect14.mapError((failure) => failure.tag === "cluster" ? clusterExhausted(failure.method, failure.path, failure.attempts) : new PveError(0, method, path, String(failure))));
|
|
1779
|
+
if (response.status < 200 || response.status >= 300) {
|
|
1780
|
+
const detail = yield* response.text.pipe(Effect14.orElseSucceed(() => ""));
|
|
1781
|
+
return yield* Effect14.fail(new PveError(response.status, method, path, detail.slice(0, 300)));
|
|
1782
|
+
}
|
|
1783
|
+
return yield* response.json.pipe(Effect14.mapError((cause) => new PveError(0, method, path, String(cause))));
|
|
1784
|
+
});
|
|
1785
|
+
var pve = (target, role, method, path, form) => Effect14.gen(function* () {
|
|
1786
|
+
const credential = yield* leased(target, role);
|
|
1787
|
+
return yield* pveWith(target, credential, method, path, form);
|
|
1788
|
+
});
|
|
1789
|
+
|
|
1790
|
+
// src/proxmox/resource.ts
|
|
1791
|
+
var pveOperations = (spec) => {
|
|
1792
|
+
const { guardCreate: guardCreate2, guardUpdate: guardUpdate2 } = specGuards(spec);
|
|
1793
|
+
const read = (props) => pve(props.target, spec.readRole ?? "read", "GET", spec.path(props)).pipe(Effect15.map((data) => data == null ? undefined : spec.attributes(data, props)), Effect15.orElseSucceed(() => {
|
|
1794
|
+
return;
|
|
1795
|
+
}));
|
|
1796
|
+
return {
|
|
1797
|
+
guardCreate: guardCreate2,
|
|
1798
|
+
guardUpdate: guardUpdate2,
|
|
1799
|
+
read,
|
|
1800
|
+
diff: (news, output) => Effect15.gen(function* () {
|
|
1801
|
+
if (!isResolved5(news))
|
|
1802
|
+
return;
|
|
1803
|
+
yield* guardCreate2(news, output === undefined);
|
|
1804
|
+
yield* guardUpdate2(news);
|
|
1805
|
+
if (output === undefined)
|
|
1806
|
+
return;
|
|
1807
|
+
const live = yield* read(news);
|
|
1808
|
+
if (live === undefined) {
|
|
1809
|
+
yield* guardCreate2(news, true);
|
|
1810
|
+
return { action: "update" };
|
|
1811
|
+
}
|
|
1812
|
+
if (spec.matches(live, news))
|
|
1813
|
+
return { action: "noop" };
|
|
1814
|
+
return spec.updateForm === undefined ? { action: "replace" } : { action: "update" };
|
|
1815
|
+
}),
|
|
1816
|
+
reconcile: (news) => Effect15.gen(function* () {
|
|
1817
|
+
const live = yield* read(news);
|
|
1818
|
+
yield* guardCreate2(news, live === undefined);
|
|
1819
|
+
yield* guardUpdate2(news);
|
|
1820
|
+
if (live === undefined) {
|
|
1821
|
+
yield* pve(news.target, "provision", "POST", spec.collection(news), spec.createForm(news));
|
|
1822
|
+
} else if (spec.updateForm !== undefined) {
|
|
1823
|
+
const form = formToSend(spec.matches, live, news, spec.updateForm(news));
|
|
1824
|
+
if (form !== undefined) {
|
|
1825
|
+
yield* pve(news.target, "provision", "PUT", spec.path(news), form);
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
const after = yield* read(news);
|
|
1829
|
+
if (after === undefined) {
|
|
1830
|
+
return yield* Effect15.die(new Error(`${spec.path(news)}: the write returned no error but the object is still absent. ` + 'PVE wraps every answer in {"data":...} and can report success on a call that did ' + "nothing -- read back rather than trusting the status code."));
|
|
1831
|
+
}
|
|
1832
|
+
return after;
|
|
1833
|
+
}),
|
|
1834
|
+
destroy: (olds) => pve(olds.target, "provision", "DELETE", spec.path(olds))
|
|
1835
|
+
};
|
|
1836
|
+
};
|
|
1837
|
+
var pveHandlers = (spec) => {
|
|
1838
|
+
const ops = pveOperations(spec);
|
|
1839
|
+
return {
|
|
1840
|
+
list: () => Effect15.succeed([]),
|
|
1841
|
+
read: ({ olds }) => ops.read(olds),
|
|
1842
|
+
diff: ({ news, output }) => ops.diff(news, output),
|
|
1843
|
+
reconcile: ({ news }) => ops.reconcile(news),
|
|
1844
|
+
delete: ({ olds }) => ops.destroy(olds)
|
|
1845
|
+
};
|
|
1846
|
+
};
|
|
1847
|
+
|
|
1848
|
+
// src/proxmox/ceph-flag.ts
|
|
1766
1849
|
var ProxmoxCephFlag = Resource5("Proxmox.CephFlag");
|
|
1767
|
-
var
|
|
1850
|
+
var handlers = pveHandlers({
|
|
1768
1851
|
attributes: (live, props) => ({ flag: props.flag, value: bool(live) }),
|
|
1769
1852
|
collection: () => "cluster/ceph/flags",
|
|
1770
1853
|
createForm: (props) => ({ [props.flag]: flag(props.value) ?? "0" }),
|
|
@@ -1773,20 +1856,20 @@ var handlers2 = pveHandlers({
|
|
|
1773
1856
|
path: (props) => `cluster/ceph/flags/${props.flag}`,
|
|
1774
1857
|
updateForm: (props) => ({ value: flag(props.value) ?? "0" })
|
|
1775
1858
|
});
|
|
1776
|
-
var ProxmoxCephFlagProvider = () => Provider5.effect(ProxmoxCephFlag,
|
|
1777
|
-
...
|
|
1778
|
-
delete: () =>
|
|
1779
|
-
reconcile:
|
|
1780
|
-
const after = yield*
|
|
1859
|
+
var ProxmoxCephFlagProvider = () => Provider5.effect(ProxmoxCephFlag, Effect16.succeed(ProxmoxCephFlag.Provider.of({
|
|
1860
|
+
...handlers,
|
|
1861
|
+
delete: () => Effect16.void,
|
|
1862
|
+
reconcile: Effect16.fn(function* ({ news }) {
|
|
1863
|
+
const after = yield* handlers.reconcile({ news });
|
|
1781
1864
|
if (after.value !== news.value) {
|
|
1782
|
-
return yield*
|
|
1865
|
+
return yield* Effect16.die(new Error(`cluster/ceph/flags/${news.flag}: the PUT returned no error but ceph still ` + `reports the flag ${after.value ? "set" : "clear"} rather than ` + `${news.value ? "set" : "clear"}. This endpoint's success is documented as a ` + "null body, so the status code proves nothing -- check `ceph osd dump | " + "head -1` and the cluster log. `pause` in particular is two ceph flags read " + "as one; see the header."));
|
|
1783
1866
|
}
|
|
1784
1867
|
return after;
|
|
1785
1868
|
})
|
|
1786
1869
|
})));
|
|
1787
1870
|
// src/proxmox/ceph-fs.ts
|
|
1788
1871
|
import { Resource as Resource6 } from "alchemy";
|
|
1789
|
-
import { isResolved as
|
|
1872
|
+
import { isResolved as isResolved6 } from "alchemy/Diff";
|
|
1790
1873
|
import * as Provider6 from "alchemy/Provider";
|
|
1791
1874
|
import * as Effect18 from "effect/Effect";
|
|
1792
1875
|
|
|
@@ -1795,7 +1878,6 @@ import * as nodes2 from "@distilled.cloud/proxmox/nodes";
|
|
|
1795
1878
|
import * as Effect17 from "effect/Effect";
|
|
1796
1879
|
|
|
1797
1880
|
// src/proxmox/ceph-fs-wire.ts
|
|
1798
|
-
import * as Effect16 from "effect/Effect";
|
|
1799
1881
|
var readRow = (live, props) => {
|
|
1800
1882
|
const row = (Array.isArray(live) ? live : []).filter((entry) => typeof entry === "object" && entry !== null).find((entry) => entry["name"] === props.name);
|
|
1801
1883
|
if (row === undefined)
|
|
@@ -1817,33 +1899,6 @@ var createForm2 = (props) => ({
|
|
|
1817
1899
|
...field("add-storage", flag(props["add-storage"])),
|
|
1818
1900
|
...field("pg_num", props.pg_num === undefined ? undefined : String(props.pg_num))
|
|
1819
1901
|
});
|
|
1820
|
-
var destroyPath = (props) => {
|
|
1821
|
-
const query = [
|
|
1822
|
-
props["remove-pools"] === true ? "remove-pools=1" : undefined,
|
|
1823
|
-
props["remove-storages"] === true ? "remove-storages=1" : undefined
|
|
1824
|
-
].filter((part) => part !== undefined);
|
|
1825
|
-
return query.length === 0 ? objectPath(props) : `${objectPath(props)}?${query.join("&")}`;
|
|
1826
|
-
};
|
|
1827
|
-
var settle = (target, node, upid, what) => Effect16.gen(function* () {
|
|
1828
|
-
for (let attempt = 0;attempt < POLL_ATTEMPTS; attempt += 1) {
|
|
1829
|
-
const status = yield* pve(target, "read", "GET", `nodes/${node}/tasks/${encodeURIComponent(upid)}/status`).pipe(Effect16.orElseSucceed(() => {
|
|
1830
|
-
return;
|
|
1831
|
-
}));
|
|
1832
|
-
if (status?.status === "stopped") {
|
|
1833
|
-
const exit = text(status.exitstatus);
|
|
1834
|
-
if (exit === "OK")
|
|
1835
|
-
return;
|
|
1836
|
-
return yield* Effect16.die(new Error(`${what}: PVE task ${upid} finished with "${exit === "" ? "no exit status" : exit}". ` + "The write returned HTTP 200 because it only forked the worker -- the real error is " + `in \`pvesh get /nodes/${node}/tasks/${upid}/log\`.`));
|
|
1837
|
-
}
|
|
1838
|
-
yield* Effect16.sleep(POLL_SECONDS * 1000);
|
|
1839
|
-
}
|
|
1840
|
-
return yield* Effect16.die(new Error(`${what}: PVE task ${upid} was still running after ` + `${String(POLL_ATTEMPTS * POLL_SECONDS)}s, or its ` + "status could not be read. Check the task log, and check that the `read` role holds " + `Sys.Audit on /nodes/${node} -- each call mints a new token, so the poller is never the ` + "task owner and is answered 403 rather than told to wait."));
|
|
1841
|
-
});
|
|
1842
|
-
var destroyFs = (props) => Effect16.gen(function* () {
|
|
1843
|
-
const upid = text(yield* pve(props.target, "provision", "DELETE", destroyPath(props)));
|
|
1844
|
-
if (upid !== "")
|
|
1845
|
-
yield* settle(props.target, props.node, upid, destroyPath(props));
|
|
1846
|
-
});
|
|
1847
1902
|
var notCreated2 = (props) => new Error(`${objectPath(props)}: the create task finished but the filesystem is not in ` + `GET nodes/${props.node}/ceph/fs. Check the task log -- a PVE worker can exit having ` + "rolled back the pools it had just made.");
|
|
1848
1903
|
var notDestroyed = (props) => new Error(`${objectPath(props)}: the destroy task finished but the filesystem is still in ` + `GET nodes/${props.node}/ceph/fs. PVE refuses to destroy a CephFS while a PVE storage entry ` + "of type cephfs still references it and is not disabled.");
|
|
1849
1904
|
|
|
@@ -1854,7 +1909,7 @@ var toDistilledCreate = (props) => ({
|
|
|
1854
1909
|
name: props.name,
|
|
1855
1910
|
node: props.node
|
|
1856
1911
|
});
|
|
1857
|
-
var
|
|
1912
|
+
var settleTask = (props, upid) => Effect17.gen(function* () {
|
|
1858
1913
|
for (let attempt = 0;attempt < POLL_ATTEMPTS; attempt += 1) {
|
|
1859
1914
|
const status = yield* runPve(props.target, "read", false, nodes2.getNodeTaskStatus({ node: props.node, upid })).pipe(Effect17.orElseSucceed(() => {
|
|
1860
1915
|
return;
|
|
@@ -1873,7 +1928,17 @@ var createFs = (props) => Effect17.gen(function* () {
|
|
|
1873
1928
|
if (upid === "") {
|
|
1874
1929
|
return yield* Effect17.die(new Error(`${objectPath(props)}: POST returned no task id. PVE wraps every answer in ` + '{"data":...} and can report success on a call that did nothing.'));
|
|
1875
1930
|
}
|
|
1876
|
-
yield*
|
|
1931
|
+
yield* settleTask(props, upid);
|
|
1932
|
+
});
|
|
1933
|
+
var destroyFs = (props) => Effect17.gen(function* () {
|
|
1934
|
+
const upid = yield* runPve(props.target, "provision", true, nodes2.deleteNodeCephFs({
|
|
1935
|
+
node: props.node,
|
|
1936
|
+
name: props.name,
|
|
1937
|
+
...props["remove-pools"] === true ? { remove_pools: "1" } : {},
|
|
1938
|
+
...props["remove-storages"] === true ? { remove_storages: "1" } : {}
|
|
1939
|
+
})).pipe(Effect17.catchTag("CephFsNotFound", () => Effect17.succeed("")));
|
|
1940
|
+
if (upid !== "")
|
|
1941
|
+
yield* settleTask(props, upid);
|
|
1877
1942
|
});
|
|
1878
1943
|
|
|
1879
1944
|
// src/proxmox/ceph-fs.ts
|
|
@@ -1886,7 +1951,7 @@ var ProxmoxCephFsProvider = () => Provider6.effect(ProxmoxCephFs, Effect18.succe
|
|
|
1886
1951
|
return yield* readFs(olds);
|
|
1887
1952
|
}),
|
|
1888
1953
|
diff: Effect18.fn(function* ({ news, output }) {
|
|
1889
|
-
if (!
|
|
1954
|
+
if (!isResolved6(news))
|
|
1890
1955
|
return;
|
|
1891
1956
|
yield* guardWrite2(CEPH_FS_CREATE, createForm2(news), output === undefined);
|
|
1892
1957
|
if (output !== undefined && news.name !== output.name) {
|
|
@@ -1922,7 +1987,7 @@ var ProxmoxCephFsProvider = () => Provider6.effect(ProxmoxCephFs, Effect18.succe
|
|
|
1922
1987
|
})));
|
|
1923
1988
|
// src/proxmox/ceph-osd.ts
|
|
1924
1989
|
import { Resource as Resource7 } from "alchemy";
|
|
1925
|
-
import { isResolved as
|
|
1990
|
+
import { isResolved as isResolved7 } from "alchemy/Diff";
|
|
1926
1991
|
import * as Provider7 from "alchemy/Provider";
|
|
1927
1992
|
import * as Effect21 from "effect/Effect";
|
|
1928
1993
|
|
|
@@ -1997,7 +2062,7 @@ var ProxmoxCephOsd = Resource7("Proxmox.CephOsd", {
|
|
|
1997
2062
|
});
|
|
1998
2063
|
var settled = (live, props) => (props.host === undefined || props.host === live.host) && (props.device_class === undefined || props.device_class === live.device_class);
|
|
1999
2064
|
var describe2 = (props) => `osd.${String(props.osdid)} (read via nodes/${props.node}/ceph/osd)`;
|
|
2000
|
-
var
|
|
2065
|
+
var handlers2 = {
|
|
2001
2066
|
list: () => Effect21.succeed([]),
|
|
2002
2067
|
read: Effect21.fn(function* ({ olds }) {
|
|
2003
2068
|
return yield* readOsd(olds);
|
|
@@ -2006,7 +2071,7 @@ var handlers3 = {
|
|
|
2006
2071
|
news,
|
|
2007
2072
|
output
|
|
2008
2073
|
}) {
|
|
2009
|
-
if (output === undefined || !
|
|
2074
|
+
if (output === undefined || !isResolved7(news))
|
|
2010
2075
|
return;
|
|
2011
2076
|
const live = yield* readOsd(news);
|
|
2012
2077
|
if (live === undefined)
|
|
@@ -2029,10 +2094,10 @@ var handlers3 = {
|
|
|
2029
2094
|
yield* destroyOsd(olds);
|
|
2030
2095
|
})
|
|
2031
2096
|
};
|
|
2032
|
-
var ProxmoxCephOsdProvider = () => Provider7.effect(ProxmoxCephOsd, Effect21.succeed(ProxmoxCephOsd.Provider.of(
|
|
2097
|
+
var ProxmoxCephOsdProvider = () => Provider7.effect(ProxmoxCephOsd, Effect21.succeed(ProxmoxCephOsd.Provider.of(handlers2)));
|
|
2033
2098
|
// src/proxmox/ceph-pool.ts
|
|
2034
2099
|
import { Resource as Resource8 } from "alchemy";
|
|
2035
|
-
import { isResolved as
|
|
2100
|
+
import { isResolved as isResolved8 } from "alchemy/Diff";
|
|
2036
2101
|
import * as Provider8 from "alchemy/Provider";
|
|
2037
2102
|
import * as nodes7 from "@distilled.cloud/proxmox/nodes";
|
|
2038
2103
|
import * as Effect24 from "effect/Effect";
|
|
@@ -2066,7 +2131,7 @@ var createBody = (props) => ({
|
|
|
2066
2131
|
import * as nodes5 from "@distilled.cloud/proxmox/nodes";
|
|
2067
2132
|
import * as Effect22 from "effect/Effect";
|
|
2068
2133
|
var confirmAbsent = (props, describe3) => runPve(props.target, "read", false, nodes5.listNodeCephPool({ node: props.node })).pipe(Effect22.flatMap((rows) => rows.some((row) => row.pool_name === props.name) ? Effect22.die(new Error(`${describe3}: the cluster lists this pool but its status could not be read, ` + "so this deploy will NOT create over it. Check that the node is up and that the " + "read role holds Sys.Audit or Datastore.Audit on / -- a create here would apply " + "pg_num to a live pool and start a PG merge.")) : Effect22.void));
|
|
2069
|
-
var
|
|
2134
|
+
var settle = (props, read, done) => Effect22.gen(function* () {
|
|
2070
2135
|
for (let attempt = 0;attempt < 30; attempt += 1) {
|
|
2071
2136
|
const live = yield* read(props);
|
|
2072
2137
|
if (done(live))
|
|
@@ -2123,7 +2188,7 @@ var ProxmoxCephPoolProvider = () => Provider8.effect(ProxmoxCephPool, Effect24.s
|
|
|
2123
2188
|
list: () => Effect24.succeed([]),
|
|
2124
2189
|
read: ({ olds }) => readPoolStatus(olds),
|
|
2125
2190
|
diff: Effect24.fn(function* ({ news, output }) {
|
|
2126
|
-
if (!
|
|
2191
|
+
if (!isResolved8(news))
|
|
2127
2192
|
return;
|
|
2128
2193
|
yield* guardWrite2(CEPH_POOL_CREATE, createBody(news), output === undefined);
|
|
2129
2194
|
yield* guardWrite2(CEPH_POOL_UPDATE, updateBody(news), false);
|
|
@@ -2150,7 +2215,7 @@ var ProxmoxCephPoolProvider = () => Provider8.effect(ProxmoxCephPool, Effect24.s
|
|
|
2150
2215
|
upid = yield* runPve(news.target, "provision", true, nodes7.putNodeCephPool(toDistilledUpdate(news)));
|
|
2151
2216
|
}
|
|
2152
2217
|
}
|
|
2153
|
-
const after = yield*
|
|
2218
|
+
const after = yield* settle(news, readPoolStatus, (row) => row !== undefined && matches3(row, news));
|
|
2154
2219
|
if (after === undefined || !matches3(after, news)) {
|
|
2155
2220
|
return yield* Effect24.die(new Error(`${object(news)}: the write returned no error but the pool still does not match ` + `the declaration after 60s. PVE answered with task ${upid ?? "(none)"} -- read ` + "its log with `pvesh get /nodes/<node>/tasks/<upid>/log` to see what Ceph " + "refused."));
|
|
2156
2221
|
}
|
|
@@ -2158,7 +2223,7 @@ var ProxmoxCephPoolProvider = () => Provider8.effect(ProxmoxCephPool, Effect24.s
|
|
|
2158
2223
|
}),
|
|
2159
2224
|
delete: Effect24.fn(function* ({ olds }) {
|
|
2160
2225
|
yield* runPve(olds.target, "provision", true, nodes7.deleteNodeCephPool({ name: olds.name, node: olds.node }));
|
|
2161
|
-
const left = yield*
|
|
2226
|
+
const left = yield* settle(olds, readPoolStatus, (row) => row === undefined);
|
|
2162
2227
|
if (left !== undefined) {
|
|
2163
2228
|
return yield* Effect24.die(new Error(`${object(olds)}: the DELETE returned no error but the pool is still there after ` + "60s. Either a storage on it still holds RBD images, or the mons are running " + "with mon_allow_pool_delete false."));
|
|
2164
2229
|
}
|
|
@@ -2166,7 +2231,7 @@ var ProxmoxCephPoolProvider = () => Provider8.effect(ProxmoxCephPool, Effect24.s
|
|
|
2166
2231
|
})));
|
|
2167
2232
|
// src/proxmox/firewall-alias.ts
|
|
2168
2233
|
import { Resource as Resource9 } from "alchemy";
|
|
2169
|
-
import { isResolved as
|
|
2234
|
+
import { isResolved as isResolved9 } from "alchemy/Diff";
|
|
2170
2235
|
import * as Provider9 from "alchemy/Provider";
|
|
2171
2236
|
import * as Effect25 from "effect/Effect";
|
|
2172
2237
|
|
|
@@ -2188,7 +2253,7 @@ var body = (props) => {
|
|
|
2188
2253
|
|
|
2189
2254
|
// src/proxmox/firewall-alias.ts
|
|
2190
2255
|
var ProxmoxFirewallAlias = Resource9("Proxmox.FirewallAlias");
|
|
2191
|
-
var
|
|
2256
|
+
var handlers3 = pveHandlers({
|
|
2192
2257
|
attributes: (live, props) => {
|
|
2193
2258
|
const address = cidr(live["cidr"]);
|
|
2194
2259
|
return address === "" ? undefined : {
|
|
@@ -2209,17 +2274,17 @@ var handlers4 = pveHandlers({
|
|
|
2209
2274
|
updateForm: body
|
|
2210
2275
|
});
|
|
2211
2276
|
var ProxmoxFirewallAliasProvider = () => Provider9.effect(ProxmoxFirewallAlias, Effect25.succeed(ProxmoxFirewallAlias.Provider.of({
|
|
2212
|
-
...
|
|
2277
|
+
...handlers3,
|
|
2213
2278
|
diff: Effect25.fn(function* ({ news, output }) {
|
|
2214
|
-
if (output !== undefined &&
|
|
2279
|
+
if (output !== undefined && isResolved9(news) && fold(news.name) !== fold(output.name)) {
|
|
2215
2280
|
return { action: "replace" };
|
|
2216
2281
|
}
|
|
2217
|
-
return yield*
|
|
2282
|
+
return yield* handlers3.diff({ news, output });
|
|
2218
2283
|
})
|
|
2219
2284
|
})));
|
|
2220
2285
|
// src/proxmox/group.ts
|
|
2221
2286
|
import { Resource as Resource10 } from "alchemy";
|
|
2222
|
-
import { isResolved as
|
|
2287
|
+
import { isResolved as isResolved10 } from "alchemy/Diff";
|
|
2223
2288
|
import * as Provider10 from "alchemy/Provider";
|
|
2224
2289
|
import * as access4 from "@distilled.cloud/proxmox/access";
|
|
2225
2290
|
import * as Effect27 from "effect/Effect";
|
|
@@ -2265,7 +2330,7 @@ var ProxmoxGroupProvider = () => Provider10.effect(ProxmoxGroup, Effect27.succee
|
|
|
2265
2330
|
return dropUnreadable3(yield* output === undefined ? readGroup(olds) : readGroupOrFail(olds));
|
|
2266
2331
|
}),
|
|
2267
2332
|
diff: Effect27.fn(function* ({ news, output }) {
|
|
2268
|
-
if (!
|
|
2333
|
+
if (!isResolved10(news))
|
|
2269
2334
|
return;
|
|
2270
2335
|
yield* guardWrite2(GROUP_CREATE, createForm3(news), output === undefined);
|
|
2271
2336
|
yield* guardWrite2(GROUP_UPDATE, updateForm(news), false);
|
|
@@ -2313,7 +2378,7 @@ var shape3 = (props) => ({
|
|
|
2313
2378
|
max_restart: String(props.max_restart ?? 1),
|
|
2314
2379
|
state: props.state ?? "started"
|
|
2315
2380
|
});
|
|
2316
|
-
var
|
|
2381
|
+
var handlers4 = pveHandlers({
|
|
2317
2382
|
attributes: (live, props) => ({
|
|
2318
2383
|
comment: typeof live["comment"] === "string" ? live["comment"] : "",
|
|
2319
2384
|
max_relocate: num(live["max_relocate"], 1),
|
|
@@ -2332,7 +2397,7 @@ var handlers5 = pveHandlers({
|
|
|
2332
2397
|
path: (props) => `cluster/ha/resources/${props.sid}`,
|
|
2333
2398
|
updateForm: shape3
|
|
2334
2399
|
});
|
|
2335
|
-
var ProxmoxHaResourceProvider = () => Provider11.effect(ProxmoxHaResource, Effect28.succeed(ProxmoxHaResource.Provider.of(
|
|
2400
|
+
var ProxmoxHaResourceProvider = () => Provider11.effect(ProxmoxHaResource, Effect28.succeed(ProxmoxHaResource.Provider.of(handlers4)));
|
|
2336
2401
|
// src/proxmox/ha-rule.ts
|
|
2337
2402
|
import { Resource as Resource12 } from "alchemy";
|
|
2338
2403
|
import * as Provider12 from "alchemy/Provider";
|
|
@@ -2384,7 +2449,7 @@ var clearList = (props) => Object.entries(optional(props)).filter(([, value]) =>
|
|
|
2384
2449
|
|
|
2385
2450
|
// src/proxmox/ha-rule.ts
|
|
2386
2451
|
var ProxmoxHaRule = Resource12("Proxmox.HaRule");
|
|
2387
|
-
var
|
|
2452
|
+
var handlers5 = pveHandlers({
|
|
2388
2453
|
attributes: (live, props) => {
|
|
2389
2454
|
const liveType = text(live["type"]);
|
|
2390
2455
|
if (liveType !== "" && liveType !== props.type)
|
|
@@ -2413,10 +2478,10 @@ var handlers6 = pveHandlers({
|
|
|
2413
2478
|
return withClears(fields, clear);
|
|
2414
2479
|
}
|
|
2415
2480
|
});
|
|
2416
|
-
var ProxmoxHaRuleProvider = () => Provider12.effect(ProxmoxHaRule, Effect29.succeed(ProxmoxHaRule.Provider.of(
|
|
2481
|
+
var ProxmoxHaRuleProvider = () => Provider12.effect(ProxmoxHaRule, Effect29.succeed(ProxmoxHaRule.Provider.of(handlers5)));
|
|
2417
2482
|
// src/proxmox/lxc.ts
|
|
2418
2483
|
import { Resource as Resource13 } from "alchemy";
|
|
2419
|
-
import { isResolved as
|
|
2484
|
+
import { isResolved as isResolved11 } from "alchemy/Diff";
|
|
2420
2485
|
import * as Provider13 from "alchemy/Provider";
|
|
2421
2486
|
import * as Effect39 from "effect/Effect";
|
|
2422
2487
|
|
|
@@ -2669,8 +2734,8 @@ var optionMap = (value, family, defaultKey) => {
|
|
|
2669
2734
|
return out;
|
|
2670
2735
|
};
|
|
2671
2736
|
var sameMap = (left, right) => left.size === right.size && [...left].every(([key, value]) => right.get(key) === value);
|
|
2672
|
-
var sortedForm = (
|
|
2673
|
-
var printMap = (
|
|
2737
|
+
var sortedForm = (map13) => [...map13].map(([key, value]) => `${key}=${value}`).sort().join(",");
|
|
2738
|
+
var printMap = (map13, lead = []) => [...lead.filter((key) => map13.has(key)), ...[...map13.keys()].filter((k) => !lead.includes(k))].map((key) => `${key}=${map13.get(key) ?? ""}`).join(",");
|
|
2674
2739
|
var netMaps = (declared, live) => {
|
|
2675
2740
|
const want = optionMap(declared, "net");
|
|
2676
2741
|
const have = optionMap(live, "net");
|
|
@@ -3102,10 +3167,10 @@ var pendingLines = (diff) => {
|
|
|
3102
3167
|
var pendingCount = (target, node) => stagedDiff(target, node).pipe(Effect36.map(pendingLines));
|
|
3103
3168
|
var clusterHealth = (target) => pve(target, "read", "GET", "cluster/status").pipe(Effect36.map((rows) => {
|
|
3104
3169
|
const all = rows ?? [];
|
|
3105
|
-
const
|
|
3170
|
+
const cluster3 = all.find((row) => row.type === "cluster");
|
|
3106
3171
|
return {
|
|
3107
3172
|
offline: all.filter((row) => row.type === "node" && !bool(row.online)).map((row) => text(row.name, "<unnamed>")),
|
|
3108
|
-
quorate:
|
|
3173
|
+
quorate: cluster3 === undefined ? true : bool(cluster3.quorate)
|
|
3109
3174
|
};
|
|
3110
3175
|
}));
|
|
3111
3176
|
var degradedReason = (target) => clusterHealth(target).pipe(Effect36.map((health) => health.quorate && health.offline.length === 0 ? undefined : `quorate=${String(health.quorate)} offline=[${health.offline.join(", ")}]`));
|
|
@@ -3221,7 +3286,7 @@ var diff = (owner, news, olds) => Effect39.gen(function* () {
|
|
|
3221
3286
|
return yield* refused(identity2);
|
|
3222
3287
|
if (output === undefined)
|
|
3223
3288
|
return;
|
|
3224
|
-
if (!
|
|
3289
|
+
if (!isResolved11(news))
|
|
3225
3290
|
return;
|
|
3226
3291
|
const live = yield* readLive(news);
|
|
3227
3292
|
if (live === undefined) {
|
|
@@ -3296,7 +3361,7 @@ var read = (owner, olds) => Effect39.gen(function* () {
|
|
|
3296
3361
|
const settled2 = Effect39.sync(() => judge(olds, live).drift.length === 0);
|
|
3297
3362
|
return yield* ownedRead(owner, attributesOf5(where, live), settled2);
|
|
3298
3363
|
});
|
|
3299
|
-
var
|
|
3364
|
+
var handlers6 = {
|
|
3300
3365
|
delete: ({ olds, output }) => destroyGuest(whereOf(olds, output), olds),
|
|
3301
3366
|
diff: ({
|
|
3302
3367
|
fqn,
|
|
@@ -3319,11 +3384,11 @@ var handlers7 = {
|
|
|
3319
3384
|
output
|
|
3320
3385
|
}) => reconcile({ fqn, instanceId, output }, news)
|
|
3321
3386
|
};
|
|
3322
|
-
var ProxmoxLxcProvider = () => Provider13.effect(ProxmoxLxc, Effect39.succeed(ProxmoxLxc.Provider.of(
|
|
3387
|
+
var ProxmoxLxcProvider = () => Provider13.effect(ProxmoxLxc, Effect39.succeed(ProxmoxLxc.Provider.of(handlers6)).pipe(Effect39.map(provingResumes)));
|
|
3323
3388
|
// src/proxmox/metric-server.ts
|
|
3324
3389
|
import { Resource as Resource14 } from "alchemy";
|
|
3325
|
-
import
|
|
3326
|
-
import * as
|
|
3390
|
+
import { isResolved as isResolved12 } from "alchemy/Diff";
|
|
3391
|
+
import * as cluster4 from "@distilled.cloud/proxmox/cluster";
|
|
3327
3392
|
|
|
3328
3393
|
// src/proxmox/metric-server-form.ts
|
|
3329
3394
|
var UNSET2 = -1;
|
|
@@ -3393,8 +3458,7 @@ var otelAttributes = (live) => ({
|
|
|
3393
3458
|
});
|
|
3394
3459
|
var otelMatches = (attributes, props) => attributes["otel-compression"] === (props["otel-compression"] ?? "") && attributes["otel-max-body-size"] === (props["otel-max-body-size"] ?? UNSET2) && attributes["otel-path"] === (props["otel-path"] ?? "") && attributes["otel-protocol"] === (props["otel-protocol"] ?? "") && attributes["otel-timeout"] === (props["otel-timeout"] ?? UNSET2) && attributes["otel-verify-ssl"] === (props["otel-verify-ssl"] !== false);
|
|
3395
3460
|
|
|
3396
|
-
// src/proxmox/metric-server.ts
|
|
3397
|
-
var ProxmoxMetricServer = Resource14("Proxmox.MetricServer");
|
|
3461
|
+
// src/proxmox/metric-server-config.ts
|
|
3398
3462
|
var matchesType = (attributes, props) => {
|
|
3399
3463
|
if (props.type === "opentelemetry")
|
|
3400
3464
|
return otelMatches(attributes, props);
|
|
@@ -3403,7 +3467,7 @@ var matchesType = (attributes, props) => {
|
|
|
3403
3467
|
return socket && attributes.path === (props.path ?? "");
|
|
3404
3468
|
return socket && attributes.influxdbproto === (props.influxdbproto ?? "") && attributes.organization === (props.organization ?? "") && attributes.bucket === (props.bucket ?? "") && attributes["api-path-prefix"] === (props["api-path-prefix"] ?? "") && attributes["max-body-size"] === (props["max-body-size"] ?? UNSET2) && attributes["verify-certificate"] === (props["verify-certificate"] !== false);
|
|
3405
3469
|
};
|
|
3406
|
-
var
|
|
3470
|
+
var metricServerSpec = {
|
|
3407
3471
|
attributes: (live, props) => {
|
|
3408
3472
|
const liveType = text(live["type"]);
|
|
3409
3473
|
if (liveType !== "" && liveType !== props.type)
|
|
@@ -3435,13 +3499,96 @@ var handlers8 = pveHandlers({
|
|
|
3435
3499
|
matches: (attributes, props) => attributes.server === props.server && attributes.port === props.port && attributes.disable === (props.disable === true) && matchesType(attributes, props),
|
|
3436
3500
|
path: (props) => `cluster/metrics/server/${props.id}`,
|
|
3437
3501
|
updateForm: update
|
|
3502
|
+
};
|
|
3503
|
+
|
|
3504
|
+
// src/proxmox/metric-server-distilled.ts
|
|
3505
|
+
import * as cluster3 from "@distilled.cloud/proxmox/cluster";
|
|
3506
|
+
import { ProxmoxParseError as ProxmoxParseError2 } from "@distilled.cloud/proxmox/Errors";
|
|
3507
|
+
import * as Effect40 from "effect/Effect";
|
|
3508
|
+
import * as Schema2 from "effect/Schema";
|
|
3509
|
+
var readMetricServerRow = (props) => runPve(props.target, "read", false, cluster3.getClusterMetricsServer({ id: props.id })).pipe(Effect40.flatMap((body4) => Schema2.decodeUnknownEffect(Schema2.Record(Schema2.String, Schema2.Unknown))(body4).pipe(Effect40.mapError(() => new ProxmoxParseError2({
|
|
3510
|
+
body: undefined,
|
|
3511
|
+
cause: "Metric server response must be an object"
|
|
3512
|
+
})))), Effect40.catchTag("MetricServerNotFound", () => Effect40.succeed(undefined)), Effect40.catchTag("NotFound", () => Effect40.succeed(undefined)));
|
|
3513
|
+
var readMetricServer = (props) => readMetricServerRow(props).pipe(Effect40.map((row) => row === undefined ? undefined : metricServerSpec.attributes(row, props)));
|
|
3514
|
+
var metricServerRequest = (props, form) => ({
|
|
3515
|
+
...asForm2({
|
|
3516
|
+
api_path_prefix: form["api-path-prefix"],
|
|
3517
|
+
bucket: form["bucket"],
|
|
3518
|
+
delete: form["delete"],
|
|
3519
|
+
disable: form["disable"],
|
|
3520
|
+
influxdbproto: form["influxdbproto"],
|
|
3521
|
+
max_body_size: form["max-body-size"],
|
|
3522
|
+
mtu: form["mtu"],
|
|
3523
|
+
organization: form["organization"],
|
|
3524
|
+
otel_compression: form["otel-compression"],
|
|
3525
|
+
otel_max_body_size: form["otel-max-body-size"],
|
|
3526
|
+
otel_path: form["otel-path"],
|
|
3527
|
+
otel_protocol: form["otel-protocol"],
|
|
3528
|
+
otel_timeout: form["otel-timeout"],
|
|
3529
|
+
otel_verify_ssl: form["otel-verify-ssl"],
|
|
3530
|
+
path: form["path"],
|
|
3531
|
+
timeout: form["timeout"],
|
|
3532
|
+
verify_certificate: form["verify-certificate"]
|
|
3533
|
+
}),
|
|
3534
|
+
id: props.id,
|
|
3535
|
+
port: String(props.port),
|
|
3536
|
+
server: props.server
|
|
3438
3537
|
});
|
|
3439
|
-
var
|
|
3538
|
+
var deleteMetricServer = (props) => Effect40.gen(function* () {
|
|
3539
|
+
if ((yield* readMetricServerRow(props)) === undefined)
|
|
3540
|
+
return;
|
|
3541
|
+
yield* runPve(props.target, "provision", true, cluster3.deleteClusterMetricsServer({ id: props.id }));
|
|
3542
|
+
});
|
|
3543
|
+
|
|
3544
|
+
// src/proxmox/metric-server.ts
|
|
3545
|
+
import * as Provider14 from "alchemy/Provider";
|
|
3546
|
+
import * as Effect41 from "effect/Effect";
|
|
3547
|
+
var ProxmoxMetricServer = Resource14("Proxmox.MetricServer");
|
|
3548
|
+
var { guardCreate: guardCreate2, guardUpdate: guardUpdate2 } = specGuards(metricServerSpec);
|
|
3549
|
+
var ProxmoxMetricServerProvider = () => Provider14.effect(ProxmoxMetricServer, Effect41.succeed(ProxmoxMetricServer.Provider.of({
|
|
3550
|
+
list: () => Effect41.succeed([]),
|
|
3551
|
+
read: ({ olds }) => readMetricServer(olds),
|
|
3552
|
+
diff: Effect41.fn(function* ({ news, output }) {
|
|
3553
|
+
if (!isResolved12(news))
|
|
3554
|
+
return;
|
|
3555
|
+
yield* guardCreate2(news, output === undefined);
|
|
3556
|
+
yield* guardUpdate2(news);
|
|
3557
|
+
if (output === undefined)
|
|
3558
|
+
return;
|
|
3559
|
+
const live = yield* readMetricServer(news);
|
|
3560
|
+
if (live === undefined) {
|
|
3561
|
+
yield* guardCreate2(news, true);
|
|
3562
|
+
return { action: "update" };
|
|
3563
|
+
}
|
|
3564
|
+
return { action: metricServerSpec.matches(live, news) ? "noop" : "update" };
|
|
3565
|
+
}),
|
|
3566
|
+
reconcile: Effect41.fn(function* ({ news }) {
|
|
3567
|
+
const live = yield* readMetricServer(news);
|
|
3568
|
+
yield* guardCreate2(news, live === undefined);
|
|
3569
|
+
yield* guardUpdate2(news);
|
|
3570
|
+
if (live === undefined) {
|
|
3571
|
+
yield* runPve(news.target, "provision", true, cluster4.updateClusterMetricsServer({
|
|
3572
|
+
...metricServerRequest(news, metricServerSpec.createForm(news)),
|
|
3573
|
+
type: news.type
|
|
3574
|
+
}));
|
|
3575
|
+
} else {
|
|
3576
|
+
const form = formToSend(metricServerSpec.matches, live, news, metricServerSpec.updateForm(news));
|
|
3577
|
+
if (form !== undefined)
|
|
3578
|
+
yield* runPve(news.target, "provision", true, cluster4.putClusterMetricsServer(metricServerRequest(news, form)));
|
|
3579
|
+
}
|
|
3580
|
+
const after = yield* readMetricServer(news);
|
|
3581
|
+
if (after === undefined)
|
|
3582
|
+
return yield* Effect41.fail(new Error(`${metricServerSpec.path(news)}: write returned success but the resource is still absent`));
|
|
3583
|
+
return after;
|
|
3584
|
+
}),
|
|
3585
|
+
delete: ({ olds }) => deleteMetricServer(olds)
|
|
3586
|
+
})));
|
|
3440
3587
|
// src/proxmox/network-apply.ts
|
|
3441
3588
|
import { Resource as Resource15 } from "alchemy";
|
|
3442
|
-
import { isResolved as
|
|
3589
|
+
import { isResolved as isResolved13 } from "alchemy/Diff";
|
|
3443
3590
|
import * as Provider15 from "alchemy/Provider";
|
|
3444
|
-
import * as
|
|
3591
|
+
import * as Effect42 from "effect/Effect";
|
|
3445
3592
|
|
|
3446
3593
|
// src/proxmox/apply-endpoints.ts
|
|
3447
3594
|
var NETWORK_APPLY_ENDPOINT = "pve:PUT /nodes/{node}/network";
|
|
@@ -3451,15 +3598,15 @@ var guardSdnApply = guardForm(SDN_APPLY_ENDPOINT, {}, true);
|
|
|
3451
3598
|
|
|
3452
3599
|
// src/proxmox/network-apply.ts
|
|
3453
3600
|
var ProxmoxNetworkApply = Resource15("Proxmox.NetworkApply");
|
|
3454
|
-
var read2 = (props) => pendingCount(props.target, props.node).pipe(
|
|
3455
|
-
var diff2 = (news) =>
|
|
3456
|
-
if (!
|
|
3601
|
+
var read2 = (props) => pendingCount(props.target, props.node).pipe(Effect42.map((pending) => ({ node: props.node, pending })));
|
|
3602
|
+
var diff2 = (news) => Effect42.gen(function* () {
|
|
3603
|
+
if (!isResolved13(news))
|
|
3457
3604
|
return;
|
|
3458
3605
|
const { pending } = yield* read2(news);
|
|
3459
3606
|
return pending === 0 ? { action: "noop" } : { action: "update" };
|
|
3460
3607
|
});
|
|
3461
|
-
var refuse = (node, why) =>
|
|
3462
|
-
var apply = (props) =>
|
|
3608
|
+
var refuse = (node, why) => Effect42.die(new Error(`Proxmox.NetworkApply ${node}: ${why}`));
|
|
3609
|
+
var apply = (props) => Effect42.gen(function* () {
|
|
3463
3610
|
const node = props.node;
|
|
3464
3611
|
const staged = yield* pendingCount(props.target, node);
|
|
3465
3612
|
if (staged === 0)
|
|
@@ -3488,20 +3635,20 @@ var apply = (props) => Effect41.gen(function* () {
|
|
|
3488
3635
|
}
|
|
3489
3636
|
return { node, pending: left };
|
|
3490
3637
|
});
|
|
3491
|
-
var
|
|
3492
|
-
delete: () =>
|
|
3638
|
+
var handlers7 = {
|
|
3639
|
+
delete: () => Effect42.void,
|
|
3493
3640
|
diff: ({ news }) => diff2(news),
|
|
3494
|
-
list: () =>
|
|
3641
|
+
list: () => Effect42.succeed([]),
|
|
3495
3642
|
read: ({ olds }) => read2(olds),
|
|
3496
3643
|
reconcile: ({ news }) => apply(news)
|
|
3497
3644
|
};
|
|
3498
|
-
var ProxmoxNetworkApplyProvider = () => Provider15.effect(ProxmoxNetworkApply,
|
|
3645
|
+
var ProxmoxNetworkApplyProvider = () => Provider15.effect(ProxmoxNetworkApply, Effect42.succeed(ProxmoxNetworkApply.Provider.of(handlers7)));
|
|
3499
3646
|
// src/proxmox/node-network.ts
|
|
3500
3647
|
import { Resource as Resource16 } from "alchemy";
|
|
3501
|
-
import { isResolved as
|
|
3648
|
+
import { isResolved as isResolved14 } from "alchemy/Diff";
|
|
3502
3649
|
import * as Provider16 from "alchemy/Provider";
|
|
3503
3650
|
import * as nodes9 from "@distilled.cloud/proxmox/nodes";
|
|
3504
|
-
import * as
|
|
3651
|
+
import * as Effect44 from "effect/Effect";
|
|
3505
3652
|
|
|
3506
3653
|
// src/proxmox/node-network-form.ts
|
|
3507
3654
|
var NODE_NETWORK_CREATE = "pve:POST /nodes/{node}/network";
|
|
@@ -3544,7 +3691,7 @@ var updateForm2 = (props) => {
|
|
|
3544
3691
|
|
|
3545
3692
|
// src/proxmox/node-network-wire.ts
|
|
3546
3693
|
import * as nodes8 from "@distilled.cloud/proxmox/nodes";
|
|
3547
|
-
import * as
|
|
3694
|
+
import * as Effect43 from "effect/Effect";
|
|
3548
3695
|
var attributesOf6 = (live, props) => {
|
|
3549
3696
|
const data = live;
|
|
3550
3697
|
const liveType = text(data["type"]);
|
|
@@ -3578,12 +3725,12 @@ var attributesOf6 = (live, props) => {
|
|
|
3578
3725
|
"vlan-raw-device": text(data["vlan-raw-device"])
|
|
3579
3726
|
};
|
|
3580
3727
|
};
|
|
3581
|
-
var readInterfaceOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, nodes8.getNodeNetwork({ iface: props.iface, node: props.node })).pipe(
|
|
3728
|
+
var readInterfaceOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, nodes8.getNodeNetwork({ iface: props.iface, node: props.node })).pipe(Effect43.catchTag("ParameterVerificationFailed", (error) => error.errors["iface"] === "interface does not exist" ? Effect43.succeed(undefined) : Effect43.fail(error)))).pipe(Effect43.map((live) => {
|
|
3582
3729
|
if (live === UNREADABLE)
|
|
3583
3730
|
return UNREADABLE;
|
|
3584
3731
|
return live === undefined ? undefined : attributesOf6(live, props);
|
|
3585
3732
|
}));
|
|
3586
|
-
var readInterface = (props) => readInterfaceOrFail(props).pipe(
|
|
3733
|
+
var readInterface = (props) => readInterfaceOrFail(props).pipe(Effect43.orElseSucceed(() => {
|
|
3587
3734
|
return;
|
|
3588
3735
|
}));
|
|
3589
3736
|
var dropUnreadable4 = (live) => live === UNREADABLE ? undefined : live;
|
|
@@ -3608,13 +3755,13 @@ var toDistilledUpdate2 = (props) => ({
|
|
|
3608
3755
|
iface: props.iface,
|
|
3609
3756
|
node: props.node
|
|
3610
3757
|
});
|
|
3611
|
-
var ProxmoxNodeNetworkProvider = () => Provider16.effect(ProxmoxNodeNetwork,
|
|
3612
|
-
list: () =>
|
|
3613
|
-
read:
|
|
3758
|
+
var ProxmoxNodeNetworkProvider = () => Provider16.effect(ProxmoxNodeNetwork, Effect44.succeed(ProxmoxNodeNetwork.Provider.of({
|
|
3759
|
+
list: () => Effect44.succeed([]),
|
|
3760
|
+
read: Effect44.fn(function* ({ olds, output }) {
|
|
3614
3761
|
return dropUnreadable4(yield* output === undefined ? readInterface(olds) : readInterfaceOrFail(olds));
|
|
3615
3762
|
}),
|
|
3616
|
-
diff:
|
|
3617
|
-
if (!
|
|
3763
|
+
diff: Effect44.fn(function* ({ news, output }) {
|
|
3764
|
+
if (!isResolved14(news))
|
|
3618
3765
|
return;
|
|
3619
3766
|
yield* guardWrite2(NODE_NETWORK_CREATE, createForm5(news), output === undefined);
|
|
3620
3767
|
yield* guardWrite2(NODE_NETWORK_UPDATE, updateForm2(news), false);
|
|
@@ -3631,7 +3778,7 @@ var ProxmoxNodeNetworkProvider = () => Provider16.effect(ProxmoxNodeNetwork, Eff
|
|
|
3631
3778
|
}
|
|
3632
3779
|
return matches5(live, news) ? { action: "noop" } : { action: "update" };
|
|
3633
3780
|
}),
|
|
3634
|
-
reconcile:
|
|
3781
|
+
reconcile: Effect44.fn(function* ({ news }) {
|
|
3635
3782
|
const before = dropUnreadable4(yield* readInterface(news));
|
|
3636
3783
|
yield* guardWrite2(NODE_NETWORK_CREATE, createForm5(news), before === undefined);
|
|
3637
3784
|
yield* guardWrite2(NODE_NETWORK_UPDATE, updateForm2(news), false);
|
|
@@ -3642,18 +3789,22 @@ var ProxmoxNodeNetworkProvider = () => Provider16.effect(ProxmoxNodeNetwork, Eff
|
|
|
3642
3789
|
}
|
|
3643
3790
|
const after = dropUnreadable4(yield* readInterface(news));
|
|
3644
3791
|
if (after === undefined) {
|
|
3645
|
-
return yield*
|
|
3792
|
+
return yield* Effect44.die(new Error(`nodes/${news.node}/network/${news.iface}: wrote with no error, but the ` + 'interface is still absent -- PVE wraps every answer in {"data":...} and can ' + "report success on a call that did nothing, so read back rather than trust it."));
|
|
3646
3793
|
}
|
|
3647
3794
|
return after;
|
|
3648
3795
|
}),
|
|
3649
|
-
delete:
|
|
3796
|
+
delete: Effect44.fn(function* ({ olds }) {
|
|
3650
3797
|
yield* runPve(olds.target, "provision", true, nodes9.deleteNodeNetwork2({ iface: olds.iface, node: olds.node }));
|
|
3651
3798
|
})
|
|
3652
3799
|
})));
|
|
3653
3800
|
// src/proxmox/notification-matcher.ts
|
|
3654
3801
|
import { Resource as Resource17 } from "alchemy";
|
|
3655
3802
|
import * as Provider17 from "alchemy/Provider";
|
|
3656
|
-
import * as
|
|
3803
|
+
import * as Effect48 from "effect/Effect";
|
|
3804
|
+
|
|
3805
|
+
// src/proxmox/notification-matcher-lifecycle.ts
|
|
3806
|
+
import { isResolved as isResolved15 } from "alchemy/Diff";
|
|
3807
|
+
import * as Effect47 from "effect/Effect";
|
|
3657
3808
|
|
|
3658
3809
|
// src/proxmox/notification-matcher-form.ts
|
|
3659
3810
|
var list = (value) => Array.isArray(value) ? value.map((item) => String(item)) : [];
|
|
@@ -3729,25 +3880,99 @@ var matcherUpdateForm = (fields) => {
|
|
|
3729
3880
|
};
|
|
3730
3881
|
};
|
|
3731
3882
|
|
|
3883
|
+
// src/proxmox/notification-matcher-distilled.ts
|
|
3884
|
+
import * as cluster5 from "@distilled.cloud/proxmox/cluster";
|
|
3885
|
+
import * as Effect46 from "effect/Effect";
|
|
3886
|
+
|
|
3887
|
+
// src/proxmox/notification-sdk-input.ts
|
|
3888
|
+
import { ProxmoxParseError as ProxmoxParseError3 } from "@distilled.cloud/proxmox/Errors";
|
|
3889
|
+
import * as Data2 from "effect/Data";
|
|
3890
|
+
import * as Effect45 from "effect/Effect";
|
|
3891
|
+
import * as Schema3 from "effect/Schema";
|
|
3892
|
+
|
|
3893
|
+
class NotificationRequestInvalid extends Data2.TaggedError("NotificationRequestInvalid") {
|
|
3894
|
+
}
|
|
3895
|
+
var notificationRequest = (schema, fields) => Schema3.decodeUnknownEffect(Schema3.toType(schema), { onExcessProperty: "error" })(Object.fromEntries(Object.entries(fields).map(([key, value]) => [key.replaceAll("-", "_"), value]))).pipe(Effect45.mapError(() => new NotificationRequestInvalid({ operation: "PVE notification write" })));
|
|
3896
|
+
var notificationResponse = (schema) => (body5) => Schema3.decodeUnknownEffect(Schema3.toType(schema))(body5).pipe(Effect45.as(body5), Effect45.mapError(() => new ProxmoxParseError3({
|
|
3897
|
+
body: undefined,
|
|
3898
|
+
cause: "PVE notification response does not match its SDK schema"
|
|
3899
|
+
})));
|
|
3900
|
+
|
|
3901
|
+
// src/proxmox/notification-matcher-distilled.ts
|
|
3902
|
+
var readMatcher = (props) => runPve(props.target, "read", false, cluster5.getClusterNotificationMatcher({ name: props.name })).pipe(Effect46.flatMap(notificationResponse(cluster5.GetClusterNotificationMatcherResponse)), Effect46.map((live) => matcherAttributes({
|
|
3903
|
+
...live,
|
|
3904
|
+
"invert-match": live.invert_match,
|
|
3905
|
+
"match-calendar": live.match_calendar,
|
|
3906
|
+
"match-field": live.match_field,
|
|
3907
|
+
"match-severity": live.match_severity
|
|
3908
|
+
}, props.name)), Effect46.catchTag("NotFound", () => Effect46.succeed(undefined)));
|
|
3909
|
+
var createMatcher = (props) => Effect46.gen(function* () {
|
|
3910
|
+
const input = yield* notificationRequest(cluster5.CreateClusterNotificationMatcherRequest, matcherCreateForm(props));
|
|
3911
|
+
yield* runPve(props.target, "provision", true, cluster5.createClusterNotificationMatcher(input));
|
|
3912
|
+
});
|
|
3913
|
+
var updateMatcher = (props) => Effect46.gen(function* () {
|
|
3914
|
+
const input = yield* notificationRequest(cluster5.PutClusterNotificationMatcherRequest, {
|
|
3915
|
+
...matcherUpdateForm(props),
|
|
3916
|
+
name: props.name
|
|
3917
|
+
});
|
|
3918
|
+
yield* runPve(props.target, "provision", true, cluster5.putClusterNotificationMatcher(input));
|
|
3919
|
+
});
|
|
3920
|
+
var deleteMatcher = (props) => runPve(props.target, "provision", true, cluster5.deleteClusterNotificationMatcher({ name: props.name })).pipe(Effect46.catchTag("NotFound", () => Effect46.void));
|
|
3921
|
+
|
|
3922
|
+
// src/proxmox/notification-matcher-lifecycle.ts
|
|
3923
|
+
var guard = (props, create) => guardForm("pve:POST /cluster/notifications/matchers", matcherCreateForm(props), create).pipe(Effect47.andThen(guardForm("pve:PUT /cluster/notifications/matchers/{name}", matcherUpdateForm(props), false)));
|
|
3924
|
+
var matcherHandlers = {
|
|
3925
|
+
list: () => Effect47.succeed([]),
|
|
3926
|
+
read: ({ olds }) => Effect47.gen(function* () {
|
|
3927
|
+
const live = yield* readMatcher(olds);
|
|
3928
|
+
yield* guard(olds, live === undefined);
|
|
3929
|
+
return live;
|
|
3930
|
+
}),
|
|
3931
|
+
diff: ({
|
|
3932
|
+
news,
|
|
3933
|
+
olds,
|
|
3934
|
+
output
|
|
3935
|
+
}) => Effect47.gen(function* () {
|
|
3936
|
+
if (!isResolved15(news))
|
|
3937
|
+
return;
|
|
3938
|
+
if (olds.name !== news.name)
|
|
3939
|
+
return { action: "replace" };
|
|
3940
|
+
yield* guard(news, output === undefined);
|
|
3941
|
+
if (output === undefined)
|
|
3942
|
+
return;
|
|
3943
|
+
const live = yield* readMatcher(news);
|
|
3944
|
+
if (live === undefined) {
|
|
3945
|
+
yield* guard(news, true);
|
|
3946
|
+
return { action: "update" };
|
|
3947
|
+
}
|
|
3948
|
+
return { action: matcherMatches(live, news) ? "noop" : "update" };
|
|
3949
|
+
}),
|
|
3950
|
+
reconcile: ({ news }) => Effect47.gen(function* () {
|
|
3951
|
+
const live = yield* readMatcher(news);
|
|
3952
|
+
yield* guard(news, live === undefined);
|
|
3953
|
+
if (live === undefined)
|
|
3954
|
+
yield* createMatcher(news);
|
|
3955
|
+
else if (!matcherMatches(live, news))
|
|
3956
|
+
yield* updateMatcher(news);
|
|
3957
|
+
const after = yield* readMatcher(news);
|
|
3958
|
+
if (after === undefined)
|
|
3959
|
+
return yield* Effect47.die(new Error(`Notification matcher ${news.name}: write returned but read-back is absent.`));
|
|
3960
|
+
return after;
|
|
3961
|
+
}),
|
|
3962
|
+
delete: ({ olds }) => deleteMatcher(olds)
|
|
3963
|
+
};
|
|
3964
|
+
|
|
3732
3965
|
// src/proxmox/notification-matcher.ts
|
|
3733
3966
|
var ProxmoxNotificationMatcher = Resource17("Proxmox.NotificationMatcher");
|
|
3734
|
-
var
|
|
3735
|
-
attributes: (live, props) => matcherAttributes(live, props.name),
|
|
3736
|
-
collection: () => "cluster/notifications/matchers",
|
|
3737
|
-
createForm: matcherCreateForm,
|
|
3738
|
-
endpoint: {
|
|
3739
|
-
create: "pve:POST /cluster/notifications/matchers",
|
|
3740
|
-
update: "pve:PUT /cluster/notifications/matchers/{name}"
|
|
3741
|
-
},
|
|
3742
|
-
matches: matcherMatches,
|
|
3743
|
-
path: (props) => `cluster/notifications/matchers/${props.name}`,
|
|
3744
|
-
updateForm: matcherUpdateForm
|
|
3745
|
-
});
|
|
3746
|
-
var ProxmoxNotificationMatcherProvider = () => Provider17.effect(ProxmoxNotificationMatcher, Effect44.succeed(ProxmoxNotificationMatcher.Provider.of(handlers10)));
|
|
3967
|
+
var ProxmoxNotificationMatcherProvider = () => Provider17.effect(ProxmoxNotificationMatcher, Effect48.succeed(ProxmoxNotificationMatcher.Provider.of(matcherHandlers)));
|
|
3747
3968
|
// src/proxmox/notification-target.ts
|
|
3748
3969
|
import { Resource as Resource18 } from "alchemy";
|
|
3749
3970
|
import * as Provider18 from "alchemy/Provider";
|
|
3750
|
-
import * as
|
|
3971
|
+
import * as Effect51 from "effect/Effect";
|
|
3972
|
+
|
|
3973
|
+
// src/proxmox/notification-target-lifecycle.ts
|
|
3974
|
+
import { isResolved as isResolved16 } from "alchemy/Diff";
|
|
3975
|
+
import * as Effect50 from "effect/Effect";
|
|
3751
3976
|
|
|
3752
3977
|
// src/proxmox/notification-target-endpoint.ts
|
|
3753
3978
|
var ENDPOINTS = {
|
|
@@ -3801,9 +4026,8 @@ var createShape = (props) => ({
|
|
|
3801
4026
|
name: props.name
|
|
3802
4027
|
});
|
|
3803
4028
|
|
|
3804
|
-
// src/proxmox/notification-target.ts
|
|
3805
|
-
var
|
|
3806
|
-
var handlers11 = pveHandlers({
|
|
4029
|
+
// src/proxmox/notification-target-spec.ts
|
|
4030
|
+
var targetSpec = {
|
|
3807
4031
|
attributes: (live, props) => ({
|
|
3808
4032
|
author: text(live["author"]),
|
|
3809
4033
|
body: text(live["body"]),
|
|
@@ -3828,16 +4052,155 @@ var handlers11 = pveHandlers({
|
|
|
3828
4052
|
matches: (attributes, props) => attributes.disable === (props.disable === true) && (props.comment === undefined || attributes.comment === props.comment) && (props.server === undefined || attributes.server === props.server) && (props.port === undefined || attributes.port === props.port) && (props.mode === undefined || attributes.mode === props.mode) && (props.username === undefined || attributes.username === props.username) && (props["from-address"] === undefined || attributes["from-address"] === props["from-address"]) && (props.author === undefined || attributes.author === props.author) && (props.url === undefined || attributes.url === props.url) && (props.method === undefined || attributes.method === props.method) && (props.body === undefined || attributes.body === props.body) && (props.mailto === undefined || attributes.mailto === addressList(props.mailto)) && (props["mailto-user"] === undefined || attributes["mailto-user"] === addressList(props["mailto-user"])) && (props.header === undefined || attributes.header === headerList(props.header)),
|
|
3829
4053
|
path: (props) => `cluster/notifications/endpoints/${props.type}/${props.name}`,
|
|
3830
4054
|
updateForm: shape4
|
|
3831
|
-
}
|
|
3832
|
-
|
|
4055
|
+
};
|
|
4056
|
+
|
|
4057
|
+
// src/proxmox/notification-target-distilled.ts
|
|
4058
|
+
import * as cluster6 from "@distilled.cloud/proxmox/cluster";
|
|
4059
|
+
import * as Effect49 from "effect/Effect";
|
|
4060
|
+
var read3 = (props) => {
|
|
4061
|
+
const request = { name: props.name };
|
|
4062
|
+
switch (props.type) {
|
|
4063
|
+
case "gotify":
|
|
4064
|
+
return cluster6.getClusterNotificationEndpointGotify(request).pipe(Effect49.flatMap(notificationResponse(cluster6.GetClusterNotificationEndpointGotifyResponse)), Effect49.map((row) => ({ ...row })));
|
|
4065
|
+
case "sendmail":
|
|
4066
|
+
return cluster6.getClusterNotificationEndpointSendmail(request).pipe(Effect49.flatMap(notificationResponse(cluster6.GetClusterNotificationEndpointSendmailResponse)), Effect49.map((row) => ({
|
|
4067
|
+
...row,
|
|
4068
|
+
"from-address": row.from_address,
|
|
4069
|
+
"mailto-user": row.mailto_user
|
|
4070
|
+
})));
|
|
4071
|
+
case "smtp":
|
|
4072
|
+
return cluster6.getClusterNotificationEndpointSmtp(request).pipe(Effect49.flatMap(notificationResponse(cluster6.GetClusterNotificationEndpointSmtpResponse)), Effect49.map((row) => ({
|
|
4073
|
+
...row,
|
|
4074
|
+
"from-address": row.from_address,
|
|
4075
|
+
"mailto-user": row.mailto_user
|
|
4076
|
+
})));
|
|
4077
|
+
case "webhook":
|
|
4078
|
+
return cluster6.getClusterNotificationEndpointWebhook(request).pipe(Effect49.flatMap(notificationResponse(cluster6.GetClusterNotificationEndpointWebhookResponse)), Effect49.map((row) => ({ ...row })));
|
|
4079
|
+
}
|
|
4080
|
+
};
|
|
4081
|
+
var readTarget = (props) => runPve(props.target, "read", false, read3(props)).pipe(Effect49.map((row) => targetSpec.attributes(row, props)), Effect49.catchTag("NotFound", () => Effect49.succeed(undefined)));
|
|
4082
|
+
var fields = (props, create) => {
|
|
4083
|
+
const form = {
|
|
4084
|
+
...create ? createShape(props) : shape4(props),
|
|
4085
|
+
name: props.name
|
|
4086
|
+
};
|
|
4087
|
+
const cleared = [];
|
|
4088
|
+
for (const [key, values] of [
|
|
4089
|
+
["header", props.header === undefined ? undefined : [...props.header]],
|
|
4090
|
+
[
|
|
4091
|
+
"mailto",
|
|
4092
|
+
props.mailto === undefined ? undefined : addressList(props.mailto).split(",").filter(Boolean)
|
|
4093
|
+
],
|
|
4094
|
+
[
|
|
4095
|
+
"mailto-user",
|
|
4096
|
+
props["mailto-user"] === undefined ? undefined : addressList(props["mailto-user"]).split(",").filter(Boolean)
|
|
4097
|
+
]
|
|
4098
|
+
]) {
|
|
4099
|
+
if (values === undefined)
|
|
4100
|
+
continue;
|
|
4101
|
+
if (values.length > 0)
|
|
4102
|
+
form[key] = values;
|
|
4103
|
+
else {
|
|
4104
|
+
delete form[key];
|
|
4105
|
+
if (!create)
|
|
4106
|
+
cleared.push(key);
|
|
4107
|
+
}
|
|
4108
|
+
}
|
|
4109
|
+
if (cleared.length > 0)
|
|
4110
|
+
form.delete = cleared;
|
|
4111
|
+
return form;
|
|
4112
|
+
};
|
|
4113
|
+
var validateTargetWrite = (props, create) => {
|
|
4114
|
+
const schema = create ? {
|
|
4115
|
+
sendmail: cluster6.CreateClusterNotificationEndpointSendmailRequest,
|
|
4116
|
+
smtp: cluster6.CreateClusterNotificationEndpointSmtpRequest,
|
|
4117
|
+
gotify: cluster6.CreateClusterNotificationEndpointGotifyRequest,
|
|
4118
|
+
webhook: cluster6.CreateClusterNotificationEndpointWebhookRequest
|
|
4119
|
+
}[props.type] : {
|
|
4120
|
+
sendmail: cluster6.PutClusterNotificationEndpointSendmailRequest,
|
|
4121
|
+
smtp: cluster6.PutClusterNotificationEndpointSmtpRequest,
|
|
4122
|
+
gotify: cluster6.PutClusterNotificationEndpointGotifyRequest,
|
|
4123
|
+
webhook: cluster6.PutClusterNotificationEndpointWebhookRequest
|
|
4124
|
+
}[props.type];
|
|
4125
|
+
return notificationRequest(schema, fields(props, create)).pipe(Effect49.asVoid);
|
|
4126
|
+
};
|
|
4127
|
+
var createTarget = (props) => {
|
|
4128
|
+
const form = fields(props, true);
|
|
4129
|
+
const operation = props.type === "sendmail" ? notificationRequest(cluster6.CreateClusterNotificationEndpointSendmailRequest, form).pipe(Effect49.flatMap(cluster6.createClusterNotificationEndpointSendmail)) : props.type === "smtp" ? notificationRequest(cluster6.CreateClusterNotificationEndpointSmtpRequest, form).pipe(Effect49.flatMap(cluster6.createClusterNotificationEndpointSmtp)) : props.type === "gotify" ? notificationRequest(cluster6.CreateClusterNotificationEndpointGotifyRequest, form).pipe(Effect49.flatMap(cluster6.createClusterNotificationEndpointGotify)) : notificationRequest(cluster6.CreateClusterNotificationEndpointWebhookRequest, form).pipe(Effect49.flatMap(cluster6.createClusterNotificationEndpointWebhook));
|
|
4130
|
+
return runPve(props.target, "provision", true, operation);
|
|
4131
|
+
};
|
|
4132
|
+
var updateTarget = (props) => {
|
|
4133
|
+
const form = fields(props, false);
|
|
4134
|
+
const operation = props.type === "sendmail" ? notificationRequest(cluster6.PutClusterNotificationEndpointSendmailRequest, form).pipe(Effect49.flatMap(cluster6.putClusterNotificationEndpointSendmail)) : props.type === "smtp" ? notificationRequest(cluster6.PutClusterNotificationEndpointSmtpRequest, form).pipe(Effect49.flatMap(cluster6.putClusterNotificationEndpointSmtp)) : props.type === "gotify" ? notificationRequest(cluster6.PutClusterNotificationEndpointGotifyRequest, form).pipe(Effect49.flatMap(cluster6.putClusterNotificationEndpointGotify)) : notificationRequest(cluster6.PutClusterNotificationEndpointWebhookRequest, form).pipe(Effect49.flatMap(cluster6.putClusterNotificationEndpointWebhook));
|
|
4135
|
+
return runPve(props.target, "provision", true, operation);
|
|
4136
|
+
};
|
|
4137
|
+
var deleteTarget = (props) => {
|
|
4138
|
+
const request = { name: props.name };
|
|
4139
|
+
const operation = props.type === "sendmail" ? cluster6.deleteClusterNotificationEndpointSendmail(request) : props.type === "smtp" ? cluster6.deleteClusterNotificationEndpointSmtp(request) : props.type === "gotify" ? cluster6.deleteClusterNotificationEndpointGotify(request) : cluster6.deleteClusterNotificationEndpointWebhook(request);
|
|
4140
|
+
return runPve(props.target, "provision", true, operation).pipe(Effect49.catchTag("NotFound", () => Effect49.void));
|
|
4141
|
+
};
|
|
4142
|
+
|
|
4143
|
+
// src/proxmox/notification-target-lifecycle.ts
|
|
4144
|
+
var { guardCreate: guardCreate3, guardUpdate: guardUpdate3 } = specGuards(targetSpec);
|
|
4145
|
+
var guard2 = (props, create) => guardCreate3(props, create).pipe(Effect50.andThen(guardUpdate3(props)));
|
|
4146
|
+
var targetHandlers = {
|
|
4147
|
+
list: () => Effect50.succeed([]),
|
|
4148
|
+
read: ({ olds }) => Effect50.gen(function* () {
|
|
4149
|
+
const live = yield* readTarget(olds);
|
|
4150
|
+
yield* guard2(olds, live === undefined);
|
|
4151
|
+
return live;
|
|
4152
|
+
}),
|
|
4153
|
+
diff: ({
|
|
4154
|
+
news,
|
|
4155
|
+
olds,
|
|
4156
|
+
output
|
|
4157
|
+
}) => Effect50.gen(function* () {
|
|
4158
|
+
if (!isResolved16(news))
|
|
4159
|
+
return;
|
|
4160
|
+
if (olds.name !== news.name || olds.type !== news.type) {
|
|
4161
|
+
const destination = yield* readTarget(news);
|
|
4162
|
+
yield* guard2(news, destination === undefined);
|
|
4163
|
+
yield* validateTargetWrite(news, destination === undefined);
|
|
4164
|
+
return { action: "replace", deleteFirst: olds.name === news.name };
|
|
4165
|
+
}
|
|
4166
|
+
yield* guard2(news, output === undefined);
|
|
4167
|
+
if (output === undefined)
|
|
4168
|
+
return;
|
|
4169
|
+
const live = yield* readTarget(news);
|
|
4170
|
+
if (live === undefined) {
|
|
4171
|
+
yield* guard2(news, true);
|
|
4172
|
+
return { action: "update" };
|
|
4173
|
+
}
|
|
4174
|
+
return { action: targetSpec.matches(live, news) ? "noop" : "update" };
|
|
4175
|
+
}),
|
|
4176
|
+
reconcile: ({ news }) => Effect50.gen(function* () {
|
|
4177
|
+
const live = yield* readTarget(news);
|
|
4178
|
+
yield* guard2(news, live === undefined);
|
|
4179
|
+
if (live === undefined)
|
|
4180
|
+
yield* createTarget(news);
|
|
4181
|
+
else if (!targetSpec.matches(live, news))
|
|
4182
|
+
yield* updateTarget(news);
|
|
4183
|
+
const after = yield* readTarget(news);
|
|
4184
|
+
if (after === undefined)
|
|
4185
|
+
return yield* Effect50.die(new Error(`Notification target ${news.name}: write returned but read-back is absent.`));
|
|
4186
|
+
return after;
|
|
4187
|
+
}),
|
|
4188
|
+
delete: ({ olds }) => deleteTarget(olds)
|
|
4189
|
+
};
|
|
4190
|
+
|
|
4191
|
+
// src/proxmox/notification-target.ts
|
|
4192
|
+
var ProxmoxNotificationTarget = Resource18("Proxmox.NotificationTarget");
|
|
4193
|
+
var ProxmoxNotificationTargetProvider = () => Provider18.effect(ProxmoxNotificationTarget, Effect51.succeed(ProxmoxNotificationTarget.Provider.of(targetHandlers)));
|
|
3833
4194
|
// src/proxmox/pbs-datastore.ts
|
|
3834
4195
|
import { Resource as Resource19 } from "alchemy";
|
|
3835
|
-
import { isResolved as isResolved13 } from "alchemy/Diff";
|
|
3836
4196
|
import * as Provider19 from "alchemy/Provider";
|
|
3837
|
-
import * as
|
|
4197
|
+
import * as Effect57 from "effect/Effect";
|
|
4198
|
+
|
|
4199
|
+
// src/proxmox/pbs-datastore-lifecycle.ts
|
|
4200
|
+
import { isResolved as isResolved17 } from "alchemy/Diff";
|
|
4201
|
+
import * as Effect56 from "effect/Effect";
|
|
3838
4202
|
|
|
3839
4203
|
// src/proxmox/pbs-datastore-form.ts
|
|
3840
|
-
import * as Effect46 from "effect/Effect";
|
|
3841
4204
|
var attributes = (live, props) => ({
|
|
3842
4205
|
backend: propertyString(live["backend"]),
|
|
3843
4206
|
comment: text(live["comment"]),
|
|
@@ -3889,48 +4252,117 @@ var createForm6 = (props) => ({
|
|
|
3889
4252
|
});
|
|
3890
4253
|
var updateForm3 = (props) => mutable(props);
|
|
3891
4254
|
var object2 = (props) => `config/datastore/${props.name}`;
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
4255
|
+
|
|
4256
|
+
// src/proxmox/pbs-datastore-wire.ts
|
|
4257
|
+
import * as config from "@distilled.cloud/proxmox-backup/config";
|
|
4258
|
+
import * as Effect53 from "effect/Effect";
|
|
4259
|
+
|
|
4260
|
+
// src/proxmox/distilled-pbs.ts
|
|
4261
|
+
import { credentials } from "@distilled.cloud/proxmox-backup/Credentials";
|
|
4262
|
+
import * as Retry from "@distilled.cloud/proxmox-backup/Retry";
|
|
4263
|
+
import * as Effect52 from "effect/Effect";
|
|
4264
|
+
var runPbsWith = (target, credential, op, timeout5 = MEMBER_TIMEOUT) => op.pipe(Retry.none, Effect52.provide(credentials({
|
|
4265
|
+
baseUrl: target.api,
|
|
4266
|
+
secret: credential.secret,
|
|
4267
|
+
tokenId: credential.tokenId
|
|
4268
|
+
})), Effect52.timeout(timeout5));
|
|
4269
|
+
var runPbs = (target, role, op) => Effect52.gen(function* () {
|
|
4270
|
+
const credential = yield* leased(target, role);
|
|
4271
|
+
return yield* runPbsWith(target, credential, op);
|
|
4272
|
+
});
|
|
4273
|
+
|
|
4274
|
+
// src/proxmox/pbs-datastore-wire.ts
|
|
4275
|
+
var members = (form) => ({
|
|
4276
|
+
...form["comment"] === undefined ? {} : { comment: form["comment"] },
|
|
4277
|
+
...form["counter-reset-schedule"] === undefined ? {} : { counter_reset_schedule: form["counter-reset-schedule"] },
|
|
4278
|
+
...form["gc-schedule"] === undefined ? {} : { gc_schedule: form["gc-schedule"] },
|
|
4279
|
+
...form["keep-daily"] === undefined ? {} : { keep_daily: form["keep-daily"] },
|
|
4280
|
+
...form["keep-hourly"] === undefined ? {} : { keep_hourly: form["keep-hourly"] },
|
|
4281
|
+
...form["keep-last"] === undefined ? {} : { keep_last: form["keep-last"] },
|
|
4282
|
+
...form["keep-monthly"] === undefined ? {} : { keep_monthly: form["keep-monthly"] },
|
|
4283
|
+
...form["keep-weekly"] === undefined ? {} : { keep_weekly: form["keep-weekly"] },
|
|
4284
|
+
...form["keep-yearly"] === undefined ? {} : { keep_yearly: form["keep-yearly"] },
|
|
4285
|
+
...form["maintenance-mode"] === undefined ? {} : { maintenance_mode: form["maintenance-mode"] },
|
|
4286
|
+
...form["notification-thresholds"] === undefined ? {} : { notification_thresholds: form["notification-thresholds"] },
|
|
4287
|
+
...form["notify"] === undefined ? {} : { notify: form["notify"] },
|
|
4288
|
+
...form["notify-user"] === undefined ? {} : { notify_user: form["notify-user"] },
|
|
4289
|
+
...form["prune-schedule"] === undefined ? {} : { prune_schedule: form["prune-schedule"] },
|
|
4290
|
+
...form["tuning"] === undefined ? {} : { tuning: form["tuning"] },
|
|
4291
|
+
...form["verify-new"] === undefined ? {} : { verify_new: form["verify-new"] }
|
|
4292
|
+
});
|
|
4293
|
+
var createRequest = (props) => ({
|
|
4294
|
+
...members(createForm6(props)),
|
|
4295
|
+
name: props.name,
|
|
4296
|
+
path: props.path,
|
|
4297
|
+
...props.backend === undefined ? {} : { backend: props.backend }
|
|
4298
|
+
});
|
|
4299
|
+
var updateRequest = (props) => ({
|
|
4300
|
+
...members(updateForm3(props)),
|
|
4301
|
+
name: props.name
|
|
4302
|
+
});
|
|
4303
|
+
var wire = (row) => ({
|
|
4304
|
+
...row,
|
|
4305
|
+
"counter-reset-schedule": row.counter_reset_schedule,
|
|
4306
|
+
"gc-schedule": row.gc_schedule,
|
|
4307
|
+
"keep-daily": row.keep_daily,
|
|
4308
|
+
"keep-hourly": row.keep_hourly,
|
|
4309
|
+
"keep-last": row.keep_last,
|
|
4310
|
+
"keep-monthly": row.keep_monthly,
|
|
4311
|
+
"keep-weekly": row.keep_weekly,
|
|
4312
|
+
"keep-yearly": row.keep_yearly,
|
|
4313
|
+
"maintenance-mode": row.maintenance_mode,
|
|
4314
|
+
"notification-thresholds": row.notification_thresholds,
|
|
4315
|
+
"notify-user": row.notify_user,
|
|
4316
|
+
"prune-schedule": row.prune_schedule,
|
|
4317
|
+
"verify-new": row.verify_new
|
|
4318
|
+
});
|
|
4319
|
+
var readOne = (props) => runPbs(props.target, "read", config.getConfigDatastore({ name: props.name })).pipe(Effect53.map((live) => attributes(wire(live), props)), Effect53.catchTag("DatastoreNotFound", () => Effect53.succeed(undefined)));
|
|
4320
|
+
var createOne = (props) => runPbs(props.target, "provision", config.createConfigDatastore(createRequest(props)));
|
|
4321
|
+
var updateOne = (props) => runPbs(props.target, "provision", config.putConfigDatastore(updateRequest(props)));
|
|
4322
|
+
var deleteOne = (props) => Effect53.gen(function* () {
|
|
4323
|
+
if ((yield* readOne(props)) === undefined)
|
|
4324
|
+
return;
|
|
4325
|
+
return yield* runPbs(props.target, "provision", config.deleteConfigDatastore({ name: props.name }));
|
|
4326
|
+
});
|
|
3895
4327
|
|
|
3896
4328
|
// src/proxmox/pbs-datastore-guard.ts
|
|
3897
|
-
import * as
|
|
4329
|
+
import * as Effect54 from "effect/Effect";
|
|
3898
4330
|
var samePath = (left, right) => {
|
|
3899
4331
|
const trim = (value) => value.length > 1 ? value.replace(/\/+$/, "") : value;
|
|
3900
4332
|
return trim(left) === trim(right);
|
|
3901
4333
|
};
|
|
3902
|
-
var guardPath = (live, props) => live.path === "" || samePath(live.path, props.path) ?
|
|
3903
|
-
var guardBackend = (live, props) => props.backend === undefined || propertyString(props.backend) === live.backend ?
|
|
3904
|
-
var
|
|
4334
|
+
var guardPath = (live, props) => live.path === "" || samePath(live.path, props.path) ? Effect54.void : Effect54.die(new Error(`${object2(props)}: declared path ${props.path} but PBS holds ${live.path}. \`path\` ` + "is create-only, so this cannot be reconciled and will NOT be planned as a silent " + "noop: backups would keep landing in the old directory behind a green plan. Correct " + "the declaration, or remove the datastore and declare a new one -- the contents live, " + "because `destroy-data` is never sent."));
|
|
4335
|
+
var guardBackend = (live, props) => props.backend === undefined || propertyString(props.backend) === live.backend ? Effect54.void : Effect54.die(new Error(`${object2(props)}: declared backend ${props.backend} but PBS holds ` + `${live.backend === "" ? "(none — a local datastore)" : live.backend}. \`backend\` ` + "is create-only: PBS has no update for it, so this cannot be reconciled and will NOT " + "be planned as a silent noop. Correct the declaration to match what is really there, " + "or remove the datastore and declare a new one -- the contents live, because " + "`destroy-data` is never sent."));
|
|
4336
|
+
var settle2 = (read4, done) => Effect54.gen(function* () {
|
|
3905
4337
|
for (let attempt = 0;attempt < 60; attempt += 1) {
|
|
3906
|
-
const live = yield*
|
|
4338
|
+
const live = yield* read4;
|
|
3907
4339
|
if (done(live))
|
|
3908
4340
|
return live;
|
|
3909
|
-
yield*
|
|
4341
|
+
yield* Effect54.sleep("2 seconds");
|
|
3910
4342
|
}
|
|
3911
|
-
return yield*
|
|
4343
|
+
return yield* read4;
|
|
3912
4344
|
});
|
|
3913
4345
|
|
|
3914
4346
|
// src/proxmox/pbs-datastore-endpoint.ts
|
|
3915
|
-
import * as
|
|
4347
|
+
import * as Effect55 from "effect/Effect";
|
|
3916
4348
|
var DATASTORE_ENDPOINT = {
|
|
3917
4349
|
create: "pbs:POST /config/datastore",
|
|
3918
4350
|
update: "pbs:PUT /config/datastore/{name}"
|
|
3919
4351
|
};
|
|
3920
|
-
var guardDatastoreForms = (props) =>
|
|
4352
|
+
var guardDatastoreForms = (props) => Effect55.gen(function* () {
|
|
3921
4353
|
yield* guardForm(DATASTORE_ENDPOINT.create, createForm6(props), true);
|
|
3922
4354
|
yield* guardForm(DATASTORE_ENDPOINT.update, updateForm3(props), false);
|
|
3923
4355
|
});
|
|
3924
4356
|
|
|
3925
|
-
// src/proxmox/pbs-datastore.ts
|
|
3926
|
-
var
|
|
3927
|
-
|
|
3928
|
-
});
|
|
3929
|
-
var PbsDatastoreProvider = () => Provider19.effect(PbsDatastore, Effect49.succeed(PbsDatastore.Provider.of({
|
|
3930
|
-
list: () => Effect49.succeed([]),
|
|
4357
|
+
// src/proxmox/pbs-datastore-lifecycle.ts
|
|
4358
|
+
var handlers8 = {
|
|
4359
|
+
list: () => Effect56.succeed([]),
|
|
3931
4360
|
read: ({ olds }) => readOne(olds),
|
|
3932
|
-
diff: ({
|
|
3933
|
-
|
|
4361
|
+
diff: ({
|
|
4362
|
+
news,
|
|
4363
|
+
output
|
|
4364
|
+
}) => Effect56.gen(function* () {
|
|
4365
|
+
if (!isResolved17(news))
|
|
3934
4366
|
return;
|
|
3935
4367
|
yield* guardDatastoreForms(news);
|
|
3936
4368
|
if (output === undefined)
|
|
@@ -3942,106 +4374,135 @@ var PbsDatastoreProvider = () => Provider19.effect(PbsDatastore, Effect49.succee
|
|
|
3942
4374
|
yield* guardBackend(live, news);
|
|
3943
4375
|
return matches6(live, news) ? { action: "noop" } : { action: "update" };
|
|
3944
4376
|
}),
|
|
3945
|
-
reconcile:
|
|
4377
|
+
reconcile: Effect56.fn(function* ({ news }) {
|
|
3946
4378
|
yield* guardDatastoreForms(news);
|
|
3947
4379
|
const live = yield* readOne(news);
|
|
3948
|
-
let upid;
|
|
3949
4380
|
if (live === undefined) {
|
|
3950
|
-
|
|
4381
|
+
yield* createOne(news);
|
|
3951
4382
|
} else {
|
|
3952
4383
|
yield* guardPath(live, news);
|
|
3953
4384
|
yield* guardBackend(live, news);
|
|
3954
4385
|
const form = formToSend(matches6, live, news, updateForm3(news));
|
|
3955
4386
|
if (form !== undefined) {
|
|
3956
|
-
yield*
|
|
4387
|
+
yield* updateOne(news);
|
|
3957
4388
|
}
|
|
3958
4389
|
}
|
|
3959
|
-
const after = yield*
|
|
4390
|
+
const after = yield* settle2(readOne(news), (row) => row !== undefined && matches6(row, news));
|
|
3960
4391
|
if (after === undefined || !matches6(after, news)) {
|
|
3961
|
-
return yield*
|
|
4392
|
+
return yield* Effect56.die(new Error(`${object2(news)}: the write returned no error but the datastore still does not ` + "match the declaration after 120s. Check the PBS task list " + "-- read it with `proxmox-backup-manager task log <upid>`. A slow disk is a " + "plausible cause (see `settle`); a create over an existing chunk store is not, " + "because PBS refuses that outright rather than timing out."));
|
|
3962
4393
|
}
|
|
3963
4394
|
return after;
|
|
3964
4395
|
}),
|
|
3965
|
-
delete:
|
|
3966
|
-
yield*
|
|
3967
|
-
const left = yield*
|
|
4396
|
+
delete: Effect56.fn(function* ({ olds }) {
|
|
4397
|
+
yield* deleteOne(olds);
|
|
4398
|
+
const left = yield* settle2(readOne(olds), (row) => row === undefined);
|
|
3968
4399
|
if (left !== undefined) {
|
|
3969
|
-
return yield*
|
|
4400
|
+
return yield* Effect56.die(new Error(`${object2(olds)}: the DELETE returned no error but the datastore is still in ` + "datastore.cfg after 120s. Either a backup, verify or GC task still holds it, " + "or the provision role lacks Datastore.Allocate on /datastore/" + `${olds.name}. Read the task log with \`proxmox-backup-manager task log <upid>\`.`));
|
|
3970
4401
|
}
|
|
3971
4402
|
})
|
|
3972
|
-
}
|
|
4403
|
+
};
|
|
4404
|
+
|
|
4405
|
+
// src/proxmox/pbs-datastore.ts
|
|
4406
|
+
var PbsDatastore = Resource19("Pbs.Datastore", {
|
|
4407
|
+
defaultRemovalPolicy: "retain"
|
|
4408
|
+
});
|
|
4409
|
+
var PbsDatastoreProvider = () => Provider19.effect(PbsDatastore, Effect57.succeed(PbsDatastore.Provider.of(handlers8)));
|
|
3973
4410
|
// src/proxmox/pbs-notification-matcher.ts
|
|
3974
4411
|
import { Resource as Resource20 } from "alchemy";
|
|
3975
4412
|
import * as Provider20 from "alchemy/Provider";
|
|
3976
|
-
import
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
4413
|
+
import { isResolved as isResolved18 } from "alchemy/Diff";
|
|
4414
|
+
import * as Effect60 from "effect/Effect";
|
|
4415
|
+
|
|
4416
|
+
// src/proxmox/pbs-notification-matcher-distilled.ts
|
|
4417
|
+
import * as config2 from "@distilled.cloud/proxmox-backup/config";
|
|
4418
|
+
import * as Effect59 from "effect/Effect";
|
|
4419
|
+
|
|
4420
|
+
// src/proxmox/pbs-notification-input.ts
|
|
4421
|
+
import * as Data3 from "effect/Data";
|
|
4422
|
+
import * as Effect58 from "effect/Effect";
|
|
4423
|
+
import * as Schema4 from "effect/Schema";
|
|
4424
|
+
|
|
4425
|
+
class PbsNotificationInputRefusal extends Data3.TaggedError("PbsNotificationInputRefusal") {
|
|
4426
|
+
}
|
|
4427
|
+
var notificationInput = (schema, fields2) => Schema4.decodeUnknownEffect(Schema4.toType(schema))(fields2).pipe(Effect58.mapError(() => new PbsNotificationInputRefusal({
|
|
4428
|
+
message: "PBS notification fields do not match the SDK request schema; no PBS request was sent."
|
|
4429
|
+
})));
|
|
4430
|
+
|
|
4431
|
+
// src/proxmox/pbs-notification-matcher-distilled.ts
|
|
4432
|
+
var readMatcher2 = (props) => runPbs(props.target, "read", config2.getConfigNotificationMatcher({ name: props.name })).pipe(Effect59.map((row) => matcherAttributes({
|
|
4433
|
+
...row,
|
|
4434
|
+
"invert-match": row.invert_match,
|
|
4435
|
+
"match-calendar": row.match_calendar,
|
|
4436
|
+
"match-field": row.match_field,
|
|
4437
|
+
"match-severity": row.match_severity
|
|
4438
|
+
}, props.name)), Effect59.catchTag("NotFound", () => Effect59.succeed(undefined)));
|
|
4439
|
+
var requestFields = (props, form) => ({
|
|
4440
|
+
...form,
|
|
4441
|
+
invert_match: form["invert-match"],
|
|
4442
|
+
match_calendar: form["match-calendar"],
|
|
4443
|
+
match_field: form["match-field"],
|
|
4444
|
+
match_severity: form["match-severity"],
|
|
4445
|
+
name: props.name
|
|
3989
4446
|
});
|
|
3990
|
-
var
|
|
4447
|
+
var createMatcher2 = (props, form) => runPbs(props.target, "provision", notificationInput(config2.CreateConfigNotificationMatcherRequest, requestFields(props, form)).pipe(Effect59.flatMap(config2.createConfigNotificationMatcher)));
|
|
4448
|
+
var updateMatcher2 = (props, form) => runPbs(props.target, "provision", notificationInput(config2.PutConfigNotificationMatcherRequest, requestFields(props, form)).pipe(Effect59.flatMap(config2.putConfigNotificationMatcher)));
|
|
4449
|
+
var deleteMatcher2 = (props) => runPbs(props.target, "provision", config2.deleteConfigNotificationMatcher({ name: props.name })).pipe(Effect59.catchTag("NotFound", () => Effect59.void));
|
|
4450
|
+
|
|
4451
|
+
// src/proxmox/pbs-notification-matcher.ts
|
|
4452
|
+
var PbsNotificationMatcher = Resource20("Pbs.NotificationMatcher");
|
|
4453
|
+
var guardCreate4 = (props, requirePresence) => guardForm("pbs:POST /config/notifications/matchers", matcherCreateForm(props), requirePresence);
|
|
4454
|
+
var guardUpdate4 = (props) => guardForm("pbs:PUT /config/notifications/matchers/{name}", matcherUpdateForm(props), false);
|
|
4455
|
+
var handlers9 = {
|
|
4456
|
+
list: () => Effect60.succeed([]),
|
|
4457
|
+
read: ({ olds }) => readMatcher2(olds),
|
|
4458
|
+
diff: ({
|
|
4459
|
+
news,
|
|
4460
|
+
olds,
|
|
4461
|
+
output
|
|
4462
|
+
}) => Effect60.gen(function* () {
|
|
4463
|
+
if (!isResolved18(news))
|
|
4464
|
+
return;
|
|
4465
|
+
yield* guardCreate4(news, output === undefined);
|
|
4466
|
+
yield* guardUpdate4(news);
|
|
4467
|
+
if (output === undefined)
|
|
4468
|
+
return;
|
|
4469
|
+
if (olds.name !== news.name)
|
|
4470
|
+
return { action: "replace" };
|
|
4471
|
+
const live = yield* readMatcher2(news);
|
|
4472
|
+
if (live === undefined) {
|
|
4473
|
+
yield* guardCreate4(news, true);
|
|
4474
|
+
return { action: "update" };
|
|
4475
|
+
}
|
|
4476
|
+
return { action: matcherMatches(live, news) ? "noop" : "update" };
|
|
4477
|
+
}),
|
|
4478
|
+
reconcile: ({ news }) => Effect60.gen(function* () {
|
|
4479
|
+
const live = yield* readMatcher2(news);
|
|
4480
|
+
yield* guardCreate4(news, live === undefined);
|
|
4481
|
+
yield* guardUpdate4(news);
|
|
4482
|
+
if (live === undefined)
|
|
4483
|
+
yield* createMatcher2(news, matcherCreateForm(news));
|
|
4484
|
+
else if (!matcherMatches(live, news))
|
|
4485
|
+
yield* updateMatcher2(news, matcherUpdateForm(news));
|
|
4486
|
+
const after = yield* readMatcher2(news);
|
|
4487
|
+
if (after === undefined)
|
|
4488
|
+
return yield* Effect60.fail(new Error(`config/notifications/matchers/${news.name}: the write returned no error but the matcher is still absent.`));
|
|
4489
|
+
return after;
|
|
4490
|
+
}),
|
|
4491
|
+
delete: ({ olds }) => deleteMatcher2(olds)
|
|
4492
|
+
};
|
|
4493
|
+
var PbsNotificationMatcherProvider = () => Provider20.effect(PbsNotificationMatcher, Effect60.succeed(PbsNotificationMatcher.Provider.of(handlers9)));
|
|
3991
4494
|
// src/proxmox/pbs-notification-target.ts
|
|
3992
4495
|
import { Resource as Resource21 } from "alchemy";
|
|
3993
4496
|
import * as Provider21 from "alchemy/Provider";
|
|
3994
|
-
import * as
|
|
4497
|
+
import * as Effect63 from "effect/Effect";
|
|
3995
4498
|
|
|
3996
4499
|
// src/proxmox/pbs-notification-target-lifecycle.ts
|
|
3997
|
-
import { isResolved as
|
|
3998
|
-
import * as
|
|
3999
|
-
|
|
4000
|
-
// src/proxmox/pbs-notification-target-endpoint.ts
|
|
4001
|
-
var ENDPOINTS2 = {
|
|
4002
|
-
sendmail: {
|
|
4003
|
-
create: "pbs:POST /config/notifications/endpoints/sendmail",
|
|
4004
|
-
update: "pbs:PUT /config/notifications/endpoints/sendmail/{name}"
|
|
4005
|
-
},
|
|
4006
|
-
smtp: {
|
|
4007
|
-
create: "pbs:POST /config/notifications/endpoints/smtp",
|
|
4008
|
-
update: "pbs:PUT /config/notifications/endpoints/smtp/{name}"
|
|
4009
|
-
},
|
|
4010
|
-
webhook: {
|
|
4011
|
-
create: "pbs:POST /config/notifications/endpoints/webhook",
|
|
4012
|
-
update: "pbs:PUT /config/notifications/endpoints/webhook/{name}"
|
|
4013
|
-
}
|
|
4014
|
-
};
|
|
4015
|
-
var pbsTargetEndpoint = (props) => ENDPOINTS2[props.type];
|
|
4500
|
+
import { isResolved as isResolved19 } from "alchemy/Diff";
|
|
4501
|
+
import * as Effect62 from "effect/Effect";
|
|
4016
4502
|
|
|
4017
|
-
// src/proxmox/
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
var CREDENTIAL_HEADER = /^(proxy-authorization|authorization|cookie)$/i;
|
|
4021
|
-
var isCredentialName = (name) => CREDENTIAL_HEADER.test(name) || CREDENTIAL_WORD.test(name);
|
|
4022
|
-
var literal2 = (value) => value !== "" && !READS_SECRET.test(value);
|
|
4023
|
-
var literalCredentialHeaders = (header) => Object.entries(header ?? {}).filter(([name, value]) => typeof value === "string" && isCredentialName(name)).filter(([, value]) => literal2(value)).map(([name]) => name).sort();
|
|
4024
|
-
var literalCredentialsInUrl = (url) => {
|
|
4025
|
-
if (url === undefined)
|
|
4026
|
-
return [];
|
|
4027
|
-
const out = [];
|
|
4028
|
-
const authority = /^[a-z][a-z0-9+.-]*:\/\/([^/?#]*)/i.exec(url)?.[1] ?? "";
|
|
4029
|
-
const at = authority.lastIndexOf("@");
|
|
4030
|
-
if (at >= 0) {
|
|
4031
|
-
const userinfo = authority.slice(0, at);
|
|
4032
|
-
const colon = userinfo.indexOf(":");
|
|
4033
|
-
if (colon >= 0 && literal2(userinfo.slice(colon + 1)))
|
|
4034
|
-
out.push("userinfo");
|
|
4035
|
-
}
|
|
4036
|
-
const params = url.replace(/^[^?#]*/, "");
|
|
4037
|
-
for (const pair of params.split(/[?&#]/)) {
|
|
4038
|
-
const eq = pair.indexOf("=");
|
|
4039
|
-
const name = eq < 0 ? pair : pair.slice(0, eq);
|
|
4040
|
-
if (eq >= 0 && isCredentialName(name) && literal2(pair.slice(eq + 1)))
|
|
4041
|
-
out.push(`?${name}`);
|
|
4042
|
-
}
|
|
4043
|
-
return out;
|
|
4044
|
-
};
|
|
4503
|
+
// src/proxmox/pbs-notification-target-distilled.ts
|
|
4504
|
+
import * as config3 from "@distilled.cloud/proxmox-backup/config";
|
|
4505
|
+
import * as Effect61 from "effect/Effect";
|
|
4045
4506
|
|
|
4046
4507
|
// src/proxmox/pbs-notification-target-wire.ts
|
|
4047
4508
|
import { Buffer as Buffer2 } from "node:buffer";
|
|
@@ -4132,6 +4593,106 @@ var targetAttributes = (live, props) => {
|
|
|
4132
4593
|
};
|
|
4133
4594
|
};
|
|
4134
4595
|
|
|
4596
|
+
// src/proxmox/pbs-notification-target-distilled.ts
|
|
4597
|
+
var requestFields2 = (props, form) => ({
|
|
4598
|
+
...form,
|
|
4599
|
+
from_address: form["from-address"],
|
|
4600
|
+
mailto_user: form["mailto-user"],
|
|
4601
|
+
name: props.name
|
|
4602
|
+
});
|
|
4603
|
+
var read4 = (props) => {
|
|
4604
|
+
switch (props.type) {
|
|
4605
|
+
case "sendmail":
|
|
4606
|
+
return config3.getConfigNotificationEndpointSendmail({ name: props.name }).pipe(Effect61.map((row) => ({
|
|
4607
|
+
...row,
|
|
4608
|
+
"from-address": row.from_address,
|
|
4609
|
+
"mailto-user": row.mailto_user
|
|
4610
|
+
})));
|
|
4611
|
+
case "smtp":
|
|
4612
|
+
return config3.getConfigNotificationEndpointSmtp({ name: props.name }).pipe(Effect61.map((row) => ({
|
|
4613
|
+
...row,
|
|
4614
|
+
"from-address": row.from_address,
|
|
4615
|
+
"mailto-user": row.mailto_user
|
|
4616
|
+
})));
|
|
4617
|
+
case "webhook":
|
|
4618
|
+
return config3.getConfigNotificationEndpointWebhook({ name: props.name }).pipe(Effect61.map((row) => ({ ...row })));
|
|
4619
|
+
}
|
|
4620
|
+
};
|
|
4621
|
+
var readTarget2 = (props) => runPbs(props.target, "read", read4(props)).pipe(Effect61.map((row) => targetAttributes(row, props)), Effect61.catchTag("NotFound", () => Effect61.succeed(undefined)));
|
|
4622
|
+
var validateTargetWrite2 = (props, form, mode) => {
|
|
4623
|
+
const schema = mode === "create" ? {
|
|
4624
|
+
sendmail: config3.CreateConfigNotificationEndpointSendmailRequest,
|
|
4625
|
+
smtp: config3.CreateConfigNotificationEndpointSmtpRequest,
|
|
4626
|
+
webhook: config3.CreateConfigNotificationEndpointWebhookRequest
|
|
4627
|
+
}[props.type] : {
|
|
4628
|
+
sendmail: config3.PutConfigNotificationEndpointSendmailRequest,
|
|
4629
|
+
smtp: config3.PutConfigNotificationEndpointSmtpRequest,
|
|
4630
|
+
webhook: config3.PutConfigNotificationEndpointWebhookRequest
|
|
4631
|
+
}[props.type];
|
|
4632
|
+
return notificationInput(schema, requestFields2(props, form)).pipe(Effect61.asVoid);
|
|
4633
|
+
};
|
|
4634
|
+
var createTarget2 = (props, form) => {
|
|
4635
|
+
const fields2 = requestFields2(props, form);
|
|
4636
|
+
const operation = props.type === "sendmail" ? notificationInput(config3.CreateConfigNotificationEndpointSendmailRequest, fields2).pipe(Effect61.flatMap(config3.createConfigNotificationEndpointSendmail)) : props.type === "smtp" ? notificationInput(config3.CreateConfigNotificationEndpointSmtpRequest, fields2).pipe(Effect61.flatMap(config3.createConfigNotificationEndpointSmtp)) : notificationInput(config3.CreateConfigNotificationEndpointWebhookRequest, fields2).pipe(Effect61.flatMap(config3.createConfigNotificationEndpointWebhook));
|
|
4637
|
+
return runPbs(props.target, "provision", operation);
|
|
4638
|
+
};
|
|
4639
|
+
var updateTarget2 = (props, form) => {
|
|
4640
|
+
const fields2 = requestFields2(props, form);
|
|
4641
|
+
const operation = props.type === "sendmail" ? notificationInput(config3.PutConfigNotificationEndpointSendmailRequest, fields2).pipe(Effect61.flatMap(config3.putConfigNotificationEndpointSendmail)) : props.type === "smtp" ? notificationInput(config3.PutConfigNotificationEndpointSmtpRequest, fields2).pipe(Effect61.flatMap(config3.putConfigNotificationEndpointSmtp)) : notificationInput(config3.PutConfigNotificationEndpointWebhookRequest, fields2).pipe(Effect61.flatMap(config3.putConfigNotificationEndpointWebhook));
|
|
4642
|
+
return runPbs(props.target, "provision", operation);
|
|
4643
|
+
};
|
|
4644
|
+
var deleteTarget2 = (props) => {
|
|
4645
|
+
const request = { name: props.name };
|
|
4646
|
+
const operation = props.type === "sendmail" ? config3.deleteConfigNotificationEndpointSendmail(request) : props.type === "smtp" ? config3.deleteConfigNotificationEndpointSmtp(request) : config3.deleteConfigNotificationEndpointWebhook(request);
|
|
4647
|
+
return runPbs(props.target, "provision", operation).pipe(Effect61.catchTag("NotFound", () => Effect61.void));
|
|
4648
|
+
};
|
|
4649
|
+
|
|
4650
|
+
// src/proxmox/pbs-notification-target-endpoint.ts
|
|
4651
|
+
var ENDPOINTS2 = {
|
|
4652
|
+
sendmail: {
|
|
4653
|
+
create: "pbs:POST /config/notifications/endpoints/sendmail",
|
|
4654
|
+
update: "pbs:PUT /config/notifications/endpoints/sendmail/{name}"
|
|
4655
|
+
},
|
|
4656
|
+
smtp: {
|
|
4657
|
+
create: "pbs:POST /config/notifications/endpoints/smtp",
|
|
4658
|
+
update: "pbs:PUT /config/notifications/endpoints/smtp/{name}"
|
|
4659
|
+
},
|
|
4660
|
+
webhook: {
|
|
4661
|
+
create: "pbs:POST /config/notifications/endpoints/webhook",
|
|
4662
|
+
update: "pbs:PUT /config/notifications/endpoints/webhook/{name}"
|
|
4663
|
+
}
|
|
4664
|
+
};
|
|
4665
|
+
var pbsTargetEndpoint = (props) => ENDPOINTS2[props.type];
|
|
4666
|
+
|
|
4667
|
+
// src/proxmox/credential-literals.ts
|
|
4668
|
+
var READS_SECRET = /\{\{[^}]*\bsecrets\./;
|
|
4669
|
+
var CREDENTIAL_WORD = /(^|[-_])(api[-_]?key|apikey|key|token|secret|password|passwd|pwd|pass|auth|signature|sig|code)($|[-_])/i;
|
|
4670
|
+
var CREDENTIAL_HEADER = /^(proxy-authorization|authorization|cookie)$/i;
|
|
4671
|
+
var isCredentialName = (name) => CREDENTIAL_HEADER.test(name) || CREDENTIAL_WORD.test(name);
|
|
4672
|
+
var literal2 = (value) => value !== "" && !READS_SECRET.test(value);
|
|
4673
|
+
var literalCredentialHeaders = (header) => Object.entries(header ?? {}).filter(([name, value]) => typeof value === "string" && isCredentialName(name)).filter(([, value]) => literal2(value)).map(([name]) => name).sort();
|
|
4674
|
+
var literalCredentialsInUrl = (url) => {
|
|
4675
|
+
if (url === undefined)
|
|
4676
|
+
return [];
|
|
4677
|
+
const out = [];
|
|
4678
|
+
const authority = /^[a-z][a-z0-9+.-]*:\/\/([^/?#]*)/i.exec(url)?.[1] ?? "";
|
|
4679
|
+
const at = authority.lastIndexOf("@");
|
|
4680
|
+
if (at >= 0) {
|
|
4681
|
+
const userinfo = authority.slice(0, at);
|
|
4682
|
+
const colon = userinfo.indexOf(":");
|
|
4683
|
+
if (colon >= 0 && literal2(userinfo.slice(colon + 1)))
|
|
4684
|
+
out.push("userinfo");
|
|
4685
|
+
}
|
|
4686
|
+
const params = url.replace(/^[^?#]*/, "");
|
|
4687
|
+
for (const pair of params.split(/[?&#]/)) {
|
|
4688
|
+
const eq = pair.indexOf("=");
|
|
4689
|
+
const name = eq < 0 ? pair : pair.slice(0, eq);
|
|
4690
|
+
if (eq >= 0 && isCredentialName(name) && literal2(pair.slice(eq + 1)))
|
|
4691
|
+
out.push(`?${name}`);
|
|
4692
|
+
}
|
|
4693
|
+
return out;
|
|
4694
|
+
};
|
|
4695
|
+
|
|
4135
4696
|
// src/proxmox/pbs-notification-target-form.ts
|
|
4136
4697
|
var OWNED = {
|
|
4137
4698
|
author: ["sendmail", "smtp"],
|
|
@@ -4272,12 +4833,9 @@ var targetForm = (props, groups, carry, mode) => {
|
|
|
4272
4833
|
|
|
4273
4834
|
// src/proxmox/pbs-notification-target-lifecycle.ts
|
|
4274
4835
|
var path = (props) => `config/notifications/endpoints/${props.type}/${props.name}`;
|
|
4275
|
-
var readLive2 = (props) => pve(props.target, "read", "GET", path(props)).pipe(Effect51.map((data) => data == null ? undefined : targetAttributes(data, props)), Effect51.orElseSucceed(() => {
|
|
4276
|
-
return;
|
|
4277
|
-
}));
|
|
4278
4836
|
var refuse2 = (props) => {
|
|
4279
4837
|
const reasons = refusals(props);
|
|
4280
|
-
return reasons.length === 0 ?
|
|
4838
|
+
return reasons.length === 0 ? Effect62.void : Effect62.die(new Error(`${path(props)}: ${reasons.join("; ")}.`));
|
|
4281
4839
|
};
|
|
4282
4840
|
var toCarry = (live, props, groups, sealed) => {
|
|
4283
4841
|
const header = headerState(live, props, groups) === "stale";
|
|
@@ -4289,39 +4847,48 @@ var requireValues = (props, groups, carry) => {
|
|
|
4289
4847
|
...carry.header ? groups.header.missing : [],
|
|
4290
4848
|
...carry.sealed ? groups.sealed.missing : []
|
|
4291
4849
|
];
|
|
4292
|
-
return missing.length === 0 ?
|
|
4850
|
+
return missing.length === 0 ? Effect62.void : Effect62.die(new Error(`${path(props)}: this write must send write-only values, and ${missing.join(", ")} ` + "is unset or empty in the deploying environment. Export it and deploy again."));
|
|
4293
4851
|
};
|
|
4294
|
-
var
|
|
4295
|
-
list: () =>
|
|
4296
|
-
read: ({ olds, output }) => (output === undefined ? refuse2(olds) :
|
|
4852
|
+
var handlers10 = {
|
|
4853
|
+
list: () => Effect62.succeed([]),
|
|
4854
|
+
read: ({ olds, output }) => (output === undefined ? refuse2(olds) : Effect62.void).pipe(Effect62.andThen(readTarget2(olds)), Effect62.map((live) => live === undefined ? undefined : { ...live, sealed: output?.sealed ?? "" })),
|
|
4297
4855
|
diff: ({
|
|
4298
4856
|
news,
|
|
4299
4857
|
olds,
|
|
4300
4858
|
output
|
|
4301
|
-
}) =>
|
|
4302
|
-
if (output === undefined || !
|
|
4859
|
+
}) => Effect62.gen(function* () {
|
|
4860
|
+
if (output === undefined || !isResolved19(news))
|
|
4303
4861
|
return;
|
|
4862
|
+
yield* refuse2(news);
|
|
4304
4863
|
if (olds !== undefined && (olds.type !== news.type || olds.name !== news.name)) {
|
|
4864
|
+
const destination = yield* readTarget2(news);
|
|
4865
|
+
const groups = resolveGroups(news);
|
|
4866
|
+
const carry = destination === undefined ? { header: true, sealed: true } : toCarry(destination, news, groups, "");
|
|
4867
|
+
if (carry !== undefined) {
|
|
4868
|
+
yield* requireValues(news, groups, carry);
|
|
4869
|
+
const mode = destination === undefined ? "create" : "update";
|
|
4870
|
+
const form = targetForm(news, groups, carry, mode);
|
|
4871
|
+
yield* guardForm(pbsTargetEndpoint(news)[mode], form, destination === undefined);
|
|
4872
|
+
yield* validateTargetWrite2(news, form, mode);
|
|
4873
|
+
}
|
|
4305
4874
|
return { action: "replace", deleteFirst: olds.name === news.name };
|
|
4306
4875
|
}
|
|
4307
|
-
yield*
|
|
4308
|
-
const live = yield* readLive2(news);
|
|
4876
|
+
const live = yield* readTarget2(news);
|
|
4309
4877
|
if (live === undefined)
|
|
4310
4878
|
return { action: "update" };
|
|
4311
4879
|
return toCarry(live, news, resolveGroups(news), output.sealed) === undefined ? { action: "noop" } : { action: "update" };
|
|
4312
4880
|
}),
|
|
4313
|
-
reconcile: ({ news, output }) =>
|
|
4881
|
+
reconcile: ({ news, output }) => Effect62.gen(function* () {
|
|
4314
4882
|
yield* refuse2(news);
|
|
4315
4883
|
const groups = resolveGroups(news);
|
|
4316
|
-
const live = yield*
|
|
4884
|
+
const live = yield* readTarget2(news);
|
|
4317
4885
|
let sealed = output?.sealed ?? "";
|
|
4318
4886
|
if (live === undefined) {
|
|
4319
4887
|
const carry = { header: true, sealed: true };
|
|
4320
4888
|
yield* requireValues(news, groups, carry);
|
|
4321
|
-
const collection = `config/notifications/endpoints/${news.type}`;
|
|
4322
4889
|
const form = targetForm(news, groups, carry, "create");
|
|
4323
4890
|
yield* guardForm(pbsTargetEndpoint(news).create, form, true);
|
|
4324
|
-
yield*
|
|
4891
|
+
yield* createTarget2(news, form);
|
|
4325
4892
|
sealed = seal(groups.sealed.values);
|
|
4326
4893
|
} else {
|
|
4327
4894
|
const carry = toCarry(live, news, groups, sealed);
|
|
@@ -4329,27 +4896,31 @@ var handlers13 = {
|
|
|
4329
4896
|
yield* requireValues(news, groups, carry);
|
|
4330
4897
|
const form = targetForm(news, groups, carry, "update");
|
|
4331
4898
|
yield* guardForm(pbsTargetEndpoint(news).update, form, false);
|
|
4332
|
-
yield*
|
|
4899
|
+
yield* updateTarget2(news, form);
|
|
4333
4900
|
if (carry.sealed)
|
|
4334
4901
|
sealed = seal(groups.sealed.values);
|
|
4335
4902
|
}
|
|
4336
4903
|
}
|
|
4337
|
-
const after = yield*
|
|
4904
|
+
const after = yield* readTarget2(news);
|
|
4338
4905
|
if (after === undefined) {
|
|
4339
|
-
return yield*
|
|
4906
|
+
return yield* Effect62.die(new Error(`${path(news)}: the write returned no error but the target is still absent.`));
|
|
4340
4907
|
}
|
|
4341
4908
|
return { ...after, sealed };
|
|
4342
4909
|
}),
|
|
4343
|
-
delete: ({ olds }) =>
|
|
4910
|
+
delete: ({ olds }) => deleteTarget2(olds)
|
|
4344
4911
|
};
|
|
4345
4912
|
|
|
4346
4913
|
// src/proxmox/pbs-notification-target.ts
|
|
4347
4914
|
var PbsNotificationTarget = Resource21("Pbs.NotificationTarget");
|
|
4348
|
-
var PbsNotificationTargetProvider = () => Provider21.effect(PbsNotificationTarget,
|
|
4915
|
+
var PbsNotificationTargetProvider = () => Provider21.effect(PbsNotificationTarget, Effect63.succeed(PbsNotificationTarget.Provider.of(handlers10)));
|
|
4349
4916
|
// src/proxmox/pbs-prune-job.ts
|
|
4350
4917
|
import { Resource as Resource22 } from "alchemy";
|
|
4351
4918
|
import * as Provider22 from "alchemy/Provider";
|
|
4352
|
-
import * as
|
|
4919
|
+
import * as Effect66 from "effect/Effect";
|
|
4920
|
+
|
|
4921
|
+
// src/proxmox/pbs-prune-job-lifecycle.ts
|
|
4922
|
+
import { isResolved as isResolved20 } from "alchemy/Diff";
|
|
4923
|
+
import * as Effect65 from "effect/Effect";
|
|
4353
4924
|
|
|
4354
4925
|
// src/proxmox/pbs-prune-job-form.ts
|
|
4355
4926
|
var KEEP_KEYS = [
|
|
@@ -4404,10 +4975,9 @@ var updateBody2 = (props) => {
|
|
|
4404
4975
|
return shape5(props);
|
|
4405
4976
|
};
|
|
4406
4977
|
|
|
4407
|
-
// src/proxmox/pbs-prune-job.ts
|
|
4408
|
-
var PbsPruneJob = Resource22("Pbs.PruneJob");
|
|
4978
|
+
// src/proxmox/pbs-prune-job-spec.ts
|
|
4409
4979
|
var same6 = (declared2, live) => declared2 === undefined || declared2 === live;
|
|
4410
|
-
var
|
|
4980
|
+
var spec = {
|
|
4411
4981
|
attributes: (live, props) => ({
|
|
4412
4982
|
comment: text(live["comment"], ""),
|
|
4413
4983
|
disable: bool(live["disable"], false),
|
|
@@ -4424,12 +4994,104 @@ var handlers14 = pveHandlers({
|
|
|
4424
4994
|
matches: (attributes2, props) => attributes2.schedule === props.schedule && attributes2.store === props.store && same6(props.ns, attributes2.ns) && same6(props["max-depth"], attributes2["max-depth"]) && same6(props.disable, attributes2.disable) && same6(props.comment, attributes2.comment) && keepsMatch(attributes2, props),
|
|
4425
4995
|
path: (props) => `config/prune/${props.id}`,
|
|
4426
4996
|
updateForm: updateBody2
|
|
4997
|
+
};
|
|
4998
|
+
|
|
4999
|
+
// src/proxmox/pbs-prune-job-wire.ts
|
|
5000
|
+
import * as config4 from "@distilled.cloud/proxmox-backup/config";
|
|
5001
|
+
import * as Effect64 from "effect/Effect";
|
|
5002
|
+
var members2 = (form) => ({
|
|
5003
|
+
...form["comment"] === undefined ? {} : { comment: form["comment"] },
|
|
5004
|
+
...form["disable"] === undefined ? {} : { disable: form["disable"] },
|
|
5005
|
+
...form["keep-daily"] === undefined ? {} : { keep_daily: form["keep-daily"] },
|
|
5006
|
+
...form["keep-hourly"] === undefined ? {} : { keep_hourly: form["keep-hourly"] },
|
|
5007
|
+
...form["keep-last"] === undefined ? {} : { keep_last: form["keep-last"] },
|
|
5008
|
+
...form["keep-monthly"] === undefined ? {} : { keep_monthly: form["keep-monthly"] },
|
|
5009
|
+
...form["keep-weekly"] === undefined ? {} : { keep_weekly: form["keep-weekly"] },
|
|
5010
|
+
...form["keep-yearly"] === undefined ? {} : { keep_yearly: form["keep-yearly"] },
|
|
5011
|
+
...form["max-depth"] === undefined ? {} : { max_depth: form["max-depth"] },
|
|
5012
|
+
...form["ns"] === undefined ? {} : { ns: form["ns"] },
|
|
5013
|
+
...form["schedule"] === undefined ? {} : { schedule: form["schedule"] },
|
|
5014
|
+
...form["store"] === undefined ? {} : { store: form["store"] }
|
|
5015
|
+
});
|
|
5016
|
+
var createRequest2 = (props) => ({
|
|
5017
|
+
...members2(createBody3(props)),
|
|
5018
|
+
id: props.id,
|
|
5019
|
+
store: props.store,
|
|
5020
|
+
schedule: props.schedule
|
|
5021
|
+
});
|
|
5022
|
+
var updateRequest2 = (props) => ({
|
|
5023
|
+
...members2(updateBody2(props)),
|
|
5024
|
+
id: props.id
|
|
5025
|
+
});
|
|
5026
|
+
var wire2 = (row) => ({
|
|
5027
|
+
...row,
|
|
5028
|
+
"keep-daily": row.keep_daily,
|
|
5029
|
+
"keep-hourly": row.keep_hourly,
|
|
5030
|
+
"keep-last": row.keep_last,
|
|
5031
|
+
"keep-monthly": row.keep_monthly,
|
|
5032
|
+
"keep-weekly": row.keep_weekly,
|
|
5033
|
+
"keep-yearly": row.keep_yearly,
|
|
5034
|
+
"max-depth": row.max_depth
|
|
5035
|
+
});
|
|
5036
|
+
var readOne2 = (props) => runPbs(props.target, "read", config4.getConfigPrune({ id: props.id })).pipe(Effect64.map((row) => spec.attributes(wire2(row), props)), Effect64.catchTag("PruneJobNotFound", () => Effect64.succeed(undefined)));
|
|
5037
|
+
var createOne2 = (props) => runPbs(props.target, "provision", config4.createConfigPrune(createRequest2(props)));
|
|
5038
|
+
var updateOne2 = (props) => runPbs(props.target, "provision", config4.putConfigPrune(updateRequest2(props)));
|
|
5039
|
+
var deleteOne2 = (props) => Effect64.gen(function* () {
|
|
5040
|
+
if ((yield* readOne2(props)) === undefined)
|
|
5041
|
+
return;
|
|
5042
|
+
return yield* runPbs(props.target, "provision", config4.deleteConfigPrune({ id: props.id }));
|
|
4427
5043
|
});
|
|
4428
|
-
|
|
5044
|
+
|
|
5045
|
+
// src/proxmox/pbs-prune-job-lifecycle.ts
|
|
5046
|
+
var { guardCreate: guardCreate5, guardUpdate: guardUpdate5 } = specGuards(spec);
|
|
5047
|
+
var handlers11 = {
|
|
5048
|
+
list: () => Effect65.succeed([]),
|
|
5049
|
+
read: ({ olds }) => readOne2(olds),
|
|
5050
|
+
diff: ({
|
|
5051
|
+
news,
|
|
5052
|
+
output
|
|
5053
|
+
}) => Effect65.gen(function* () {
|
|
5054
|
+
if (!isResolved20(news))
|
|
5055
|
+
return;
|
|
5056
|
+
yield* guardCreate5(news, output === undefined);
|
|
5057
|
+
yield* guardUpdate5(news);
|
|
5058
|
+
if (output === undefined)
|
|
5059
|
+
return;
|
|
5060
|
+
const live = yield* readOne2(news);
|
|
5061
|
+
if (live === undefined) {
|
|
5062
|
+
yield* guardCreate5(news, true);
|
|
5063
|
+
return { action: "update" };
|
|
5064
|
+
}
|
|
5065
|
+
return { action: spec.matches(live, news) ? "noop" : "update" };
|
|
5066
|
+
}),
|
|
5067
|
+
reconcile: ({ news }) => Effect65.gen(function* () {
|
|
5068
|
+
const live = yield* readOne2(news);
|
|
5069
|
+
yield* guardCreate5(news, live === undefined);
|
|
5070
|
+
yield* guardUpdate5(news);
|
|
5071
|
+
if (live === undefined)
|
|
5072
|
+
yield* createOne2(news);
|
|
5073
|
+
else if (!spec.matches(live, news))
|
|
5074
|
+
yield* updateOne2(news);
|
|
5075
|
+
const after = yield* readOne2(news);
|
|
5076
|
+
if (after === undefined) {
|
|
5077
|
+
return yield* Effect65.fail(new Error(`${spec.path(news)}: write succeeded but job is absent.`));
|
|
5078
|
+
}
|
|
5079
|
+
return after;
|
|
5080
|
+
}),
|
|
5081
|
+
delete: ({ olds }) => deleteOne2(olds)
|
|
5082
|
+
};
|
|
5083
|
+
|
|
5084
|
+
// src/proxmox/pbs-prune-job.ts
|
|
5085
|
+
var PbsPruneJob = Resource22("Pbs.PruneJob");
|
|
5086
|
+
var PbsPruneJobProvider = () => Provider22.effect(PbsPruneJob, Effect66.succeed(PbsPruneJob.Provider.of(handlers11)));
|
|
4429
5087
|
// src/proxmox/pbs-sync-job.ts
|
|
4430
5088
|
import { Resource as Resource23 } from "alchemy";
|
|
4431
5089
|
import * as Provider23 from "alchemy/Provider";
|
|
4432
|
-
import * as
|
|
5090
|
+
import * as Effect69 from "effect/Effect";
|
|
5091
|
+
|
|
5092
|
+
// src/proxmox/pbs-sync-job-lifecycle.ts
|
|
5093
|
+
import { isResolved as isResolved21 } from "alchemy/Diff";
|
|
5094
|
+
import * as Effect68 from "effect/Effect";
|
|
4433
5095
|
|
|
4434
5096
|
// src/proxmox/pbs-sync-job-form.ts
|
|
4435
5097
|
var UNSET4 = -1;
|
|
@@ -4477,7 +5139,7 @@ var createBody4 = (props) => ({
|
|
|
4477
5139
|
...props["sync-direction"] === undefined ? {} : { "sync-direction": props["sync-direction"] }
|
|
4478
5140
|
});
|
|
4479
5141
|
var updateBody3 = (props) => shape6(props);
|
|
4480
|
-
var
|
|
5142
|
+
var spec2 = {
|
|
4481
5143
|
attributes: (live, props) => ({
|
|
4482
5144
|
comment: text(live["comment"], ""),
|
|
4483
5145
|
"group-filter": filters(live["group-filter"]),
|
|
@@ -4502,15 +5164,105 @@ var handlers15 = pveHandlers({
|
|
|
4502
5164
|
matches: (attributes2, props) => attributes2["remove-vanished"] === (props["remove-vanished"] === true) && attributes2.store === props.store && attributes2["remote-store"] === props["remote-store"] && (props.remote === undefined || attributes2.remote === props.remote) && (props.ns === undefined || attributes2.ns === props.ns) && (props["remote-ns"] === undefined || attributes2["remote-ns"] === props["remote-ns"]) && (props.schedule === undefined || attributes2.schedule === props.schedule) && (props.comment === undefined || attributes2.comment === props.comment) && (props.owner === undefined || attributes2.owner === props.owner) && (props["verified-only"] === undefined || attributes2["verified-only"] === props["verified-only"]) && (props["rate-in"] === undefined || attributes2["rate-in"] === props["rate-in"]) && (props["max-depth"] === undefined || attributes2["max-depth"] === props["max-depth"]) && (props["transfer-last"] === undefined || attributes2["transfer-last"] === props["transfer-last"]),
|
|
4503
5165
|
path: (props) => `config/sync/${props.id}`,
|
|
4504
5166
|
updateForm: updateBody3
|
|
5167
|
+
};
|
|
5168
|
+
|
|
5169
|
+
// src/proxmox/pbs-sync-job-wire.ts
|
|
5170
|
+
import * as config5 from "@distilled.cloud/proxmox-backup/config";
|
|
5171
|
+
import * as Effect67 from "effect/Effect";
|
|
5172
|
+
var members3 = (form) => ({
|
|
5173
|
+
...form["remote-store"] === undefined ? {} : { remote_store: form["remote-store"] },
|
|
5174
|
+
...form["remove-vanished"] === undefined ? {} : { remove_vanished: form["remove-vanished"] },
|
|
5175
|
+
...form["store"] === undefined ? {} : { store: form["store"] },
|
|
5176
|
+
...form["comment"] === undefined ? {} : { comment: form["comment"] },
|
|
5177
|
+
...form["max-depth"] === undefined ? {} : { max_depth: form["max-depth"] },
|
|
5178
|
+
...form["ns"] === undefined ? {} : { ns: form["ns"] },
|
|
5179
|
+
...form["owner"] === undefined ? {} : { owner: form["owner"] },
|
|
5180
|
+
...form["rate-in"] === undefined ? {} : { rate_in: form["rate-in"] },
|
|
5181
|
+
...form["remote"] === undefined ? {} : { remote: form["remote"] },
|
|
5182
|
+
...form["remote-ns"] === undefined ? {} : { remote_ns: form["remote-ns"] },
|
|
5183
|
+
...form["schedule"] === undefined ? {} : { schedule: form["schedule"] },
|
|
5184
|
+
...form["verified-only"] === undefined ? {} : { verified_only: form["verified-only"] },
|
|
5185
|
+
...form["transfer-last"] === undefined ? {} : { transfer_last: form["transfer-last"] },
|
|
5186
|
+
...form["sync-direction"] === undefined ? {} : { sync_direction: form["sync-direction"] }
|
|
5187
|
+
});
|
|
5188
|
+
var createRequest3 = (props) => ({
|
|
5189
|
+
...members3(createBody4(props)),
|
|
5190
|
+
id: props.id,
|
|
5191
|
+
store: props.store,
|
|
5192
|
+
remote_store: props["remote-store"]
|
|
5193
|
+
});
|
|
5194
|
+
var updateRequest3 = (props) => ({
|
|
5195
|
+
...members3(updateBody3(props)),
|
|
5196
|
+
id: props.id
|
|
5197
|
+
});
|
|
5198
|
+
var wire3 = (row) => ({
|
|
5199
|
+
...row,
|
|
5200
|
+
"remote-store": row.remote_store,
|
|
5201
|
+
"remove-vanished": row.remove_vanished,
|
|
5202
|
+
"max-depth": row.max_depth,
|
|
5203
|
+
"rate-in": row.rate_in,
|
|
5204
|
+
"remote-ns": row.remote_ns,
|
|
5205
|
+
"verified-only": row.verified_only,
|
|
5206
|
+
"transfer-last": row.transfer_last,
|
|
5207
|
+
"sync-direction": row.sync_direction,
|
|
5208
|
+
"group-filter": row.group_filter
|
|
5209
|
+
});
|
|
5210
|
+
var readOne3 = (props) => runPbs(props.target, "read", config5.getConfigSync({ id: props.id })).pipe(Effect67.map((row) => spec2.attributes(wire3(row), props)), Effect67.catchTag("SyncJobNotFound", () => Effect67.succeed(undefined)));
|
|
5211
|
+
var createOne3 = (props) => runPbs(props.target, "provision", config5.syncConfig(createRequest3(props)));
|
|
5212
|
+
var updateOne3 = (props) => runPbs(props.target, "provision", config5.putConfigSync(updateRequest3(props)));
|
|
5213
|
+
var deleteOne3 = (props) => Effect67.gen(function* () {
|
|
5214
|
+
if ((yield* readOne3(props)) === undefined)
|
|
5215
|
+
return;
|
|
5216
|
+
return yield* runPbs(props.target, "provision", config5.deleteConfigSync({ id: props.id }));
|
|
4505
5217
|
});
|
|
4506
5218
|
|
|
5219
|
+
// src/proxmox/pbs-sync-job-lifecycle.ts
|
|
5220
|
+
var { guardCreate: guardCreate6, guardUpdate: guardUpdate6 } = specGuards(spec2);
|
|
5221
|
+
var handlers12 = {
|
|
5222
|
+
list: () => Effect68.succeed([]),
|
|
5223
|
+
read: ({ olds }) => readOne3(olds),
|
|
5224
|
+
diff: ({ news, output }) => Effect68.gen(function* () {
|
|
5225
|
+
if (!isResolved21(news))
|
|
5226
|
+
return;
|
|
5227
|
+
yield* guardCreate6(news, output === undefined);
|
|
5228
|
+
yield* guardUpdate6(news);
|
|
5229
|
+
if (output === undefined)
|
|
5230
|
+
return;
|
|
5231
|
+
const live = yield* readOne3(news);
|
|
5232
|
+
if (live === undefined) {
|
|
5233
|
+
yield* guardCreate6(news, true);
|
|
5234
|
+
return { action: "update" };
|
|
5235
|
+
}
|
|
5236
|
+
return { action: spec2.matches(live, news) ? "noop" : "update" };
|
|
5237
|
+
}),
|
|
5238
|
+
reconcile: ({ news }) => Effect68.gen(function* () {
|
|
5239
|
+
const live = yield* readOne3(news);
|
|
5240
|
+
yield* guardCreate6(news, live === undefined);
|
|
5241
|
+
yield* guardUpdate6(news);
|
|
5242
|
+
if (live === undefined)
|
|
5243
|
+
yield* createOne3(news);
|
|
5244
|
+
else if (!spec2.matches(live, news))
|
|
5245
|
+
yield* updateOne3(news);
|
|
5246
|
+
const after = yield* readOne3(news);
|
|
5247
|
+
if (after === undefined) {
|
|
5248
|
+
return yield* Effect68.fail(new Error(`${spec2.path(news)}: write succeeded but job is absent.`));
|
|
5249
|
+
}
|
|
5250
|
+
return after;
|
|
5251
|
+
}),
|
|
5252
|
+
delete: ({ olds }) => deleteOne3(olds)
|
|
5253
|
+
};
|
|
5254
|
+
|
|
4507
5255
|
// src/proxmox/pbs-sync-job.ts
|
|
4508
5256
|
var PbsSyncJob = Resource23("Pbs.SyncJob");
|
|
4509
|
-
var PbsSyncJobProvider = () => Provider23.effect(PbsSyncJob,
|
|
5257
|
+
var PbsSyncJobProvider = () => Provider23.effect(PbsSyncJob, Effect69.succeed(PbsSyncJob.Provider.of(handlers12)));
|
|
4510
5258
|
// src/proxmox/pbs-verify-job.ts
|
|
4511
5259
|
import { Resource as Resource24 } from "alchemy";
|
|
4512
5260
|
import * as Provider24 from "alchemy/Provider";
|
|
4513
|
-
import * as
|
|
5261
|
+
import * as Effect72 from "effect/Effect";
|
|
5262
|
+
|
|
5263
|
+
// src/proxmox/pbs-verify-job-lifecycle.ts
|
|
5264
|
+
import { isResolved as isResolved22 } from "alchemy/Diff";
|
|
5265
|
+
import * as Effect71 from "effect/Effect";
|
|
4514
5266
|
|
|
4515
5267
|
// src/proxmox/pbs-verify-job-form.ts
|
|
4516
5268
|
var rechecks = (ignoreVerified, outdatedAfter) => {
|
|
@@ -4530,9 +5282,8 @@ var shape7 = (props) => ({
|
|
|
4530
5282
|
...props["outdated-after"] === undefined ? {} : { "outdated-after": String(props["outdated-after"]) }
|
|
4531
5283
|
});
|
|
4532
5284
|
|
|
4533
|
-
// src/proxmox/pbs-verify-job.ts
|
|
4534
|
-
var
|
|
4535
|
-
var handlers16 = pveHandlers({
|
|
5285
|
+
// src/proxmox/pbs-verify-job-spec.ts
|
|
5286
|
+
var spec3 = {
|
|
4536
5287
|
attributes: (live, props) => {
|
|
4537
5288
|
const ignoreVerified = bool(live["ignore-verified"], true);
|
|
4538
5289
|
const outdatedAfter = int(live["outdated-after"], -1);
|
|
@@ -4554,12 +5305,94 @@ var handlers16 = pveHandlers({
|
|
|
4554
5305
|
matches: (attributes2, props) => attributes2.store === props.store && attributes2.schedule === (props.schedule ?? "") && attributes2["ignore-verified"] === (props["ignore-verified"] !== false) && (props["outdated-after"] === undefined || attributes2["outdated-after"] === props["outdated-after"]) && (props["max-depth"] === undefined || attributes2["max-depth"] === props["max-depth"]) && (props.ns === undefined || attributes2.ns === props.ns) && (props.comment === undefined || attributes2.comment === props.comment),
|
|
4555
5306
|
path: (props) => `config/verify/${props.id}`,
|
|
4556
5307
|
updateForm: shape7
|
|
5308
|
+
};
|
|
5309
|
+
|
|
5310
|
+
// src/proxmox/pbs-verify-job-wire.ts
|
|
5311
|
+
import * as config6 from "@distilled.cloud/proxmox-backup/config";
|
|
5312
|
+
import * as Effect70 from "effect/Effect";
|
|
5313
|
+
var members4 = (form) => ({
|
|
5314
|
+
...form["ignore-verified"] === undefined ? {} : { ignore_verified: form["ignore-verified"] },
|
|
5315
|
+
...form["store"] === undefined ? {} : { store: form["store"] },
|
|
5316
|
+
...form["schedule"] === undefined ? {} : { schedule: form["schedule"] },
|
|
5317
|
+
...form["comment"] === undefined ? {} : { comment: form["comment"] },
|
|
5318
|
+
...form["max-depth"] === undefined ? {} : { max_depth: form["max-depth"] },
|
|
5319
|
+
...form["ns"] === undefined ? {} : { ns: form["ns"] },
|
|
5320
|
+
...form["outdated-after"] === undefined ? {} : { outdated_after: form["outdated-after"] }
|
|
5321
|
+
});
|
|
5322
|
+
var createRequest4 = (props) => ({
|
|
5323
|
+
...members4({ ...shape7(props), id: props.id }),
|
|
5324
|
+
id: props.id,
|
|
5325
|
+
store: props.store
|
|
5326
|
+
});
|
|
5327
|
+
var updateRequest4 = (props) => ({
|
|
5328
|
+
...members4(shape7(props)),
|
|
5329
|
+
id: props.id
|
|
5330
|
+
});
|
|
5331
|
+
var wire4 = (row) => ({
|
|
5332
|
+
...row,
|
|
5333
|
+
"ignore-verified": row.ignore_verified,
|
|
5334
|
+
"max-depth": row.max_depth,
|
|
5335
|
+
"outdated-after": row.outdated_after
|
|
5336
|
+
});
|
|
5337
|
+
var readOne4 = (props) => runPbs(props.target, "read", config6.getConfigVerify({ id: props.id })).pipe(Effect70.map((row) => spec3.attributes(wire4(row), props)), Effect70.catchTag("VerifyJobNotFound", () => Effect70.succeed(undefined)));
|
|
5338
|
+
var createOne4 = (props) => runPbs(props.target, "provision", config6.verifyConfig(createRequest4(props)));
|
|
5339
|
+
var updateOne4 = (props) => runPbs(props.target, "provision", config6.putConfigVerify(updateRequest4(props)));
|
|
5340
|
+
var deleteOne4 = (props) => Effect70.gen(function* () {
|
|
5341
|
+
if ((yield* readOne4(props)) === undefined)
|
|
5342
|
+
return;
|
|
5343
|
+
return yield* runPbs(props.target, "provision", config6.deleteConfigVerify({ id: props.id }));
|
|
4557
5344
|
});
|
|
4558
|
-
|
|
5345
|
+
|
|
5346
|
+
// src/proxmox/pbs-verify-job-lifecycle.ts
|
|
5347
|
+
var { guardCreate: guardCreate7, guardUpdate: guardUpdate7 } = specGuards(spec3);
|
|
5348
|
+
var handlers13 = {
|
|
5349
|
+
list: () => Effect71.succeed([]),
|
|
5350
|
+
read: ({ olds }) => readOne4(olds),
|
|
5351
|
+
diff: ({
|
|
5352
|
+
news,
|
|
5353
|
+
output
|
|
5354
|
+
}) => Effect71.gen(function* () {
|
|
5355
|
+
if (!isResolved22(news))
|
|
5356
|
+
return;
|
|
5357
|
+
yield* guardCreate7(news, output === undefined);
|
|
5358
|
+
yield* guardUpdate7(news);
|
|
5359
|
+
if (output === undefined)
|
|
5360
|
+
return;
|
|
5361
|
+
const live = yield* readOne4(news);
|
|
5362
|
+
if (live === undefined) {
|
|
5363
|
+
yield* guardCreate7(news, true);
|
|
5364
|
+
return { action: "update" };
|
|
5365
|
+
}
|
|
5366
|
+
return { action: spec3.matches(live, news) ? "noop" : "update" };
|
|
5367
|
+
}),
|
|
5368
|
+
reconcile: ({ news }) => Effect71.gen(function* () {
|
|
5369
|
+
const live = yield* readOne4(news);
|
|
5370
|
+
yield* guardCreate7(news, live === undefined);
|
|
5371
|
+
yield* guardUpdate7(news);
|
|
5372
|
+
if (live === undefined)
|
|
5373
|
+
yield* createOne4(news);
|
|
5374
|
+
else if (!spec3.matches(live, news))
|
|
5375
|
+
yield* updateOne4(news);
|
|
5376
|
+
const after = yield* readOne4(news);
|
|
5377
|
+
if (after === undefined) {
|
|
5378
|
+
return yield* Effect71.fail(new Error(`${spec3.path(news)}: write succeeded but job is absent.`));
|
|
5379
|
+
}
|
|
5380
|
+
return after;
|
|
5381
|
+
}),
|
|
5382
|
+
delete: ({ olds }) => deleteOne4(olds)
|
|
5383
|
+
};
|
|
5384
|
+
|
|
5385
|
+
// src/proxmox/pbs-verify-job.ts
|
|
5386
|
+
var PbsVerifyJob = Resource24("Pbs.VerifyJob");
|
|
5387
|
+
var PbsVerifyJobProvider = () => Provider24.effect(PbsVerifyJob, Effect72.succeed(PbsVerifyJob.Provider.of(handlers13)));
|
|
4559
5388
|
// src/proxmox/pool.ts
|
|
4560
5389
|
import { Resource as Resource25 } from "alchemy";
|
|
5390
|
+
import { isResolved as isResolved23 } from "alchemy/Diff";
|
|
4561
5391
|
import * as Provider25 from "alchemy/Provider";
|
|
4562
|
-
import * as
|
|
5392
|
+
import * as pools from "@distilled.cloud/proxmox/pools";
|
|
5393
|
+
import { ProxmoxParseError as ProxmoxParseError4 } from "@distilled.cloud/proxmox/Errors";
|
|
5394
|
+
import * as Schema5 from "effect/Schema";
|
|
5395
|
+
import * as Effect73 from "effect/Effect";
|
|
4563
5396
|
var ProxmoxPool = Resource25("Proxmox.Pool");
|
|
4564
5397
|
var poolCreateForm = (props) => ({
|
|
4565
5398
|
comment: props.comment ?? "",
|
|
@@ -4568,7 +5401,7 @@ var poolCreateForm = (props) => ({
|
|
|
4568
5401
|
var poolUpdateForm = (props) => ({
|
|
4569
5402
|
comment: props.comment ?? ""
|
|
4570
5403
|
});
|
|
4571
|
-
var
|
|
5404
|
+
var spec4 = {
|
|
4572
5405
|
attributes: (live, props) => {
|
|
4573
5406
|
const row = live;
|
|
4574
5407
|
return {
|
|
@@ -4583,8 +5416,51 @@ var handlers17 = pveHandlers({
|
|
|
4583
5416
|
matches: (attributes2, props) => attributes2.comment === (props.comment ?? ""),
|
|
4584
5417
|
path: (props) => `pools/${props.poolid}`,
|
|
4585
5418
|
updateForm: poolUpdateForm
|
|
4586
|
-
}
|
|
4587
|
-
var
|
|
5419
|
+
};
|
|
5420
|
+
var { guardCreate: guardCreate8, guardUpdate: guardUpdate8 } = specGuards(spec4);
|
|
5421
|
+
var readPool = (props) => runPve(props.target, "read", false, pools.getPool({ poolid: props.poolid })).pipe(Effect73.flatMap((live) => Schema5.decodeUnknownEffect(Schema5.toType(pools.GetPoolResponse))(live).pipe(Effect73.mapError(() => new ProxmoxParseError4({
|
|
5422
|
+
body: undefined,
|
|
5423
|
+
cause: "Pool response does not match its vendor schema"
|
|
5424
|
+
})), Effect73.as(live))), Effect73.map((live) => spec4.attributes({ ...live }, props)), Effect73.catchTag("PoolNotFound", () => Effect73.succeed(undefined)), Effect73.catchTag("NotFound", () => Effect73.succeed(undefined)));
|
|
5425
|
+
var deletePool2 = (props) => runPve(props.target, "provision", true, pools.deletePool({ poolid: props.poolid })).pipe(Effect73.catchTag("PoolNotFound", () => Effect73.void), Effect73.catchTag("NotFound", () => Effect73.void));
|
|
5426
|
+
var ProxmoxPoolProvider = () => Provider25.effect(ProxmoxPool, Effect73.succeed(ProxmoxPool.Provider.of({
|
|
5427
|
+
list: () => Effect73.succeed([]),
|
|
5428
|
+
read: ({ olds }) => readPool(olds),
|
|
5429
|
+
diff: Effect73.fn(function* ({ news, output }) {
|
|
5430
|
+
if (!isResolved23(news))
|
|
5431
|
+
return;
|
|
5432
|
+
yield* guardCreate8(news, output === undefined);
|
|
5433
|
+
yield* guardUpdate8(news);
|
|
5434
|
+
if (output === undefined)
|
|
5435
|
+
return;
|
|
5436
|
+
const live = yield* readPool(news);
|
|
5437
|
+
if (live === undefined) {
|
|
5438
|
+
yield* guardCreate8(news, true);
|
|
5439
|
+
return { action: "update" };
|
|
5440
|
+
}
|
|
5441
|
+
return { action: spec4.matches(live, news) ? "noop" : "update" };
|
|
5442
|
+
}),
|
|
5443
|
+
reconcile: Effect73.fn(function* ({ news }) {
|
|
5444
|
+
const live = yield* readPool(news);
|
|
5445
|
+
yield* guardCreate8(news, live === undefined);
|
|
5446
|
+
yield* guardUpdate8(news);
|
|
5447
|
+
if (live === undefined) {
|
|
5448
|
+
yield* runPve(news.target, "provision", true, pools.createPool(poolCreateForm(news)));
|
|
5449
|
+
} else {
|
|
5450
|
+
const form = formToSend(spec4.matches, live, news, poolUpdateForm(news));
|
|
5451
|
+
if (form !== undefined)
|
|
5452
|
+
yield* runPve(news.target, "provision", true, pools.putPool({
|
|
5453
|
+
...form,
|
|
5454
|
+
poolid: news.poolid
|
|
5455
|
+
}));
|
|
5456
|
+
}
|
|
5457
|
+
const after = yield* readPool(news);
|
|
5458
|
+
if (after === undefined)
|
|
5459
|
+
return yield* Effect73.fail(new Error(`pools/${news.poolid}: write returned success but the pool is still absent`));
|
|
5460
|
+
return after;
|
|
5461
|
+
}),
|
|
5462
|
+
delete: ({ olds }) => deletePool2(olds)
|
|
5463
|
+
})));
|
|
4588
5464
|
// src/proxmox/provision-baseline.ts
|
|
4589
5465
|
var PROVISION_PRIVILEGES = Object.freeze([
|
|
4590
5466
|
"Datastore.Allocate",
|
|
@@ -4736,15 +5612,15 @@ fi
|
|
|
4736
5612
|
var userStep = (user) => {
|
|
4737
5613
|
const groups = [...user.groups].sort().join(",");
|
|
4738
5614
|
const want = `groups=${groups};enable=1;expire=0;email=;firstname=;lastname=;comment=${user.comment}`;
|
|
4739
|
-
const
|
|
5615
|
+
const fields2 = `--groups ${groups} --comment '${user.comment}'`;
|
|
4740
5616
|
return `
|
|
4741
5617
|
# User ${user.userid} (the ${user.lane} lane): in ${groups}, enabled, no password.
|
|
4742
5618
|
if have=$(get /access/users/${user.userid} '${USER_STATE}'); then
|
|
4743
5619
|
if [ "$have" = '${want}' ]; then echo 'user ${user.userid}: ok'
|
|
4744
5620
|
else echo 'user ${user.userid}: setting groups, comment and account fields'
|
|
4745
|
-
pveum user modify ${user.userid} ${
|
|
5621
|
+
pveum user modify ${user.userid} ${fields2} --enable 1 --expire 0 --email '' --firstname '' --lastname ''; fi
|
|
4746
5622
|
else
|
|
4747
|
-
echo 'user ${user.userid}: adding'; pveum user add ${user.userid} ${
|
|
5623
|
+
echo 'user ${user.userid}: adding'; pveum user add ${user.userid} ${fields2}
|
|
4748
5624
|
fi
|
|
4749
5625
|
`;
|
|
4750
5626
|
};
|
|
@@ -4777,14 +5653,14 @@ echo 'provision baseline: done. Next: point the OpenBao proxmox mount at these u
|
|
|
4777
5653
|
// src/proxmox/provision-declare.ts
|
|
4778
5654
|
import { adopt } from "alchemy/AdoptPolicy";
|
|
4779
5655
|
import * as RemovalPolicy from "alchemy/RemovalPolicy";
|
|
4780
|
-
import * as
|
|
5656
|
+
import * as Effect77 from "effect/Effect";
|
|
4781
5657
|
|
|
4782
5658
|
// src/proxmox/role.ts
|
|
4783
5659
|
import { Resource as Resource26 } from "alchemy";
|
|
4784
|
-
import { isResolved as
|
|
5660
|
+
import { isResolved as isResolved24 } from "alchemy/Diff";
|
|
4785
5661
|
import * as Provider26 from "alchemy/Provider";
|
|
4786
5662
|
import * as access5 from "@distilled.cloud/proxmox/access";
|
|
4787
|
-
import * as
|
|
5663
|
+
import * as Effect74 from "effect/Effect";
|
|
4788
5664
|
|
|
4789
5665
|
// src/proxmox/role-wire.ts
|
|
4790
5666
|
var ROLE_CREATE = "pve:POST /access/roles";
|
|
@@ -4807,20 +5683,20 @@ var matches7 = (attributes2, props) => canonical2(attributes2.privs).join(",") =
|
|
|
4807
5683
|
|
|
4808
5684
|
// src/proxmox/role.ts
|
|
4809
5685
|
var ProxmoxRole = Resource26("Proxmox.Role");
|
|
4810
|
-
var readRole = (props) => readOrUnreadable(runPve(props.target, "read", false, access5.listAccessRoles({}))).pipe(
|
|
5686
|
+
var readRole = (props) => readOrUnreadable(runPve(props.target, "read", false, access5.listAccessRoles({}))).pipe(Effect74.map((rows) => {
|
|
4811
5687
|
if (rows === UNREADABLE)
|
|
4812
5688
|
return UNREADABLE;
|
|
4813
5689
|
const row = find2(rows, props.roleid);
|
|
4814
5690
|
return row === undefined ? undefined : attributesOf7(row, props);
|
|
4815
5691
|
}));
|
|
4816
5692
|
var dropUnreadable5 = (live) => live === UNREADABLE ? undefined : live;
|
|
4817
|
-
var ProxmoxRoleProvider = () => Provider26.effect(ProxmoxRole,
|
|
4818
|
-
list: () =>
|
|
4819
|
-
read:
|
|
5693
|
+
var ProxmoxRoleProvider = () => Provider26.effect(ProxmoxRole, Effect74.succeed(ProxmoxRole.Provider.of({
|
|
5694
|
+
list: () => Effect74.succeed([]),
|
|
5695
|
+
read: Effect74.fn(function* ({ olds }) {
|
|
4820
5696
|
return dropUnreadable5(yield* readRole(olds));
|
|
4821
5697
|
}),
|
|
4822
|
-
diff:
|
|
4823
|
-
if (!
|
|
5698
|
+
diff: Effect74.fn(function* ({ news, output }) {
|
|
5699
|
+
if (!isResolved24(news))
|
|
4824
5700
|
return;
|
|
4825
5701
|
yield* guardWrite2(ROLE_CREATE, createForm7(news), output === undefined);
|
|
4826
5702
|
yield* guardWrite2(ROLE_UPDATE, updateForm4(news), false);
|
|
@@ -4837,7 +5713,7 @@ var ProxmoxRoleProvider = () => Provider26.effect(ProxmoxRole, Effect57.succeed(
|
|
|
4837
5713
|
}
|
|
4838
5714
|
return matches7(live, news) ? { action: "noop" } : { action: "update" };
|
|
4839
5715
|
}),
|
|
4840
|
-
reconcile:
|
|
5716
|
+
reconcile: Effect74.fn(function* ({ news }) {
|
|
4841
5717
|
const before = dropUnreadable5(yield* readRole(news));
|
|
4842
5718
|
yield* guardWrite2(ROLE_CREATE, createForm7(news), before === undefined);
|
|
4843
5719
|
yield* guardWrite2(ROLE_UPDATE, updateForm4(news), false);
|
|
@@ -4848,25 +5724,25 @@ var ProxmoxRoleProvider = () => Provider26.effect(ProxmoxRole, Effect57.succeed(
|
|
|
4848
5724
|
}
|
|
4849
5725
|
const after = dropUnreadable5(yield* readRole(news));
|
|
4850
5726
|
if (after === undefined) {
|
|
4851
|
-
return yield*
|
|
5727
|
+
return yield* Effect74.die(new Error(`access/roles/${news.roleid}: the write returned no error but the role is ` + 'still absent. PVE wraps every answer in {"data":...} and can report success ' + "on a call that did nothing -- read back rather than trusting the status code."));
|
|
4852
5728
|
}
|
|
4853
5729
|
return after;
|
|
4854
5730
|
}),
|
|
4855
|
-
delete:
|
|
5731
|
+
delete: Effect74.fn(function* ({ olds }) {
|
|
4856
5732
|
yield* runPve(olds.target, "provision", true, access5.deleteAccessRole({ roleid: olds.roleid }));
|
|
4857
5733
|
})
|
|
4858
5734
|
})));
|
|
4859
5735
|
|
|
4860
5736
|
// src/proxmox/user.ts
|
|
4861
5737
|
import { Resource as Resource27 } from "alchemy";
|
|
4862
|
-
import { isResolved as
|
|
5738
|
+
import { isResolved as isResolved25 } from "alchemy/Diff";
|
|
4863
5739
|
import * as Provider27 from "alchemy/Provider";
|
|
4864
5740
|
import * as access7 from "@distilled.cloud/proxmox/access";
|
|
4865
|
-
import * as
|
|
5741
|
+
import * as Effect76 from "effect/Effect";
|
|
4866
5742
|
|
|
4867
5743
|
// src/proxmox/user-wire.ts
|
|
4868
5744
|
import * as access6 from "@distilled.cloud/proxmox/access";
|
|
4869
|
-
import * as
|
|
5745
|
+
import * as Effect75 from "effect/Effect";
|
|
4870
5746
|
var USER_CREATE = "pve:POST /access/users";
|
|
4871
5747
|
var USER_UPDATE = "pve:PUT /access/users/{userid}";
|
|
4872
5748
|
var int2 = (value) => {
|
|
@@ -4908,19 +5784,19 @@ var attributesOf8 = (live, props) => ({
|
|
|
4908
5784
|
userid: props.userid
|
|
4909
5785
|
});
|
|
4910
5786
|
var matches8 = (attributes2, props) => attributes2.comment === (props.comment ?? "") && attributes2.email === (props.email ?? "") && attributes2.enable === (props.enable !== false) && attributes2.expire === (props.expire ?? 0) && attributes2.firstname === (props.firstname ?? "") && attributes2.lastname === (props.lastname ?? "") && attributes2.groups.join(",") === groupSet(props.groups).join(",");
|
|
4911
|
-
var readUserOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, access6.getAccessUser({ userid: props.userid }))).pipe(
|
|
4912
|
-
var readUser = (props) => readUserOrFail(props).pipe(
|
|
5787
|
+
var readUserOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, access6.getAccessUser({ userid: props.userid }))).pipe(Effect75.map((live) => live === UNREADABLE ? UNREADABLE : attributesOf8(live, props)));
|
|
5788
|
+
var readUser = (props) => readUserOrFail(props).pipe(Effect75.catchTag("UserNotFound", () => Effect75.succeed(undefined)));
|
|
4913
5789
|
var dropUnreadable6 = (live) => live === UNREADABLE ? undefined : live;
|
|
4914
5790
|
|
|
4915
5791
|
// src/proxmox/user.ts
|
|
4916
5792
|
var ProxmoxUser = Resource27("Proxmox.User");
|
|
4917
|
-
var ProxmoxUserProvider = () => Provider27.effect(ProxmoxUser,
|
|
4918
|
-
list: () =>
|
|
4919
|
-
read:
|
|
5793
|
+
var ProxmoxUserProvider = () => Provider27.effect(ProxmoxUser, Effect76.succeed(ProxmoxUser.Provider.of({
|
|
5794
|
+
list: () => Effect76.succeed([]),
|
|
5795
|
+
read: Effect76.fn(function* ({ olds, output }) {
|
|
4920
5796
|
return dropUnreadable6(yield* output === undefined ? readUser(olds) : readUserOrFail(olds));
|
|
4921
5797
|
}),
|
|
4922
|
-
diff:
|
|
4923
|
-
if (!
|
|
5798
|
+
diff: Effect76.fn(function* ({ news, output }) {
|
|
5799
|
+
if (!isResolved25(news))
|
|
4924
5800
|
return;
|
|
4925
5801
|
yield* guardWrite2(USER_CREATE, createForm8(news), output === undefined);
|
|
4926
5802
|
yield* guardWrite2(USER_UPDATE, updateForm5(news), false);
|
|
@@ -4937,7 +5813,7 @@ var ProxmoxUserProvider = () => Provider27.effect(ProxmoxUser, Effect59.succeed(
|
|
|
4937
5813
|
}
|
|
4938
5814
|
return matches8(live, news) ? { action: "noop" } : { action: "update" };
|
|
4939
5815
|
}),
|
|
4940
|
-
reconcile:
|
|
5816
|
+
reconcile: Effect76.fn(function* ({ news }) {
|
|
4941
5817
|
const before = dropUnreadable6(yield* readUser(news));
|
|
4942
5818
|
yield* guardWrite2(USER_CREATE, createForm8(news), before === undefined);
|
|
4943
5819
|
yield* guardWrite2(USER_UPDATE, updateForm5(news), false);
|
|
@@ -4948,18 +5824,18 @@ var ProxmoxUserProvider = () => Provider27.effect(ProxmoxUser, Effect59.succeed(
|
|
|
4948
5824
|
}
|
|
4949
5825
|
const after = dropUnreadable6(yield* readUser(news));
|
|
4950
5826
|
if (after === undefined) {
|
|
4951
|
-
return yield*
|
|
5827
|
+
return yield* Effect76.die(new Error(`access/users/${news.userid}: the write returned no error but the account is ` + 'still absent. PVE wraps every answer in {"data":...} and can report success ' + "on a call that did nothing -- read back rather than trusting the status code."));
|
|
4952
5828
|
}
|
|
4953
5829
|
return after;
|
|
4954
5830
|
}),
|
|
4955
|
-
delete:
|
|
5831
|
+
delete: Effect76.fn(function* ({ olds }) {
|
|
4956
5832
|
yield* runPve(olds.target, "provision", true, access7.deleteAccessUser({ userid: olds.userid }));
|
|
4957
5833
|
})
|
|
4958
5834
|
})));
|
|
4959
5835
|
|
|
4960
5836
|
// src/proxmox/provision-declare.ts
|
|
4961
|
-
var declareProvisionBaseline = (id, target, names2 = {}, options = {}) =>
|
|
4962
|
-
const baseline = yield*
|
|
5837
|
+
var declareProvisionBaseline = (id, target, names2 = {}, options = {}) => Effect77.gen(function* () {
|
|
5838
|
+
const baseline = yield* Effect77.try({
|
|
4963
5839
|
catch: (cause) => cause instanceof Error ? cause : new Error(String(cause)),
|
|
4964
5840
|
try: () => provisionBaseline(names2)
|
|
4965
5841
|
});
|
|
@@ -4973,13 +5849,13 @@ var declareProvisionBaseline = (id, target, names2 = {}, options = {}) => Effect
|
|
|
4973
5849
|
target
|
|
4974
5850
|
}));
|
|
4975
5851
|
const group = yield* owned(ProxmoxGroup(`${id}-mint-group`, { ...baseline.group, target }));
|
|
4976
|
-
const users = yield*
|
|
5852
|
+
const users = yield* Effect77.all(baseline.users.map((user) => owned(ProxmoxUser(`${id}-${user.lane}-user`, {
|
|
4977
5853
|
comment: user.comment,
|
|
4978
5854
|
groups: [...user.groups],
|
|
4979
5855
|
target,
|
|
4980
5856
|
userid: user.userid
|
|
4981
5857
|
}))));
|
|
4982
|
-
const grants = yield*
|
|
5858
|
+
const grants = yield* Effect77.all(baseline.grants.map((grant) => owned(ProxmoxAcl(`${id}-${grant.lane}-grant`, {
|
|
4983
5859
|
path: "/",
|
|
4984
5860
|
propagate: true,
|
|
4985
5861
|
roleid: grant.roleid,
|
|
@@ -4992,7 +5868,7 @@ var declareProvisionBaseline = (id, target, names2 = {}, options = {}) => Effect
|
|
|
4992
5868
|
// src/proxmox/qemu.ts
|
|
4993
5869
|
import { Resource as Resource28 } from "alchemy";
|
|
4994
5870
|
import * as Provider28 from "alchemy/Provider";
|
|
4995
|
-
import * as
|
|
5871
|
+
import * as Effect78 from "effect/Effect";
|
|
4996
5872
|
var ProxmoxVm = Resource28("Proxmox.Vm");
|
|
4997
5873
|
var createForm9 = (props) => ({ ...shape9(props), vmid: String(props.vmid) });
|
|
4998
5874
|
var shape9 = (props) => ({
|
|
@@ -5003,7 +5879,7 @@ var shape9 = (props) => ({
|
|
|
5003
5879
|
sockets: String(props.sockets ?? 1),
|
|
5004
5880
|
...props.net0 === undefined ? {} : { net0: props.net0 }
|
|
5005
5881
|
});
|
|
5006
|
-
var
|
|
5882
|
+
var handlers14 = pveHandlers({
|
|
5007
5883
|
attributes: (live, props) => ({
|
|
5008
5884
|
cores: num(live["cores"], 1),
|
|
5009
5885
|
memory: num(live["memory"], 512),
|
|
@@ -5023,11 +5899,11 @@ var handlers18 = pveHandlers({
|
|
|
5023
5899
|
path: (props) => `nodes/${props.node}/qemu/${String(props.vmid)}/config`,
|
|
5024
5900
|
updateForm: shape9
|
|
5025
5901
|
});
|
|
5026
|
-
var ProxmoxVmProvider = () => Provider28.effect(ProxmoxVm,
|
|
5902
|
+
var ProxmoxVmProvider = () => Provider28.effect(ProxmoxVm, Effect78.succeed(ProxmoxVm.Provider.of(handlers14)));
|
|
5027
5903
|
// src/proxmox/replication-job.ts
|
|
5028
5904
|
import { Resource as Resource29 } from "alchemy";
|
|
5029
5905
|
import * as Provider29 from "alchemy/Provider";
|
|
5030
|
-
import * as
|
|
5906
|
+
import * as Effect79 from "effect/Effect";
|
|
5031
5907
|
|
|
5032
5908
|
// src/proxmox/replication-job-form.ts
|
|
5033
5909
|
var DEFAULT_SCHEDULE = "*/15";
|
|
@@ -5057,7 +5933,7 @@ var rateOf = (value) => {
|
|
|
5057
5933
|
const parsed = Number.parseFloat(text(value));
|
|
5058
5934
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
5059
5935
|
};
|
|
5060
|
-
var
|
|
5936
|
+
var handlers15 = pveHandlers({
|
|
5061
5937
|
attributes: (live, props) => ({
|
|
5062
5938
|
comment: text(live["comment"], ""),
|
|
5063
5939
|
disable: bool(live["disable"], false),
|
|
@@ -5081,15 +5957,15 @@ var handlers19 = pveHandlers({
|
|
|
5081
5957
|
path: (props) => `cluster/replication/${jobId(props)}`,
|
|
5082
5958
|
updateForm: updateBody4
|
|
5083
5959
|
});
|
|
5084
|
-
var ProxmoxReplicationJobProvider = () => Provider29.effect(ProxmoxReplicationJob,
|
|
5960
|
+
var ProxmoxReplicationJobProvider = () => Provider29.effect(ProxmoxReplicationJob, Effect79.succeed(ProxmoxReplicationJob.Provider.of(handlers15)));
|
|
5085
5961
|
// src/proxmox/sdn-apply.ts
|
|
5086
5962
|
import { Resource as Resource30 } from "alchemy";
|
|
5087
|
-
import { isResolved as
|
|
5963
|
+
import { isResolved as isResolved26 } from "alchemy/Diff";
|
|
5088
5964
|
import * as Provider30 from "alchemy/Provider";
|
|
5089
|
-
import * as
|
|
5965
|
+
import * as Effect81 from "effect/Effect";
|
|
5090
5966
|
|
|
5091
5967
|
// src/proxmox/sdn-apply-read.ts
|
|
5092
|
-
import * as
|
|
5968
|
+
import * as Effect80 from "effect/Effect";
|
|
5093
5969
|
var MARKED = [
|
|
5094
5970
|
"cluster/sdn/zones",
|
|
5095
5971
|
"cluster/sdn/vnets",
|
|
@@ -5098,7 +5974,7 @@ var MARKED = [
|
|
|
5098
5974
|
"cluster/sdn/route-maps"
|
|
5099
5975
|
];
|
|
5100
5976
|
var subsystemAbsent = (error) => error instanceof PveError && error.status === 501;
|
|
5101
|
-
var markedPending = (target) =>
|
|
5977
|
+
var markedPending = (target) => Effect80.all(MARKED.map((collection) => pve(target, "read", "GET", `${collection}?pending=1`).pipe(Effect80.map((rows) => (rows ?? []).filter((row) => row.state !== undefined).length), Effect80.catchIf(subsystemAbsent, () => Effect80.succeed(0)))), { concurrency: "unbounded" }).pipe(Effect80.map((counts) => counts.reduce((total, count2) => total + count2, 0)));
|
|
5102
5978
|
var fabricStaged = (staged, running) => {
|
|
5103
5979
|
if (staged === undefined && running === undefined)
|
|
5104
5980
|
return 0;
|
|
@@ -5106,9 +5982,9 @@ var fabricStaged = (staged, running) => {
|
|
|
5106
5982
|
return "inconsistent";
|
|
5107
5983
|
return staged === running ? 0 : 1;
|
|
5108
5984
|
};
|
|
5109
|
-
var fabricPending = (target) =>
|
|
5985
|
+
var fabricPending = (target) => Effect80.all(["pending", "running"].map((view) => pve(target, "read", "GET", `cluster/sdn/fabrics/all?${view}=1`).pipe(Effect80.map(canonical3), Effect80.catchIf(subsystemAbsent, () => Effect80.succeed(undefined)))), { concurrency: "unbounded" }).pipe(Effect80.flatMap(([staged, running]) => {
|
|
5110
5986
|
const verdict = fabricStaged(staged, running);
|
|
5111
|
-
return verdict === "inconsistent" ?
|
|
5987
|
+
return verdict === "inconsistent" ? Effect80.die(new Error("cluster/sdn/fabrics/all answered 501 for one of ?pending=1 and ?running=1 and not " + "the other. Refusing to read that as a staged fabric change.")) : Effect80.succeed(verdict);
|
|
5112
5988
|
}));
|
|
5113
5989
|
var canonical3 = (value) => JSON.stringify(sortDeep(value));
|
|
5114
5990
|
var sortDeep = (value) => {
|
|
@@ -5121,40 +5997,40 @@ var sortDeep = (value) => {
|
|
|
5121
5997
|
}
|
|
5122
5998
|
return value;
|
|
5123
5999
|
};
|
|
5124
|
-
var sdnPendingCount = (target) =>
|
|
6000
|
+
var sdnPendingCount = (target) => Effect80.all([markedPending(target), fabricPending(target)], { concurrency: "unbounded" }).pipe(Effect80.map(([marked, fabric]) => marked + fabric));
|
|
5125
6001
|
|
|
5126
6002
|
// src/proxmox/sdn-apply.ts
|
|
5127
6003
|
var ProxmoxSdnApply = Resource30("Proxmox.SdnApply");
|
|
5128
|
-
var
|
|
5129
|
-
var ProxmoxSdnApplyProvider = () => Provider30.effect(ProxmoxSdnApply,
|
|
5130
|
-
list: () =>
|
|
5131
|
-
read:
|
|
5132
|
-
return yield*
|
|
6004
|
+
var read5 = (props) => sdnPendingCount(props.target).pipe(Effect81.map((pending) => ({ pending })));
|
|
6005
|
+
var ProxmoxSdnApplyProvider = () => Provider30.effect(ProxmoxSdnApply, Effect81.succeed(ProxmoxSdnApply.Provider.of({
|
|
6006
|
+
list: () => Effect81.succeed([]),
|
|
6007
|
+
read: Effect81.fn(function* ({ olds }) {
|
|
6008
|
+
return yield* read5(olds);
|
|
5133
6009
|
}),
|
|
5134
|
-
diff:
|
|
5135
|
-
if (!
|
|
6010
|
+
diff: Effect81.fn(function* ({ news }) {
|
|
6011
|
+
if (!isResolved26(news))
|
|
5136
6012
|
return;
|
|
5137
|
-
const { pending } = yield*
|
|
6013
|
+
const { pending } = yield* read5(news);
|
|
5138
6014
|
return pending === 0 ? { action: "noop" } : { action: "update" };
|
|
5139
6015
|
}),
|
|
5140
|
-
reconcile:
|
|
5141
|
-
const staged = yield*
|
|
6016
|
+
reconcile: Effect81.fn(function* ({ news }) {
|
|
6017
|
+
const staged = yield* read5(news);
|
|
5142
6018
|
if (staged.pending === 0)
|
|
5143
6019
|
return staged;
|
|
5144
6020
|
yield* guardSdnApply;
|
|
5145
6021
|
yield* pve(news.target, "provision", "PUT", "cluster/sdn");
|
|
5146
|
-
const after = yield*
|
|
6022
|
+
const after = yield* read5(news);
|
|
5147
6023
|
if (after.pending !== 0) {
|
|
5148
|
-
return yield*
|
|
6024
|
+
return yield* Effect81.die(new Error(`PUT /cluster/sdn returned no error but ${String(after.pending)} SDN object(s) ` + "are still staged. The running config does not match the declared one -- check " + "`pvesh get /cluster/sdn/zones --pending 1` for the objects PVE refused."));
|
|
5149
6025
|
}
|
|
5150
6026
|
return after;
|
|
5151
6027
|
}),
|
|
5152
|
-
delete: () =>
|
|
6028
|
+
delete: () => Effect81.void
|
|
5153
6029
|
})));
|
|
5154
6030
|
// src/proxmox/sdn-subnet.ts
|
|
5155
6031
|
import { Resource as Resource31 } from "alchemy";
|
|
5156
6032
|
import * as Provider31 from "alchemy/Provider";
|
|
5157
|
-
import * as
|
|
6033
|
+
import * as Effect82 from "effect/Effect";
|
|
5158
6034
|
|
|
5159
6035
|
// src/proxmox/sdn-subnet-form.ts
|
|
5160
6036
|
var subnetId = (props) => `${props.zone}-${props.cidr.replace("/", "-")}`;
|
|
@@ -5185,7 +6061,7 @@ var updateForm6 = (props) => {
|
|
|
5185
6061
|
|
|
5186
6062
|
// src/proxmox/sdn-subnet.ts
|
|
5187
6063
|
var ProxmoxSdnSubnet = Resource31("Proxmox.SdnSubnet");
|
|
5188
|
-
var
|
|
6064
|
+
var handlers16 = pveHandlers({
|
|
5189
6065
|
attributes: (live, props) => {
|
|
5190
6066
|
const liveVnet = text(live["vnet"]);
|
|
5191
6067
|
if (liveVnet !== "" && liveVnet !== props.vnet)
|
|
@@ -5214,11 +6090,11 @@ var handlers20 = pveHandlers({
|
|
|
5214
6090
|
path: (props) => `cluster/sdn/vnets/${props.vnet}/subnets/${subnetId(props)}`,
|
|
5215
6091
|
updateForm: updateForm6
|
|
5216
6092
|
});
|
|
5217
|
-
var ProxmoxSdnSubnetProvider = () => Provider31.effect(ProxmoxSdnSubnet,
|
|
6093
|
+
var ProxmoxSdnSubnetProvider = () => Provider31.effect(ProxmoxSdnSubnet, Effect82.succeed(ProxmoxSdnSubnet.Provider.of(handlers16)));
|
|
5218
6094
|
// src/proxmox/sdn-vnet.ts
|
|
5219
6095
|
import { Resource as Resource32 } from "alchemy";
|
|
5220
6096
|
import * as Provider32 from "alchemy/Provider";
|
|
5221
|
-
import * as
|
|
6097
|
+
import * as Effect83 from "effect/Effect";
|
|
5222
6098
|
var ProxmoxSdnVnet = Resource32("Proxmox.SdnVnet");
|
|
5223
6099
|
var tagOf = (value) => typeof value === "number" ? value : undefined;
|
|
5224
6100
|
var shape11 = (props) => {
|
|
@@ -5238,7 +6114,7 @@ var updateForm7 = (props) => {
|
|
|
5238
6114
|
];
|
|
5239
6115
|
return withClears(shape11(props), unset2);
|
|
5240
6116
|
};
|
|
5241
|
-
var
|
|
6117
|
+
var handlers17 = pveHandlers({
|
|
5242
6118
|
readRole: "provision",
|
|
5243
6119
|
attributes: (live, props) => ({
|
|
5244
6120
|
alias: typeof live["alias"] === "string" ? live["alias"] : "",
|
|
@@ -5256,11 +6132,11 @@ var handlers21 = pveHandlers({
|
|
|
5256
6132
|
path: (props) => `cluster/sdn/vnets/${props.vnet}`,
|
|
5257
6133
|
updateForm: updateForm7
|
|
5258
6134
|
});
|
|
5259
|
-
var ProxmoxSdnVnetProvider = () => Provider32.effect(ProxmoxSdnVnet,
|
|
6135
|
+
var ProxmoxSdnVnetProvider = () => Provider32.effect(ProxmoxSdnVnet, Effect83.succeed(ProxmoxSdnVnet.Provider.of(handlers17)));
|
|
5260
6136
|
// src/proxmox/sdn-zone.ts
|
|
5261
6137
|
import { Resource as Resource33 } from "alchemy";
|
|
5262
6138
|
import * as Provider33 from "alchemy/Provider";
|
|
5263
|
-
import * as
|
|
6139
|
+
import * as Effect84 from "effect/Effect";
|
|
5264
6140
|
var ProxmoxSdnZone = Resource33("Proxmox.SdnZone");
|
|
5265
6141
|
var sameIpam = (live, declared2) => (live === "" ? "pve" : live) === (declared2 === undefined || declared2 === "" ? "pve" : declared2);
|
|
5266
6142
|
var body6 = (props) => ({
|
|
@@ -5281,7 +6157,7 @@ var clearable = {
|
|
|
5281
6157
|
vlan: ["dns", "dnszone", "ipam", "mtu", "nodes"],
|
|
5282
6158
|
vxlan: ["dns", "dnszone", "ipam", "mtu", "nodes"]
|
|
5283
6159
|
};
|
|
5284
|
-
var
|
|
6160
|
+
var handlers18 = pveHandlers({
|
|
5285
6161
|
readRole: "provision",
|
|
5286
6162
|
attributes: (live, props) => {
|
|
5287
6163
|
const liveType = text(live["type"]);
|
|
@@ -5308,18 +6184,18 @@ var handlers22 = pveHandlers({
|
|
|
5308
6184
|
matches: (attributes2, props) => attributes2.mtu === (props.mtu ?? 0) && attributes2.nodes === csv(props.nodes) && sameIpam(attributes2.ipam, props.ipam) && attributes2.dns === (props.dns ?? "") && attributes2.dnszone === (props.dnszone ?? "") && attributes2.peers === csv(props.peers) && attributes2["vrf-vxlan"] === (props["vrf-vxlan"] ?? 0) && attributes2.tag === (props.tag ?? 0) && attributes2.bridge === (props.bridge ?? ""),
|
|
5309
6185
|
path: (props) => `cluster/sdn/zones/${props.zone}`,
|
|
5310
6186
|
updateForm: (props) => {
|
|
5311
|
-
const
|
|
5312
|
-
const clear = clearable[props.type].filter((option) =>
|
|
5313
|
-
return withClears(
|
|
6187
|
+
const fields2 = body6(props);
|
|
6188
|
+
const clear = clearable[props.type].filter((option) => fields2[option] === undefined);
|
|
6189
|
+
return withClears(fields2, clear);
|
|
5314
6190
|
}
|
|
5315
6191
|
});
|
|
5316
|
-
var ProxmoxSdnZoneProvider = () => Provider33.effect(ProxmoxSdnZone,
|
|
6192
|
+
var ProxmoxSdnZoneProvider = () => Provider33.effect(ProxmoxSdnZone, Effect84.succeed(ProxmoxSdnZone.Provider.of(handlers18)));
|
|
5317
6193
|
// src/proxmox/storage.ts
|
|
5318
6194
|
import { Resource as Resource34 } from "alchemy";
|
|
5319
|
-
import { isResolved as
|
|
6195
|
+
import { isResolved as isResolved27 } from "alchemy/Diff";
|
|
5320
6196
|
import * as Provider34 from "alchemy/Provider";
|
|
5321
6197
|
import * as storage2 from "@distilled.cloud/proxmox/storage";
|
|
5322
|
-
import * as
|
|
6198
|
+
import * as Effect86 from "effect/Effect";
|
|
5323
6199
|
|
|
5324
6200
|
// src/proxmox/storage-form.ts
|
|
5325
6201
|
var STORAGE_CREATE = "pve:POST /storage";
|
|
@@ -5351,7 +6227,7 @@ var toDistilledUpdate3 = (props) => underscored(updateForm8(props));
|
|
|
5351
6227
|
|
|
5352
6228
|
// src/proxmox/storage-wire.ts
|
|
5353
6229
|
import * as storage from "@distilled.cloud/proxmox/storage";
|
|
5354
|
-
import * as
|
|
6230
|
+
import * as Effect85 from "effect/Effect";
|
|
5355
6231
|
var str2 = (value, fallback = "") => typeof value === "string" ? value : fallback;
|
|
5356
6232
|
var tokens = (value) => value.split(",").map((part) => part.trim()).filter((part) => part !== "").sort().join(",");
|
|
5357
6233
|
var sameList2 = (declared2, live) => declared2 === undefined || tokens(declared2) === tokens(live);
|
|
@@ -5370,8 +6246,8 @@ var attributesOf9 = (live, props) => {
|
|
|
5370
6246
|
type: str2(data["type"], props.type)
|
|
5371
6247
|
};
|
|
5372
6248
|
};
|
|
5373
|
-
var readStorageOrFail = (props) => readOrUnreadable(runPve(props.target, "provision", false, storage.getStorage({ storage: props.storage }))).pipe(
|
|
5374
|
-
var readStorage = (props) => readStorageOrFail(props).pipe(
|
|
6249
|
+
var readStorageOrFail = (props) => readOrUnreadable(runPve(props.target, "provision", false, storage.getStorage({ storage: props.storage }))).pipe(Effect85.map((live) => live === UNREADABLE ? UNREADABLE : attributesOf9(live, props)));
|
|
6250
|
+
var readStorage = (props) => readStorageOrFail(props).pipe(Effect85.catchTag("StorageNotFound", () => Effect85.succeed(undefined)));
|
|
5375
6251
|
var dropUnreadable7 = (live) => live === UNREADABLE ? undefined : live;
|
|
5376
6252
|
var matches9 = (attributes2, props) => sameList2(props.content, attributes2.content) && sameList2(props.nodes, attributes2.nodes) && sameList2(props["prune-backups"], attributes2["prune-backups"]) && same7(props.disable, attributes2.disable) && same7(props.shared, attributes2.shared) && same7(props.preallocation, attributes2.preallocation) && same7(props.comment, attributes2.comment);
|
|
5377
6253
|
|
|
@@ -5379,13 +6255,13 @@ var matches9 = (attributes2, props) => sameList2(props.content, attributes2.cont
|
|
|
5379
6255
|
var ProxmoxStorage = Resource34("Proxmox.Storage", {
|
|
5380
6256
|
defaultRemovalPolicy: "retain"
|
|
5381
6257
|
});
|
|
5382
|
-
var ProxmoxStorageProvider = () => Provider34.effect(ProxmoxStorage,
|
|
5383
|
-
list: () =>
|
|
5384
|
-
read:
|
|
6258
|
+
var ProxmoxStorageProvider = () => Provider34.effect(ProxmoxStorage, Effect86.succeed(ProxmoxStorage.Provider.of({
|
|
6259
|
+
list: () => Effect86.succeed([]),
|
|
6260
|
+
read: Effect86.fn(function* ({ olds, output }) {
|
|
5385
6261
|
return dropUnreadable7(yield* output === undefined ? readStorage(olds) : readStorageOrFail(olds));
|
|
5386
6262
|
}),
|
|
5387
|
-
diff:
|
|
5388
|
-
if (!
|
|
6263
|
+
diff: Effect86.fn(function* ({ news, output }) {
|
|
6264
|
+
if (!isResolved27(news))
|
|
5389
6265
|
return;
|
|
5390
6266
|
yield* guardWrite2(STORAGE_CREATE, createForm11(news), output === undefined);
|
|
5391
6267
|
yield* guardWrite2(STORAGE_UPDATE, updateForm8(news), false);
|
|
@@ -5402,7 +6278,7 @@ var ProxmoxStorageProvider = () => Provider34.effect(ProxmoxStorage, Effect69.su
|
|
|
5402
6278
|
}
|
|
5403
6279
|
return matches9(live, news) ? { action: "noop" } : { action: "update" };
|
|
5404
6280
|
}),
|
|
5405
|
-
reconcile:
|
|
6281
|
+
reconcile: Effect86.fn(function* ({ news }) {
|
|
5406
6282
|
const before = dropUnreadable7(yield* readStorage(news));
|
|
5407
6283
|
yield* guardWrite2(STORAGE_CREATE, createForm11(news), before === undefined);
|
|
5408
6284
|
yield* guardWrite2(STORAGE_UPDATE, updateForm8(news), false);
|
|
@@ -5413,23 +6289,23 @@ var ProxmoxStorageProvider = () => Provider34.effect(ProxmoxStorage, Effect69.su
|
|
|
5413
6289
|
}
|
|
5414
6290
|
const after = dropUnreadable7(yield* readStorage(news));
|
|
5415
6291
|
if (after === undefined) {
|
|
5416
|
-
return yield*
|
|
6292
|
+
return yield* Effect86.die(new Error(`storage/${news.storage}: the write returned no error but the storage is still ` + 'absent. PVE wraps every answer in {"data":...} and can report success on a ' + "call that did nothing -- read back rather than trusting the status code."));
|
|
5417
6293
|
}
|
|
5418
6294
|
return after;
|
|
5419
6295
|
}),
|
|
5420
|
-
delete:
|
|
6296
|
+
delete: Effect86.fn(function* ({ olds }) {
|
|
5421
6297
|
yield* runPve(olds.target, "provision", true, storage2.deleteStorage({ storage: olds.storage }));
|
|
5422
6298
|
})
|
|
5423
6299
|
})));
|
|
5424
6300
|
// src/proxmox/zfs-pool.ts
|
|
5425
6301
|
import { Resource as Resource35 } from "alchemy";
|
|
5426
|
-
import { isResolved as
|
|
6302
|
+
import { isResolved as isResolved28 } from "alchemy/Diff";
|
|
5427
6303
|
import * as Provider35 from "alchemy/Provider";
|
|
5428
|
-
import * as
|
|
6304
|
+
import * as Effect89 from "effect/Effect";
|
|
5429
6305
|
|
|
5430
6306
|
// src/proxmox/zfs-pool-wire.ts
|
|
5431
6307
|
import * as nodes10 from "@distilled.cloud/proxmox/nodes";
|
|
5432
|
-
import * as
|
|
6308
|
+
import * as Effect87 from "effect/Effect";
|
|
5433
6309
|
var leafDevices = (children) => Array.isArray(children) ? children.flatMap((entry) => {
|
|
5434
6310
|
const vdev = entry;
|
|
5435
6311
|
const nested = leafDevices(vdev.children);
|
|
@@ -5442,18 +6318,18 @@ var attributesOf10 = (live, props) => ({
|
|
|
5442
6318
|
node: props.node,
|
|
5443
6319
|
state: text(live.state, "unknown")
|
|
5444
6320
|
});
|
|
5445
|
-
var readPoolOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, nodes10.getNodeDiskZfs({ name: props.name, node: props.node }))).pipe(
|
|
6321
|
+
var readPoolOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, nodes10.getNodeDiskZfs({ name: props.name, node: props.node }))).pipe(Effect87.map((live) => {
|
|
5446
6322
|
if (live === UNREADABLE)
|
|
5447
6323
|
return UNREADABLE;
|
|
5448
6324
|
return text(live.name) === "" ? undefined : attributesOf10(live, props);
|
|
5449
6325
|
}));
|
|
5450
|
-
var
|
|
6326
|
+
var readPool2 = (props) => readPoolOrFail(props).pipe(Effect87.orElseSucceed(() => {
|
|
5451
6327
|
return;
|
|
5452
6328
|
}));
|
|
5453
6329
|
var dropUnreadable8 = (live) => live === UNREADABLE ? undefined : live;
|
|
5454
6330
|
|
|
5455
6331
|
// src/proxmox/zfs-pool-form.ts
|
|
5456
|
-
import * as
|
|
6332
|
+
import * as Effect88 from "effect/Effect";
|
|
5457
6333
|
import * as nodes11 from "@distilled.cloud/proxmox/nodes";
|
|
5458
6334
|
var ZFS_POOL_CREATE = "pve:POST /nodes/{node}/disks/zfs";
|
|
5459
6335
|
var zfsPoolCreateEndpoint = (props) => props.devices === undefined || props.raidlevel === undefined ? undefined : ZFS_POOL_CREATE;
|
|
@@ -5474,25 +6350,25 @@ var toDistilledCreate5 = (props) => {
|
|
|
5474
6350
|
};
|
|
5475
6351
|
};
|
|
5476
6352
|
var SETTLE_ATTEMPTS = 30;
|
|
5477
|
-
var
|
|
5478
|
-
const existing = yield*
|
|
6353
|
+
var createPool2 = (props, read6) => Effect88.gen(function* () {
|
|
6354
|
+
const existing = yield* read6(props);
|
|
5479
6355
|
if (existing !== undefined)
|
|
5480
6356
|
return existing;
|
|
5481
6357
|
if (props.devices === undefined || props.raidlevel === undefined) {
|
|
5482
|
-
return yield*
|
|
6358
|
+
return yield* Effect88.die(new Error(`${props.node}/${props.name}: this is an adopt-only declaration (no \`devices\` or ` + "`raidlevel`) and the pool is not there to adopt. An adopt-only Proxmox.ZfsPool never " + "creates -- declare both fields for a pool this resource should build, or point this " + "declaration at a pool that already exists on this node."));
|
|
5483
6359
|
}
|
|
5484
6360
|
yield* guardForm(zfsPoolCreateEndpoint(props), asForm2(createForm12(props)), true);
|
|
5485
6361
|
const upid = yield* runPve(props.target, "provision", true, nodes11.createNodeDiskZfs(toDistilledCreate5(props)));
|
|
5486
|
-
const
|
|
5487
|
-
const live = yield*
|
|
6362
|
+
const settle3 = (attempts) => Effect88.gen(function* () {
|
|
6363
|
+
const live = yield* read6(props);
|
|
5488
6364
|
if (live !== undefined || attempts <= 0)
|
|
5489
6365
|
return live;
|
|
5490
|
-
yield*
|
|
5491
|
-
return yield*
|
|
6366
|
+
yield* Effect88.sleep("2 seconds");
|
|
6367
|
+
return yield* settle3(attempts - 1);
|
|
5492
6368
|
});
|
|
5493
|
-
const after = yield*
|
|
6369
|
+
const after = yield* settle3(SETTLE_ATTEMPTS);
|
|
5494
6370
|
if (after === undefined) {
|
|
5495
|
-
return yield*
|
|
6371
|
+
return yield* Effect88.die(new Error(`nodes/${props.node}/disks/zfs/${props.name}: POST returned ${text(upid, "(no UPID)")} ` + "but the pool has not appeared. That POST only forks a `zfscreate` worker, so the task " + `holds the real error -- read it with \`pvesh get /nodes/${props.node}/tasks/<upid>/log\`. ` + "NOTHING WAS RETRIED AND NOTHING WAS CLEANED UP: if the worker reached `zpool create`, " + "the declared devices have already been written to. DO NOT RE-RUN THIS DEPLOY BEFORE " + 'READING THAT LOG: `read` cannot tell "the worker is still running" from "the pool is ' + 'genuinely gone", so a re-run this soon can POST a SECOND `zpool create` at the SAME ' + "devices while the first one is still mid-write -- confirm the task finished (one way " + "or the other) before trying again."));
|
|
5496
6372
|
}
|
|
5497
6373
|
return after;
|
|
5498
6374
|
});
|
|
@@ -5502,13 +6378,13 @@ var destroyPool = (props) => runPve(props.target, "provision", true, nodes11.del
|
|
|
5502
6378
|
var ProxmoxZfsPool = Resource35("Proxmox.ZfsPool", {
|
|
5503
6379
|
defaultRemovalPolicy: "retain"
|
|
5504
6380
|
});
|
|
5505
|
-
var ProxmoxZfsPoolProvider = () => Provider35.effect(ProxmoxZfsPool,
|
|
5506
|
-
list: () =>
|
|
5507
|
-
read:
|
|
5508
|
-
return dropUnreadable8(yield* output === undefined ?
|
|
6381
|
+
var ProxmoxZfsPoolProvider = () => Provider35.effect(ProxmoxZfsPool, Effect89.succeed(ProxmoxZfsPool.Provider.of({
|
|
6382
|
+
list: () => Effect89.succeed([]),
|
|
6383
|
+
read: Effect89.fn(function* ({ olds, output }) {
|
|
6384
|
+
return dropUnreadable8(yield* output === undefined ? readPool2(olds) : readPoolOrFail(olds));
|
|
5509
6385
|
}),
|
|
5510
|
-
diff:
|
|
5511
|
-
if (!
|
|
6386
|
+
diff: Effect89.fn(function* ({ news, output }) {
|
|
6387
|
+
if (!isResolved28(news))
|
|
5512
6388
|
return;
|
|
5513
6389
|
yield* guardForm(zfsPoolCreateEndpoint(news), asForm2(createForm12(news)), output === undefined);
|
|
5514
6390
|
if (output === undefined)
|
|
@@ -5524,10 +6400,10 @@ var ProxmoxZfsPoolProvider = () => Provider35.effect(ProxmoxZfsPool, Effect72.su
|
|
|
5524
6400
|
}
|
|
5525
6401
|
return { action: "noop" };
|
|
5526
6402
|
}),
|
|
5527
|
-
reconcile:
|
|
5528
|
-
return yield*
|
|
6403
|
+
reconcile: Effect89.fn(function* ({ news }) {
|
|
6404
|
+
return yield* createPool2(news, (props) => readPool2(props).pipe(Effect89.map(dropUnreadable8)));
|
|
5529
6405
|
}),
|
|
5530
|
-
delete:
|
|
6406
|
+
delete: Effect89.fn(function* ({ olds }) {
|
|
5531
6407
|
yield* destroyPool(olds);
|
|
5532
6408
|
})
|
|
5533
6409
|
})));
|
|
@@ -5607,5 +6483,5 @@ export {
|
|
|
5607
6483
|
violations
|
|
5608
6484
|
};
|
|
5609
6485
|
|
|
5610
|
-
//# debugId=
|
|
6486
|
+
//# debugId=ECA89AEF81AD3AD164756E2164756E21
|
|
5611
6487
|
//# sourceMappingURL=index.js.map
|