@homeflare/alchemy 0.11.0 → 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 +2 -2
- 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/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.map +14 -14
- 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/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/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 +356 -142
- package/dist/proxmox/index.js.map +59 -56
- package/dist/proxmox/lease-cache.d.ts +4 -4
- package/dist/proxmox/lxc-harness.d.ts +1 -1
- package/dist/proxmox/lxc-props.d.ts +2 -2
- package/dist/proxmox/lxc.d.ts +3 -3
- 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 +1 -1
- package/docs/ownership.md +26 -1
- package/docs/provision-baseline.md +106 -0
- package/docs/proxmox-lxc-adopt.md +2 -2
- package/docs/proxmox-lxc.md +5 -4
- package/docs/proxmox.md +6 -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/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/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 +3 -3
- package/src/proxmox/lxc-create-form.ts +1 -1
- package/src/proxmox/lxc-guards.test.ts +4 -4
- package/src/proxmox/lxc-harness.ts +3 -3
- package/src/proxmox/lxc-judge.test.ts +5 -5
- package/src/proxmox/lxc-lifecycle.ts +1 -1
- 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 +1 -1
- 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 +3 -3
- 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
|
})
|
|
@@ -2604,12 +2604,286 @@ var handlers18 = pveHandlers({
|
|
|
2604
2604
|
updateForm: poolUpdateForm
|
|
2605
2605
|
});
|
|
2606
2606
|
var ProxmoxPoolProvider = () => Provider22.effect(ProxmoxPool, Effect42.succeed(ProxmoxPool.Provider.of(handlers18)));
|
|
2607
|
-
// src/proxmox/
|
|
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
|
|
2608
2783
|
import { Resource as Resource23 } from "alchemy";
|
|
2609
2784
|
import * as Provider23 from "alchemy/Provider";
|
|
2610
2785
|
import * as Effect43 from "effect/Effect";
|
|
2611
|
-
var
|
|
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() : [];
|
|
2612
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) => ({
|
|
2613
2887
|
cores: String(props.cores ?? 1),
|
|
2614
2888
|
memory: String(props.memory ?? 512),
|
|
2615
2889
|
name: props.name ?? `vm${String(props.vmid)}`,
|
|
@@ -2617,7 +2891,7 @@ var shape8 = (props) => ({
|
|
|
2617
2891
|
sockets: String(props.sockets ?? 1),
|
|
2618
2892
|
...props.net0 === undefined ? {} : { net0: props.net0 }
|
|
2619
2893
|
});
|
|
2620
|
-
var
|
|
2894
|
+
var handlers21 = pveHandlers({
|
|
2621
2895
|
attributes: (live, props) => ({
|
|
2622
2896
|
cores: num(live["cores"], 1),
|
|
2623
2897
|
memory: num(live["memory"], 512),
|
|
@@ -2628,46 +2902,46 @@ var handlers19 = pveHandlers({
|
|
|
2628
2902
|
vmid: props.vmid
|
|
2629
2903
|
}),
|
|
2630
2904
|
collection: (props) => `nodes/${props.node}/qemu`,
|
|
2631
|
-
createForm: (props) => ({ ...
|
|
2905
|
+
createForm: (props) => ({ ...shape9(props), vmid: String(props.vmid) }),
|
|
2632
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),
|
|
2633
2907
|
path: (props) => `nodes/${props.node}/qemu/${String(props.vmid)}/config`,
|
|
2634
|
-
updateForm:
|
|
2908
|
+
updateForm: shape9
|
|
2635
2909
|
});
|
|
2636
|
-
var ProxmoxVmProvider = () =>
|
|
2910
|
+
var ProxmoxVmProvider = () => Provider25.effect(ProxmoxVm, Effect46.succeed(ProxmoxVm.Provider.of(handlers21)));
|
|
2637
2911
|
// src/proxmox/replication-job.ts
|
|
2638
|
-
import { Resource as
|
|
2639
|
-
import * as
|
|
2640
|
-
import * as
|
|
2912
|
+
import { Resource as Resource26 } from "alchemy";
|
|
2913
|
+
import * as Provider26 from "alchemy/Provider";
|
|
2914
|
+
import * as Effect47 from "effect/Effect";
|
|
2641
2915
|
|
|
2642
2916
|
// src/proxmox/replication-job-form.ts
|
|
2643
2917
|
var DEFAULT_SCHEDULE = "*/15";
|
|
2644
2918
|
var jobId = (props) => `${String(props.guest)}-${String(props.jobnum)}`;
|
|
2645
|
-
var
|
|
2919
|
+
var shape10 = (props) => ({
|
|
2646
2920
|
comment: props.comment ?? "",
|
|
2647
2921
|
disable: props.disable === true ? "1" : "0",
|
|
2648
2922
|
schedule: props.schedule ?? DEFAULT_SCHEDULE,
|
|
2649
2923
|
...props.rate === undefined ? {} : { rate: String(props.rate) }
|
|
2650
2924
|
});
|
|
2651
2925
|
var createBody4 = (props) => ({
|
|
2652
|
-
...
|
|
2926
|
+
...shape10(props),
|
|
2653
2927
|
id: jobId(props),
|
|
2654
2928
|
target: props.targetNode,
|
|
2655
2929
|
type: "local"
|
|
2656
2930
|
});
|
|
2657
2931
|
var updateBody5 = (props) => ({
|
|
2658
|
-
...
|
|
2932
|
+
...shape10(props),
|
|
2659
2933
|
delete: "remove_job"
|
|
2660
2934
|
});
|
|
2661
2935
|
|
|
2662
2936
|
// src/proxmox/replication-job.ts
|
|
2663
|
-
var ProxmoxReplicationJob =
|
|
2937
|
+
var ProxmoxReplicationJob = Resource26("Proxmox.ReplicationJob");
|
|
2664
2938
|
var rateOf = (value) => {
|
|
2665
2939
|
if (typeof value === "number")
|
|
2666
2940
|
return value;
|
|
2667
2941
|
const parsed = Number.parseFloat(text(value));
|
|
2668
2942
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
2669
2943
|
};
|
|
2670
|
-
var
|
|
2944
|
+
var handlers22 = pveHandlers({
|
|
2671
2945
|
attributes: (live, props) => ({
|
|
2672
2946
|
comment: text(live["comment"], ""),
|
|
2673
2947
|
disable: bool(live["disable"], false),
|
|
@@ -2687,36 +2961,15 @@ var handlers20 = pveHandlers({
|
|
|
2687
2961
|
path: (props) => `cluster/replication/${jobId(props)}`,
|
|
2688
2962
|
updateForm: updateBody5
|
|
2689
2963
|
});
|
|
2690
|
-
var ProxmoxReplicationJobProvider = () =>
|
|
2691
|
-
// src/proxmox/role.ts
|
|
2692
|
-
import { Resource as Resource25 } from "alchemy";
|
|
2693
|
-
import * as Provider25 from "alchemy/Provider";
|
|
2694
|
-
import * as Effect45 from "effect/Effect";
|
|
2695
|
-
var ProxmoxRole = Resource25("Proxmox.Role");
|
|
2696
|
-
var canonical = (privs) => [...new Set(privs.map((priv) => priv.trim()).filter((priv) => priv.length > 0))].sort();
|
|
2697
|
-
var livePrivs = (live) => {
|
|
2698
|
-
const listed = live["privs"];
|
|
2699
|
-
if (typeof listed === "string")
|
|
2700
|
-
return canonical(listed.split(","));
|
|
2701
|
-
return canonical(Object.keys(live).filter((key) => key.includes(".")));
|
|
2702
|
-
};
|
|
2703
|
-
var handlers21 = pveHandlers({
|
|
2704
|
-
attributes: (live, props) => ({ privs: livePrivs(live), roleid: props.roleid }),
|
|
2705
|
-
collection: () => "access/roles",
|
|
2706
|
-
createForm: (props) => ({ privs: canonical(props.privs).join(","), roleid: props.roleid }),
|
|
2707
|
-
matches: (attributes2, props) => canonical(attributes2.privs).join(",") === canonical(props.privs).join(","),
|
|
2708
|
-
path: (props) => `access/roles/${props.roleid}`,
|
|
2709
|
-
updateForm: (props) => ({ privs: canonical(props.privs).join(",") })
|
|
2710
|
-
});
|
|
2711
|
-
var ProxmoxRoleProvider = () => Provider25.effect(ProxmoxRole, Effect45.succeed(ProxmoxRole.Provider.of(handlers21)));
|
|
2964
|
+
var ProxmoxReplicationJobProvider = () => Provider26.effect(ProxmoxReplicationJob, Effect47.succeed(ProxmoxReplicationJob.Provider.of(handlers22)));
|
|
2712
2965
|
// src/proxmox/sdn-apply.ts
|
|
2713
|
-
import { Resource as
|
|
2966
|
+
import { Resource as Resource27 } from "alchemy";
|
|
2714
2967
|
import { isResolved as isResolved9 } from "alchemy/Diff";
|
|
2715
|
-
import * as
|
|
2716
|
-
import * as
|
|
2968
|
+
import * as Provider27 from "alchemy/Provider";
|
|
2969
|
+
import * as Effect49 from "effect/Effect";
|
|
2717
2970
|
|
|
2718
2971
|
// src/proxmox/sdn-apply-read.ts
|
|
2719
|
-
import * as
|
|
2972
|
+
import * as Effect48 from "effect/Effect";
|
|
2720
2973
|
var MARKED = [
|
|
2721
2974
|
"cluster/sdn/zones",
|
|
2722
2975
|
"cluster/sdn/vnets",
|
|
@@ -2725,7 +2978,7 @@ var MARKED = [
|
|
|
2725
2978
|
"cluster/sdn/route-maps"
|
|
2726
2979
|
];
|
|
2727
2980
|
var subsystemAbsent = (error) => error instanceof PveError && error.status === 501;
|
|
2728
|
-
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)));
|
|
2729
2982
|
var fabricStaged = (staged, running) => {
|
|
2730
2983
|
if (staged === undefined && running === undefined)
|
|
2731
2984
|
return 0;
|
|
@@ -2733,9 +2986,9 @@ var fabricStaged = (staged, running) => {
|
|
|
2733
2986
|
return "inconsistent";
|
|
2734
2987
|
return staged === running ? 0 : 1;
|
|
2735
2988
|
};
|
|
2736
|
-
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]) => {
|
|
2737
2990
|
const verdict = fabricStaged(staged, running);
|
|
2738
|
-
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);
|
|
2739
2992
|
}));
|
|
2740
2993
|
var canonical2 = (value) => JSON.stringify(sortDeep(value));
|
|
2741
2994
|
var sortDeep = (value) => {
|
|
@@ -2748,39 +3001,39 @@ var sortDeep = (value) => {
|
|
|
2748
3001
|
}
|
|
2749
3002
|
return value;
|
|
2750
3003
|
};
|
|
2751
|
-
var sdnPendingCount = (target) =>
|
|
3004
|
+
var sdnPendingCount = (target) => Effect48.all([markedPending(target), fabricPending(target)], { concurrency: "unbounded" }).pipe(Effect48.map(([marked, fabric]) => marked + fabric));
|
|
2752
3005
|
|
|
2753
3006
|
// src/proxmox/sdn-apply.ts
|
|
2754
|
-
var ProxmoxSdnApply =
|
|
2755
|
-
var read3 = (props) => sdnPendingCount(props.target).pipe(
|
|
2756
|
-
var ProxmoxSdnApplyProvider = () =>
|
|
2757
|
-
list: () =>
|
|
2758
|
-
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 }) {
|
|
2759
3012
|
return yield* read3(olds);
|
|
2760
3013
|
}),
|
|
2761
|
-
diff:
|
|
3014
|
+
diff: Effect49.fn(function* ({ news }) {
|
|
2762
3015
|
if (!isResolved9(news))
|
|
2763
3016
|
return;
|
|
2764
3017
|
const { pending } = yield* read3(news);
|
|
2765
3018
|
return pending === 0 ? { action: "noop" } : { action: "update" };
|
|
2766
3019
|
}),
|
|
2767
|
-
reconcile:
|
|
3020
|
+
reconcile: Effect49.fn(function* ({ news }) {
|
|
2768
3021
|
const staged = yield* read3(news);
|
|
2769
3022
|
if (staged.pending === 0)
|
|
2770
3023
|
return staged;
|
|
2771
3024
|
yield* pve(news.target, "provision", "PUT", "cluster/sdn");
|
|
2772
3025
|
const after = yield* read3(news);
|
|
2773
3026
|
if (after.pending !== 0) {
|
|
2774
|
-
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."));
|
|
2775
3028
|
}
|
|
2776
3029
|
return after;
|
|
2777
3030
|
}),
|
|
2778
|
-
delete: () =>
|
|
3031
|
+
delete: () => Effect49.void
|
|
2779
3032
|
})));
|
|
2780
3033
|
// src/proxmox/sdn-subnet.ts
|
|
2781
|
-
import { Resource as
|
|
2782
|
-
import * as
|
|
2783
|
-
import * as
|
|
3034
|
+
import { Resource as Resource28 } from "alchemy";
|
|
3035
|
+
import * as Provider28 from "alchemy/Provider";
|
|
3036
|
+
import * as Effect50 from "effect/Effect";
|
|
2784
3037
|
|
|
2785
3038
|
// src/proxmox/sdn-subnet-form.ts
|
|
2786
3039
|
var subnetId = (props) => `${props.zone}-${props.cidr.replace("/", "-")}`;
|
|
@@ -2810,8 +3063,8 @@ var updateForm2 = (props) => {
|
|
|
2810
3063
|
};
|
|
2811
3064
|
|
|
2812
3065
|
// src/proxmox/sdn-subnet.ts
|
|
2813
|
-
var ProxmoxSdnSubnet =
|
|
2814
|
-
var
|
|
3066
|
+
var ProxmoxSdnSubnet = Resource28("Proxmox.SdnSubnet");
|
|
3067
|
+
var handlers23 = pveHandlers({
|
|
2815
3068
|
attributes: (live, props) => {
|
|
2816
3069
|
const liveVnet = text(live["vnet"]);
|
|
2817
3070
|
if (liveVnet !== "" && liveVnet !== props.vnet)
|
|
@@ -2836,14 +3089,14 @@ var handlers22 = pveHandlers({
|
|
|
2836
3089
|
path: (props) => `cluster/sdn/vnets/${props.vnet}/subnets/${subnetId(props)}`,
|
|
2837
3090
|
updateForm: updateForm2
|
|
2838
3091
|
});
|
|
2839
|
-
var ProxmoxSdnSubnetProvider = () =>
|
|
3092
|
+
var ProxmoxSdnSubnetProvider = () => Provider28.effect(ProxmoxSdnSubnet, Effect50.succeed(ProxmoxSdnSubnet.Provider.of(handlers23)));
|
|
2840
3093
|
// src/proxmox/sdn-vnet.ts
|
|
2841
|
-
import { Resource as
|
|
2842
|
-
import * as
|
|
2843
|
-
import * as
|
|
2844
|
-
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");
|
|
2845
3098
|
var tagOf = (value) => typeof value === "number" ? value : undefined;
|
|
2846
|
-
var
|
|
3099
|
+
var shape11 = (props) => {
|
|
2847
3100
|
const alias = props.alias ?? "";
|
|
2848
3101
|
return {
|
|
2849
3102
|
"isolate-ports": props.isolatePorts === true ? "1" : "0",
|
|
@@ -2858,9 +3111,9 @@ var updateForm3 = (props) => {
|
|
|
2858
3111
|
...(props.alias ?? "") === "" ? ["alias"] : [],
|
|
2859
3112
|
...props.tag === undefined ? ["tag"] : []
|
|
2860
3113
|
];
|
|
2861
|
-
return withClears(
|
|
3114
|
+
return withClears(shape11(props), unset2);
|
|
2862
3115
|
};
|
|
2863
|
-
var
|
|
3116
|
+
var handlers24 = pveHandlers({
|
|
2864
3117
|
readRole: "provision",
|
|
2865
3118
|
attributes: (live, props) => ({
|
|
2866
3119
|
alias: typeof live["alias"] === "string" ? live["alias"] : "",
|
|
@@ -2872,17 +3125,17 @@ var handlers23 = pveHandlers({
|
|
|
2872
3125
|
zone: typeof live["zone"] === "string" ? live["zone"] : ""
|
|
2873
3126
|
}),
|
|
2874
3127
|
collection: () => "cluster/sdn/vnets",
|
|
2875
|
-
createForm: (props) => ({ ...
|
|
3128
|
+
createForm: (props) => ({ ...shape11(props), vnet: props.vnet }),
|
|
2876
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),
|
|
2877
3130
|
path: (props) => `cluster/sdn/vnets/${props.vnet}`,
|
|
2878
3131
|
updateForm: updateForm3
|
|
2879
3132
|
});
|
|
2880
|
-
var ProxmoxSdnVnetProvider = () =>
|
|
3133
|
+
var ProxmoxSdnVnetProvider = () => Provider29.effect(ProxmoxSdnVnet, Effect51.succeed(ProxmoxSdnVnet.Provider.of(handlers24)));
|
|
2881
3134
|
// src/proxmox/sdn-zone.ts
|
|
2882
|
-
import { Resource as
|
|
2883
|
-
import * as
|
|
2884
|
-
import * as
|
|
2885
|
-
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");
|
|
2886
3139
|
var sameIpam = (live, declared) => (live === "" ? "pve" : live) === (declared === undefined || declared === "" ? "pve" : declared);
|
|
2887
3140
|
var body6 = (props) => ({
|
|
2888
3141
|
...props.bridge === undefined ? {} : { bridge: props.bridge },
|
|
@@ -2902,7 +3155,7 @@ var clearable = {
|
|
|
2902
3155
|
vlan: ["dns", "dnszone", "ipam", "mtu", "nodes"],
|
|
2903
3156
|
vxlan: ["dns", "dnszone", "ipam", "mtu", "nodes"]
|
|
2904
3157
|
};
|
|
2905
|
-
var
|
|
3158
|
+
var handlers25 = pveHandlers({
|
|
2906
3159
|
readRole: "provision",
|
|
2907
3160
|
attributes: (live, props) => {
|
|
2908
3161
|
const liveType = text(live["type"]);
|
|
@@ -2933,15 +3186,15 @@ var handlers24 = pveHandlers({
|
|
|
2933
3186
|
return withClears(fields, clear);
|
|
2934
3187
|
}
|
|
2935
3188
|
});
|
|
2936
|
-
var ProxmoxSdnZoneProvider = () =>
|
|
3189
|
+
var ProxmoxSdnZoneProvider = () => Provider30.effect(ProxmoxSdnZone, Effect52.succeed(ProxmoxSdnZone.Provider.of(handlers25)));
|
|
2937
3190
|
// src/proxmox/storage.ts
|
|
2938
|
-
import { Resource as
|
|
2939
|
-
import * as
|
|
2940
|
-
import * as
|
|
2941
|
-
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", {
|
|
2942
3195
|
defaultRemovalPolicy: "retain"
|
|
2943
3196
|
});
|
|
2944
|
-
var
|
|
3197
|
+
var str3 = (value, fallback = "") => typeof value === "string" ? value : fallback;
|
|
2945
3198
|
var field5 = (name, value) => value === undefined ? {} : { [name]: value };
|
|
2946
3199
|
var locatorForm = (locator) => ({ ...locator });
|
|
2947
3200
|
var tokens = (value) => value.split(",").map((part) => part.trim()).filter((part) => part !== "").sort().join(",");
|
|
@@ -2957,19 +3210,19 @@ var mutable2 = (props) => ({
|
|
|
2957
3210
|
...field5("prune-backups", props["prune-backups"]),
|
|
2958
3211
|
...field5("shared", flag(props.shared))
|
|
2959
3212
|
});
|
|
2960
|
-
var
|
|
3213
|
+
var handlers26 = pveHandlers({
|
|
2961
3214
|
readRole: "provision",
|
|
2962
3215
|
attributes: (live, props) => ({
|
|
2963
|
-
comment:
|
|
2964
|
-
content:
|
|
3216
|
+
comment: str3(live["comment"]),
|
|
3217
|
+
content: str3(live["content"]),
|
|
2965
3218
|
disable: bool(live["disable"]),
|
|
2966
3219
|
maxfiles: typeof live["maxfiles"] === "number" ? live["maxfiles"] : -1,
|
|
2967
|
-
nodes:
|
|
2968
|
-
preallocation:
|
|
2969
|
-
"prune-backups":
|
|
3220
|
+
nodes: str3(live["nodes"]),
|
|
3221
|
+
preallocation: str3(live["preallocation"]),
|
|
3222
|
+
"prune-backups": str3(live["prune-backups"]),
|
|
2970
3223
|
shared: bool(live["shared"]),
|
|
2971
3224
|
storage: props.storage,
|
|
2972
|
-
type:
|
|
3225
|
+
type: str3(live["type"], props.type)
|
|
2973
3226
|
}),
|
|
2974
3227
|
collection: () => "storage",
|
|
2975
3228
|
createForm: (props) => ({
|
|
@@ -2982,58 +3235,14 @@ var handlers25 = pveHandlers({
|
|
|
2982
3235
|
path: (props) => `storage/${props.storage}`,
|
|
2983
3236
|
updateForm: mutable2
|
|
2984
3237
|
});
|
|
2985
|
-
var ProxmoxStorageProvider = () =>
|
|
2986
|
-
// src/proxmox/user.ts
|
|
2987
|
-
import { Resource as Resource31 } from "alchemy";
|
|
2988
|
-
import * as Provider31 from "alchemy/Provider";
|
|
2989
|
-
import * as Effect52 from "effect/Effect";
|
|
2990
|
-
var ProxmoxUser = Resource31("Proxmox.User");
|
|
2991
|
-
var str3 = (value) => typeof value === "string" ? value : "";
|
|
2992
|
-
var int2 = (value) => {
|
|
2993
|
-
const parsed = typeof value === "number" ? value : Number(value ?? 0);
|
|
2994
|
-
return Number.isFinite(parsed) ? parsed : 0;
|
|
2995
|
-
};
|
|
2996
|
-
var groupSet = (value) => {
|
|
2997
|
-
const raw = Array.isArray(value) ? value : typeof value === "string" ? value.split(",") : [];
|
|
2998
|
-
const names = raw.map((group) => str3(group).trim()).filter((group) => group !== "");
|
|
2999
|
-
return [...new Set(names)].sort();
|
|
3000
|
-
};
|
|
3001
|
-
var tokenNames = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? Object.keys(value).sort() : [];
|
|
3002
|
-
var shape11 = (props) => ({
|
|
3003
|
-
comment: props.comment ?? "",
|
|
3004
|
-
email: props.email ?? "",
|
|
3005
|
-
enable: props.enable === false ? "0" : "1",
|
|
3006
|
-
expire: String(props.expire ?? 0),
|
|
3007
|
-
firstname: props.firstname ?? "",
|
|
3008
|
-
groups: groupSet(props.groups).join(","),
|
|
3009
|
-
lastname: props.lastname ?? ""
|
|
3010
|
-
});
|
|
3011
|
-
var handlers26 = pveHandlers({
|
|
3012
|
-
attributes: (live, props) => ({
|
|
3013
|
-
comment: str3(live["comment"]),
|
|
3014
|
-
email: str3(live["email"]),
|
|
3015
|
-
enable: bool(live["enable"], true),
|
|
3016
|
-
expire: int2(live["expire"]),
|
|
3017
|
-
firstname: str3(live["firstname"]),
|
|
3018
|
-
groups: groupSet(live["groups"]),
|
|
3019
|
-
lastname: str3(live["lastname"]),
|
|
3020
|
-
tokens: tokenNames(live["tokens"]),
|
|
3021
|
-
userid: props.userid
|
|
3022
|
-
}),
|
|
3023
|
-
collection: () => "access/users",
|
|
3024
|
-
createForm: (props) => ({ ...shape11(props), userid: props.userid }),
|
|
3025
|
-
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(","),
|
|
3026
|
-
path: (props) => `access/users/${props.userid}`,
|
|
3027
|
-
updateForm: shape11
|
|
3028
|
-
});
|
|
3029
|
-
var ProxmoxUserProvider = () => Provider31.effect(ProxmoxUser, Effect52.succeed(ProxmoxUser.Provider.of(handlers26)));
|
|
3238
|
+
var ProxmoxStorageProvider = () => Provider31.effect(ProxmoxStorage, Effect53.succeed(ProxmoxStorage.Provider.of(handlers26)));
|
|
3030
3239
|
// src/proxmox/zfs-pool.ts
|
|
3031
3240
|
import { Resource as Resource32 } from "alchemy";
|
|
3032
3241
|
import * as Provider32 from "alchemy/Provider";
|
|
3033
|
-
import * as
|
|
3242
|
+
import * as Effect55 from "effect/Effect";
|
|
3034
3243
|
|
|
3035
3244
|
// src/proxmox/zfs-pool-write.ts
|
|
3036
|
-
import * as
|
|
3245
|
+
import * as Effect54 from "effect/Effect";
|
|
3037
3246
|
var createForm6 = (props) => ({
|
|
3038
3247
|
devices: props.devices.join(","),
|
|
3039
3248
|
name: props.name,
|
|
@@ -3043,21 +3252,21 @@ var createForm6 = (props) => ({
|
|
|
3043
3252
|
...props["draid-config"] === undefined ? {} : { "draid-config": props["draid-config"] }
|
|
3044
3253
|
});
|
|
3045
3254
|
var SETTLE_ATTEMPTS = 30;
|
|
3046
|
-
var createPool = (props, collection2, path, read4) =>
|
|
3255
|
+
var createPool = (props, collection2, path, read4) => Effect54.gen(function* () {
|
|
3047
3256
|
const existing = yield* read4(props);
|
|
3048
3257
|
if (existing !== undefined)
|
|
3049
3258
|
return existing;
|
|
3050
3259
|
const upid = yield* pve(props.target, "provision", "POST", collection2, createForm6(props));
|
|
3051
|
-
const settle4 = (attempts) =>
|
|
3260
|
+
const settle4 = (attempts) => Effect54.gen(function* () {
|
|
3052
3261
|
const live = yield* read4(props);
|
|
3053
3262
|
if (live !== undefined || attempts <= 0)
|
|
3054
3263
|
return live;
|
|
3055
|
-
yield*
|
|
3264
|
+
yield* Effect54.sleep("2 seconds");
|
|
3056
3265
|
return yield* settle4(attempts - 1);
|
|
3057
3266
|
});
|
|
3058
3267
|
const after = yield* settle4(SETTLE_ATTEMPTS);
|
|
3059
3268
|
if (after === undefined) {
|
|
3060
|
-
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."));
|
|
3061
3270
|
}
|
|
3062
3271
|
return after;
|
|
3063
3272
|
});
|
|
@@ -3092,13 +3301,15 @@ var spec2 = {
|
|
|
3092
3301
|
var ops5 = pveOperations(spec2);
|
|
3093
3302
|
var handlers27 = {
|
|
3094
3303
|
...pveHandlers(spec2),
|
|
3095
|
-
delete:
|
|
3304
|
+
delete: Effect55.fn(function* ({ olds }) {
|
|
3096
3305
|
yield* destroyPool(olds, spec2.path(olds));
|
|
3097
3306
|
}),
|
|
3098
3307
|
reconcile: ({ news }) => createPool(news, spec2.collection(news), spec2.path(news), ops5.read)
|
|
3099
3308
|
};
|
|
3100
|
-
var ProxmoxZfsPoolProvider = () => Provider32.effect(ProxmoxZfsPool,
|
|
3309
|
+
var ProxmoxZfsPoolProvider = () => Provider32.effect(ProxmoxZfsPool, Effect55.succeed(ProxmoxZfsPool.Provider.of(handlers27)));
|
|
3101
3310
|
export {
|
|
3311
|
+
PROVISION_DEFAULTS,
|
|
3312
|
+
PROVISION_PRIVILEGES,
|
|
3102
3313
|
PbsDatastore,
|
|
3103
3314
|
PbsDatastoreProvider,
|
|
3104
3315
|
PbsPruneJob,
|
|
@@ -3151,8 +3362,11 @@ export {
|
|
|
3151
3362
|
ProxmoxUser,
|
|
3152
3363
|
ProxmoxUserProvider,
|
|
3153
3364
|
ProxmoxVmProvider,
|
|
3154
|
-
ProxmoxZfsPoolProvider
|
|
3365
|
+
ProxmoxZfsPoolProvider,
|
|
3366
|
+
declareProvisionBaseline,
|
|
3367
|
+
provisionBaseline,
|
|
3368
|
+
provisionBootstrap
|
|
3155
3369
|
};
|
|
3156
3370
|
|
|
3157
|
-
//# debugId=
|
|
3371
|
+
//# debugId=FF23B52181D77F1964756E2164756E21
|
|
3158
3372
|
//# sourceMappingURL=index.js.map
|