@homeflare/alchemy 0.10.1 → 0.12.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 +3 -3
- package/dist/caddy/admin-guard.d.ts +1 -1
- package/dist/caddy/index.js.map +4 -4
- package/dist/cloudflare/client.d.ts +1 -1
- package/dist/cloudflare/index.js.map +2 -2
- package/dist/cloudflare/lock-rules.d.ts +1 -1
- package/dist/forgejo/branch-protection-form.d.ts +1 -1
- package/dist/forgejo/branch-protection.d.ts +1 -1
- package/dist/forgejo/index.js.map +10 -10
- package/dist/forgejo/org-actions-secrets.d.ts +1 -1
- package/dist/forgejo/org-label.d.ts +1 -1
- package/dist/forgejo/org-team.d.ts +1 -1
- package/dist/forgejo/repo-webhook.d.ts +1 -1
- package/dist/forgejo/repository.d.ts +2 -2
- package/dist/forgejo/repository.d.ts.map +1 -1
- package/dist/forgejo/resource.d.ts +2 -2
- package/dist/forgejo/team-member.d.ts +1 -1
- package/dist/launchd/index.js.map +2 -2
- package/dist/openbao/auth-role.d.ts +1 -1
- package/dist/openbao/bao-http.d.ts +4 -4
- package/dist/openbao/cloudflare-permission-groups.d.ts +1 -1
- package/dist/openbao/cloudflare-role-form.d.ts +2 -2
- package/dist/openbao/cloudflare-role-form.d.ts.map +1 -1
- package/dist/openbao/cloudflare-roles-expand.d.ts +1 -1
- package/dist/openbao/digest.d.ts +1 -1
- package/dist/openbao/forgejo-bootstrap-parse.d.ts +2 -2
- package/dist/openbao/forgejo-bootstrap-parse.d.ts.map +1 -1
- package/dist/openbao/forgejo-bootstrap.d.ts +1 -1
- package/dist/openbao/index.js +10 -8
- package/dist/openbao/index.js.map +18 -18
- package/dist/openbao/mount.d.ts +1 -1
- package/dist/openbao/pki-role.d.ts +1 -1
- package/dist/openbao/policy.d.ts +2 -2
- package/dist/openbao/proxmox-role-form.d.ts +2 -2
- package/dist/openbao/proxmox-role-form.d.ts.map +1 -1
- package/dist/openbao/proxmox-role.d.ts +10 -10
- package/dist/openbao/ssh-role.d.ts +3 -3
- package/dist/ownership/adopting.d.ts +34 -0
- package/dist/ownership/adopting.d.ts.map +1 -0
- package/dist/ownership/probe.d.ts +15 -5
- package/dist/ownership/probe.d.ts.map +1 -1
- package/dist/ownership/rows.d.ts +12 -0
- package/dist/ownership/rows.d.ts.map +1 -1
- package/dist/ownership/store-fixture.d.ts +10 -0
- package/dist/ownership/store-fixture.d.ts.map +1 -0
- package/dist/proxmox/acl.d.ts +2 -2
- package/dist/proxmox/acl.d.ts.map +1 -1
- package/dist/proxmox/api-token-form.d.ts +2 -2
- package/dist/proxmox/api-token.d.ts +11 -11
- package/dist/proxmox/ceph-daemon-form.d.ts +3 -3
- package/dist/proxmox/ceph-daemon.d.ts +19 -19
- package/dist/proxmox/ceph-daemon.d.ts.map +1 -1
- package/dist/proxmox/ceph-flag.d.ts +5 -5
- package/dist/proxmox/ceph-fs-wire.d.ts +5 -5
- package/dist/proxmox/ceph-fs.d.ts +16 -16
- package/dist/proxmox/ceph-osd-tree.d.ts +16 -16
- package/dist/proxmox/ceph-osd.d.ts +5 -5
- package/dist/proxmox/ceph-pool-form.d.ts +13 -13
- package/dist/proxmox/ceph-pool-form.d.ts.map +1 -1
- package/dist/proxmox/ceph-pool.d.ts +3 -3
- package/dist/proxmox/client.d.ts +2 -2
- package/dist/proxmox/credentials.d.ts +8 -8
- package/dist/proxmox/credentials.d.ts.map +1 -1
- package/dist/proxmox/fake-pve-lxc.d.ts +8 -0
- package/dist/proxmox/fake-pve-lxc.d.ts.map +1 -1
- package/dist/proxmox/firewall-alias-form.d.ts +1 -1
- package/dist/proxmox/firewall-alias.d.ts +1 -1
- package/dist/proxmox/generated/pbs.d.ts +1 -1
- package/dist/proxmox/generated/pve.d.ts +1 -1
- package/dist/proxmox/group.d.ts +8 -8
- package/dist/proxmox/ha-resource.d.ts +3 -2
- package/dist/proxmox/ha-resource.d.ts.map +1 -1
- package/dist/proxmox/ha-rule-form.d.ts +5 -5
- package/dist/proxmox/ha-rule.d.ts +3 -3
- package/dist/proxmox/index.d.ts +6 -0
- package/dist/proxmox/index.d.ts.map +1 -1
- package/dist/proxmox/index.js +465 -217
- package/dist/proxmox/index.js.map +63 -58
- package/dist/proxmox/lease-cache.d.ts +4 -4
- package/dist/proxmox/lxc-adoption.d.ts +32 -0
- package/dist/proxmox/lxc-adoption.d.ts.map +1 -0
- package/dist/proxmox/lxc-harness.d.ts +12 -0
- package/dist/proxmox/lxc-harness.d.ts.map +1 -1
- package/dist/proxmox/lxc-props.d.ts +2 -2
- package/dist/proxmox/lxc.d.ts +8 -8
- package/dist/proxmox/lxc.d.ts.map +1 -1
- package/dist/proxmox/members.d.ts +3 -3
- package/dist/proxmox/network-apply-read.d.ts +5 -5
- package/dist/proxmox/network-apply.d.ts +9 -9
- package/dist/proxmox/node-network-form.d.ts +6 -6
- package/dist/proxmox/node-network.d.ts +17 -17
- package/dist/proxmox/node-network.d.ts.map +1 -1
- package/dist/proxmox/notification-target.d.ts +3 -2
- package/dist/proxmox/notification-target.d.ts.map +1 -1
- package/dist/proxmox/pbs-datastore-guard.d.ts +1 -1
- package/dist/proxmox/pbs-datastore.d.ts +10 -10
- package/dist/proxmox/pbs-prune-job.d.ts +2 -2
- package/dist/proxmox/pbs-sync-job.d.ts +4 -4
- package/dist/proxmox/pbs-verify-job.d.ts +1 -1
- package/dist/proxmox/pool.d.ts +4 -4
- package/dist/proxmox/provision-baseline.d.ts +74 -0
- package/dist/proxmox/provision-baseline.d.ts.map +1 -0
- package/dist/proxmox/provision-bootstrap.d.ts +33 -0
- package/dist/proxmox/provision-bootstrap.d.ts.map +1 -0
- package/dist/proxmox/provision-cli-fake.d.ts +38 -0
- package/dist/proxmox/provision-cli-fake.d.ts.map +1 -0
- package/dist/proxmox/provision-declare.d.ts +25 -0
- package/dist/proxmox/provision-declare.d.ts.map +1 -0
- package/dist/proxmox/replication-job.d.ts +9 -8
- package/dist/proxmox/replication-job.d.ts.map +1 -1
- package/dist/proxmox/resource.d.ts +2 -2
- package/dist/proxmox/resource.d.ts.map +1 -1
- package/dist/proxmox/sdn-apply-read.d.ts +7 -7
- package/dist/proxmox/sdn-apply.d.ts +1 -1
- package/dist/proxmox/sdn-subnet-form.d.ts +1 -1
- package/dist/proxmox/sdn-subnet.d.ts +5 -5
- package/dist/proxmox/sdn-vnet.d.ts +5 -6
- package/dist/proxmox/sdn-vnet.d.ts.map +1 -1
- package/dist/proxmox/storage.d.ts +4 -3
- package/dist/proxmox/storage.d.ts.map +1 -1
- package/dist/proxmox/user.d.ts +3 -2
- package/dist/proxmox/user.d.ts.map +1 -1
- package/dist/proxmox/values.d.ts +1 -1
- package/dist/proxmox/zfs-pool-write.d.ts +1 -1
- package/dist/proxmox/zfs-pool.d.ts +5 -5
- package/dist/talos/credentials.d.ts +1 -1
- package/dist/talos/credentials.d.ts.map +1 -1
- package/dist/talos/index.js.map +1 -1
- package/docs/adopt-verify.md +21 -5
- package/docs/adopted-deploys.md +5 -3
- package/docs/ownership.md +27 -2
- package/docs/provision-baseline.md +106 -0
- package/docs/proxmox-lxc-adopt.md +83 -0
- package/docs/proxmox-lxc.md +12 -49
- package/docs/proxmox.md +8 -0
- package/package.json +1 -1
- package/src/caddy/admin-guard.ts +1 -1
- package/src/caddy/local-admin.ts +1 -1
- package/src/cloudflare/client.ts +1 -1
- package/src/cloudflare/lock-rules.ts +1 -1
- package/src/forgejo/branch-protection-form.ts +1 -1
- package/src/forgejo/branch-protection.ts +1 -1
- package/src/forgejo/client.ts +1 -1
- package/src/forgejo/org-actions-secrets.ts +1 -1
- package/src/forgejo/org-label.ts +1 -1
- package/src/forgejo/org-team.ts +1 -1
- package/src/forgejo/repo-webhook.ts +1 -1
- package/src/forgejo/repository.ts +2 -2
- package/src/forgejo/resource.ts +2 -2
- package/src/forgejo/team-member.ts +1 -1
- package/src/openbao/README.md +4 -4
- package/src/openbao/auth-role.ts +1 -1
- package/src/openbao/bao-http.test.ts +2 -2
- package/src/openbao/bao-http.ts +6 -6
- package/src/openbao/bao-status.test.ts +1 -1
- package/src/openbao/bao-status.ts +1 -1
- package/src/openbao/cloudflare-group-scope.test.ts +1 -1
- package/src/openbao/cloudflare-permission-groups.test.ts +1 -1
- package/src/openbao/cloudflare-permission-groups.ts +3 -3
- package/src/openbao/cloudflare-policy.ts +1 -1
- package/src/openbao/cloudflare-role-form.ts +2 -2
- package/src/openbao/cloudflare-roles-expand.ts +1 -1
- package/src/openbao/digest.ts +1 -1
- package/src/openbao/forgejo-bootstrap-parse.test.ts +3 -3
- package/src/openbao/forgejo-bootstrap-parse.ts +2 -2
- package/src/openbao/forgejo-bootstrap.ts +4 -4
- package/src/openbao/kubernetes-role-form.test.ts +5 -5
- package/src/openbao/mount.ts +1 -1
- package/src/openbao/pki-role.ts +1 -1
- package/src/openbao/policy-wire.test.ts +3 -3
- package/src/openbao/policy.ts +2 -2
- package/src/openbao/proxmox-role-form.ts +5 -5
- package/src/openbao/proxmox-role.ts +11 -11
- package/src/openbao/ssh-role.ts +3 -3
- package/src/ownership/adopting.test.ts +63 -0
- package/src/ownership/adopting.ts +46 -0
- package/src/ownership/ownership.test.ts +1 -34
- package/src/ownership/probe.ts +27 -14
- package/src/ownership/rows.ts +8 -2
- package/src/ownership/store-fixture.ts +50 -0
- package/src/proxmox/acl.ts +4 -4
- package/src/proxmox/adopt-noop.test.ts +6 -6
- package/src/proxmox/api-token-form.ts +4 -4
- package/src/proxmox/api-token.ts +13 -13
- package/src/proxmox/ceph-daemon-form.ts +3 -3
- package/src/proxmox/ceph-daemon.ts +20 -20
- package/src/proxmox/ceph-flag.ts +7 -7
- package/src/proxmox/ceph-fs-wire.ts +7 -7
- package/src/proxmox/ceph-fs.ts +18 -18
- package/src/proxmox/ceph-osd-tree.ts +16 -16
- package/src/proxmox/ceph-osd.ts +6 -6
- package/src/proxmox/ceph-pool-adopt.test.ts +14 -14
- package/src/proxmox/ceph-pool-form.ts +13 -13
- package/src/proxmox/ceph-pool.ts +6 -6
- package/src/proxmox/client.test.ts +3 -3
- package/src/proxmox/client.ts +3 -3
- package/src/proxmox/credentials.test.ts +6 -8
- package/src/proxmox/credentials.ts +8 -8
- package/src/proxmox/fake-pve-lxc.ts +13 -1
- package/src/proxmox/firewall-alias-form.ts +1 -1
- package/src/proxmox/firewall-alias.ts +1 -1
- package/src/proxmox/generated/pbs.ts +1 -1
- package/src/proxmox/generated/pve.ts +1 -1
- package/src/proxmox/group.ts +10 -10
- package/src/proxmox/ha-resource.ts +3 -2
- package/src/proxmox/ha-rule-form.ts +5 -5
- package/src/proxmox/ha-rule.ts +3 -3
- package/src/proxmox/index.test.ts +7 -0
- package/src/proxmox/index.ts +14 -0
- package/src/proxmox/lease-cache.test.ts +19 -19
- package/src/proxmox/lease-cache.ts +4 -4
- package/src/proxmox/lxc-adopt.test.ts +16 -23
- package/src/proxmox/lxc-adoption.ts +50 -0
- package/src/proxmox/lxc-create-form.ts +1 -1
- package/src/proxmox/lxc-guards.test.ts +10 -17
- package/src/proxmox/lxc-harness.ts +27 -4
- package/src/proxmox/lxc-judge.test.ts +5 -5
- package/src/proxmox/lxc-lifecycle.ts +1 -1
- package/src/proxmox/lxc-ownership.test.ts +19 -14
- package/src/proxmox/lxc-props.ts +2 -2
- package/src/proxmox/lxc-read.test.ts +3 -3
- package/src/proxmox/lxc-strict-adopt.test.ts +104 -0
- package/src/proxmox/lxc-volume.test.ts +9 -9
- package/src/proxmox/lxc-volume.ts +1 -1
- package/src/proxmox/lxc-wire.ts +1 -1
- package/src/proxmox/lxc.ts +19 -15
- package/src/proxmox/members.test.ts +1 -1
- package/src/proxmox/members.ts +3 -3
- package/src/proxmox/metric-server-otel.ts +2 -2
- package/src/proxmox/network-apply-read.ts +10 -10
- package/src/proxmox/network-apply.ts +10 -10
- package/src/proxmox/node-network-form.ts +7 -7
- package/src/proxmox/node-network.ts +18 -18
- package/src/proxmox/notification-target.ts +3 -2
- package/src/proxmox/pbs-datastore-guard.ts +1 -1
- package/src/proxmox/pbs-datastore.ts +10 -10
- package/src/proxmox/pbs-prune-job.ts +2 -2
- package/src/proxmox/pbs-sync-job.ts +4 -4
- package/src/proxmox/pbs-verify-job.ts +1 -1
- package/src/proxmox/pool.test.ts +2 -2
- package/src/proxmox/pool.ts +4 -4
- package/src/proxmox/provision-baseline.test.ts +87 -0
- package/src/proxmox/provision-baseline.ts +162 -0
- package/src/proxmox/provision-bootstrap.test.ts +132 -0
- package/src/proxmox/provision-bootstrap.ts +135 -0
- package/src/proxmox/provision-cli-fake.ts +217 -0
- package/src/proxmox/provision-declare.test.ts +111 -0
- package/src/proxmox/provision-declare.ts +103 -0
- package/src/proxmox/replication-job.ts +9 -8
- package/src/proxmox/resource.ts +3 -3
- package/src/proxmox/sdn-apply-read.test.ts +16 -16
- package/src/proxmox/sdn-apply-read.ts +7 -7
- package/src/proxmox/sdn-apply.ts +3 -3
- package/src/proxmox/sdn-subnet-form.ts +1 -1
- package/src/proxmox/sdn-subnet.ts +5 -5
- package/src/proxmox/sdn-vnet.ts +5 -6
- package/src/proxmox/storage.ts +4 -3
- package/src/proxmox/user.ts +3 -2
- package/src/proxmox/values.test.ts +2 -2
- package/src/proxmox/values.ts +1 -1
- package/src/proxmox/zfs-pool-write.ts +1 -1
- package/src/proxmox/zfs-pool.ts +6 -6
- package/src/talos/credentials.ts +3 -3
- package/src/talos/values.test.ts +9 -9
- package/src/verify/cli.test.ts +1 -1
package/dist/proxmox/index.js
CHANGED
|
@@ -422,7 +422,7 @@ var handlers = {
|
|
|
422
422
|
reconcile: Effect7.fn(function* ({ news }) {
|
|
423
423
|
const live = yield* ops2.read(news);
|
|
424
424
|
if (live === undefined) {
|
|
425
|
-
return yield* Effect7.die(new Error(`${news.userid}!${news.tokenid}: this resource does not create API tokens. PVE returns ` + "the secret only in the create response and it cannot be stored, so a token made here " + "would be a live credential nobody holds. Create it with `pveum user token add` and " + "capture the value, or mint a short-lived one from the OpenBao proxmox mount " + "(`bao read proxmox-
|
|
425
|
+
return yield* Effect7.die(new Error(`${news.userid}!${news.tokenid}: this resource does not create API tokens. PVE returns ` + "the secret only in the create response and it cannot be stored, so a token made here " + "would be a live credential nobody holds. Create it with `pveum user token add` and " + "capture the value, or mint a short-lived one from the OpenBao proxmox mount " + "(`bao read proxmox-c1/creds/<role>`) -- then declare it here to manage it."));
|
|
426
426
|
}
|
|
427
427
|
return yield* ops2.reconcile(news);
|
|
428
428
|
})
|
|
@@ -1048,7 +1048,7 @@ var ProxmoxHaRuleProvider = () => Provider12.effect(ProxmoxHaRule, Effect21.succ
|
|
|
1048
1048
|
import { Resource as Resource13 } from "alchemy";
|
|
1049
1049
|
import { isResolved as isResolved6 } from "alchemy/Diff";
|
|
1050
1050
|
import * as Provider13 from "alchemy/Provider";
|
|
1051
|
-
import * as
|
|
1051
|
+
import * as Effect31 from "effect/Effect";
|
|
1052
1052
|
|
|
1053
1053
|
// src/ownership/adopt.ts
|
|
1054
1054
|
import { AdoptPolicy } from "alchemy/AdoptPolicy";
|
|
@@ -1230,19 +1230,21 @@ var claimFor = (owner) => (what) => refuseTakeover(owner, what);
|
|
|
1230
1230
|
// src/ownership/probe.ts
|
|
1231
1231
|
import { Unowned as Unowned2 } from "alchemy/AdoptPolicy";
|
|
1232
1232
|
import * as Effect26 from "effect/Effect";
|
|
1233
|
-
var
|
|
1234
|
-
if (found === undefined || ask.output !== undefined)
|
|
1235
|
-
return found;
|
|
1233
|
+
var provenOurs = (ask, settled2) => Effect26.gen(function* () {
|
|
1236
1234
|
const recorded = yield* recordedInstance(ask.fqn, ask.instanceId);
|
|
1237
1235
|
if (recorded === "absent")
|
|
1238
|
-
return
|
|
1236
|
+
return false;
|
|
1239
1237
|
if (recorded === "partial") {
|
|
1240
1238
|
yield* Effect26.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");
|
|
1241
|
-
return
|
|
1239
|
+
return false;
|
|
1242
1240
|
}
|
|
1243
1241
|
const unproven = (reason) => Effect26.as(Effect26.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);
|
|
1244
|
-
|
|
1245
|
-
|
|
1242
|
+
return yield* settled2.pipe(Effect26.catch(unproven), Effect26.catchDefect(unproven));
|
|
1243
|
+
});
|
|
1244
|
+
var ownedRead = (ask, found, settled2) => Effect26.gen(function* () {
|
|
1245
|
+
if (found === undefined || ask.output !== undefined)
|
|
1246
|
+
return found;
|
|
1247
|
+
return (yield* provenOurs(ask, settled2)) ? found : Unowned2(found);
|
|
1246
1248
|
});
|
|
1247
1249
|
|
|
1248
1250
|
// src/proxmox/lxc-wire.ts
|
|
@@ -1693,12 +1695,28 @@ var identityRefusals = (news, olds, output) => {
|
|
|
1693
1695
|
return reasons;
|
|
1694
1696
|
};
|
|
1695
1697
|
|
|
1698
|
+
// src/proxmox/lxc-adoption.ts
|
|
1699
|
+
import * as Effect30 from "effect/Effect";
|
|
1700
|
+
|
|
1701
|
+
// src/ownership/adopting.ts
|
|
1702
|
+
import * as Effect27 from "effect/Effect";
|
|
1703
|
+
var adopting = (owner, settled2) => Effect27.gen(function* () {
|
|
1704
|
+
if (owner.output === undefined)
|
|
1705
|
+
return false;
|
|
1706
|
+
const generation = yield* recordedGeneration(owner.fqn, owner.instanceId);
|
|
1707
|
+
if (generation === undefined || generation.adopting === true)
|
|
1708
|
+
return true;
|
|
1709
|
+
if (generation.attr !== undefined && generation.attr !== null)
|
|
1710
|
+
return false;
|
|
1711
|
+
return !(yield* provenOurs(owner, settled2(generation.props)));
|
|
1712
|
+
});
|
|
1713
|
+
|
|
1696
1714
|
// src/proxmox/lxc-lifecycle.ts
|
|
1697
|
-
import * as
|
|
1715
|
+
import * as Effect29 from "effect/Effect";
|
|
1698
1716
|
|
|
1699
1717
|
// src/proxmox/network-apply-read.ts
|
|
1700
|
-
import * as
|
|
1701
|
-
var stagedDiff = (target, node) =>
|
|
1718
|
+
import * as Effect28 from "effect/Effect";
|
|
1719
|
+
var stagedDiff = (target, node) => Effect28.gen(function* () {
|
|
1702
1720
|
const path = `nodes/${node}/network`;
|
|
1703
1721
|
const credential = yield* leased(target, "read");
|
|
1704
1722
|
const envelope = yield* pveEnvelopeWith(target, credential, "GET", path);
|
|
@@ -1711,8 +1729,8 @@ var pendingLines = (diff) => {
|
|
|
1711
1729
|
`).filter((line) => (line.startsWith("+") || line.startsWith("-")) && !line.startsWith("+++") && !line.startsWith("---")).length;
|
|
1712
1730
|
return Math.max(changed, 1);
|
|
1713
1731
|
};
|
|
1714
|
-
var pendingCount = (target, node) => stagedDiff(target, node).pipe(
|
|
1715
|
-
var clusterHealth = (target) => pve(target, "read", "GET", "cluster/status").pipe(
|
|
1732
|
+
var pendingCount = (target, node) => stagedDiff(target, node).pipe(Effect28.map(pendingLines));
|
|
1733
|
+
var clusterHealth = (target) => pve(target, "read", "GET", "cluster/status").pipe(Effect28.map((rows) => {
|
|
1716
1734
|
const all = rows ?? [];
|
|
1717
1735
|
const cluster = all.find((row) => row.type === "cluster");
|
|
1718
1736
|
return {
|
|
@@ -1720,21 +1738,21 @@ var clusterHealth = (target) => pve(target, "read", "GET", "cluster/status").pip
|
|
|
1720
1738
|
quorate: cluster === undefined ? true : bool(cluster.quorate)
|
|
1721
1739
|
};
|
|
1722
1740
|
}));
|
|
1723
|
-
var degradedReason = (target) => clusterHealth(target).pipe(
|
|
1741
|
+
var degradedReason = (target) => clusterHealth(target).pipe(Effect28.map((health) => health.quorate && health.offline.length === 0 ? undefined : `quorate=${String(health.quorate)} offline=[${health.offline.join(", ")}]`));
|
|
1724
1742
|
var UNREACHABLE = "the task could not be read -- the node stopped answering, or this lease cannot see it";
|
|
1725
1743
|
var STILL_RUNNING = "still running when this provider stopped waiting";
|
|
1726
1744
|
var ATTEMPTS = 30;
|
|
1727
|
-
var awaitTask = (target, credential, node, upid, attempts = ATTEMPTS) =>
|
|
1745
|
+
var awaitTask = (target, credential, node, upid, attempts = ATTEMPTS) => Effect28.gen(function* () {
|
|
1728
1746
|
const path = `nodes/${node}/tasks/${upid}/status`;
|
|
1729
1747
|
for (let attempt = 0;attempt < attempts; attempt += 1) {
|
|
1730
|
-
const status = yield* pveWith(target, credential, "GET", path).pipe(
|
|
1748
|
+
const status = yield* pveWith(target, credential, "GET", path).pipe(Effect28.orElseSucceed(() => {
|
|
1731
1749
|
return;
|
|
1732
1750
|
}));
|
|
1733
1751
|
if (status === undefined)
|
|
1734
1752
|
return UNREACHABLE;
|
|
1735
1753
|
if (status.status === "stopped")
|
|
1736
1754
|
return text(status.exitstatus, "stopped with no exitstatus");
|
|
1737
|
-
yield*
|
|
1755
|
+
yield* Effect28.sleep("1 second");
|
|
1738
1756
|
}
|
|
1739
1757
|
return STILL_RUNNING;
|
|
1740
1758
|
});
|
|
@@ -1747,17 +1765,17 @@ var guestPath = (where) => `nodes/${where.node}/lxc/${String(where.vmid)}`;
|
|
|
1747
1765
|
var CREATE_POLLS = 180;
|
|
1748
1766
|
var TASK_POLLS = 60;
|
|
1749
1767
|
var isServerError = (error) => error instanceof PveError && error.status === 500;
|
|
1750
|
-
var readLive = (where) =>
|
|
1751
|
-
const answer = yield* pve(where.target, "read", "GET", `${guestPath(where)}/config`).pipe(
|
|
1768
|
+
var readLive = (where) => Effect29.gen(function* () {
|
|
1769
|
+
const answer = yield* pve(where.target, "read", "GET", `${guestPath(where)}/config`).pipe(Effect29.map((data) => ({ data, error: undefined })), Effect29.catchIf(isServerError, (error) => Effect29.succeed({ data: undefined, error })));
|
|
1752
1770
|
if (answer.data !== undefined && answer.data !== null)
|
|
1753
1771
|
return answer.data;
|
|
1754
1772
|
const rows = yield* pve(where.target, "read", "GET", "cluster/resources?type=vm");
|
|
1755
1773
|
const found = (rows ?? []).find((row) => Number(row.vmid) === where.vmid);
|
|
1756
1774
|
if (found !== undefined && found.node === where.node && found.type === "lxc") {
|
|
1757
|
-
return yield*
|
|
1775
|
+
return yield* Effect29.fail(answer.error ?? new LxcRefusedError(`CT ${String(where.vmid)} is listed but its config read empty.`));
|
|
1758
1776
|
}
|
|
1759
1777
|
if (found !== undefined) {
|
|
1760
|
-
return yield*
|
|
1778
|
+
return yield* Effect29.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."));
|
|
1761
1779
|
}
|
|
1762
1780
|
return;
|
|
1763
1781
|
});
|
|
@@ -1767,19 +1785,19 @@ var attributesOf = (where, live) => ({
|
|
|
1767
1785
|
rawKeys: rawKeysOf(live),
|
|
1768
1786
|
vmid: where.vmid
|
|
1769
1787
|
});
|
|
1770
|
-
var task = (where, method, path, what, polls, form) =>
|
|
1788
|
+
var task = (where, method, path, what, polls, form) => Effect29.gen(function* () {
|
|
1771
1789
|
const credential = yield* mint(where.target, "provision");
|
|
1772
1790
|
const upid = text(yield* pveWith(where.target, credential, method, path, form));
|
|
1773
1791
|
if (upid === "") {
|
|
1774
|
-
return yield*
|
|
1792
|
+
return yield* Effect29.fail(new LxcRefusedError(`${what}: PVE returned no task id, so the write cannot be confirmed and is not claimed.`));
|
|
1775
1793
|
}
|
|
1776
1794
|
const exit = yield* awaitTask(where.target, credential, where.node, upid, polls);
|
|
1777
1795
|
if (exit !== "OK") {
|
|
1778
|
-
return yield*
|
|
1796
|
+
return yield* Effect29.fail(new LxcRefusedError(`${what}: task ${upid} ended "${exit}". Read \`pvesh get /nodes/${where.node}/tasks/` + `${upid}/log\` before running the deploy again.`));
|
|
1779
1797
|
}
|
|
1780
1798
|
});
|
|
1781
1799
|
var createGuest = (props) => task(props, "POST", `nodes/${props.node}/lxc`, `create CT ${String(props.vmid)}`, CREATE_POLLS, createForm3(props));
|
|
1782
|
-
var updateGuest = (props, change, digest) =>
|
|
1800
|
+
var updateGuest = (props, change, digest) => Effect29.gen(function* () {
|
|
1783
1801
|
if (Object.keys(change.put).length > 0 || change.clear.length > 0) {
|
|
1784
1802
|
const form = withClears({ ...change.put, ...digest === "" ? {} : { digest } }, change.clear);
|
|
1785
1803
|
yield* pve(props.target, "provision", "PUT", `${guestPath(props)}/config`, form);
|
|
@@ -1788,22 +1806,34 @@ var updateGuest = (props, change, digest) => Effect28.gen(function* () {
|
|
|
1788
1806
|
yield* task(props, "PUT", `${guestPath(props)}/resize`, `grow ${grow.disk} of CT ${String(props.vmid)} to ${grow.size}`, TASK_POLLS, { disk: grow.disk, size: grow.size });
|
|
1789
1807
|
}
|
|
1790
1808
|
});
|
|
1791
|
-
var destroyGuest = (where, declared) =>
|
|
1809
|
+
var destroyGuest = (where, declared) => Effect29.gen(function* () {
|
|
1792
1810
|
const live = yield* readLive(where);
|
|
1793
1811
|
if (live === undefined)
|
|
1794
1812
|
return;
|
|
1795
1813
|
const drift = judge(declared, live).drift;
|
|
1796
1814
|
if (drift.length > 0) {
|
|
1797
|
-
return yield*
|
|
1815
|
+
return yield* Effect29.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."));
|
|
1798
1816
|
}
|
|
1799
1817
|
yield* task(where, "DELETE", guestPath(where), `destroy CT ${String(where.vmid)}`, TASK_POLLS);
|
|
1800
1818
|
});
|
|
1801
1819
|
|
|
1820
|
+
// src/proxmox/lxc-adoption.ts
|
|
1821
|
+
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.";
|
|
1822
|
+
var refuseAdoptedDrift = (owner, props, live, change) => Effect30.gen(function* () {
|
|
1823
|
+
if (change.drift.length === 0)
|
|
1824
|
+
return;
|
|
1825
|
+
const settled2 = (recorded) => Effect30.sync(() => judge(recorded, live).drift.length === 0);
|
|
1826
|
+
if (!(yield* adopting(owner, settled2)))
|
|
1827
|
+
return;
|
|
1828
|
+
return yield* Effect30.fail(new LxcRefusedError(adoptionRefusal(props, change.drift)));
|
|
1829
|
+
});
|
|
1830
|
+
|
|
1802
1831
|
// src/proxmox/lxc.ts
|
|
1803
1832
|
var ProxmoxLxc = Resource13("Proxmox.Lxc", { defaultRemovalPolicy: "retain" });
|
|
1804
|
-
var refused = (reasons) =>
|
|
1833
|
+
var refused = (reasons) => Effect31.fail(new LxcRefusedError(reasons.join(`
|
|
1805
1834
|
`)));
|
|
1806
|
-
var diff = (news, olds
|
|
1835
|
+
var diff = (owner, news, olds) => Effect31.gen(function* () {
|
|
1836
|
+
const { output } = owner;
|
|
1807
1837
|
const identity2 = identityRefusals(news, olds, output);
|
|
1808
1838
|
if (identity2.length > 0)
|
|
1809
1839
|
return yield* refused(identity2);
|
|
@@ -1816,20 +1846,21 @@ var diff = (news, olds, output) => Effect29.gen(function* () {
|
|
|
1816
1846
|
const refusals = createRefusals(news);
|
|
1817
1847
|
if (refusals.length > 0)
|
|
1818
1848
|
return yield* refused(refusals);
|
|
1819
|
-
yield*
|
|
1849
|
+
yield* Effect31.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.");
|
|
1820
1850
|
return { action: "update" };
|
|
1821
1851
|
}
|
|
1822
1852
|
const change = judge(news, live);
|
|
1853
|
+
yield* refuseAdoptedDrift(owner, news, live, change);
|
|
1823
1854
|
if (change.refuse.length > 0)
|
|
1824
1855
|
return yield* refused(change.refuse);
|
|
1825
1856
|
const moved = news.node !== output.node;
|
|
1826
1857
|
if (change.drift.length === 0 && !moved)
|
|
1827
1858
|
return { action: "noop" };
|
|
1828
1859
|
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.";
|
|
1829
|
-
yield*
|
|
1860
|
+
yield* Effect31.logWarning(`Proxmox.Lxc ${news.node}/${String(news.vmid)}: ${what}`);
|
|
1830
1861
|
return { action: "update" };
|
|
1831
1862
|
});
|
|
1832
|
-
var reconcile = (owner, news) =>
|
|
1863
|
+
var reconcile = (owner, news) => Effect31.gen(function* () {
|
|
1833
1864
|
const { output } = owner;
|
|
1834
1865
|
const identity2 = identityRefusals(news, news, output);
|
|
1835
1866
|
if (identity2.length > 0)
|
|
@@ -1850,6 +1881,7 @@ var reconcile = (owner, news) => Effect29.gen(function* () {
|
|
|
1850
1881
|
`CT ${String(news.vmid)} already exists on ${news.node} and differs in ` + `${change.drift.join(", ")}, but this deploy planned a create. A create never writes ` + "onto an existing guest: plan again, so the plan can show it and adopt it."
|
|
1851
1882
|
]);
|
|
1852
1883
|
}
|
|
1884
|
+
yield* refuseAdoptedDrift(owner, news, live, change);
|
|
1853
1885
|
if (change.refuse.length > 0)
|
|
1854
1886
|
return yield* refused(change.refuse);
|
|
1855
1887
|
if (change.drift.length > 0)
|
|
@@ -1874,22 +1906,24 @@ var whereOf = (olds, output) => ({
|
|
|
1874
1906
|
target: olds.target,
|
|
1875
1907
|
vmid: output?.vmid ?? olds.vmid
|
|
1876
1908
|
});
|
|
1877
|
-
var read = (owner, olds) =>
|
|
1909
|
+
var read = (owner, olds) => Effect31.gen(function* () {
|
|
1878
1910
|
const where = whereOf(olds, owner.output);
|
|
1879
1911
|
const live = yield* readLive(where);
|
|
1880
1912
|
if (live === undefined)
|
|
1881
1913
|
return;
|
|
1882
|
-
const settled2 =
|
|
1914
|
+
const settled2 = Effect31.sync(() => judge(olds, live).drift.length === 0);
|
|
1883
1915
|
return yield* ownedRead(owner, attributesOf(where, live), settled2);
|
|
1884
1916
|
});
|
|
1885
1917
|
var handlers10 = {
|
|
1886
1918
|
delete: ({ olds, output }) => destroyGuest(whereOf(olds, output), olds),
|
|
1887
1919
|
diff: ({
|
|
1920
|
+
fqn,
|
|
1921
|
+
instanceId,
|
|
1888
1922
|
news,
|
|
1889
1923
|
olds,
|
|
1890
1924
|
output
|
|
1891
|
-
}) => diff(
|
|
1892
|
-
list: () =>
|
|
1925
|
+
}) => diff({ fqn, instanceId, output }, news, olds),
|
|
1926
|
+
list: () => Effect31.succeed([]),
|
|
1893
1927
|
read: ({
|
|
1894
1928
|
fqn,
|
|
1895
1929
|
instanceId,
|
|
@@ -1903,11 +1937,11 @@ var handlers10 = {
|
|
|
1903
1937
|
output
|
|
1904
1938
|
}) => reconcile({ fqn, instanceId, output }, news)
|
|
1905
1939
|
};
|
|
1906
|
-
var ProxmoxLxcProvider = () => Provider13.effect(ProxmoxLxc,
|
|
1940
|
+
var ProxmoxLxcProvider = () => Provider13.effect(ProxmoxLxc, Effect31.succeed(ProxmoxLxc.Provider.of(handlers10)).pipe(Effect31.map(provingResumes)));
|
|
1907
1941
|
// src/proxmox/metric-server.ts
|
|
1908
1942
|
import { Resource as Resource14 } from "alchemy";
|
|
1909
1943
|
import * as Provider14 from "alchemy/Provider";
|
|
1910
|
-
import * as
|
|
1944
|
+
import * as Effect32 from "effect/Effect";
|
|
1911
1945
|
|
|
1912
1946
|
// src/proxmox/metric-server-form.ts
|
|
1913
1947
|
var UNSET2 = -1;
|
|
@@ -2015,22 +2049,22 @@ var handlers11 = pveHandlers({
|
|
|
2015
2049
|
path: (props) => `cluster/metrics/server/${props.id}`,
|
|
2016
2050
|
updateForm: update
|
|
2017
2051
|
});
|
|
2018
|
-
var ProxmoxMetricServerProvider = () => Provider14.effect(ProxmoxMetricServer,
|
|
2052
|
+
var ProxmoxMetricServerProvider = () => Provider14.effect(ProxmoxMetricServer, Effect32.succeed(ProxmoxMetricServer.Provider.of(handlers11)));
|
|
2019
2053
|
// src/proxmox/network-apply.ts
|
|
2020
2054
|
import { Resource as Resource15 } from "alchemy";
|
|
2021
2055
|
import { isResolved as isResolved7 } from "alchemy/Diff";
|
|
2022
2056
|
import * as Provider15 from "alchemy/Provider";
|
|
2023
|
-
import * as
|
|
2057
|
+
import * as Effect33 from "effect/Effect";
|
|
2024
2058
|
var ProxmoxNetworkApply = Resource15("Proxmox.NetworkApply");
|
|
2025
|
-
var read2 = (props) => pendingCount(props.target, props.node).pipe(
|
|
2026
|
-
var diff2 = (news) =>
|
|
2059
|
+
var read2 = (props) => pendingCount(props.target, props.node).pipe(Effect33.map((pending) => ({ node: props.node, pending })));
|
|
2060
|
+
var diff2 = (news) => Effect33.gen(function* () {
|
|
2027
2061
|
if (!isResolved7(news))
|
|
2028
2062
|
return;
|
|
2029
2063
|
const { pending } = yield* read2(news);
|
|
2030
2064
|
return pending === 0 ? { action: "noop" } : { action: "update" };
|
|
2031
2065
|
});
|
|
2032
|
-
var refuse = (node, why) =>
|
|
2033
|
-
var apply = (props) =>
|
|
2066
|
+
var refuse = (node, why) => Effect33.die(new Error(`Proxmox.NetworkApply ${node}: ${why}`));
|
|
2067
|
+
var apply = (props) => Effect33.gen(function* () {
|
|
2034
2068
|
const node = props.node;
|
|
2035
2069
|
const staged = yield* pendingCount(props.target, node);
|
|
2036
2070
|
if (staged === 0)
|
|
@@ -2059,17 +2093,17 @@ var apply = (props) => Effect31.gen(function* () {
|
|
|
2059
2093
|
return { node, pending: left };
|
|
2060
2094
|
});
|
|
2061
2095
|
var handlers12 = {
|
|
2062
|
-
delete: () =>
|
|
2096
|
+
delete: () => Effect33.void,
|
|
2063
2097
|
diff: ({ news }) => diff2(news),
|
|
2064
|
-
list: () =>
|
|
2098
|
+
list: () => Effect33.succeed([]),
|
|
2065
2099
|
read: ({ olds }) => read2(olds),
|
|
2066
2100
|
reconcile: ({ news }) => apply(news)
|
|
2067
2101
|
};
|
|
2068
|
-
var ProxmoxNetworkApplyProvider = () => Provider15.effect(ProxmoxNetworkApply,
|
|
2102
|
+
var ProxmoxNetworkApplyProvider = () => Provider15.effect(ProxmoxNetworkApply, Effect33.succeed(ProxmoxNetworkApply.Provider.of(handlers12)));
|
|
2069
2103
|
// src/proxmox/node-network.ts
|
|
2070
2104
|
import { Resource as Resource16 } from "alchemy";
|
|
2071
2105
|
import * as Provider16 from "alchemy/Provider";
|
|
2072
|
-
import * as
|
|
2106
|
+
import * as Effect34 from "effect/Effect";
|
|
2073
2107
|
|
|
2074
2108
|
// src/proxmox/node-network-form.ts
|
|
2075
2109
|
var UNSET3 = -1;
|
|
@@ -2149,11 +2183,11 @@ var handlers13 = pveHandlers({
|
|
|
2149
2183
|
path: (props) => `nodes/${props.node}/network/${props.iface}`,
|
|
2150
2184
|
updateForm: updateBody2
|
|
2151
2185
|
});
|
|
2152
|
-
var ProxmoxNodeNetworkProvider = () => Provider16.effect(ProxmoxNodeNetwork,
|
|
2186
|
+
var ProxmoxNodeNetworkProvider = () => Provider16.effect(ProxmoxNodeNetwork, Effect34.succeed(ProxmoxNodeNetwork.Provider.of(handlers13)));
|
|
2153
2187
|
// src/proxmox/notification-target.ts
|
|
2154
2188
|
import { Resource as Resource17 } from "alchemy";
|
|
2155
2189
|
import * as Provider17 from "alchemy/Provider";
|
|
2156
|
-
import * as
|
|
2190
|
+
import * as Effect35 from "effect/Effect";
|
|
2157
2191
|
|
|
2158
2192
|
// src/proxmox/notification-target-form.ts
|
|
2159
2193
|
var REPEATED_KEY = "\x00";
|
|
@@ -2209,15 +2243,15 @@ var handlers14 = pveHandlers({
|
|
|
2209
2243
|
path: (props) => `cluster/notifications/endpoints/${props.type}/${props.name}`,
|
|
2210
2244
|
updateForm: shape4
|
|
2211
2245
|
});
|
|
2212
|
-
var ProxmoxNotificationTargetProvider = () => Provider17.effect(ProxmoxNotificationTarget,
|
|
2246
|
+
var ProxmoxNotificationTargetProvider = () => Provider17.effect(ProxmoxNotificationTarget, Effect35.succeed(ProxmoxNotificationTarget.Provider.of(handlers14)));
|
|
2213
2247
|
// src/proxmox/pbs-datastore.ts
|
|
2214
2248
|
import { Resource as Resource18 } from "alchemy";
|
|
2215
2249
|
import { isResolved as isResolved8 } from "alchemy/Diff";
|
|
2216
2250
|
import * as Provider18 from "alchemy/Provider";
|
|
2217
|
-
import * as
|
|
2251
|
+
import * as Effect38 from "effect/Effect";
|
|
2218
2252
|
|
|
2219
2253
|
// src/proxmox/pbs-datastore-form.ts
|
|
2220
|
-
import * as
|
|
2254
|
+
import * as Effect36 from "effect/Effect";
|
|
2221
2255
|
var attributes = (live, props) => ({
|
|
2222
2256
|
backend: propertyString(live["backend"]),
|
|
2223
2257
|
comment: text(live["comment"]),
|
|
@@ -2269,24 +2303,24 @@ var createForm4 = (props) => ({
|
|
|
2269
2303
|
});
|
|
2270
2304
|
var updateForm = (props) => mutable(props);
|
|
2271
2305
|
var object2 = (props) => `config/datastore/${props.name}`;
|
|
2272
|
-
var readOne = (props) => pve(props.target, "read", "GET", object2(props)).pipe(
|
|
2306
|
+
var readOne = (props) => pve(props.target, "read", "GET", object2(props)).pipe(Effect36.map((live) => live === undefined || typeof live["name"] !== "string" ? undefined : attributes(live, props)), Effect36.orElseSucceed(() => {
|
|
2273
2307
|
return;
|
|
2274
2308
|
}));
|
|
2275
2309
|
|
|
2276
2310
|
// src/proxmox/pbs-datastore-guard.ts
|
|
2277
|
-
import * as
|
|
2311
|
+
import * as Effect37 from "effect/Effect";
|
|
2278
2312
|
var samePath = (left, right) => {
|
|
2279
2313
|
const trim = (value) => value.length > 1 ? value.replace(/\/+$/, "") : value;
|
|
2280
2314
|
return trim(left) === trim(right);
|
|
2281
2315
|
};
|
|
2282
|
-
var guardPath = (live, props) => live.path === "" || samePath(live.path, props.path) ?
|
|
2283
|
-
var guardBackend = (live, props) => props.backend === undefined || propertyString(props.backend) === live.backend ?
|
|
2284
|
-
var settle3 = (read3, done) =>
|
|
2316
|
+
var guardPath = (live, props) => live.path === "" || samePath(live.path, props.path) ? Effect37.void : Effect37.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."));
|
|
2317
|
+
var guardBackend = (live, props) => props.backend === undefined || propertyString(props.backend) === live.backend ? Effect37.void : Effect37.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."));
|
|
2318
|
+
var settle3 = (read3, done) => Effect37.gen(function* () {
|
|
2285
2319
|
for (let attempt = 0;attempt < 60; attempt += 1) {
|
|
2286
2320
|
const live = yield* read3;
|
|
2287
2321
|
if (done(live))
|
|
2288
2322
|
return live;
|
|
2289
|
-
yield*
|
|
2323
|
+
yield* Effect37.sleep("2 seconds");
|
|
2290
2324
|
}
|
|
2291
2325
|
return yield* read3;
|
|
2292
2326
|
});
|
|
@@ -2295,10 +2329,10 @@ var settle3 = (read3, done) => Effect35.gen(function* () {
|
|
|
2295
2329
|
var PbsDatastore = Resource18("Pbs.Datastore", {
|
|
2296
2330
|
defaultRemovalPolicy: "retain"
|
|
2297
2331
|
});
|
|
2298
|
-
var PbsDatastoreProvider = () => Provider18.effect(PbsDatastore,
|
|
2299
|
-
list: () =>
|
|
2332
|
+
var PbsDatastoreProvider = () => Provider18.effect(PbsDatastore, Effect38.succeed(PbsDatastore.Provider.of({
|
|
2333
|
+
list: () => Effect38.succeed([]),
|
|
2300
2334
|
read: ({ olds }) => readOne(olds),
|
|
2301
|
-
diff: ({ news, output }) =>
|
|
2335
|
+
diff: ({ news, output }) => Effect38.gen(function* () {
|
|
2302
2336
|
if (output === undefined || !isResolved8(news))
|
|
2303
2337
|
return;
|
|
2304
2338
|
const live = yield* readOne(news);
|
|
@@ -2308,7 +2342,7 @@ var PbsDatastoreProvider = () => Provider18.effect(PbsDatastore, Effect36.succee
|
|
|
2308
2342
|
yield* guardBackend(live, news);
|
|
2309
2343
|
return matches(live, news) ? { action: "noop" } : { action: "update" };
|
|
2310
2344
|
}),
|
|
2311
|
-
reconcile:
|
|
2345
|
+
reconcile: Effect38.fn(function* ({ news }) {
|
|
2312
2346
|
const live = yield* readOne(news);
|
|
2313
2347
|
let upid;
|
|
2314
2348
|
if (live === undefined) {
|
|
@@ -2323,22 +2357,22 @@ var PbsDatastoreProvider = () => Provider18.effect(PbsDatastore, Effect36.succee
|
|
|
2323
2357
|
}
|
|
2324
2358
|
const after = yield* settle3(readOne(news), (row) => row !== undefined && matches(row, news));
|
|
2325
2359
|
if (after === undefined || !matches(after, news)) {
|
|
2326
|
-
return yield*
|
|
2360
|
+
return yield* Effect38.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."));
|
|
2327
2361
|
}
|
|
2328
2362
|
return after;
|
|
2329
2363
|
}),
|
|
2330
|
-
delete:
|
|
2364
|
+
delete: Effect38.fn(function* ({ olds }) {
|
|
2331
2365
|
yield* pve(olds.target, "provision", "DELETE", object2(olds));
|
|
2332
2366
|
const left = yield* settle3(readOne(olds), (row) => row === undefined);
|
|
2333
2367
|
if (left !== undefined) {
|
|
2334
|
-
return yield*
|
|
2368
|
+
return yield* Effect38.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>\`.`));
|
|
2335
2369
|
}
|
|
2336
2370
|
})
|
|
2337
2371
|
})));
|
|
2338
2372
|
// src/proxmox/pbs-prune-job.ts
|
|
2339
2373
|
import { Resource as Resource19 } from "alchemy";
|
|
2340
2374
|
import * as Provider19 from "alchemy/Provider";
|
|
2341
|
-
import * as
|
|
2375
|
+
import * as Effect39 from "effect/Effect";
|
|
2342
2376
|
|
|
2343
2377
|
// src/proxmox/pbs-prune-job-form.ts
|
|
2344
2378
|
var KEEP_KEYS = [
|
|
@@ -2413,11 +2447,11 @@ var handlers15 = pveHandlers({
|
|
|
2413
2447
|
path: (props) => `config/prune/${props.id}`,
|
|
2414
2448
|
updateForm: updateBody3
|
|
2415
2449
|
});
|
|
2416
|
-
var PbsPruneJobProvider = () => Provider19.effect(PbsPruneJob,
|
|
2450
|
+
var PbsPruneJobProvider = () => Provider19.effect(PbsPruneJob, Effect39.succeed(PbsPruneJob.Provider.of(handlers15)));
|
|
2417
2451
|
// src/proxmox/pbs-sync-job.ts
|
|
2418
2452
|
import { Resource as Resource20 } from "alchemy";
|
|
2419
2453
|
import * as Provider20 from "alchemy/Provider";
|
|
2420
|
-
import * as
|
|
2454
|
+
import * as Effect40 from "effect/Effect";
|
|
2421
2455
|
|
|
2422
2456
|
// src/proxmox/pbs-sync-job-form.ts
|
|
2423
2457
|
var UNSET4 = -1;
|
|
@@ -2493,11 +2527,11 @@ var handlers16 = pveHandlers({
|
|
|
2493
2527
|
|
|
2494
2528
|
// src/proxmox/pbs-sync-job.ts
|
|
2495
2529
|
var PbsSyncJob = Resource20("Pbs.SyncJob");
|
|
2496
|
-
var PbsSyncJobProvider = () => Provider20.effect(PbsSyncJob,
|
|
2530
|
+
var PbsSyncJobProvider = () => Provider20.effect(PbsSyncJob, Effect40.succeed(PbsSyncJob.Provider.of(handlers16)));
|
|
2497
2531
|
// src/proxmox/pbs-verify-job.ts
|
|
2498
2532
|
import { Resource as Resource21 } from "alchemy";
|
|
2499
2533
|
import * as Provider21 from "alchemy/Provider";
|
|
2500
|
-
import * as
|
|
2534
|
+
import * as Effect41 from "effect/Effect";
|
|
2501
2535
|
|
|
2502
2536
|
// src/proxmox/pbs-verify-job-form.ts
|
|
2503
2537
|
var rechecks = (ignoreVerified, outdatedAfter) => {
|
|
@@ -2541,11 +2575,11 @@ var handlers17 = pveHandlers({
|
|
|
2541
2575
|
path: (props) => `config/verify/${props.id}`,
|
|
2542
2576
|
updateForm: shape7
|
|
2543
2577
|
});
|
|
2544
|
-
var PbsVerifyJobProvider = () => Provider21.effect(PbsVerifyJob,
|
|
2578
|
+
var PbsVerifyJobProvider = () => Provider21.effect(PbsVerifyJob, Effect41.succeed(PbsVerifyJob.Provider.of(handlers17)));
|
|
2545
2579
|
// src/proxmox/pool.ts
|
|
2546
2580
|
import { Resource as Resource22 } from "alchemy";
|
|
2547
2581
|
import * as Provider22 from "alchemy/Provider";
|
|
2548
|
-
import * as
|
|
2582
|
+
import * as Effect42 from "effect/Effect";
|
|
2549
2583
|
var ProxmoxPool = Resource22("Proxmox.Pool");
|
|
2550
2584
|
var poolCreateForm = (props) => ({
|
|
2551
2585
|
comment: props.comment ?? "",
|
|
@@ -2569,13 +2603,287 @@ var handlers18 = pveHandlers({
|
|
|
2569
2603
|
path: (props) => `pools/${props.poolid}`,
|
|
2570
2604
|
updateForm: poolUpdateForm
|
|
2571
2605
|
});
|
|
2572
|
-
var ProxmoxPoolProvider = () => Provider22.effect(ProxmoxPool,
|
|
2573
|
-
// src/proxmox/
|
|
2606
|
+
var ProxmoxPoolProvider = () => Provider22.effect(ProxmoxPool, Effect42.succeed(ProxmoxPool.Provider.of(handlers18)));
|
|
2607
|
+
// src/proxmox/provision-baseline.ts
|
|
2608
|
+
var PROVISION_PRIVILEGES = Object.freeze([
|
|
2609
|
+
"Datastore.Allocate",
|
|
2610
|
+
"Datastore.AllocateSpace",
|
|
2611
|
+
"Datastore.Audit",
|
|
2612
|
+
"Group.Allocate",
|
|
2613
|
+
"Mapping.Audit",
|
|
2614
|
+
"Mapping.Modify",
|
|
2615
|
+
"Permissions.Modify",
|
|
2616
|
+
"Pool.Allocate",
|
|
2617
|
+
"Pool.Audit",
|
|
2618
|
+
"Realm.AllocateUser",
|
|
2619
|
+
"SDN.Allocate",
|
|
2620
|
+
"SDN.Audit",
|
|
2621
|
+
"SDN.Use",
|
|
2622
|
+
"Sys.Audit",
|
|
2623
|
+
"Sys.Console",
|
|
2624
|
+
"Sys.Modify",
|
|
2625
|
+
"User.Modify",
|
|
2626
|
+
"VM.Allocate",
|
|
2627
|
+
"VM.Audit",
|
|
2628
|
+
"VM.Backup",
|
|
2629
|
+
"VM.Config.CPU",
|
|
2630
|
+
"VM.Config.Disk",
|
|
2631
|
+
"VM.Config.HWType",
|
|
2632
|
+
"VM.Config.Memory",
|
|
2633
|
+
"VM.Config.Network",
|
|
2634
|
+
"VM.Config.Options",
|
|
2635
|
+
"VM.PowerMgmt"
|
|
2636
|
+
]);
|
|
2637
|
+
var PROVISION_DEFAULTS = Object.freeze({
|
|
2638
|
+
comment: "provision baseline from @homeflare/alchemy - OpenBao mints short-lived tokens for members",
|
|
2639
|
+
mintGroup: "hf-mint",
|
|
2640
|
+
provisionUser: "hf-provision@pve",
|
|
2641
|
+
readRole: "PVEAuditor",
|
|
2642
|
+
readUser: "hf-read@pve",
|
|
2643
|
+
role: "HfProvisioner"
|
|
2644
|
+
});
|
|
2645
|
+
var ID = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
2646
|
+
var USERID = /^[A-Za-z0-9][A-Za-z0-9._-]*@[A-Za-z][A-Za-z0-9._-]*$/;
|
|
2647
|
+
var COMMENT = /^[A-Za-z0-9 ._,()@/+-]*$/;
|
|
2648
|
+
var problems2 = (names) => {
|
|
2649
|
+
const found = [];
|
|
2650
|
+
if (!ID.test(names.role))
|
|
2651
|
+
found.push(`role \`${names.role}\` is not a PVE role id`);
|
|
2652
|
+
if (!ID.test(names.mintGroup))
|
|
2653
|
+
found.push(`mintGroup \`${names.mintGroup}\` is not a group id`);
|
|
2654
|
+
if (!ID.test(names.readRole))
|
|
2655
|
+
found.push(`readRole \`${names.readRole}\` is not a PVE role id`);
|
|
2656
|
+
for (const [field5, userid] of [
|
|
2657
|
+
["provisionUser", names.provisionUser],
|
|
2658
|
+
["readUser", names.readUser]
|
|
2659
|
+
]) {
|
|
2660
|
+
if (userid !== null && !USERID.test(userid)) {
|
|
2661
|
+
found.push(`${field5} \`${userid}\` is not a realm-qualified user id (name@realm)`);
|
|
2662
|
+
}
|
|
2663
|
+
}
|
|
2664
|
+
if (names.readUser === names.provisionUser)
|
|
2665
|
+
found.push("readUser and provisionUser are one user");
|
|
2666
|
+
if (!COMMENT.test(names.comment))
|
|
2667
|
+
found.push("comment holds a character the bootstrap will not quote");
|
|
2668
|
+
return found;
|
|
2669
|
+
};
|
|
2670
|
+
var provisionBaseline = (names = {}) => {
|
|
2671
|
+
const given = Object.entries(names).filter(([, value]) => value !== undefined);
|
|
2672
|
+
const resolved = {
|
|
2673
|
+
...PROVISION_DEFAULTS,
|
|
2674
|
+
...Object.fromEntries(given)
|
|
2675
|
+
};
|
|
2676
|
+
const found = problems2(resolved);
|
|
2677
|
+
if (found.length > 0)
|
|
2678
|
+
throw new Error(`provision baseline: ${found.join("; ")}`);
|
|
2679
|
+
const lanes = [
|
|
2680
|
+
["provision", resolved.provisionUser, resolved.role]
|
|
2681
|
+
];
|
|
2682
|
+
if (resolved.readUser !== null)
|
|
2683
|
+
lanes.push(["read", resolved.readUser, resolved.readRole]);
|
|
2684
|
+
return {
|
|
2685
|
+
grants: lanes.map(([lane, userid, roleid]) => ({ lane, roleid, userid })),
|
|
2686
|
+
group: { comment: resolved.comment, groupid: resolved.mintGroup },
|
|
2687
|
+
role: { privs: PROVISION_PRIVILEGES, roleid: resolved.role },
|
|
2688
|
+
users: lanes.map(([lane, userid]) => ({
|
|
2689
|
+
comment: resolved.comment,
|
|
2690
|
+
groups: [resolved.mintGroup],
|
|
2691
|
+
lane,
|
|
2692
|
+
userid
|
|
2693
|
+
}))
|
|
2694
|
+
};
|
|
2695
|
+
};
|
|
2696
|
+
// src/proxmox/provision-bootstrap.ts
|
|
2697
|
+
var HEADER = `#!/bin/sh
|
|
2698
|
+
# Provisioning baseline bootstrap, generated by @homeflare/alchemy (provisionBootstrap).
|
|
2699
|
+
# Run ONCE, as root, on one node of a NEW cluster (or a new standalone node). Every step reads
|
|
2700
|
+
# first and writes only what differs; a second run changes nothing. No token, no password.
|
|
2701
|
+
set -eu
|
|
2702
|
+
[ "$(id -u)" -eq 0 ] || { echo 'bootstrap: run this as root' >&2; exit 1; }
|
|
2703
|
+
command -v pvesh >/dev/null 2>&1 || { echo 'bootstrap: no pvesh; run this on a PVE node' >&2; exit 1; }
|
|
2704
|
+
perl -MJSON::PP -e 1 || { echo 'bootstrap: perl cannot load JSON::PP' >&2; exit 1; }
|
|
2705
|
+
|
|
2706
|
+
# get <api-path> <perl>: print <perl> evaluated over the object at <api-path> (as $d).
|
|
2707
|
+
# Fails, silently, when the object does not exist: the empty input is not JSON.
|
|
2708
|
+
get() {
|
|
2709
|
+
pvesh get "$1" --output-format json 2>/dev/null | perl -MJSON::PP -0777 -ne "\\$d = decode_json(\\$_); $2" 2>/dev/null
|
|
2710
|
+
}
|
|
2711
|
+
`;
|
|
2712
|
+
var perlText = (text3) => `q{${text3}}`;
|
|
2713
|
+
var USER_STATE = "my $g = $d->{groups} // []; $g = [split /,/, $g] unless ref $g; " + 'print join(";", "groups=" . join(",", sort @$g), "enable=" . (($d->{enable} // 1) ? 1 : 0), ' + '"expire=" . ($d->{expire} // 0), "email=" . ($d->{email} // ""), ' + '"firstname=" . ($d->{firstname} // ""), "lastname=" . ($d->{lastname} // ""), ' + '"comment=" . ($d->{comment} // ""))';
|
|
2714
|
+
var roleStep = ({ role }) => {
|
|
2715
|
+
const privs = [...role.privs].sort().join(",");
|
|
2716
|
+
return `
|
|
2717
|
+
# Role ${role.roleid}: exactly the provision privileges, on no path yet.
|
|
2718
|
+
want='${privs}'
|
|
2719
|
+
if have=$(get /access/roles/${role.roleid} 'print join(",", sort grep { /\\./ } keys %$d)'); then
|
|
2720
|
+
if [ "$have" = "$want" ]; then echo 'role ${role.roleid}: ok'
|
|
2721
|
+
else echo 'role ${role.roleid}: setting the baseline privileges'; pveum role modify ${role.roleid} --privs "$want"; fi
|
|
2722
|
+
else
|
|
2723
|
+
echo 'role ${role.roleid}: adding'; pveum role add ${role.roleid} --privs "$want"
|
|
2724
|
+
fi
|
|
2725
|
+
`;
|
|
2726
|
+
};
|
|
2727
|
+
var groupStep = ({ group }) => `
|
|
2728
|
+
# Group ${group.groupid}: OpenBao may mint only for its members.
|
|
2729
|
+
if have=$(get /access/groups/${group.groupid} 'print $d->{comment} // ""'); then
|
|
2730
|
+
if [ "$have" = '${group.comment}' ]; then echo 'group ${group.groupid}: ok'
|
|
2731
|
+
else echo 'group ${group.groupid}: setting the comment'; pveum group modify ${group.groupid} --comment '${group.comment}'; fi
|
|
2732
|
+
else
|
|
2733
|
+
echo 'group ${group.groupid}: adding'; pveum group add ${group.groupid} --comment '${group.comment}'
|
|
2734
|
+
fi
|
|
2735
|
+
`;
|
|
2736
|
+
var userStep = (user) => {
|
|
2737
|
+
const groups = [...user.groups].sort().join(",");
|
|
2738
|
+
const want = `groups=${groups};enable=1;expire=0;email=;firstname=;lastname=;comment=${user.comment}`;
|
|
2739
|
+
const fields = `--groups ${groups} --comment '${user.comment}'`;
|
|
2740
|
+
return `
|
|
2741
|
+
# User ${user.userid} (the ${user.lane} lane): in ${groups}, enabled, no password.
|
|
2742
|
+
if have=$(get /access/users/${user.userid} '${USER_STATE}'); then
|
|
2743
|
+
if [ "$have" = '${want}' ]; then echo 'user ${user.userid}: ok'
|
|
2744
|
+
else echo 'user ${user.userid}: setting groups, comment and account fields'
|
|
2745
|
+
pveum user modify ${user.userid} ${fields} --enable 1 --expire 0 --email '' --firstname '' --lastname ''; fi
|
|
2746
|
+
else
|
|
2747
|
+
echo 'user ${user.userid}: adding'; pveum user add ${user.userid} ${fields}
|
|
2748
|
+
fi
|
|
2749
|
+
`;
|
|
2750
|
+
};
|
|
2751
|
+
var grantStep = (grant) => {
|
|
2752
|
+
const match = `print scalar grep { $_->{path} eq "/" && $_->{type} eq "user" && ` + `$_->{ugid} eq ${perlText(grant.userid)} && $_->{roleid} eq ${perlText(grant.roleid)} && ` + `($_->{propagate} // 1) } @$d`;
|
|
2753
|
+
return `
|
|
2754
|
+
# Grant: ${grant.userid} holds ${grant.roleid} on /, propagated (the ${grant.lane} lane).
|
|
2755
|
+
if [ "$(get /access/acl '${match}')" = 1 ]; then echo 'grant ${grant.userid} ${grant.roleid} /: ok'
|
|
2756
|
+
else echo 'grant ${grant.userid} ${grant.roleid} /: adding'; pveum acl modify / --users ${grant.userid} --roles ${grant.roleid} --propagate 1; fi
|
|
2757
|
+
`;
|
|
2758
|
+
};
|
|
2759
|
+
var requireRole = (roleid) => `
|
|
2760
|
+
get /access/roles/${roleid} 'print 1' >/dev/null || { echo 'bootstrap: role ${roleid} does not exist; create it, or name another readRole' >&2; exit 1; }
|
|
2761
|
+
`;
|
|
2762
|
+
var provisionBootstrap = (names = {}) => {
|
|
2763
|
+
const baseline = provisionBaseline(names);
|
|
2764
|
+
const borrowed = baseline.grants.map((grant) => grant.roleid).filter((roleid) => roleid !== baseline.role.roleid);
|
|
2765
|
+
return [
|
|
2766
|
+
HEADER,
|
|
2767
|
+
...[...new Set(borrowed)].map(requireRole),
|
|
2768
|
+
roleStep(baseline),
|
|
2769
|
+
groupStep(baseline),
|
|
2770
|
+
...baseline.users.map(userStep),
|
|
2771
|
+
...baseline.grants.map(grantStep),
|
|
2772
|
+
`
|
|
2773
|
+
echo 'provision baseline: done. Next: point the OpenBao proxmox mount at these users, then deploy the stack that declares this baseline with --adopt.'
|
|
2774
|
+
`
|
|
2775
|
+
].join("");
|
|
2776
|
+
};
|
|
2777
|
+
// src/proxmox/provision-declare.ts
|
|
2778
|
+
import { adopt } from "alchemy/AdoptPolicy";
|
|
2779
|
+
import * as RemovalPolicy from "alchemy/RemovalPolicy";
|
|
2780
|
+
import * as Effect45 from "effect/Effect";
|
|
2781
|
+
|
|
2782
|
+
// src/proxmox/role.ts
|
|
2574
2783
|
import { Resource as Resource23 } from "alchemy";
|
|
2575
2784
|
import * as Provider23 from "alchemy/Provider";
|
|
2576
|
-
import * as
|
|
2577
|
-
var
|
|
2785
|
+
import * as Effect43 from "effect/Effect";
|
|
2786
|
+
var ProxmoxRole = Resource23("Proxmox.Role");
|
|
2787
|
+
var canonical = (privs) => [...new Set(privs.map((priv) => priv.trim()).filter((priv) => priv.length > 0))].sort();
|
|
2788
|
+
var livePrivs = (live) => {
|
|
2789
|
+
const listed = live["privs"];
|
|
2790
|
+
if (typeof listed === "string")
|
|
2791
|
+
return canonical(listed.split(","));
|
|
2792
|
+
return canonical(Object.keys(live).filter((key) => key.includes(".")));
|
|
2793
|
+
};
|
|
2794
|
+
var handlers19 = pveHandlers({
|
|
2795
|
+
attributes: (live, props) => ({ privs: livePrivs(live), roleid: props.roleid }),
|
|
2796
|
+
collection: () => "access/roles",
|
|
2797
|
+
createForm: (props) => ({ privs: canonical(props.privs).join(","), roleid: props.roleid }),
|
|
2798
|
+
matches: (attributes2, props) => canonical(attributes2.privs).join(",") === canonical(props.privs).join(","),
|
|
2799
|
+
path: (props) => `access/roles/${props.roleid}`,
|
|
2800
|
+
updateForm: (props) => ({ privs: canonical(props.privs).join(",") })
|
|
2801
|
+
});
|
|
2802
|
+
var ProxmoxRoleProvider = () => Provider23.effect(ProxmoxRole, Effect43.succeed(ProxmoxRole.Provider.of(handlers19)));
|
|
2803
|
+
|
|
2804
|
+
// src/proxmox/user.ts
|
|
2805
|
+
import { Resource as Resource24 } from "alchemy";
|
|
2806
|
+
import * as Provider24 from "alchemy/Provider";
|
|
2807
|
+
import * as Effect44 from "effect/Effect";
|
|
2808
|
+
var ProxmoxUser = Resource24("Proxmox.User");
|
|
2809
|
+
var str2 = (value) => typeof value === "string" ? value : "";
|
|
2810
|
+
var int2 = (value) => {
|
|
2811
|
+
const parsed = typeof value === "number" ? value : Number(value ?? 0);
|
|
2812
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
2813
|
+
};
|
|
2814
|
+
var groupSet = (value) => {
|
|
2815
|
+
const raw = Array.isArray(value) ? value : typeof value === "string" ? value.split(",") : [];
|
|
2816
|
+
const names = raw.map((group) => str2(group).trim()).filter((group) => group !== "");
|
|
2817
|
+
return [...new Set(names)].sort();
|
|
2818
|
+
};
|
|
2819
|
+
var tokenNames = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? Object.keys(value).sort() : [];
|
|
2578
2820
|
var shape8 = (props) => ({
|
|
2821
|
+
comment: props.comment ?? "",
|
|
2822
|
+
email: props.email ?? "",
|
|
2823
|
+
enable: props.enable === false ? "0" : "1",
|
|
2824
|
+
expire: String(props.expire ?? 0),
|
|
2825
|
+
firstname: props.firstname ?? "",
|
|
2826
|
+
groups: groupSet(props.groups).join(","),
|
|
2827
|
+
lastname: props.lastname ?? ""
|
|
2828
|
+
});
|
|
2829
|
+
var handlers20 = pveHandlers({
|
|
2830
|
+
attributes: (live, props) => ({
|
|
2831
|
+
comment: str2(live["comment"]),
|
|
2832
|
+
email: str2(live["email"]),
|
|
2833
|
+
enable: bool(live["enable"], true),
|
|
2834
|
+
expire: int2(live["expire"]),
|
|
2835
|
+
firstname: str2(live["firstname"]),
|
|
2836
|
+
groups: groupSet(live["groups"]),
|
|
2837
|
+
lastname: str2(live["lastname"]),
|
|
2838
|
+
tokens: tokenNames(live["tokens"]),
|
|
2839
|
+
userid: props.userid
|
|
2840
|
+
}),
|
|
2841
|
+
collection: () => "access/users",
|
|
2842
|
+
createForm: (props) => ({ ...shape8(props), userid: props.userid }),
|
|
2843
|
+
matches: (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(","),
|
|
2844
|
+
path: (props) => `access/users/${props.userid}`,
|
|
2845
|
+
updateForm: shape8
|
|
2846
|
+
});
|
|
2847
|
+
var ProxmoxUserProvider = () => Provider24.effect(ProxmoxUser, Effect44.succeed(ProxmoxUser.Provider.of(handlers20)));
|
|
2848
|
+
|
|
2849
|
+
// src/proxmox/provision-declare.ts
|
|
2850
|
+
var declareProvisionBaseline = (id, target, names = {}, options = {}) => Effect45.gen(function* () {
|
|
2851
|
+
const baseline = yield* Effect45.try({
|
|
2852
|
+
catch: (cause) => cause instanceof Error ? cause : new Error(String(cause)),
|
|
2853
|
+
try: () => provisionBaseline(names)
|
|
2854
|
+
});
|
|
2855
|
+
const owned = (resource) => {
|
|
2856
|
+
const kept = resource.pipe(RemovalPolicy.retain());
|
|
2857
|
+
return options.adopt === undefined ? kept : kept.pipe(adopt(options.adopt));
|
|
2858
|
+
};
|
|
2859
|
+
const role = yield* owned(ProxmoxRole(`${id}-role`, {
|
|
2860
|
+
privs: [...baseline.role.privs],
|
|
2861
|
+
roleid: baseline.role.roleid,
|
|
2862
|
+
target
|
|
2863
|
+
}));
|
|
2864
|
+
const group = yield* owned(ProxmoxGroup(`${id}-mint-group`, { ...baseline.group, target }));
|
|
2865
|
+
const users = yield* Effect45.all(baseline.users.map((user) => owned(ProxmoxUser(`${id}-${user.lane}-user`, {
|
|
2866
|
+
comment: user.comment,
|
|
2867
|
+
groups: [...user.groups],
|
|
2868
|
+
target,
|
|
2869
|
+
userid: user.userid
|
|
2870
|
+
}))));
|
|
2871
|
+
const grants = yield* Effect45.all(baseline.grants.map((grant) => owned(ProxmoxAcl(`${id}-${grant.lane}-grant`, {
|
|
2872
|
+
path: "/",
|
|
2873
|
+
propagate: true,
|
|
2874
|
+
roleid: grant.roleid,
|
|
2875
|
+
target,
|
|
2876
|
+
type: "user",
|
|
2877
|
+
ugid: grant.userid
|
|
2878
|
+
}))));
|
|
2879
|
+
return { baseline, grants, group, role, users };
|
|
2880
|
+
});
|
|
2881
|
+
// src/proxmox/qemu.ts
|
|
2882
|
+
import { Resource as Resource25 } from "alchemy";
|
|
2883
|
+
import * as Provider25 from "alchemy/Provider";
|
|
2884
|
+
import * as Effect46 from "effect/Effect";
|
|
2885
|
+
var ProxmoxVm = Resource25("Proxmox.Vm");
|
|
2886
|
+
var shape9 = (props) => ({
|
|
2579
2887
|
cores: String(props.cores ?? 1),
|
|
2580
2888
|
memory: String(props.memory ?? 512),
|
|
2581
2889
|
name: props.name ?? `vm${String(props.vmid)}`,
|
|
@@ -2583,7 +2891,7 @@ var shape8 = (props) => ({
|
|
|
2583
2891
|
sockets: String(props.sockets ?? 1),
|
|
2584
2892
|
...props.net0 === undefined ? {} : { net0: props.net0 }
|
|
2585
2893
|
});
|
|
2586
|
-
var
|
|
2894
|
+
var handlers21 = pveHandlers({
|
|
2587
2895
|
attributes: (live, props) => ({
|
|
2588
2896
|
cores: num(live["cores"], 1),
|
|
2589
2897
|
memory: num(live["memory"], 512),
|
|
@@ -2594,46 +2902,46 @@ var handlers19 = pveHandlers({
|
|
|
2594
2902
|
vmid: props.vmid
|
|
2595
2903
|
}),
|
|
2596
2904
|
collection: (props) => `nodes/${props.node}/qemu`,
|
|
2597
|
-
createForm: (props) => ({ ...
|
|
2905
|
+
createForm: (props) => ({ ...shape9(props), vmid: String(props.vmid) }),
|
|
2598
2906
|
matches: (attributes2, props) => attributes2.name === (props.name ?? `vm${String(props.vmid)}`) && attributes2.memory === (props.memory ?? 512) && attributes2.cores === (props.cores ?? 1) && attributes2.sockets === (props.sockets ?? 1) && attributes2.onboot === (props.onboot === true),
|
|
2599
2907
|
path: (props) => `nodes/${props.node}/qemu/${String(props.vmid)}/config`,
|
|
2600
|
-
updateForm:
|
|
2908
|
+
updateForm: shape9
|
|
2601
2909
|
});
|
|
2602
|
-
var ProxmoxVmProvider = () =>
|
|
2910
|
+
var ProxmoxVmProvider = () => Provider25.effect(ProxmoxVm, Effect46.succeed(ProxmoxVm.Provider.of(handlers21)));
|
|
2603
2911
|
// src/proxmox/replication-job.ts
|
|
2604
|
-
import { Resource as
|
|
2605
|
-
import * as
|
|
2606
|
-
import * as
|
|
2912
|
+
import { Resource as Resource26 } from "alchemy";
|
|
2913
|
+
import * as Provider26 from "alchemy/Provider";
|
|
2914
|
+
import * as Effect47 from "effect/Effect";
|
|
2607
2915
|
|
|
2608
2916
|
// src/proxmox/replication-job-form.ts
|
|
2609
2917
|
var DEFAULT_SCHEDULE = "*/15";
|
|
2610
2918
|
var jobId = (props) => `${String(props.guest)}-${String(props.jobnum)}`;
|
|
2611
|
-
var
|
|
2919
|
+
var shape10 = (props) => ({
|
|
2612
2920
|
comment: props.comment ?? "",
|
|
2613
2921
|
disable: props.disable === true ? "1" : "0",
|
|
2614
2922
|
schedule: props.schedule ?? DEFAULT_SCHEDULE,
|
|
2615
2923
|
...props.rate === undefined ? {} : { rate: String(props.rate) }
|
|
2616
2924
|
});
|
|
2617
2925
|
var createBody4 = (props) => ({
|
|
2618
|
-
...
|
|
2926
|
+
...shape10(props),
|
|
2619
2927
|
id: jobId(props),
|
|
2620
2928
|
target: props.targetNode,
|
|
2621
2929
|
type: "local"
|
|
2622
2930
|
});
|
|
2623
2931
|
var updateBody5 = (props) => ({
|
|
2624
|
-
...
|
|
2932
|
+
...shape10(props),
|
|
2625
2933
|
delete: "remove_job"
|
|
2626
2934
|
});
|
|
2627
2935
|
|
|
2628
2936
|
// src/proxmox/replication-job.ts
|
|
2629
|
-
var ProxmoxReplicationJob =
|
|
2937
|
+
var ProxmoxReplicationJob = Resource26("Proxmox.ReplicationJob");
|
|
2630
2938
|
var rateOf = (value) => {
|
|
2631
2939
|
if (typeof value === "number")
|
|
2632
2940
|
return value;
|
|
2633
2941
|
const parsed = Number.parseFloat(text(value));
|
|
2634
2942
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
2635
2943
|
};
|
|
2636
|
-
var
|
|
2944
|
+
var handlers22 = pveHandlers({
|
|
2637
2945
|
attributes: (live, props) => ({
|
|
2638
2946
|
comment: text(live["comment"], ""),
|
|
2639
2947
|
disable: bool(live["disable"], false),
|
|
@@ -2653,36 +2961,15 @@ var handlers20 = pveHandlers({
|
|
|
2653
2961
|
path: (props) => `cluster/replication/${jobId(props)}`,
|
|
2654
2962
|
updateForm: updateBody5
|
|
2655
2963
|
});
|
|
2656
|
-
var ProxmoxReplicationJobProvider = () =>
|
|
2657
|
-
// src/proxmox/role.ts
|
|
2658
|
-
import { Resource as Resource25 } from "alchemy";
|
|
2659
|
-
import * as Provider25 from "alchemy/Provider";
|
|
2660
|
-
import * as Effect43 from "effect/Effect";
|
|
2661
|
-
var ProxmoxRole = Resource25("Proxmox.Role");
|
|
2662
|
-
var canonical = (privs) => [...new Set(privs.map((priv) => priv.trim()).filter((priv) => priv.length > 0))].sort();
|
|
2663
|
-
var livePrivs = (live) => {
|
|
2664
|
-
const listed = live["privs"];
|
|
2665
|
-
if (typeof listed === "string")
|
|
2666
|
-
return canonical(listed.split(","));
|
|
2667
|
-
return canonical(Object.keys(live).filter((key) => key.includes(".")));
|
|
2668
|
-
};
|
|
2669
|
-
var handlers21 = pveHandlers({
|
|
2670
|
-
attributes: (live, props) => ({ privs: livePrivs(live), roleid: props.roleid }),
|
|
2671
|
-
collection: () => "access/roles",
|
|
2672
|
-
createForm: (props) => ({ privs: canonical(props.privs).join(","), roleid: props.roleid }),
|
|
2673
|
-
matches: (attributes2, props) => canonical(attributes2.privs).join(",") === canonical(props.privs).join(","),
|
|
2674
|
-
path: (props) => `access/roles/${props.roleid}`,
|
|
2675
|
-
updateForm: (props) => ({ privs: canonical(props.privs).join(",") })
|
|
2676
|
-
});
|
|
2677
|
-
var ProxmoxRoleProvider = () => Provider25.effect(ProxmoxRole, Effect43.succeed(ProxmoxRole.Provider.of(handlers21)));
|
|
2964
|
+
var ProxmoxReplicationJobProvider = () => Provider26.effect(ProxmoxReplicationJob, Effect47.succeed(ProxmoxReplicationJob.Provider.of(handlers22)));
|
|
2678
2965
|
// src/proxmox/sdn-apply.ts
|
|
2679
|
-
import { Resource as
|
|
2966
|
+
import { Resource as Resource27 } from "alchemy";
|
|
2680
2967
|
import { isResolved as isResolved9 } from "alchemy/Diff";
|
|
2681
|
-
import * as
|
|
2682
|
-
import * as
|
|
2968
|
+
import * as Provider27 from "alchemy/Provider";
|
|
2969
|
+
import * as Effect49 from "effect/Effect";
|
|
2683
2970
|
|
|
2684
2971
|
// src/proxmox/sdn-apply-read.ts
|
|
2685
|
-
import * as
|
|
2972
|
+
import * as Effect48 from "effect/Effect";
|
|
2686
2973
|
var MARKED = [
|
|
2687
2974
|
"cluster/sdn/zones",
|
|
2688
2975
|
"cluster/sdn/vnets",
|
|
@@ -2691,7 +2978,7 @@ var MARKED = [
|
|
|
2691
2978
|
"cluster/sdn/route-maps"
|
|
2692
2979
|
];
|
|
2693
2980
|
var subsystemAbsent = (error) => error instanceof PveError && error.status === 501;
|
|
2694
|
-
var markedPending = (target) =>
|
|
2981
|
+
var markedPending = (target) => Effect48.all(MARKED.map((collection2) => pve(target, "read", "GET", `${collection2}?pending=1`).pipe(Effect48.map((rows) => (rows ?? []).filter((row) => row.state !== undefined).length), Effect48.catchIf(subsystemAbsent, () => Effect48.succeed(0)))), { concurrency: "unbounded" }).pipe(Effect48.map((counts) => counts.reduce((total, count2) => total + count2, 0)));
|
|
2695
2982
|
var fabricStaged = (staged, running) => {
|
|
2696
2983
|
if (staged === undefined && running === undefined)
|
|
2697
2984
|
return 0;
|
|
@@ -2699,9 +2986,9 @@ var fabricStaged = (staged, running) => {
|
|
|
2699
2986
|
return "inconsistent";
|
|
2700
2987
|
return staged === running ? 0 : 1;
|
|
2701
2988
|
};
|
|
2702
|
-
var fabricPending = (target) =>
|
|
2989
|
+
var fabricPending = (target) => Effect48.all(["pending", "running"].map((view) => pve(target, "read", "GET", `cluster/sdn/fabrics/all?${view}=1`).pipe(Effect48.map(canonical2), Effect48.catchIf(subsystemAbsent, () => Effect48.succeed(undefined)))), { concurrency: "unbounded" }).pipe(Effect48.flatMap(([staged, running]) => {
|
|
2703
2990
|
const verdict = fabricStaged(staged, running);
|
|
2704
|
-
return verdict === "inconsistent" ?
|
|
2991
|
+
return verdict === "inconsistent" ? Effect48.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.")) : Effect48.succeed(verdict);
|
|
2705
2992
|
}));
|
|
2706
2993
|
var canonical2 = (value) => JSON.stringify(sortDeep(value));
|
|
2707
2994
|
var sortDeep = (value) => {
|
|
@@ -2714,39 +3001,39 @@ var sortDeep = (value) => {
|
|
|
2714
3001
|
}
|
|
2715
3002
|
return value;
|
|
2716
3003
|
};
|
|
2717
|
-
var sdnPendingCount = (target) =>
|
|
3004
|
+
var sdnPendingCount = (target) => Effect48.all([markedPending(target), fabricPending(target)], { concurrency: "unbounded" }).pipe(Effect48.map(([marked, fabric]) => marked + fabric));
|
|
2718
3005
|
|
|
2719
3006
|
// src/proxmox/sdn-apply.ts
|
|
2720
|
-
var ProxmoxSdnApply =
|
|
2721
|
-
var read3 = (props) => sdnPendingCount(props.target).pipe(
|
|
2722
|
-
var ProxmoxSdnApplyProvider = () =>
|
|
2723
|
-
list: () =>
|
|
2724
|
-
read:
|
|
3007
|
+
var ProxmoxSdnApply = Resource27("Proxmox.SdnApply");
|
|
3008
|
+
var read3 = (props) => sdnPendingCount(props.target).pipe(Effect49.map((pending) => ({ pending })));
|
|
3009
|
+
var ProxmoxSdnApplyProvider = () => Provider27.effect(ProxmoxSdnApply, Effect49.succeed(ProxmoxSdnApply.Provider.of({
|
|
3010
|
+
list: () => Effect49.succeed([]),
|
|
3011
|
+
read: Effect49.fn(function* ({ olds }) {
|
|
2725
3012
|
return yield* read3(olds);
|
|
2726
3013
|
}),
|
|
2727
|
-
diff:
|
|
3014
|
+
diff: Effect49.fn(function* ({ news }) {
|
|
2728
3015
|
if (!isResolved9(news))
|
|
2729
3016
|
return;
|
|
2730
3017
|
const { pending } = yield* read3(news);
|
|
2731
3018
|
return pending === 0 ? { action: "noop" } : { action: "update" };
|
|
2732
3019
|
}),
|
|
2733
|
-
reconcile:
|
|
3020
|
+
reconcile: Effect49.fn(function* ({ news }) {
|
|
2734
3021
|
const staged = yield* read3(news);
|
|
2735
3022
|
if (staged.pending === 0)
|
|
2736
3023
|
return staged;
|
|
2737
3024
|
yield* pve(news.target, "provision", "PUT", "cluster/sdn");
|
|
2738
3025
|
const after = yield* read3(news);
|
|
2739
3026
|
if (after.pending !== 0) {
|
|
2740
|
-
return yield*
|
|
3027
|
+
return yield* Effect49.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."));
|
|
2741
3028
|
}
|
|
2742
3029
|
return after;
|
|
2743
3030
|
}),
|
|
2744
|
-
delete: () =>
|
|
3031
|
+
delete: () => Effect49.void
|
|
2745
3032
|
})));
|
|
2746
3033
|
// src/proxmox/sdn-subnet.ts
|
|
2747
|
-
import { Resource as
|
|
2748
|
-
import * as
|
|
2749
|
-
import * as
|
|
3034
|
+
import { Resource as Resource28 } from "alchemy";
|
|
3035
|
+
import * as Provider28 from "alchemy/Provider";
|
|
3036
|
+
import * as Effect50 from "effect/Effect";
|
|
2750
3037
|
|
|
2751
3038
|
// src/proxmox/sdn-subnet-form.ts
|
|
2752
3039
|
var subnetId = (props) => `${props.zone}-${props.cidr.replace("/", "-")}`;
|
|
@@ -2776,8 +3063,8 @@ var updateForm2 = (props) => {
|
|
|
2776
3063
|
};
|
|
2777
3064
|
|
|
2778
3065
|
// src/proxmox/sdn-subnet.ts
|
|
2779
|
-
var ProxmoxSdnSubnet =
|
|
2780
|
-
var
|
|
3066
|
+
var ProxmoxSdnSubnet = Resource28("Proxmox.SdnSubnet");
|
|
3067
|
+
var handlers23 = pveHandlers({
|
|
2781
3068
|
attributes: (live, props) => {
|
|
2782
3069
|
const liveVnet = text(live["vnet"]);
|
|
2783
3070
|
if (liveVnet !== "" && liveVnet !== props.vnet)
|
|
@@ -2802,14 +3089,14 @@ var handlers22 = pveHandlers({
|
|
|
2802
3089
|
path: (props) => `cluster/sdn/vnets/${props.vnet}/subnets/${subnetId(props)}`,
|
|
2803
3090
|
updateForm: updateForm2
|
|
2804
3091
|
});
|
|
2805
|
-
var ProxmoxSdnSubnetProvider = () =>
|
|
3092
|
+
var ProxmoxSdnSubnetProvider = () => Provider28.effect(ProxmoxSdnSubnet, Effect50.succeed(ProxmoxSdnSubnet.Provider.of(handlers23)));
|
|
2806
3093
|
// src/proxmox/sdn-vnet.ts
|
|
2807
|
-
import { Resource as
|
|
2808
|
-
import * as
|
|
2809
|
-
import * as
|
|
2810
|
-
var ProxmoxSdnVnet =
|
|
3094
|
+
import { Resource as Resource29 } from "alchemy";
|
|
3095
|
+
import * as Provider29 from "alchemy/Provider";
|
|
3096
|
+
import * as Effect51 from "effect/Effect";
|
|
3097
|
+
var ProxmoxSdnVnet = Resource29("Proxmox.SdnVnet");
|
|
2811
3098
|
var tagOf = (value) => typeof value === "number" ? value : undefined;
|
|
2812
|
-
var
|
|
3099
|
+
var shape11 = (props) => {
|
|
2813
3100
|
const alias = props.alias ?? "";
|
|
2814
3101
|
return {
|
|
2815
3102
|
"isolate-ports": props.isolatePorts === true ? "1" : "0",
|
|
@@ -2824,9 +3111,9 @@ var updateForm3 = (props) => {
|
|
|
2824
3111
|
...(props.alias ?? "") === "" ? ["alias"] : [],
|
|
2825
3112
|
...props.tag === undefined ? ["tag"] : []
|
|
2826
3113
|
];
|
|
2827
|
-
return withClears(
|
|
3114
|
+
return withClears(shape11(props), unset2);
|
|
2828
3115
|
};
|
|
2829
|
-
var
|
|
3116
|
+
var handlers24 = pveHandlers({
|
|
2830
3117
|
readRole: "provision",
|
|
2831
3118
|
attributes: (live, props) => ({
|
|
2832
3119
|
alias: typeof live["alias"] === "string" ? live["alias"] : "",
|
|
@@ -2838,17 +3125,17 @@ var handlers23 = pveHandlers({
|
|
|
2838
3125
|
zone: typeof live["zone"] === "string" ? live["zone"] : ""
|
|
2839
3126
|
}),
|
|
2840
3127
|
collection: () => "cluster/sdn/vnets",
|
|
2841
|
-
createForm: (props) => ({ ...
|
|
3128
|
+
createForm: (props) => ({ ...shape11(props), vnet: props.vnet }),
|
|
2842
3129
|
matches: (attributes2, props) => attributes2.zone === props.zone && attributes2.alias === (props.alias ?? "") && attributes2.tag === props.tag && attributes2.vlanaware === (props.vlanaware === true) && attributes2.isolatePorts === (props.isolatePorts === true),
|
|
2843
3130
|
path: (props) => `cluster/sdn/vnets/${props.vnet}`,
|
|
2844
3131
|
updateForm: updateForm3
|
|
2845
3132
|
});
|
|
2846
|
-
var ProxmoxSdnVnetProvider = () =>
|
|
3133
|
+
var ProxmoxSdnVnetProvider = () => Provider29.effect(ProxmoxSdnVnet, Effect51.succeed(ProxmoxSdnVnet.Provider.of(handlers24)));
|
|
2847
3134
|
// src/proxmox/sdn-zone.ts
|
|
2848
|
-
import { Resource as
|
|
2849
|
-
import * as
|
|
2850
|
-
import * as
|
|
2851
|
-
var ProxmoxSdnZone =
|
|
3135
|
+
import { Resource as Resource30 } from "alchemy";
|
|
3136
|
+
import * as Provider30 from "alchemy/Provider";
|
|
3137
|
+
import * as Effect52 from "effect/Effect";
|
|
3138
|
+
var ProxmoxSdnZone = Resource30("Proxmox.SdnZone");
|
|
2852
3139
|
var sameIpam = (live, declared) => (live === "" ? "pve" : live) === (declared === undefined || declared === "" ? "pve" : declared);
|
|
2853
3140
|
var body6 = (props) => ({
|
|
2854
3141
|
...props.bridge === undefined ? {} : { bridge: props.bridge },
|
|
@@ -2868,7 +3155,7 @@ var clearable = {
|
|
|
2868
3155
|
vlan: ["dns", "dnszone", "ipam", "mtu", "nodes"],
|
|
2869
3156
|
vxlan: ["dns", "dnszone", "ipam", "mtu", "nodes"]
|
|
2870
3157
|
};
|
|
2871
|
-
var
|
|
3158
|
+
var handlers25 = pveHandlers({
|
|
2872
3159
|
readRole: "provision",
|
|
2873
3160
|
attributes: (live, props) => {
|
|
2874
3161
|
const liveType = text(live["type"]);
|
|
@@ -2899,15 +3186,15 @@ var handlers24 = pveHandlers({
|
|
|
2899
3186
|
return withClears(fields, clear);
|
|
2900
3187
|
}
|
|
2901
3188
|
});
|
|
2902
|
-
var ProxmoxSdnZoneProvider = () =>
|
|
3189
|
+
var ProxmoxSdnZoneProvider = () => Provider30.effect(ProxmoxSdnZone, Effect52.succeed(ProxmoxSdnZone.Provider.of(handlers25)));
|
|
2903
3190
|
// src/proxmox/storage.ts
|
|
2904
|
-
import { Resource as
|
|
2905
|
-
import * as
|
|
2906
|
-
import * as
|
|
2907
|
-
var ProxmoxStorage =
|
|
3191
|
+
import { Resource as Resource31 } from "alchemy";
|
|
3192
|
+
import * as Provider31 from "alchemy/Provider";
|
|
3193
|
+
import * as Effect53 from "effect/Effect";
|
|
3194
|
+
var ProxmoxStorage = Resource31("Proxmox.Storage", {
|
|
2908
3195
|
defaultRemovalPolicy: "retain"
|
|
2909
3196
|
});
|
|
2910
|
-
var
|
|
3197
|
+
var str3 = (value, fallback = "") => typeof value === "string" ? value : fallback;
|
|
2911
3198
|
var field5 = (name, value) => value === undefined ? {} : { [name]: value };
|
|
2912
3199
|
var locatorForm = (locator) => ({ ...locator });
|
|
2913
3200
|
var tokens = (value) => value.split(",").map((part) => part.trim()).filter((part) => part !== "").sort().join(",");
|
|
@@ -2923,19 +3210,19 @@ var mutable2 = (props) => ({
|
|
|
2923
3210
|
...field5("prune-backups", props["prune-backups"]),
|
|
2924
3211
|
...field5("shared", flag(props.shared))
|
|
2925
3212
|
});
|
|
2926
|
-
var
|
|
3213
|
+
var handlers26 = pveHandlers({
|
|
2927
3214
|
readRole: "provision",
|
|
2928
3215
|
attributes: (live, props) => ({
|
|
2929
|
-
comment:
|
|
2930
|
-
content:
|
|
3216
|
+
comment: str3(live["comment"]),
|
|
3217
|
+
content: str3(live["content"]),
|
|
2931
3218
|
disable: bool(live["disable"]),
|
|
2932
3219
|
maxfiles: typeof live["maxfiles"] === "number" ? live["maxfiles"] : -1,
|
|
2933
|
-
nodes:
|
|
2934
|
-
preallocation:
|
|
2935
|
-
"prune-backups":
|
|
3220
|
+
nodes: str3(live["nodes"]),
|
|
3221
|
+
preallocation: str3(live["preallocation"]),
|
|
3222
|
+
"prune-backups": str3(live["prune-backups"]),
|
|
2936
3223
|
shared: bool(live["shared"]),
|
|
2937
3224
|
storage: props.storage,
|
|
2938
|
-
type:
|
|
3225
|
+
type: str3(live["type"], props.type)
|
|
2939
3226
|
}),
|
|
2940
3227
|
collection: () => "storage",
|
|
2941
3228
|
createForm: (props) => ({
|
|
@@ -2948,58 +3235,14 @@ var handlers25 = pveHandlers({
|
|
|
2948
3235
|
path: (props) => `storage/${props.storage}`,
|
|
2949
3236
|
updateForm: mutable2
|
|
2950
3237
|
});
|
|
2951
|
-
var ProxmoxStorageProvider = () =>
|
|
2952
|
-
// src/proxmox/user.ts
|
|
2953
|
-
import { Resource as Resource31 } from "alchemy";
|
|
2954
|
-
import * as Provider31 from "alchemy/Provider";
|
|
2955
|
-
import * as Effect50 from "effect/Effect";
|
|
2956
|
-
var ProxmoxUser = Resource31("Proxmox.User");
|
|
2957
|
-
var str3 = (value) => typeof value === "string" ? value : "";
|
|
2958
|
-
var int2 = (value) => {
|
|
2959
|
-
const parsed = typeof value === "number" ? value : Number(value ?? 0);
|
|
2960
|
-
return Number.isFinite(parsed) ? parsed : 0;
|
|
2961
|
-
};
|
|
2962
|
-
var groupSet = (value) => {
|
|
2963
|
-
const raw = Array.isArray(value) ? value : typeof value === "string" ? value.split(",") : [];
|
|
2964
|
-
const names = raw.map((group) => str3(group).trim()).filter((group) => group !== "");
|
|
2965
|
-
return [...new Set(names)].sort();
|
|
2966
|
-
};
|
|
2967
|
-
var tokenNames = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? Object.keys(value).sort() : [];
|
|
2968
|
-
var shape11 = (props) => ({
|
|
2969
|
-
comment: props.comment ?? "",
|
|
2970
|
-
email: props.email ?? "",
|
|
2971
|
-
enable: props.enable === false ? "0" : "1",
|
|
2972
|
-
expire: String(props.expire ?? 0),
|
|
2973
|
-
firstname: props.firstname ?? "",
|
|
2974
|
-
groups: groupSet(props.groups).join(","),
|
|
2975
|
-
lastname: props.lastname ?? ""
|
|
2976
|
-
});
|
|
2977
|
-
var handlers26 = pveHandlers({
|
|
2978
|
-
attributes: (live, props) => ({
|
|
2979
|
-
comment: str3(live["comment"]),
|
|
2980
|
-
email: str3(live["email"]),
|
|
2981
|
-
enable: bool(live["enable"], true),
|
|
2982
|
-
expire: int2(live["expire"]),
|
|
2983
|
-
firstname: str3(live["firstname"]),
|
|
2984
|
-
groups: groupSet(live["groups"]),
|
|
2985
|
-
lastname: str3(live["lastname"]),
|
|
2986
|
-
tokens: tokenNames(live["tokens"]),
|
|
2987
|
-
userid: props.userid
|
|
2988
|
-
}),
|
|
2989
|
-
collection: () => "access/users",
|
|
2990
|
-
createForm: (props) => ({ ...shape11(props), userid: props.userid }),
|
|
2991
|
-
matches: (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(","),
|
|
2992
|
-
path: (props) => `access/users/${props.userid}`,
|
|
2993
|
-
updateForm: shape11
|
|
2994
|
-
});
|
|
2995
|
-
var ProxmoxUserProvider = () => Provider31.effect(ProxmoxUser, Effect50.succeed(ProxmoxUser.Provider.of(handlers26)));
|
|
3238
|
+
var ProxmoxStorageProvider = () => Provider31.effect(ProxmoxStorage, Effect53.succeed(ProxmoxStorage.Provider.of(handlers26)));
|
|
2996
3239
|
// src/proxmox/zfs-pool.ts
|
|
2997
3240
|
import { Resource as Resource32 } from "alchemy";
|
|
2998
3241
|
import * as Provider32 from "alchemy/Provider";
|
|
2999
|
-
import * as
|
|
3242
|
+
import * as Effect55 from "effect/Effect";
|
|
3000
3243
|
|
|
3001
3244
|
// src/proxmox/zfs-pool-write.ts
|
|
3002
|
-
import * as
|
|
3245
|
+
import * as Effect54 from "effect/Effect";
|
|
3003
3246
|
var createForm6 = (props) => ({
|
|
3004
3247
|
devices: props.devices.join(","),
|
|
3005
3248
|
name: props.name,
|
|
@@ -3009,21 +3252,21 @@ var createForm6 = (props) => ({
|
|
|
3009
3252
|
...props["draid-config"] === undefined ? {} : { "draid-config": props["draid-config"] }
|
|
3010
3253
|
});
|
|
3011
3254
|
var SETTLE_ATTEMPTS = 30;
|
|
3012
|
-
var createPool = (props, collection2, path, read4) =>
|
|
3255
|
+
var createPool = (props, collection2, path, read4) => Effect54.gen(function* () {
|
|
3013
3256
|
const existing = yield* read4(props);
|
|
3014
3257
|
if (existing !== undefined)
|
|
3015
3258
|
return existing;
|
|
3016
3259
|
const upid = yield* pve(props.target, "provision", "POST", collection2, createForm6(props));
|
|
3017
|
-
const settle4 = (attempts) =>
|
|
3260
|
+
const settle4 = (attempts) => Effect54.gen(function* () {
|
|
3018
3261
|
const live = yield* read4(props);
|
|
3019
3262
|
if (live !== undefined || attempts <= 0)
|
|
3020
3263
|
return live;
|
|
3021
|
-
yield*
|
|
3264
|
+
yield* Effect54.sleep("2 seconds");
|
|
3022
3265
|
return yield* settle4(attempts - 1);
|
|
3023
3266
|
});
|
|
3024
3267
|
const after = yield* settle4(SETTLE_ATTEMPTS);
|
|
3025
3268
|
if (after === undefined) {
|
|
3026
|
-
return yield*
|
|
3269
|
+
return yield* Effect54.die(new Error(`${path}: 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."));
|
|
3027
3270
|
}
|
|
3028
3271
|
return after;
|
|
3029
3272
|
});
|
|
@@ -3058,13 +3301,15 @@ var spec2 = {
|
|
|
3058
3301
|
var ops5 = pveOperations(spec2);
|
|
3059
3302
|
var handlers27 = {
|
|
3060
3303
|
...pveHandlers(spec2),
|
|
3061
|
-
delete:
|
|
3304
|
+
delete: Effect55.fn(function* ({ olds }) {
|
|
3062
3305
|
yield* destroyPool(olds, spec2.path(olds));
|
|
3063
3306
|
}),
|
|
3064
3307
|
reconcile: ({ news }) => createPool(news, spec2.collection(news), spec2.path(news), ops5.read)
|
|
3065
3308
|
};
|
|
3066
|
-
var ProxmoxZfsPoolProvider = () => Provider32.effect(ProxmoxZfsPool,
|
|
3309
|
+
var ProxmoxZfsPoolProvider = () => Provider32.effect(ProxmoxZfsPool, Effect55.succeed(ProxmoxZfsPool.Provider.of(handlers27)));
|
|
3067
3310
|
export {
|
|
3311
|
+
PROVISION_DEFAULTS,
|
|
3312
|
+
PROVISION_PRIVILEGES,
|
|
3068
3313
|
PbsDatastore,
|
|
3069
3314
|
PbsDatastoreProvider,
|
|
3070
3315
|
PbsPruneJob,
|
|
@@ -3117,8 +3362,11 @@ export {
|
|
|
3117
3362
|
ProxmoxUser,
|
|
3118
3363
|
ProxmoxUserProvider,
|
|
3119
3364
|
ProxmoxVmProvider,
|
|
3120
|
-
ProxmoxZfsPoolProvider
|
|
3365
|
+
ProxmoxZfsPoolProvider,
|
|
3366
|
+
declareProvisionBaseline,
|
|
3367
|
+
provisionBaseline,
|
|
3368
|
+
provisionBootstrap
|
|
3121
3369
|
};
|
|
3122
3370
|
|
|
3123
|
-
//# debugId=
|
|
3371
|
+
//# debugId=FF23B52181D77F1964756E2164756E21
|
|
3124
3372
|
//# sourceMappingURL=index.js.map
|