@homeflare/alchemy 0.36.0 → 0.37.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -0
- package/dist/argocd/cluster.d.ts +102 -0
- package/dist/argocd/cluster.d.ts.map +1 -0
- package/dist/argocd/credentials.d.ts +37 -0
- package/dist/argocd/credentials.d.ts.map +1 -0
- package/dist/argocd/fake-argocd.d.ts +42 -0
- package/dist/argocd/fake-argocd.d.ts.map +1 -0
- package/dist/argocd/git-credentials.d.ts +29 -0
- package/dist/argocd/git-credentials.d.ts.map +1 -0
- package/dist/argocd/index.d.ts +19 -0
- package/dist/argocd/index.d.ts.map +1 -0
- package/dist/argocd/index.js +306 -0
- package/dist/argocd/index.js.map +17 -0
- package/dist/argocd/providers.d.ts +13 -0
- package/dist/argocd/providers.d.ts.map +1 -0
- package/dist/argocd/repo-creds.d.ts +67 -0
- package/dist/argocd/repo-creds.d.ts.map +1 -0
- package/dist/argocd/repository.d.ts +87 -0
- package/dist/argocd/repository.d.ts.map +1 -0
- package/dist/argocd/resource.d.ts +90 -0
- package/dist/argocd/resource.d.ts.map +1 -0
- package/dist/cloudflare/index.js +4 -4
- package/dist/cloudflare/index.js.map +3 -3
- package/dist/cloudflare/r2-bucket-lock.d.ts +14 -5
- package/dist/cloudflare/r2-bucket-lock.d.ts.map +1 -1
- package/dist/litellm/index.js.map +2 -2
- package/dist/litellm/operations.d.ts.map +1 -1
- package/dist/proxmox/ceph-daemon-form.d.ts +4 -5
- package/dist/proxmox/ceph-daemon-form.d.ts.map +1 -1
- package/dist/proxmox/ceph-daemon-wire.d.ts +31 -0
- package/dist/proxmox/ceph-daemon-wire.d.ts.map +1 -0
- package/dist/proxmox/ceph-daemon.d.ts +40 -6
- package/dist/proxmox/ceph-daemon.d.ts.map +1 -1
- package/dist/proxmox/ceph-fs-distilled.d.ts +33 -0
- package/dist/proxmox/ceph-fs-distilled.d.ts.map +1 -0
- package/dist/proxmox/ceph-fs-wire.d.ts +20 -11
- package/dist/proxmox/ceph-fs-wire.d.ts.map +1 -1
- package/dist/proxmox/ceph-fs.d.ts +30 -6
- package/dist/proxmox/ceph-fs.d.ts.map +1 -1
- package/dist/proxmox/ceph-osd-tree.d.ts +12 -3
- package/dist/proxmox/ceph-osd-tree.d.ts.map +1 -1
- package/dist/proxmox/ceph-osd-write.d.ts +17 -2
- package/dist/proxmox/ceph-osd-write.d.ts.map +1 -1
- package/dist/proxmox/ceph-osd.d.ts +6 -0
- package/dist/proxmox/ceph-osd.d.ts.map +1 -1
- package/dist/proxmox/ceph-pool-settle.d.ts +19 -12
- package/dist/proxmox/ceph-pool-settle.d.ts.map +1 -1
- package/dist/proxmox/ceph-pool-wire.d.ts +37 -0
- package/dist/proxmox/ceph-pool-wire.d.ts.map +1 -0
- package/dist/proxmox/ceph-pool.d.ts +10 -5
- package/dist/proxmox/ceph-pool.d.ts.map +1 -1
- package/dist/proxmox/group-wire.d.ts +8 -13
- package/dist/proxmox/group-wire.d.ts.map +1 -1
- package/dist/proxmox/index.js +605 -483
- package/dist/proxmox/index.js.map +20 -17
- package/dist/proxmox/storage-wire.d.ts +15 -11
- package/dist/proxmox/storage-wire.d.ts.map +1 -1
- package/dist/proxmox/unreadable-read.d.ts +7 -7
- package/dist/proxmox/user-wire.d.ts +8 -11
- package/dist/proxmox/user-wire.d.ts.map +1 -1
- package/dist/proxmox/user.d.ts +5 -5
- package/docs/argocd-kubernetes.md +100 -0
- package/docs/argocd.md +139 -0
- package/docs/credentials.md +9 -0
- package/docs/distilled-interim.md +15 -3
- package/docs/litellm.md +17 -0
- package/docs/peers.md +7 -0
- package/docs/provider-standard.md +24 -6
- package/docs/proxmox.md +20 -0
- package/docs/unifi.md +45 -0
- package/docs/upstream-conformance-bun.md +67 -0
- package/docs/upstream-conformance-conforms.md +55 -0
- package/docs/upstream-conformance-families.md +95 -0
- package/docs/upstream-conformance-grafana.md +106 -0
- package/docs/upstream-conformance.md +20 -298
- package/package.json +8 -3
- package/src/argocd/cluster.test.ts +120 -0
- package/src/argocd/cluster.ts +222 -0
- package/src/argocd/credentials.ts +38 -0
- package/src/argocd/fake-argocd.ts +60 -0
- package/src/argocd/git-credentials.ts +71 -0
- package/src/argocd/index.ts +40 -0
- package/src/argocd/providers.ts +17 -0
- package/src/argocd/repo-creds.test.ts +123 -0
- package/src/argocd/repo-creds.ts +115 -0
- package/src/argocd/repository.test.ts +167 -0
- package/src/argocd/repository.ts +165 -0
- package/src/argocd/resource.ts +150 -0
- package/src/cloudflare/r2-bucket-lock.test.ts +54 -18
- package/src/cloudflare/r2-bucket-lock.ts +25 -18
- package/src/litellm/operations.ts +8 -4
- package/src/proxmox/ceph-daemon-form.ts +7 -8
- package/src/proxmox/ceph-daemon-wire.ts +112 -0
- package/src/proxmox/ceph-daemon-write.test.ts +158 -0
- package/src/proxmox/ceph-daemon.ts +70 -53
- package/src/proxmox/ceph-fs-delete.test.ts +81 -0
- package/src/proxmox/ceph-fs-distilled.ts +100 -0
- package/src/proxmox/ceph-fs-wire.ts +17 -31
- package/src/proxmox/ceph-fs-write.test.ts +67 -0
- package/src/proxmox/ceph-fs.ts +68 -73
- package/src/proxmox/ceph-osd-tree.ts +14 -5
- package/src/proxmox/ceph-osd-write.test.ts +61 -0
- package/src/proxmox/ceph-osd-write.ts +33 -6
- package/src/proxmox/ceph-osd.ts +6 -0
- package/src/proxmox/ceph-pool-settle.ts +24 -17
- package/src/proxmox/ceph-pool-wire.ts +101 -0
- package/src/proxmox/ceph-pool-write.test.ts +146 -0
- package/src/proxmox/ceph-pool.ts +87 -98
- package/src/proxmox/group-wire.ts +7 -12
- package/src/proxmox/read-failure-propagation.test.ts +5 -5
- package/src/proxmox/storage-wire.ts +9 -10
- package/src/proxmox/typed-absence.test.ts +146 -0
- package/src/proxmox/unreadable-read.ts +7 -7
- package/src/proxmox/user-wire.ts +7 -10
- package/src/proxmox/user.ts +5 -5
- package/src/proxmox/distilled-task-await.test.ts +0 -124
package/dist/proxmox/index.js
CHANGED
|
@@ -1635,14 +1635,15 @@ var handlers = pveHandlers({
|
|
|
1635
1635
|
var ProxmoxBackupJobProvider = () => Provider3.effect(ProxmoxBackupJob, Effect12.succeed(ProxmoxBackupJob.Provider.of(handlers)));
|
|
1636
1636
|
// src/proxmox/ceph-daemon.ts
|
|
1637
1637
|
import { Resource as Resource4 } from "alchemy";
|
|
1638
|
+
import { isResolved as isResolved4 } from "alchemy/Diff";
|
|
1638
1639
|
import * as Provider4 from "alchemy/Provider";
|
|
1639
|
-
import * as
|
|
1640
|
+
import * as Effect14 from "effect/Effect";
|
|
1640
1641
|
|
|
1641
1642
|
// src/proxmox/ceph-daemon-form.ts
|
|
1642
1643
|
var DAEMON_ENDPOINTS = {
|
|
1643
|
-
mds:
|
|
1644
|
-
mgr:
|
|
1645
|
-
mon:
|
|
1644
|
+
mds: "pve:POST /nodes/{node}/ceph/mds/{name}",
|
|
1645
|
+
mgr: "pve:POST /nodes/{node}/ceph/mgr/{id}",
|
|
1646
|
+
mon: "pve:POST /nodes/{node}/ceph/mon/{monid}"
|
|
1646
1647
|
};
|
|
1647
1648
|
var daemonId = (props) => props.name ?? props.node;
|
|
1648
1649
|
var collectionPath = (props) => `nodes/${props.node}/ceph/${props.kind}`;
|
|
@@ -1675,28 +1676,95 @@ var daemonAttributes = (live, props) => {
|
|
|
1675
1676
|
};
|
|
1676
1677
|
};
|
|
1677
1678
|
|
|
1679
|
+
// src/proxmox/ceph-daemon-wire.ts
|
|
1680
|
+
import * as nodes from "@distilled.cloud/proxmox/nodes";
|
|
1681
|
+
import * as Effect13 from "effect/Effect";
|
|
1682
|
+
var listRows = (props) => {
|
|
1683
|
+
const node = props.node;
|
|
1684
|
+
switch (props.kind) {
|
|
1685
|
+
case "mon":
|
|
1686
|
+
return runPve(props.target, "read", false, nodes.listNodeCephMon({ node })).pipe(Effect13.map((rows) => rows));
|
|
1687
|
+
case "mgr":
|
|
1688
|
+
return runPve(props.target, "read", false, nodes.listNodeCephMgr({ node })).pipe(Effect13.map((rows) => rows));
|
|
1689
|
+
case "mds":
|
|
1690
|
+
return runPve(props.target, "read", false, nodes.listNodeCephMds({ node })).pipe(Effect13.map((rows) => rows));
|
|
1691
|
+
}
|
|
1692
|
+
};
|
|
1693
|
+
var readDaemon = (props) => listRows(props).pipe(Effect13.map((rows) => daemonAttributes(rows, props)));
|
|
1694
|
+
var createDaemon = (props) => {
|
|
1695
|
+
const node = props.node;
|
|
1696
|
+
const id = daemonId(props);
|
|
1697
|
+
switch (props.kind) {
|
|
1698
|
+
case "mon":
|
|
1699
|
+
return runPve(props.target, "provision", true, nodes.updateNodeCephMon({
|
|
1700
|
+
...props["mon-address"] === undefined ? {} : { mon_address: props["mon-address"] },
|
|
1701
|
+
monid: id,
|
|
1702
|
+
node
|
|
1703
|
+
}));
|
|
1704
|
+
case "mgr":
|
|
1705
|
+
return runPve(props.target, "provision", true, nodes.updateNodeCephMgr({ id, node }));
|
|
1706
|
+
case "mds":
|
|
1707
|
+
return runPve(props.target, "provision", true, nodes.updateNodeCephMds({
|
|
1708
|
+
...props.hotstandby === undefined ? {} : { hotstandby: props.hotstandby ? "1" : "0" },
|
|
1709
|
+
name: id,
|
|
1710
|
+
node
|
|
1711
|
+
}));
|
|
1712
|
+
}
|
|
1713
|
+
};
|
|
1714
|
+
var deleteDaemon = (props) => {
|
|
1715
|
+
const node = props.node;
|
|
1716
|
+
const id = daemonId(props);
|
|
1717
|
+
switch (props.kind) {
|
|
1718
|
+
case "mon":
|
|
1719
|
+
return runPve(props.target, "provision", true, nodes.deleteNodeCephMon({ monid: id, node }));
|
|
1720
|
+
case "mgr":
|
|
1721
|
+
return runPve(props.target, "provision", true, nodes.deleteNodeCephMgr({ id, node }));
|
|
1722
|
+
case "mds":
|
|
1723
|
+
return runPve(props.target, "provision", true, nodes.deleteNodeCephMds({ name: id, node }));
|
|
1724
|
+
}
|
|
1725
|
+
};
|
|
1726
|
+
var notCreated = (props) => new Error(`${daemonPath(props)}: no ${props.kind} named ${daemonId(props)} in GET nodes/` + `${props.node}/ceph/${props.kind} after the create task finished. POST answers a UPID, ` + "not a result -- watch the task in the PVE UI (ceph-daemon-form.ts).");
|
|
1727
|
+
|
|
1678
1728
|
// src/proxmox/ceph-daemon.ts
|
|
1679
1729
|
var ProxmoxCephDaemon = Resource4("Proxmox.CephDaemon", {
|
|
1680
1730
|
defaultRemovalPolicy: "retain"
|
|
1681
1731
|
});
|
|
1682
|
-
var
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1732
|
+
var reconcileDaemon = Effect14.fn(function* ({ news }) {
|
|
1733
|
+
const live = yield* readDaemon(news);
|
|
1734
|
+
yield* guardWrite2(DAEMON_ENDPOINTS[news.kind], createForm(news), live === undefined);
|
|
1735
|
+
if (live !== undefined)
|
|
1736
|
+
return live;
|
|
1737
|
+
yield* createDaemon(news);
|
|
1738
|
+
const after = yield* readDaemon(news);
|
|
1739
|
+
if (after === undefined)
|
|
1740
|
+
return yield* Effect14.die(notCreated(news));
|
|
1741
|
+
return after;
|
|
1742
|
+
});
|
|
1743
|
+
var ProxmoxCephDaemonProvider = () => Provider4.effect(ProxmoxCephDaemon, Effect14.succeed(ProxmoxCephDaemon.Provider.of({
|
|
1744
|
+
list: () => Effect14.succeed([]),
|
|
1745
|
+
read: ({ olds }) => readDaemon(olds),
|
|
1746
|
+
diff: Effect14.fn(function* ({ news, output }) {
|
|
1747
|
+
if (!isResolved4(news))
|
|
1748
|
+
return;
|
|
1749
|
+
yield* guardWrite2(DAEMON_ENDPOINTS[news.kind], createForm(news), output === undefined);
|
|
1750
|
+
if (output === undefined)
|
|
1751
|
+
return;
|
|
1752
|
+
const live = yield* readDaemon(news);
|
|
1753
|
+
if (live === undefined) {
|
|
1754
|
+
yield* guardWrite2(DAEMON_ENDPOINTS[news.kind], createForm(news), true);
|
|
1755
|
+
return { action: "update" };
|
|
1756
|
+
}
|
|
1757
|
+
return { action: "noop" };
|
|
1758
|
+
}),
|
|
1759
|
+
reconcile: ({ news }) => reconcileDaemon({ news }),
|
|
1760
|
+
delete: ({ olds }) => deleteDaemon(olds)
|
|
1693
1761
|
})));
|
|
1694
1762
|
// src/proxmox/ceph-flag.ts
|
|
1695
1763
|
import { Resource as Resource5 } from "alchemy";
|
|
1696
1764
|
import * as Provider5 from "alchemy/Provider";
|
|
1697
|
-
import * as
|
|
1765
|
+
import * as Effect15 from "effect/Effect";
|
|
1698
1766
|
var ProxmoxCephFlag = Resource5("Proxmox.CephFlag");
|
|
1699
|
-
var
|
|
1767
|
+
var handlers2 = pveHandlers({
|
|
1700
1768
|
attributes: (live, props) => ({ flag: props.flag, value: bool(live) }),
|
|
1701
1769
|
collection: () => "cluster/ceph/flags",
|
|
1702
1770
|
createForm: (props) => ({ [props.flag]: flag(props.value) ?? "0" }),
|
|
@@ -1705,25 +1773,29 @@ var handlers3 = pveHandlers({
|
|
|
1705
1773
|
path: (props) => `cluster/ceph/flags/${props.flag}`,
|
|
1706
1774
|
updateForm: (props) => ({ value: flag(props.value) ?? "0" })
|
|
1707
1775
|
});
|
|
1708
|
-
var ProxmoxCephFlagProvider = () => Provider5.effect(ProxmoxCephFlag,
|
|
1709
|
-
...
|
|
1710
|
-
delete: () =>
|
|
1711
|
-
reconcile:
|
|
1712
|
-
const after = yield*
|
|
1776
|
+
var ProxmoxCephFlagProvider = () => Provider5.effect(ProxmoxCephFlag, Effect15.succeed(ProxmoxCephFlag.Provider.of({
|
|
1777
|
+
...handlers2,
|
|
1778
|
+
delete: () => Effect15.void,
|
|
1779
|
+
reconcile: Effect15.fn(function* ({ news }) {
|
|
1780
|
+
const after = yield* handlers2.reconcile({ news });
|
|
1713
1781
|
if (after.value !== news.value) {
|
|
1714
|
-
return yield*
|
|
1782
|
+
return yield* Effect15.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."));
|
|
1715
1783
|
}
|
|
1716
1784
|
return after;
|
|
1717
1785
|
})
|
|
1718
1786
|
})));
|
|
1719
1787
|
// src/proxmox/ceph-fs.ts
|
|
1720
1788
|
import { Resource as Resource6 } from "alchemy";
|
|
1721
|
-
import { isResolved as
|
|
1789
|
+
import { isResolved as isResolved5 } from "alchemy/Diff";
|
|
1722
1790
|
import * as Provider6 from "alchemy/Provider";
|
|
1723
|
-
import * as
|
|
1791
|
+
import * as Effect18 from "effect/Effect";
|
|
1792
|
+
|
|
1793
|
+
// src/proxmox/ceph-fs-distilled.ts
|
|
1794
|
+
import * as nodes2 from "@distilled.cloud/proxmox/nodes";
|
|
1795
|
+
import * as Effect17 from "effect/Effect";
|
|
1724
1796
|
|
|
1725
1797
|
// src/proxmox/ceph-fs-wire.ts
|
|
1726
|
-
import * as
|
|
1798
|
+
import * as Effect16 from "effect/Effect";
|
|
1727
1799
|
var readRow = (live, props) => {
|
|
1728
1800
|
const row = (Array.isArray(live) ? live : []).filter((entry) => typeof entry === "object" && entry !== null).find((entry) => entry["name"] === props.name);
|
|
1729
1801
|
if (row === undefined)
|
|
@@ -1739,6 +1811,7 @@ var readRow = (live, props) => {
|
|
|
1739
1811
|
var POLL_SECONDS = 2;
|
|
1740
1812
|
var POLL_ATTEMPTS = 45;
|
|
1741
1813
|
var objectPath = (props) => `nodes/${props.node}/ceph/fs/${props.name}`;
|
|
1814
|
+
var CEPH_FS_CREATE = "pve:POST /nodes/{node}/ceph/fs/{name}";
|
|
1742
1815
|
var field = (name, value) => value === undefined ? {} : { [name]: value };
|
|
1743
1816
|
var createForm2 = (props) => ({
|
|
1744
1817
|
...field("add-storage", flag(props["add-storage"])),
|
|
@@ -1751,86 +1824,111 @@ var destroyPath = (props) => {
|
|
|
1751
1824
|
].filter((part) => part !== undefined);
|
|
1752
1825
|
return query.length === 0 ? objectPath(props) : `${objectPath(props)}?${query.join("&")}`;
|
|
1753
1826
|
};
|
|
1754
|
-
var settle = (target, node, upid, what) =>
|
|
1827
|
+
var settle = (target, node, upid, what) => Effect16.gen(function* () {
|
|
1755
1828
|
for (let attempt = 0;attempt < POLL_ATTEMPTS; attempt += 1) {
|
|
1756
|
-
const status = yield* pve(target, "read", "GET", `nodes/${node}/tasks/${encodeURIComponent(upid)}/status`).pipe(
|
|
1829
|
+
const status = yield* pve(target, "read", "GET", `nodes/${node}/tasks/${encodeURIComponent(upid)}/status`).pipe(Effect16.orElseSucceed(() => {
|
|
1757
1830
|
return;
|
|
1758
1831
|
}));
|
|
1759
1832
|
if (status?.status === "stopped") {
|
|
1760
1833
|
const exit = text(status.exitstatus);
|
|
1761
1834
|
if (exit === "OK")
|
|
1762
1835
|
return;
|
|
1763
|
-
return yield*
|
|
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\`.`));
|
|
1764
1837
|
}
|
|
1765
|
-
yield*
|
|
1838
|
+
yield* Effect16.sleep(POLL_SECONDS * 1000);
|
|
1766
1839
|
}
|
|
1767
|
-
return yield*
|
|
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."));
|
|
1768
1841
|
});
|
|
1769
|
-
var
|
|
1770
|
-
const upid = text(yield* pve(props.target, "provision", "POST", objectPath(props), createForm2(props)));
|
|
1771
|
-
if (upid === "") {
|
|
1772
|
-
return yield* Effect15.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.'));
|
|
1773
|
-
}
|
|
1774
|
-
yield* settle(props.target, props.node, upid, objectPath(props));
|
|
1775
|
-
});
|
|
1776
|
-
var destroyFs = (props) => Effect15.gen(function* () {
|
|
1842
|
+
var destroyFs = (props) => Effect16.gen(function* () {
|
|
1777
1843
|
const upid = text(yield* pve(props.target, "provision", "DELETE", destroyPath(props)));
|
|
1778
1844
|
if (upid !== "")
|
|
1779
1845
|
yield* settle(props.target, props.node, upid, destroyPath(props));
|
|
1780
1846
|
});
|
|
1781
|
-
var
|
|
1847
|
+
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.");
|
|
1782
1848
|
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.");
|
|
1783
1849
|
|
|
1850
|
+
// src/proxmox/ceph-fs-distilled.ts
|
|
1851
|
+
var readFs = (props) => runPve(props.target, "read", false, nodes2.listNodeCephFs({ node: props.node })).pipe(Effect17.map((rows) => readRow(rows, props)));
|
|
1852
|
+
var toDistilledCreate = (props) => ({
|
|
1853
|
+
...createForm2(props),
|
|
1854
|
+
name: props.name,
|
|
1855
|
+
node: props.node
|
|
1856
|
+
});
|
|
1857
|
+
var settleCreate = (props, upid) => Effect17.gen(function* () {
|
|
1858
|
+
for (let attempt = 0;attempt < POLL_ATTEMPTS; attempt += 1) {
|
|
1859
|
+
const status = yield* runPve(props.target, "read", false, nodes2.getNodeTaskStatus({ node: props.node, upid })).pipe(Effect17.orElseSucceed(() => {
|
|
1860
|
+
return;
|
|
1861
|
+
}));
|
|
1862
|
+
if (status?.status === "stopped") {
|
|
1863
|
+
if (status.exitstatus === "OK")
|
|
1864
|
+
return;
|
|
1865
|
+
return yield* Effect17.die(new Error(`${objectPath(props)}: PVE task ${upid} finished with "${status.exitstatus ?? "no exit status"}". ` + "The write returned HTTP 200 because it only forked the worker -- the real error is " + `in \`pvesh get /nodes/${props.node}/tasks/${upid}/log\`.`));
|
|
1866
|
+
}
|
|
1867
|
+
yield* Effect17.sleep(POLL_SECONDS * 1000);
|
|
1868
|
+
}
|
|
1869
|
+
return yield* Effect17.die(new Error(`${objectPath(props)}: 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/${props.node} -- each call mints a new token, so the poller is never the task ` + "owner and is answered 403 rather than told to wait."));
|
|
1870
|
+
});
|
|
1871
|
+
var createFs = (props) => Effect17.gen(function* () {
|
|
1872
|
+
const upid = yield* runPve(props.target, "provision", true, nodes2.updateNodeCephFs(toDistilledCreate(props)));
|
|
1873
|
+
if (upid === "") {
|
|
1874
|
+
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
|
+
}
|
|
1876
|
+
yield* settleCreate(props, upid);
|
|
1877
|
+
});
|
|
1878
|
+
|
|
1784
1879
|
// src/proxmox/ceph-fs.ts
|
|
1785
1880
|
var ProxmoxCephFs = Resource6("Proxmox.CephFs", {
|
|
1786
1881
|
defaultRemovalPolicy: "retain"
|
|
1787
1882
|
});
|
|
1788
|
-
var
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
endpoint: { create: "pve:POST /nodes/{node}/ceph/fs/{name}" },
|
|
1793
|
-
matches: () => true,
|
|
1794
|
-
path: (props) => `nodes/${props.node}/ceph/fs`
|
|
1795
|
-
});
|
|
1796
|
-
var ProxmoxCephFsProvider = () => Provider6.effect(ProxmoxCephFs, Effect16.succeed(ProxmoxCephFs.Provider.of({
|
|
1797
|
-
list: () => Effect16.succeed([]),
|
|
1798
|
-
read: Effect16.fn(function* ({ olds }) {
|
|
1799
|
-
return yield* ops.read(olds);
|
|
1883
|
+
var ProxmoxCephFsProvider = () => Provider6.effect(ProxmoxCephFs, Effect18.succeed(ProxmoxCephFs.Provider.of({
|
|
1884
|
+
list: () => Effect18.succeed([]),
|
|
1885
|
+
read: Effect18.fn(function* ({ olds }) {
|
|
1886
|
+
return yield* readFs(olds);
|
|
1800
1887
|
}),
|
|
1801
|
-
diff:
|
|
1802
|
-
if (
|
|
1888
|
+
diff: Effect18.fn(function* ({ news, output }) {
|
|
1889
|
+
if (!isResolved5(news))
|
|
1890
|
+
return;
|
|
1891
|
+
yield* guardWrite2(CEPH_FS_CREATE, createForm2(news), output === undefined);
|
|
1892
|
+
if (output !== undefined && news.name !== output.name) {
|
|
1803
1893
|
return { action: "replace" };
|
|
1804
1894
|
}
|
|
1805
|
-
|
|
1895
|
+
if (output === undefined)
|
|
1896
|
+
return;
|
|
1897
|
+
const live = yield* readFs(news);
|
|
1898
|
+
if (live === undefined) {
|
|
1899
|
+
yield* guardWrite2(CEPH_FS_CREATE, createForm2(news), true);
|
|
1900
|
+
return { action: "update" };
|
|
1901
|
+
}
|
|
1902
|
+
return { action: "noop" };
|
|
1806
1903
|
}),
|
|
1807
|
-
reconcile:
|
|
1808
|
-
const live = yield*
|
|
1904
|
+
reconcile: Effect18.fn(function* ({ news }) {
|
|
1905
|
+
const live = yield* readFs(news);
|
|
1809
1906
|
if (live !== undefined)
|
|
1810
1907
|
return live;
|
|
1811
1908
|
yield* createFs(news);
|
|
1812
|
-
const after = yield*
|
|
1909
|
+
const after = yield* readFs(news);
|
|
1813
1910
|
if (after === undefined)
|
|
1814
|
-
return yield*
|
|
1911
|
+
return yield* Effect18.die(notCreated2(news));
|
|
1815
1912
|
return after;
|
|
1816
1913
|
}),
|
|
1817
|
-
delete:
|
|
1818
|
-
if ((yield*
|
|
1914
|
+
delete: Effect18.fn(function* ({ olds }) {
|
|
1915
|
+
if ((yield* readFs(olds)) === undefined)
|
|
1819
1916
|
return;
|
|
1820
1917
|
yield* destroyFs(olds);
|
|
1821
|
-
const after = yield*
|
|
1918
|
+
const after = yield* readFs(olds);
|
|
1822
1919
|
if (after !== undefined)
|
|
1823
|
-
return yield*
|
|
1920
|
+
return yield* Effect18.die(notDestroyed(olds));
|
|
1824
1921
|
})
|
|
1825
1922
|
})));
|
|
1826
1923
|
// src/proxmox/ceph-osd.ts
|
|
1827
1924
|
import { Resource as Resource7 } from "alchemy";
|
|
1828
|
-
import { isResolved as
|
|
1925
|
+
import { isResolved as isResolved6 } from "alchemy/Diff";
|
|
1829
1926
|
import * as Provider7 from "alchemy/Provider";
|
|
1830
|
-
import * as
|
|
1927
|
+
import * as Effect21 from "effect/Effect";
|
|
1831
1928
|
|
|
1832
1929
|
// src/proxmox/ceph-osd-tree.ts
|
|
1833
|
-
import * as
|
|
1930
|
+
import * as nodes3 from "@distilled.cloud/proxmox/nodes";
|
|
1931
|
+
import * as Effect19 from "effect/Effect";
|
|
1834
1932
|
var branches = (node) => (Array.isArray(node["children"]) ? node["children"] : []).filter((child) => typeof child === "object" && child !== null);
|
|
1835
1933
|
var osdLeaves = (tree) => {
|
|
1836
1934
|
const root = tree["root"];
|
|
@@ -1856,33 +1954,42 @@ var osdAttributes = (leaf, props) => ({
|
|
|
1856
1954
|
status: text(leaf["status"]),
|
|
1857
1955
|
total_space: num(leaf["total_space"], 0)
|
|
1858
1956
|
});
|
|
1859
|
-
var readOsd = (props) =>
|
|
1860
|
-
const leaf =
|
|
1957
|
+
var readOsd = (props) => runPve(props.target, "read", false, nodes3.getNodeCephOsd({ node: props.node })).pipe(Effect19.map((t) => {
|
|
1958
|
+
const leaf = findOsd(t, props.osdid);
|
|
1861
1959
|
return leaf === undefined ? undefined : osdAttributes(leaf, props);
|
|
1862
1960
|
}));
|
|
1863
1961
|
|
|
1864
1962
|
// src/proxmox/ceph-osd-write.ts
|
|
1865
|
-
import * as
|
|
1963
|
+
import * as nodes4 from "@distilled.cloud/proxmox/nodes";
|
|
1964
|
+
import * as Effect20 from "effect/Effect";
|
|
1866
1965
|
var OSD_CREATE_ENDPOINT = "pve:POST /nodes/{node}/ceph/osd";
|
|
1867
1966
|
var describe = (props) => `osd.${String(props.osdid)} on ${props.node}`;
|
|
1868
1967
|
var createOsdForm = (props, dev) => ({
|
|
1869
1968
|
dev,
|
|
1870
1969
|
...props.device_class === undefined ? {} : { "crush-device-class": props.device_class }
|
|
1871
1970
|
});
|
|
1872
|
-
var createOsd =
|
|
1971
|
+
var createOsd = Effect20.fn(function* (news) {
|
|
1873
1972
|
if (news.dev === undefined) {
|
|
1874
|
-
return yield*
|
|
1973
|
+
return yield* Effect20.die(new Error(`${describe(news)}: no such OSD in the CRUSH tree, and no \`dev\` was declared. ` + "Without a device this resource is an ASSERTION over an OSD that must already " + "exist. Declare `dev` to let it create one -- which WIPES that device -- or fix " + "the declaration; if the disk was there yesterday, a replica is missing and Ceph " + "is degraded."));
|
|
1875
1974
|
}
|
|
1876
1975
|
const form = createOsdForm(news, news.dev);
|
|
1877
1976
|
yield* guardForm(OSD_CREATE_ENDPOINT, form, true);
|
|
1878
|
-
yield*
|
|
1977
|
+
yield* runPve(news.target, "provision", true, nodes4.createNodeCephOsd({
|
|
1978
|
+
dev: news.dev,
|
|
1979
|
+
node: news.node,
|
|
1980
|
+
...news.device_class === undefined ? {} : { crush_device_class: news.device_class }
|
|
1981
|
+
}));
|
|
1879
1982
|
const created = yield* readOsd(news);
|
|
1880
1983
|
if (created === undefined) {
|
|
1881
|
-
return yield*
|
|
1984
|
+
return yield* Effect20.die(new Error(`${describe(news)}: the create returned no error but osd.${String(news.osdid)} is ` + "still absent. Ceph allocates OSD ids and cannot be told which to use, so it most " + "likely assigned a different number -- check `ceph osd tree`, and declare the id it " + "actually gave the device rather than deploying again."));
|
|
1882
1985
|
}
|
|
1883
1986
|
return created;
|
|
1884
1987
|
});
|
|
1885
|
-
var destroyOsd = (olds) =>
|
|
1988
|
+
var destroyOsd = (olds) => runPve(olds.target, "provision", true, nodes4.deleteNodeCephOsd({
|
|
1989
|
+
node: olds.node,
|
|
1990
|
+
osdid: String(olds.osdid),
|
|
1991
|
+
...olds.cleanup === true ? { cleanup: "1" } : {}
|
|
1992
|
+
}));
|
|
1886
1993
|
|
|
1887
1994
|
// src/proxmox/ceph-osd.ts
|
|
1888
1995
|
var ProxmoxCephOsd = Resource7("Proxmox.CephOsd", {
|
|
@@ -1890,16 +1997,16 @@ var ProxmoxCephOsd = Resource7("Proxmox.CephOsd", {
|
|
|
1890
1997
|
});
|
|
1891
1998
|
var settled = (live, props) => (props.host === undefined || props.host === live.host) && (props.device_class === undefined || props.device_class === live.device_class);
|
|
1892
1999
|
var describe2 = (props) => `osd.${String(props.osdid)} (read via nodes/${props.node}/ceph/osd)`;
|
|
1893
|
-
var
|
|
1894
|
-
list: () =>
|
|
1895
|
-
read:
|
|
2000
|
+
var handlers3 = {
|
|
2001
|
+
list: () => Effect21.succeed([]),
|
|
2002
|
+
read: Effect21.fn(function* ({ olds }) {
|
|
1896
2003
|
return yield* readOsd(olds);
|
|
1897
2004
|
}),
|
|
1898
|
-
diff:
|
|
2005
|
+
diff: Effect21.fn(function* ({
|
|
1899
2006
|
news,
|
|
1900
2007
|
output
|
|
1901
2008
|
}) {
|
|
1902
|
-
if (output === undefined || !
|
|
2009
|
+
if (output === undefined || !isResolved6(news))
|
|
1903
2010
|
return;
|
|
1904
2011
|
const live = yield* readOsd(news);
|
|
1905
2012
|
if (live === undefined)
|
|
@@ -1908,29 +2015,30 @@ var handlers4 = {
|
|
|
1908
2015
|
return { action: "update" };
|
|
1909
2016
|
return settled(live, news) ? { action: "noop" } : { action: "update" };
|
|
1910
2017
|
}),
|
|
1911
|
-
reconcile:
|
|
2018
|
+
reconcile: Effect21.fn(function* ({ news }) {
|
|
1912
2019
|
const live = yield* readOsd(news);
|
|
1913
2020
|
if (live === undefined) {
|
|
1914
2021
|
return yield* createOsd(news);
|
|
1915
2022
|
}
|
|
1916
2023
|
if (!settled(live, news)) {
|
|
1917
|
-
return yield*
|
|
2024
|
+
return yield* Effect21.die(new Error(`${describe2(news)}: exists but not as declared -- live host=${live.host} ` + `device_class=${live.device_class}. Nothing here moves an OSD between hosts or ` + "reclassifies it; fix the declaration, or move it deliberately with `ceph osd crush " + "move` / `ceph osd crush set-device-class` and deploy again."));
|
|
1918
2025
|
}
|
|
1919
2026
|
return live;
|
|
1920
2027
|
}),
|
|
1921
|
-
delete:
|
|
2028
|
+
delete: Effect21.fn(function* ({ olds }) {
|
|
1922
2029
|
yield* destroyOsd(olds);
|
|
1923
2030
|
})
|
|
1924
2031
|
};
|
|
1925
|
-
var ProxmoxCephOsdProvider = () => Provider7.effect(ProxmoxCephOsd,
|
|
2032
|
+
var ProxmoxCephOsdProvider = () => Provider7.effect(ProxmoxCephOsd, Effect21.succeed(ProxmoxCephOsd.Provider.of(handlers3)));
|
|
1926
2033
|
// src/proxmox/ceph-pool.ts
|
|
1927
2034
|
import { Resource as Resource8 } from "alchemy";
|
|
2035
|
+
import { isResolved as isResolved7 } from "alchemy/Diff";
|
|
1928
2036
|
import * as Provider8 from "alchemy/Provider";
|
|
1929
|
-
import * as
|
|
2037
|
+
import * as nodes7 from "@distilled.cloud/proxmox/nodes";
|
|
2038
|
+
import * as Effect24 from "effect/Effect";
|
|
1930
2039
|
|
|
1931
2040
|
// src/proxmox/ceph-pool-form.ts
|
|
1932
2041
|
var object = (props) => `nodes/${props.node}/ceph/pool/${props.name}`;
|
|
1933
|
-
var collection = (props) => `nodes/${props.node}/ceph/pool`;
|
|
1934
2042
|
var UNSET = -1;
|
|
1935
2043
|
var applications = (value) => csv(Array.isArray(value) ? value.map((entry) => String(entry)) : []);
|
|
1936
2044
|
var same = (declared, live, frozen = false) => declared === undefined || frozen || declared === live;
|
|
@@ -1955,92 +2063,112 @@ var createBody = (props) => ({
|
|
|
1955
2063
|
});
|
|
1956
2064
|
|
|
1957
2065
|
// src/proxmox/ceph-pool-settle.ts
|
|
1958
|
-
import * as
|
|
1959
|
-
|
|
1960
|
-
var
|
|
2066
|
+
import * as nodes5 from "@distilled.cloud/proxmox/nodes";
|
|
2067
|
+
import * as Effect22 from "effect/Effect";
|
|
2068
|
+
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 settle2 = (props, read, done) => Effect22.gen(function* () {
|
|
1961
2070
|
for (let attempt = 0;attempt < 30; attempt += 1) {
|
|
1962
2071
|
const live = yield* read(props);
|
|
1963
2072
|
if (done(live))
|
|
1964
2073
|
return live;
|
|
1965
|
-
yield*
|
|
2074
|
+
yield* Effect22.sleep("2 seconds");
|
|
1966
2075
|
}
|
|
1967
2076
|
return yield* read(props);
|
|
1968
2077
|
});
|
|
1969
2078
|
|
|
2079
|
+
// src/proxmox/ceph-pool-wire.ts
|
|
2080
|
+
import * as nodes6 from "@distilled.cloud/proxmox/nodes";
|
|
2081
|
+
import * as Effect23 from "effect/Effect";
|
|
2082
|
+
var attributesOf3 = (live, props) => {
|
|
2083
|
+
const row = live;
|
|
2084
|
+
if (typeof row["name"] !== "string")
|
|
2085
|
+
return;
|
|
2086
|
+
return {
|
|
2087
|
+
applications: applications(row["application_list"]),
|
|
2088
|
+
crush_rule: text(row["crush_rule"]),
|
|
2089
|
+
id: int(row["id"], UNSET),
|
|
2090
|
+
min_size: int(row["min_size"], UNSET),
|
|
2091
|
+
name: props.name,
|
|
2092
|
+
node: props.node,
|
|
2093
|
+
nodelete: bool(row["nodelete"]),
|
|
2094
|
+
nopgchange: bool(row["nopgchange"]),
|
|
2095
|
+
nosizechange: bool(row["nosizechange"]),
|
|
2096
|
+
pg_autoscale_mode: text(row["pg_autoscale_mode"]),
|
|
2097
|
+
pg_num: int(row["pg_num"], UNSET),
|
|
2098
|
+
pg_num_min: int(row["pg_num_min"], UNSET),
|
|
2099
|
+
size: int(row["size"], UNSET),
|
|
2100
|
+
target_size: int(row["target_size"], UNSET),
|
|
2101
|
+
target_size_ratio: num(row["target_size_ratio"], UNSET)
|
|
2102
|
+
};
|
|
2103
|
+
};
|
|
2104
|
+
var readPoolStatus = (props) => runPve(props.target, "read", false, nodes6.getNodeCephPoolStatus({ name: props.name, node: props.node, verbose: "1" })).pipe(Effect23.map((live) => attributesOf3(live, props)), Effect23.catchTag("CephPoolNotFound", () => Effect23.succeed(undefined)));
|
|
2105
|
+
var CEPH_POOL_CREATE = "pve:POST /nodes/{node}/ceph/pool";
|
|
2106
|
+
var CEPH_POOL_UPDATE = "pve:PUT /nodes/{node}/ceph/pool/{name}";
|
|
2107
|
+
var toDistilledCreate2 = (props) => ({
|
|
2108
|
+
...createBody(props),
|
|
2109
|
+
node: props.node
|
|
2110
|
+
});
|
|
2111
|
+
var toDistilledUpdate = (props) => ({
|
|
2112
|
+
...updateBody(props),
|
|
2113
|
+
name: props.name,
|
|
2114
|
+
node: props.node
|
|
2115
|
+
});
|
|
2116
|
+
|
|
1970
2117
|
// src/proxmox/ceph-pool.ts
|
|
1971
2118
|
var ProxmoxCephPool = Resource8("Proxmox.CephPool", {
|
|
1972
2119
|
defaultRemovalPolicy: "retain"
|
|
1973
2120
|
});
|
|
1974
|
-
var
|
|
1975
|
-
|
|
1976
|
-
|
|
2121
|
+
var matches3 = (attributes, props) => same(props.size, attributes.size, attributes.nosizechange) && same(props.min_size, attributes.min_size, attributes.nosizechange) && same(props.pg_autoscale_mode, attributes.pg_autoscale_mode) && same(hint(props.pg_num_min), attributes.pg_num_min, attributes.nopgchange) && same(props.crush_rule, attributes.crush_rule, attributes.crush_rule === "") && same(hint(props.target_size), attributes.target_size);
|
|
2122
|
+
var ProxmoxCephPoolProvider = () => Provider8.effect(ProxmoxCephPool, Effect24.succeed(ProxmoxCephPool.Provider.of({
|
|
2123
|
+
list: () => Effect24.succeed([]),
|
|
2124
|
+
read: ({ olds }) => readPoolStatus(olds),
|
|
2125
|
+
diff: Effect24.fn(function* ({ news, output }) {
|
|
2126
|
+
if (!isResolved7(news))
|
|
1977
2127
|
return;
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
size: int(live["size"], UNSET),
|
|
1992
|
-
target_size: int(live["target_size"], UNSET),
|
|
1993
|
-
target_size_ratio: num(live["target_size_ratio"], UNSET)
|
|
1994
|
-
};
|
|
1995
|
-
},
|
|
1996
|
-
collection,
|
|
1997
|
-
createForm: createBody,
|
|
1998
|
-
endpoint: {
|
|
1999
|
-
create: "pve:POST /nodes/{node}/ceph/pool",
|
|
2000
|
-
update: "pve:PUT /nodes/{node}/ceph/pool/{name}"
|
|
2001
|
-
},
|
|
2002
|
-
matches: (attributes, props) => same(props.size, attributes.size, attributes.nosizechange) && same(props.min_size, attributes.min_size, attributes.nosizechange) && same(props.pg_autoscale_mode, attributes.pg_autoscale_mode) && same(hint(props.pg_num_min), attributes.pg_num_min, attributes.nopgchange) && same(props.crush_rule, attributes.crush_rule, attributes.crush_rule === "") && same(hint(props.target_size), attributes.target_size),
|
|
2003
|
-
path: (props) => `${object(props)}/status?verbose=1`,
|
|
2004
|
-
updateForm: updateBody
|
|
2005
|
-
};
|
|
2006
|
-
var ops2 = pveOperations(spec);
|
|
2007
|
-
var ProxmoxCephPoolProvider = () => Provider8.effect(ProxmoxCephPool, Effect21.succeed(ProxmoxCephPool.Provider.of({
|
|
2008
|
-
list: () => Effect21.succeed([]),
|
|
2009
|
-
read: ({ olds }) => ops2.read(olds),
|
|
2010
|
-
diff: ({ news, output }) => ops2.diff(news, output),
|
|
2011
|
-
reconcile: Effect21.fn(function* ({ news }) {
|
|
2012
|
-
const live = yield* ops2.read(news);
|
|
2128
|
+
yield* guardWrite2(CEPH_POOL_CREATE, createBody(news), output === undefined);
|
|
2129
|
+
yield* guardWrite2(CEPH_POOL_UPDATE, updateBody(news), false);
|
|
2130
|
+
if (output === undefined)
|
|
2131
|
+
return;
|
|
2132
|
+
const live = yield* readPoolStatus(news);
|
|
2133
|
+
if (live === undefined) {
|
|
2134
|
+
yield* guardWrite2(CEPH_POOL_CREATE, createBody(news), true);
|
|
2135
|
+
return { action: "update" };
|
|
2136
|
+
}
|
|
2137
|
+
return matches3(live, news) ? { action: "noop" } : { action: "update" };
|
|
2138
|
+
}),
|
|
2139
|
+
reconcile: Effect24.fn(function* ({ news }) {
|
|
2140
|
+
const live = yield* readPoolStatus(news);
|
|
2013
2141
|
let upid;
|
|
2014
2142
|
if (live === undefined) {
|
|
2015
|
-
yield*
|
|
2016
|
-
yield* confirmAbsent(news,
|
|
2017
|
-
upid = yield*
|
|
2143
|
+
yield* guardWrite2(CEPH_POOL_CREATE, createBody(news), true);
|
|
2144
|
+
yield* confirmAbsent(news, object(news));
|
|
2145
|
+
upid = yield* runPve(news.target, "provision", true, nodes7.createNodeCephPool(toDistilledCreate2(news)));
|
|
2018
2146
|
} else {
|
|
2019
|
-
const form = formToSend(
|
|
2147
|
+
const form = formToSend(matches3, live, news, updateBody(news));
|
|
2020
2148
|
if (form !== undefined) {
|
|
2021
|
-
yield*
|
|
2022
|
-
upid = yield*
|
|
2149
|
+
yield* guardWrite2(CEPH_POOL_UPDATE, updateBody(news), false);
|
|
2150
|
+
upid = yield* runPve(news.target, "provision", true, nodes7.putNodeCephPool(toDistilledUpdate(news)));
|
|
2023
2151
|
}
|
|
2024
2152
|
}
|
|
2025
|
-
const after = yield* settle2(news,
|
|
2026
|
-
if (after === undefined || !
|
|
2027
|
-
return yield*
|
|
2153
|
+
const after = yield* settle2(news, readPoolStatus, (row) => row !== undefined && matches3(row, news));
|
|
2154
|
+
if (after === undefined || !matches3(after, news)) {
|
|
2155
|
+
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."));
|
|
2028
2156
|
}
|
|
2029
2157
|
return after;
|
|
2030
2158
|
}),
|
|
2031
|
-
delete:
|
|
2032
|
-
yield*
|
|
2033
|
-
const left = yield* settle2(olds,
|
|
2159
|
+
delete: Effect24.fn(function* ({ olds }) {
|
|
2160
|
+
yield* runPve(olds.target, "provision", true, nodes7.deleteNodeCephPool({ name: olds.name, node: olds.node }));
|
|
2161
|
+
const left = yield* settle2(olds, readPoolStatus, (row) => row === undefined);
|
|
2034
2162
|
if (left !== undefined) {
|
|
2035
|
-
return yield*
|
|
2163
|
+
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."));
|
|
2036
2164
|
}
|
|
2037
2165
|
})
|
|
2038
2166
|
})));
|
|
2039
2167
|
// src/proxmox/firewall-alias.ts
|
|
2040
2168
|
import { Resource as Resource9 } from "alchemy";
|
|
2041
|
-
import { isResolved as
|
|
2169
|
+
import { isResolved as isResolved8 } from "alchemy/Diff";
|
|
2042
2170
|
import * as Provider9 from "alchemy/Provider";
|
|
2043
|
-
import * as
|
|
2171
|
+
import * as Effect25 from "effect/Effect";
|
|
2044
2172
|
|
|
2045
2173
|
// src/proxmox/firewall-alias-form.ts
|
|
2046
2174
|
var fold = (name) => name.trim().toLowerCase();
|
|
@@ -2060,7 +2188,7 @@ var body = (props) => {
|
|
|
2060
2188
|
|
|
2061
2189
|
// src/proxmox/firewall-alias.ts
|
|
2062
2190
|
var ProxmoxFirewallAlias = Resource9("Proxmox.FirewallAlias");
|
|
2063
|
-
var
|
|
2191
|
+
var handlers4 = pveHandlers({
|
|
2064
2192
|
attributes: (live, props) => {
|
|
2065
2193
|
const address = cidr(live["cidr"]);
|
|
2066
2194
|
return address === "" ? undefined : {
|
|
@@ -2080,25 +2208,25 @@ var handlers5 = pveHandlers({
|
|
|
2080
2208
|
path: (props) => `cluster/firewall/aliases/${props.name}`,
|
|
2081
2209
|
updateForm: body
|
|
2082
2210
|
});
|
|
2083
|
-
var ProxmoxFirewallAliasProvider = () => Provider9.effect(ProxmoxFirewallAlias,
|
|
2084
|
-
...
|
|
2085
|
-
diff:
|
|
2086
|
-
if (output !== undefined &&
|
|
2211
|
+
var ProxmoxFirewallAliasProvider = () => Provider9.effect(ProxmoxFirewallAlias, Effect25.succeed(ProxmoxFirewallAlias.Provider.of({
|
|
2212
|
+
...handlers4,
|
|
2213
|
+
diff: Effect25.fn(function* ({ news, output }) {
|
|
2214
|
+
if (output !== undefined && isResolved8(news) && fold(news.name) !== fold(output.name)) {
|
|
2087
2215
|
return { action: "replace" };
|
|
2088
2216
|
}
|
|
2089
|
-
return yield*
|
|
2217
|
+
return yield* handlers4.diff({ news, output });
|
|
2090
2218
|
})
|
|
2091
2219
|
})));
|
|
2092
2220
|
// src/proxmox/group.ts
|
|
2093
2221
|
import { Resource as Resource10 } from "alchemy";
|
|
2094
|
-
import { isResolved as
|
|
2222
|
+
import { isResolved as isResolved9 } from "alchemy/Diff";
|
|
2095
2223
|
import * as Provider10 from "alchemy/Provider";
|
|
2096
2224
|
import * as access4 from "@distilled.cloud/proxmox/access";
|
|
2097
|
-
import * as
|
|
2225
|
+
import * as Effect27 from "effect/Effect";
|
|
2098
2226
|
|
|
2099
2227
|
// src/proxmox/group-wire.ts
|
|
2100
2228
|
import * as access3 from "@distilled.cloud/proxmox/access";
|
|
2101
|
-
import * as
|
|
2229
|
+
import * as Effect26 from "effect/Effect";
|
|
2102
2230
|
var GROUP_CREATE = "pve:POST /access/groups";
|
|
2103
2231
|
var GROUP_UPDATE = "pve:PUT /access/groups/{groupid}";
|
|
2104
2232
|
var storedComment = (comment2) => {
|
|
@@ -2117,29 +2245,27 @@ var updateForm = (props) => ({
|
|
|
2117
2245
|
comment: storedComment(props.comment),
|
|
2118
2246
|
groupid: props.groupid
|
|
2119
2247
|
});
|
|
2120
|
-
var
|
|
2248
|
+
var attributesOf4 = (live, props) => ({
|
|
2121
2249
|
comment: text(live.comment),
|
|
2122
2250
|
groupid: props.groupid,
|
|
2123
2251
|
members: memberList(live.members)
|
|
2124
2252
|
});
|
|
2125
|
-
var
|
|
2126
|
-
var readGroupOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, access3.getAccessGroup({ groupid: props.groupid }))).pipe(
|
|
2127
|
-
var readGroup = (props) => readGroupOrFail(props).pipe(
|
|
2128
|
-
return;
|
|
2129
|
-
}));
|
|
2253
|
+
var matches4 = (attributes, props) => attributes.comment === storedComment(props.comment);
|
|
2254
|
+
var readGroupOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, access3.getAccessGroup({ groupid: props.groupid }))).pipe(Effect26.map((live) => live === UNREADABLE ? UNREADABLE : live.members === undefined ? undefined : attributesOf4(live, props)));
|
|
2255
|
+
var readGroup = (props) => readGroupOrFail(props).pipe(Effect26.catchTag("GroupNotFound", () => Effect26.succeed(undefined)));
|
|
2130
2256
|
var dropUnreadable3 = (live) => live === UNREADABLE ? undefined : live;
|
|
2131
2257
|
|
|
2132
2258
|
// src/proxmox/group.ts
|
|
2133
2259
|
var ProxmoxGroup = Resource10("Proxmox.Group", {
|
|
2134
2260
|
defaultRemovalPolicy: "retain"
|
|
2135
2261
|
});
|
|
2136
|
-
var ProxmoxGroupProvider = () => Provider10.effect(ProxmoxGroup,
|
|
2137
|
-
list: () =>
|
|
2138
|
-
read:
|
|
2262
|
+
var ProxmoxGroupProvider = () => Provider10.effect(ProxmoxGroup, Effect27.succeed(ProxmoxGroup.Provider.of({
|
|
2263
|
+
list: () => Effect27.succeed([]),
|
|
2264
|
+
read: Effect27.fn(function* ({ olds, output }) {
|
|
2139
2265
|
return dropUnreadable3(yield* output === undefined ? readGroup(olds) : readGroupOrFail(olds));
|
|
2140
2266
|
}),
|
|
2141
|
-
diff:
|
|
2142
|
-
if (!
|
|
2267
|
+
diff: Effect27.fn(function* ({ news, output }) {
|
|
2268
|
+
if (!isResolved9(news))
|
|
2143
2269
|
return;
|
|
2144
2270
|
yield* guardWrite2(GROUP_CREATE, createForm3(news), output === undefined);
|
|
2145
2271
|
yield* guardWrite2(GROUP_UPDATE, updateForm(news), false);
|
|
@@ -2154,31 +2280,31 @@ var ProxmoxGroupProvider = () => Provider10.effect(ProxmoxGroup, Effect24.succee
|
|
|
2154
2280
|
yield* guardWrite2(GROUP_CREATE, createForm3(news), true);
|
|
2155
2281
|
return { action: "update" };
|
|
2156
2282
|
}
|
|
2157
|
-
return
|
|
2283
|
+
return matches4(live, news) ? { action: "noop" } : { action: "update" };
|
|
2158
2284
|
}),
|
|
2159
|
-
reconcile:
|
|
2285
|
+
reconcile: Effect27.fn(function* ({ news }) {
|
|
2160
2286
|
const before = dropUnreadable3(yield* readGroup(news));
|
|
2161
2287
|
yield* guardWrite2(GROUP_CREATE, createForm3(news), before === undefined);
|
|
2162
2288
|
yield* guardWrite2(GROUP_UPDATE, updateForm(news), false);
|
|
2163
2289
|
if (before === undefined) {
|
|
2164
2290
|
yield* runPve(news.target, "provision", true, access4.createAccessGroup(createForm3(news)));
|
|
2165
|
-
} else if (!
|
|
2291
|
+
} else if (!matches4(before, news)) {
|
|
2166
2292
|
yield* runPve(news.target, "provision", true, access4.putAccessGroup(updateForm(news)));
|
|
2167
2293
|
}
|
|
2168
2294
|
const after = dropUnreadable3(yield* readGroup(news));
|
|
2169
2295
|
if (after === undefined) {
|
|
2170
|
-
return yield*
|
|
2296
|
+
return yield* Effect27.die(new Error(`access/groups/${news.groupid}: the write returned no error but the group 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."));
|
|
2171
2297
|
}
|
|
2172
2298
|
return after;
|
|
2173
2299
|
}),
|
|
2174
|
-
delete:
|
|
2300
|
+
delete: Effect27.fn(function* ({ olds }) {
|
|
2175
2301
|
yield* runPve(olds.target, "provision", true, access4.deleteAccessGroup({ groupid: olds.groupid }));
|
|
2176
2302
|
})
|
|
2177
2303
|
})));
|
|
2178
2304
|
// src/proxmox/ha-resource.ts
|
|
2179
2305
|
import { Resource as Resource11 } from "alchemy";
|
|
2180
2306
|
import * as Provider11 from "alchemy/Provider";
|
|
2181
|
-
import * as
|
|
2307
|
+
import * as Effect28 from "effect/Effect";
|
|
2182
2308
|
var ProxmoxHaResource = Resource11("Proxmox.HaResource");
|
|
2183
2309
|
var kind = (sid) => sid.split(":")[0] ?? "";
|
|
2184
2310
|
var shape3 = (props) => ({
|
|
@@ -2187,7 +2313,7 @@ var shape3 = (props) => ({
|
|
|
2187
2313
|
max_restart: String(props.max_restart ?? 1),
|
|
2188
2314
|
state: props.state ?? "started"
|
|
2189
2315
|
});
|
|
2190
|
-
var
|
|
2316
|
+
var handlers5 = pveHandlers({
|
|
2191
2317
|
attributes: (live, props) => ({
|
|
2192
2318
|
comment: typeof live["comment"] === "string" ? live["comment"] : "",
|
|
2193
2319
|
max_relocate: num(live["max_relocate"], 1),
|
|
@@ -2206,11 +2332,11 @@ var handlers6 = pveHandlers({
|
|
|
2206
2332
|
path: (props) => `cluster/ha/resources/${props.sid}`,
|
|
2207
2333
|
updateForm: shape3
|
|
2208
2334
|
});
|
|
2209
|
-
var ProxmoxHaResourceProvider = () => Provider11.effect(ProxmoxHaResource,
|
|
2335
|
+
var ProxmoxHaResourceProvider = () => Provider11.effect(ProxmoxHaResource, Effect28.succeed(ProxmoxHaResource.Provider.of(handlers5)));
|
|
2210
2336
|
// src/proxmox/ha-rule.ts
|
|
2211
2337
|
import { Resource as Resource12 } from "alchemy";
|
|
2212
2338
|
import * as Provider12 from "alchemy/Provider";
|
|
2213
|
-
import * as
|
|
2339
|
+
import * as Effect29 from "effect/Effect";
|
|
2214
2340
|
|
|
2215
2341
|
// src/proxmox/ha-rule-form.ts
|
|
2216
2342
|
var nodeList = (value) => {
|
|
@@ -2258,7 +2384,7 @@ var clearList = (props) => Object.entries(optional(props)).filter(([, value]) =>
|
|
|
2258
2384
|
|
|
2259
2385
|
// src/proxmox/ha-rule.ts
|
|
2260
2386
|
var ProxmoxHaRule = Resource12("Proxmox.HaRule");
|
|
2261
|
-
var
|
|
2387
|
+
var handlers6 = pveHandlers({
|
|
2262
2388
|
attributes: (live, props) => {
|
|
2263
2389
|
const liveType = text(live["type"]);
|
|
2264
2390
|
if (liveType !== "" && liveType !== props.type)
|
|
@@ -2287,43 +2413,43 @@ var handlers7 = pveHandlers({
|
|
|
2287
2413
|
return withClears(fields, clear);
|
|
2288
2414
|
}
|
|
2289
2415
|
});
|
|
2290
|
-
var ProxmoxHaRuleProvider = () => Provider12.effect(ProxmoxHaRule,
|
|
2416
|
+
var ProxmoxHaRuleProvider = () => Provider12.effect(ProxmoxHaRule, Effect29.succeed(ProxmoxHaRule.Provider.of(handlers6)));
|
|
2291
2417
|
// src/proxmox/lxc.ts
|
|
2292
2418
|
import { Resource as Resource13 } from "alchemy";
|
|
2293
|
-
import { isResolved as
|
|
2419
|
+
import { isResolved as isResolved10 } from "alchemy/Diff";
|
|
2294
2420
|
import * as Provider13 from "alchemy/Provider";
|
|
2295
|
-
import * as
|
|
2421
|
+
import * as Effect39 from "effect/Effect";
|
|
2296
2422
|
|
|
2297
2423
|
// src/ownership/adopt.ts
|
|
2298
2424
|
import { AdoptPolicy } from "alchemy/AdoptPolicy";
|
|
2299
2425
|
import { AlchemyContext } from "alchemy/AlchemyContext";
|
|
2300
2426
|
import { Stack as Stack3 } from "alchemy/Stack";
|
|
2301
|
-
import * as
|
|
2427
|
+
import * as Effect33 from "effect/Effect";
|
|
2302
2428
|
import * as Option5 from "effect/Option";
|
|
2303
2429
|
|
|
2304
2430
|
// src/ownership/resume.ts
|
|
2305
2431
|
import { Unowned } from "alchemy/AdoptPolicy";
|
|
2306
2432
|
import { Artifacts } from "alchemy/Artifacts";
|
|
2307
|
-
import * as
|
|
2433
|
+
import * as Effect30 from "effect/Effect";
|
|
2308
2434
|
import * as Option2 from "effect/Option";
|
|
2309
2435
|
var PROVEN = "homeflare/ownership/resumes";
|
|
2310
2436
|
var UNFINISHED = "homeflare/ownership/unfinished";
|
|
2311
|
-
var note = (key, instanceId) =>
|
|
2312
|
-
const bag = yield*
|
|
2437
|
+
var note = (key, instanceId) => Effect30.gen(function* () {
|
|
2438
|
+
const bag = yield* Effect30.serviceOption(Artifacts);
|
|
2313
2439
|
if (Option2.isSome(bag))
|
|
2314
2440
|
yield* bag.value.set(key, instanceId);
|
|
2315
2441
|
});
|
|
2316
|
-
var noted = (key, instanceId) =>
|
|
2317
|
-
const bag = yield*
|
|
2442
|
+
var noted = (key, instanceId) => Effect30.gen(function* () {
|
|
2443
|
+
const bag = yield* Effect30.serviceOption(Artifacts);
|
|
2318
2444
|
if (Option2.isNone(bag))
|
|
2319
2445
|
return false;
|
|
2320
2446
|
return (yield* bag.value.get(key)) === instanceId;
|
|
2321
2447
|
});
|
|
2322
|
-
var noteUnfinished = (instanceId) =>
|
|
2323
|
-
var noteResume = (instanceId, recovered) =>
|
|
2448
|
+
var noteUnfinished = (instanceId) => Effect30.as(note(UNFINISHED, instanceId), undefined);
|
|
2449
|
+
var noteResume = (instanceId, recovered) => Effect30.gen(function* () {
|
|
2324
2450
|
yield* noteUnfinished(instanceId);
|
|
2325
|
-
const unproven = () =>
|
|
2326
|
-
const found = yield* recovered.pipe(
|
|
2451
|
+
const unproven = () => Effect30.succeed(undefined);
|
|
2452
|
+
const found = yield* recovered.pipe(Effect30.catch(unproven), Effect30.catchDefect(unproven));
|
|
2327
2453
|
if (found === undefined || found === null || Unowned.is(found))
|
|
2328
2454
|
return;
|
|
2329
2455
|
yield* note(PROVEN, instanceId);
|
|
@@ -2334,7 +2460,7 @@ var provingResumes = (service) => {
|
|
|
2334
2460
|
const { diff, read } = service;
|
|
2335
2461
|
if (diff === undefined || read === undefined)
|
|
2336
2462
|
return service;
|
|
2337
|
-
const proving = (input) =>
|
|
2463
|
+
const proving = (input) => Effect30.gen(function* () {
|
|
2338
2464
|
if (input.output === undefined) {
|
|
2339
2465
|
yield* noteResume(input.instanceId, read({ ...input, output: undefined }));
|
|
2340
2466
|
}
|
|
@@ -2346,12 +2472,12 @@ var provingResumes = (service) => {
|
|
|
2346
2472
|
// src/ownership/rows.ts
|
|
2347
2473
|
import { Stack as Stack2 } from "alchemy/Stack";
|
|
2348
2474
|
import { State, isActionState } from "alchemy/State/State";
|
|
2349
|
-
import * as
|
|
2475
|
+
import * as Effect32 from "effect/Effect";
|
|
2350
2476
|
import * as Option4 from "effect/Option";
|
|
2351
2477
|
|
|
2352
2478
|
// src/ownership/whole.ts
|
|
2353
2479
|
import { Stack } from "alchemy/Stack";
|
|
2354
|
-
import * as
|
|
2480
|
+
import * as Effect31 from "effect/Effect";
|
|
2355
2481
|
import * as Option3 from "effect/Option";
|
|
2356
2482
|
var absent = (value) => value === undefined || value === null;
|
|
2357
2483
|
var isPlainObject = (value) => {
|
|
@@ -2377,22 +2503,22 @@ var carries = (declared, recorded) => {
|
|
|
2377
2503
|
}
|
|
2378
2504
|
return true;
|
|
2379
2505
|
};
|
|
2380
|
-
var wholeDeclaration = (fqn, props) =>
|
|
2506
|
+
var wholeDeclaration = (fqn, props) => Effect31.map(Effect31.serviceOption(Stack), (stack) => {
|
|
2381
2507
|
const declared = Option3.isSome(stack) ? stack.value.resources[fqn] : undefined;
|
|
2382
2508
|
return declared !== undefined && carries(declared.Props, props);
|
|
2383
2509
|
});
|
|
2384
2510
|
|
|
2385
2511
|
// src/ownership/rows.ts
|
|
2386
|
-
var storeOf =
|
|
2387
|
-
const stack = yield*
|
|
2388
|
-
const state = yield*
|
|
2512
|
+
var storeOf = Effect32.gen(function* () {
|
|
2513
|
+
const stack = yield* Effect32.serviceOption(Stack2);
|
|
2514
|
+
const state = yield* Effect32.serviceOption(State);
|
|
2389
2515
|
if (Option4.isNone(stack) || Option4.isNone(state))
|
|
2390
2516
|
return;
|
|
2391
2517
|
const store = yield* state.value;
|
|
2392
2518
|
const where = { stack: stack.value.name, stage: stack.value.stage };
|
|
2393
2519
|
return { resources: stack.value.resources, store, where };
|
|
2394
2520
|
});
|
|
2395
|
-
var rowAt = (store, where, fqn) => store.get({ ...where, fqn }).pipe(
|
|
2521
|
+
var rowAt = (store, where, fqn) => store.get({ ...where, fqn }).pipe(Effect32.orElseSucceed(() => {
|
|
2396
2522
|
return;
|
|
2397
2523
|
}));
|
|
2398
2524
|
var generationOf = (row, instanceId) => {
|
|
@@ -2405,7 +2531,7 @@ var generationOf = (row, instanceId) => {
|
|
|
2405
2531
|
}
|
|
2406
2532
|
return;
|
|
2407
2533
|
};
|
|
2408
|
-
var recordedGeneration = (fqn, instanceId) =>
|
|
2534
|
+
var recordedGeneration = (fqn, instanceId) => Effect32.gen(function* () {
|
|
2409
2535
|
const found = yield* storeOf;
|
|
2410
2536
|
if (found === undefined)
|
|
2411
2537
|
return;
|
|
@@ -2416,14 +2542,14 @@ var recordedGeneration = (fqn, instanceId) => Effect29.gen(function* () {
|
|
|
2416
2542
|
}
|
|
2417
2543
|
return;
|
|
2418
2544
|
});
|
|
2419
|
-
var recordedInstance = (fqn, instanceId) =>
|
|
2545
|
+
var recordedInstance = (fqn, instanceId) => Effect32.gen(function* () {
|
|
2420
2546
|
const generation = yield* recordedGeneration(fqn, instanceId);
|
|
2421
2547
|
if (generation === undefined)
|
|
2422
2548
|
return "absent";
|
|
2423
2549
|
return (yield* wholeDeclaration(fqn, generation.props)) ? "whole" : "partial";
|
|
2424
2550
|
});
|
|
2425
|
-
var isCreate = (fqn, instanceId) =>
|
|
2426
|
-
var forgetRefusedCreate = (fqn, instanceId) =>
|
|
2551
|
+
var isCreate = (fqn, instanceId) => Effect32.map(recordedGeneration(fqn, instanceId), (generation) => generation?.status === "creating");
|
|
2552
|
+
var forgetRefusedCreate = (fqn, instanceId) => Effect32.gen(function* () {
|
|
2427
2553
|
const found = yield* storeOf;
|
|
2428
2554
|
if (found === undefined)
|
|
2429
2555
|
return;
|
|
@@ -2432,32 +2558,32 @@ var forgetRefusedCreate = (fqn, instanceId) => Effect29.gen(function* () {
|
|
|
2432
2558
|
if (mine?.status !== "creating" || mine.instanceId !== instanceId || mine.attr !== undefined) {
|
|
2433
2559
|
return;
|
|
2434
2560
|
}
|
|
2435
|
-
yield* found.store.delete({ ...found.where, fqn }).pipe(
|
|
2561
|
+
yield* found.store.delete({ ...found.where, fqn }).pipe(Effect32.ignore);
|
|
2436
2562
|
});
|
|
2437
2563
|
|
|
2438
2564
|
// src/ownership/adopt.ts
|
|
2439
|
-
var adoptEnabled = (fqn) =>
|
|
2440
|
-
const stack = yield*
|
|
2565
|
+
var adoptEnabled = (fqn) => Effect33.gen(function* () {
|
|
2566
|
+
const stack = yield* Effect33.serviceOption(Stack3);
|
|
2441
2567
|
const scoped = Option5.isSome(stack) ? stack.value.resources[fqn]?.Adopt : undefined;
|
|
2442
2568
|
if (scoped !== undefined)
|
|
2443
2569
|
return scoped;
|
|
2444
|
-
const policy = yield*
|
|
2570
|
+
const policy = yield* Effect33.serviceOption(AdoptPolicy);
|
|
2445
2571
|
if (Option5.isSome(policy))
|
|
2446
2572
|
return policy.value;
|
|
2447
|
-
const context = yield*
|
|
2573
|
+
const context = yield* Effect33.serviceOption(AlchemyContext);
|
|
2448
2574
|
return Option5.isSome(context) ? context.value.adopt : false;
|
|
2449
2575
|
});
|
|
2450
|
-
var adoptable = (owner) =>
|
|
2576
|
+
var adoptable = (owner) => Effect33.gen(function* () {
|
|
2451
2577
|
if (yield* isCreate(owner.fqn, owner.instanceId))
|
|
2452
2578
|
return true;
|
|
2453
2579
|
return yield* unfinished(owner.instanceId);
|
|
2454
2580
|
});
|
|
2455
|
-
var adoptsAtApply = (owner) =>
|
|
2581
|
+
var adoptsAtApply = (owner) => Effect33.gen(function* () {
|
|
2456
2582
|
if (!(yield* adoptEnabled(owner.fqn)))
|
|
2457
2583
|
return false;
|
|
2458
2584
|
return yield* adoptable(owner);
|
|
2459
2585
|
});
|
|
2460
|
-
var refuseTakeover = (owner, what) =>
|
|
2586
|
+
var refuseTakeover = (owner, what) => Effect33.gen(function* () {
|
|
2461
2587
|
if (owner.output !== undefined)
|
|
2462
2588
|
return;
|
|
2463
2589
|
if (yield* resumes(owner.instanceId))
|
|
@@ -2467,25 +2593,25 @@ var refuseTakeover = (owner, what) => Effect30.gen(function* () {
|
|
|
2467
2593
|
return;
|
|
2468
2594
|
yield* forgetRefusedCreate(owner.fqn, owner.instanceId);
|
|
2469
2595
|
const remedy = may ? "Deploy with --adopt (or wrap the resource in adopt(true)) to take it over, or remove it first." : "It is the new identity of a replace, which --adopt does not cover: remove it first.";
|
|
2470
|
-
return yield*
|
|
2596
|
+
return yield* Effect33.die(new Error(`${what}: already exists, and this stack holds no state for it — the plan could not ask ` + "first (a prop was still an Output, a replace moved onto it, or it appeared after the " + `plan). Nothing was written. ${remedy}`));
|
|
2471
2597
|
});
|
|
2472
2598
|
var claimFor = (owner) => (what) => refuseTakeover(owner, what);
|
|
2473
2599
|
|
|
2474
2600
|
// src/ownership/probe.ts
|
|
2475
2601
|
import { Unowned as Unowned2 } from "alchemy/AdoptPolicy";
|
|
2476
|
-
import * as
|
|
2477
|
-
var provenOurs = (ask, settled2) =>
|
|
2602
|
+
import * as Effect34 from "effect/Effect";
|
|
2603
|
+
var provenOurs = (ask, settled2) => Effect34.gen(function* () {
|
|
2478
2604
|
const recorded = yield* recordedInstance(ask.fqn, ask.instanceId);
|
|
2479
2605
|
if (recorded === "absent")
|
|
2480
2606
|
return false;
|
|
2481
2607
|
if (recorded === "partial") {
|
|
2482
|
-
yield*
|
|
2608
|
+
yield* Effect34.logWarning(`${ask.fqn}: the interrupted create's state row lacks part of the declaration (a prop ` + "was still an Output when it was written), so the live object is not proven ours");
|
|
2483
2609
|
return false;
|
|
2484
2610
|
}
|
|
2485
|
-
const unproven = (reason) =>
|
|
2486
|
-
return yield* settled2.pipe(
|
|
2611
|
+
const unproven = (reason) => Effect34.as(Effect34.logWarning(`${ask.fqn}: the interrupted create's object could not be compared with its props ` + `(${reason instanceof Error ? reason.message : String(reason)}), so it is not treated as ours`), false);
|
|
2612
|
+
return yield* settled2.pipe(Effect34.catch(unproven), Effect34.catchDefect(unproven));
|
|
2487
2613
|
});
|
|
2488
|
-
var ownedRead = (ask, found, settled2) =>
|
|
2614
|
+
var ownedRead = (ask, found, settled2) => Effect34.gen(function* () {
|
|
2489
2615
|
if (found === undefined || ask.output !== undefined)
|
|
2490
2616
|
return found;
|
|
2491
2617
|
return (yield* provenOurs(ask, settled2)) ? found : Unowned2(found);
|
|
@@ -2543,8 +2669,8 @@ var optionMap = (value, family, defaultKey) => {
|
|
|
2543
2669
|
return out;
|
|
2544
2670
|
};
|
|
2545
2671
|
var sameMap = (left, right) => left.size === right.size && [...left].every(([key, value]) => right.get(key) === value);
|
|
2546
|
-
var sortedForm = (
|
|
2547
|
-
var printMap = (
|
|
2672
|
+
var sortedForm = (map12) => [...map12].map(([key, value]) => `${key}=${value}`).sort().join(",");
|
|
2673
|
+
var printMap = (map12, lead = []) => [...lead.filter((key) => map12.has(key)), ...[...map12.keys()].filter((k) => !lead.includes(k))].map((key) => `${key}=${map12.get(key) ?? ""}`).join(",");
|
|
2548
2674
|
var netMaps = (declared, live) => {
|
|
2549
2675
|
const want = optionMap(declared, "net");
|
|
2550
2676
|
const have = optionMap(live, "net");
|
|
@@ -2690,12 +2816,12 @@ var sizeBytes = (text2) => {
|
|
|
2690
2816
|
};
|
|
2691
2817
|
var parseVolume = (text2) => {
|
|
2692
2818
|
const [first] = pairs(text2, "volume");
|
|
2693
|
-
const
|
|
2819
|
+
const spec = first?.[0] === "volume" ? first[1] : "";
|
|
2694
2820
|
const options = optionMap(text2, "volume", "volume");
|
|
2695
2821
|
options.delete("volume");
|
|
2696
2822
|
const sizeText = options.get("size");
|
|
2697
2823
|
options.delete("size");
|
|
2698
|
-
const fresh = NEW_DISK.exec(
|
|
2824
|
+
const fresh = NEW_DISK.exec(spec);
|
|
2699
2825
|
if (fresh !== null) {
|
|
2700
2826
|
const gib = `${fresh[2] ?? "0"}G`;
|
|
2701
2827
|
return {
|
|
@@ -2708,18 +2834,18 @@ var parseVolume = (text2) => {
|
|
|
2708
2834
|
};
|
|
2709
2835
|
}
|
|
2710
2836
|
const size = sizeText === undefined ? undefined : sizeBytes(sizeText);
|
|
2711
|
-
if (
|
|
2712
|
-
const kind2 =
|
|
2713
|
-
return { kind: kind2, options, size, sizeText, source:
|
|
2837
|
+
if (spec.startsWith("/")) {
|
|
2838
|
+
const kind2 = spec.startsWith("/dev/") ? "device" : "bind";
|
|
2839
|
+
return { kind: kind2, options, size, sizeText, source: spec, volname: "" };
|
|
2714
2840
|
}
|
|
2715
|
-
const at =
|
|
2841
|
+
const at = spec.indexOf(":");
|
|
2716
2842
|
return {
|
|
2717
2843
|
kind: "volume",
|
|
2718
2844
|
options,
|
|
2719
2845
|
size,
|
|
2720
2846
|
sizeText,
|
|
2721
|
-
source: at === -1 ?
|
|
2722
|
-
volname: at === -1 ? "" :
|
|
2847
|
+
source: at === -1 ? spec : spec.slice(0, at),
|
|
2848
|
+
volname: at === -1 ? "" : spec.slice(at + 1)
|
|
2723
2849
|
};
|
|
2724
2850
|
};
|
|
2725
2851
|
var isHostPath = (volume) => volume.kind === "bind" || volume.kind === "device";
|
|
@@ -2940,11 +3066,11 @@ var identityRefusals = (news, olds, output) => {
|
|
|
2940
3066
|
};
|
|
2941
3067
|
|
|
2942
3068
|
// src/proxmox/lxc-adoption.ts
|
|
2943
|
-
import * as
|
|
3069
|
+
import * as Effect38 from "effect/Effect";
|
|
2944
3070
|
|
|
2945
3071
|
// src/ownership/adopting.ts
|
|
2946
|
-
import * as
|
|
2947
|
-
var adopting = (owner, settled2) =>
|
|
3072
|
+
import * as Effect35 from "effect/Effect";
|
|
3073
|
+
var adopting = (owner, settled2) => Effect35.gen(function* () {
|
|
2948
3074
|
if (owner.output === undefined)
|
|
2949
3075
|
return false;
|
|
2950
3076
|
const generation = yield* recordedGeneration(owner.fqn, owner.instanceId);
|
|
@@ -2956,11 +3082,11 @@ var adopting = (owner, settled2) => Effect32.gen(function* () {
|
|
|
2956
3082
|
});
|
|
2957
3083
|
|
|
2958
3084
|
// src/proxmox/lxc-lifecycle.ts
|
|
2959
|
-
import * as
|
|
3085
|
+
import * as Effect37 from "effect/Effect";
|
|
2960
3086
|
|
|
2961
3087
|
// src/proxmox/network-apply-read.ts
|
|
2962
|
-
import * as
|
|
2963
|
-
var stagedDiff = (target, node) =>
|
|
3088
|
+
import * as Effect36 from "effect/Effect";
|
|
3089
|
+
var stagedDiff = (target, node) => Effect36.gen(function* () {
|
|
2964
3090
|
const path = `nodes/${node}/network`;
|
|
2965
3091
|
const credential = yield* leased(target, "read");
|
|
2966
3092
|
const envelope = yield* pveEnvelopeWith(target, credential, "GET", path);
|
|
@@ -2973,8 +3099,8 @@ var pendingLines = (diff) => {
|
|
|
2973
3099
|
`).filter((line) => (line.startsWith("+") || line.startsWith("-")) && !line.startsWith("+++") && !line.startsWith("---")).length;
|
|
2974
3100
|
return Math.max(changed, 1);
|
|
2975
3101
|
};
|
|
2976
|
-
var pendingCount = (target, node) => stagedDiff(target, node).pipe(
|
|
2977
|
-
var clusterHealth = (target) => pve(target, "read", "GET", "cluster/status").pipe(
|
|
3102
|
+
var pendingCount = (target, node) => stagedDiff(target, node).pipe(Effect36.map(pendingLines));
|
|
3103
|
+
var clusterHealth = (target) => pve(target, "read", "GET", "cluster/status").pipe(Effect36.map((rows) => {
|
|
2978
3104
|
const all = rows ?? [];
|
|
2979
3105
|
const cluster = all.find((row) => row.type === "cluster");
|
|
2980
3106
|
return {
|
|
@@ -2982,21 +3108,21 @@ var clusterHealth = (target) => pve(target, "read", "GET", "cluster/status").pip
|
|
|
2982
3108
|
quorate: cluster === undefined ? true : bool(cluster.quorate)
|
|
2983
3109
|
};
|
|
2984
3110
|
}));
|
|
2985
|
-
var degradedReason = (target) => clusterHealth(target).pipe(
|
|
3111
|
+
var degradedReason = (target) => clusterHealth(target).pipe(Effect36.map((health) => health.quorate && health.offline.length === 0 ? undefined : `quorate=${String(health.quorate)} offline=[${health.offline.join(", ")}]`));
|
|
2986
3112
|
var UNREACHABLE = "the task could not be read -- the node stopped answering, or this lease cannot see it";
|
|
2987
3113
|
var STILL_RUNNING = "still running when this provider stopped waiting";
|
|
2988
3114
|
var ATTEMPTS = 30;
|
|
2989
|
-
var awaitTask = (target, credential, node, upid, attempts = ATTEMPTS) =>
|
|
3115
|
+
var awaitTask = (target, credential, node, upid, attempts = ATTEMPTS) => Effect36.gen(function* () {
|
|
2990
3116
|
const path = `nodes/${node}/tasks/${upid}/status`;
|
|
2991
3117
|
for (let attempt = 0;attempt < attempts; attempt += 1) {
|
|
2992
|
-
const status = yield* pveWith(target, credential, "GET", path).pipe(
|
|
3118
|
+
const status = yield* pveWith(target, credential, "GET", path).pipe(Effect36.orElseSucceed(() => {
|
|
2993
3119
|
return;
|
|
2994
3120
|
}));
|
|
2995
3121
|
if (status === undefined)
|
|
2996
3122
|
return UNREACHABLE;
|
|
2997
3123
|
if (status.status === "stopped")
|
|
2998
3124
|
return text(status.exitstatus, "stopped with no exitstatus");
|
|
2999
|
-
yield*
|
|
3125
|
+
yield* Effect36.sleep("1 second");
|
|
3000
3126
|
}
|
|
3001
3127
|
return STILL_RUNNING;
|
|
3002
3128
|
});
|
|
@@ -3014,43 +3140,43 @@ var LXC_ENDPOINTS = {
|
|
|
3014
3140
|
var CREATE_POLLS = 180;
|
|
3015
3141
|
var TASK_POLLS = 60;
|
|
3016
3142
|
var isServerError = (error) => error instanceof PveError && error.status === 500;
|
|
3017
|
-
var readLive = (where) =>
|
|
3018
|
-
const answer = yield* pve(where.target, "read", "GET", `${guestPath(where)}/config`).pipe(
|
|
3143
|
+
var readLive = (where) => Effect37.gen(function* () {
|
|
3144
|
+
const answer = yield* pve(where.target, "read", "GET", `${guestPath(where)}/config`).pipe(Effect37.map((data) => ({ data, error: undefined })), Effect37.catchIf(isServerError, (error) => Effect37.succeed({ data: undefined, error })));
|
|
3019
3145
|
if (answer.data !== undefined && answer.data !== null)
|
|
3020
3146
|
return answer.data;
|
|
3021
3147
|
const rows = yield* pve(where.target, "read", "GET", "cluster/resources?type=vm");
|
|
3022
3148
|
const found = (rows ?? []).find((row) => Number(row.vmid) === where.vmid);
|
|
3023
3149
|
if (found !== undefined && found.node === where.node && found.type === "lxc") {
|
|
3024
|
-
return yield*
|
|
3150
|
+
return yield* Effect37.fail(answer.error ?? new LxcRefusedError(`CT ${String(where.vmid)} is listed but its config read empty.`));
|
|
3025
3151
|
}
|
|
3026
3152
|
if (found !== undefined) {
|
|
3027
|
-
return yield*
|
|
3153
|
+
return yield* Effect37.fail(new LxcRefusedError(`vmid ${String(where.vmid)} is a ${text(found.type, "guest")} on ` + `${text(found.node, "?")}, not a container on ${where.node}. A migration is not an ` + "update and vmids are cluster-wide: declare the node it is on, or pick a free vmid."));
|
|
3028
3154
|
}
|
|
3029
3155
|
return;
|
|
3030
3156
|
});
|
|
3031
|
-
var
|
|
3157
|
+
var attributesOf5 = (where, live) => ({
|
|
3032
3158
|
config: storedConfig(live),
|
|
3033
3159
|
node: where.node,
|
|
3034
3160
|
rawKeys: rawKeysOf(live),
|
|
3035
3161
|
vmid: where.vmid
|
|
3036
3162
|
});
|
|
3037
|
-
var task = (where, method, path, what, polls, form) =>
|
|
3163
|
+
var task = (where, method, path, what, polls, form) => Effect37.gen(function* () {
|
|
3038
3164
|
const credential = yield* mint(where.target, "provision");
|
|
3039
3165
|
const upid = text(yield* pveWith(where.target, credential, method, path, form));
|
|
3040
3166
|
if (upid === "") {
|
|
3041
|
-
return yield*
|
|
3167
|
+
return yield* Effect37.fail(new LxcRefusedError(`${what}: PVE returned no task id, so the write cannot be confirmed and is not claimed.`));
|
|
3042
3168
|
}
|
|
3043
3169
|
const exit = yield* awaitTask(where.target, credential, where.node, upid, polls);
|
|
3044
3170
|
if (exit !== "OK") {
|
|
3045
|
-
return yield*
|
|
3171
|
+
return yield* Effect37.fail(new LxcRefusedError(`${what}: task ${upid} ended "${exit}". Read \`pvesh get /nodes/${where.node}/tasks/` + `${upid}/log\` before running the deploy again.`));
|
|
3046
3172
|
}
|
|
3047
3173
|
});
|
|
3048
|
-
var createGuest = (props) =>
|
|
3174
|
+
var createGuest = (props) => Effect37.gen(function* () {
|
|
3049
3175
|
const form = createForm4(props);
|
|
3050
3176
|
yield* guardForm(LXC_ENDPOINTS.create, form, true);
|
|
3051
3177
|
return yield* task(props, "POST", `nodes/${props.node}/lxc`, `create CT ${String(props.vmid)}`, CREATE_POLLS, form);
|
|
3052
3178
|
});
|
|
3053
|
-
var updateGuest = (props, change, digest) =>
|
|
3179
|
+
var updateGuest = (props, change, digest) => Effect37.gen(function* () {
|
|
3054
3180
|
if (Object.keys(change.put).length > 0 || change.clear.length > 0) {
|
|
3055
3181
|
const form = withClears({ ...change.put, ...digest === "" ? {} : { digest } }, change.clear);
|
|
3056
3182
|
yield* guardForm(LXC_ENDPOINTS.update, form, false);
|
|
@@ -3062,47 +3188,47 @@ var updateGuest = (props, change, digest) => Effect34.gen(function* () {
|
|
|
3062
3188
|
yield* task(props, "PUT", `${guestPath(props)}/resize`, `grow ${grow.disk} of CT ${String(props.vmid)} to ${grow.size}`, TASK_POLLS, grown);
|
|
3063
3189
|
}
|
|
3064
3190
|
});
|
|
3065
|
-
var destroyGuest = (where, declared) =>
|
|
3191
|
+
var destroyGuest = (where, declared) => Effect37.gen(function* () {
|
|
3066
3192
|
const live = yield* readLive(where);
|
|
3067
3193
|
if (live === undefined)
|
|
3068
3194
|
return;
|
|
3069
3195
|
const drift = judge(declared, live).drift;
|
|
3070
3196
|
if (drift.length > 0) {
|
|
3071
|
-
return yield*
|
|
3197
|
+
return yield* Effect37.fail(new LxcRefusedError(`destroy CT ${String(where.vmid)}: the guest on ${where.node} no longer matches its last ` + `declaration (${drift.join(", ")}), so it may not be the guest this stack made. ` + "Nothing was deleted. Delete it by hand if it should go, or declare it again."));
|
|
3072
3198
|
}
|
|
3073
3199
|
yield* task(where, "DELETE", guestPath(where), `destroy CT ${String(where.vmid)}`, TASK_POLLS);
|
|
3074
3200
|
});
|
|
3075
3201
|
|
|
3076
3202
|
// src/proxmox/lxc-adoption.ts
|
|
3077
3203
|
var adoptionRefusal = (props, drift) => `CT ${String(props.vmid)} on ${props.node}: adopting it would change ${drift.join(", ")}. An ` + "adoption never changes a guest, so nothing is written. Declare what is live for these keys " + "(the plan names keys, never values) and plan again; change them after the adoption, as an " + "update.";
|
|
3078
|
-
var refuseAdoptedDrift = (owner, props, live, change) =>
|
|
3204
|
+
var refuseAdoptedDrift = (owner, props, live, change) => Effect38.gen(function* () {
|
|
3079
3205
|
if (change.drift.length === 0)
|
|
3080
3206
|
return;
|
|
3081
|
-
const settled2 = (recorded) =>
|
|
3207
|
+
const settled2 = (recorded) => Effect38.sync(() => judge(recorded, live).drift.length === 0);
|
|
3082
3208
|
if (!(yield* adopting(owner, settled2)))
|
|
3083
3209
|
return;
|
|
3084
|
-
return yield*
|
|
3210
|
+
return yield* Effect38.fail(new LxcRefusedError(adoptionRefusal(props, change.drift)));
|
|
3085
3211
|
});
|
|
3086
3212
|
|
|
3087
3213
|
// src/proxmox/lxc.ts
|
|
3088
3214
|
var ProxmoxLxc = Resource13("Proxmox.Lxc", { defaultRemovalPolicy: "retain" });
|
|
3089
|
-
var refused = (reasons) =>
|
|
3215
|
+
var refused = (reasons) => Effect39.fail(new LxcRefusedError(reasons.join(`
|
|
3090
3216
|
`)));
|
|
3091
|
-
var diff = (owner, news, olds) =>
|
|
3217
|
+
var diff = (owner, news, olds) => Effect39.gen(function* () {
|
|
3092
3218
|
const { output } = owner;
|
|
3093
3219
|
const identity2 = identityRefusals(news, olds, output);
|
|
3094
3220
|
if (identity2.length > 0)
|
|
3095
3221
|
return yield* refused(identity2);
|
|
3096
3222
|
if (output === undefined)
|
|
3097
3223
|
return;
|
|
3098
|
-
if (!
|
|
3224
|
+
if (!isResolved10(news))
|
|
3099
3225
|
return;
|
|
3100
3226
|
const live = yield* readLive(news);
|
|
3101
3227
|
if (live === undefined) {
|
|
3102
3228
|
const refusals = createRefusals(news);
|
|
3103
3229
|
if (refusals.length > 0)
|
|
3104
3230
|
return yield* refused(refusals);
|
|
3105
|
-
yield*
|
|
3231
|
+
yield* Effect39.logWarning(`Proxmox.Lxc ${news.node}/${String(news.vmid)}: the cluster lists no guest with this ` + "vmid -- a deploy CREATES it again from ostemplate, with new, empty volumes.");
|
|
3106
3232
|
return { action: "update" };
|
|
3107
3233
|
}
|
|
3108
3234
|
const change = judge(news, live);
|
|
@@ -3113,10 +3239,10 @@ var diff = (owner, news, olds) => Effect36.gen(function* () {
|
|
|
3113
3239
|
if (change.drift.length === 0 && !moved)
|
|
3114
3240
|
return { action: "noop" };
|
|
3115
3241
|
const what = change.drift.length === 0 ? `the guest is on ${news.node} now, not ${output.node} -- a deploy re-records it, ` + "writing nothing." : `live config differs from the declaration in ${change.drift.join(", ")} -- a ` + "deploy writes these.";
|
|
3116
|
-
yield*
|
|
3242
|
+
yield* Effect39.logWarning(`Proxmox.Lxc ${news.node}/${String(news.vmid)}: ${what}`);
|
|
3117
3243
|
return { action: "update" };
|
|
3118
3244
|
});
|
|
3119
|
-
var reconcile = (owner, news) =>
|
|
3245
|
+
var reconcile = (owner, news) => Effect39.gen(function* () {
|
|
3120
3246
|
const { output } = owner;
|
|
3121
3247
|
const identity2 = identityRefusals(news, news, output);
|
|
3122
3248
|
if (identity2.length > 0)
|
|
@@ -3155,22 +3281,22 @@ var reconcile = (owner, news) => Effect36.gen(function* () {
|
|
|
3155
3281
|
`CT ${String(news.vmid)}: after the write, ${left.join(", ")} still differ from the ` + "declaration. PVE may have normalised the value (tags are lower-cased by default) -- " + "declare what it stores."
|
|
3156
3282
|
]);
|
|
3157
3283
|
}
|
|
3158
|
-
return
|
|
3284
|
+
return attributesOf5(news, after);
|
|
3159
3285
|
});
|
|
3160
3286
|
var whereOf = (olds, output) => ({
|
|
3161
3287
|
node: output?.node ?? olds.node,
|
|
3162
3288
|
target: olds.target,
|
|
3163
3289
|
vmid: output?.vmid ?? olds.vmid
|
|
3164
3290
|
});
|
|
3165
|
-
var read = (owner, olds) =>
|
|
3291
|
+
var read = (owner, olds) => Effect39.gen(function* () {
|
|
3166
3292
|
const where = whereOf(olds, owner.output);
|
|
3167
3293
|
const live = yield* readLive(where);
|
|
3168
3294
|
if (live === undefined)
|
|
3169
3295
|
return;
|
|
3170
|
-
const settled2 =
|
|
3171
|
-
return yield* ownedRead(owner,
|
|
3296
|
+
const settled2 = Effect39.sync(() => judge(olds, live).drift.length === 0);
|
|
3297
|
+
return yield* ownedRead(owner, attributesOf5(where, live), settled2);
|
|
3172
3298
|
});
|
|
3173
|
-
var
|
|
3299
|
+
var handlers7 = {
|
|
3174
3300
|
delete: ({ olds, output }) => destroyGuest(whereOf(olds, output), olds),
|
|
3175
3301
|
diff: ({
|
|
3176
3302
|
fqn,
|
|
@@ -3179,7 +3305,7 @@ var handlers8 = {
|
|
|
3179
3305
|
olds,
|
|
3180
3306
|
output
|
|
3181
3307
|
}) => diff({ fqn, instanceId, output }, news, olds),
|
|
3182
|
-
list: () =>
|
|
3308
|
+
list: () => Effect39.succeed([]),
|
|
3183
3309
|
read: ({
|
|
3184
3310
|
fqn,
|
|
3185
3311
|
instanceId,
|
|
@@ -3193,11 +3319,11 @@ var handlers8 = {
|
|
|
3193
3319
|
output
|
|
3194
3320
|
}) => reconcile({ fqn, instanceId, output }, news)
|
|
3195
3321
|
};
|
|
3196
|
-
var ProxmoxLxcProvider = () => Provider13.effect(ProxmoxLxc,
|
|
3322
|
+
var ProxmoxLxcProvider = () => Provider13.effect(ProxmoxLxc, Effect39.succeed(ProxmoxLxc.Provider.of(handlers7)).pipe(Effect39.map(provingResumes)));
|
|
3197
3323
|
// src/proxmox/metric-server.ts
|
|
3198
3324
|
import { Resource as Resource14 } from "alchemy";
|
|
3199
3325
|
import * as Provider14 from "alchemy/Provider";
|
|
3200
|
-
import * as
|
|
3326
|
+
import * as Effect40 from "effect/Effect";
|
|
3201
3327
|
|
|
3202
3328
|
// src/proxmox/metric-server-form.ts
|
|
3203
3329
|
var UNSET2 = -1;
|
|
@@ -3277,7 +3403,7 @@ var matchesType = (attributes, props) => {
|
|
|
3277
3403
|
return socket && attributes.path === (props.path ?? "");
|
|
3278
3404
|
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);
|
|
3279
3405
|
};
|
|
3280
|
-
var
|
|
3406
|
+
var handlers8 = pveHandlers({
|
|
3281
3407
|
attributes: (live, props) => {
|
|
3282
3408
|
const liveType = text(live["type"]);
|
|
3283
3409
|
if (liveType !== "" && liveType !== props.type)
|
|
@@ -3310,12 +3436,12 @@ var handlers9 = pveHandlers({
|
|
|
3310
3436
|
path: (props) => `cluster/metrics/server/${props.id}`,
|
|
3311
3437
|
updateForm: update
|
|
3312
3438
|
});
|
|
3313
|
-
var ProxmoxMetricServerProvider = () => Provider14.effect(ProxmoxMetricServer,
|
|
3439
|
+
var ProxmoxMetricServerProvider = () => Provider14.effect(ProxmoxMetricServer, Effect40.succeed(ProxmoxMetricServer.Provider.of(handlers8)));
|
|
3314
3440
|
// src/proxmox/network-apply.ts
|
|
3315
3441
|
import { Resource as Resource15 } from "alchemy";
|
|
3316
|
-
import { isResolved as
|
|
3442
|
+
import { isResolved as isResolved11 } from "alchemy/Diff";
|
|
3317
3443
|
import * as Provider15 from "alchemy/Provider";
|
|
3318
|
-
import * as
|
|
3444
|
+
import * as Effect41 from "effect/Effect";
|
|
3319
3445
|
|
|
3320
3446
|
// src/proxmox/apply-endpoints.ts
|
|
3321
3447
|
var NETWORK_APPLY_ENDPOINT = "pve:PUT /nodes/{node}/network";
|
|
@@ -3325,15 +3451,15 @@ var guardSdnApply = guardForm(SDN_APPLY_ENDPOINT, {}, true);
|
|
|
3325
3451
|
|
|
3326
3452
|
// src/proxmox/network-apply.ts
|
|
3327
3453
|
var ProxmoxNetworkApply = Resource15("Proxmox.NetworkApply");
|
|
3328
|
-
var read2 = (props) => pendingCount(props.target, props.node).pipe(
|
|
3329
|
-
var diff2 = (news) =>
|
|
3330
|
-
if (!
|
|
3454
|
+
var read2 = (props) => pendingCount(props.target, props.node).pipe(Effect41.map((pending) => ({ node: props.node, pending })));
|
|
3455
|
+
var diff2 = (news) => Effect41.gen(function* () {
|
|
3456
|
+
if (!isResolved11(news))
|
|
3331
3457
|
return;
|
|
3332
3458
|
const { pending } = yield* read2(news);
|
|
3333
3459
|
return pending === 0 ? { action: "noop" } : { action: "update" };
|
|
3334
3460
|
});
|
|
3335
|
-
var refuse = (node, why) =>
|
|
3336
|
-
var apply = (props) =>
|
|
3461
|
+
var refuse = (node, why) => Effect41.die(new Error(`Proxmox.NetworkApply ${node}: ${why}`));
|
|
3462
|
+
var apply = (props) => Effect41.gen(function* () {
|
|
3337
3463
|
const node = props.node;
|
|
3338
3464
|
const staged = yield* pendingCount(props.target, node);
|
|
3339
3465
|
if (staged === 0)
|
|
@@ -3362,20 +3488,20 @@ var apply = (props) => Effect38.gen(function* () {
|
|
|
3362
3488
|
}
|
|
3363
3489
|
return { node, pending: left };
|
|
3364
3490
|
});
|
|
3365
|
-
var
|
|
3366
|
-
delete: () =>
|
|
3491
|
+
var handlers9 = {
|
|
3492
|
+
delete: () => Effect41.void,
|
|
3367
3493
|
diff: ({ news }) => diff2(news),
|
|
3368
|
-
list: () =>
|
|
3494
|
+
list: () => Effect41.succeed([]),
|
|
3369
3495
|
read: ({ olds }) => read2(olds),
|
|
3370
3496
|
reconcile: ({ news }) => apply(news)
|
|
3371
3497
|
};
|
|
3372
|
-
var ProxmoxNetworkApplyProvider = () => Provider15.effect(ProxmoxNetworkApply,
|
|
3498
|
+
var ProxmoxNetworkApplyProvider = () => Provider15.effect(ProxmoxNetworkApply, Effect41.succeed(ProxmoxNetworkApply.Provider.of(handlers9)));
|
|
3373
3499
|
// src/proxmox/node-network.ts
|
|
3374
3500
|
import { Resource as Resource16 } from "alchemy";
|
|
3375
|
-
import { isResolved as
|
|
3501
|
+
import { isResolved as isResolved12 } from "alchemy/Diff";
|
|
3376
3502
|
import * as Provider16 from "alchemy/Provider";
|
|
3377
|
-
import * as
|
|
3378
|
-
import * as
|
|
3503
|
+
import * as nodes9 from "@distilled.cloud/proxmox/nodes";
|
|
3504
|
+
import * as Effect43 from "effect/Effect";
|
|
3379
3505
|
|
|
3380
3506
|
// src/proxmox/node-network-form.ts
|
|
3381
3507
|
var NODE_NETWORK_CREATE = "pve:POST /nodes/{node}/network";
|
|
@@ -3417,9 +3543,9 @@ var updateForm2 = (props) => {
|
|
|
3417
3543
|
};
|
|
3418
3544
|
|
|
3419
3545
|
// src/proxmox/node-network-wire.ts
|
|
3420
|
-
import * as
|
|
3421
|
-
import * as
|
|
3422
|
-
var
|
|
3546
|
+
import * as nodes8 from "@distilled.cloud/proxmox/nodes";
|
|
3547
|
+
import * as Effect42 from "effect/Effect";
|
|
3548
|
+
var attributesOf6 = (live, props) => {
|
|
3423
3549
|
const data = live;
|
|
3424
3550
|
const liveType = text(data["type"]);
|
|
3425
3551
|
if (liveType !== "" && liveType !== props.type)
|
|
@@ -3452,16 +3578,16 @@ var attributesOf5 = (live, props) => {
|
|
|
3452
3578
|
"vlan-raw-device": text(data["vlan-raw-device"])
|
|
3453
3579
|
};
|
|
3454
3580
|
};
|
|
3455
|
-
var readInterfaceOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false,
|
|
3581
|
+
var readInterfaceOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, nodes8.getNodeNetwork({ iface: props.iface, node: props.node })).pipe(Effect42.catchTag("ParameterVerificationFailed", (error) => error.errors["iface"] === "interface does not exist" ? Effect42.succeed(undefined) : Effect42.fail(error)))).pipe(Effect42.map((live) => {
|
|
3456
3582
|
if (live === UNREADABLE)
|
|
3457
3583
|
return UNREADABLE;
|
|
3458
|
-
return live === undefined ? undefined :
|
|
3584
|
+
return live === undefined ? undefined : attributesOf6(live, props);
|
|
3459
3585
|
}));
|
|
3460
|
-
var readInterface = (props) => readInterfaceOrFail(props).pipe(
|
|
3586
|
+
var readInterface = (props) => readInterfaceOrFail(props).pipe(Effect42.orElseSucceed(() => {
|
|
3461
3587
|
return;
|
|
3462
3588
|
}));
|
|
3463
3589
|
var dropUnreadable4 = (live) => live === UNREADABLE ? undefined : live;
|
|
3464
|
-
var
|
|
3590
|
+
var matches5 = (attributes, props) => attributes.cidr === (props.cidr ?? "") && same2(props.gateway, attributes.gateway) && same2(props.autostart, attributes.autostart) && same2(props.mtu, attributes.mtu) && sameComment(props.comments, attributes.comments) && sameList(props.bridge_ports, attributes.bridge_ports) && sameList(props.bridge_vids, attributes.bridge_vids) && same2(props.bridge_vlan_aware, attributes.bridge_vlan_aware) && sameList(props.slaves, attributes.slaves) && same2(props.bond_mode, attributes.bond_mode) && same2(props.bond_xmit_hash_policy, attributes.bond_xmit_hash_policy) && same2(props["bond-primary"], attributes["bond-primary"]) && same2(props["vlan-id"], attributes["vlan-id"]) && same2(props["vlan-raw-device"], attributes["vlan-raw-device"]);
|
|
3465
3591
|
|
|
3466
3592
|
// src/proxmox/node-network.ts
|
|
3467
3593
|
var ProxmoxNodeNetwork = Resource16("Proxmox.NodeNetwork", {
|
|
@@ -3473,22 +3599,22 @@ var RENAMED = {
|
|
|
3473
3599
|
"vlan-raw-device": "vlan_raw_device"
|
|
3474
3600
|
};
|
|
3475
3601
|
var toDistilled = (form) => Object.fromEntries(Object.entries(form).map(([key, value]) => [RENAMED[key] ?? key, value]));
|
|
3476
|
-
var
|
|
3602
|
+
var toDistilledCreate3 = (props) => ({
|
|
3477
3603
|
...toDistilled(createForm5(props)),
|
|
3478
3604
|
node: props.node
|
|
3479
3605
|
});
|
|
3480
|
-
var
|
|
3606
|
+
var toDistilledUpdate2 = (props) => ({
|
|
3481
3607
|
...toDistilled(updateForm2(props)),
|
|
3482
3608
|
iface: props.iface,
|
|
3483
3609
|
node: props.node
|
|
3484
3610
|
});
|
|
3485
|
-
var ProxmoxNodeNetworkProvider = () => Provider16.effect(ProxmoxNodeNetwork,
|
|
3486
|
-
list: () =>
|
|
3487
|
-
read:
|
|
3611
|
+
var ProxmoxNodeNetworkProvider = () => Provider16.effect(ProxmoxNodeNetwork, Effect43.succeed(ProxmoxNodeNetwork.Provider.of({
|
|
3612
|
+
list: () => Effect43.succeed([]),
|
|
3613
|
+
read: Effect43.fn(function* ({ olds, output }) {
|
|
3488
3614
|
return dropUnreadable4(yield* output === undefined ? readInterface(olds) : readInterfaceOrFail(olds));
|
|
3489
3615
|
}),
|
|
3490
|
-
diff:
|
|
3491
|
-
if (!
|
|
3616
|
+
diff: Effect43.fn(function* ({ news, output }) {
|
|
3617
|
+
if (!isResolved12(news))
|
|
3492
3618
|
return;
|
|
3493
3619
|
yield* guardWrite2(NODE_NETWORK_CREATE, createForm5(news), output === undefined);
|
|
3494
3620
|
yield* guardWrite2(NODE_NETWORK_UPDATE, updateForm2(news), false);
|
|
@@ -3503,31 +3629,31 @@ var ProxmoxNodeNetworkProvider = () => Provider16.effect(ProxmoxNodeNetwork, Eff
|
|
|
3503
3629
|
yield* guardWrite2(NODE_NETWORK_CREATE, createForm5(news), true);
|
|
3504
3630
|
return { action: "update" };
|
|
3505
3631
|
}
|
|
3506
|
-
return
|
|
3632
|
+
return matches5(live, news) ? { action: "noop" } : { action: "update" };
|
|
3507
3633
|
}),
|
|
3508
|
-
reconcile:
|
|
3634
|
+
reconcile: Effect43.fn(function* ({ news }) {
|
|
3509
3635
|
const before = dropUnreadable4(yield* readInterface(news));
|
|
3510
3636
|
yield* guardWrite2(NODE_NETWORK_CREATE, createForm5(news), before === undefined);
|
|
3511
3637
|
yield* guardWrite2(NODE_NETWORK_UPDATE, updateForm2(news), false);
|
|
3512
3638
|
if (before === undefined) {
|
|
3513
|
-
yield* runPve(news.target, "provision", true,
|
|
3514
|
-
} else if (!
|
|
3515
|
-
yield* runPve(news.target, "provision", true,
|
|
3639
|
+
yield* runPve(news.target, "provision", true, nodes9.createNodeNetwork(toDistilledCreate3(news)));
|
|
3640
|
+
} else if (!matches5(before, news)) {
|
|
3641
|
+
yield* runPve(news.target, "provision", true, nodes9.putNodeNetwork2(toDistilledUpdate2(news)));
|
|
3516
3642
|
}
|
|
3517
3643
|
const after = dropUnreadable4(yield* readInterface(news));
|
|
3518
3644
|
if (after === undefined) {
|
|
3519
|
-
return yield*
|
|
3645
|
+
return yield* Effect43.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."));
|
|
3520
3646
|
}
|
|
3521
3647
|
return after;
|
|
3522
3648
|
}),
|
|
3523
|
-
delete:
|
|
3524
|
-
yield* runPve(olds.target, "provision", true,
|
|
3649
|
+
delete: Effect43.fn(function* ({ olds }) {
|
|
3650
|
+
yield* runPve(olds.target, "provision", true, nodes9.deleteNodeNetwork2({ iface: olds.iface, node: olds.node }));
|
|
3525
3651
|
})
|
|
3526
3652
|
})));
|
|
3527
3653
|
// src/proxmox/notification-matcher.ts
|
|
3528
3654
|
import { Resource as Resource17 } from "alchemy";
|
|
3529
3655
|
import * as Provider17 from "alchemy/Provider";
|
|
3530
|
-
import * as
|
|
3656
|
+
import * as Effect44 from "effect/Effect";
|
|
3531
3657
|
|
|
3532
3658
|
// src/proxmox/notification-matcher-form.ts
|
|
3533
3659
|
var list = (value) => Array.isArray(value) ? value.map((item) => String(item)) : [];
|
|
@@ -3605,7 +3731,7 @@ var matcherUpdateForm = (fields) => {
|
|
|
3605
3731
|
|
|
3606
3732
|
// src/proxmox/notification-matcher.ts
|
|
3607
3733
|
var ProxmoxNotificationMatcher = Resource17("Proxmox.NotificationMatcher");
|
|
3608
|
-
var
|
|
3734
|
+
var handlers10 = pveHandlers({
|
|
3609
3735
|
attributes: (live, props) => matcherAttributes(live, props.name),
|
|
3610
3736
|
collection: () => "cluster/notifications/matchers",
|
|
3611
3737
|
createForm: matcherCreateForm,
|
|
@@ -3617,11 +3743,11 @@ var handlers11 = pveHandlers({
|
|
|
3617
3743
|
path: (props) => `cluster/notifications/matchers/${props.name}`,
|
|
3618
3744
|
updateForm: matcherUpdateForm
|
|
3619
3745
|
});
|
|
3620
|
-
var ProxmoxNotificationMatcherProvider = () => Provider17.effect(ProxmoxNotificationMatcher,
|
|
3746
|
+
var ProxmoxNotificationMatcherProvider = () => Provider17.effect(ProxmoxNotificationMatcher, Effect44.succeed(ProxmoxNotificationMatcher.Provider.of(handlers10)));
|
|
3621
3747
|
// src/proxmox/notification-target.ts
|
|
3622
3748
|
import { Resource as Resource18 } from "alchemy";
|
|
3623
3749
|
import * as Provider18 from "alchemy/Provider";
|
|
3624
|
-
import * as
|
|
3750
|
+
import * as Effect45 from "effect/Effect";
|
|
3625
3751
|
|
|
3626
3752
|
// src/proxmox/notification-target-endpoint.ts
|
|
3627
3753
|
var ENDPOINTS = {
|
|
@@ -3677,7 +3803,7 @@ var createShape = (props) => ({
|
|
|
3677
3803
|
|
|
3678
3804
|
// src/proxmox/notification-target.ts
|
|
3679
3805
|
var ProxmoxNotificationTarget = Resource18("Proxmox.NotificationTarget");
|
|
3680
|
-
var
|
|
3806
|
+
var handlers11 = pveHandlers({
|
|
3681
3807
|
attributes: (live, props) => ({
|
|
3682
3808
|
author: text(live["author"]),
|
|
3683
3809
|
body: text(live["body"]),
|
|
@@ -3703,15 +3829,15 @@ var handlers12 = pveHandlers({
|
|
|
3703
3829
|
path: (props) => `cluster/notifications/endpoints/${props.type}/${props.name}`,
|
|
3704
3830
|
updateForm: shape4
|
|
3705
3831
|
});
|
|
3706
|
-
var ProxmoxNotificationTargetProvider = () => Provider18.effect(ProxmoxNotificationTarget,
|
|
3832
|
+
var ProxmoxNotificationTargetProvider = () => Provider18.effect(ProxmoxNotificationTarget, Effect45.succeed(ProxmoxNotificationTarget.Provider.of(handlers11)));
|
|
3707
3833
|
// src/proxmox/pbs-datastore.ts
|
|
3708
3834
|
import { Resource as Resource19 } from "alchemy";
|
|
3709
|
-
import { isResolved as
|
|
3835
|
+
import { isResolved as isResolved13 } from "alchemy/Diff";
|
|
3710
3836
|
import * as Provider19 from "alchemy/Provider";
|
|
3711
|
-
import * as
|
|
3837
|
+
import * as Effect49 from "effect/Effect";
|
|
3712
3838
|
|
|
3713
3839
|
// src/proxmox/pbs-datastore-form.ts
|
|
3714
|
-
import * as
|
|
3840
|
+
import * as Effect46 from "effect/Effect";
|
|
3715
3841
|
var attributes = (live, props) => ({
|
|
3716
3842
|
backend: propertyString(live["backend"]),
|
|
3717
3843
|
comment: text(live["comment"]),
|
|
@@ -3734,7 +3860,7 @@ var attributes = (live, props) => ({
|
|
|
3734
3860
|
"verify-new": bool(live["verify-new"])
|
|
3735
3861
|
});
|
|
3736
3862
|
var same4 = (declared, live) => declared === undefined || declared === live;
|
|
3737
|
-
var
|
|
3863
|
+
var matches6 = (live, props) => same4(props.comment, live.comment) && same4(props["gc-schedule"]?.trim(), live["gc-schedule"]) && same4(props["prune-schedule"]?.trim(), live["prune-schedule"]) && same4(props["keep-last"], live["keep-last"]) && same4(props["keep-hourly"], live["keep-hourly"]) && same4(props["keep-daily"], live["keep-daily"]) && same4(props["keep-weekly"], live["keep-weekly"]) && same4(props["keep-monthly"], live["keep-monthly"]) && same4(props["keep-yearly"], live["keep-yearly"]) && same4(props["notify-user"], live["notify-user"]) && same4(props["counter-reset-schedule"]?.trim(), live["counter-reset-schedule"]) && (props["notification-thresholds"] === undefined || propertyString(props["notification-thresholds"]) === live["notification-thresholds"]) && same4(props["verify-new"], live["verify-new"]) && (props.notify === undefined || propertyString(props.notify) === live.notify) && (props.tuning === undefined || propertyString(props.tuning) === live.tuning) && (props["maintenance-mode"] === undefined || propertyString(props["maintenance-mode"], "type") === live["maintenance-mode"]);
|
|
3738
3864
|
var set2 = (key, value) => value === undefined ? {} : { [key]: value };
|
|
3739
3865
|
var count = (value) => value === undefined ? undefined : String(value);
|
|
3740
3866
|
var mutable = (props) => ({
|
|
@@ -3763,35 +3889,35 @@ var createForm6 = (props) => ({
|
|
|
3763
3889
|
});
|
|
3764
3890
|
var updateForm3 = (props) => mutable(props);
|
|
3765
3891
|
var object2 = (props) => `config/datastore/${props.name}`;
|
|
3766
|
-
var readOne = (props) => pve(props.target, "read", "GET", object2(props)).pipe(
|
|
3892
|
+
var readOne = (props) => pve(props.target, "read", "GET", object2(props)).pipe(Effect46.map((live) => live === undefined || typeof live["name"] !== "string" ? undefined : attributes(live, props)), Effect46.orElseSucceed(() => {
|
|
3767
3893
|
return;
|
|
3768
3894
|
}));
|
|
3769
3895
|
|
|
3770
3896
|
// src/proxmox/pbs-datastore-guard.ts
|
|
3771
|
-
import * as
|
|
3897
|
+
import * as Effect47 from "effect/Effect";
|
|
3772
3898
|
var samePath = (left, right) => {
|
|
3773
3899
|
const trim = (value) => value.length > 1 ? value.replace(/\/+$/, "") : value;
|
|
3774
3900
|
return trim(left) === trim(right);
|
|
3775
3901
|
};
|
|
3776
|
-
var guardPath = (live, props) => live.path === "" || samePath(live.path, props.path) ?
|
|
3777
|
-
var guardBackend = (live, props) => props.backend === undefined || propertyString(props.backend) === live.backend ?
|
|
3778
|
-
var settle3 = (read3, done) =>
|
|
3902
|
+
var guardPath = (live, props) => live.path === "" || samePath(live.path, props.path) ? Effect47.void : Effect47.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."));
|
|
3903
|
+
var guardBackend = (live, props) => props.backend === undefined || propertyString(props.backend) === live.backend ? Effect47.void : Effect47.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."));
|
|
3904
|
+
var settle3 = (read3, done) => Effect47.gen(function* () {
|
|
3779
3905
|
for (let attempt = 0;attempt < 60; attempt += 1) {
|
|
3780
3906
|
const live = yield* read3;
|
|
3781
3907
|
if (done(live))
|
|
3782
3908
|
return live;
|
|
3783
|
-
yield*
|
|
3909
|
+
yield* Effect47.sleep("2 seconds");
|
|
3784
3910
|
}
|
|
3785
3911
|
return yield* read3;
|
|
3786
3912
|
});
|
|
3787
3913
|
|
|
3788
3914
|
// src/proxmox/pbs-datastore-endpoint.ts
|
|
3789
|
-
import * as
|
|
3915
|
+
import * as Effect48 from "effect/Effect";
|
|
3790
3916
|
var DATASTORE_ENDPOINT = {
|
|
3791
3917
|
create: "pbs:POST /config/datastore",
|
|
3792
3918
|
update: "pbs:PUT /config/datastore/{name}"
|
|
3793
3919
|
};
|
|
3794
|
-
var guardDatastoreForms = (props) =>
|
|
3920
|
+
var guardDatastoreForms = (props) => Effect48.gen(function* () {
|
|
3795
3921
|
yield* guardForm(DATASTORE_ENDPOINT.create, createForm6(props), true);
|
|
3796
3922
|
yield* guardForm(DATASTORE_ENDPOINT.update, updateForm3(props), false);
|
|
3797
3923
|
});
|
|
@@ -3800,11 +3926,11 @@ var guardDatastoreForms = (props) => Effect45.gen(function* () {
|
|
|
3800
3926
|
var PbsDatastore = Resource19("Pbs.Datastore", {
|
|
3801
3927
|
defaultRemovalPolicy: "retain"
|
|
3802
3928
|
});
|
|
3803
|
-
var PbsDatastoreProvider = () => Provider19.effect(PbsDatastore,
|
|
3804
|
-
list: () =>
|
|
3929
|
+
var PbsDatastoreProvider = () => Provider19.effect(PbsDatastore, Effect49.succeed(PbsDatastore.Provider.of({
|
|
3930
|
+
list: () => Effect49.succeed([]),
|
|
3805
3931
|
read: ({ olds }) => readOne(olds),
|
|
3806
|
-
diff: ({ news, output }) =>
|
|
3807
|
-
if (!
|
|
3932
|
+
diff: ({ news, output }) => Effect49.gen(function* () {
|
|
3933
|
+
if (!isResolved13(news))
|
|
3808
3934
|
return;
|
|
3809
3935
|
yield* guardDatastoreForms(news);
|
|
3810
3936
|
if (output === undefined)
|
|
@@ -3814,9 +3940,9 @@ var PbsDatastoreProvider = () => Provider19.effect(PbsDatastore, Effect46.succee
|
|
|
3814
3940
|
return { action: "update" };
|
|
3815
3941
|
yield* guardPath(live, news);
|
|
3816
3942
|
yield* guardBackend(live, news);
|
|
3817
|
-
return
|
|
3943
|
+
return matches6(live, news) ? { action: "noop" } : { action: "update" };
|
|
3818
3944
|
}),
|
|
3819
|
-
reconcile:
|
|
3945
|
+
reconcile: Effect49.fn(function* ({ news }) {
|
|
3820
3946
|
yield* guardDatastoreForms(news);
|
|
3821
3947
|
const live = yield* readOne(news);
|
|
3822
3948
|
let upid;
|
|
@@ -3825,31 +3951,31 @@ var PbsDatastoreProvider = () => Provider19.effect(PbsDatastore, Effect46.succee
|
|
|
3825
3951
|
} else {
|
|
3826
3952
|
yield* guardPath(live, news);
|
|
3827
3953
|
yield* guardBackend(live, news);
|
|
3828
|
-
const form = formToSend(
|
|
3954
|
+
const form = formToSend(matches6, live, news, updateForm3(news));
|
|
3829
3955
|
if (form !== undefined) {
|
|
3830
3956
|
yield* pve(news.target, "provision", "PUT", object2(news), form);
|
|
3831
3957
|
}
|
|
3832
3958
|
}
|
|
3833
|
-
const after = yield* settle3(readOne(news), (row) => row !== undefined &&
|
|
3834
|
-
if (after === undefined || !
|
|
3835
|
-
return yield*
|
|
3959
|
+
const after = yield* settle3(readOne(news), (row) => row !== undefined && matches6(row, news));
|
|
3960
|
+
if (after === undefined || !matches6(after, news)) {
|
|
3961
|
+
return yield* Effect49.die(new Error(`${object2(news)}: the write returned no error but the datastore still does not ` + `match the declaration after 120s. PBS answered with task ${upid ?? "(none)"} ` + "-- 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."));
|
|
3836
3962
|
}
|
|
3837
3963
|
return after;
|
|
3838
3964
|
}),
|
|
3839
|
-
delete:
|
|
3965
|
+
delete: Effect49.fn(function* ({ olds }) {
|
|
3840
3966
|
yield* pve(olds.target, "provision", "DELETE", object2(olds));
|
|
3841
3967
|
const left = yield* settle3(readOne(olds), (row) => row === undefined);
|
|
3842
3968
|
if (left !== undefined) {
|
|
3843
|
-
return yield*
|
|
3969
|
+
return yield* Effect49.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>\`.`));
|
|
3844
3970
|
}
|
|
3845
3971
|
})
|
|
3846
3972
|
})));
|
|
3847
3973
|
// src/proxmox/pbs-notification-matcher.ts
|
|
3848
3974
|
import { Resource as Resource20 } from "alchemy";
|
|
3849
3975
|
import * as Provider20 from "alchemy/Provider";
|
|
3850
|
-
import * as
|
|
3976
|
+
import * as Effect50 from "effect/Effect";
|
|
3851
3977
|
var PbsNotificationMatcher = Resource20("Pbs.NotificationMatcher");
|
|
3852
|
-
var
|
|
3978
|
+
var handlers12 = pveHandlers({
|
|
3853
3979
|
attributes: (live, props) => matcherAttributes(live, props.name),
|
|
3854
3980
|
collection: () => "config/notifications/matchers",
|
|
3855
3981
|
createForm: matcherCreateForm,
|
|
@@ -3861,15 +3987,15 @@ var handlers13 = pveHandlers({
|
|
|
3861
3987
|
path: (props) => `config/notifications/matchers/${props.name}`,
|
|
3862
3988
|
updateForm: matcherUpdateForm
|
|
3863
3989
|
});
|
|
3864
|
-
var PbsNotificationMatcherProvider = () => Provider20.effect(PbsNotificationMatcher,
|
|
3990
|
+
var PbsNotificationMatcherProvider = () => Provider20.effect(PbsNotificationMatcher, Effect50.succeed(PbsNotificationMatcher.Provider.of(handlers12)));
|
|
3865
3991
|
// src/proxmox/pbs-notification-target.ts
|
|
3866
3992
|
import { Resource as Resource21 } from "alchemy";
|
|
3867
3993
|
import * as Provider21 from "alchemy/Provider";
|
|
3868
|
-
import * as
|
|
3994
|
+
import * as Effect52 from "effect/Effect";
|
|
3869
3995
|
|
|
3870
3996
|
// src/proxmox/pbs-notification-target-lifecycle.ts
|
|
3871
|
-
import { isResolved as
|
|
3872
|
-
import * as
|
|
3997
|
+
import { isResolved as isResolved14 } from "alchemy/Diff";
|
|
3998
|
+
import * as Effect51 from "effect/Effect";
|
|
3873
3999
|
|
|
3874
4000
|
// src/proxmox/pbs-notification-target-endpoint.ts
|
|
3875
4001
|
var ENDPOINTS2 = {
|
|
@@ -4146,12 +4272,12 @@ var targetForm = (props, groups, carry, mode) => {
|
|
|
4146
4272
|
|
|
4147
4273
|
// src/proxmox/pbs-notification-target-lifecycle.ts
|
|
4148
4274
|
var path = (props) => `config/notifications/endpoints/${props.type}/${props.name}`;
|
|
4149
|
-
var readLive2 = (props) => pve(props.target, "read", "GET", path(props)).pipe(
|
|
4275
|
+
var readLive2 = (props) => pve(props.target, "read", "GET", path(props)).pipe(Effect51.map((data) => data == null ? undefined : targetAttributes(data, props)), Effect51.orElseSucceed(() => {
|
|
4150
4276
|
return;
|
|
4151
4277
|
}));
|
|
4152
4278
|
var refuse2 = (props) => {
|
|
4153
4279
|
const reasons = refusals(props);
|
|
4154
|
-
return reasons.length === 0 ?
|
|
4280
|
+
return reasons.length === 0 ? Effect51.void : Effect51.die(new Error(`${path(props)}: ${reasons.join("; ")}.`));
|
|
4155
4281
|
};
|
|
4156
4282
|
var toCarry = (live, props, groups, sealed) => {
|
|
4157
4283
|
const header = headerState(live, props, groups) === "stale";
|
|
@@ -4163,17 +4289,17 @@ var requireValues = (props, groups, carry) => {
|
|
|
4163
4289
|
...carry.header ? groups.header.missing : [],
|
|
4164
4290
|
...carry.sealed ? groups.sealed.missing : []
|
|
4165
4291
|
];
|
|
4166
|
-
return missing.length === 0 ?
|
|
4292
|
+
return missing.length === 0 ? Effect51.void : Effect51.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."));
|
|
4167
4293
|
};
|
|
4168
|
-
var
|
|
4169
|
-
list: () =>
|
|
4170
|
-
read: ({ olds, output }) => (output === undefined ? refuse2(olds) :
|
|
4294
|
+
var handlers13 = {
|
|
4295
|
+
list: () => Effect51.succeed([]),
|
|
4296
|
+
read: ({ olds, output }) => (output === undefined ? refuse2(olds) : Effect51.void).pipe(Effect51.andThen(readLive2(olds)), Effect51.map((live) => live === undefined ? undefined : { ...live, sealed: output?.sealed ?? "" })),
|
|
4171
4297
|
diff: ({
|
|
4172
4298
|
news,
|
|
4173
4299
|
olds,
|
|
4174
4300
|
output
|
|
4175
|
-
}) =>
|
|
4176
|
-
if (output === undefined || !
|
|
4301
|
+
}) => Effect51.gen(function* () {
|
|
4302
|
+
if (output === undefined || !isResolved14(news))
|
|
4177
4303
|
return;
|
|
4178
4304
|
if (olds !== undefined && (olds.type !== news.type || olds.name !== news.name)) {
|
|
4179
4305
|
return { action: "replace", deleteFirst: olds.name === news.name };
|
|
@@ -4184,7 +4310,7 @@ var handlers14 = {
|
|
|
4184
4310
|
return { action: "update" };
|
|
4185
4311
|
return toCarry(live, news, resolveGroups(news), output.sealed) === undefined ? { action: "noop" } : { action: "update" };
|
|
4186
4312
|
}),
|
|
4187
|
-
reconcile: ({ news, output }) =>
|
|
4313
|
+
reconcile: ({ news, output }) => Effect51.gen(function* () {
|
|
4188
4314
|
yield* refuse2(news);
|
|
4189
4315
|
const groups = resolveGroups(news);
|
|
4190
4316
|
const live = yield* readLive2(news);
|
|
@@ -4192,10 +4318,10 @@ var handlers14 = {
|
|
|
4192
4318
|
if (live === undefined) {
|
|
4193
4319
|
const carry = { header: true, sealed: true };
|
|
4194
4320
|
yield* requireValues(news, groups, carry);
|
|
4195
|
-
const
|
|
4321
|
+
const collection = `config/notifications/endpoints/${news.type}`;
|
|
4196
4322
|
const form = targetForm(news, groups, carry, "create");
|
|
4197
4323
|
yield* guardForm(pbsTargetEndpoint(news).create, form, true);
|
|
4198
|
-
yield* pve(news.target, "provision", "POST",
|
|
4324
|
+
yield* pve(news.target, "provision", "POST", collection, form);
|
|
4199
4325
|
sealed = seal(groups.sealed.values);
|
|
4200
4326
|
} else {
|
|
4201
4327
|
const carry = toCarry(live, news, groups, sealed);
|
|
@@ -4210,7 +4336,7 @@ var handlers14 = {
|
|
|
4210
4336
|
}
|
|
4211
4337
|
const after = yield* readLive2(news);
|
|
4212
4338
|
if (after === undefined) {
|
|
4213
|
-
return yield*
|
|
4339
|
+
return yield* Effect51.die(new Error(`${path(news)}: the write returned no error but the target is still absent.`));
|
|
4214
4340
|
}
|
|
4215
4341
|
return { ...after, sealed };
|
|
4216
4342
|
}),
|
|
@@ -4219,11 +4345,11 @@ var handlers14 = {
|
|
|
4219
4345
|
|
|
4220
4346
|
// src/proxmox/pbs-notification-target.ts
|
|
4221
4347
|
var PbsNotificationTarget = Resource21("Pbs.NotificationTarget");
|
|
4222
|
-
var PbsNotificationTargetProvider = () => Provider21.effect(PbsNotificationTarget,
|
|
4348
|
+
var PbsNotificationTargetProvider = () => Provider21.effect(PbsNotificationTarget, Effect52.succeed(PbsNotificationTarget.Provider.of(handlers13)));
|
|
4223
4349
|
// src/proxmox/pbs-prune-job.ts
|
|
4224
4350
|
import { Resource as Resource22 } from "alchemy";
|
|
4225
4351
|
import * as Provider22 from "alchemy/Provider";
|
|
4226
|
-
import * as
|
|
4352
|
+
import * as Effect53 from "effect/Effect";
|
|
4227
4353
|
|
|
4228
4354
|
// src/proxmox/pbs-prune-job-form.ts
|
|
4229
4355
|
var KEEP_KEYS = [
|
|
@@ -4281,7 +4407,7 @@ var updateBody2 = (props) => {
|
|
|
4281
4407
|
// src/proxmox/pbs-prune-job.ts
|
|
4282
4408
|
var PbsPruneJob = Resource22("Pbs.PruneJob");
|
|
4283
4409
|
var same6 = (declared2, live) => declared2 === undefined || declared2 === live;
|
|
4284
|
-
var
|
|
4410
|
+
var handlers14 = pveHandlers({
|
|
4285
4411
|
attributes: (live, props) => ({
|
|
4286
4412
|
comment: text(live["comment"], ""),
|
|
4287
4413
|
disable: bool(live["disable"], false),
|
|
@@ -4299,11 +4425,11 @@ var handlers15 = pveHandlers({
|
|
|
4299
4425
|
path: (props) => `config/prune/${props.id}`,
|
|
4300
4426
|
updateForm: updateBody2
|
|
4301
4427
|
});
|
|
4302
|
-
var PbsPruneJobProvider = () => Provider22.effect(PbsPruneJob,
|
|
4428
|
+
var PbsPruneJobProvider = () => Provider22.effect(PbsPruneJob, Effect53.succeed(PbsPruneJob.Provider.of(handlers14)));
|
|
4303
4429
|
// src/proxmox/pbs-sync-job.ts
|
|
4304
4430
|
import { Resource as Resource23 } from "alchemy";
|
|
4305
4431
|
import * as Provider23 from "alchemy/Provider";
|
|
4306
|
-
import * as
|
|
4432
|
+
import * as Effect54 from "effect/Effect";
|
|
4307
4433
|
|
|
4308
4434
|
// src/proxmox/pbs-sync-job-form.ts
|
|
4309
4435
|
var UNSET4 = -1;
|
|
@@ -4351,7 +4477,7 @@ var createBody4 = (props) => ({
|
|
|
4351
4477
|
...props["sync-direction"] === undefined ? {} : { "sync-direction": props["sync-direction"] }
|
|
4352
4478
|
});
|
|
4353
4479
|
var updateBody3 = (props) => shape6(props);
|
|
4354
|
-
var
|
|
4480
|
+
var handlers15 = pveHandlers({
|
|
4355
4481
|
attributes: (live, props) => ({
|
|
4356
4482
|
comment: text(live["comment"], ""),
|
|
4357
4483
|
"group-filter": filters(live["group-filter"]),
|
|
@@ -4380,11 +4506,11 @@ var handlers16 = pveHandlers({
|
|
|
4380
4506
|
|
|
4381
4507
|
// src/proxmox/pbs-sync-job.ts
|
|
4382
4508
|
var PbsSyncJob = Resource23("Pbs.SyncJob");
|
|
4383
|
-
var PbsSyncJobProvider = () => Provider23.effect(PbsSyncJob,
|
|
4509
|
+
var PbsSyncJobProvider = () => Provider23.effect(PbsSyncJob, Effect54.succeed(PbsSyncJob.Provider.of(handlers15)));
|
|
4384
4510
|
// src/proxmox/pbs-verify-job.ts
|
|
4385
4511
|
import { Resource as Resource24 } from "alchemy";
|
|
4386
4512
|
import * as Provider24 from "alchemy/Provider";
|
|
4387
|
-
import * as
|
|
4513
|
+
import * as Effect55 from "effect/Effect";
|
|
4388
4514
|
|
|
4389
4515
|
// src/proxmox/pbs-verify-job-form.ts
|
|
4390
4516
|
var rechecks = (ignoreVerified, outdatedAfter) => {
|
|
@@ -4406,7 +4532,7 @@ var shape7 = (props) => ({
|
|
|
4406
4532
|
|
|
4407
4533
|
// src/proxmox/pbs-verify-job.ts
|
|
4408
4534
|
var PbsVerifyJob = Resource24("Pbs.VerifyJob");
|
|
4409
|
-
var
|
|
4535
|
+
var handlers16 = pveHandlers({
|
|
4410
4536
|
attributes: (live, props) => {
|
|
4411
4537
|
const ignoreVerified = bool(live["ignore-verified"], true);
|
|
4412
4538
|
const outdatedAfter = int(live["outdated-after"], -1);
|
|
@@ -4429,11 +4555,11 @@ var handlers17 = pveHandlers({
|
|
|
4429
4555
|
path: (props) => `config/verify/${props.id}`,
|
|
4430
4556
|
updateForm: shape7
|
|
4431
4557
|
});
|
|
4432
|
-
var PbsVerifyJobProvider = () => Provider24.effect(PbsVerifyJob,
|
|
4558
|
+
var PbsVerifyJobProvider = () => Provider24.effect(PbsVerifyJob, Effect55.succeed(PbsVerifyJob.Provider.of(handlers16)));
|
|
4433
4559
|
// src/proxmox/pool.ts
|
|
4434
4560
|
import { Resource as Resource25 } from "alchemy";
|
|
4435
4561
|
import * as Provider25 from "alchemy/Provider";
|
|
4436
|
-
import * as
|
|
4562
|
+
import * as Effect56 from "effect/Effect";
|
|
4437
4563
|
var ProxmoxPool = Resource25("Proxmox.Pool");
|
|
4438
4564
|
var poolCreateForm = (props) => ({
|
|
4439
4565
|
comment: props.comment ?? "",
|
|
@@ -4442,7 +4568,7 @@ var poolCreateForm = (props) => ({
|
|
|
4442
4568
|
var poolUpdateForm = (props) => ({
|
|
4443
4569
|
comment: props.comment ?? ""
|
|
4444
4570
|
});
|
|
4445
|
-
var
|
|
4571
|
+
var handlers17 = pveHandlers({
|
|
4446
4572
|
attributes: (live, props) => {
|
|
4447
4573
|
const row = live;
|
|
4448
4574
|
return {
|
|
@@ -4458,7 +4584,7 @@ var handlers18 = pveHandlers({
|
|
|
4458
4584
|
path: (props) => `pools/${props.poolid}`,
|
|
4459
4585
|
updateForm: poolUpdateForm
|
|
4460
4586
|
});
|
|
4461
|
-
var ProxmoxPoolProvider = () => Provider25.effect(ProxmoxPool,
|
|
4587
|
+
var ProxmoxPoolProvider = () => Provider25.effect(ProxmoxPool, Effect56.succeed(ProxmoxPool.Provider.of(handlers17)));
|
|
4462
4588
|
// src/proxmox/provision-baseline.ts
|
|
4463
4589
|
var PROVISION_PRIVILEGES = Object.freeze([
|
|
4464
4590
|
"Datastore.Allocate",
|
|
@@ -4651,14 +4777,14 @@ echo 'provision baseline: done. Next: point the OpenBao proxmox mount at these u
|
|
|
4651
4777
|
// src/proxmox/provision-declare.ts
|
|
4652
4778
|
import { adopt } from "alchemy/AdoptPolicy";
|
|
4653
4779
|
import * as RemovalPolicy from "alchemy/RemovalPolicy";
|
|
4654
|
-
import * as
|
|
4780
|
+
import * as Effect60 from "effect/Effect";
|
|
4655
4781
|
|
|
4656
4782
|
// src/proxmox/role.ts
|
|
4657
4783
|
import { Resource as Resource26 } from "alchemy";
|
|
4658
|
-
import { isResolved as
|
|
4784
|
+
import { isResolved as isResolved15 } from "alchemy/Diff";
|
|
4659
4785
|
import * as Provider26 from "alchemy/Provider";
|
|
4660
4786
|
import * as access5 from "@distilled.cloud/proxmox/access";
|
|
4661
|
-
import * as
|
|
4787
|
+
import * as Effect57 from "effect/Effect";
|
|
4662
4788
|
|
|
4663
4789
|
// src/proxmox/role-wire.ts
|
|
4664
4790
|
var ROLE_CREATE = "pve:POST /access/roles";
|
|
@@ -4673,28 +4799,28 @@ var updateForm4 = (props) => ({
|
|
|
4673
4799
|
roleid: props.roleid
|
|
4674
4800
|
});
|
|
4675
4801
|
var find2 = (rows, roleid) => rows.find((row) => row.roleid === roleid);
|
|
4676
|
-
var
|
|
4802
|
+
var attributesOf7 = (row, props) => ({
|
|
4677
4803
|
privs: canonical2(row.privs === undefined ? [] : row.privs.split(",")),
|
|
4678
4804
|
roleid: props.roleid
|
|
4679
4805
|
});
|
|
4680
|
-
var
|
|
4806
|
+
var matches7 = (attributes2, props) => canonical2(attributes2.privs).join(",") === canonical2(props.privs).join(",");
|
|
4681
4807
|
|
|
4682
4808
|
// src/proxmox/role.ts
|
|
4683
4809
|
var ProxmoxRole = Resource26("Proxmox.Role");
|
|
4684
|
-
var readRole = (props) => readOrUnreadable(runPve(props.target, "read", false, access5.listAccessRoles({}))).pipe(
|
|
4810
|
+
var readRole = (props) => readOrUnreadable(runPve(props.target, "read", false, access5.listAccessRoles({}))).pipe(Effect57.map((rows) => {
|
|
4685
4811
|
if (rows === UNREADABLE)
|
|
4686
4812
|
return UNREADABLE;
|
|
4687
4813
|
const row = find2(rows, props.roleid);
|
|
4688
|
-
return row === undefined ? undefined :
|
|
4814
|
+
return row === undefined ? undefined : attributesOf7(row, props);
|
|
4689
4815
|
}));
|
|
4690
4816
|
var dropUnreadable5 = (live) => live === UNREADABLE ? undefined : live;
|
|
4691
|
-
var ProxmoxRoleProvider = () => Provider26.effect(ProxmoxRole,
|
|
4692
|
-
list: () =>
|
|
4693
|
-
read:
|
|
4817
|
+
var ProxmoxRoleProvider = () => Provider26.effect(ProxmoxRole, Effect57.succeed(ProxmoxRole.Provider.of({
|
|
4818
|
+
list: () => Effect57.succeed([]),
|
|
4819
|
+
read: Effect57.fn(function* ({ olds }) {
|
|
4694
4820
|
return dropUnreadable5(yield* readRole(olds));
|
|
4695
4821
|
}),
|
|
4696
|
-
diff:
|
|
4697
|
-
if (!
|
|
4822
|
+
diff: Effect57.fn(function* ({ news, output }) {
|
|
4823
|
+
if (!isResolved15(news))
|
|
4698
4824
|
return;
|
|
4699
4825
|
yield* guardWrite2(ROLE_CREATE, createForm7(news), output === undefined);
|
|
4700
4826
|
yield* guardWrite2(ROLE_UPDATE, updateForm4(news), false);
|
|
@@ -4709,38 +4835,38 @@ var ProxmoxRoleProvider = () => Provider26.effect(ProxmoxRole, Effect54.succeed(
|
|
|
4709
4835
|
yield* guardWrite2(ROLE_CREATE, createForm7(news), true);
|
|
4710
4836
|
return { action: "update" };
|
|
4711
4837
|
}
|
|
4712
|
-
return
|
|
4838
|
+
return matches7(live, news) ? { action: "noop" } : { action: "update" };
|
|
4713
4839
|
}),
|
|
4714
|
-
reconcile:
|
|
4840
|
+
reconcile: Effect57.fn(function* ({ news }) {
|
|
4715
4841
|
const before = dropUnreadable5(yield* readRole(news));
|
|
4716
4842
|
yield* guardWrite2(ROLE_CREATE, createForm7(news), before === undefined);
|
|
4717
4843
|
yield* guardWrite2(ROLE_UPDATE, updateForm4(news), false);
|
|
4718
4844
|
if (before === undefined) {
|
|
4719
4845
|
yield* runPve(news.target, "provision", true, access5.createAccessRole(createForm7(news)));
|
|
4720
|
-
} else if (!
|
|
4846
|
+
} else if (!matches7(before, news)) {
|
|
4721
4847
|
yield* runPve(news.target, "provision", true, access5.putAccessRole(updateForm4(news)));
|
|
4722
4848
|
}
|
|
4723
4849
|
const after = dropUnreadable5(yield* readRole(news));
|
|
4724
4850
|
if (after === undefined) {
|
|
4725
|
-
return yield*
|
|
4851
|
+
return yield* Effect57.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."));
|
|
4726
4852
|
}
|
|
4727
4853
|
return after;
|
|
4728
4854
|
}),
|
|
4729
|
-
delete:
|
|
4855
|
+
delete: Effect57.fn(function* ({ olds }) {
|
|
4730
4856
|
yield* runPve(olds.target, "provision", true, access5.deleteAccessRole({ roleid: olds.roleid }));
|
|
4731
4857
|
})
|
|
4732
4858
|
})));
|
|
4733
4859
|
|
|
4734
4860
|
// src/proxmox/user.ts
|
|
4735
4861
|
import { Resource as Resource27 } from "alchemy";
|
|
4736
|
-
import { isResolved as
|
|
4862
|
+
import { isResolved as isResolved16 } from "alchemy/Diff";
|
|
4737
4863
|
import * as Provider27 from "alchemy/Provider";
|
|
4738
4864
|
import * as access7 from "@distilled.cloud/proxmox/access";
|
|
4739
|
-
import * as
|
|
4865
|
+
import * as Effect59 from "effect/Effect";
|
|
4740
4866
|
|
|
4741
4867
|
// src/proxmox/user-wire.ts
|
|
4742
4868
|
import * as access6 from "@distilled.cloud/proxmox/access";
|
|
4743
|
-
import * as
|
|
4869
|
+
import * as Effect58 from "effect/Effect";
|
|
4744
4870
|
var USER_CREATE = "pve:POST /access/users";
|
|
4745
4871
|
var USER_UPDATE = "pve:PUT /access/users/{userid}";
|
|
4746
4872
|
var int2 = (value) => {
|
|
@@ -4770,7 +4896,7 @@ var updateForm5 = (props) => ({
|
|
|
4770
4896
|
...shape8(props),
|
|
4771
4897
|
userid: props.userid
|
|
4772
4898
|
});
|
|
4773
|
-
var
|
|
4899
|
+
var attributesOf8 = (live, props) => ({
|
|
4774
4900
|
comment: text(live.comment),
|
|
4775
4901
|
email: text(live.email),
|
|
4776
4902
|
enable: bool(live.enable, true),
|
|
@@ -4781,22 +4907,20 @@ var attributesOf7 = (live, props) => ({
|
|
|
4781
4907
|
tokens: tokenNames(live.tokens),
|
|
4782
4908
|
userid: props.userid
|
|
4783
4909
|
});
|
|
4784
|
-
var
|
|
4785
|
-
var readUserOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, access6.getAccessUser({ userid: props.userid }))).pipe(
|
|
4786
|
-
var readUser = (props) => readUserOrFail(props).pipe(
|
|
4787
|
-
return;
|
|
4788
|
-
}));
|
|
4910
|
+
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(Effect58.map((live) => live === UNREADABLE ? UNREADABLE : attributesOf8(live, props)));
|
|
4912
|
+
var readUser = (props) => readUserOrFail(props).pipe(Effect58.catchTag("UserNotFound", () => Effect58.succeed(undefined)));
|
|
4789
4913
|
var dropUnreadable6 = (live) => live === UNREADABLE ? undefined : live;
|
|
4790
4914
|
|
|
4791
4915
|
// src/proxmox/user.ts
|
|
4792
4916
|
var ProxmoxUser = Resource27("Proxmox.User");
|
|
4793
|
-
var ProxmoxUserProvider = () => Provider27.effect(ProxmoxUser,
|
|
4794
|
-
list: () =>
|
|
4795
|
-
read:
|
|
4917
|
+
var ProxmoxUserProvider = () => Provider27.effect(ProxmoxUser, Effect59.succeed(ProxmoxUser.Provider.of({
|
|
4918
|
+
list: () => Effect59.succeed([]),
|
|
4919
|
+
read: Effect59.fn(function* ({ olds, output }) {
|
|
4796
4920
|
return dropUnreadable6(yield* output === undefined ? readUser(olds) : readUserOrFail(olds));
|
|
4797
4921
|
}),
|
|
4798
|
-
diff:
|
|
4799
|
-
if (!
|
|
4922
|
+
diff: Effect59.fn(function* ({ news, output }) {
|
|
4923
|
+
if (!isResolved16(news))
|
|
4800
4924
|
return;
|
|
4801
4925
|
yield* guardWrite2(USER_CREATE, createForm8(news), output === undefined);
|
|
4802
4926
|
yield* guardWrite2(USER_UPDATE, updateForm5(news), false);
|
|
@@ -4811,31 +4935,31 @@ var ProxmoxUserProvider = () => Provider27.effect(ProxmoxUser, Effect56.succeed(
|
|
|
4811
4935
|
yield* guardWrite2(USER_CREATE, createForm8(news), true);
|
|
4812
4936
|
return { action: "update" };
|
|
4813
4937
|
}
|
|
4814
|
-
return
|
|
4938
|
+
return matches8(live, news) ? { action: "noop" } : { action: "update" };
|
|
4815
4939
|
}),
|
|
4816
|
-
reconcile:
|
|
4940
|
+
reconcile: Effect59.fn(function* ({ news }) {
|
|
4817
4941
|
const before = dropUnreadable6(yield* readUser(news));
|
|
4818
4942
|
yield* guardWrite2(USER_CREATE, createForm8(news), before === undefined);
|
|
4819
4943
|
yield* guardWrite2(USER_UPDATE, updateForm5(news), false);
|
|
4820
4944
|
if (before === undefined) {
|
|
4821
4945
|
yield* runPve(news.target, "provision", true, access7.createAccessUser(createForm8(news)));
|
|
4822
|
-
} else if (!
|
|
4946
|
+
} else if (!matches8(before, news)) {
|
|
4823
4947
|
yield* runPve(news.target, "provision", true, access7.putAccessUser(updateForm5(news)));
|
|
4824
4948
|
}
|
|
4825
4949
|
const after = dropUnreadable6(yield* readUser(news));
|
|
4826
4950
|
if (after === undefined) {
|
|
4827
|
-
return yield*
|
|
4951
|
+
return yield* Effect59.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."));
|
|
4828
4952
|
}
|
|
4829
4953
|
return after;
|
|
4830
4954
|
}),
|
|
4831
|
-
delete:
|
|
4955
|
+
delete: Effect59.fn(function* ({ olds }) {
|
|
4832
4956
|
yield* runPve(olds.target, "provision", true, access7.deleteAccessUser({ userid: olds.userid }));
|
|
4833
4957
|
})
|
|
4834
4958
|
})));
|
|
4835
4959
|
|
|
4836
4960
|
// src/proxmox/provision-declare.ts
|
|
4837
|
-
var declareProvisionBaseline = (id, target, names2 = {}, options = {}) =>
|
|
4838
|
-
const baseline = yield*
|
|
4961
|
+
var declareProvisionBaseline = (id, target, names2 = {}, options = {}) => Effect60.gen(function* () {
|
|
4962
|
+
const baseline = yield* Effect60.try({
|
|
4839
4963
|
catch: (cause) => cause instanceof Error ? cause : new Error(String(cause)),
|
|
4840
4964
|
try: () => provisionBaseline(names2)
|
|
4841
4965
|
});
|
|
@@ -4849,13 +4973,13 @@ var declareProvisionBaseline = (id, target, names2 = {}, options = {}) => Effect
|
|
|
4849
4973
|
target
|
|
4850
4974
|
}));
|
|
4851
4975
|
const group = yield* owned(ProxmoxGroup(`${id}-mint-group`, { ...baseline.group, target }));
|
|
4852
|
-
const users = yield*
|
|
4976
|
+
const users = yield* Effect60.all(baseline.users.map((user) => owned(ProxmoxUser(`${id}-${user.lane}-user`, {
|
|
4853
4977
|
comment: user.comment,
|
|
4854
4978
|
groups: [...user.groups],
|
|
4855
4979
|
target,
|
|
4856
4980
|
userid: user.userid
|
|
4857
4981
|
}))));
|
|
4858
|
-
const grants = yield*
|
|
4982
|
+
const grants = yield* Effect60.all(baseline.grants.map((grant) => owned(ProxmoxAcl(`${id}-${grant.lane}-grant`, {
|
|
4859
4983
|
path: "/",
|
|
4860
4984
|
propagate: true,
|
|
4861
4985
|
roleid: grant.roleid,
|
|
@@ -4868,7 +4992,7 @@ var declareProvisionBaseline = (id, target, names2 = {}, options = {}) => Effect
|
|
|
4868
4992
|
// src/proxmox/qemu.ts
|
|
4869
4993
|
import { Resource as Resource28 } from "alchemy";
|
|
4870
4994
|
import * as Provider28 from "alchemy/Provider";
|
|
4871
|
-
import * as
|
|
4995
|
+
import * as Effect61 from "effect/Effect";
|
|
4872
4996
|
var ProxmoxVm = Resource28("Proxmox.Vm");
|
|
4873
4997
|
var createForm9 = (props) => ({ ...shape9(props), vmid: String(props.vmid) });
|
|
4874
4998
|
var shape9 = (props) => ({
|
|
@@ -4879,7 +5003,7 @@ var shape9 = (props) => ({
|
|
|
4879
5003
|
sockets: String(props.sockets ?? 1),
|
|
4880
5004
|
...props.net0 === undefined ? {} : { net0: props.net0 }
|
|
4881
5005
|
});
|
|
4882
|
-
var
|
|
5006
|
+
var handlers18 = pveHandlers({
|
|
4883
5007
|
attributes: (live, props) => ({
|
|
4884
5008
|
cores: num(live["cores"], 1),
|
|
4885
5009
|
memory: num(live["memory"], 512),
|
|
@@ -4899,11 +5023,11 @@ var handlers19 = pveHandlers({
|
|
|
4899
5023
|
path: (props) => `nodes/${props.node}/qemu/${String(props.vmid)}/config`,
|
|
4900
5024
|
updateForm: shape9
|
|
4901
5025
|
});
|
|
4902
|
-
var ProxmoxVmProvider = () => Provider28.effect(ProxmoxVm,
|
|
5026
|
+
var ProxmoxVmProvider = () => Provider28.effect(ProxmoxVm, Effect61.succeed(ProxmoxVm.Provider.of(handlers18)));
|
|
4903
5027
|
// src/proxmox/replication-job.ts
|
|
4904
5028
|
import { Resource as Resource29 } from "alchemy";
|
|
4905
5029
|
import * as Provider29 from "alchemy/Provider";
|
|
4906
|
-
import * as
|
|
5030
|
+
import * as Effect62 from "effect/Effect";
|
|
4907
5031
|
|
|
4908
5032
|
// src/proxmox/replication-job-form.ts
|
|
4909
5033
|
var DEFAULT_SCHEDULE = "*/15";
|
|
@@ -4933,7 +5057,7 @@ var rateOf = (value) => {
|
|
|
4933
5057
|
const parsed = Number.parseFloat(text(value));
|
|
4934
5058
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
4935
5059
|
};
|
|
4936
|
-
var
|
|
5060
|
+
var handlers19 = pveHandlers({
|
|
4937
5061
|
attributes: (live, props) => ({
|
|
4938
5062
|
comment: text(live["comment"], ""),
|
|
4939
5063
|
disable: bool(live["disable"], false),
|
|
@@ -4957,15 +5081,15 @@ var handlers20 = pveHandlers({
|
|
|
4957
5081
|
path: (props) => `cluster/replication/${jobId(props)}`,
|
|
4958
5082
|
updateForm: updateBody4
|
|
4959
5083
|
});
|
|
4960
|
-
var ProxmoxReplicationJobProvider = () => Provider29.effect(ProxmoxReplicationJob,
|
|
5084
|
+
var ProxmoxReplicationJobProvider = () => Provider29.effect(ProxmoxReplicationJob, Effect62.succeed(ProxmoxReplicationJob.Provider.of(handlers19)));
|
|
4961
5085
|
// src/proxmox/sdn-apply.ts
|
|
4962
5086
|
import { Resource as Resource30 } from "alchemy";
|
|
4963
|
-
import { isResolved as
|
|
5087
|
+
import { isResolved as isResolved17 } from "alchemy/Diff";
|
|
4964
5088
|
import * as Provider30 from "alchemy/Provider";
|
|
4965
|
-
import * as
|
|
5089
|
+
import * as Effect64 from "effect/Effect";
|
|
4966
5090
|
|
|
4967
5091
|
// src/proxmox/sdn-apply-read.ts
|
|
4968
|
-
import * as
|
|
5092
|
+
import * as Effect63 from "effect/Effect";
|
|
4969
5093
|
var MARKED = [
|
|
4970
5094
|
"cluster/sdn/zones",
|
|
4971
5095
|
"cluster/sdn/vnets",
|
|
@@ -4974,7 +5098,7 @@ var MARKED = [
|
|
|
4974
5098
|
"cluster/sdn/route-maps"
|
|
4975
5099
|
];
|
|
4976
5100
|
var subsystemAbsent = (error) => error instanceof PveError && error.status === 501;
|
|
4977
|
-
var markedPending = (target) =>
|
|
5101
|
+
var markedPending = (target) => Effect63.all(MARKED.map((collection) => pve(target, "read", "GET", `${collection}?pending=1`).pipe(Effect63.map((rows) => (rows ?? []).filter((row) => row.state !== undefined).length), Effect63.catchIf(subsystemAbsent, () => Effect63.succeed(0)))), { concurrency: "unbounded" }).pipe(Effect63.map((counts) => counts.reduce((total, count2) => total + count2, 0)));
|
|
4978
5102
|
var fabricStaged = (staged, running) => {
|
|
4979
5103
|
if (staged === undefined && running === undefined)
|
|
4980
5104
|
return 0;
|
|
@@ -4982,9 +5106,9 @@ var fabricStaged = (staged, running) => {
|
|
|
4982
5106
|
return "inconsistent";
|
|
4983
5107
|
return staged === running ? 0 : 1;
|
|
4984
5108
|
};
|
|
4985
|
-
var fabricPending = (target) =>
|
|
5109
|
+
var fabricPending = (target) => Effect63.all(["pending", "running"].map((view) => pve(target, "read", "GET", `cluster/sdn/fabrics/all?${view}=1`).pipe(Effect63.map(canonical3), Effect63.catchIf(subsystemAbsent, () => Effect63.succeed(undefined)))), { concurrency: "unbounded" }).pipe(Effect63.flatMap(([staged, running]) => {
|
|
4986
5110
|
const verdict = fabricStaged(staged, running);
|
|
4987
|
-
return verdict === "inconsistent" ?
|
|
5111
|
+
return verdict === "inconsistent" ? Effect63.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.")) : Effect63.succeed(verdict);
|
|
4988
5112
|
}));
|
|
4989
5113
|
var canonical3 = (value) => JSON.stringify(sortDeep(value));
|
|
4990
5114
|
var sortDeep = (value) => {
|
|
@@ -4997,23 +5121,23 @@ var sortDeep = (value) => {
|
|
|
4997
5121
|
}
|
|
4998
5122
|
return value;
|
|
4999
5123
|
};
|
|
5000
|
-
var sdnPendingCount = (target) =>
|
|
5124
|
+
var sdnPendingCount = (target) => Effect63.all([markedPending(target), fabricPending(target)], { concurrency: "unbounded" }).pipe(Effect63.map(([marked, fabric]) => marked + fabric));
|
|
5001
5125
|
|
|
5002
5126
|
// src/proxmox/sdn-apply.ts
|
|
5003
5127
|
var ProxmoxSdnApply = Resource30("Proxmox.SdnApply");
|
|
5004
|
-
var read3 = (props) => sdnPendingCount(props.target).pipe(
|
|
5005
|
-
var ProxmoxSdnApplyProvider = () => Provider30.effect(ProxmoxSdnApply,
|
|
5006
|
-
list: () =>
|
|
5007
|
-
read:
|
|
5128
|
+
var read3 = (props) => sdnPendingCount(props.target).pipe(Effect64.map((pending) => ({ pending })));
|
|
5129
|
+
var ProxmoxSdnApplyProvider = () => Provider30.effect(ProxmoxSdnApply, Effect64.succeed(ProxmoxSdnApply.Provider.of({
|
|
5130
|
+
list: () => Effect64.succeed([]),
|
|
5131
|
+
read: Effect64.fn(function* ({ olds }) {
|
|
5008
5132
|
return yield* read3(olds);
|
|
5009
5133
|
}),
|
|
5010
|
-
diff:
|
|
5011
|
-
if (!
|
|
5134
|
+
diff: Effect64.fn(function* ({ news }) {
|
|
5135
|
+
if (!isResolved17(news))
|
|
5012
5136
|
return;
|
|
5013
5137
|
const { pending } = yield* read3(news);
|
|
5014
5138
|
return pending === 0 ? { action: "noop" } : { action: "update" };
|
|
5015
5139
|
}),
|
|
5016
|
-
reconcile:
|
|
5140
|
+
reconcile: Effect64.fn(function* ({ news }) {
|
|
5017
5141
|
const staged = yield* read3(news);
|
|
5018
5142
|
if (staged.pending === 0)
|
|
5019
5143
|
return staged;
|
|
@@ -5021,16 +5145,16 @@ var ProxmoxSdnApplyProvider = () => Provider30.effect(ProxmoxSdnApply, Effect61.
|
|
|
5021
5145
|
yield* pve(news.target, "provision", "PUT", "cluster/sdn");
|
|
5022
5146
|
const after = yield* read3(news);
|
|
5023
5147
|
if (after.pending !== 0) {
|
|
5024
|
-
return yield*
|
|
5148
|
+
return yield* Effect64.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."));
|
|
5025
5149
|
}
|
|
5026
5150
|
return after;
|
|
5027
5151
|
}),
|
|
5028
|
-
delete: () =>
|
|
5152
|
+
delete: () => Effect64.void
|
|
5029
5153
|
})));
|
|
5030
5154
|
// src/proxmox/sdn-subnet.ts
|
|
5031
5155
|
import { Resource as Resource31 } from "alchemy";
|
|
5032
5156
|
import * as Provider31 from "alchemy/Provider";
|
|
5033
|
-
import * as
|
|
5157
|
+
import * as Effect65 from "effect/Effect";
|
|
5034
5158
|
|
|
5035
5159
|
// src/proxmox/sdn-subnet-form.ts
|
|
5036
5160
|
var subnetId = (props) => `${props.zone}-${props.cidr.replace("/", "-")}`;
|
|
@@ -5061,7 +5185,7 @@ var updateForm6 = (props) => {
|
|
|
5061
5185
|
|
|
5062
5186
|
// src/proxmox/sdn-subnet.ts
|
|
5063
5187
|
var ProxmoxSdnSubnet = Resource31("Proxmox.SdnSubnet");
|
|
5064
|
-
var
|
|
5188
|
+
var handlers20 = pveHandlers({
|
|
5065
5189
|
attributes: (live, props) => {
|
|
5066
5190
|
const liveVnet = text(live["vnet"]);
|
|
5067
5191
|
if (liveVnet !== "" && liveVnet !== props.vnet)
|
|
@@ -5090,11 +5214,11 @@ var handlers21 = pveHandlers({
|
|
|
5090
5214
|
path: (props) => `cluster/sdn/vnets/${props.vnet}/subnets/${subnetId(props)}`,
|
|
5091
5215
|
updateForm: updateForm6
|
|
5092
5216
|
});
|
|
5093
|
-
var ProxmoxSdnSubnetProvider = () => Provider31.effect(ProxmoxSdnSubnet,
|
|
5217
|
+
var ProxmoxSdnSubnetProvider = () => Provider31.effect(ProxmoxSdnSubnet, Effect65.succeed(ProxmoxSdnSubnet.Provider.of(handlers20)));
|
|
5094
5218
|
// src/proxmox/sdn-vnet.ts
|
|
5095
5219
|
import { Resource as Resource32 } from "alchemy";
|
|
5096
5220
|
import * as Provider32 from "alchemy/Provider";
|
|
5097
|
-
import * as
|
|
5221
|
+
import * as Effect66 from "effect/Effect";
|
|
5098
5222
|
var ProxmoxSdnVnet = Resource32("Proxmox.SdnVnet");
|
|
5099
5223
|
var tagOf = (value) => typeof value === "number" ? value : undefined;
|
|
5100
5224
|
var shape11 = (props) => {
|
|
@@ -5114,7 +5238,7 @@ var updateForm7 = (props) => {
|
|
|
5114
5238
|
];
|
|
5115
5239
|
return withClears(shape11(props), unset2);
|
|
5116
5240
|
};
|
|
5117
|
-
var
|
|
5241
|
+
var handlers21 = pveHandlers({
|
|
5118
5242
|
readRole: "provision",
|
|
5119
5243
|
attributes: (live, props) => ({
|
|
5120
5244
|
alias: typeof live["alias"] === "string" ? live["alias"] : "",
|
|
@@ -5132,11 +5256,11 @@ var handlers22 = pveHandlers({
|
|
|
5132
5256
|
path: (props) => `cluster/sdn/vnets/${props.vnet}`,
|
|
5133
5257
|
updateForm: updateForm7
|
|
5134
5258
|
});
|
|
5135
|
-
var ProxmoxSdnVnetProvider = () => Provider32.effect(ProxmoxSdnVnet,
|
|
5259
|
+
var ProxmoxSdnVnetProvider = () => Provider32.effect(ProxmoxSdnVnet, Effect66.succeed(ProxmoxSdnVnet.Provider.of(handlers21)));
|
|
5136
5260
|
// src/proxmox/sdn-zone.ts
|
|
5137
5261
|
import { Resource as Resource33 } from "alchemy";
|
|
5138
5262
|
import * as Provider33 from "alchemy/Provider";
|
|
5139
|
-
import * as
|
|
5263
|
+
import * as Effect67 from "effect/Effect";
|
|
5140
5264
|
var ProxmoxSdnZone = Resource33("Proxmox.SdnZone");
|
|
5141
5265
|
var sameIpam = (live, declared2) => (live === "" ? "pve" : live) === (declared2 === undefined || declared2 === "" ? "pve" : declared2);
|
|
5142
5266
|
var body6 = (props) => ({
|
|
@@ -5157,7 +5281,7 @@ var clearable = {
|
|
|
5157
5281
|
vlan: ["dns", "dnszone", "ipam", "mtu", "nodes"],
|
|
5158
5282
|
vxlan: ["dns", "dnszone", "ipam", "mtu", "nodes"]
|
|
5159
5283
|
};
|
|
5160
|
-
var
|
|
5284
|
+
var handlers22 = pveHandlers({
|
|
5161
5285
|
readRole: "provision",
|
|
5162
5286
|
attributes: (live, props) => {
|
|
5163
5287
|
const liveType = text(live["type"]);
|
|
@@ -5189,13 +5313,13 @@ var handlers23 = pveHandlers({
|
|
|
5189
5313
|
return withClears(fields, clear);
|
|
5190
5314
|
}
|
|
5191
5315
|
});
|
|
5192
|
-
var ProxmoxSdnZoneProvider = () => Provider33.effect(ProxmoxSdnZone,
|
|
5316
|
+
var ProxmoxSdnZoneProvider = () => Provider33.effect(ProxmoxSdnZone, Effect67.succeed(ProxmoxSdnZone.Provider.of(handlers22)));
|
|
5193
5317
|
// src/proxmox/storage.ts
|
|
5194
5318
|
import { Resource as Resource34 } from "alchemy";
|
|
5195
|
-
import { isResolved as
|
|
5319
|
+
import { isResolved as isResolved18 } from "alchemy/Diff";
|
|
5196
5320
|
import * as Provider34 from "alchemy/Provider";
|
|
5197
5321
|
import * as storage2 from "@distilled.cloud/proxmox/storage";
|
|
5198
|
-
import * as
|
|
5322
|
+
import * as Effect69 from "effect/Effect";
|
|
5199
5323
|
|
|
5200
5324
|
// src/proxmox/storage-form.ts
|
|
5201
5325
|
var STORAGE_CREATE = "pve:POST /storage";
|
|
@@ -5222,17 +5346,17 @@ var updateForm8 = (props) => ({
|
|
|
5222
5346
|
...mutable2(props),
|
|
5223
5347
|
storage: props.storage
|
|
5224
5348
|
});
|
|
5225
|
-
var
|
|
5226
|
-
var
|
|
5349
|
+
var toDistilledCreate4 = (props) => underscored(createForm11(props));
|
|
5350
|
+
var toDistilledUpdate3 = (props) => underscored(updateForm8(props));
|
|
5227
5351
|
|
|
5228
5352
|
// src/proxmox/storage-wire.ts
|
|
5229
5353
|
import * as storage from "@distilled.cloud/proxmox/storage";
|
|
5230
|
-
import * as
|
|
5354
|
+
import * as Effect68 from "effect/Effect";
|
|
5231
5355
|
var str2 = (value, fallback = "") => typeof value === "string" ? value : fallback;
|
|
5232
5356
|
var tokens = (value) => value.split(",").map((part) => part.trim()).filter((part) => part !== "").sort().join(",");
|
|
5233
5357
|
var sameList2 = (declared2, live) => declared2 === undefined || tokens(declared2) === tokens(live);
|
|
5234
5358
|
var same7 = (declared2, live) => declared2 === undefined || declared2 === live;
|
|
5235
|
-
var
|
|
5359
|
+
var attributesOf9 = (live, props) => {
|
|
5236
5360
|
const data = live;
|
|
5237
5361
|
return {
|
|
5238
5362
|
comment: str2(data["comment"]),
|
|
@@ -5246,24 +5370,22 @@ var attributesOf8 = (live, props) => {
|
|
|
5246
5370
|
type: str2(data["type"], props.type)
|
|
5247
5371
|
};
|
|
5248
5372
|
};
|
|
5249
|
-
var readStorageOrFail = (props) => readOrUnreadable(runPve(props.target, "provision", false, storage.getStorage({ storage: props.storage }))).pipe(
|
|
5250
|
-
var readStorage = (props) => readStorageOrFail(props).pipe(
|
|
5251
|
-
return;
|
|
5252
|
-
}));
|
|
5373
|
+
var readStorageOrFail = (props) => readOrUnreadable(runPve(props.target, "provision", false, storage.getStorage({ storage: props.storage }))).pipe(Effect68.map((live) => live === UNREADABLE ? UNREADABLE : attributesOf9(live, props)));
|
|
5374
|
+
var readStorage = (props) => readStorageOrFail(props).pipe(Effect68.catchTag("StorageNotFound", () => Effect68.succeed(undefined)));
|
|
5253
5375
|
var dropUnreadable7 = (live) => live === UNREADABLE ? undefined : live;
|
|
5254
|
-
var
|
|
5376
|
+
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);
|
|
5255
5377
|
|
|
5256
5378
|
// src/proxmox/storage.ts
|
|
5257
5379
|
var ProxmoxStorage = Resource34("Proxmox.Storage", {
|
|
5258
5380
|
defaultRemovalPolicy: "retain"
|
|
5259
5381
|
});
|
|
5260
|
-
var ProxmoxStorageProvider = () => Provider34.effect(ProxmoxStorage,
|
|
5261
|
-
list: () =>
|
|
5262
|
-
read:
|
|
5382
|
+
var ProxmoxStorageProvider = () => Provider34.effect(ProxmoxStorage, Effect69.succeed(ProxmoxStorage.Provider.of({
|
|
5383
|
+
list: () => Effect69.succeed([]),
|
|
5384
|
+
read: Effect69.fn(function* ({ olds, output }) {
|
|
5263
5385
|
return dropUnreadable7(yield* output === undefined ? readStorage(olds) : readStorageOrFail(olds));
|
|
5264
5386
|
}),
|
|
5265
|
-
diff:
|
|
5266
|
-
if (!
|
|
5387
|
+
diff: Effect69.fn(function* ({ news, output }) {
|
|
5388
|
+
if (!isResolved18(news))
|
|
5267
5389
|
return;
|
|
5268
5390
|
yield* guardWrite2(STORAGE_CREATE, createForm11(news), output === undefined);
|
|
5269
5391
|
yield* guardWrite2(STORAGE_UPDATE, updateForm8(news), false);
|
|
@@ -5278,61 +5400,61 @@ var ProxmoxStorageProvider = () => Provider34.effect(ProxmoxStorage, Effect66.su
|
|
|
5278
5400
|
yield* guardWrite2(STORAGE_CREATE, createForm11(news), true);
|
|
5279
5401
|
return { action: "update" };
|
|
5280
5402
|
}
|
|
5281
|
-
return
|
|
5403
|
+
return matches9(live, news) ? { action: "noop" } : { action: "update" };
|
|
5282
5404
|
}),
|
|
5283
|
-
reconcile:
|
|
5405
|
+
reconcile: Effect69.fn(function* ({ news }) {
|
|
5284
5406
|
const before = dropUnreadable7(yield* readStorage(news));
|
|
5285
5407
|
yield* guardWrite2(STORAGE_CREATE, createForm11(news), before === undefined);
|
|
5286
5408
|
yield* guardWrite2(STORAGE_UPDATE, updateForm8(news), false);
|
|
5287
5409
|
if (before === undefined) {
|
|
5288
|
-
yield* runPve(news.target, "provision", true, storage2.createStorage(
|
|
5289
|
-
} else if (!
|
|
5290
|
-
yield* runPve(news.target, "provision", true, storage2.putStorage(
|
|
5410
|
+
yield* runPve(news.target, "provision", true, storage2.createStorage(toDistilledCreate4(news)));
|
|
5411
|
+
} else if (!matches9(before, news)) {
|
|
5412
|
+
yield* runPve(news.target, "provision", true, storage2.putStorage(toDistilledUpdate3(news)));
|
|
5291
5413
|
}
|
|
5292
5414
|
const after = dropUnreadable7(yield* readStorage(news));
|
|
5293
5415
|
if (after === undefined) {
|
|
5294
|
-
return yield*
|
|
5416
|
+
return yield* Effect69.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."));
|
|
5295
5417
|
}
|
|
5296
5418
|
return after;
|
|
5297
5419
|
}),
|
|
5298
|
-
delete:
|
|
5420
|
+
delete: Effect69.fn(function* ({ olds }) {
|
|
5299
5421
|
yield* runPve(olds.target, "provision", true, storage2.deleteStorage({ storage: olds.storage }));
|
|
5300
5422
|
})
|
|
5301
5423
|
})));
|
|
5302
5424
|
// src/proxmox/zfs-pool.ts
|
|
5303
5425
|
import { Resource as Resource35 } from "alchemy";
|
|
5304
|
-
import { isResolved as
|
|
5426
|
+
import { isResolved as isResolved19 } from "alchemy/Diff";
|
|
5305
5427
|
import * as Provider35 from "alchemy/Provider";
|
|
5306
|
-
import * as
|
|
5428
|
+
import * as Effect72 from "effect/Effect";
|
|
5307
5429
|
|
|
5308
5430
|
// src/proxmox/zfs-pool-wire.ts
|
|
5309
|
-
import * as
|
|
5310
|
-
import * as
|
|
5431
|
+
import * as nodes10 from "@distilled.cloud/proxmox/nodes";
|
|
5432
|
+
import * as Effect70 from "effect/Effect";
|
|
5311
5433
|
var leafDevices = (children) => Array.isArray(children) ? children.flatMap((entry) => {
|
|
5312
5434
|
const vdev = entry;
|
|
5313
5435
|
const nested = leafDevices(vdev.children);
|
|
5314
5436
|
return nested.length > 0 ? nested : [text(vdev.name)].filter((name) => name !== "");
|
|
5315
5437
|
}) : [];
|
|
5316
|
-
var
|
|
5438
|
+
var attributesOf10 = (live, props) => ({
|
|
5317
5439
|
devices: leafDevices(live.children).join(","),
|
|
5318
5440
|
errors: text(live.errors, "unknown"),
|
|
5319
5441
|
name: props.name,
|
|
5320
5442
|
node: props.node,
|
|
5321
5443
|
state: text(live.state, "unknown")
|
|
5322
5444
|
});
|
|
5323
|
-
var readPoolOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false,
|
|
5445
|
+
var readPoolOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, nodes10.getNodeDiskZfs({ name: props.name, node: props.node }))).pipe(Effect70.map((live) => {
|
|
5324
5446
|
if (live === UNREADABLE)
|
|
5325
5447
|
return UNREADABLE;
|
|
5326
|
-
return text(live.name) === "" ? undefined :
|
|
5448
|
+
return text(live.name) === "" ? undefined : attributesOf10(live, props);
|
|
5327
5449
|
}));
|
|
5328
|
-
var readPool = (props) => readPoolOrFail(props).pipe(
|
|
5450
|
+
var readPool = (props) => readPoolOrFail(props).pipe(Effect70.orElseSucceed(() => {
|
|
5329
5451
|
return;
|
|
5330
5452
|
}));
|
|
5331
5453
|
var dropUnreadable8 = (live) => live === UNREADABLE ? undefined : live;
|
|
5332
5454
|
|
|
5333
5455
|
// src/proxmox/zfs-pool-form.ts
|
|
5334
|
-
import * as
|
|
5335
|
-
import * as
|
|
5456
|
+
import * as Effect71 from "effect/Effect";
|
|
5457
|
+
import * as nodes11 from "@distilled.cloud/proxmox/nodes";
|
|
5336
5458
|
var ZFS_POOL_CREATE = "pve:POST /nodes/{node}/disks/zfs";
|
|
5337
5459
|
var zfsPoolCreateEndpoint = (props) => props.devices === undefined || props.raidlevel === undefined ? undefined : ZFS_POOL_CREATE;
|
|
5338
5460
|
var createForm12 = (props) => ({
|
|
@@ -5343,7 +5465,7 @@ var createForm12 = (props) => ({
|
|
|
5343
5465
|
...props.compression === undefined ? {} : { compression: props.compression },
|
|
5344
5466
|
...props["draid-config"] === undefined ? {} : { "draid-config": props["draid-config"] }
|
|
5345
5467
|
});
|
|
5346
|
-
var
|
|
5468
|
+
var toDistilledCreate5 = (props) => {
|
|
5347
5469
|
const form = createForm12(props);
|
|
5348
5470
|
return {
|
|
5349
5471
|
...form,
|
|
@@ -5352,41 +5474,41 @@ var toDistilledCreate3 = (props) => {
|
|
|
5352
5474
|
};
|
|
5353
5475
|
};
|
|
5354
5476
|
var SETTLE_ATTEMPTS = 30;
|
|
5355
|
-
var createPool = (props, read4) =>
|
|
5477
|
+
var createPool = (props, read4) => Effect71.gen(function* () {
|
|
5356
5478
|
const existing = yield* read4(props);
|
|
5357
5479
|
if (existing !== undefined)
|
|
5358
5480
|
return existing;
|
|
5359
5481
|
if (props.devices === undefined || props.raidlevel === undefined) {
|
|
5360
|
-
return yield*
|
|
5482
|
+
return yield* Effect71.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."));
|
|
5361
5483
|
}
|
|
5362
5484
|
yield* guardForm(zfsPoolCreateEndpoint(props), asForm2(createForm12(props)), true);
|
|
5363
|
-
const upid = yield* runPve(props.target, "provision", true,
|
|
5364
|
-
const settle4 = (attempts) =>
|
|
5485
|
+
const upid = yield* runPve(props.target, "provision", true, nodes11.createNodeDiskZfs(toDistilledCreate5(props)));
|
|
5486
|
+
const settle4 = (attempts) => Effect71.gen(function* () {
|
|
5365
5487
|
const live = yield* read4(props);
|
|
5366
5488
|
if (live !== undefined || attempts <= 0)
|
|
5367
5489
|
return live;
|
|
5368
|
-
yield*
|
|
5490
|
+
yield* Effect71.sleep("2 seconds");
|
|
5369
5491
|
return yield* settle4(attempts - 1);
|
|
5370
5492
|
});
|
|
5371
5493
|
const after = yield* settle4(SETTLE_ATTEMPTS);
|
|
5372
5494
|
if (after === undefined) {
|
|
5373
|
-
return yield*
|
|
5495
|
+
return yield* Effect71.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."));
|
|
5374
5496
|
}
|
|
5375
5497
|
return after;
|
|
5376
5498
|
});
|
|
5377
|
-
var destroyPool = (props) => runPve(props.target, "provision", true,
|
|
5499
|
+
var destroyPool = (props) => runPve(props.target, "provision", true, nodes11.deleteNodeDiskZfs({ name: props.name, node: props.node }));
|
|
5378
5500
|
|
|
5379
5501
|
// src/proxmox/zfs-pool.ts
|
|
5380
5502
|
var ProxmoxZfsPool = Resource35("Proxmox.ZfsPool", {
|
|
5381
5503
|
defaultRemovalPolicy: "retain"
|
|
5382
5504
|
});
|
|
5383
|
-
var ProxmoxZfsPoolProvider = () => Provider35.effect(ProxmoxZfsPool,
|
|
5384
|
-
list: () =>
|
|
5385
|
-
read:
|
|
5505
|
+
var ProxmoxZfsPoolProvider = () => Provider35.effect(ProxmoxZfsPool, Effect72.succeed(ProxmoxZfsPool.Provider.of({
|
|
5506
|
+
list: () => Effect72.succeed([]),
|
|
5507
|
+
read: Effect72.fn(function* ({ olds, output }) {
|
|
5386
5508
|
return dropUnreadable8(yield* output === undefined ? readPool(olds) : readPoolOrFail(olds));
|
|
5387
5509
|
}),
|
|
5388
|
-
diff:
|
|
5389
|
-
if (!
|
|
5510
|
+
diff: Effect72.fn(function* ({ news, output }) {
|
|
5511
|
+
if (!isResolved19(news))
|
|
5390
5512
|
return;
|
|
5391
5513
|
yield* guardForm(zfsPoolCreateEndpoint(news), asForm2(createForm12(news)), output === undefined);
|
|
5392
5514
|
if (output === undefined)
|
|
@@ -5402,10 +5524,10 @@ var ProxmoxZfsPoolProvider = () => Provider35.effect(ProxmoxZfsPool, Effect69.su
|
|
|
5402
5524
|
}
|
|
5403
5525
|
return { action: "noop" };
|
|
5404
5526
|
}),
|
|
5405
|
-
reconcile:
|
|
5406
|
-
return yield* createPool(news, (props) => readPool(props).pipe(
|
|
5527
|
+
reconcile: Effect72.fn(function* ({ news }) {
|
|
5528
|
+
return yield* createPool(news, (props) => readPool(props).pipe(Effect72.map(dropUnreadable8)));
|
|
5407
5529
|
}),
|
|
5408
|
-
delete:
|
|
5530
|
+
delete: Effect72.fn(function* ({ olds }) {
|
|
5409
5531
|
yield* destroyPool(olds);
|
|
5410
5532
|
})
|
|
5411
5533
|
})));
|
|
@@ -5485,5 +5607,5 @@ export {
|
|
|
5485
5607
|
violations
|
|
5486
5608
|
};
|
|
5487
5609
|
|
|
5488
|
-
//# debugId=
|
|
5610
|
+
//# debugId=34DF7A874403817964756E2164756E21
|
|
5489
5611
|
//# sourceMappingURL=index.js.map
|