@homeflare/alchemy 0.36.0 → 0.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +33 -0
- package/dist/proxmox/ceph-daemon-wire.d.ts.map +1 -0
- package/dist/proxmox/ceph-daemon.d.ts +35 -0
- package/dist/proxmox/ceph-daemon.d.ts.map +1 -1
- package/dist/proxmox/ceph-fs-distilled.d.ts +14 -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 +31 -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 +10 -4
- package/dist/proxmox/ceph-pool-settle.d.ts.map +1 -1
- package/dist/proxmox/ceph-pool-wire.d.ts +46 -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 +1 -1
- package/dist/proxmox/group-wire.d.ts.map +1 -1
- package/dist/proxmox/index.js +611 -477
- package/dist/proxmox/index.js.map +15 -12
- package/dist/proxmox/storage-wire.d.ts +8 -1
- package/dist/proxmox/storage-wire.d.ts.map +1 -1
- package/dist/proxmox/user-wire.d.ts +1 -1
- package/dist/proxmox/user-wire.d.ts.map +1 -1
- package/docs/argocd-kubernetes.md +100 -0
- package/docs/argocd.md +139 -0
- package/docs/credentials.md +9 -0
- package/docs/litellm.md +17 -0
- package/docs/peers.md +7 -0
- package/docs/unifi.md +45 -0
- 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 +115 -0
- package/src/proxmox/ceph-daemon-write.test.ts +158 -0
- package/src/proxmox/ceph-daemon.ts +65 -47
- package/src/proxmox/ceph-fs-delete.test.ts +81 -0
- package/src/proxmox/ceph-fs-distilled.ts +102 -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 +66 -69
- 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 +13 -7
- package/src/proxmox/ceph-pool-wire.ts +110 -0
- package/src/proxmox/ceph-pool-write.test.ts +145 -0
- package/src/proxmox/ceph-pool.ts +87 -98
- 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,97 @@ 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)), Effect13.orElseSucceed(() => {
|
|
1694
|
+
return;
|
|
1695
|
+
}));
|
|
1696
|
+
var createDaemon = (props) => {
|
|
1697
|
+
const node = props.node;
|
|
1698
|
+
const id = daemonId(props);
|
|
1699
|
+
switch (props.kind) {
|
|
1700
|
+
case "mon":
|
|
1701
|
+
return runPve(props.target, "provision", true, nodes.updateNodeCephMon({
|
|
1702
|
+
...props["mon-address"] === undefined ? {} : { mon_address: props["mon-address"] },
|
|
1703
|
+
monid: id,
|
|
1704
|
+
node
|
|
1705
|
+
}));
|
|
1706
|
+
case "mgr":
|
|
1707
|
+
return runPve(props.target, "provision", true, nodes.updateNodeCephMgr({ id, node }));
|
|
1708
|
+
case "mds":
|
|
1709
|
+
return runPve(props.target, "provision", true, nodes.updateNodeCephMds({
|
|
1710
|
+
...props.hotstandby === undefined ? {} : { hotstandby: props.hotstandby ? "1" : "0" },
|
|
1711
|
+
name: id,
|
|
1712
|
+
node
|
|
1713
|
+
}));
|
|
1714
|
+
}
|
|
1715
|
+
};
|
|
1716
|
+
var deleteDaemon = (props) => {
|
|
1717
|
+
const node = props.node;
|
|
1718
|
+
const id = daemonId(props);
|
|
1719
|
+
switch (props.kind) {
|
|
1720
|
+
case "mon":
|
|
1721
|
+
return runPve(props.target, "provision", true, nodes.deleteNodeCephMon({ monid: id, node }));
|
|
1722
|
+
case "mgr":
|
|
1723
|
+
return runPve(props.target, "provision", true, nodes.deleteNodeCephMgr({ id, node }));
|
|
1724
|
+
case "mds":
|
|
1725
|
+
return runPve(props.target, "provision", true, nodes.deleteNodeCephMds({ name: id, node }));
|
|
1726
|
+
}
|
|
1727
|
+
};
|
|
1728
|
+
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).");
|
|
1729
|
+
|
|
1678
1730
|
// src/proxmox/ceph-daemon.ts
|
|
1679
1731
|
var ProxmoxCephDaemon = Resource4("Proxmox.CephDaemon", {
|
|
1680
1732
|
defaultRemovalPolicy: "retain"
|
|
1681
1733
|
});
|
|
1682
|
-
var
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1734
|
+
var reconcileDaemon = Effect14.fn(function* ({ news }) {
|
|
1735
|
+
const live = yield* readDaemon(news);
|
|
1736
|
+
yield* guardWrite2(DAEMON_ENDPOINTS[news.kind], createForm(news), live === undefined);
|
|
1737
|
+
if (live !== undefined)
|
|
1738
|
+
return live;
|
|
1739
|
+
yield* createDaemon(news);
|
|
1740
|
+
const after = yield* readDaemon(news);
|
|
1741
|
+
if (after === undefined)
|
|
1742
|
+
return yield* Effect14.die(notCreated(news));
|
|
1743
|
+
return after;
|
|
1744
|
+
});
|
|
1745
|
+
var ProxmoxCephDaemonProvider = () => Provider4.effect(ProxmoxCephDaemon, Effect14.succeed(ProxmoxCephDaemon.Provider.of({
|
|
1746
|
+
list: () => Effect14.succeed([]),
|
|
1747
|
+
read: ({ olds }) => readDaemon(olds),
|
|
1748
|
+
diff: Effect14.fn(function* ({ news, output }) {
|
|
1749
|
+
if (!isResolved4(news))
|
|
1750
|
+
return;
|
|
1751
|
+
yield* guardWrite2(DAEMON_ENDPOINTS[news.kind], createForm(news), output === undefined);
|
|
1752
|
+
if (output === undefined)
|
|
1753
|
+
return;
|
|
1754
|
+
const live = yield* readDaemon(news);
|
|
1755
|
+
if (live === undefined) {
|
|
1756
|
+
yield* guardWrite2(DAEMON_ENDPOINTS[news.kind], createForm(news), true);
|
|
1757
|
+
return { action: "update" };
|
|
1758
|
+
}
|
|
1759
|
+
return { action: "noop" };
|
|
1760
|
+
}),
|
|
1761
|
+
reconcile: ({ news }) => reconcileDaemon({ news }),
|
|
1762
|
+
delete: ({ olds }) => deleteDaemon(olds)
|
|
1693
1763
|
})));
|
|
1694
1764
|
// src/proxmox/ceph-flag.ts
|
|
1695
1765
|
import { Resource as Resource5 } from "alchemy";
|
|
1696
1766
|
import * as Provider5 from "alchemy/Provider";
|
|
1697
|
-
import * as
|
|
1767
|
+
import * as Effect15 from "effect/Effect";
|
|
1698
1768
|
var ProxmoxCephFlag = Resource5("Proxmox.CephFlag");
|
|
1699
|
-
var
|
|
1769
|
+
var handlers2 = pveHandlers({
|
|
1700
1770
|
attributes: (live, props) => ({ flag: props.flag, value: bool(live) }),
|
|
1701
1771
|
collection: () => "cluster/ceph/flags",
|
|
1702
1772
|
createForm: (props) => ({ [props.flag]: flag(props.value) ?? "0" }),
|
|
@@ -1705,25 +1775,29 @@ var handlers3 = pveHandlers({
|
|
|
1705
1775
|
path: (props) => `cluster/ceph/flags/${props.flag}`,
|
|
1706
1776
|
updateForm: (props) => ({ value: flag(props.value) ?? "0" })
|
|
1707
1777
|
});
|
|
1708
|
-
var ProxmoxCephFlagProvider = () => Provider5.effect(ProxmoxCephFlag,
|
|
1709
|
-
...
|
|
1710
|
-
delete: () =>
|
|
1711
|
-
reconcile:
|
|
1712
|
-
const after = yield*
|
|
1778
|
+
var ProxmoxCephFlagProvider = () => Provider5.effect(ProxmoxCephFlag, Effect15.succeed(ProxmoxCephFlag.Provider.of({
|
|
1779
|
+
...handlers2,
|
|
1780
|
+
delete: () => Effect15.void,
|
|
1781
|
+
reconcile: Effect15.fn(function* ({ news }) {
|
|
1782
|
+
const after = yield* handlers2.reconcile({ news });
|
|
1713
1783
|
if (after.value !== news.value) {
|
|
1714
|
-
return yield*
|
|
1784
|
+
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
1785
|
}
|
|
1716
1786
|
return after;
|
|
1717
1787
|
})
|
|
1718
1788
|
})));
|
|
1719
1789
|
// src/proxmox/ceph-fs.ts
|
|
1720
1790
|
import { Resource as Resource6 } from "alchemy";
|
|
1721
|
-
import { isResolved as
|
|
1791
|
+
import { isResolved as isResolved5 } from "alchemy/Diff";
|
|
1722
1792
|
import * as Provider6 from "alchemy/Provider";
|
|
1723
|
-
import * as
|
|
1793
|
+
import * as Effect18 from "effect/Effect";
|
|
1794
|
+
|
|
1795
|
+
// src/proxmox/ceph-fs-distilled.ts
|
|
1796
|
+
import * as nodes2 from "@distilled.cloud/proxmox/nodes";
|
|
1797
|
+
import * as Effect17 from "effect/Effect";
|
|
1724
1798
|
|
|
1725
1799
|
// src/proxmox/ceph-fs-wire.ts
|
|
1726
|
-
import * as
|
|
1800
|
+
import * as Effect16 from "effect/Effect";
|
|
1727
1801
|
var readRow = (live, props) => {
|
|
1728
1802
|
const row = (Array.isArray(live) ? live : []).filter((entry) => typeof entry === "object" && entry !== null).find((entry) => entry["name"] === props.name);
|
|
1729
1803
|
if (row === undefined)
|
|
@@ -1739,6 +1813,7 @@ var readRow = (live, props) => {
|
|
|
1739
1813
|
var POLL_SECONDS = 2;
|
|
1740
1814
|
var POLL_ATTEMPTS = 45;
|
|
1741
1815
|
var objectPath = (props) => `nodes/${props.node}/ceph/fs/${props.name}`;
|
|
1816
|
+
var CEPH_FS_CREATE = "pve:POST /nodes/{node}/ceph/fs/{name}";
|
|
1742
1817
|
var field = (name, value) => value === undefined ? {} : { [name]: value };
|
|
1743
1818
|
var createForm2 = (props) => ({
|
|
1744
1819
|
...field("add-storage", flag(props["add-storage"])),
|
|
@@ -1751,86 +1826,113 @@ var destroyPath = (props) => {
|
|
|
1751
1826
|
].filter((part) => part !== undefined);
|
|
1752
1827
|
return query.length === 0 ? objectPath(props) : `${objectPath(props)}?${query.join("&")}`;
|
|
1753
1828
|
};
|
|
1754
|
-
var settle = (target, node, upid, what) =>
|
|
1829
|
+
var settle = (target, node, upid, what) => Effect16.gen(function* () {
|
|
1755
1830
|
for (let attempt = 0;attempt < POLL_ATTEMPTS; attempt += 1) {
|
|
1756
|
-
const status = yield* pve(target, "read", "GET", `nodes/${node}/tasks/${encodeURIComponent(upid)}/status`).pipe(
|
|
1831
|
+
const status = yield* pve(target, "read", "GET", `nodes/${node}/tasks/${encodeURIComponent(upid)}/status`).pipe(Effect16.orElseSucceed(() => {
|
|
1757
1832
|
return;
|
|
1758
1833
|
}));
|
|
1759
1834
|
if (status?.status === "stopped") {
|
|
1760
1835
|
const exit = text(status.exitstatus);
|
|
1761
1836
|
if (exit === "OK")
|
|
1762
1837
|
return;
|
|
1763
|
-
return yield*
|
|
1838
|
+
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
1839
|
}
|
|
1765
|
-
yield*
|
|
1766
|
-
}
|
|
1767
|
-
return yield* Effect15.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
|
-
});
|
|
1769
|
-
var createFs = (props) => Effect15.gen(function* () {
|
|
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.'));
|
|
1840
|
+
yield* Effect16.sleep(POLL_SECONDS * 1000);
|
|
1773
1841
|
}
|
|
1774
|
-
yield*
|
|
1842
|
+
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."));
|
|
1775
1843
|
});
|
|
1776
|
-
var destroyFs = (props) =>
|
|
1844
|
+
var destroyFs = (props) => Effect16.gen(function* () {
|
|
1777
1845
|
const upid = text(yield* pve(props.target, "provision", "DELETE", destroyPath(props)));
|
|
1778
1846
|
if (upid !== "")
|
|
1779
1847
|
yield* settle(props.target, props.node, upid, destroyPath(props));
|
|
1780
1848
|
});
|
|
1781
|
-
var
|
|
1849
|
+
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
1850
|
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
1851
|
|
|
1852
|
+
// src/proxmox/ceph-fs-distilled.ts
|
|
1853
|
+
var readFs = (props) => runPve(props.target, "read", false, nodes2.listNodeCephFs({ node: props.node })).pipe(Effect17.map((rows) => readRow(rows, props)), Effect17.orElseSucceed(() => {
|
|
1854
|
+
return;
|
|
1855
|
+
}));
|
|
1856
|
+
var toDistilledCreate = (props) => ({
|
|
1857
|
+
...createForm2(props),
|
|
1858
|
+
name: props.name,
|
|
1859
|
+
node: props.node
|
|
1860
|
+
});
|
|
1861
|
+
var settleCreate = (props, upid) => Effect17.gen(function* () {
|
|
1862
|
+
for (let attempt = 0;attempt < POLL_ATTEMPTS; attempt += 1) {
|
|
1863
|
+
const status = yield* runPve(props.target, "read", false, nodes2.getNodeTaskStatus({ node: props.node, upid })).pipe(Effect17.orElseSucceed(() => {
|
|
1864
|
+
return;
|
|
1865
|
+
}));
|
|
1866
|
+
if (status?.status === "stopped") {
|
|
1867
|
+
if (status.exitstatus === "OK")
|
|
1868
|
+
return;
|
|
1869
|
+
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\`.`));
|
|
1870
|
+
}
|
|
1871
|
+
yield* Effect17.sleep(POLL_SECONDS * 1000);
|
|
1872
|
+
}
|
|
1873
|
+
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."));
|
|
1874
|
+
});
|
|
1875
|
+
var createFs = (props) => Effect17.gen(function* () {
|
|
1876
|
+
const upid = yield* runPve(props.target, "provision", true, nodes2.updateNodeCephFs(toDistilledCreate(props)));
|
|
1877
|
+
if (upid === "") {
|
|
1878
|
+
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.'));
|
|
1879
|
+
}
|
|
1880
|
+
yield* settleCreate(props, upid);
|
|
1881
|
+
});
|
|
1882
|
+
|
|
1784
1883
|
// src/proxmox/ceph-fs.ts
|
|
1785
1884
|
var ProxmoxCephFs = Resource6("Proxmox.CephFs", {
|
|
1786
1885
|
defaultRemovalPolicy: "retain"
|
|
1787
1886
|
});
|
|
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);
|
|
1887
|
+
var ProxmoxCephFsProvider = () => Provider6.effect(ProxmoxCephFs, Effect18.succeed(ProxmoxCephFs.Provider.of({
|
|
1888
|
+
list: () => Effect18.succeed([]),
|
|
1889
|
+
read: Effect18.fn(function* ({ olds }) {
|
|
1890
|
+
return yield* readFs(olds);
|
|
1800
1891
|
}),
|
|
1801
|
-
diff:
|
|
1802
|
-
if (
|
|
1892
|
+
diff: Effect18.fn(function* ({ news, output }) {
|
|
1893
|
+
if (!isResolved5(news))
|
|
1894
|
+
return;
|
|
1895
|
+
yield* guardWrite2(CEPH_FS_CREATE, createForm2(news), output === undefined);
|
|
1896
|
+
if (output !== undefined && news.name !== output.name) {
|
|
1803
1897
|
return { action: "replace" };
|
|
1804
1898
|
}
|
|
1805
|
-
|
|
1899
|
+
if (output === undefined)
|
|
1900
|
+
return;
|
|
1901
|
+
const live = yield* readFs(news);
|
|
1902
|
+
if (live === undefined) {
|
|
1903
|
+
yield* guardWrite2(CEPH_FS_CREATE, createForm2(news), true);
|
|
1904
|
+
return { action: "update" };
|
|
1905
|
+
}
|
|
1906
|
+
return { action: "noop" };
|
|
1806
1907
|
}),
|
|
1807
|
-
reconcile:
|
|
1808
|
-
const live = yield*
|
|
1908
|
+
reconcile: Effect18.fn(function* ({ news }) {
|
|
1909
|
+
const live = yield* readFs(news);
|
|
1809
1910
|
if (live !== undefined)
|
|
1810
1911
|
return live;
|
|
1811
1912
|
yield* createFs(news);
|
|
1812
|
-
const after = yield*
|
|
1913
|
+
const after = yield* readFs(news);
|
|
1813
1914
|
if (after === undefined)
|
|
1814
|
-
return yield*
|
|
1915
|
+
return yield* Effect18.die(notCreated2(news));
|
|
1815
1916
|
return after;
|
|
1816
1917
|
}),
|
|
1817
|
-
delete:
|
|
1818
|
-
if ((yield*
|
|
1918
|
+
delete: Effect18.fn(function* ({ olds }) {
|
|
1919
|
+
if ((yield* readFs(olds)) === undefined)
|
|
1819
1920
|
return;
|
|
1820
1921
|
yield* destroyFs(olds);
|
|
1821
|
-
const after = yield*
|
|
1922
|
+
const after = yield* readFs(olds);
|
|
1822
1923
|
if (after !== undefined)
|
|
1823
|
-
return yield*
|
|
1924
|
+
return yield* Effect18.die(notDestroyed(olds));
|
|
1824
1925
|
})
|
|
1825
1926
|
})));
|
|
1826
1927
|
// src/proxmox/ceph-osd.ts
|
|
1827
1928
|
import { Resource as Resource7 } from "alchemy";
|
|
1828
|
-
import { isResolved as
|
|
1929
|
+
import { isResolved as isResolved6 } from "alchemy/Diff";
|
|
1829
1930
|
import * as Provider7 from "alchemy/Provider";
|
|
1830
|
-
import * as
|
|
1931
|
+
import * as Effect21 from "effect/Effect";
|
|
1831
1932
|
|
|
1832
1933
|
// src/proxmox/ceph-osd-tree.ts
|
|
1833
|
-
import * as
|
|
1934
|
+
import * as nodes3 from "@distilled.cloud/proxmox/nodes";
|
|
1935
|
+
import * as Effect19 from "effect/Effect";
|
|
1834
1936
|
var branches = (node) => (Array.isArray(node["children"]) ? node["children"] : []).filter((child) => typeof child === "object" && child !== null);
|
|
1835
1937
|
var osdLeaves = (tree) => {
|
|
1836
1938
|
const root = tree["root"];
|
|
@@ -1856,33 +1958,42 @@ var osdAttributes = (leaf, props) => ({
|
|
|
1856
1958
|
status: text(leaf["status"]),
|
|
1857
1959
|
total_space: num(leaf["total_space"], 0)
|
|
1858
1960
|
});
|
|
1859
|
-
var readOsd = (props) =>
|
|
1860
|
-
const leaf =
|
|
1961
|
+
var readOsd = (props) => runPve(props.target, "read", false, nodes3.getNodeCephOsd({ node: props.node })).pipe(Effect19.map((t) => {
|
|
1962
|
+
const leaf = findOsd(t, props.osdid);
|
|
1861
1963
|
return leaf === undefined ? undefined : osdAttributes(leaf, props);
|
|
1862
1964
|
}));
|
|
1863
1965
|
|
|
1864
1966
|
// src/proxmox/ceph-osd-write.ts
|
|
1865
|
-
import * as
|
|
1967
|
+
import * as nodes4 from "@distilled.cloud/proxmox/nodes";
|
|
1968
|
+
import * as Effect20 from "effect/Effect";
|
|
1866
1969
|
var OSD_CREATE_ENDPOINT = "pve:POST /nodes/{node}/ceph/osd";
|
|
1867
1970
|
var describe = (props) => `osd.${String(props.osdid)} on ${props.node}`;
|
|
1868
1971
|
var createOsdForm = (props, dev) => ({
|
|
1869
1972
|
dev,
|
|
1870
1973
|
...props.device_class === undefined ? {} : { "crush-device-class": props.device_class }
|
|
1871
1974
|
});
|
|
1872
|
-
var createOsd =
|
|
1975
|
+
var createOsd = Effect20.fn(function* (news) {
|
|
1873
1976
|
if (news.dev === undefined) {
|
|
1874
|
-
return yield*
|
|
1977
|
+
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
1978
|
}
|
|
1876
1979
|
const form = createOsdForm(news, news.dev);
|
|
1877
1980
|
yield* guardForm(OSD_CREATE_ENDPOINT, form, true);
|
|
1878
|
-
yield*
|
|
1981
|
+
yield* runPve(news.target, "provision", true, nodes4.createNodeCephOsd({
|
|
1982
|
+
dev: news.dev,
|
|
1983
|
+
node: news.node,
|
|
1984
|
+
...news.device_class === undefined ? {} : { crush_device_class: news.device_class }
|
|
1985
|
+
}));
|
|
1879
1986
|
const created = yield* readOsd(news);
|
|
1880
1987
|
if (created === undefined) {
|
|
1881
|
-
return yield*
|
|
1988
|
+
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
1989
|
}
|
|
1883
1990
|
return created;
|
|
1884
1991
|
});
|
|
1885
|
-
var destroyOsd = (olds) =>
|
|
1992
|
+
var destroyOsd = (olds) => runPve(olds.target, "provision", true, nodes4.deleteNodeCephOsd({
|
|
1993
|
+
node: olds.node,
|
|
1994
|
+
osdid: String(olds.osdid),
|
|
1995
|
+
...olds.cleanup === true ? { cleanup: "1" } : {}
|
|
1996
|
+
}));
|
|
1886
1997
|
|
|
1887
1998
|
// src/proxmox/ceph-osd.ts
|
|
1888
1999
|
var ProxmoxCephOsd = Resource7("Proxmox.CephOsd", {
|
|
@@ -1890,16 +2001,16 @@ var ProxmoxCephOsd = Resource7("Proxmox.CephOsd", {
|
|
|
1890
2001
|
});
|
|
1891
2002
|
var settled = (live, props) => (props.host === undefined || props.host === live.host) && (props.device_class === undefined || props.device_class === live.device_class);
|
|
1892
2003
|
var describe2 = (props) => `osd.${String(props.osdid)} (read via nodes/${props.node}/ceph/osd)`;
|
|
1893
|
-
var
|
|
1894
|
-
list: () =>
|
|
1895
|
-
read:
|
|
2004
|
+
var handlers3 = {
|
|
2005
|
+
list: () => Effect21.succeed([]),
|
|
2006
|
+
read: Effect21.fn(function* ({ olds }) {
|
|
1896
2007
|
return yield* readOsd(olds);
|
|
1897
2008
|
}),
|
|
1898
|
-
diff:
|
|
2009
|
+
diff: Effect21.fn(function* ({
|
|
1899
2010
|
news,
|
|
1900
2011
|
output
|
|
1901
2012
|
}) {
|
|
1902
|
-
if (output === undefined || !
|
|
2013
|
+
if (output === undefined || !isResolved6(news))
|
|
1903
2014
|
return;
|
|
1904
2015
|
const live = yield* readOsd(news);
|
|
1905
2016
|
if (live === undefined)
|
|
@@ -1908,29 +2019,30 @@ var handlers4 = {
|
|
|
1908
2019
|
return { action: "update" };
|
|
1909
2020
|
return settled(live, news) ? { action: "noop" } : { action: "update" };
|
|
1910
2021
|
}),
|
|
1911
|
-
reconcile:
|
|
2022
|
+
reconcile: Effect21.fn(function* ({ news }) {
|
|
1912
2023
|
const live = yield* readOsd(news);
|
|
1913
2024
|
if (live === undefined) {
|
|
1914
2025
|
return yield* createOsd(news);
|
|
1915
2026
|
}
|
|
1916
2027
|
if (!settled(live, news)) {
|
|
1917
|
-
return yield*
|
|
2028
|
+
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
2029
|
}
|
|
1919
2030
|
return live;
|
|
1920
2031
|
}),
|
|
1921
|
-
delete:
|
|
2032
|
+
delete: Effect21.fn(function* ({ olds }) {
|
|
1922
2033
|
yield* destroyOsd(olds);
|
|
1923
2034
|
})
|
|
1924
2035
|
};
|
|
1925
|
-
var ProxmoxCephOsdProvider = () => Provider7.effect(ProxmoxCephOsd,
|
|
2036
|
+
var ProxmoxCephOsdProvider = () => Provider7.effect(ProxmoxCephOsd, Effect21.succeed(ProxmoxCephOsd.Provider.of(handlers3)));
|
|
1926
2037
|
// src/proxmox/ceph-pool.ts
|
|
1927
2038
|
import { Resource as Resource8 } from "alchemy";
|
|
2039
|
+
import { isResolved as isResolved7 } from "alchemy/Diff";
|
|
1928
2040
|
import * as Provider8 from "alchemy/Provider";
|
|
1929
|
-
import * as
|
|
2041
|
+
import * as nodes7 from "@distilled.cloud/proxmox/nodes";
|
|
2042
|
+
import * as Effect24 from "effect/Effect";
|
|
1930
2043
|
|
|
1931
2044
|
// src/proxmox/ceph-pool-form.ts
|
|
1932
2045
|
var object = (props) => `nodes/${props.node}/ceph/pool/${props.name}`;
|
|
1933
|
-
var collection = (props) => `nodes/${props.node}/ceph/pool`;
|
|
1934
2046
|
var UNSET = -1;
|
|
1935
2047
|
var applications = (value) => csv(Array.isArray(value) ? value.map((entry) => String(entry)) : []);
|
|
1936
2048
|
var same = (declared, live, frozen = false) => declared === undefined || frozen || declared === live;
|
|
@@ -1955,92 +2067,114 @@ var createBody = (props) => ({
|
|
|
1955
2067
|
});
|
|
1956
2068
|
|
|
1957
2069
|
// src/proxmox/ceph-pool-settle.ts
|
|
1958
|
-
import * as
|
|
1959
|
-
|
|
1960
|
-
var
|
|
2070
|
+
import * as nodes5 from "@distilled.cloud/proxmox/nodes";
|
|
2071
|
+
import * as Effect22 from "effect/Effect";
|
|
2072
|
+
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));
|
|
2073
|
+
var settle2 = (props, read, done) => Effect22.gen(function* () {
|
|
1961
2074
|
for (let attempt = 0;attempt < 30; attempt += 1) {
|
|
1962
2075
|
const live = yield* read(props);
|
|
1963
2076
|
if (done(live))
|
|
1964
2077
|
return live;
|
|
1965
|
-
yield*
|
|
2078
|
+
yield* Effect22.sleep("2 seconds");
|
|
1966
2079
|
}
|
|
1967
2080
|
return yield* read(props);
|
|
1968
2081
|
});
|
|
1969
2082
|
|
|
2083
|
+
// src/proxmox/ceph-pool-wire.ts
|
|
2084
|
+
import * as nodes6 from "@distilled.cloud/proxmox/nodes";
|
|
2085
|
+
import * as Effect23 from "effect/Effect";
|
|
2086
|
+
var attributesOf3 = (live, props) => {
|
|
2087
|
+
const row = live;
|
|
2088
|
+
if (typeof row["name"] !== "string")
|
|
2089
|
+
return;
|
|
2090
|
+
return {
|
|
2091
|
+
applications: applications(row["application_list"]),
|
|
2092
|
+
crush_rule: text(row["crush_rule"]),
|
|
2093
|
+
id: int(row["id"], UNSET),
|
|
2094
|
+
min_size: int(row["min_size"], UNSET),
|
|
2095
|
+
name: props.name,
|
|
2096
|
+
node: props.node,
|
|
2097
|
+
nodelete: bool(row["nodelete"]),
|
|
2098
|
+
nopgchange: bool(row["nopgchange"]),
|
|
2099
|
+
nosizechange: bool(row["nosizechange"]),
|
|
2100
|
+
pg_autoscale_mode: text(row["pg_autoscale_mode"]),
|
|
2101
|
+
pg_num: int(row["pg_num"], UNSET),
|
|
2102
|
+
pg_num_min: int(row["pg_num_min"], UNSET),
|
|
2103
|
+
size: int(row["size"], UNSET),
|
|
2104
|
+
target_size: int(row["target_size"], UNSET),
|
|
2105
|
+
target_size_ratio: num(row["target_size_ratio"], UNSET)
|
|
2106
|
+
};
|
|
2107
|
+
};
|
|
2108
|
+
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.orElseSucceed(() => {
|
|
2109
|
+
return;
|
|
2110
|
+
}));
|
|
2111
|
+
var CEPH_POOL_CREATE = "pve:POST /nodes/{node}/ceph/pool";
|
|
2112
|
+
var CEPH_POOL_UPDATE = "pve:PUT /nodes/{node}/ceph/pool/{name}";
|
|
2113
|
+
var toDistilledCreate2 = (props) => ({
|
|
2114
|
+
...createBody(props),
|
|
2115
|
+
node: props.node
|
|
2116
|
+
});
|
|
2117
|
+
var toDistilledUpdate = (props) => ({
|
|
2118
|
+
...updateBody(props),
|
|
2119
|
+
name: props.name,
|
|
2120
|
+
node: props.node
|
|
2121
|
+
});
|
|
2122
|
+
|
|
1970
2123
|
// src/proxmox/ceph-pool.ts
|
|
1971
2124
|
var ProxmoxCephPool = Resource8("Proxmox.CephPool", {
|
|
1972
2125
|
defaultRemovalPolicy: "retain"
|
|
1973
2126
|
});
|
|
1974
|
-
var
|
|
1975
|
-
|
|
1976
|
-
|
|
2127
|
+
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);
|
|
2128
|
+
var ProxmoxCephPoolProvider = () => Provider8.effect(ProxmoxCephPool, Effect24.succeed(ProxmoxCephPool.Provider.of({
|
|
2129
|
+
list: () => Effect24.succeed([]),
|
|
2130
|
+
read: ({ olds }) => readPoolStatus(olds),
|
|
2131
|
+
diff: Effect24.fn(function* ({ news, output }) {
|
|
2132
|
+
if (!isResolved7(news))
|
|
1977
2133
|
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);
|
|
2134
|
+
yield* guardWrite2(CEPH_POOL_CREATE, createBody(news), output === undefined);
|
|
2135
|
+
yield* guardWrite2(CEPH_POOL_UPDATE, updateBody(news), false);
|
|
2136
|
+
if (output === undefined)
|
|
2137
|
+
return;
|
|
2138
|
+
const live = yield* readPoolStatus(news);
|
|
2139
|
+
if (live === undefined) {
|
|
2140
|
+
yield* guardWrite2(CEPH_POOL_CREATE, createBody(news), true);
|
|
2141
|
+
return { action: "update" };
|
|
2142
|
+
}
|
|
2143
|
+
return matches3(live, news) ? { action: "noop" } : { action: "update" };
|
|
2144
|
+
}),
|
|
2145
|
+
reconcile: Effect24.fn(function* ({ news }) {
|
|
2146
|
+
const live = yield* readPoolStatus(news);
|
|
2013
2147
|
let upid;
|
|
2014
2148
|
if (live === undefined) {
|
|
2015
|
-
yield*
|
|
2016
|
-
yield* confirmAbsent(news,
|
|
2017
|
-
upid = yield*
|
|
2149
|
+
yield* guardWrite2(CEPH_POOL_CREATE, createBody(news), true);
|
|
2150
|
+
yield* confirmAbsent(news, object(news));
|
|
2151
|
+
upid = yield* runPve(news.target, "provision", true, nodes7.createNodeCephPool(toDistilledCreate2(news)));
|
|
2018
2152
|
} else {
|
|
2019
|
-
const form = formToSend(
|
|
2153
|
+
const form = formToSend(matches3, live, news, updateBody(news));
|
|
2020
2154
|
if (form !== undefined) {
|
|
2021
|
-
yield*
|
|
2022
|
-
upid = yield*
|
|
2155
|
+
yield* guardWrite2(CEPH_POOL_UPDATE, updateBody(news), false);
|
|
2156
|
+
upid = yield* runPve(news.target, "provision", true, nodes7.putNodeCephPool(toDistilledUpdate(news)));
|
|
2023
2157
|
}
|
|
2024
2158
|
}
|
|
2025
|
-
const after = yield* settle2(news,
|
|
2026
|
-
if (after === undefined || !
|
|
2027
|
-
return yield*
|
|
2159
|
+
const after = yield* settle2(news, readPoolStatus, (row) => row !== undefined && matches3(row, news));
|
|
2160
|
+
if (after === undefined || !matches3(after, news)) {
|
|
2161
|
+
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
2162
|
}
|
|
2029
2163
|
return after;
|
|
2030
2164
|
}),
|
|
2031
|
-
delete:
|
|
2032
|
-
yield*
|
|
2033
|
-
const left = yield* settle2(olds,
|
|
2165
|
+
delete: Effect24.fn(function* ({ olds }) {
|
|
2166
|
+
yield* runPve(olds.target, "provision", true, nodes7.deleteNodeCephPool({ name: olds.name, node: olds.node }));
|
|
2167
|
+
const left = yield* settle2(olds, readPoolStatus, (row) => row === undefined);
|
|
2034
2168
|
if (left !== undefined) {
|
|
2035
|
-
return yield*
|
|
2169
|
+
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
2170
|
}
|
|
2037
2171
|
})
|
|
2038
2172
|
})));
|
|
2039
2173
|
// src/proxmox/firewall-alias.ts
|
|
2040
2174
|
import { Resource as Resource9 } from "alchemy";
|
|
2041
|
-
import { isResolved as
|
|
2175
|
+
import { isResolved as isResolved8 } from "alchemy/Diff";
|
|
2042
2176
|
import * as Provider9 from "alchemy/Provider";
|
|
2043
|
-
import * as
|
|
2177
|
+
import * as Effect25 from "effect/Effect";
|
|
2044
2178
|
|
|
2045
2179
|
// src/proxmox/firewall-alias-form.ts
|
|
2046
2180
|
var fold = (name) => name.trim().toLowerCase();
|
|
@@ -2060,7 +2194,7 @@ var body = (props) => {
|
|
|
2060
2194
|
|
|
2061
2195
|
// src/proxmox/firewall-alias.ts
|
|
2062
2196
|
var ProxmoxFirewallAlias = Resource9("Proxmox.FirewallAlias");
|
|
2063
|
-
var
|
|
2197
|
+
var handlers4 = pveHandlers({
|
|
2064
2198
|
attributes: (live, props) => {
|
|
2065
2199
|
const address = cidr(live["cidr"]);
|
|
2066
2200
|
return address === "" ? undefined : {
|
|
@@ -2080,25 +2214,25 @@ var handlers5 = pveHandlers({
|
|
|
2080
2214
|
path: (props) => `cluster/firewall/aliases/${props.name}`,
|
|
2081
2215
|
updateForm: body
|
|
2082
2216
|
});
|
|
2083
|
-
var ProxmoxFirewallAliasProvider = () => Provider9.effect(ProxmoxFirewallAlias,
|
|
2084
|
-
...
|
|
2085
|
-
diff:
|
|
2086
|
-
if (output !== undefined &&
|
|
2217
|
+
var ProxmoxFirewallAliasProvider = () => Provider9.effect(ProxmoxFirewallAlias, Effect25.succeed(ProxmoxFirewallAlias.Provider.of({
|
|
2218
|
+
...handlers4,
|
|
2219
|
+
diff: Effect25.fn(function* ({ news, output }) {
|
|
2220
|
+
if (output !== undefined && isResolved8(news) && fold(news.name) !== fold(output.name)) {
|
|
2087
2221
|
return { action: "replace" };
|
|
2088
2222
|
}
|
|
2089
|
-
return yield*
|
|
2223
|
+
return yield* handlers4.diff({ news, output });
|
|
2090
2224
|
})
|
|
2091
2225
|
})));
|
|
2092
2226
|
// src/proxmox/group.ts
|
|
2093
2227
|
import { Resource as Resource10 } from "alchemy";
|
|
2094
|
-
import { isResolved as
|
|
2228
|
+
import { isResolved as isResolved9 } from "alchemy/Diff";
|
|
2095
2229
|
import * as Provider10 from "alchemy/Provider";
|
|
2096
2230
|
import * as access4 from "@distilled.cloud/proxmox/access";
|
|
2097
|
-
import * as
|
|
2231
|
+
import * as Effect27 from "effect/Effect";
|
|
2098
2232
|
|
|
2099
2233
|
// src/proxmox/group-wire.ts
|
|
2100
2234
|
import * as access3 from "@distilled.cloud/proxmox/access";
|
|
2101
|
-
import * as
|
|
2235
|
+
import * as Effect26 from "effect/Effect";
|
|
2102
2236
|
var GROUP_CREATE = "pve:POST /access/groups";
|
|
2103
2237
|
var GROUP_UPDATE = "pve:PUT /access/groups/{groupid}";
|
|
2104
2238
|
var storedComment = (comment2) => {
|
|
@@ -2117,14 +2251,14 @@ var updateForm = (props) => ({
|
|
|
2117
2251
|
comment: storedComment(props.comment),
|
|
2118
2252
|
groupid: props.groupid
|
|
2119
2253
|
});
|
|
2120
|
-
var
|
|
2254
|
+
var attributesOf4 = (live, props) => ({
|
|
2121
2255
|
comment: text(live.comment),
|
|
2122
2256
|
groupid: props.groupid,
|
|
2123
2257
|
members: memberList(live.members)
|
|
2124
2258
|
});
|
|
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(
|
|
2259
|
+
var matches4 = (attributes, props) => attributes.comment === storedComment(props.comment);
|
|
2260
|
+
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)));
|
|
2261
|
+
var readGroup = (props) => readGroupOrFail(props).pipe(Effect26.orElseSucceed(() => {
|
|
2128
2262
|
return;
|
|
2129
2263
|
}));
|
|
2130
2264
|
var dropUnreadable3 = (live) => live === UNREADABLE ? undefined : live;
|
|
@@ -2133,13 +2267,13 @@ var dropUnreadable3 = (live) => live === UNREADABLE ? undefined : live;
|
|
|
2133
2267
|
var ProxmoxGroup = Resource10("Proxmox.Group", {
|
|
2134
2268
|
defaultRemovalPolicy: "retain"
|
|
2135
2269
|
});
|
|
2136
|
-
var ProxmoxGroupProvider = () => Provider10.effect(ProxmoxGroup,
|
|
2137
|
-
list: () =>
|
|
2138
|
-
read:
|
|
2270
|
+
var ProxmoxGroupProvider = () => Provider10.effect(ProxmoxGroup, Effect27.succeed(ProxmoxGroup.Provider.of({
|
|
2271
|
+
list: () => Effect27.succeed([]),
|
|
2272
|
+
read: Effect27.fn(function* ({ olds, output }) {
|
|
2139
2273
|
return dropUnreadable3(yield* output === undefined ? readGroup(olds) : readGroupOrFail(olds));
|
|
2140
2274
|
}),
|
|
2141
|
-
diff:
|
|
2142
|
-
if (!
|
|
2275
|
+
diff: Effect27.fn(function* ({ news, output }) {
|
|
2276
|
+
if (!isResolved9(news))
|
|
2143
2277
|
return;
|
|
2144
2278
|
yield* guardWrite2(GROUP_CREATE, createForm3(news), output === undefined);
|
|
2145
2279
|
yield* guardWrite2(GROUP_UPDATE, updateForm(news), false);
|
|
@@ -2154,31 +2288,31 @@ var ProxmoxGroupProvider = () => Provider10.effect(ProxmoxGroup, Effect24.succee
|
|
|
2154
2288
|
yield* guardWrite2(GROUP_CREATE, createForm3(news), true);
|
|
2155
2289
|
return { action: "update" };
|
|
2156
2290
|
}
|
|
2157
|
-
return
|
|
2291
|
+
return matches4(live, news) ? { action: "noop" } : { action: "update" };
|
|
2158
2292
|
}),
|
|
2159
|
-
reconcile:
|
|
2293
|
+
reconcile: Effect27.fn(function* ({ news }) {
|
|
2160
2294
|
const before = dropUnreadable3(yield* readGroup(news));
|
|
2161
2295
|
yield* guardWrite2(GROUP_CREATE, createForm3(news), before === undefined);
|
|
2162
2296
|
yield* guardWrite2(GROUP_UPDATE, updateForm(news), false);
|
|
2163
2297
|
if (before === undefined) {
|
|
2164
2298
|
yield* runPve(news.target, "provision", true, access4.createAccessGroup(createForm3(news)));
|
|
2165
|
-
} else if (!
|
|
2299
|
+
} else if (!matches4(before, news)) {
|
|
2166
2300
|
yield* runPve(news.target, "provision", true, access4.putAccessGroup(updateForm(news)));
|
|
2167
2301
|
}
|
|
2168
2302
|
const after = dropUnreadable3(yield* readGroup(news));
|
|
2169
2303
|
if (after === undefined) {
|
|
2170
|
-
return yield*
|
|
2304
|
+
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
2305
|
}
|
|
2172
2306
|
return after;
|
|
2173
2307
|
}),
|
|
2174
|
-
delete:
|
|
2308
|
+
delete: Effect27.fn(function* ({ olds }) {
|
|
2175
2309
|
yield* runPve(olds.target, "provision", true, access4.deleteAccessGroup({ groupid: olds.groupid }));
|
|
2176
2310
|
})
|
|
2177
2311
|
})));
|
|
2178
2312
|
// src/proxmox/ha-resource.ts
|
|
2179
2313
|
import { Resource as Resource11 } from "alchemy";
|
|
2180
2314
|
import * as Provider11 from "alchemy/Provider";
|
|
2181
|
-
import * as
|
|
2315
|
+
import * as Effect28 from "effect/Effect";
|
|
2182
2316
|
var ProxmoxHaResource = Resource11("Proxmox.HaResource");
|
|
2183
2317
|
var kind = (sid) => sid.split(":")[0] ?? "";
|
|
2184
2318
|
var shape3 = (props) => ({
|
|
@@ -2187,7 +2321,7 @@ var shape3 = (props) => ({
|
|
|
2187
2321
|
max_restart: String(props.max_restart ?? 1),
|
|
2188
2322
|
state: props.state ?? "started"
|
|
2189
2323
|
});
|
|
2190
|
-
var
|
|
2324
|
+
var handlers5 = pveHandlers({
|
|
2191
2325
|
attributes: (live, props) => ({
|
|
2192
2326
|
comment: typeof live["comment"] === "string" ? live["comment"] : "",
|
|
2193
2327
|
max_relocate: num(live["max_relocate"], 1),
|
|
@@ -2206,11 +2340,11 @@ var handlers6 = pveHandlers({
|
|
|
2206
2340
|
path: (props) => `cluster/ha/resources/${props.sid}`,
|
|
2207
2341
|
updateForm: shape3
|
|
2208
2342
|
});
|
|
2209
|
-
var ProxmoxHaResourceProvider = () => Provider11.effect(ProxmoxHaResource,
|
|
2343
|
+
var ProxmoxHaResourceProvider = () => Provider11.effect(ProxmoxHaResource, Effect28.succeed(ProxmoxHaResource.Provider.of(handlers5)));
|
|
2210
2344
|
// src/proxmox/ha-rule.ts
|
|
2211
2345
|
import { Resource as Resource12 } from "alchemy";
|
|
2212
2346
|
import * as Provider12 from "alchemy/Provider";
|
|
2213
|
-
import * as
|
|
2347
|
+
import * as Effect29 from "effect/Effect";
|
|
2214
2348
|
|
|
2215
2349
|
// src/proxmox/ha-rule-form.ts
|
|
2216
2350
|
var nodeList = (value) => {
|
|
@@ -2258,7 +2392,7 @@ var clearList = (props) => Object.entries(optional(props)).filter(([, value]) =>
|
|
|
2258
2392
|
|
|
2259
2393
|
// src/proxmox/ha-rule.ts
|
|
2260
2394
|
var ProxmoxHaRule = Resource12("Proxmox.HaRule");
|
|
2261
|
-
var
|
|
2395
|
+
var handlers6 = pveHandlers({
|
|
2262
2396
|
attributes: (live, props) => {
|
|
2263
2397
|
const liveType = text(live["type"]);
|
|
2264
2398
|
if (liveType !== "" && liveType !== props.type)
|
|
@@ -2287,43 +2421,43 @@ var handlers7 = pveHandlers({
|
|
|
2287
2421
|
return withClears(fields, clear);
|
|
2288
2422
|
}
|
|
2289
2423
|
});
|
|
2290
|
-
var ProxmoxHaRuleProvider = () => Provider12.effect(ProxmoxHaRule,
|
|
2424
|
+
var ProxmoxHaRuleProvider = () => Provider12.effect(ProxmoxHaRule, Effect29.succeed(ProxmoxHaRule.Provider.of(handlers6)));
|
|
2291
2425
|
// src/proxmox/lxc.ts
|
|
2292
2426
|
import { Resource as Resource13 } from "alchemy";
|
|
2293
|
-
import { isResolved as
|
|
2427
|
+
import { isResolved as isResolved10 } from "alchemy/Diff";
|
|
2294
2428
|
import * as Provider13 from "alchemy/Provider";
|
|
2295
|
-
import * as
|
|
2429
|
+
import * as Effect39 from "effect/Effect";
|
|
2296
2430
|
|
|
2297
2431
|
// src/ownership/adopt.ts
|
|
2298
2432
|
import { AdoptPolicy } from "alchemy/AdoptPolicy";
|
|
2299
2433
|
import { AlchemyContext } from "alchemy/AlchemyContext";
|
|
2300
2434
|
import { Stack as Stack3 } from "alchemy/Stack";
|
|
2301
|
-
import * as
|
|
2435
|
+
import * as Effect33 from "effect/Effect";
|
|
2302
2436
|
import * as Option5 from "effect/Option";
|
|
2303
2437
|
|
|
2304
2438
|
// src/ownership/resume.ts
|
|
2305
2439
|
import { Unowned } from "alchemy/AdoptPolicy";
|
|
2306
2440
|
import { Artifacts } from "alchemy/Artifacts";
|
|
2307
|
-
import * as
|
|
2441
|
+
import * as Effect30 from "effect/Effect";
|
|
2308
2442
|
import * as Option2 from "effect/Option";
|
|
2309
2443
|
var PROVEN = "homeflare/ownership/resumes";
|
|
2310
2444
|
var UNFINISHED = "homeflare/ownership/unfinished";
|
|
2311
|
-
var note = (key, instanceId) =>
|
|
2312
|
-
const bag = yield*
|
|
2445
|
+
var note = (key, instanceId) => Effect30.gen(function* () {
|
|
2446
|
+
const bag = yield* Effect30.serviceOption(Artifacts);
|
|
2313
2447
|
if (Option2.isSome(bag))
|
|
2314
2448
|
yield* bag.value.set(key, instanceId);
|
|
2315
2449
|
});
|
|
2316
|
-
var noted = (key, instanceId) =>
|
|
2317
|
-
const bag = yield*
|
|
2450
|
+
var noted = (key, instanceId) => Effect30.gen(function* () {
|
|
2451
|
+
const bag = yield* Effect30.serviceOption(Artifacts);
|
|
2318
2452
|
if (Option2.isNone(bag))
|
|
2319
2453
|
return false;
|
|
2320
2454
|
return (yield* bag.value.get(key)) === instanceId;
|
|
2321
2455
|
});
|
|
2322
|
-
var noteUnfinished = (instanceId) =>
|
|
2323
|
-
var noteResume = (instanceId, recovered) =>
|
|
2456
|
+
var noteUnfinished = (instanceId) => Effect30.as(note(UNFINISHED, instanceId), undefined);
|
|
2457
|
+
var noteResume = (instanceId, recovered) => Effect30.gen(function* () {
|
|
2324
2458
|
yield* noteUnfinished(instanceId);
|
|
2325
|
-
const unproven = () =>
|
|
2326
|
-
const found = yield* recovered.pipe(
|
|
2459
|
+
const unproven = () => Effect30.succeed(undefined);
|
|
2460
|
+
const found = yield* recovered.pipe(Effect30.catch(unproven), Effect30.catchDefect(unproven));
|
|
2327
2461
|
if (found === undefined || found === null || Unowned.is(found))
|
|
2328
2462
|
return;
|
|
2329
2463
|
yield* note(PROVEN, instanceId);
|
|
@@ -2334,7 +2468,7 @@ var provingResumes = (service) => {
|
|
|
2334
2468
|
const { diff, read } = service;
|
|
2335
2469
|
if (diff === undefined || read === undefined)
|
|
2336
2470
|
return service;
|
|
2337
|
-
const proving = (input) =>
|
|
2471
|
+
const proving = (input) => Effect30.gen(function* () {
|
|
2338
2472
|
if (input.output === undefined) {
|
|
2339
2473
|
yield* noteResume(input.instanceId, read({ ...input, output: undefined }));
|
|
2340
2474
|
}
|
|
@@ -2346,12 +2480,12 @@ var provingResumes = (service) => {
|
|
|
2346
2480
|
// src/ownership/rows.ts
|
|
2347
2481
|
import { Stack as Stack2 } from "alchemy/Stack";
|
|
2348
2482
|
import { State, isActionState } from "alchemy/State/State";
|
|
2349
|
-
import * as
|
|
2483
|
+
import * as Effect32 from "effect/Effect";
|
|
2350
2484
|
import * as Option4 from "effect/Option";
|
|
2351
2485
|
|
|
2352
2486
|
// src/ownership/whole.ts
|
|
2353
2487
|
import { Stack } from "alchemy/Stack";
|
|
2354
|
-
import * as
|
|
2488
|
+
import * as Effect31 from "effect/Effect";
|
|
2355
2489
|
import * as Option3 from "effect/Option";
|
|
2356
2490
|
var absent = (value) => value === undefined || value === null;
|
|
2357
2491
|
var isPlainObject = (value) => {
|
|
@@ -2377,22 +2511,22 @@ var carries = (declared, recorded) => {
|
|
|
2377
2511
|
}
|
|
2378
2512
|
return true;
|
|
2379
2513
|
};
|
|
2380
|
-
var wholeDeclaration = (fqn, props) =>
|
|
2514
|
+
var wholeDeclaration = (fqn, props) => Effect31.map(Effect31.serviceOption(Stack), (stack) => {
|
|
2381
2515
|
const declared = Option3.isSome(stack) ? stack.value.resources[fqn] : undefined;
|
|
2382
2516
|
return declared !== undefined && carries(declared.Props, props);
|
|
2383
2517
|
});
|
|
2384
2518
|
|
|
2385
2519
|
// src/ownership/rows.ts
|
|
2386
|
-
var storeOf =
|
|
2387
|
-
const stack = yield*
|
|
2388
|
-
const state = yield*
|
|
2520
|
+
var storeOf = Effect32.gen(function* () {
|
|
2521
|
+
const stack = yield* Effect32.serviceOption(Stack2);
|
|
2522
|
+
const state = yield* Effect32.serviceOption(State);
|
|
2389
2523
|
if (Option4.isNone(stack) || Option4.isNone(state))
|
|
2390
2524
|
return;
|
|
2391
2525
|
const store = yield* state.value;
|
|
2392
2526
|
const where = { stack: stack.value.name, stage: stack.value.stage };
|
|
2393
2527
|
return { resources: stack.value.resources, store, where };
|
|
2394
2528
|
});
|
|
2395
|
-
var rowAt = (store, where, fqn) => store.get({ ...where, fqn }).pipe(
|
|
2529
|
+
var rowAt = (store, where, fqn) => store.get({ ...where, fqn }).pipe(Effect32.orElseSucceed(() => {
|
|
2396
2530
|
return;
|
|
2397
2531
|
}));
|
|
2398
2532
|
var generationOf = (row, instanceId) => {
|
|
@@ -2405,7 +2539,7 @@ var generationOf = (row, instanceId) => {
|
|
|
2405
2539
|
}
|
|
2406
2540
|
return;
|
|
2407
2541
|
};
|
|
2408
|
-
var recordedGeneration = (fqn, instanceId) =>
|
|
2542
|
+
var recordedGeneration = (fqn, instanceId) => Effect32.gen(function* () {
|
|
2409
2543
|
const found = yield* storeOf;
|
|
2410
2544
|
if (found === undefined)
|
|
2411
2545
|
return;
|
|
@@ -2416,14 +2550,14 @@ var recordedGeneration = (fqn, instanceId) => Effect29.gen(function* () {
|
|
|
2416
2550
|
}
|
|
2417
2551
|
return;
|
|
2418
2552
|
});
|
|
2419
|
-
var recordedInstance = (fqn, instanceId) =>
|
|
2553
|
+
var recordedInstance = (fqn, instanceId) => Effect32.gen(function* () {
|
|
2420
2554
|
const generation = yield* recordedGeneration(fqn, instanceId);
|
|
2421
2555
|
if (generation === undefined)
|
|
2422
2556
|
return "absent";
|
|
2423
2557
|
return (yield* wholeDeclaration(fqn, generation.props)) ? "whole" : "partial";
|
|
2424
2558
|
});
|
|
2425
|
-
var isCreate = (fqn, instanceId) =>
|
|
2426
|
-
var forgetRefusedCreate = (fqn, instanceId) =>
|
|
2559
|
+
var isCreate = (fqn, instanceId) => Effect32.map(recordedGeneration(fqn, instanceId), (generation) => generation?.status === "creating");
|
|
2560
|
+
var forgetRefusedCreate = (fqn, instanceId) => Effect32.gen(function* () {
|
|
2427
2561
|
const found = yield* storeOf;
|
|
2428
2562
|
if (found === undefined)
|
|
2429
2563
|
return;
|
|
@@ -2432,32 +2566,32 @@ var forgetRefusedCreate = (fqn, instanceId) => Effect29.gen(function* () {
|
|
|
2432
2566
|
if (mine?.status !== "creating" || mine.instanceId !== instanceId || mine.attr !== undefined) {
|
|
2433
2567
|
return;
|
|
2434
2568
|
}
|
|
2435
|
-
yield* found.store.delete({ ...found.where, fqn }).pipe(
|
|
2569
|
+
yield* found.store.delete({ ...found.where, fqn }).pipe(Effect32.ignore);
|
|
2436
2570
|
});
|
|
2437
2571
|
|
|
2438
2572
|
// src/ownership/adopt.ts
|
|
2439
|
-
var adoptEnabled = (fqn) =>
|
|
2440
|
-
const stack = yield*
|
|
2573
|
+
var adoptEnabled = (fqn) => Effect33.gen(function* () {
|
|
2574
|
+
const stack = yield* Effect33.serviceOption(Stack3);
|
|
2441
2575
|
const scoped = Option5.isSome(stack) ? stack.value.resources[fqn]?.Adopt : undefined;
|
|
2442
2576
|
if (scoped !== undefined)
|
|
2443
2577
|
return scoped;
|
|
2444
|
-
const policy = yield*
|
|
2578
|
+
const policy = yield* Effect33.serviceOption(AdoptPolicy);
|
|
2445
2579
|
if (Option5.isSome(policy))
|
|
2446
2580
|
return policy.value;
|
|
2447
|
-
const context = yield*
|
|
2581
|
+
const context = yield* Effect33.serviceOption(AlchemyContext);
|
|
2448
2582
|
return Option5.isSome(context) ? context.value.adopt : false;
|
|
2449
2583
|
});
|
|
2450
|
-
var adoptable = (owner) =>
|
|
2584
|
+
var adoptable = (owner) => Effect33.gen(function* () {
|
|
2451
2585
|
if (yield* isCreate(owner.fqn, owner.instanceId))
|
|
2452
2586
|
return true;
|
|
2453
2587
|
return yield* unfinished(owner.instanceId);
|
|
2454
2588
|
});
|
|
2455
|
-
var adoptsAtApply = (owner) =>
|
|
2589
|
+
var adoptsAtApply = (owner) => Effect33.gen(function* () {
|
|
2456
2590
|
if (!(yield* adoptEnabled(owner.fqn)))
|
|
2457
2591
|
return false;
|
|
2458
2592
|
return yield* adoptable(owner);
|
|
2459
2593
|
});
|
|
2460
|
-
var refuseTakeover = (owner, what) =>
|
|
2594
|
+
var refuseTakeover = (owner, what) => Effect33.gen(function* () {
|
|
2461
2595
|
if (owner.output !== undefined)
|
|
2462
2596
|
return;
|
|
2463
2597
|
if (yield* resumes(owner.instanceId))
|
|
@@ -2467,25 +2601,25 @@ var refuseTakeover = (owner, what) => Effect30.gen(function* () {
|
|
|
2467
2601
|
return;
|
|
2468
2602
|
yield* forgetRefusedCreate(owner.fqn, owner.instanceId);
|
|
2469
2603
|
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*
|
|
2604
|
+
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
2605
|
});
|
|
2472
2606
|
var claimFor = (owner) => (what) => refuseTakeover(owner, what);
|
|
2473
2607
|
|
|
2474
2608
|
// src/ownership/probe.ts
|
|
2475
2609
|
import { Unowned as Unowned2 } from "alchemy/AdoptPolicy";
|
|
2476
|
-
import * as
|
|
2477
|
-
var provenOurs = (ask, settled2) =>
|
|
2610
|
+
import * as Effect34 from "effect/Effect";
|
|
2611
|
+
var provenOurs = (ask, settled2) => Effect34.gen(function* () {
|
|
2478
2612
|
const recorded = yield* recordedInstance(ask.fqn, ask.instanceId);
|
|
2479
2613
|
if (recorded === "absent")
|
|
2480
2614
|
return false;
|
|
2481
2615
|
if (recorded === "partial") {
|
|
2482
|
-
yield*
|
|
2616
|
+
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
2617
|
return false;
|
|
2484
2618
|
}
|
|
2485
|
-
const unproven = (reason) =>
|
|
2486
|
-
return yield* settled2.pipe(
|
|
2619
|
+
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);
|
|
2620
|
+
return yield* settled2.pipe(Effect34.catch(unproven), Effect34.catchDefect(unproven));
|
|
2487
2621
|
});
|
|
2488
|
-
var ownedRead = (ask, found, settled2) =>
|
|
2622
|
+
var ownedRead = (ask, found, settled2) => Effect34.gen(function* () {
|
|
2489
2623
|
if (found === undefined || ask.output !== undefined)
|
|
2490
2624
|
return found;
|
|
2491
2625
|
return (yield* provenOurs(ask, settled2)) ? found : Unowned2(found);
|
|
@@ -2543,8 +2677,8 @@ var optionMap = (value, family, defaultKey) => {
|
|
|
2543
2677
|
return out;
|
|
2544
2678
|
};
|
|
2545
2679
|
var sameMap = (left, right) => left.size === right.size && [...left].every(([key, value]) => right.get(key) === value);
|
|
2546
|
-
var sortedForm = (
|
|
2547
|
-
var printMap = (
|
|
2680
|
+
var sortedForm = (map12) => [...map12].map(([key, value]) => `${key}=${value}`).sort().join(",");
|
|
2681
|
+
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
2682
|
var netMaps = (declared, live) => {
|
|
2549
2683
|
const want = optionMap(declared, "net");
|
|
2550
2684
|
const have = optionMap(live, "net");
|
|
@@ -2690,12 +2824,12 @@ var sizeBytes = (text2) => {
|
|
|
2690
2824
|
};
|
|
2691
2825
|
var parseVolume = (text2) => {
|
|
2692
2826
|
const [first] = pairs(text2, "volume");
|
|
2693
|
-
const
|
|
2827
|
+
const spec = first?.[0] === "volume" ? first[1] : "";
|
|
2694
2828
|
const options = optionMap(text2, "volume", "volume");
|
|
2695
2829
|
options.delete("volume");
|
|
2696
2830
|
const sizeText = options.get("size");
|
|
2697
2831
|
options.delete("size");
|
|
2698
|
-
const fresh = NEW_DISK.exec(
|
|
2832
|
+
const fresh = NEW_DISK.exec(spec);
|
|
2699
2833
|
if (fresh !== null) {
|
|
2700
2834
|
const gib = `${fresh[2] ?? "0"}G`;
|
|
2701
2835
|
return {
|
|
@@ -2708,18 +2842,18 @@ var parseVolume = (text2) => {
|
|
|
2708
2842
|
};
|
|
2709
2843
|
}
|
|
2710
2844
|
const size = sizeText === undefined ? undefined : sizeBytes(sizeText);
|
|
2711
|
-
if (
|
|
2712
|
-
const kind2 =
|
|
2713
|
-
return { kind: kind2, options, size, sizeText, source:
|
|
2845
|
+
if (spec.startsWith("/")) {
|
|
2846
|
+
const kind2 = spec.startsWith("/dev/") ? "device" : "bind";
|
|
2847
|
+
return { kind: kind2, options, size, sizeText, source: spec, volname: "" };
|
|
2714
2848
|
}
|
|
2715
|
-
const at =
|
|
2849
|
+
const at = spec.indexOf(":");
|
|
2716
2850
|
return {
|
|
2717
2851
|
kind: "volume",
|
|
2718
2852
|
options,
|
|
2719
2853
|
size,
|
|
2720
2854
|
sizeText,
|
|
2721
|
-
source: at === -1 ?
|
|
2722
|
-
volname: at === -1 ? "" :
|
|
2855
|
+
source: at === -1 ? spec : spec.slice(0, at),
|
|
2856
|
+
volname: at === -1 ? "" : spec.slice(at + 1)
|
|
2723
2857
|
};
|
|
2724
2858
|
};
|
|
2725
2859
|
var isHostPath = (volume) => volume.kind === "bind" || volume.kind === "device";
|
|
@@ -2940,11 +3074,11 @@ var identityRefusals = (news, olds, output) => {
|
|
|
2940
3074
|
};
|
|
2941
3075
|
|
|
2942
3076
|
// src/proxmox/lxc-adoption.ts
|
|
2943
|
-
import * as
|
|
3077
|
+
import * as Effect38 from "effect/Effect";
|
|
2944
3078
|
|
|
2945
3079
|
// src/ownership/adopting.ts
|
|
2946
|
-
import * as
|
|
2947
|
-
var adopting = (owner, settled2) =>
|
|
3080
|
+
import * as Effect35 from "effect/Effect";
|
|
3081
|
+
var adopting = (owner, settled2) => Effect35.gen(function* () {
|
|
2948
3082
|
if (owner.output === undefined)
|
|
2949
3083
|
return false;
|
|
2950
3084
|
const generation = yield* recordedGeneration(owner.fqn, owner.instanceId);
|
|
@@ -2956,11 +3090,11 @@ var adopting = (owner, settled2) => Effect32.gen(function* () {
|
|
|
2956
3090
|
});
|
|
2957
3091
|
|
|
2958
3092
|
// src/proxmox/lxc-lifecycle.ts
|
|
2959
|
-
import * as
|
|
3093
|
+
import * as Effect37 from "effect/Effect";
|
|
2960
3094
|
|
|
2961
3095
|
// src/proxmox/network-apply-read.ts
|
|
2962
|
-
import * as
|
|
2963
|
-
var stagedDiff = (target, node) =>
|
|
3096
|
+
import * as Effect36 from "effect/Effect";
|
|
3097
|
+
var stagedDiff = (target, node) => Effect36.gen(function* () {
|
|
2964
3098
|
const path = `nodes/${node}/network`;
|
|
2965
3099
|
const credential = yield* leased(target, "read");
|
|
2966
3100
|
const envelope = yield* pveEnvelopeWith(target, credential, "GET", path);
|
|
@@ -2973,8 +3107,8 @@ var pendingLines = (diff) => {
|
|
|
2973
3107
|
`).filter((line) => (line.startsWith("+") || line.startsWith("-")) && !line.startsWith("+++") && !line.startsWith("---")).length;
|
|
2974
3108
|
return Math.max(changed, 1);
|
|
2975
3109
|
};
|
|
2976
|
-
var pendingCount = (target, node) => stagedDiff(target, node).pipe(
|
|
2977
|
-
var clusterHealth = (target) => pve(target, "read", "GET", "cluster/status").pipe(
|
|
3110
|
+
var pendingCount = (target, node) => stagedDiff(target, node).pipe(Effect36.map(pendingLines));
|
|
3111
|
+
var clusterHealth = (target) => pve(target, "read", "GET", "cluster/status").pipe(Effect36.map((rows) => {
|
|
2978
3112
|
const all = rows ?? [];
|
|
2979
3113
|
const cluster = all.find((row) => row.type === "cluster");
|
|
2980
3114
|
return {
|
|
@@ -2982,21 +3116,21 @@ var clusterHealth = (target) => pve(target, "read", "GET", "cluster/status").pip
|
|
|
2982
3116
|
quorate: cluster === undefined ? true : bool(cluster.quorate)
|
|
2983
3117
|
};
|
|
2984
3118
|
}));
|
|
2985
|
-
var degradedReason = (target) => clusterHealth(target).pipe(
|
|
3119
|
+
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
3120
|
var UNREACHABLE = "the task could not be read -- the node stopped answering, or this lease cannot see it";
|
|
2987
3121
|
var STILL_RUNNING = "still running when this provider stopped waiting";
|
|
2988
3122
|
var ATTEMPTS = 30;
|
|
2989
|
-
var awaitTask = (target, credential, node, upid, attempts = ATTEMPTS) =>
|
|
3123
|
+
var awaitTask = (target, credential, node, upid, attempts = ATTEMPTS) => Effect36.gen(function* () {
|
|
2990
3124
|
const path = `nodes/${node}/tasks/${upid}/status`;
|
|
2991
3125
|
for (let attempt = 0;attempt < attempts; attempt += 1) {
|
|
2992
|
-
const status = yield* pveWith(target, credential, "GET", path).pipe(
|
|
3126
|
+
const status = yield* pveWith(target, credential, "GET", path).pipe(Effect36.orElseSucceed(() => {
|
|
2993
3127
|
return;
|
|
2994
3128
|
}));
|
|
2995
3129
|
if (status === undefined)
|
|
2996
3130
|
return UNREACHABLE;
|
|
2997
3131
|
if (status.status === "stopped")
|
|
2998
3132
|
return text(status.exitstatus, "stopped with no exitstatus");
|
|
2999
|
-
yield*
|
|
3133
|
+
yield* Effect36.sleep("1 second");
|
|
3000
3134
|
}
|
|
3001
3135
|
return STILL_RUNNING;
|
|
3002
3136
|
});
|
|
@@ -3014,43 +3148,43 @@ var LXC_ENDPOINTS = {
|
|
|
3014
3148
|
var CREATE_POLLS = 180;
|
|
3015
3149
|
var TASK_POLLS = 60;
|
|
3016
3150
|
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(
|
|
3151
|
+
var readLive = (where) => Effect37.gen(function* () {
|
|
3152
|
+
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
3153
|
if (answer.data !== undefined && answer.data !== null)
|
|
3020
3154
|
return answer.data;
|
|
3021
3155
|
const rows = yield* pve(where.target, "read", "GET", "cluster/resources?type=vm");
|
|
3022
3156
|
const found = (rows ?? []).find((row) => Number(row.vmid) === where.vmid);
|
|
3023
3157
|
if (found !== undefined && found.node === where.node && found.type === "lxc") {
|
|
3024
|
-
return yield*
|
|
3158
|
+
return yield* Effect37.fail(answer.error ?? new LxcRefusedError(`CT ${String(where.vmid)} is listed but its config read empty.`));
|
|
3025
3159
|
}
|
|
3026
3160
|
if (found !== undefined) {
|
|
3027
|
-
return yield*
|
|
3161
|
+
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
3162
|
}
|
|
3029
3163
|
return;
|
|
3030
3164
|
});
|
|
3031
|
-
var
|
|
3165
|
+
var attributesOf5 = (where, live) => ({
|
|
3032
3166
|
config: storedConfig(live),
|
|
3033
3167
|
node: where.node,
|
|
3034
3168
|
rawKeys: rawKeysOf(live),
|
|
3035
3169
|
vmid: where.vmid
|
|
3036
3170
|
});
|
|
3037
|
-
var task = (where, method, path, what, polls, form) =>
|
|
3171
|
+
var task = (where, method, path, what, polls, form) => Effect37.gen(function* () {
|
|
3038
3172
|
const credential = yield* mint(where.target, "provision");
|
|
3039
3173
|
const upid = text(yield* pveWith(where.target, credential, method, path, form));
|
|
3040
3174
|
if (upid === "") {
|
|
3041
|
-
return yield*
|
|
3175
|
+
return yield* Effect37.fail(new LxcRefusedError(`${what}: PVE returned no task id, so the write cannot be confirmed and is not claimed.`));
|
|
3042
3176
|
}
|
|
3043
3177
|
const exit = yield* awaitTask(where.target, credential, where.node, upid, polls);
|
|
3044
3178
|
if (exit !== "OK") {
|
|
3045
|
-
return yield*
|
|
3179
|
+
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
3180
|
}
|
|
3047
3181
|
});
|
|
3048
|
-
var createGuest = (props) =>
|
|
3182
|
+
var createGuest = (props) => Effect37.gen(function* () {
|
|
3049
3183
|
const form = createForm4(props);
|
|
3050
3184
|
yield* guardForm(LXC_ENDPOINTS.create, form, true);
|
|
3051
3185
|
return yield* task(props, "POST", `nodes/${props.node}/lxc`, `create CT ${String(props.vmid)}`, CREATE_POLLS, form);
|
|
3052
3186
|
});
|
|
3053
|
-
var updateGuest = (props, change, digest) =>
|
|
3187
|
+
var updateGuest = (props, change, digest) => Effect37.gen(function* () {
|
|
3054
3188
|
if (Object.keys(change.put).length > 0 || change.clear.length > 0) {
|
|
3055
3189
|
const form = withClears({ ...change.put, ...digest === "" ? {} : { digest } }, change.clear);
|
|
3056
3190
|
yield* guardForm(LXC_ENDPOINTS.update, form, false);
|
|
@@ -3062,47 +3196,47 @@ var updateGuest = (props, change, digest) => Effect34.gen(function* () {
|
|
|
3062
3196
|
yield* task(props, "PUT", `${guestPath(props)}/resize`, `grow ${grow.disk} of CT ${String(props.vmid)} to ${grow.size}`, TASK_POLLS, grown);
|
|
3063
3197
|
}
|
|
3064
3198
|
});
|
|
3065
|
-
var destroyGuest = (where, declared) =>
|
|
3199
|
+
var destroyGuest = (where, declared) => Effect37.gen(function* () {
|
|
3066
3200
|
const live = yield* readLive(where);
|
|
3067
3201
|
if (live === undefined)
|
|
3068
3202
|
return;
|
|
3069
3203
|
const drift = judge(declared, live).drift;
|
|
3070
3204
|
if (drift.length > 0) {
|
|
3071
|
-
return yield*
|
|
3205
|
+
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
3206
|
}
|
|
3073
3207
|
yield* task(where, "DELETE", guestPath(where), `destroy CT ${String(where.vmid)}`, TASK_POLLS);
|
|
3074
3208
|
});
|
|
3075
3209
|
|
|
3076
3210
|
// src/proxmox/lxc-adoption.ts
|
|
3077
3211
|
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) =>
|
|
3212
|
+
var refuseAdoptedDrift = (owner, props, live, change) => Effect38.gen(function* () {
|
|
3079
3213
|
if (change.drift.length === 0)
|
|
3080
3214
|
return;
|
|
3081
|
-
const settled2 = (recorded) =>
|
|
3215
|
+
const settled2 = (recorded) => Effect38.sync(() => judge(recorded, live).drift.length === 0);
|
|
3082
3216
|
if (!(yield* adopting(owner, settled2)))
|
|
3083
3217
|
return;
|
|
3084
|
-
return yield*
|
|
3218
|
+
return yield* Effect38.fail(new LxcRefusedError(adoptionRefusal(props, change.drift)));
|
|
3085
3219
|
});
|
|
3086
3220
|
|
|
3087
3221
|
// src/proxmox/lxc.ts
|
|
3088
3222
|
var ProxmoxLxc = Resource13("Proxmox.Lxc", { defaultRemovalPolicy: "retain" });
|
|
3089
|
-
var refused = (reasons) =>
|
|
3223
|
+
var refused = (reasons) => Effect39.fail(new LxcRefusedError(reasons.join(`
|
|
3090
3224
|
`)));
|
|
3091
|
-
var diff = (owner, news, olds) =>
|
|
3225
|
+
var diff = (owner, news, olds) => Effect39.gen(function* () {
|
|
3092
3226
|
const { output } = owner;
|
|
3093
3227
|
const identity2 = identityRefusals(news, olds, output);
|
|
3094
3228
|
if (identity2.length > 0)
|
|
3095
3229
|
return yield* refused(identity2);
|
|
3096
3230
|
if (output === undefined)
|
|
3097
3231
|
return;
|
|
3098
|
-
if (!
|
|
3232
|
+
if (!isResolved10(news))
|
|
3099
3233
|
return;
|
|
3100
3234
|
const live = yield* readLive(news);
|
|
3101
3235
|
if (live === undefined) {
|
|
3102
3236
|
const refusals = createRefusals(news);
|
|
3103
3237
|
if (refusals.length > 0)
|
|
3104
3238
|
return yield* refused(refusals);
|
|
3105
|
-
yield*
|
|
3239
|
+
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
3240
|
return { action: "update" };
|
|
3107
3241
|
}
|
|
3108
3242
|
const change = judge(news, live);
|
|
@@ -3113,10 +3247,10 @@ var diff = (owner, news, olds) => Effect36.gen(function* () {
|
|
|
3113
3247
|
if (change.drift.length === 0 && !moved)
|
|
3114
3248
|
return { action: "noop" };
|
|
3115
3249
|
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*
|
|
3250
|
+
yield* Effect39.logWarning(`Proxmox.Lxc ${news.node}/${String(news.vmid)}: ${what}`);
|
|
3117
3251
|
return { action: "update" };
|
|
3118
3252
|
});
|
|
3119
|
-
var reconcile = (owner, news) =>
|
|
3253
|
+
var reconcile = (owner, news) => Effect39.gen(function* () {
|
|
3120
3254
|
const { output } = owner;
|
|
3121
3255
|
const identity2 = identityRefusals(news, news, output);
|
|
3122
3256
|
if (identity2.length > 0)
|
|
@@ -3155,22 +3289,22 @@ var reconcile = (owner, news) => Effect36.gen(function* () {
|
|
|
3155
3289
|
`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
3290
|
]);
|
|
3157
3291
|
}
|
|
3158
|
-
return
|
|
3292
|
+
return attributesOf5(news, after);
|
|
3159
3293
|
});
|
|
3160
3294
|
var whereOf = (olds, output) => ({
|
|
3161
3295
|
node: output?.node ?? olds.node,
|
|
3162
3296
|
target: olds.target,
|
|
3163
3297
|
vmid: output?.vmid ?? olds.vmid
|
|
3164
3298
|
});
|
|
3165
|
-
var read = (owner, olds) =>
|
|
3299
|
+
var read = (owner, olds) => Effect39.gen(function* () {
|
|
3166
3300
|
const where = whereOf(olds, owner.output);
|
|
3167
3301
|
const live = yield* readLive(where);
|
|
3168
3302
|
if (live === undefined)
|
|
3169
3303
|
return;
|
|
3170
|
-
const settled2 =
|
|
3171
|
-
return yield* ownedRead(owner,
|
|
3304
|
+
const settled2 = Effect39.sync(() => judge(olds, live).drift.length === 0);
|
|
3305
|
+
return yield* ownedRead(owner, attributesOf5(where, live), settled2);
|
|
3172
3306
|
});
|
|
3173
|
-
var
|
|
3307
|
+
var handlers7 = {
|
|
3174
3308
|
delete: ({ olds, output }) => destroyGuest(whereOf(olds, output), olds),
|
|
3175
3309
|
diff: ({
|
|
3176
3310
|
fqn,
|
|
@@ -3179,7 +3313,7 @@ var handlers8 = {
|
|
|
3179
3313
|
olds,
|
|
3180
3314
|
output
|
|
3181
3315
|
}) => diff({ fqn, instanceId, output }, news, olds),
|
|
3182
|
-
list: () =>
|
|
3316
|
+
list: () => Effect39.succeed([]),
|
|
3183
3317
|
read: ({
|
|
3184
3318
|
fqn,
|
|
3185
3319
|
instanceId,
|
|
@@ -3193,11 +3327,11 @@ var handlers8 = {
|
|
|
3193
3327
|
output
|
|
3194
3328
|
}) => reconcile({ fqn, instanceId, output }, news)
|
|
3195
3329
|
};
|
|
3196
|
-
var ProxmoxLxcProvider = () => Provider13.effect(ProxmoxLxc,
|
|
3330
|
+
var ProxmoxLxcProvider = () => Provider13.effect(ProxmoxLxc, Effect39.succeed(ProxmoxLxc.Provider.of(handlers7)).pipe(Effect39.map(provingResumes)));
|
|
3197
3331
|
// src/proxmox/metric-server.ts
|
|
3198
3332
|
import { Resource as Resource14 } from "alchemy";
|
|
3199
3333
|
import * as Provider14 from "alchemy/Provider";
|
|
3200
|
-
import * as
|
|
3334
|
+
import * as Effect40 from "effect/Effect";
|
|
3201
3335
|
|
|
3202
3336
|
// src/proxmox/metric-server-form.ts
|
|
3203
3337
|
var UNSET2 = -1;
|
|
@@ -3277,7 +3411,7 @@ var matchesType = (attributes, props) => {
|
|
|
3277
3411
|
return socket && attributes.path === (props.path ?? "");
|
|
3278
3412
|
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
3413
|
};
|
|
3280
|
-
var
|
|
3414
|
+
var handlers8 = pveHandlers({
|
|
3281
3415
|
attributes: (live, props) => {
|
|
3282
3416
|
const liveType = text(live["type"]);
|
|
3283
3417
|
if (liveType !== "" && liveType !== props.type)
|
|
@@ -3310,12 +3444,12 @@ var handlers9 = pveHandlers({
|
|
|
3310
3444
|
path: (props) => `cluster/metrics/server/${props.id}`,
|
|
3311
3445
|
updateForm: update
|
|
3312
3446
|
});
|
|
3313
|
-
var ProxmoxMetricServerProvider = () => Provider14.effect(ProxmoxMetricServer,
|
|
3447
|
+
var ProxmoxMetricServerProvider = () => Provider14.effect(ProxmoxMetricServer, Effect40.succeed(ProxmoxMetricServer.Provider.of(handlers8)));
|
|
3314
3448
|
// src/proxmox/network-apply.ts
|
|
3315
3449
|
import { Resource as Resource15 } from "alchemy";
|
|
3316
|
-
import { isResolved as
|
|
3450
|
+
import { isResolved as isResolved11 } from "alchemy/Diff";
|
|
3317
3451
|
import * as Provider15 from "alchemy/Provider";
|
|
3318
|
-
import * as
|
|
3452
|
+
import * as Effect41 from "effect/Effect";
|
|
3319
3453
|
|
|
3320
3454
|
// src/proxmox/apply-endpoints.ts
|
|
3321
3455
|
var NETWORK_APPLY_ENDPOINT = "pve:PUT /nodes/{node}/network";
|
|
@@ -3325,15 +3459,15 @@ var guardSdnApply = guardForm(SDN_APPLY_ENDPOINT, {}, true);
|
|
|
3325
3459
|
|
|
3326
3460
|
// src/proxmox/network-apply.ts
|
|
3327
3461
|
var ProxmoxNetworkApply = Resource15("Proxmox.NetworkApply");
|
|
3328
|
-
var read2 = (props) => pendingCount(props.target, props.node).pipe(
|
|
3329
|
-
var diff2 = (news) =>
|
|
3330
|
-
if (!
|
|
3462
|
+
var read2 = (props) => pendingCount(props.target, props.node).pipe(Effect41.map((pending) => ({ node: props.node, pending })));
|
|
3463
|
+
var diff2 = (news) => Effect41.gen(function* () {
|
|
3464
|
+
if (!isResolved11(news))
|
|
3331
3465
|
return;
|
|
3332
3466
|
const { pending } = yield* read2(news);
|
|
3333
3467
|
return pending === 0 ? { action: "noop" } : { action: "update" };
|
|
3334
3468
|
});
|
|
3335
|
-
var refuse = (node, why) =>
|
|
3336
|
-
var apply = (props) =>
|
|
3469
|
+
var refuse = (node, why) => Effect41.die(new Error(`Proxmox.NetworkApply ${node}: ${why}`));
|
|
3470
|
+
var apply = (props) => Effect41.gen(function* () {
|
|
3337
3471
|
const node = props.node;
|
|
3338
3472
|
const staged = yield* pendingCount(props.target, node);
|
|
3339
3473
|
if (staged === 0)
|
|
@@ -3362,20 +3496,20 @@ var apply = (props) => Effect38.gen(function* () {
|
|
|
3362
3496
|
}
|
|
3363
3497
|
return { node, pending: left };
|
|
3364
3498
|
});
|
|
3365
|
-
var
|
|
3366
|
-
delete: () =>
|
|
3499
|
+
var handlers9 = {
|
|
3500
|
+
delete: () => Effect41.void,
|
|
3367
3501
|
diff: ({ news }) => diff2(news),
|
|
3368
|
-
list: () =>
|
|
3502
|
+
list: () => Effect41.succeed([]),
|
|
3369
3503
|
read: ({ olds }) => read2(olds),
|
|
3370
3504
|
reconcile: ({ news }) => apply(news)
|
|
3371
3505
|
};
|
|
3372
|
-
var ProxmoxNetworkApplyProvider = () => Provider15.effect(ProxmoxNetworkApply,
|
|
3506
|
+
var ProxmoxNetworkApplyProvider = () => Provider15.effect(ProxmoxNetworkApply, Effect41.succeed(ProxmoxNetworkApply.Provider.of(handlers9)));
|
|
3373
3507
|
// src/proxmox/node-network.ts
|
|
3374
3508
|
import { Resource as Resource16 } from "alchemy";
|
|
3375
|
-
import { isResolved as
|
|
3509
|
+
import { isResolved as isResolved12 } from "alchemy/Diff";
|
|
3376
3510
|
import * as Provider16 from "alchemy/Provider";
|
|
3377
|
-
import * as
|
|
3378
|
-
import * as
|
|
3511
|
+
import * as nodes9 from "@distilled.cloud/proxmox/nodes";
|
|
3512
|
+
import * as Effect43 from "effect/Effect";
|
|
3379
3513
|
|
|
3380
3514
|
// src/proxmox/node-network-form.ts
|
|
3381
3515
|
var NODE_NETWORK_CREATE = "pve:POST /nodes/{node}/network";
|
|
@@ -3417,9 +3551,9 @@ var updateForm2 = (props) => {
|
|
|
3417
3551
|
};
|
|
3418
3552
|
|
|
3419
3553
|
// src/proxmox/node-network-wire.ts
|
|
3420
|
-
import * as
|
|
3421
|
-
import * as
|
|
3422
|
-
var
|
|
3554
|
+
import * as nodes8 from "@distilled.cloud/proxmox/nodes";
|
|
3555
|
+
import * as Effect42 from "effect/Effect";
|
|
3556
|
+
var attributesOf6 = (live, props) => {
|
|
3423
3557
|
const data = live;
|
|
3424
3558
|
const liveType = text(data["type"]);
|
|
3425
3559
|
if (liveType !== "" && liveType !== props.type)
|
|
@@ -3452,16 +3586,16 @@ var attributesOf5 = (live, props) => {
|
|
|
3452
3586
|
"vlan-raw-device": text(data["vlan-raw-device"])
|
|
3453
3587
|
};
|
|
3454
3588
|
};
|
|
3455
|
-
var readInterfaceOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false,
|
|
3589
|
+
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
3590
|
if (live === UNREADABLE)
|
|
3457
3591
|
return UNREADABLE;
|
|
3458
|
-
return live === undefined ? undefined :
|
|
3592
|
+
return live === undefined ? undefined : attributesOf6(live, props);
|
|
3459
3593
|
}));
|
|
3460
|
-
var readInterface = (props) => readInterfaceOrFail(props).pipe(
|
|
3594
|
+
var readInterface = (props) => readInterfaceOrFail(props).pipe(Effect42.orElseSucceed(() => {
|
|
3461
3595
|
return;
|
|
3462
3596
|
}));
|
|
3463
3597
|
var dropUnreadable4 = (live) => live === UNREADABLE ? undefined : live;
|
|
3464
|
-
var
|
|
3598
|
+
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
3599
|
|
|
3466
3600
|
// src/proxmox/node-network.ts
|
|
3467
3601
|
var ProxmoxNodeNetwork = Resource16("Proxmox.NodeNetwork", {
|
|
@@ -3473,22 +3607,22 @@ var RENAMED = {
|
|
|
3473
3607
|
"vlan-raw-device": "vlan_raw_device"
|
|
3474
3608
|
};
|
|
3475
3609
|
var toDistilled = (form) => Object.fromEntries(Object.entries(form).map(([key, value]) => [RENAMED[key] ?? key, value]));
|
|
3476
|
-
var
|
|
3610
|
+
var toDistilledCreate3 = (props) => ({
|
|
3477
3611
|
...toDistilled(createForm5(props)),
|
|
3478
3612
|
node: props.node
|
|
3479
3613
|
});
|
|
3480
|
-
var
|
|
3614
|
+
var toDistilledUpdate2 = (props) => ({
|
|
3481
3615
|
...toDistilled(updateForm2(props)),
|
|
3482
3616
|
iface: props.iface,
|
|
3483
3617
|
node: props.node
|
|
3484
3618
|
});
|
|
3485
|
-
var ProxmoxNodeNetworkProvider = () => Provider16.effect(ProxmoxNodeNetwork,
|
|
3486
|
-
list: () =>
|
|
3487
|
-
read:
|
|
3619
|
+
var ProxmoxNodeNetworkProvider = () => Provider16.effect(ProxmoxNodeNetwork, Effect43.succeed(ProxmoxNodeNetwork.Provider.of({
|
|
3620
|
+
list: () => Effect43.succeed([]),
|
|
3621
|
+
read: Effect43.fn(function* ({ olds, output }) {
|
|
3488
3622
|
return dropUnreadable4(yield* output === undefined ? readInterface(olds) : readInterfaceOrFail(olds));
|
|
3489
3623
|
}),
|
|
3490
|
-
diff:
|
|
3491
|
-
if (!
|
|
3624
|
+
diff: Effect43.fn(function* ({ news, output }) {
|
|
3625
|
+
if (!isResolved12(news))
|
|
3492
3626
|
return;
|
|
3493
3627
|
yield* guardWrite2(NODE_NETWORK_CREATE, createForm5(news), output === undefined);
|
|
3494
3628
|
yield* guardWrite2(NODE_NETWORK_UPDATE, updateForm2(news), false);
|
|
@@ -3503,31 +3637,31 @@ var ProxmoxNodeNetworkProvider = () => Provider16.effect(ProxmoxNodeNetwork, Eff
|
|
|
3503
3637
|
yield* guardWrite2(NODE_NETWORK_CREATE, createForm5(news), true);
|
|
3504
3638
|
return { action: "update" };
|
|
3505
3639
|
}
|
|
3506
|
-
return
|
|
3640
|
+
return matches5(live, news) ? { action: "noop" } : { action: "update" };
|
|
3507
3641
|
}),
|
|
3508
|
-
reconcile:
|
|
3642
|
+
reconcile: Effect43.fn(function* ({ news }) {
|
|
3509
3643
|
const before = dropUnreadable4(yield* readInterface(news));
|
|
3510
3644
|
yield* guardWrite2(NODE_NETWORK_CREATE, createForm5(news), before === undefined);
|
|
3511
3645
|
yield* guardWrite2(NODE_NETWORK_UPDATE, updateForm2(news), false);
|
|
3512
3646
|
if (before === undefined) {
|
|
3513
|
-
yield* runPve(news.target, "provision", true,
|
|
3514
|
-
} else if (!
|
|
3515
|
-
yield* runPve(news.target, "provision", true,
|
|
3647
|
+
yield* runPve(news.target, "provision", true, nodes9.createNodeNetwork(toDistilledCreate3(news)));
|
|
3648
|
+
} else if (!matches5(before, news)) {
|
|
3649
|
+
yield* runPve(news.target, "provision", true, nodes9.putNodeNetwork2(toDistilledUpdate2(news)));
|
|
3516
3650
|
}
|
|
3517
3651
|
const after = dropUnreadable4(yield* readInterface(news));
|
|
3518
3652
|
if (after === undefined) {
|
|
3519
|
-
return yield*
|
|
3653
|
+
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
3654
|
}
|
|
3521
3655
|
return after;
|
|
3522
3656
|
}),
|
|
3523
|
-
delete:
|
|
3524
|
-
yield* runPve(olds.target, "provision", true,
|
|
3657
|
+
delete: Effect43.fn(function* ({ olds }) {
|
|
3658
|
+
yield* runPve(olds.target, "provision", true, nodes9.deleteNodeNetwork2({ iface: olds.iface, node: olds.node }));
|
|
3525
3659
|
})
|
|
3526
3660
|
})));
|
|
3527
3661
|
// src/proxmox/notification-matcher.ts
|
|
3528
3662
|
import { Resource as Resource17 } from "alchemy";
|
|
3529
3663
|
import * as Provider17 from "alchemy/Provider";
|
|
3530
|
-
import * as
|
|
3664
|
+
import * as Effect44 from "effect/Effect";
|
|
3531
3665
|
|
|
3532
3666
|
// src/proxmox/notification-matcher-form.ts
|
|
3533
3667
|
var list = (value) => Array.isArray(value) ? value.map((item) => String(item)) : [];
|
|
@@ -3605,7 +3739,7 @@ var matcherUpdateForm = (fields) => {
|
|
|
3605
3739
|
|
|
3606
3740
|
// src/proxmox/notification-matcher.ts
|
|
3607
3741
|
var ProxmoxNotificationMatcher = Resource17("Proxmox.NotificationMatcher");
|
|
3608
|
-
var
|
|
3742
|
+
var handlers10 = pveHandlers({
|
|
3609
3743
|
attributes: (live, props) => matcherAttributes(live, props.name),
|
|
3610
3744
|
collection: () => "cluster/notifications/matchers",
|
|
3611
3745
|
createForm: matcherCreateForm,
|
|
@@ -3617,11 +3751,11 @@ var handlers11 = pveHandlers({
|
|
|
3617
3751
|
path: (props) => `cluster/notifications/matchers/${props.name}`,
|
|
3618
3752
|
updateForm: matcherUpdateForm
|
|
3619
3753
|
});
|
|
3620
|
-
var ProxmoxNotificationMatcherProvider = () => Provider17.effect(ProxmoxNotificationMatcher,
|
|
3754
|
+
var ProxmoxNotificationMatcherProvider = () => Provider17.effect(ProxmoxNotificationMatcher, Effect44.succeed(ProxmoxNotificationMatcher.Provider.of(handlers10)));
|
|
3621
3755
|
// src/proxmox/notification-target.ts
|
|
3622
3756
|
import { Resource as Resource18 } from "alchemy";
|
|
3623
3757
|
import * as Provider18 from "alchemy/Provider";
|
|
3624
|
-
import * as
|
|
3758
|
+
import * as Effect45 from "effect/Effect";
|
|
3625
3759
|
|
|
3626
3760
|
// src/proxmox/notification-target-endpoint.ts
|
|
3627
3761
|
var ENDPOINTS = {
|
|
@@ -3677,7 +3811,7 @@ var createShape = (props) => ({
|
|
|
3677
3811
|
|
|
3678
3812
|
// src/proxmox/notification-target.ts
|
|
3679
3813
|
var ProxmoxNotificationTarget = Resource18("Proxmox.NotificationTarget");
|
|
3680
|
-
var
|
|
3814
|
+
var handlers11 = pveHandlers({
|
|
3681
3815
|
attributes: (live, props) => ({
|
|
3682
3816
|
author: text(live["author"]),
|
|
3683
3817
|
body: text(live["body"]),
|
|
@@ -3703,15 +3837,15 @@ var handlers12 = pveHandlers({
|
|
|
3703
3837
|
path: (props) => `cluster/notifications/endpoints/${props.type}/${props.name}`,
|
|
3704
3838
|
updateForm: shape4
|
|
3705
3839
|
});
|
|
3706
|
-
var ProxmoxNotificationTargetProvider = () => Provider18.effect(ProxmoxNotificationTarget,
|
|
3840
|
+
var ProxmoxNotificationTargetProvider = () => Provider18.effect(ProxmoxNotificationTarget, Effect45.succeed(ProxmoxNotificationTarget.Provider.of(handlers11)));
|
|
3707
3841
|
// src/proxmox/pbs-datastore.ts
|
|
3708
3842
|
import { Resource as Resource19 } from "alchemy";
|
|
3709
|
-
import { isResolved as
|
|
3843
|
+
import { isResolved as isResolved13 } from "alchemy/Diff";
|
|
3710
3844
|
import * as Provider19 from "alchemy/Provider";
|
|
3711
|
-
import * as
|
|
3845
|
+
import * as Effect49 from "effect/Effect";
|
|
3712
3846
|
|
|
3713
3847
|
// src/proxmox/pbs-datastore-form.ts
|
|
3714
|
-
import * as
|
|
3848
|
+
import * as Effect46 from "effect/Effect";
|
|
3715
3849
|
var attributes = (live, props) => ({
|
|
3716
3850
|
backend: propertyString(live["backend"]),
|
|
3717
3851
|
comment: text(live["comment"]),
|
|
@@ -3734,7 +3868,7 @@ var attributes = (live, props) => ({
|
|
|
3734
3868
|
"verify-new": bool(live["verify-new"])
|
|
3735
3869
|
});
|
|
3736
3870
|
var same4 = (declared, live) => declared === undefined || declared === live;
|
|
3737
|
-
var
|
|
3871
|
+
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
3872
|
var set2 = (key, value) => value === undefined ? {} : { [key]: value };
|
|
3739
3873
|
var count = (value) => value === undefined ? undefined : String(value);
|
|
3740
3874
|
var mutable = (props) => ({
|
|
@@ -3763,35 +3897,35 @@ var createForm6 = (props) => ({
|
|
|
3763
3897
|
});
|
|
3764
3898
|
var updateForm3 = (props) => mutable(props);
|
|
3765
3899
|
var object2 = (props) => `config/datastore/${props.name}`;
|
|
3766
|
-
var readOne = (props) => pve(props.target, "read", "GET", object2(props)).pipe(
|
|
3900
|
+
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
3901
|
return;
|
|
3768
3902
|
}));
|
|
3769
3903
|
|
|
3770
3904
|
// src/proxmox/pbs-datastore-guard.ts
|
|
3771
|
-
import * as
|
|
3905
|
+
import * as Effect47 from "effect/Effect";
|
|
3772
3906
|
var samePath = (left, right) => {
|
|
3773
3907
|
const trim = (value) => value.length > 1 ? value.replace(/\/+$/, "") : value;
|
|
3774
3908
|
return trim(left) === trim(right);
|
|
3775
3909
|
};
|
|
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) =>
|
|
3910
|
+
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."));
|
|
3911
|
+
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."));
|
|
3912
|
+
var settle3 = (read3, done) => Effect47.gen(function* () {
|
|
3779
3913
|
for (let attempt = 0;attempt < 60; attempt += 1) {
|
|
3780
3914
|
const live = yield* read3;
|
|
3781
3915
|
if (done(live))
|
|
3782
3916
|
return live;
|
|
3783
|
-
yield*
|
|
3917
|
+
yield* Effect47.sleep("2 seconds");
|
|
3784
3918
|
}
|
|
3785
3919
|
return yield* read3;
|
|
3786
3920
|
});
|
|
3787
3921
|
|
|
3788
3922
|
// src/proxmox/pbs-datastore-endpoint.ts
|
|
3789
|
-
import * as
|
|
3923
|
+
import * as Effect48 from "effect/Effect";
|
|
3790
3924
|
var DATASTORE_ENDPOINT = {
|
|
3791
3925
|
create: "pbs:POST /config/datastore",
|
|
3792
3926
|
update: "pbs:PUT /config/datastore/{name}"
|
|
3793
3927
|
};
|
|
3794
|
-
var guardDatastoreForms = (props) =>
|
|
3928
|
+
var guardDatastoreForms = (props) => Effect48.gen(function* () {
|
|
3795
3929
|
yield* guardForm(DATASTORE_ENDPOINT.create, createForm6(props), true);
|
|
3796
3930
|
yield* guardForm(DATASTORE_ENDPOINT.update, updateForm3(props), false);
|
|
3797
3931
|
});
|
|
@@ -3800,11 +3934,11 @@ var guardDatastoreForms = (props) => Effect45.gen(function* () {
|
|
|
3800
3934
|
var PbsDatastore = Resource19("Pbs.Datastore", {
|
|
3801
3935
|
defaultRemovalPolicy: "retain"
|
|
3802
3936
|
});
|
|
3803
|
-
var PbsDatastoreProvider = () => Provider19.effect(PbsDatastore,
|
|
3804
|
-
list: () =>
|
|
3937
|
+
var PbsDatastoreProvider = () => Provider19.effect(PbsDatastore, Effect49.succeed(PbsDatastore.Provider.of({
|
|
3938
|
+
list: () => Effect49.succeed([]),
|
|
3805
3939
|
read: ({ olds }) => readOne(olds),
|
|
3806
|
-
diff: ({ news, output }) =>
|
|
3807
|
-
if (!
|
|
3940
|
+
diff: ({ news, output }) => Effect49.gen(function* () {
|
|
3941
|
+
if (!isResolved13(news))
|
|
3808
3942
|
return;
|
|
3809
3943
|
yield* guardDatastoreForms(news);
|
|
3810
3944
|
if (output === undefined)
|
|
@@ -3814,9 +3948,9 @@ var PbsDatastoreProvider = () => Provider19.effect(PbsDatastore, Effect46.succee
|
|
|
3814
3948
|
return { action: "update" };
|
|
3815
3949
|
yield* guardPath(live, news);
|
|
3816
3950
|
yield* guardBackend(live, news);
|
|
3817
|
-
return
|
|
3951
|
+
return matches6(live, news) ? { action: "noop" } : { action: "update" };
|
|
3818
3952
|
}),
|
|
3819
|
-
reconcile:
|
|
3953
|
+
reconcile: Effect49.fn(function* ({ news }) {
|
|
3820
3954
|
yield* guardDatastoreForms(news);
|
|
3821
3955
|
const live = yield* readOne(news);
|
|
3822
3956
|
let upid;
|
|
@@ -3825,31 +3959,31 @@ var PbsDatastoreProvider = () => Provider19.effect(PbsDatastore, Effect46.succee
|
|
|
3825
3959
|
} else {
|
|
3826
3960
|
yield* guardPath(live, news);
|
|
3827
3961
|
yield* guardBackend(live, news);
|
|
3828
|
-
const form = formToSend(
|
|
3962
|
+
const form = formToSend(matches6, live, news, updateForm3(news));
|
|
3829
3963
|
if (form !== undefined) {
|
|
3830
3964
|
yield* pve(news.target, "provision", "PUT", object2(news), form);
|
|
3831
3965
|
}
|
|
3832
3966
|
}
|
|
3833
|
-
const after = yield* settle3(readOne(news), (row) => row !== undefined &&
|
|
3834
|
-
if (after === undefined || !
|
|
3835
|
-
return yield*
|
|
3967
|
+
const after = yield* settle3(readOne(news), (row) => row !== undefined && matches6(row, news));
|
|
3968
|
+
if (after === undefined || !matches6(after, news)) {
|
|
3969
|
+
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
3970
|
}
|
|
3837
3971
|
return after;
|
|
3838
3972
|
}),
|
|
3839
|
-
delete:
|
|
3973
|
+
delete: Effect49.fn(function* ({ olds }) {
|
|
3840
3974
|
yield* pve(olds.target, "provision", "DELETE", object2(olds));
|
|
3841
3975
|
const left = yield* settle3(readOne(olds), (row) => row === undefined);
|
|
3842
3976
|
if (left !== undefined) {
|
|
3843
|
-
return yield*
|
|
3977
|
+
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
3978
|
}
|
|
3845
3979
|
})
|
|
3846
3980
|
})));
|
|
3847
3981
|
// src/proxmox/pbs-notification-matcher.ts
|
|
3848
3982
|
import { Resource as Resource20 } from "alchemy";
|
|
3849
3983
|
import * as Provider20 from "alchemy/Provider";
|
|
3850
|
-
import * as
|
|
3984
|
+
import * as Effect50 from "effect/Effect";
|
|
3851
3985
|
var PbsNotificationMatcher = Resource20("Pbs.NotificationMatcher");
|
|
3852
|
-
var
|
|
3986
|
+
var handlers12 = pveHandlers({
|
|
3853
3987
|
attributes: (live, props) => matcherAttributes(live, props.name),
|
|
3854
3988
|
collection: () => "config/notifications/matchers",
|
|
3855
3989
|
createForm: matcherCreateForm,
|
|
@@ -3861,15 +3995,15 @@ var handlers13 = pveHandlers({
|
|
|
3861
3995
|
path: (props) => `config/notifications/matchers/${props.name}`,
|
|
3862
3996
|
updateForm: matcherUpdateForm
|
|
3863
3997
|
});
|
|
3864
|
-
var PbsNotificationMatcherProvider = () => Provider20.effect(PbsNotificationMatcher,
|
|
3998
|
+
var PbsNotificationMatcherProvider = () => Provider20.effect(PbsNotificationMatcher, Effect50.succeed(PbsNotificationMatcher.Provider.of(handlers12)));
|
|
3865
3999
|
// src/proxmox/pbs-notification-target.ts
|
|
3866
4000
|
import { Resource as Resource21 } from "alchemy";
|
|
3867
4001
|
import * as Provider21 from "alchemy/Provider";
|
|
3868
|
-
import * as
|
|
4002
|
+
import * as Effect52 from "effect/Effect";
|
|
3869
4003
|
|
|
3870
4004
|
// src/proxmox/pbs-notification-target-lifecycle.ts
|
|
3871
|
-
import { isResolved as
|
|
3872
|
-
import * as
|
|
4005
|
+
import { isResolved as isResolved14 } from "alchemy/Diff";
|
|
4006
|
+
import * as Effect51 from "effect/Effect";
|
|
3873
4007
|
|
|
3874
4008
|
// src/proxmox/pbs-notification-target-endpoint.ts
|
|
3875
4009
|
var ENDPOINTS2 = {
|
|
@@ -4146,12 +4280,12 @@ var targetForm = (props, groups, carry, mode) => {
|
|
|
4146
4280
|
|
|
4147
4281
|
// src/proxmox/pbs-notification-target-lifecycle.ts
|
|
4148
4282
|
var path = (props) => `config/notifications/endpoints/${props.type}/${props.name}`;
|
|
4149
|
-
var readLive2 = (props) => pve(props.target, "read", "GET", path(props)).pipe(
|
|
4283
|
+
var readLive2 = (props) => pve(props.target, "read", "GET", path(props)).pipe(Effect51.map((data) => data == null ? undefined : targetAttributes(data, props)), Effect51.orElseSucceed(() => {
|
|
4150
4284
|
return;
|
|
4151
4285
|
}));
|
|
4152
4286
|
var refuse2 = (props) => {
|
|
4153
4287
|
const reasons = refusals(props);
|
|
4154
|
-
return reasons.length === 0 ?
|
|
4288
|
+
return reasons.length === 0 ? Effect51.void : Effect51.die(new Error(`${path(props)}: ${reasons.join("; ")}.`));
|
|
4155
4289
|
};
|
|
4156
4290
|
var toCarry = (live, props, groups, sealed) => {
|
|
4157
4291
|
const header = headerState(live, props, groups) === "stale";
|
|
@@ -4163,17 +4297,17 @@ var requireValues = (props, groups, carry) => {
|
|
|
4163
4297
|
...carry.header ? groups.header.missing : [],
|
|
4164
4298
|
...carry.sealed ? groups.sealed.missing : []
|
|
4165
4299
|
];
|
|
4166
|
-
return missing.length === 0 ?
|
|
4300
|
+
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
4301
|
};
|
|
4168
|
-
var
|
|
4169
|
-
list: () =>
|
|
4170
|
-
read: ({ olds, output }) => (output === undefined ? refuse2(olds) :
|
|
4302
|
+
var handlers13 = {
|
|
4303
|
+
list: () => Effect51.succeed([]),
|
|
4304
|
+
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
4305
|
diff: ({
|
|
4172
4306
|
news,
|
|
4173
4307
|
olds,
|
|
4174
4308
|
output
|
|
4175
|
-
}) =>
|
|
4176
|
-
if (output === undefined || !
|
|
4309
|
+
}) => Effect51.gen(function* () {
|
|
4310
|
+
if (output === undefined || !isResolved14(news))
|
|
4177
4311
|
return;
|
|
4178
4312
|
if (olds !== undefined && (olds.type !== news.type || olds.name !== news.name)) {
|
|
4179
4313
|
return { action: "replace", deleteFirst: olds.name === news.name };
|
|
@@ -4184,7 +4318,7 @@ var handlers14 = {
|
|
|
4184
4318
|
return { action: "update" };
|
|
4185
4319
|
return toCarry(live, news, resolveGroups(news), output.sealed) === undefined ? { action: "noop" } : { action: "update" };
|
|
4186
4320
|
}),
|
|
4187
|
-
reconcile: ({ news, output }) =>
|
|
4321
|
+
reconcile: ({ news, output }) => Effect51.gen(function* () {
|
|
4188
4322
|
yield* refuse2(news);
|
|
4189
4323
|
const groups = resolveGroups(news);
|
|
4190
4324
|
const live = yield* readLive2(news);
|
|
@@ -4192,10 +4326,10 @@ var handlers14 = {
|
|
|
4192
4326
|
if (live === undefined) {
|
|
4193
4327
|
const carry = { header: true, sealed: true };
|
|
4194
4328
|
yield* requireValues(news, groups, carry);
|
|
4195
|
-
const
|
|
4329
|
+
const collection = `config/notifications/endpoints/${news.type}`;
|
|
4196
4330
|
const form = targetForm(news, groups, carry, "create");
|
|
4197
4331
|
yield* guardForm(pbsTargetEndpoint(news).create, form, true);
|
|
4198
|
-
yield* pve(news.target, "provision", "POST",
|
|
4332
|
+
yield* pve(news.target, "provision", "POST", collection, form);
|
|
4199
4333
|
sealed = seal(groups.sealed.values);
|
|
4200
4334
|
} else {
|
|
4201
4335
|
const carry = toCarry(live, news, groups, sealed);
|
|
@@ -4210,7 +4344,7 @@ var handlers14 = {
|
|
|
4210
4344
|
}
|
|
4211
4345
|
const after = yield* readLive2(news);
|
|
4212
4346
|
if (after === undefined) {
|
|
4213
|
-
return yield*
|
|
4347
|
+
return yield* Effect51.die(new Error(`${path(news)}: the write returned no error but the target is still absent.`));
|
|
4214
4348
|
}
|
|
4215
4349
|
return { ...after, sealed };
|
|
4216
4350
|
}),
|
|
@@ -4219,11 +4353,11 @@ var handlers14 = {
|
|
|
4219
4353
|
|
|
4220
4354
|
// src/proxmox/pbs-notification-target.ts
|
|
4221
4355
|
var PbsNotificationTarget = Resource21("Pbs.NotificationTarget");
|
|
4222
|
-
var PbsNotificationTargetProvider = () => Provider21.effect(PbsNotificationTarget,
|
|
4356
|
+
var PbsNotificationTargetProvider = () => Provider21.effect(PbsNotificationTarget, Effect52.succeed(PbsNotificationTarget.Provider.of(handlers13)));
|
|
4223
4357
|
// src/proxmox/pbs-prune-job.ts
|
|
4224
4358
|
import { Resource as Resource22 } from "alchemy";
|
|
4225
4359
|
import * as Provider22 from "alchemy/Provider";
|
|
4226
|
-
import * as
|
|
4360
|
+
import * as Effect53 from "effect/Effect";
|
|
4227
4361
|
|
|
4228
4362
|
// src/proxmox/pbs-prune-job-form.ts
|
|
4229
4363
|
var KEEP_KEYS = [
|
|
@@ -4281,7 +4415,7 @@ var updateBody2 = (props) => {
|
|
|
4281
4415
|
// src/proxmox/pbs-prune-job.ts
|
|
4282
4416
|
var PbsPruneJob = Resource22("Pbs.PruneJob");
|
|
4283
4417
|
var same6 = (declared2, live) => declared2 === undefined || declared2 === live;
|
|
4284
|
-
var
|
|
4418
|
+
var handlers14 = pveHandlers({
|
|
4285
4419
|
attributes: (live, props) => ({
|
|
4286
4420
|
comment: text(live["comment"], ""),
|
|
4287
4421
|
disable: bool(live["disable"], false),
|
|
@@ -4299,11 +4433,11 @@ var handlers15 = pveHandlers({
|
|
|
4299
4433
|
path: (props) => `config/prune/${props.id}`,
|
|
4300
4434
|
updateForm: updateBody2
|
|
4301
4435
|
});
|
|
4302
|
-
var PbsPruneJobProvider = () => Provider22.effect(PbsPruneJob,
|
|
4436
|
+
var PbsPruneJobProvider = () => Provider22.effect(PbsPruneJob, Effect53.succeed(PbsPruneJob.Provider.of(handlers14)));
|
|
4303
4437
|
// src/proxmox/pbs-sync-job.ts
|
|
4304
4438
|
import { Resource as Resource23 } from "alchemy";
|
|
4305
4439
|
import * as Provider23 from "alchemy/Provider";
|
|
4306
|
-
import * as
|
|
4440
|
+
import * as Effect54 from "effect/Effect";
|
|
4307
4441
|
|
|
4308
4442
|
// src/proxmox/pbs-sync-job-form.ts
|
|
4309
4443
|
var UNSET4 = -1;
|
|
@@ -4351,7 +4485,7 @@ var createBody4 = (props) => ({
|
|
|
4351
4485
|
...props["sync-direction"] === undefined ? {} : { "sync-direction": props["sync-direction"] }
|
|
4352
4486
|
});
|
|
4353
4487
|
var updateBody3 = (props) => shape6(props);
|
|
4354
|
-
var
|
|
4488
|
+
var handlers15 = pveHandlers({
|
|
4355
4489
|
attributes: (live, props) => ({
|
|
4356
4490
|
comment: text(live["comment"], ""),
|
|
4357
4491
|
"group-filter": filters(live["group-filter"]),
|
|
@@ -4380,11 +4514,11 @@ var handlers16 = pveHandlers({
|
|
|
4380
4514
|
|
|
4381
4515
|
// src/proxmox/pbs-sync-job.ts
|
|
4382
4516
|
var PbsSyncJob = Resource23("Pbs.SyncJob");
|
|
4383
|
-
var PbsSyncJobProvider = () => Provider23.effect(PbsSyncJob,
|
|
4517
|
+
var PbsSyncJobProvider = () => Provider23.effect(PbsSyncJob, Effect54.succeed(PbsSyncJob.Provider.of(handlers15)));
|
|
4384
4518
|
// src/proxmox/pbs-verify-job.ts
|
|
4385
4519
|
import { Resource as Resource24 } from "alchemy";
|
|
4386
4520
|
import * as Provider24 from "alchemy/Provider";
|
|
4387
|
-
import * as
|
|
4521
|
+
import * as Effect55 from "effect/Effect";
|
|
4388
4522
|
|
|
4389
4523
|
// src/proxmox/pbs-verify-job-form.ts
|
|
4390
4524
|
var rechecks = (ignoreVerified, outdatedAfter) => {
|
|
@@ -4406,7 +4540,7 @@ var shape7 = (props) => ({
|
|
|
4406
4540
|
|
|
4407
4541
|
// src/proxmox/pbs-verify-job.ts
|
|
4408
4542
|
var PbsVerifyJob = Resource24("Pbs.VerifyJob");
|
|
4409
|
-
var
|
|
4543
|
+
var handlers16 = pveHandlers({
|
|
4410
4544
|
attributes: (live, props) => {
|
|
4411
4545
|
const ignoreVerified = bool(live["ignore-verified"], true);
|
|
4412
4546
|
const outdatedAfter = int(live["outdated-after"], -1);
|
|
@@ -4429,11 +4563,11 @@ var handlers17 = pveHandlers({
|
|
|
4429
4563
|
path: (props) => `config/verify/${props.id}`,
|
|
4430
4564
|
updateForm: shape7
|
|
4431
4565
|
});
|
|
4432
|
-
var PbsVerifyJobProvider = () => Provider24.effect(PbsVerifyJob,
|
|
4566
|
+
var PbsVerifyJobProvider = () => Provider24.effect(PbsVerifyJob, Effect55.succeed(PbsVerifyJob.Provider.of(handlers16)));
|
|
4433
4567
|
// src/proxmox/pool.ts
|
|
4434
4568
|
import { Resource as Resource25 } from "alchemy";
|
|
4435
4569
|
import * as Provider25 from "alchemy/Provider";
|
|
4436
|
-
import * as
|
|
4570
|
+
import * as Effect56 from "effect/Effect";
|
|
4437
4571
|
var ProxmoxPool = Resource25("Proxmox.Pool");
|
|
4438
4572
|
var poolCreateForm = (props) => ({
|
|
4439
4573
|
comment: props.comment ?? "",
|
|
@@ -4442,7 +4576,7 @@ var poolCreateForm = (props) => ({
|
|
|
4442
4576
|
var poolUpdateForm = (props) => ({
|
|
4443
4577
|
comment: props.comment ?? ""
|
|
4444
4578
|
});
|
|
4445
|
-
var
|
|
4579
|
+
var handlers17 = pveHandlers({
|
|
4446
4580
|
attributes: (live, props) => {
|
|
4447
4581
|
const row = live;
|
|
4448
4582
|
return {
|
|
@@ -4458,7 +4592,7 @@ var handlers18 = pveHandlers({
|
|
|
4458
4592
|
path: (props) => `pools/${props.poolid}`,
|
|
4459
4593
|
updateForm: poolUpdateForm
|
|
4460
4594
|
});
|
|
4461
|
-
var ProxmoxPoolProvider = () => Provider25.effect(ProxmoxPool,
|
|
4595
|
+
var ProxmoxPoolProvider = () => Provider25.effect(ProxmoxPool, Effect56.succeed(ProxmoxPool.Provider.of(handlers17)));
|
|
4462
4596
|
// src/proxmox/provision-baseline.ts
|
|
4463
4597
|
var PROVISION_PRIVILEGES = Object.freeze([
|
|
4464
4598
|
"Datastore.Allocate",
|
|
@@ -4651,14 +4785,14 @@ echo 'provision baseline: done. Next: point the OpenBao proxmox mount at these u
|
|
|
4651
4785
|
// src/proxmox/provision-declare.ts
|
|
4652
4786
|
import { adopt } from "alchemy/AdoptPolicy";
|
|
4653
4787
|
import * as RemovalPolicy from "alchemy/RemovalPolicy";
|
|
4654
|
-
import * as
|
|
4788
|
+
import * as Effect60 from "effect/Effect";
|
|
4655
4789
|
|
|
4656
4790
|
// src/proxmox/role.ts
|
|
4657
4791
|
import { Resource as Resource26 } from "alchemy";
|
|
4658
|
-
import { isResolved as
|
|
4792
|
+
import { isResolved as isResolved15 } from "alchemy/Diff";
|
|
4659
4793
|
import * as Provider26 from "alchemy/Provider";
|
|
4660
4794
|
import * as access5 from "@distilled.cloud/proxmox/access";
|
|
4661
|
-
import * as
|
|
4795
|
+
import * as Effect57 from "effect/Effect";
|
|
4662
4796
|
|
|
4663
4797
|
// src/proxmox/role-wire.ts
|
|
4664
4798
|
var ROLE_CREATE = "pve:POST /access/roles";
|
|
@@ -4673,28 +4807,28 @@ var updateForm4 = (props) => ({
|
|
|
4673
4807
|
roleid: props.roleid
|
|
4674
4808
|
});
|
|
4675
4809
|
var find2 = (rows, roleid) => rows.find((row) => row.roleid === roleid);
|
|
4676
|
-
var
|
|
4810
|
+
var attributesOf7 = (row, props) => ({
|
|
4677
4811
|
privs: canonical2(row.privs === undefined ? [] : row.privs.split(",")),
|
|
4678
4812
|
roleid: props.roleid
|
|
4679
4813
|
});
|
|
4680
|
-
var
|
|
4814
|
+
var matches7 = (attributes2, props) => canonical2(attributes2.privs).join(",") === canonical2(props.privs).join(",");
|
|
4681
4815
|
|
|
4682
4816
|
// src/proxmox/role.ts
|
|
4683
4817
|
var ProxmoxRole = Resource26("Proxmox.Role");
|
|
4684
|
-
var readRole = (props) => readOrUnreadable(runPve(props.target, "read", false, access5.listAccessRoles({}))).pipe(
|
|
4818
|
+
var readRole = (props) => readOrUnreadable(runPve(props.target, "read", false, access5.listAccessRoles({}))).pipe(Effect57.map((rows) => {
|
|
4685
4819
|
if (rows === UNREADABLE)
|
|
4686
4820
|
return UNREADABLE;
|
|
4687
4821
|
const row = find2(rows, props.roleid);
|
|
4688
|
-
return row === undefined ? undefined :
|
|
4822
|
+
return row === undefined ? undefined : attributesOf7(row, props);
|
|
4689
4823
|
}));
|
|
4690
4824
|
var dropUnreadable5 = (live) => live === UNREADABLE ? undefined : live;
|
|
4691
|
-
var ProxmoxRoleProvider = () => Provider26.effect(ProxmoxRole,
|
|
4692
|
-
list: () =>
|
|
4693
|
-
read:
|
|
4825
|
+
var ProxmoxRoleProvider = () => Provider26.effect(ProxmoxRole, Effect57.succeed(ProxmoxRole.Provider.of({
|
|
4826
|
+
list: () => Effect57.succeed([]),
|
|
4827
|
+
read: Effect57.fn(function* ({ olds }) {
|
|
4694
4828
|
return dropUnreadable5(yield* readRole(olds));
|
|
4695
4829
|
}),
|
|
4696
|
-
diff:
|
|
4697
|
-
if (!
|
|
4830
|
+
diff: Effect57.fn(function* ({ news, output }) {
|
|
4831
|
+
if (!isResolved15(news))
|
|
4698
4832
|
return;
|
|
4699
4833
|
yield* guardWrite2(ROLE_CREATE, createForm7(news), output === undefined);
|
|
4700
4834
|
yield* guardWrite2(ROLE_UPDATE, updateForm4(news), false);
|
|
@@ -4709,38 +4843,38 @@ var ProxmoxRoleProvider = () => Provider26.effect(ProxmoxRole, Effect54.succeed(
|
|
|
4709
4843
|
yield* guardWrite2(ROLE_CREATE, createForm7(news), true);
|
|
4710
4844
|
return { action: "update" };
|
|
4711
4845
|
}
|
|
4712
|
-
return
|
|
4846
|
+
return matches7(live, news) ? { action: "noop" } : { action: "update" };
|
|
4713
4847
|
}),
|
|
4714
|
-
reconcile:
|
|
4848
|
+
reconcile: Effect57.fn(function* ({ news }) {
|
|
4715
4849
|
const before = dropUnreadable5(yield* readRole(news));
|
|
4716
4850
|
yield* guardWrite2(ROLE_CREATE, createForm7(news), before === undefined);
|
|
4717
4851
|
yield* guardWrite2(ROLE_UPDATE, updateForm4(news), false);
|
|
4718
4852
|
if (before === undefined) {
|
|
4719
4853
|
yield* runPve(news.target, "provision", true, access5.createAccessRole(createForm7(news)));
|
|
4720
|
-
} else if (!
|
|
4854
|
+
} else if (!matches7(before, news)) {
|
|
4721
4855
|
yield* runPve(news.target, "provision", true, access5.putAccessRole(updateForm4(news)));
|
|
4722
4856
|
}
|
|
4723
4857
|
const after = dropUnreadable5(yield* readRole(news));
|
|
4724
4858
|
if (after === undefined) {
|
|
4725
|
-
return yield*
|
|
4859
|
+
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
4860
|
}
|
|
4727
4861
|
return after;
|
|
4728
4862
|
}),
|
|
4729
|
-
delete:
|
|
4863
|
+
delete: Effect57.fn(function* ({ olds }) {
|
|
4730
4864
|
yield* runPve(olds.target, "provision", true, access5.deleteAccessRole({ roleid: olds.roleid }));
|
|
4731
4865
|
})
|
|
4732
4866
|
})));
|
|
4733
4867
|
|
|
4734
4868
|
// src/proxmox/user.ts
|
|
4735
4869
|
import { Resource as Resource27 } from "alchemy";
|
|
4736
|
-
import { isResolved as
|
|
4870
|
+
import { isResolved as isResolved16 } from "alchemy/Diff";
|
|
4737
4871
|
import * as Provider27 from "alchemy/Provider";
|
|
4738
4872
|
import * as access7 from "@distilled.cloud/proxmox/access";
|
|
4739
|
-
import * as
|
|
4873
|
+
import * as Effect59 from "effect/Effect";
|
|
4740
4874
|
|
|
4741
4875
|
// src/proxmox/user-wire.ts
|
|
4742
4876
|
import * as access6 from "@distilled.cloud/proxmox/access";
|
|
4743
|
-
import * as
|
|
4877
|
+
import * as Effect58 from "effect/Effect";
|
|
4744
4878
|
var USER_CREATE = "pve:POST /access/users";
|
|
4745
4879
|
var USER_UPDATE = "pve:PUT /access/users/{userid}";
|
|
4746
4880
|
var int2 = (value) => {
|
|
@@ -4770,7 +4904,7 @@ var updateForm5 = (props) => ({
|
|
|
4770
4904
|
...shape8(props),
|
|
4771
4905
|
userid: props.userid
|
|
4772
4906
|
});
|
|
4773
|
-
var
|
|
4907
|
+
var attributesOf8 = (live, props) => ({
|
|
4774
4908
|
comment: text(live.comment),
|
|
4775
4909
|
email: text(live.email),
|
|
4776
4910
|
enable: bool(live.enable, true),
|
|
@@ -4781,22 +4915,22 @@ var attributesOf7 = (live, props) => ({
|
|
|
4781
4915
|
tokens: tokenNames(live.tokens),
|
|
4782
4916
|
userid: props.userid
|
|
4783
4917
|
});
|
|
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(
|
|
4918
|
+
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(",");
|
|
4919
|
+
var readUserOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, access6.getAccessUser({ userid: props.userid }))).pipe(Effect58.map((live) => live === UNREADABLE ? UNREADABLE : attributesOf8(live, props)));
|
|
4920
|
+
var readUser = (props) => readUserOrFail(props).pipe(Effect58.orElseSucceed(() => {
|
|
4787
4921
|
return;
|
|
4788
4922
|
}));
|
|
4789
4923
|
var dropUnreadable6 = (live) => live === UNREADABLE ? undefined : live;
|
|
4790
4924
|
|
|
4791
4925
|
// src/proxmox/user.ts
|
|
4792
4926
|
var ProxmoxUser = Resource27("Proxmox.User");
|
|
4793
|
-
var ProxmoxUserProvider = () => Provider27.effect(ProxmoxUser,
|
|
4794
|
-
list: () =>
|
|
4795
|
-
read:
|
|
4927
|
+
var ProxmoxUserProvider = () => Provider27.effect(ProxmoxUser, Effect59.succeed(ProxmoxUser.Provider.of({
|
|
4928
|
+
list: () => Effect59.succeed([]),
|
|
4929
|
+
read: Effect59.fn(function* ({ olds, output }) {
|
|
4796
4930
|
return dropUnreadable6(yield* output === undefined ? readUser(olds) : readUserOrFail(olds));
|
|
4797
4931
|
}),
|
|
4798
|
-
diff:
|
|
4799
|
-
if (!
|
|
4932
|
+
diff: Effect59.fn(function* ({ news, output }) {
|
|
4933
|
+
if (!isResolved16(news))
|
|
4800
4934
|
return;
|
|
4801
4935
|
yield* guardWrite2(USER_CREATE, createForm8(news), output === undefined);
|
|
4802
4936
|
yield* guardWrite2(USER_UPDATE, updateForm5(news), false);
|
|
@@ -4811,31 +4945,31 @@ var ProxmoxUserProvider = () => Provider27.effect(ProxmoxUser, Effect56.succeed(
|
|
|
4811
4945
|
yield* guardWrite2(USER_CREATE, createForm8(news), true);
|
|
4812
4946
|
return { action: "update" };
|
|
4813
4947
|
}
|
|
4814
|
-
return
|
|
4948
|
+
return matches8(live, news) ? { action: "noop" } : { action: "update" };
|
|
4815
4949
|
}),
|
|
4816
|
-
reconcile:
|
|
4950
|
+
reconcile: Effect59.fn(function* ({ news }) {
|
|
4817
4951
|
const before = dropUnreadable6(yield* readUser(news));
|
|
4818
4952
|
yield* guardWrite2(USER_CREATE, createForm8(news), before === undefined);
|
|
4819
4953
|
yield* guardWrite2(USER_UPDATE, updateForm5(news), false);
|
|
4820
4954
|
if (before === undefined) {
|
|
4821
4955
|
yield* runPve(news.target, "provision", true, access7.createAccessUser(createForm8(news)));
|
|
4822
|
-
} else if (!
|
|
4956
|
+
} else if (!matches8(before, news)) {
|
|
4823
4957
|
yield* runPve(news.target, "provision", true, access7.putAccessUser(updateForm5(news)));
|
|
4824
4958
|
}
|
|
4825
4959
|
const after = dropUnreadable6(yield* readUser(news));
|
|
4826
4960
|
if (after === undefined) {
|
|
4827
|
-
return yield*
|
|
4961
|
+
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
4962
|
}
|
|
4829
4963
|
return after;
|
|
4830
4964
|
}),
|
|
4831
|
-
delete:
|
|
4965
|
+
delete: Effect59.fn(function* ({ olds }) {
|
|
4832
4966
|
yield* runPve(olds.target, "provision", true, access7.deleteAccessUser({ userid: olds.userid }));
|
|
4833
4967
|
})
|
|
4834
4968
|
})));
|
|
4835
4969
|
|
|
4836
4970
|
// src/proxmox/provision-declare.ts
|
|
4837
|
-
var declareProvisionBaseline = (id, target, names2 = {}, options = {}) =>
|
|
4838
|
-
const baseline = yield*
|
|
4971
|
+
var declareProvisionBaseline = (id, target, names2 = {}, options = {}) => Effect60.gen(function* () {
|
|
4972
|
+
const baseline = yield* Effect60.try({
|
|
4839
4973
|
catch: (cause) => cause instanceof Error ? cause : new Error(String(cause)),
|
|
4840
4974
|
try: () => provisionBaseline(names2)
|
|
4841
4975
|
});
|
|
@@ -4849,13 +4983,13 @@ var declareProvisionBaseline = (id, target, names2 = {}, options = {}) => Effect
|
|
|
4849
4983
|
target
|
|
4850
4984
|
}));
|
|
4851
4985
|
const group = yield* owned(ProxmoxGroup(`${id}-mint-group`, { ...baseline.group, target }));
|
|
4852
|
-
const users = yield*
|
|
4986
|
+
const users = yield* Effect60.all(baseline.users.map((user) => owned(ProxmoxUser(`${id}-${user.lane}-user`, {
|
|
4853
4987
|
comment: user.comment,
|
|
4854
4988
|
groups: [...user.groups],
|
|
4855
4989
|
target,
|
|
4856
4990
|
userid: user.userid
|
|
4857
4991
|
}))));
|
|
4858
|
-
const grants = yield*
|
|
4992
|
+
const grants = yield* Effect60.all(baseline.grants.map((grant) => owned(ProxmoxAcl(`${id}-${grant.lane}-grant`, {
|
|
4859
4993
|
path: "/",
|
|
4860
4994
|
propagate: true,
|
|
4861
4995
|
roleid: grant.roleid,
|
|
@@ -4868,7 +5002,7 @@ var declareProvisionBaseline = (id, target, names2 = {}, options = {}) => Effect
|
|
|
4868
5002
|
// src/proxmox/qemu.ts
|
|
4869
5003
|
import { Resource as Resource28 } from "alchemy";
|
|
4870
5004
|
import * as Provider28 from "alchemy/Provider";
|
|
4871
|
-
import * as
|
|
5005
|
+
import * as Effect61 from "effect/Effect";
|
|
4872
5006
|
var ProxmoxVm = Resource28("Proxmox.Vm");
|
|
4873
5007
|
var createForm9 = (props) => ({ ...shape9(props), vmid: String(props.vmid) });
|
|
4874
5008
|
var shape9 = (props) => ({
|
|
@@ -4879,7 +5013,7 @@ var shape9 = (props) => ({
|
|
|
4879
5013
|
sockets: String(props.sockets ?? 1),
|
|
4880
5014
|
...props.net0 === undefined ? {} : { net0: props.net0 }
|
|
4881
5015
|
});
|
|
4882
|
-
var
|
|
5016
|
+
var handlers18 = pveHandlers({
|
|
4883
5017
|
attributes: (live, props) => ({
|
|
4884
5018
|
cores: num(live["cores"], 1),
|
|
4885
5019
|
memory: num(live["memory"], 512),
|
|
@@ -4899,11 +5033,11 @@ var handlers19 = pveHandlers({
|
|
|
4899
5033
|
path: (props) => `nodes/${props.node}/qemu/${String(props.vmid)}/config`,
|
|
4900
5034
|
updateForm: shape9
|
|
4901
5035
|
});
|
|
4902
|
-
var ProxmoxVmProvider = () => Provider28.effect(ProxmoxVm,
|
|
5036
|
+
var ProxmoxVmProvider = () => Provider28.effect(ProxmoxVm, Effect61.succeed(ProxmoxVm.Provider.of(handlers18)));
|
|
4903
5037
|
// src/proxmox/replication-job.ts
|
|
4904
5038
|
import { Resource as Resource29 } from "alchemy";
|
|
4905
5039
|
import * as Provider29 from "alchemy/Provider";
|
|
4906
|
-
import * as
|
|
5040
|
+
import * as Effect62 from "effect/Effect";
|
|
4907
5041
|
|
|
4908
5042
|
// src/proxmox/replication-job-form.ts
|
|
4909
5043
|
var DEFAULT_SCHEDULE = "*/15";
|
|
@@ -4933,7 +5067,7 @@ var rateOf = (value) => {
|
|
|
4933
5067
|
const parsed = Number.parseFloat(text(value));
|
|
4934
5068
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
4935
5069
|
};
|
|
4936
|
-
var
|
|
5070
|
+
var handlers19 = pveHandlers({
|
|
4937
5071
|
attributes: (live, props) => ({
|
|
4938
5072
|
comment: text(live["comment"], ""),
|
|
4939
5073
|
disable: bool(live["disable"], false),
|
|
@@ -4957,15 +5091,15 @@ var handlers20 = pveHandlers({
|
|
|
4957
5091
|
path: (props) => `cluster/replication/${jobId(props)}`,
|
|
4958
5092
|
updateForm: updateBody4
|
|
4959
5093
|
});
|
|
4960
|
-
var ProxmoxReplicationJobProvider = () => Provider29.effect(ProxmoxReplicationJob,
|
|
5094
|
+
var ProxmoxReplicationJobProvider = () => Provider29.effect(ProxmoxReplicationJob, Effect62.succeed(ProxmoxReplicationJob.Provider.of(handlers19)));
|
|
4961
5095
|
// src/proxmox/sdn-apply.ts
|
|
4962
5096
|
import { Resource as Resource30 } from "alchemy";
|
|
4963
|
-
import { isResolved as
|
|
5097
|
+
import { isResolved as isResolved17 } from "alchemy/Diff";
|
|
4964
5098
|
import * as Provider30 from "alchemy/Provider";
|
|
4965
|
-
import * as
|
|
5099
|
+
import * as Effect64 from "effect/Effect";
|
|
4966
5100
|
|
|
4967
5101
|
// src/proxmox/sdn-apply-read.ts
|
|
4968
|
-
import * as
|
|
5102
|
+
import * as Effect63 from "effect/Effect";
|
|
4969
5103
|
var MARKED = [
|
|
4970
5104
|
"cluster/sdn/zones",
|
|
4971
5105
|
"cluster/sdn/vnets",
|
|
@@ -4974,7 +5108,7 @@ var MARKED = [
|
|
|
4974
5108
|
"cluster/sdn/route-maps"
|
|
4975
5109
|
];
|
|
4976
5110
|
var subsystemAbsent = (error) => error instanceof PveError && error.status === 501;
|
|
4977
|
-
var markedPending = (target) =>
|
|
5111
|
+
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
5112
|
var fabricStaged = (staged, running) => {
|
|
4979
5113
|
if (staged === undefined && running === undefined)
|
|
4980
5114
|
return 0;
|
|
@@ -4982,9 +5116,9 @@ var fabricStaged = (staged, running) => {
|
|
|
4982
5116
|
return "inconsistent";
|
|
4983
5117
|
return staged === running ? 0 : 1;
|
|
4984
5118
|
};
|
|
4985
|
-
var fabricPending = (target) =>
|
|
5119
|
+
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
5120
|
const verdict = fabricStaged(staged, running);
|
|
4987
|
-
return verdict === "inconsistent" ?
|
|
5121
|
+
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
5122
|
}));
|
|
4989
5123
|
var canonical3 = (value) => JSON.stringify(sortDeep(value));
|
|
4990
5124
|
var sortDeep = (value) => {
|
|
@@ -4997,23 +5131,23 @@ var sortDeep = (value) => {
|
|
|
4997
5131
|
}
|
|
4998
5132
|
return value;
|
|
4999
5133
|
};
|
|
5000
|
-
var sdnPendingCount = (target) =>
|
|
5134
|
+
var sdnPendingCount = (target) => Effect63.all([markedPending(target), fabricPending(target)], { concurrency: "unbounded" }).pipe(Effect63.map(([marked, fabric]) => marked + fabric));
|
|
5001
5135
|
|
|
5002
5136
|
// src/proxmox/sdn-apply.ts
|
|
5003
5137
|
var ProxmoxSdnApply = Resource30("Proxmox.SdnApply");
|
|
5004
|
-
var read3 = (props) => sdnPendingCount(props.target).pipe(
|
|
5005
|
-
var ProxmoxSdnApplyProvider = () => Provider30.effect(ProxmoxSdnApply,
|
|
5006
|
-
list: () =>
|
|
5007
|
-
read:
|
|
5138
|
+
var read3 = (props) => sdnPendingCount(props.target).pipe(Effect64.map((pending) => ({ pending })));
|
|
5139
|
+
var ProxmoxSdnApplyProvider = () => Provider30.effect(ProxmoxSdnApply, Effect64.succeed(ProxmoxSdnApply.Provider.of({
|
|
5140
|
+
list: () => Effect64.succeed([]),
|
|
5141
|
+
read: Effect64.fn(function* ({ olds }) {
|
|
5008
5142
|
return yield* read3(olds);
|
|
5009
5143
|
}),
|
|
5010
|
-
diff:
|
|
5011
|
-
if (!
|
|
5144
|
+
diff: Effect64.fn(function* ({ news }) {
|
|
5145
|
+
if (!isResolved17(news))
|
|
5012
5146
|
return;
|
|
5013
5147
|
const { pending } = yield* read3(news);
|
|
5014
5148
|
return pending === 0 ? { action: "noop" } : { action: "update" };
|
|
5015
5149
|
}),
|
|
5016
|
-
reconcile:
|
|
5150
|
+
reconcile: Effect64.fn(function* ({ news }) {
|
|
5017
5151
|
const staged = yield* read3(news);
|
|
5018
5152
|
if (staged.pending === 0)
|
|
5019
5153
|
return staged;
|
|
@@ -5021,16 +5155,16 @@ var ProxmoxSdnApplyProvider = () => Provider30.effect(ProxmoxSdnApply, Effect61.
|
|
|
5021
5155
|
yield* pve(news.target, "provision", "PUT", "cluster/sdn");
|
|
5022
5156
|
const after = yield* read3(news);
|
|
5023
5157
|
if (after.pending !== 0) {
|
|
5024
|
-
return yield*
|
|
5158
|
+
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
5159
|
}
|
|
5026
5160
|
return after;
|
|
5027
5161
|
}),
|
|
5028
|
-
delete: () =>
|
|
5162
|
+
delete: () => Effect64.void
|
|
5029
5163
|
})));
|
|
5030
5164
|
// src/proxmox/sdn-subnet.ts
|
|
5031
5165
|
import { Resource as Resource31 } from "alchemy";
|
|
5032
5166
|
import * as Provider31 from "alchemy/Provider";
|
|
5033
|
-
import * as
|
|
5167
|
+
import * as Effect65 from "effect/Effect";
|
|
5034
5168
|
|
|
5035
5169
|
// src/proxmox/sdn-subnet-form.ts
|
|
5036
5170
|
var subnetId = (props) => `${props.zone}-${props.cidr.replace("/", "-")}`;
|
|
@@ -5061,7 +5195,7 @@ var updateForm6 = (props) => {
|
|
|
5061
5195
|
|
|
5062
5196
|
// src/proxmox/sdn-subnet.ts
|
|
5063
5197
|
var ProxmoxSdnSubnet = Resource31("Proxmox.SdnSubnet");
|
|
5064
|
-
var
|
|
5198
|
+
var handlers20 = pveHandlers({
|
|
5065
5199
|
attributes: (live, props) => {
|
|
5066
5200
|
const liveVnet = text(live["vnet"]);
|
|
5067
5201
|
if (liveVnet !== "" && liveVnet !== props.vnet)
|
|
@@ -5090,11 +5224,11 @@ var handlers21 = pveHandlers({
|
|
|
5090
5224
|
path: (props) => `cluster/sdn/vnets/${props.vnet}/subnets/${subnetId(props)}`,
|
|
5091
5225
|
updateForm: updateForm6
|
|
5092
5226
|
});
|
|
5093
|
-
var ProxmoxSdnSubnetProvider = () => Provider31.effect(ProxmoxSdnSubnet,
|
|
5227
|
+
var ProxmoxSdnSubnetProvider = () => Provider31.effect(ProxmoxSdnSubnet, Effect65.succeed(ProxmoxSdnSubnet.Provider.of(handlers20)));
|
|
5094
5228
|
// src/proxmox/sdn-vnet.ts
|
|
5095
5229
|
import { Resource as Resource32 } from "alchemy";
|
|
5096
5230
|
import * as Provider32 from "alchemy/Provider";
|
|
5097
|
-
import * as
|
|
5231
|
+
import * as Effect66 from "effect/Effect";
|
|
5098
5232
|
var ProxmoxSdnVnet = Resource32("Proxmox.SdnVnet");
|
|
5099
5233
|
var tagOf = (value) => typeof value === "number" ? value : undefined;
|
|
5100
5234
|
var shape11 = (props) => {
|
|
@@ -5114,7 +5248,7 @@ var updateForm7 = (props) => {
|
|
|
5114
5248
|
];
|
|
5115
5249
|
return withClears(shape11(props), unset2);
|
|
5116
5250
|
};
|
|
5117
|
-
var
|
|
5251
|
+
var handlers21 = pveHandlers({
|
|
5118
5252
|
readRole: "provision",
|
|
5119
5253
|
attributes: (live, props) => ({
|
|
5120
5254
|
alias: typeof live["alias"] === "string" ? live["alias"] : "",
|
|
@@ -5132,11 +5266,11 @@ var handlers22 = pveHandlers({
|
|
|
5132
5266
|
path: (props) => `cluster/sdn/vnets/${props.vnet}`,
|
|
5133
5267
|
updateForm: updateForm7
|
|
5134
5268
|
});
|
|
5135
|
-
var ProxmoxSdnVnetProvider = () => Provider32.effect(ProxmoxSdnVnet,
|
|
5269
|
+
var ProxmoxSdnVnetProvider = () => Provider32.effect(ProxmoxSdnVnet, Effect66.succeed(ProxmoxSdnVnet.Provider.of(handlers21)));
|
|
5136
5270
|
// src/proxmox/sdn-zone.ts
|
|
5137
5271
|
import { Resource as Resource33 } from "alchemy";
|
|
5138
5272
|
import * as Provider33 from "alchemy/Provider";
|
|
5139
|
-
import * as
|
|
5273
|
+
import * as Effect67 from "effect/Effect";
|
|
5140
5274
|
var ProxmoxSdnZone = Resource33("Proxmox.SdnZone");
|
|
5141
5275
|
var sameIpam = (live, declared2) => (live === "" ? "pve" : live) === (declared2 === undefined || declared2 === "" ? "pve" : declared2);
|
|
5142
5276
|
var body6 = (props) => ({
|
|
@@ -5157,7 +5291,7 @@ var clearable = {
|
|
|
5157
5291
|
vlan: ["dns", "dnszone", "ipam", "mtu", "nodes"],
|
|
5158
5292
|
vxlan: ["dns", "dnszone", "ipam", "mtu", "nodes"]
|
|
5159
5293
|
};
|
|
5160
|
-
var
|
|
5294
|
+
var handlers22 = pveHandlers({
|
|
5161
5295
|
readRole: "provision",
|
|
5162
5296
|
attributes: (live, props) => {
|
|
5163
5297
|
const liveType = text(live["type"]);
|
|
@@ -5189,13 +5323,13 @@ var handlers23 = pveHandlers({
|
|
|
5189
5323
|
return withClears(fields, clear);
|
|
5190
5324
|
}
|
|
5191
5325
|
});
|
|
5192
|
-
var ProxmoxSdnZoneProvider = () => Provider33.effect(ProxmoxSdnZone,
|
|
5326
|
+
var ProxmoxSdnZoneProvider = () => Provider33.effect(ProxmoxSdnZone, Effect67.succeed(ProxmoxSdnZone.Provider.of(handlers22)));
|
|
5193
5327
|
// src/proxmox/storage.ts
|
|
5194
5328
|
import { Resource as Resource34 } from "alchemy";
|
|
5195
|
-
import { isResolved as
|
|
5329
|
+
import { isResolved as isResolved18 } from "alchemy/Diff";
|
|
5196
5330
|
import * as Provider34 from "alchemy/Provider";
|
|
5197
5331
|
import * as storage2 from "@distilled.cloud/proxmox/storage";
|
|
5198
|
-
import * as
|
|
5332
|
+
import * as Effect69 from "effect/Effect";
|
|
5199
5333
|
|
|
5200
5334
|
// src/proxmox/storage-form.ts
|
|
5201
5335
|
var STORAGE_CREATE = "pve:POST /storage";
|
|
@@ -5222,17 +5356,17 @@ var updateForm8 = (props) => ({
|
|
|
5222
5356
|
...mutable2(props),
|
|
5223
5357
|
storage: props.storage
|
|
5224
5358
|
});
|
|
5225
|
-
var
|
|
5226
|
-
var
|
|
5359
|
+
var toDistilledCreate4 = (props) => underscored(createForm11(props));
|
|
5360
|
+
var toDistilledUpdate3 = (props) => underscored(updateForm8(props));
|
|
5227
5361
|
|
|
5228
5362
|
// src/proxmox/storage-wire.ts
|
|
5229
5363
|
import * as storage from "@distilled.cloud/proxmox/storage";
|
|
5230
|
-
import * as
|
|
5364
|
+
import * as Effect68 from "effect/Effect";
|
|
5231
5365
|
var str2 = (value, fallback = "") => typeof value === "string" ? value : fallback;
|
|
5232
5366
|
var tokens = (value) => value.split(",").map((part) => part.trim()).filter((part) => part !== "").sort().join(",");
|
|
5233
5367
|
var sameList2 = (declared2, live) => declared2 === undefined || tokens(declared2) === tokens(live);
|
|
5234
5368
|
var same7 = (declared2, live) => declared2 === undefined || declared2 === live;
|
|
5235
|
-
var
|
|
5369
|
+
var attributesOf9 = (live, props) => {
|
|
5236
5370
|
const data = live;
|
|
5237
5371
|
return {
|
|
5238
5372
|
comment: str2(data["comment"]),
|
|
@@ -5246,24 +5380,24 @@ var attributesOf8 = (live, props) => {
|
|
|
5246
5380
|
type: str2(data["type"], props.type)
|
|
5247
5381
|
};
|
|
5248
5382
|
};
|
|
5249
|
-
var readStorageOrFail = (props) => readOrUnreadable(runPve(props.target, "provision", false, storage.getStorage({ storage: props.storage }))).pipe(
|
|
5250
|
-
var readStorage = (props) => readStorageOrFail(props).pipe(
|
|
5383
|
+
var readStorageOrFail = (props) => readOrUnreadable(runPve(props.target, "provision", false, storage.getStorage({ storage: props.storage }))).pipe(Effect68.map((live) => live === UNREADABLE ? UNREADABLE : attributesOf9(live, props)));
|
|
5384
|
+
var readStorage = (props) => readStorageOrFail(props).pipe(Effect68.orElseSucceed(() => {
|
|
5251
5385
|
return;
|
|
5252
5386
|
}));
|
|
5253
5387
|
var dropUnreadable7 = (live) => live === UNREADABLE ? undefined : live;
|
|
5254
|
-
var
|
|
5388
|
+
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
5389
|
|
|
5256
5390
|
// src/proxmox/storage.ts
|
|
5257
5391
|
var ProxmoxStorage = Resource34("Proxmox.Storage", {
|
|
5258
5392
|
defaultRemovalPolicy: "retain"
|
|
5259
5393
|
});
|
|
5260
|
-
var ProxmoxStorageProvider = () => Provider34.effect(ProxmoxStorage,
|
|
5261
|
-
list: () =>
|
|
5262
|
-
read:
|
|
5394
|
+
var ProxmoxStorageProvider = () => Provider34.effect(ProxmoxStorage, Effect69.succeed(ProxmoxStorage.Provider.of({
|
|
5395
|
+
list: () => Effect69.succeed([]),
|
|
5396
|
+
read: Effect69.fn(function* ({ olds, output }) {
|
|
5263
5397
|
return dropUnreadable7(yield* output === undefined ? readStorage(olds) : readStorageOrFail(olds));
|
|
5264
5398
|
}),
|
|
5265
|
-
diff:
|
|
5266
|
-
if (!
|
|
5399
|
+
diff: Effect69.fn(function* ({ news, output }) {
|
|
5400
|
+
if (!isResolved18(news))
|
|
5267
5401
|
return;
|
|
5268
5402
|
yield* guardWrite2(STORAGE_CREATE, createForm11(news), output === undefined);
|
|
5269
5403
|
yield* guardWrite2(STORAGE_UPDATE, updateForm8(news), false);
|
|
@@ -5278,61 +5412,61 @@ var ProxmoxStorageProvider = () => Provider34.effect(ProxmoxStorage, Effect66.su
|
|
|
5278
5412
|
yield* guardWrite2(STORAGE_CREATE, createForm11(news), true);
|
|
5279
5413
|
return { action: "update" };
|
|
5280
5414
|
}
|
|
5281
|
-
return
|
|
5415
|
+
return matches9(live, news) ? { action: "noop" } : { action: "update" };
|
|
5282
5416
|
}),
|
|
5283
|
-
reconcile:
|
|
5417
|
+
reconcile: Effect69.fn(function* ({ news }) {
|
|
5284
5418
|
const before = dropUnreadable7(yield* readStorage(news));
|
|
5285
5419
|
yield* guardWrite2(STORAGE_CREATE, createForm11(news), before === undefined);
|
|
5286
5420
|
yield* guardWrite2(STORAGE_UPDATE, updateForm8(news), false);
|
|
5287
5421
|
if (before === undefined) {
|
|
5288
|
-
yield* runPve(news.target, "provision", true, storage2.createStorage(
|
|
5289
|
-
} else if (!
|
|
5290
|
-
yield* runPve(news.target, "provision", true, storage2.putStorage(
|
|
5422
|
+
yield* runPve(news.target, "provision", true, storage2.createStorage(toDistilledCreate4(news)));
|
|
5423
|
+
} else if (!matches9(before, news)) {
|
|
5424
|
+
yield* runPve(news.target, "provision", true, storage2.putStorage(toDistilledUpdate3(news)));
|
|
5291
5425
|
}
|
|
5292
5426
|
const after = dropUnreadable7(yield* readStorage(news));
|
|
5293
5427
|
if (after === undefined) {
|
|
5294
|
-
return yield*
|
|
5428
|
+
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
5429
|
}
|
|
5296
5430
|
return after;
|
|
5297
5431
|
}),
|
|
5298
|
-
delete:
|
|
5432
|
+
delete: Effect69.fn(function* ({ olds }) {
|
|
5299
5433
|
yield* runPve(olds.target, "provision", true, storage2.deleteStorage({ storage: olds.storage }));
|
|
5300
5434
|
})
|
|
5301
5435
|
})));
|
|
5302
5436
|
// src/proxmox/zfs-pool.ts
|
|
5303
5437
|
import { Resource as Resource35 } from "alchemy";
|
|
5304
|
-
import { isResolved as
|
|
5438
|
+
import { isResolved as isResolved19 } from "alchemy/Diff";
|
|
5305
5439
|
import * as Provider35 from "alchemy/Provider";
|
|
5306
|
-
import * as
|
|
5440
|
+
import * as Effect72 from "effect/Effect";
|
|
5307
5441
|
|
|
5308
5442
|
// src/proxmox/zfs-pool-wire.ts
|
|
5309
|
-
import * as
|
|
5310
|
-
import * as
|
|
5443
|
+
import * as nodes10 from "@distilled.cloud/proxmox/nodes";
|
|
5444
|
+
import * as Effect70 from "effect/Effect";
|
|
5311
5445
|
var leafDevices = (children) => Array.isArray(children) ? children.flatMap((entry) => {
|
|
5312
5446
|
const vdev = entry;
|
|
5313
5447
|
const nested = leafDevices(vdev.children);
|
|
5314
5448
|
return nested.length > 0 ? nested : [text(vdev.name)].filter((name) => name !== "");
|
|
5315
5449
|
}) : [];
|
|
5316
|
-
var
|
|
5450
|
+
var attributesOf10 = (live, props) => ({
|
|
5317
5451
|
devices: leafDevices(live.children).join(","),
|
|
5318
5452
|
errors: text(live.errors, "unknown"),
|
|
5319
5453
|
name: props.name,
|
|
5320
5454
|
node: props.node,
|
|
5321
5455
|
state: text(live.state, "unknown")
|
|
5322
5456
|
});
|
|
5323
|
-
var readPoolOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false,
|
|
5457
|
+
var readPoolOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, nodes10.getNodeDiskZfs({ name: props.name, node: props.node }))).pipe(Effect70.map((live) => {
|
|
5324
5458
|
if (live === UNREADABLE)
|
|
5325
5459
|
return UNREADABLE;
|
|
5326
|
-
return text(live.name) === "" ? undefined :
|
|
5460
|
+
return text(live.name) === "" ? undefined : attributesOf10(live, props);
|
|
5327
5461
|
}));
|
|
5328
|
-
var readPool = (props) => readPoolOrFail(props).pipe(
|
|
5462
|
+
var readPool = (props) => readPoolOrFail(props).pipe(Effect70.orElseSucceed(() => {
|
|
5329
5463
|
return;
|
|
5330
5464
|
}));
|
|
5331
5465
|
var dropUnreadable8 = (live) => live === UNREADABLE ? undefined : live;
|
|
5332
5466
|
|
|
5333
5467
|
// src/proxmox/zfs-pool-form.ts
|
|
5334
|
-
import * as
|
|
5335
|
-
import * as
|
|
5468
|
+
import * as Effect71 from "effect/Effect";
|
|
5469
|
+
import * as nodes11 from "@distilled.cloud/proxmox/nodes";
|
|
5336
5470
|
var ZFS_POOL_CREATE = "pve:POST /nodes/{node}/disks/zfs";
|
|
5337
5471
|
var zfsPoolCreateEndpoint = (props) => props.devices === undefined || props.raidlevel === undefined ? undefined : ZFS_POOL_CREATE;
|
|
5338
5472
|
var createForm12 = (props) => ({
|
|
@@ -5343,7 +5477,7 @@ var createForm12 = (props) => ({
|
|
|
5343
5477
|
...props.compression === undefined ? {} : { compression: props.compression },
|
|
5344
5478
|
...props["draid-config"] === undefined ? {} : { "draid-config": props["draid-config"] }
|
|
5345
5479
|
});
|
|
5346
|
-
var
|
|
5480
|
+
var toDistilledCreate5 = (props) => {
|
|
5347
5481
|
const form = createForm12(props);
|
|
5348
5482
|
return {
|
|
5349
5483
|
...form,
|
|
@@ -5352,41 +5486,41 @@ var toDistilledCreate3 = (props) => {
|
|
|
5352
5486
|
};
|
|
5353
5487
|
};
|
|
5354
5488
|
var SETTLE_ATTEMPTS = 30;
|
|
5355
|
-
var createPool = (props, read4) =>
|
|
5489
|
+
var createPool = (props, read4) => Effect71.gen(function* () {
|
|
5356
5490
|
const existing = yield* read4(props);
|
|
5357
5491
|
if (existing !== undefined)
|
|
5358
5492
|
return existing;
|
|
5359
5493
|
if (props.devices === undefined || props.raidlevel === undefined) {
|
|
5360
|
-
return yield*
|
|
5494
|
+
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
5495
|
}
|
|
5362
5496
|
yield* guardForm(zfsPoolCreateEndpoint(props), asForm2(createForm12(props)), true);
|
|
5363
|
-
const upid = yield* runPve(props.target, "provision", true,
|
|
5364
|
-
const settle4 = (attempts) =>
|
|
5497
|
+
const upid = yield* runPve(props.target, "provision", true, nodes11.createNodeDiskZfs(toDistilledCreate5(props)));
|
|
5498
|
+
const settle4 = (attempts) => Effect71.gen(function* () {
|
|
5365
5499
|
const live = yield* read4(props);
|
|
5366
5500
|
if (live !== undefined || attempts <= 0)
|
|
5367
5501
|
return live;
|
|
5368
|
-
yield*
|
|
5502
|
+
yield* Effect71.sleep("2 seconds");
|
|
5369
5503
|
return yield* settle4(attempts - 1);
|
|
5370
5504
|
});
|
|
5371
5505
|
const after = yield* settle4(SETTLE_ATTEMPTS);
|
|
5372
5506
|
if (after === undefined) {
|
|
5373
|
-
return yield*
|
|
5507
|
+
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
5508
|
}
|
|
5375
5509
|
return after;
|
|
5376
5510
|
});
|
|
5377
|
-
var destroyPool = (props) => runPve(props.target, "provision", true,
|
|
5511
|
+
var destroyPool = (props) => runPve(props.target, "provision", true, nodes11.deleteNodeDiskZfs({ name: props.name, node: props.node }));
|
|
5378
5512
|
|
|
5379
5513
|
// src/proxmox/zfs-pool.ts
|
|
5380
5514
|
var ProxmoxZfsPool = Resource35("Proxmox.ZfsPool", {
|
|
5381
5515
|
defaultRemovalPolicy: "retain"
|
|
5382
5516
|
});
|
|
5383
|
-
var ProxmoxZfsPoolProvider = () => Provider35.effect(ProxmoxZfsPool,
|
|
5384
|
-
list: () =>
|
|
5385
|
-
read:
|
|
5517
|
+
var ProxmoxZfsPoolProvider = () => Provider35.effect(ProxmoxZfsPool, Effect72.succeed(ProxmoxZfsPool.Provider.of({
|
|
5518
|
+
list: () => Effect72.succeed([]),
|
|
5519
|
+
read: Effect72.fn(function* ({ olds, output }) {
|
|
5386
5520
|
return dropUnreadable8(yield* output === undefined ? readPool(olds) : readPoolOrFail(olds));
|
|
5387
5521
|
}),
|
|
5388
|
-
diff:
|
|
5389
|
-
if (!
|
|
5522
|
+
diff: Effect72.fn(function* ({ news, output }) {
|
|
5523
|
+
if (!isResolved19(news))
|
|
5390
5524
|
return;
|
|
5391
5525
|
yield* guardForm(zfsPoolCreateEndpoint(news), asForm2(createForm12(news)), output === undefined);
|
|
5392
5526
|
if (output === undefined)
|
|
@@ -5402,10 +5536,10 @@ var ProxmoxZfsPoolProvider = () => Provider35.effect(ProxmoxZfsPool, Effect69.su
|
|
|
5402
5536
|
}
|
|
5403
5537
|
return { action: "noop" };
|
|
5404
5538
|
}),
|
|
5405
|
-
reconcile:
|
|
5406
|
-
return yield* createPool(news, (props) => readPool(props).pipe(
|
|
5539
|
+
reconcile: Effect72.fn(function* ({ news }) {
|
|
5540
|
+
return yield* createPool(news, (props) => readPool(props).pipe(Effect72.map(dropUnreadable8)));
|
|
5407
5541
|
}),
|
|
5408
|
-
delete:
|
|
5542
|
+
delete: Effect72.fn(function* ({ olds }) {
|
|
5409
5543
|
yield* destroyPool(olds);
|
|
5410
5544
|
})
|
|
5411
5545
|
})));
|
|
@@ -5485,5 +5619,5 @@ export {
|
|
|
5485
5619
|
violations
|
|
5486
5620
|
};
|
|
5487
5621
|
|
|
5488
|
-
//# debugId=
|
|
5622
|
+
//# debugId=483DD0A45E397D2364756E2164756E21
|
|
5489
5623
|
//# sourceMappingURL=index.js.map
|