@homeflare/alchemy 0.15.0 → 0.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -0
- package/dist/linux/directory-lifecycle.d.ts +50 -0
- package/dist/linux/directory-lifecycle.d.ts.map +1 -0
- package/dist/linux/directory.d.ts +19 -0
- package/dist/linux/directory.d.ts.map +1 -0
- package/dist/linux/fake-linux-host.d.ts +35 -0
- package/dist/linux/fake-linux-host.d.ts.map +1 -0
- package/dist/linux/fake-systemd.d.ts +34 -0
- package/dist/linux/fake-systemd.d.ts.map +1 -0
- package/dist/linux/index.d.ts +27 -0
- package/dist/linux/index.d.ts.map +1 -0
- package/dist/linux/index.js +1575 -0
- package/dist/linux/index.js.map +38 -0
- package/dist/linux/providers.d.ts +17 -0
- package/dist/linux/providers.d.ts.map +1 -0
- package/dist/linux/region.d.ts +43 -0
- package/dist/linux/region.d.ts.map +1 -0
- package/dist/linux/remote-file-form.d.ts +47 -0
- package/dist/linux/remote-file-form.d.ts.map +1 -0
- package/dist/linux/remote-file-lifecycle.d.ts +36 -0
- package/dist/linux/remote-file-lifecycle.d.ts.map +1 -0
- package/dist/linux/remote-file-plan.d.ts +33 -0
- package/dist/linux/remote-file-plan.d.ts.map +1 -0
- package/dist/linux/remote-file.d.ts +28 -0
- package/dist/linux/remote-file.d.ts.map +1 -0
- package/dist/linux/ssh-command.d.ts +68 -0
- package/dist/linux/ssh-command.d.ts.map +1 -0
- package/dist/linux/ssh-runner.d.ts +22 -0
- package/dist/linux/ssh-runner.d.ts.map +1 -0
- package/dist/linux/ssh-scripts.d.ts +27 -0
- package/dist/linux/ssh-scripts.d.ts.map +1 -0
- package/dist/linux/systemctl.d.ts +67 -0
- package/dist/linux/systemctl.d.ts.map +1 -0
- package/dist/linux/unit-form.d.ts +78 -0
- package/dist/linux/unit-form.d.ts.map +1 -0
- package/dist/linux/unit-handlers.d.ts +15 -0
- package/dist/linux/unit-handlers.d.ts.map +1 -0
- package/dist/linux/unit-lifecycle.d.ts +40 -0
- package/dist/linux/unit-lifecycle.d.ts.map +1 -0
- package/dist/linux/unit-preflight.d.ts +41 -0
- package/dist/linux/unit-preflight.d.ts.map +1 -0
- package/dist/linux/unit-settle.d.ts +31 -0
- package/dist/linux/unit-settle.d.ts.map +1 -0
- package/dist/linux/unit.d.ts +35 -0
- package/dist/linux/unit.d.ts.map +1 -0
- package/dist/netbox/client.d.ts +52 -0
- package/dist/netbox/client.d.ts.map +1 -0
- package/dist/netbox/constraint-guard.d.ts +26 -0
- package/dist/netbox/constraint-guard.d.ts.map +1 -0
- package/dist/netbox/constraints.d.ts +76 -0
- package/dist/netbox/constraints.d.ts.map +1 -0
- package/dist/netbox/generated/constraints/index.d.ts +19 -0
- package/dist/netbox/generated/constraints/index.d.ts.map +1 -0
- package/dist/netbox/generated/constraints/netbox-ipam.d.ts +21 -0
- package/dist/netbox/generated/constraints/netbox-ipam.d.ts.map +1 -0
- package/dist/netbox/index.d.ts +17 -0
- package/dist/netbox/index.d.ts.map +1 -0
- package/dist/netbox/index.js +331 -0
- package/dist/netbox/index.js.map +18 -0
- package/dist/netbox/prefix-form.d.ts +54 -0
- package/dist/netbox/prefix-form.d.ts.map +1 -0
- package/dist/netbox/prefix.d.ts +80 -0
- package/dist/netbox/prefix.d.ts.map +1 -0
- package/dist/netbox/resource.d.ts +94 -0
- package/dist/netbox/resource.d.ts.map +1 -0
- package/dist/netbox/values.d.ts +24 -0
- package/dist/netbox/values.d.ts.map +1 -0
- package/dist/proxmox/api-token-form.d.ts.map +1 -1
- package/dist/proxmox/apply-endpoints.d.ts +30 -0
- package/dist/proxmox/apply-endpoints.d.ts.map +1 -0
- package/dist/proxmox/ceph-daemon-form.d.ts +29 -1
- package/dist/proxmox/ceph-daemon-form.d.ts.map +1 -1
- package/dist/proxmox/ceph-daemon.d.ts.map +1 -1
- package/dist/proxmox/ceph-flag.d.ts.map +1 -1
- package/dist/proxmox/ceph-fs.d.ts.map +1 -1
- package/dist/proxmox/ceph-osd-write.d.ts +19 -0
- package/dist/proxmox/ceph-osd-write.d.ts.map +1 -1
- package/dist/proxmox/ceph-pool.d.ts.map +1 -1
- package/dist/proxmox/constraints.d.ts +28 -5
- package/dist/proxmox/constraints.d.ts.map +1 -1
- package/dist/proxmox/generated/constraints/index.d.ts +1 -1
- package/dist/proxmox/generated/constraints/index.d.ts.map +1 -1
- package/dist/proxmox/generated/constraints/pbs-config.d.ts +6 -1
- package/dist/proxmox/generated/constraints/pbs-config.d.ts.map +1 -1
- package/dist/proxmox/generated/constraints/pve-access.d.ts +6 -1
- package/dist/proxmox/generated/constraints/pve-access.d.ts.map +1 -1
- package/dist/proxmox/generated/constraints/pve-cluster-backup.d.ts +23 -0
- package/dist/proxmox/generated/constraints/pve-cluster-backup.d.ts.map +1 -0
- package/dist/proxmox/generated/constraints/pve-cluster-ceph.d.ts +23 -0
- package/dist/proxmox/generated/constraints/pve-cluster-ceph.d.ts.map +1 -0
- package/dist/proxmox/generated/constraints/pve-cluster-firewall.d.ts +23 -0
- package/dist/proxmox/generated/constraints/pve-cluster-firewall.d.ts.map +1 -0
- package/dist/proxmox/generated/constraints/pve-cluster-ha.d.ts +23 -0
- package/dist/proxmox/generated/constraints/pve-cluster-ha.d.ts.map +1 -0
- package/dist/proxmox/generated/constraints/pve-cluster-metrics.d.ts +23 -0
- package/dist/proxmox/generated/constraints/pve-cluster-metrics.d.ts.map +1 -0
- package/dist/proxmox/generated/constraints/pve-cluster-notifications.d.ts +23 -0
- package/dist/proxmox/generated/constraints/pve-cluster-notifications.d.ts.map +1 -0
- package/dist/proxmox/generated/constraints/pve-cluster-replication.d.ts +23 -0
- package/dist/proxmox/generated/constraints/pve-cluster-replication.d.ts.map +1 -0
- package/dist/proxmox/generated/constraints/pve-cluster-sdn.d.ts +23 -0
- package/dist/proxmox/generated/constraints/pve-cluster-sdn.d.ts.map +1 -0
- package/dist/proxmox/generated/constraints/pve-nodes-ceph.d.ts +23 -0
- package/dist/proxmox/generated/constraints/pve-nodes-ceph.d.ts.map +1 -0
- package/dist/proxmox/generated/constraints/pve-nodes-disks.d.ts +23 -0
- package/dist/proxmox/generated/constraints/pve-nodes-disks.d.ts.map +1 -0
- package/dist/proxmox/generated/constraints/pve-nodes-lxc.d.ts +23 -0
- package/dist/proxmox/generated/constraints/pve-nodes-lxc.d.ts.map +1 -0
- package/dist/proxmox/generated/constraints/pve-nodes-network.d.ts +23 -0
- package/dist/proxmox/generated/constraints/pve-nodes-network.d.ts.map +1 -0
- package/dist/proxmox/generated/constraints/pve-nodes-qemu.d.ts +23 -0
- package/dist/proxmox/generated/constraints/pve-nodes-qemu.d.ts.map +1 -0
- package/dist/proxmox/generated/constraints/pve-pools.d.ts +6 -1
- package/dist/proxmox/generated/constraints/pve-pools.d.ts.map +1 -1
- package/dist/proxmox/generated/constraints/pve-storage.d.ts +6 -1
- package/dist/proxmox/generated/constraints/pve-storage.d.ts.map +1 -1
- package/dist/proxmox/generated/pbs/access-users.d.ts +137 -0
- package/dist/proxmox/generated/pbs/access-users.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/access.d.ts +171 -0
- package/dist/proxmox/generated/pbs/access.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/admin-datastore-store.d.ts +252 -0
- package/dist/proxmox/generated/pbs/admin-datastore-store.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/admin-metrics.d.ts +147 -0
- package/dist/proxmox/generated/pbs/admin-metrics.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/admin.d.ts +217 -0
- package/dist/proxmox/generated/pbs/admin.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/backup.d.ts +114 -0
- package/dist/proxmox/generated/pbs/backup.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/config-access-pam.d.ts +218 -0
- package/dist/proxmox/generated/pbs/config-access-pam.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/config-access.d.ts +203 -0
- package/dist/proxmox/generated/pbs/config-access.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/config-datastore.d.ts +195 -0
- package/dist/proxmox/generated/pbs/config-datastore.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/config-media-pool.d.ts +207 -0
- package/dist/proxmox/generated/pbs/config-media-pool.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/config-notifications-matchers.d.ts +159 -0
- package/dist/proxmox/generated/pbs/config-notifications-matchers.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/config-notifications.d.ts +199 -0
- package/dist/proxmox/generated/pbs/config-notifications.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/config-remote.d.ts +175 -0
- package/dist/proxmox/generated/pbs/config-remote.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/config-sync.d.ts +145 -0
- package/dist/proxmox/generated/pbs/config-sync.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/config-tape.d.ts +200 -0
- package/dist/proxmox/generated/pbs/config-tape.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/config-verify.d.ts +77 -0
- package/dist/proxmox/generated/pbs/config-verify.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/config.d.ts +119 -0
- package/dist/proxmox/generated/pbs/config.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/nodes-node-dns.d.ts +178 -0
- package/dist/proxmox/generated/pbs/nodes-node-dns.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/nodes-node-services.d.ts +205 -0
- package/dist/proxmox/generated/pbs/nodes-node-services.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/nodes-node.d.ts +166 -0
- package/dist/proxmox/generated/pbs/nodes-node.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/nodes.d.ts +171 -0
- package/dist/proxmox/generated/pbs/nodes.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/ping.d.ts +18 -0
- package/dist/proxmox/generated/pbs/ping.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/pull.d.ts +37 -0
- package/dist/proxmox/generated/pbs/pull.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/push.d.ts +36 -0
- package/dist/proxmox/generated/pbs/push.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/reader.d.ts +41 -0
- package/dist/proxmox/generated/pbs/reader.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/root.d.ts +16 -0
- package/dist/proxmox/generated/pbs/root.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/status.d.ts +54 -0
- package/dist/proxmox/generated/pbs/status.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/tape-drive.d.ts +181 -0
- package/dist/proxmox/generated/pbs/tape-drive.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/tape-media.d.ts +126 -0
- package/dist/proxmox/generated/pbs/tape-media.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/tape.d.ts +90 -0
- package/dist/proxmox/generated/pbs/tape.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/version.d.ts +20 -0
- package/dist/proxmox/generated/pbs/version.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs.d.ts +44 -662
- package/dist/proxmox/generated/pbs.d.ts.map +1 -1
- package/dist/proxmox/generated/pve/access-roles.d.ts +164 -0
- package/dist/proxmox/generated/pve/access-roles.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/access-users.d.ts +152 -0
- package/dist/proxmox/generated/pve/access-users.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/access.d.ts +221 -0
- package/dist/proxmox/generated/pve/access.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-acme-plugins-id.d.ts +45 -0
- package/dist/proxmox/generated/pve/cluster-acme-plugins-id.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-acme-plugins.d.ts +47 -0
- package/dist/proxmox/generated/pve/cluster-acme-plugins.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-acme-tos.d.ts +240 -0
- package/dist/proxmox/generated/pve/cluster-acme-tos.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-bulk-action.d.ts +200 -0
- package/dist/proxmox/generated/pve/cluster-bulk-action.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-firewall-refs.d.ts +109 -0
- package/dist/proxmox/generated/pve/cluster-firewall-refs.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-firewall.d.ts +225 -0
- package/dist/proxmox/generated/pve/cluster-firewall.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-ha.d.ts +199 -0
- package/dist/proxmox/generated/pve/cluster-ha.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-jobs.d.ts +183 -0
- package/dist/proxmox/generated/pve/cluster-jobs.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-metrics.d.ts +110 -0
- package/dist/proxmox/generated/pve/cluster-metrics.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-notifications-endpoints.d.ts +224 -0
- package/dist/proxmox/generated/pve/cluster-notifications-endpoints.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-notifications.d.ts +91 -0
- package/dist/proxmox/generated/pve/cluster-notifications.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-options.d.ts +85 -0
- package/dist/proxmox/generated/pve/cluster-options.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-qemu-custom-cpu-models-cputype.d.ts +48 -0
- package/dist/proxmox/generated/pve/cluster-qemu-custom-cpu-models-cputype.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-qemu-custom-cpu-models.d.ts +45 -0
- package/dist/proxmox/generated/pve/cluster-qemu-custom-cpu-models.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-replication.d.ts +130 -0
- package/dist/proxmox/generated/pve/cluster-replication.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-sdn-fabrics.d.ts +220 -0
- package/dist/proxmox/generated/pve/cluster-sdn-fabrics.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-sdn-ipams.d.ts +229 -0
- package/dist/proxmox/generated/pve/cluster-sdn-ipams.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-sdn-vnets-vnet.d.ts +229 -0
- package/dist/proxmox/generated/pve/cluster-sdn-vnets-vnet.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-sdn-vnets.d.ts +51 -0
- package/dist/proxmox/generated/pve/cluster-sdn-vnets.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-sdn-zones.d.ts +225 -0
- package/dist/proxmox/generated/pve/cluster-sdn-zones.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-sdn.d.ts +202 -0
- package/dist/proxmox/generated/pve/cluster-sdn.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster.d.ts +69 -0
- package/dist/proxmox/generated/pve/cluster.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-ceph.d.ts +167 -0
- package/dist/proxmox/generated/pve/nodes-node-ceph.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-config.d.ts +43 -0
- package/dist/proxmox/generated/pve/nodes-node-config.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-disks.d.ts +225 -0
- package/dist/proxmox/generated/pve/nodes-node-disks.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-firewall.d.ts +145 -0
- package/dist/proxmox/generated/pve/nodes-node-firewall.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-hardware.d.ts +195 -0
- package/dist/proxmox/generated/pve/nodes-node-hardware.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-firewall.d.ts +191 -0
- package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-firewall.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-move-volume.d.ts +33 -0
- package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-move-volume.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-mtunnel.d.ts +56 -0
- package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-mtunnel.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-resize.d.ts +28 -0
- package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-resize.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-rrd.d.ts +186 -0
- package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-rrd.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-lxc-vmid.d.ts +191 -0
- package/dist/proxmox/generated/pve/nodes-node-lxc-vmid.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-network.d.ts +150 -0
- package/dist/proxmox/generated/pve/nodes-node-network.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-config.d.ts +301 -0
- package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-config.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-dbus-vmstate.d.ts +170 -0
- package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-dbus-vmstate.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-firewall-rules.d.ts +144 -0
- package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-firewall-rules.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-move-disk.d.ts +34 -0
- package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-move-disk.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-mtunnel.d.ts +137 -0
- package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-mtunnel.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-status.d.ts +179 -0
- package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-status.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-qemu-vmid.d.ts +143 -0
- package/dist/proxmox/generated/pve/nodes-node-qemu-vmid.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-qemu.d.ts +158 -0
- package/dist/proxmox/generated/pve/nodes-node-qemu.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-replication.d.ts +187 -0
- package/dist/proxmox/generated/pve/nodes-node-replication.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-services.d.ts +115 -0
- package/dist/proxmox/generated/pve/nodes-node-services.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-storage-storage.d.ts +202 -0
- package/dist/proxmox/generated/pve/nodes-node-storage-storage.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-storage.d.ts +40 -0
- package/dist/proxmox/generated/pve/nodes-node-storage.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-subscription.d.ts +173 -0
- package/dist/proxmox/generated/pve/nodes-node-subscription.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-vzdump.d.ts +97 -0
- package/dist/proxmox/generated/pve/nodes-node-vzdump.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node.d.ts +207 -0
- package/dist/proxmox/generated/pve/nodes-node.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes.d.ts +236 -0
- package/dist/proxmox/generated/pve/nodes.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/pools.d.ts +82 -0
- package/dist/proxmox/generated/pve/pools.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/storage.d.ts +159 -0
- package/dist/proxmox/generated/pve/storage.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/version.d.ts +21 -0
- package/dist/proxmox/generated/pve/version.d.ts.map +1 -0
- package/dist/proxmox/generated/pve.d.ts +69 -4766
- package/dist/proxmox/generated/pve.d.ts.map +1 -1
- package/dist/proxmox/index.js +996 -372
- package/dist/proxmox/index.js.map +48 -32
- package/dist/proxmox/lxc-lifecycle.d.ts.map +1 -1
- package/dist/proxmox/metric-server-form.d.ts +11 -0
- package/dist/proxmox/metric-server-form.d.ts.map +1 -1
- package/dist/proxmox/metric-server.d.ts.map +1 -1
- package/dist/proxmox/network-apply.d.ts.map +1 -1
- package/dist/proxmox/node-network-form.d.ts +15 -0
- package/dist/proxmox/node-network-form.d.ts.map +1 -1
- package/dist/proxmox/node-network.d.ts.map +1 -1
- package/dist/proxmox/notification-target-endpoint.d.ts +26 -0
- package/dist/proxmox/notification-target-endpoint.d.ts.map +1 -0
- package/dist/proxmox/notification-target-form.d.ts +7 -0
- package/dist/proxmox/notification-target-form.d.ts.map +1 -1
- package/dist/proxmox/notification-target.d.ts.map +1 -1
- package/dist/proxmox/pbs-notification-target-endpoint.d.ts +29 -0
- package/dist/proxmox/pbs-notification-target-endpoint.d.ts.map +1 -0
- package/dist/proxmox/pbs-notification-target-lifecycle.d.ts.map +1 -1
- package/dist/proxmox/qemu.d.ts +10 -0
- package/dist/proxmox/qemu.d.ts.map +1 -1
- package/dist/proxmox/resource-guard.d.ts +15 -0
- package/dist/proxmox/resource-guard.d.ts.map +1 -0
- package/dist/proxmox/resource-spec.d.ts +15 -4
- package/dist/proxmox/resource-spec.d.ts.map +1 -1
- package/dist/proxmox/resource.d.ts +8 -0
- package/dist/proxmox/resource.d.ts.map +1 -1
- package/dist/proxmox/sdn-apply.d.ts.map +1 -1
- package/dist/proxmox/sdn-subnet.d.ts.map +1 -1
- package/dist/proxmox/zfs-pool-write.d.ts +11 -0
- package/dist/proxmox/zfs-pool-write.d.ts.map +1 -1
- package/dist/proxmox/zfs-pool.d.ts.map +1 -1
- package/docs/linux-host-measured.md +56 -0
- package/docs/linux-host.md +192 -0
- package/docs/netbox.md +103 -0
- package/package.json +9 -1
- package/src/linux/directory-lifecycle.ts +220 -0
- package/src/linux/directory.test.ts +132 -0
- package/src/linux/directory.ts +71 -0
- package/src/linux/fake-linux-host.ts +197 -0
- package/src/linux/fake-systemd.ts +103 -0
- package/src/linux/index.ts +32 -0
- package/src/linux/providers.ts +26 -0
- package/src/linux/region.test.ts +89 -0
- package/src/linux/region.ts +118 -0
- package/src/linux/remote-file-form.ts +96 -0
- package/src/linux/remote-file-lifecycle.test.ts +191 -0
- package/src/linux/remote-file-lifecycle.ts +161 -0
- package/src/linux/remote-file-plan.ts +154 -0
- package/src/linux/remote-file.ts +88 -0
- package/src/linux/ssh-command.test.ts +140 -0
- package/src/linux/ssh-command.ts +99 -0
- package/src/linux/ssh-runner.ts +188 -0
- package/src/linux/ssh-scripts.ts +105 -0
- package/src/linux/systemctl.ts +145 -0
- package/src/linux/unit-form.ts +183 -0
- package/src/linux/unit-handlers.ts +73 -0
- package/src/linux/unit-lifecycle.test.ts +220 -0
- package/src/linux/unit-lifecycle.ts +175 -0
- package/src/linux/unit-preflight.test.ts +128 -0
- package/src/linux/unit-preflight.ts +100 -0
- package/src/linux/unit-settle.ts +110 -0
- package/src/linux/unit.ts +81 -0
- package/src/netbox/client.test.ts +67 -0
- package/src/netbox/client.ts +146 -0
- package/src/netbox/constraint-guard.ts +57 -0
- package/src/netbox/constraints.test.ts +114 -0
- package/src/netbox/constraints.ts +152 -0
- package/src/netbox/generated/constraints/index.ts +23 -0
- package/src/netbox/generated/constraints/netbox-ipam.ts +32 -0
- package/src/netbox/index.ts +34 -0
- package/src/netbox/prefix-form.test.ts +127 -0
- package/src/netbox/prefix-form.ts +84 -0
- package/src/netbox/prefix.ts +136 -0
- package/src/netbox/resource.ts +208 -0
- package/src/netbox/values.test.ts +66 -0
- package/src/netbox/values.ts +40 -0
- package/src/proxmox/api-token-form.ts +5 -0
- package/src/proxmox/apply-endpoints.ts +34 -0
- package/src/proxmox/ceph-daemon-form.ts +56 -2
- package/src/proxmox/ceph-daemon.ts +10 -21
- package/src/proxmox/ceph-flag.ts +1 -0
- package/src/proxmox/ceph-fs.ts +1 -0
- package/src/proxmox/ceph-osd-write.ts +30 -4
- package/src/proxmox/ceph-pool.ts +10 -0
- package/src/proxmox/constraints-dialect.test.ts +165 -0
- package/src/proxmox/constraints-forms.test.ts +228 -0
- package/src/proxmox/constraints-host-forms.test.ts +132 -0
- package/src/proxmox/constraints-live.test.ts +155 -0
- package/src/proxmox/constraints-node-forms.test.ts +197 -0
- package/src/proxmox/constraints-required.test.ts +172 -0
- package/src/proxmox/constraints.test.ts +8 -63
- package/src/proxmox/constraints.ts +31 -6
- package/src/proxmox/generated/constraints/index.ts +27 -3
- package/src/proxmox/generated/constraints/pbs-config.ts +63 -1
- package/src/proxmox/generated/constraints/pve-access.ts +15 -3
- package/src/proxmox/generated/constraints/pve-cluster-backup.ts +74 -0
- package/src/proxmox/generated/constraints/pve-cluster-ceph.ts +27 -0
- package/src/proxmox/generated/constraints/pve-cluster-firewall.ts +35 -0
- package/src/proxmox/generated/constraints/pve-cluster-ha.ts +55 -0
- package/src/proxmox/generated/constraints/pve-cluster-metrics.ts +59 -0
- package/src/proxmox/generated/constraints/pve-cluster-notifications.ts +80 -0
- package/src/proxmox/generated/constraints/pve-cluster-replication.ts +43 -0
- package/src/proxmox/generated/constraints/pve-cluster-sdn.ts +90 -0
- package/src/proxmox/generated/constraints/pve-nodes-ceph.ts +57 -0
- package/src/proxmox/generated/constraints/pve-nodes-disks.ts +31 -0
- package/src/proxmox/generated/constraints/pve-nodes-lxc.ts +82 -0
- package/src/proxmox/generated/constraints/pve-nodes-network.ts +77 -0
- package/src/proxmox/generated/constraints/pve-nodes-qemu.ts +125 -0
- package/src/proxmox/generated/constraints/pve-pools.ts +6 -1
- package/src/proxmox/generated/constraints/pve-storage.ts +8 -3
- package/src/proxmox/generated/pbs/access-users.ts +137 -0
- package/src/proxmox/generated/pbs/access.ts +232 -0
- package/src/proxmox/generated/pbs/admin-datastore-store.ts +238 -0
- package/src/proxmox/generated/pbs/admin-metrics.ts +147 -0
- package/src/proxmox/generated/pbs/admin.ts +225 -0
- package/src/proxmox/generated/pbs/backup.ts +121 -0
- package/src/proxmox/generated/pbs/config-access-pam.ts +228 -0
- package/src/proxmox/generated/pbs/config-access.ts +228 -0
- package/src/proxmox/generated/pbs/config-datastore.ts +217 -0
- package/src/proxmox/generated/pbs/config-media-pool.ts +232 -0
- package/src/proxmox/generated/pbs/config-notifications-matchers.ts +187 -0
- package/src/proxmox/generated/pbs/config-notifications.ts +221 -0
- package/src/proxmox/generated/pbs/config-remote.ts +192 -0
- package/src/proxmox/generated/pbs/config-sync.ts +167 -0
- package/src/proxmox/generated/pbs/config-tape.ts +226 -0
- package/src/proxmox/generated/pbs/config-verify.ts +87 -0
- package/src/proxmox/generated/pbs/config.ts +135 -0
- package/src/proxmox/generated/pbs/nodes-node-dns.ts +229 -0
- package/src/proxmox/generated/pbs/nodes-node-services.ts +181 -0
- package/src/proxmox/generated/pbs/nodes-node.ts +244 -0
- package/src/proxmox/generated/pbs/nodes.ts +160 -0
- package/src/proxmox/generated/pbs/ping.ts +16 -0
- package/src/proxmox/generated/pbs/pull.ts +37 -0
- package/src/proxmox/generated/pbs/push.ts +36 -0
- package/src/proxmox/generated/pbs/reader.ts +41 -0
- package/src/proxmox/generated/pbs/root.ts +16 -0
- package/src/proxmox/generated/pbs/status.ts +50 -0
- package/src/proxmox/generated/pbs/tape-drive.ts +220 -0
- package/src/proxmox/generated/pbs/tape-media.ts +133 -0
- package/src/proxmox/generated/pbs/tape.ts +92 -0
- package/src/proxmox/generated/pbs/version.ts +16 -0
- package/src/proxmox/generated/pbs.ts +44 -787
- package/src/proxmox/generated/pve/access-roles.ts +168 -0
- package/src/proxmox/generated/pve/access-users.ts +155 -0
- package/src/proxmox/generated/pve/access.ts +217 -0
- package/src/proxmox/generated/pve/cluster-acme-plugins-id.ts +367 -0
- package/src/proxmox/generated/pve/cluster-acme-plugins.ts +366 -0
- package/src/proxmox/generated/pve/cluster-acme-tos.ts +232 -0
- package/src/proxmox/generated/pve/cluster-bulk-action.ts +227 -0
- package/src/proxmox/generated/pve/cluster-firewall-refs.ts +110 -0
- package/src/proxmox/generated/pve/cluster-firewall.ts +236 -0
- package/src/proxmox/generated/pve/cluster-ha.ts +203 -0
- package/src/proxmox/generated/pve/cluster-jobs.ts +190 -0
- package/src/proxmox/generated/pve/cluster-metrics.ts +116 -0
- package/src/proxmox/generated/pve/cluster-notifications-endpoints.ts +244 -0
- package/src/proxmox/generated/pve/cluster-notifications.ts +99 -0
- package/src/proxmox/generated/pve/cluster-options.ts +189 -0
- package/src/proxmox/generated/pve/cluster-qemu-custom-cpu-models-cputype.ts +272 -0
- package/src/proxmox/generated/pve/cluster-qemu-custom-cpu-models.ts +268 -0
- package/src/proxmox/generated/pve/cluster-replication.ts +127 -0
- package/src/proxmox/generated/pve/cluster-sdn-fabrics.ts +218 -0
- package/src/proxmox/generated/pve/cluster-sdn-ipams.ts +229 -0
- package/src/proxmox/generated/pve/cluster-sdn-vnets-vnet.ts +245 -0
- package/src/proxmox/generated/pve/cluster-sdn-vnets.ts +49 -0
- package/src/proxmox/generated/pve/cluster-sdn-zones.ts +224 -0
- package/src/proxmox/generated/pve/cluster-sdn.ts +201 -0
- package/src/proxmox/generated/pve/cluster.ts +71 -0
- package/src/proxmox/generated/pve/nodes-node-ceph.ts +167 -0
- package/src/proxmox/generated/pve/nodes-node-config.ts +56 -0
- package/src/proxmox/generated/pve/nodes-node-disks.ts +237 -0
- package/src/proxmox/generated/pve/nodes-node-firewall.ts +239 -0
- package/src/proxmox/generated/pve/nodes-node-hardware.ts +215 -0
- package/src/proxmox/generated/pve/nodes-node-lxc-vmid-firewall.ts +232 -0
- package/src/proxmox/generated/pve/nodes-node-lxc-vmid-move-volume.ts +1059 -0
- package/src/proxmox/generated/pve/nodes-node-lxc-vmid-mtunnel.ts +49 -0
- package/src/proxmox/generated/pve/nodes-node-lxc-vmid-resize.ts +285 -0
- package/src/proxmox/generated/pve/nodes-node-lxc-vmid-rrd.ts +184 -0
- package/src/proxmox/generated/pve/nodes-node-lxc-vmid.ts +240 -0
- package/src/proxmox/generated/pve/nodes-node-network.ts +234 -0
- package/src/proxmox/generated/pve/nodes-node-qemu-vmid-config.ts +441 -0
- package/src/proxmox/generated/pve/nodes-node-qemu-vmid-dbus-vmstate.ts +215 -0
- package/src/proxmox/generated/pve/nodes-node-qemu-vmid-firewall-rules.ts +145 -0
- package/src/proxmox/generated/pve/nodes-node-qemu-vmid-move-disk.ts +664 -0
- package/src/proxmox/generated/pve/nodes-node-qemu-vmid-mtunnel.ts +188 -0
- package/src/proxmox/generated/pve/nodes-node-qemu-vmid-status.ts +233 -0
- package/src/proxmox/generated/pve/nodes-node-qemu-vmid.ts +187 -0
- package/src/proxmox/generated/pve/nodes-node-qemu.ts +204 -0
- package/src/proxmox/generated/pve/nodes-node-replication.ts +187 -0
- package/src/proxmox/generated/pve/nodes-node-services.ts +221 -0
- package/src/proxmox/generated/pve/nodes-node-storage-storage.ts +226 -0
- package/src/proxmox/generated/pve/nodes-node-storage.ts +40 -0
- package/src/proxmox/generated/pve/nodes-node-subscription.ts +168 -0
- package/src/proxmox/generated/pve/nodes-node-vzdump.ts +96 -0
- package/src/proxmox/generated/pve/nodes-node.ts +211 -0
- package/src/proxmox/generated/pve/nodes.ts +235 -0
- package/src/proxmox/generated/pve/pools.ts +78 -0
- package/src/proxmox/generated/pve/storage.ts +213 -0
- package/src/proxmox/generated/pve/version.ts +21 -0
- package/src/proxmox/generated/pve.ts +69 -7403
- package/src/proxmox/lxc-interior.test.ts +17 -1
- package/src/proxmox/lxc-lifecycle.ts +48 -9
- package/src/proxmox/metric-server-form.ts +10 -0
- package/src/proxmox/metric-server.ts +11 -2
- package/src/proxmox/network-apply.ts +4 -0
- package/src/proxmox/node-network-form.ts +19 -0
- package/src/proxmox/node-network.ts +9 -3
- package/src/proxmox/notification-target-endpoint.ts +46 -0
- package/src/proxmox/notification-target-form.ts +11 -0
- package/src/proxmox/notification-target.ts +5 -2
- package/src/proxmox/pbs-notification-target-endpoint.ts +45 -0
- package/src/proxmox/pbs-notification-target-lifecycle.ts +16 -14
- package/src/proxmox/qemu.ts +9 -1
- package/src/proxmox/resource-guard.ts +61 -0
- package/src/proxmox/resource-spec.ts +16 -4
- package/src/proxmox/resource.ts +27 -27
- package/src/proxmox/sdn-apply.ts +4 -0
- package/src/proxmox/sdn-subnet.ts +5 -0
- package/src/proxmox/zfs-pool-write.ts +12 -0
- package/src/proxmox/zfs-pool.ts +2 -1
- package/dist/proxmox/generated/constraints/pve-cluster.d.ts +0 -18
- package/dist/proxmox/generated/constraints/pve-cluster.d.ts.map +0 -1
- package/src/proxmox/generated/constraints/pve-cluster.ts +0 -174
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two applies, and the vendor endpoints they publish through.
|
|
3
|
+
*
|
|
4
|
+
* ★ AN ACTION ENDPOINT IS STILL WIRED, EVEN THOUGH ITS TABLE IS EMPTY, AND THE REASON IS THE
|
|
5
|
+
* GENERATOR RATHER THAN THE VALIDATOR. `PUT /nodes/{node}/network` and `PUT /cluster/sdn` take
|
|
6
|
+
* no body at all — there is nothing for a length or a range to be wrong about — but
|
|
7
|
+
* `codegen/constraints.ts` RESOLVES every key named in this package against the vendor's own
|
|
8
|
+
* schema and STOPS when one is absent. So the key buys the thing these two families most need:
|
|
9
|
+
* a PVE that moves, renames or withdraws the apply fails `bun run check` instead of failing an
|
|
10
|
+
* `ifreload -a` on three nodes at once.
|
|
11
|
+
* ⚠️ AND THE LOOKUP IS NOT FREE EITHER. `constraintsFor` throws when a key has no table, so a
|
|
12
|
+
* checkout whose generated tables were never refreshed refuses the apply with the command to
|
|
13
|
+
* run rather than quietly doing nothing — the same defect `constraint-guard.ts` names.
|
|
14
|
+
*
|
|
15
|
+
* ⛔ NEITHER OF THESE IS A CREATE, AND NEITHER FAMILY HAS A `PveSpec`. Both write their own five
|
|
16
|
+
* handlers (an apply is not an object — see the ⛔ in each file), so they reach the shared check
|
|
17
|
+
* through `guardForm` by name, exactly as `pbs-datastore-endpoint.ts` does.
|
|
18
|
+
*/
|
|
19
|
+
import type * as Effect from 'effect/Effect';
|
|
20
|
+
import { guardForm } from './constraint-guard.ts';
|
|
21
|
+
import type { EndpointKey } from './constraints.ts';
|
|
22
|
+
|
|
23
|
+
/** ⚠️ `{node}` is a path segment, so this really does take no form keys at all. */
|
|
24
|
+
export const NETWORK_APPLY_ENDPOINT: EndpointKey = 'pve:PUT /nodes/{node}/network';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* ⛔ CLUSTER-WIDE. One PUT publishes every staged zone, vnet and subnet on every node; there is no
|
|
28
|
+
* per-object apply and no pre-apply veto. `sdn-apply.ts` is the whole argument.
|
|
29
|
+
*/
|
|
30
|
+
export const SDN_APPLY_ENDPOINT: EndpointKey = 'pve:PUT /cluster/sdn';
|
|
31
|
+
|
|
32
|
+
export const guardNetworkApply: Effect.Effect<void> = guardForm(NETWORK_APPLY_ENDPOINT, {}, true);
|
|
33
|
+
|
|
34
|
+
export const guardSdnApply: Effect.Effect<void> = guardForm(SDN_APPLY_ENDPOINT, {}, true);
|
|
@@ -28,8 +28,30 @@
|
|
|
28
28
|
* the task in the PVE UI before concluding the create failed.
|
|
29
29
|
*
|
|
30
30
|
*/
|
|
31
|
-
import type { CephDaemonProps } from './ceph-daemon.ts';
|
|
32
|
-
import {
|
|
31
|
+
import type { CephDaemonAttributes, CephDaemonKind, CephDaemonProps } from './ceph-daemon.ts';
|
|
32
|
+
import type { EndpointPair } from './resource-spec.ts';
|
|
33
|
+
import { bool, flag, num, text } from './values.ts';
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The vendor endpoint each kind is created at, as the generated tables key it.
|
|
37
|
+
*
|
|
38
|
+
* ⛔ ONE PER KIND, NOT A `{type}` PATH. PVE registers `mds`, `mgr` and `mon` as three separate
|
|
39
|
+
* nodes with three separate parameter schemas — `hotstandby` belongs to the first and
|
|
40
|
+
* `mon-address` to the third — so a single key would enforce one kind's rules on all three.
|
|
41
|
+
* That is why `PveSpec['endpoint']` admits a function of props at all.
|
|
42
|
+
* ⛔ THE STRINGS MUST STAY LITERALS. `codegen/constraints.ts` finds the endpoints to table by
|
|
43
|
+
* SCANNING THIS PACKAGE'S TEXT, so a key assembled from `props.kind` would be tabled by
|
|
44
|
+
* nothing and `constraintsFor` would throw on the first deploy that reached it.
|
|
45
|
+
* ⚠️ THE PARAMETER NAMES DIFFER AND ARE THE VENDOR'S OWN — `{name}`, `{id}`, `{monid}`. The
|
|
46
|
+
* generator resolves each against the schema, so a tidied-up spelling stops the build.
|
|
47
|
+
* ⚠️ NO `update` ON ANY OF THEM: none of the three has a PUT, which is why the spec declares no
|
|
48
|
+
* `updateForm` either.
|
|
49
|
+
*/
|
|
50
|
+
export const DAEMON_ENDPOINTS: Readonly<Record<CephDaemonKind, EndpointPair>> = {
|
|
51
|
+
mds: { create: 'pve:POST /nodes/{node}/ceph/mds/{name}' },
|
|
52
|
+
mgr: { create: 'pve:POST /nodes/{node}/ceph/mgr/{id}' },
|
|
53
|
+
mon: { create: 'pve:POST /nodes/{node}/ceph/mon/{monid}' },
|
|
54
|
+
};
|
|
33
55
|
|
|
34
56
|
/**
|
|
35
57
|
* PVE defaults the id to the nodename for all three kinds, and that default is resolved HERE
|
|
@@ -82,3 +104,35 @@ export const findRow = (live: unknown, props: CephDaemonProps) =>
|
|
|
82
104
|
(Array.isArray(live) ? live : [])
|
|
83
105
|
.filter((row): row is Record<string, unknown> => typeof row === 'object' && row !== null)
|
|
84
106
|
.find((row) => row['name'] === daemonId(props));
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* One live row as attributes, or `undefined` — "no daemon of this kind carries this name".
|
|
110
|
+
*
|
|
111
|
+
* ★ HERE RATHER THAN IN ceph-daemon.ts FOR THE REASON THAT FILE'S SIBLING EXISTS AT ALL: the
|
|
112
|
+
* 250-line cap, and a seam that is real. It reads the row `findRow` picked out of an answer its
|
|
113
|
+
* caller already fetched, three lines above, so the lookup and the mapping now live together
|
|
114
|
+
* instead of a function call apart.
|
|
115
|
+
*
|
|
116
|
+
* ⚠️ `undefined` WHEN NO ROW CARRIES THIS NAME: that is how the factory learns to create.
|
|
117
|
+
*/
|
|
118
|
+
export const daemonAttributes = (
|
|
119
|
+
live: Record<string, unknown>,
|
|
120
|
+
props: CephDaemonProps,
|
|
121
|
+
): CephDaemonAttributes | undefined => {
|
|
122
|
+
const row = findRow(live, props);
|
|
123
|
+
if (row === undefined) return undefined;
|
|
124
|
+
return {
|
|
125
|
+
addr: text(row['addr']),
|
|
126
|
+
fsName: text(row['fs_name']),
|
|
127
|
+
host: text(row['host']),
|
|
128
|
+
kind: props.kind,
|
|
129
|
+
name: daemonId(props),
|
|
130
|
+
node: props.node,
|
|
131
|
+
quorum: bool(row['quorum']),
|
|
132
|
+
rank: num(row['rank'], -1),
|
|
133
|
+
service: bool(row['service']),
|
|
134
|
+
standbyReplay: bool(row['standby_replay']),
|
|
135
|
+
state: text(row['state']),
|
|
136
|
+
version: text(row['ceph_version_short']),
|
|
137
|
+
};
|
|
138
|
+
};
|
|
@@ -49,10 +49,15 @@
|
|
|
49
49
|
import { Resource } from 'alchemy';
|
|
50
50
|
import * as Provider from 'alchemy/Provider';
|
|
51
51
|
import * as Effect from 'effect/Effect';
|
|
52
|
-
import {
|
|
52
|
+
import {
|
|
53
|
+
DAEMON_ENDPOINTS,
|
|
54
|
+
collectionPath,
|
|
55
|
+
createForm,
|
|
56
|
+
daemonAttributes,
|
|
57
|
+
daemonPath,
|
|
58
|
+
} from './ceph-daemon-form.ts';
|
|
53
59
|
import { pve } from './client.ts';
|
|
54
60
|
import { type PveRequirements, type WithTarget, pveHandlers } from './resource.ts';
|
|
55
|
-
import { bool, num, text } from './values.ts';
|
|
56
61
|
|
|
57
62
|
/**
|
|
58
63
|
* Which Ceph daemon this is. The discriminant picks the collection, the create parameters and
|
|
@@ -176,28 +181,12 @@ export const ProxmoxCephDaemon = Resource<ProxmoxCephDaemon>('Proxmox.CephDaemon
|
|
|
176
181
|
});
|
|
177
182
|
|
|
178
183
|
const handlers = pveHandlers<CephDaemonProps, CephDaemonAttributes>({
|
|
179
|
-
|
|
180
|
-
attributes: (live, props) => {
|
|
181
|
-
const row = findRow(live, props);
|
|
182
|
-
if (row === undefined) return undefined;
|
|
183
|
-
return {
|
|
184
|
-
addr: text(row['addr']),
|
|
185
|
-
fsName: text(row['fs_name']),
|
|
186
|
-
host: text(row['host']),
|
|
187
|
-
kind: props.kind,
|
|
188
|
-
name: daemonId(props),
|
|
189
|
-
node: props.node,
|
|
190
|
-
quorum: bool(row['quorum']),
|
|
191
|
-
rank: num(row['rank'], -1),
|
|
192
|
-
service: bool(row['service']),
|
|
193
|
-
standbyReplay: bool(row['standby_replay']),
|
|
194
|
-
state: text(row['state']),
|
|
195
|
-
version: text(row['ceph_version_short']),
|
|
196
|
-
};
|
|
197
|
-
},
|
|
184
|
+
attributes: daemonAttributes,
|
|
198
185
|
/** ⛔ The ID path, not the collection — POST is registered on `{id}`, exactly like a metric server. */
|
|
199
186
|
collection: daemonPath,
|
|
200
187
|
createForm,
|
|
188
|
+
/** ⚠️ A function, because the kind picks the endpoint — see `DAEMON_ENDPOINTS`. */
|
|
189
|
+
endpoint: (props: CephDaemonProps) => DAEMON_ENDPOINTS[props.kind],
|
|
201
190
|
/**
|
|
202
191
|
* ⛔ TOTAL, AND THIS IS THE REASON THE FILE EXISTS RATHER THAN A SHORTCUT PAST WRITING IT. Every
|
|
203
192
|
* field a declaration can carry is CREATE-ONLY and unreadable — `mon-address` comes back as a
|
package/src/proxmox/ceph-flag.ts
CHANGED
|
@@ -181,6 +181,7 @@ const handlers = pveHandlers<CephFlagProps, CephFlagAttributes>({
|
|
|
181
181
|
* only body `cluster/ceph/flags` has ever documented. Nothing sends it today.
|
|
182
182
|
*/
|
|
183
183
|
createForm: (props) => ({ [props.flag]: flag(props.value) ?? '0' }),
|
|
184
|
+
endpoint: { update: 'pve:PUT /cluster/ceph/flags/{flag}' }, // ⛔ No create: see above.
|
|
184
185
|
/**
|
|
185
186
|
* ⚠️ `value` IS THE ONLY THING COMPARED, AND THE LIST OF THINGS DELIBERATELY NOT COMPARED IS THE
|
|
186
187
|
* point of this resource. `flag` is props-derived (see `CephFlagAttributes`). `description`
|
package/src/proxmox/ceph-fs.ts
CHANGED
|
@@ -148,6 +148,7 @@ const ops = pveOperations<CephFsProps, CephFsAttributes>({
|
|
|
148
148
|
/** ⚠️ The `{name}` path — longer than `path()`, deliberately. See the second ⛔ in the header. */
|
|
149
149
|
collection: objectPath,
|
|
150
150
|
createForm,
|
|
151
|
+
endpoint: { create: 'pve:POST /nodes/{node}/ceph/fs/{name}' }, // ⚠️ POSTed on the object.
|
|
151
152
|
/**
|
|
152
153
|
* ⛔ NOTHING IS COMPARED, AND THAT IS THE MOST DELIBERATE LINE IN THIS FILE. A CephFS has no
|
|
153
154
|
* readable, writable field: the index returns only the name and the pools behind it, and every
|
|
@@ -15,9 +15,34 @@ import * as Effect from 'effect/Effect';
|
|
|
15
15
|
import { readOsd } from './ceph-osd-tree.ts';
|
|
16
16
|
import type { CephOsdProps } from './ceph-osd.ts';
|
|
17
17
|
import { pve } from './client.ts';
|
|
18
|
+
import { guardForm } from './constraint-guard.ts';
|
|
19
|
+
import type { EndpointKey } from './constraints.ts';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The vendor rules the create form is checked against at plan time.
|
|
23
|
+
*
|
|
24
|
+
* ⛔ DECLARED HERE RATHER THAN ON A SPEC BECAUSE THIS FAMILY HAS NO SPEC — `ceph-osd.ts` writes
|
|
25
|
+
* all five handlers by hand (the ⛔ there says why), so it cannot inherit the shared guard and
|
|
26
|
+
* would otherwise be the one family that writes to Ceph with nothing checking the body.
|
|
27
|
+
* ⚠️ `dev` IS REQUIRED AND THE REFUSAL BELOW ALREADY COVERS IT, with a message about a missing
|
|
28
|
+
* replica that is far more useful than `dev: required`. The table is still worth checking: it
|
|
29
|
+
* also carries `db_dev_size` ≥ 1, `wal_dev_size` ≥ 0.5 and `osds-per-device` ≥ 1, and nothing
|
|
30
|
+
* else in this package knows those numbers.
|
|
31
|
+
* ⚠️ THERE IS NO UPDATE KEY: PVE registers no PUT under `ceph/osd`, and an OSD is not editable.
|
|
32
|
+
*/
|
|
33
|
+
export const OSD_CREATE_ENDPOINT: EndpointKey = 'pve:POST /nodes/{node}/ceph/osd';
|
|
18
34
|
|
|
19
35
|
const describe = (props: CephOsdProps) => `osd.${String(props.osdid)} on ${props.node}`;
|
|
20
36
|
|
|
37
|
+
/**
|
|
38
|
+
* The create body. ★ Exported so the constraint proof can run the REAL form.
|
|
39
|
+
* ⚠️ `dev` is `string | undefined` on the props and non-optional here: `createOsd` refuses first.
|
|
40
|
+
*/
|
|
41
|
+
export const createOsdForm = (props: CephOsdProps, dev: string): Record<string, string> => ({
|
|
42
|
+
dev,
|
|
43
|
+
...(props.device_class === undefined ? {} : { 'crush-device-class': props.device_class }),
|
|
44
|
+
});
|
|
45
|
+
|
|
21
46
|
export const createOsd = Effect.fn(function* (news: CephOsdProps) {
|
|
22
47
|
if (news.dev === undefined) {
|
|
23
48
|
return yield* Effect.die(
|
|
@@ -30,10 +55,11 @@ export const createOsd = Effect.fn(function* (news: CephOsdProps) {
|
|
|
30
55
|
),
|
|
31
56
|
);
|
|
32
57
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
58
|
+
const form = createOsdForm(news, news.dev);
|
|
59
|
+
// ⛔ BEFORE THE POST, BECAUSE THE POST ZAPS A BLOCK DEVICE. A vendor rule broken here is worth
|
|
60
|
+
// refusing at plan rather than discovering from a 400 after `ceph-volume` has run.
|
|
61
|
+
yield* guardForm(OSD_CREATE_ENDPOINT, form, true);
|
|
62
|
+
yield* pve(news.target, 'provision', 'POST', `nodes/${news.node}/ceph/osd`, form);
|
|
37
63
|
const created = yield* readOsd(news);
|
|
38
64
|
if (created === undefined) {
|
|
39
65
|
return yield* Effect.die(
|
package/src/proxmox/ceph-pool.ts
CHANGED
|
@@ -101,6 +101,11 @@ const spec: PveSpec<CephPoolProps, CephPoolAttributes> = {
|
|
|
101
101
|
},
|
|
102
102
|
collection,
|
|
103
103
|
createForm: createBody,
|
|
104
|
+
/** The vendor rules both forms are checked against at plan time — resource-spec.ts. */
|
|
105
|
+
endpoint: {
|
|
106
|
+
create: 'pve:POST /nodes/{node}/ceph/pool',
|
|
107
|
+
update: 'pve:PUT /nodes/{node}/ceph/pool/{name}',
|
|
108
|
+
},
|
|
104
109
|
/**
|
|
105
110
|
* ⛔ DECLARING WHAT IS LIVE MUST PLAN noop, AND EVERY OMISSION HERE IS WHY. Out, each with its
|
|
106
111
|
* reason on the prop it belongs to: `pg_num` (the autoscaler rewrites it), `application` (the
|
|
@@ -152,6 +157,10 @@ export const ProxmoxCephPoolProvider = () =>
|
|
|
152
157
|
const live = yield* ops.read(news);
|
|
153
158
|
let upid: string | undefined;
|
|
154
159
|
if (live === undefined) {
|
|
160
|
+
// ⛔ THE GUARD `ops.reconcile` WOULD HAVE RUN, RESTORED. This handler replaces it
|
|
161
|
+
// wholesale, so without these two calls an ADOPTED pool — whose diff answer Alchemy
|
|
162
|
+
// discards — would reach the cluster with nothing having checked its body.
|
|
163
|
+
yield* ops.guardCreate(news, true);
|
|
155
164
|
yield* confirmAbsent(news, spec.collection(news), object(news));
|
|
156
165
|
upid = yield* pve<string>(
|
|
157
166
|
news.target,
|
|
@@ -170,6 +179,7 @@ export const ProxmoxCephPoolProvider = () =>
|
|
|
170
179
|
*/
|
|
171
180
|
const form = formToSend(spec.matches, live, news, updateBody(news));
|
|
172
181
|
if (form !== undefined) {
|
|
182
|
+
yield* ops.guardUpdate(news);
|
|
173
183
|
upid = yield* pve<string>(news.target, 'provision', 'PUT', object(news), form);
|
|
174
184
|
}
|
|
175
185
|
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two rule kinds the first generation dropped, and the false positive neither may cause.
|
|
3
|
+
*
|
|
4
|
+
* 🔴 ADVERSARIAL REVIEW OF THE 2026-09-22 CONSTRAINT GUARD, SAME DAY. The guard caught the
|
|
5
|
+
* 128-character comment, and two whole rule kinds were passing through it unchecked:
|
|
6
|
+
* - PVE applies `m/^$pattern$/` (MEASURED in JSONSchema.pm:1636) and the table carried the
|
|
7
|
+
* UNANCHORED body, so `RegExp.test` searched: a firewall alias named `ok name!` matched on
|
|
8
|
+
* `ok`, passed plan, and was rejected by PVE with the 400 this feature exists to prevent.
|
|
9
|
+
* - An array parameter states its rules on `items`, and the emitter read only the parameter,
|
|
10
|
+
* so `target`, `associated-key` and `secondary-controllers` were tabled with no rule at all —
|
|
11
|
+
* while `violations` was already checking every element against that empty rule.
|
|
12
|
+
*
|
|
13
|
+
* ★ EVERY CASE HERE IS A MUTANT: it passes on the fixed generator and fails on the one that
|
|
14
|
+
* shipped. The last describe is the opposite risk and the more dangerous one — a rule that
|
|
15
|
+
* refuses a declaration the vendor ACCEPTS breaks a stack that was always legal.
|
|
16
|
+
*/
|
|
17
|
+
import { describe, expect, test } from 'bun:test';
|
|
18
|
+
import { constraintsFor, formViolations } from './constraint-guard.ts';
|
|
19
|
+
import { matcherCreateForm, matcherUpdateForm } from './notification-matcher-form.ts';
|
|
20
|
+
|
|
21
|
+
const ALIAS = 'pve:POST /cluster/firewall/aliases';
|
|
22
|
+
const PBS_MATCHER = 'pbs:POST /config/notifications/matchers';
|
|
23
|
+
const PBS_MATCHER_PUT = 'pbs:PUT /config/notifications/matchers/{name}';
|
|
24
|
+
const PVE_MATCHER = 'pve:POST /cluster/notifications/matchers';
|
|
25
|
+
const BACKUP = 'pve:POST /cluster/backup';
|
|
26
|
+
|
|
27
|
+
describe('a PVE pattern is anchored the way PVE anchors it', () => {
|
|
28
|
+
/**
|
|
29
|
+
* ⛔ THE MUTANT. `[A-Za-z][A-Za-z0-9\-\_]+` unanchored matches the `ok` inside `ok name!`, so
|
|
30
|
+
* `RegExp.test` said yes and PVE said 400. Deleting the `^`/`$` in param-rules.ts brings this
|
|
31
|
+
* failure back exactly.
|
|
32
|
+
*/
|
|
33
|
+
test('a name PVE rejects is refused, not matched on its prefix', () => {
|
|
34
|
+
expect(formViolations(ALIAS, { cidr: '10.0.0.0/24', name: 'ok-name' }, false)).toEqual([]);
|
|
35
|
+
expect(formViolations(ALIAS, { cidr: '10.0.0.0/24', name: 'ok name!' }, false)).toEqual([
|
|
36
|
+
'name: must match [A-Za-z][A-Za-z0-9\\-\\_]+',
|
|
37
|
+
]);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
/** The violation quotes the VENDOR's spelling, so an operator can search PVE's own docs for it. */
|
|
41
|
+
test('the message is the vendor pattern, never the anchored translation', () => {
|
|
42
|
+
const rule = constraintsFor(ALIAS)['name'];
|
|
43
|
+
expect(rule?.pattern).toBe('^[A-Za-z][A-Za-z0-9\\-\\_]+\\n?$');
|
|
44
|
+
expect(rule?.patternSource).toBe('[A-Za-z][A-Za-z0-9\\-\\_]+');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* ⚠️ PERL'S `$`, NOT JAVASCRIPT'S. `m/^x$/` matches `"x\n"`; a bare `$` in JavaScript does not.
|
|
49
|
+
* Without the `\n?` this line would be a refusal of a value PVE accepts — the false positive
|
|
50
|
+
* that is worse than the server 400 it replaces.
|
|
51
|
+
*/
|
|
52
|
+
test('a trailing newline is accepted, because Perl accepts it', () => {
|
|
53
|
+
expect(formViolations(BACKUP, { starttime: '02:00\n' }, false)).toEqual([]);
|
|
54
|
+
expect(formViolations(BACKUP, { starttime: '02:00' }, false)).toEqual([]);
|
|
55
|
+
expect(formViolations(BACKUP, { starttime: 'at 02:00' }, false)).toEqual([
|
|
56
|
+
'starttime: must match \\d{1,2}:\\d{1,2}',
|
|
57
|
+
]);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
/** ⚠️ PBS patterns already carry their own `^…$`; anchoring them again would invent a rule. */
|
|
61
|
+
test('a PBS pattern is left exactly as the vendor wrote it', () => {
|
|
62
|
+
const rule = constraintsFor(PBS_MATCHER)['name'];
|
|
63
|
+
expect(rule?.pattern).toBe('^(?:[A-Za-z0-9_][A-Za-z0-9._\\-]*)$');
|
|
64
|
+
expect(rule?.patternSource).toStartWith('/^');
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe("an array's rules come from its items, and reach every element", () => {
|
|
69
|
+
const fields = (targets: readonly string[]) => ({ name: 'default-matcher', targets });
|
|
70
|
+
|
|
71
|
+
test('PBS bounds each target name, and the table says the rule is per element', () => {
|
|
72
|
+
const rule = constraintsFor(PBS_MATCHER)['target'];
|
|
73
|
+
expect(rule).toMatchObject({ each: true, maxLength: 32, minLength: 2, type: 'array' });
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('one over-long element is refused although the parameter is a list', () => {
|
|
77
|
+
expect(formViolations(PBS_MATCHER, matcherCreateForm(fields(['mail-to-root'])), false)).toEqual(
|
|
78
|
+
[],
|
|
79
|
+
);
|
|
80
|
+
expect(formViolations(PBS_MATCHER, matcherCreateForm(fields(['m'.repeat(33)])), false)).toEqual(
|
|
81
|
+
['target: at most 32 characters'],
|
|
82
|
+
);
|
|
83
|
+
// ⚠️ AND THE SHORT ONE TOO: `minLength` on an element is the bound a joined string would hide.
|
|
84
|
+
expect(formViolations(PBS_MATCHER, matcherCreateForm(fields(['m'])), false)).toEqual([
|
|
85
|
+
'target: at least 2 characters',
|
|
86
|
+
]);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* ⛔ `delete` IS AN ARRAY OF AN ENUM ON PBS, and `matcherUpdateForm` fills it from its own
|
|
91
|
+
* `Deletable` union. This is the one place the two could drift: a member this package clears
|
|
92
|
+
* that PBS does not accept is a 400 the vendor's own enum can refuse at plan time.
|
|
93
|
+
*/
|
|
94
|
+
test("the update form's cleared fields are all members of PBS's own delete enum", () => {
|
|
95
|
+
const form = matcherUpdateForm({ comment: '', name: 'default-matcher', targets: [] });
|
|
96
|
+
expect(form.delete).toEqual([
|
|
97
|
+
'match-calendar',
|
|
98
|
+
'match-field',
|
|
99
|
+
'match-severity',
|
|
100
|
+
'target',
|
|
101
|
+
'comment',
|
|
102
|
+
]);
|
|
103
|
+
expect(formViolations(PBS_MATCHER_PUT, form, false)).toEqual([]);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
/** PVE spells the same list with `format: pve-configid`, which is a NAME and stays unchecked. */
|
|
107
|
+
test('a PVE element rule with only a format is recorded and not enforced', () => {
|
|
108
|
+
expect(constraintsFor(PVE_MATCHER)['target']).toMatchObject({
|
|
109
|
+
each: true,
|
|
110
|
+
format: 'pve-configid',
|
|
111
|
+
});
|
|
112
|
+
expect(formViolations(PVE_MATCHER, matcherCreateForm(fields(['m'.repeat(80)])), false)).toEqual(
|
|
113
|
+
[],
|
|
114
|
+
);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* 🔴 `Proxmox.HaRule`'s TABLE WAS EMPTY AND NOTHING SAID SO. PVE spells `POST /cluster/ha/rules`
|
|
120
|
+
* as `allOf: [{properties: {rule}}, {oneOf: [node-affinity, resource-affinity]}]`, the reader
|
|
121
|
+
* asked for `parameters.properties`, got `undefined`, and emitted `{}` — which is exactly what
|
|
122
|
+
* an endpoint with no rules emits. A wired family's guard checked nothing, silently.
|
|
123
|
+
*/
|
|
124
|
+
describe('a parameter schema wrapped in allOf/oneOf is read, not skipped', () => {
|
|
125
|
+
const CREATE = 'pve:POST /cluster/ha/rules';
|
|
126
|
+
|
|
127
|
+
test('the table is no longer empty, and carries the limits both branches agree on', () => {
|
|
128
|
+
const table = constraintsFor(CREATE);
|
|
129
|
+
expect(Object.keys(table).sort()).toEqual(['affinity', 'comment', 'resources', 'rule']);
|
|
130
|
+
expect(table['comment']).toMatchObject({ maxLength: 4096 });
|
|
131
|
+
expect(table['affinity']).toMatchObject({ enum: ['positive', 'negative'] });
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* ⛔ `nodes` AND `strict` EXIST ONLY ON THE node-affinity BRANCH, so enforcing them would refuse
|
|
136
|
+
* a resource-affinity rule for breaking a rule PVE never applies to it. `oneOf` intersects.
|
|
137
|
+
*/
|
|
138
|
+
test('a rule only one branch states is NOT enforced', () => {
|
|
139
|
+
expect(constraintsFor(CREATE)['nodes']).toBeUndefined();
|
|
140
|
+
expect(constraintsFor(CREATE)['strict']).toBeUndefined();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* ⚠️ `affinity` IS REQUIRED ON resource-affinity AND OPTIONAL ON node-affinity, so presence is
|
|
145
|
+
* not enforced. Dropping a disagreeing `optional` would have read as REQUIRED — absence means
|
|
146
|
+
* required in this schema — and refused every legal node-affinity rule.
|
|
147
|
+
*/
|
|
148
|
+
test('presence is required only where every branch requires it', () => {
|
|
149
|
+
const table = constraintsFor(CREATE);
|
|
150
|
+
expect(table['rule']?.required).toBe(true);
|
|
151
|
+
expect(table['resources']?.required).toBe(true);
|
|
152
|
+
expect(table['affinity']?.required).toBeUndefined();
|
|
153
|
+
expect(formViolations(CREATE, { affinity: 'positive', resources: 'vm:100' }, true)).toEqual([
|
|
154
|
+
'rule: required',
|
|
155
|
+
]);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test('the live shape of a node-affinity rule has no violations', () => {
|
|
159
|
+
const form = { affinity: 'positive', nodes: 'n1:2,n2:1', resources: 'vm:100', rule: 'pin-100' };
|
|
160
|
+
expect(formViolations(CREATE, form, true)).toEqual([]);
|
|
161
|
+
expect(formViolations(CREATE, { ...form, comment: 'c'.repeat(4097) }, true)).toEqual([
|
|
162
|
+
'comment: at most 4096 characters',
|
|
163
|
+
]);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The cluster-scoped families' REAL create forms, against the vendor's own tables.
|
|
3
|
+
*
|
|
4
|
+
* ⛔ THE RISK THIS FEATURE CARRIES IS NOT THE ONE IT FIXES. A table that refuses a declaration the
|
|
5
|
+
* vendor would have ACCEPTED blocks a deploy that was always legal, and the operator cannot tell
|
|
6
|
+
* that from a genuine violation. So every family wired on 2026-09-22 gets the same proof: build
|
|
7
|
+
* the form the provider really builds, run it through `formViolations(key, form, true)` — the
|
|
8
|
+
* create check, presence included — and require an empty result.
|
|
9
|
+
*
|
|
10
|
+
* ⛔ THE FORM, NEVER A RETYPED COPY OF IT. Four families built their create form inline in a spec
|
|
11
|
+
* literal; each one gained an exported builder that the provider now uses, so this file and the
|
|
12
|
+
* provider cannot disagree. A test that retypes the form proves the test.
|
|
13
|
+
*
|
|
14
|
+
* ★ THE NODE-SCOPED HALF IS IN constraints-node-forms.test.ts — split for the 250-line cap, on the
|
|
15
|
+
* same seam the generated tables are split on (`/cluster` and `/access` here, `/nodes/{node}`
|
|
16
|
+
* there).
|
|
17
|
+
*/
|
|
18
|
+
import { describe, expect, test } from 'bun:test';
|
|
19
|
+
import { shape as apiTokenForm } from './api-token-form.ts';
|
|
20
|
+
import { NETWORK_APPLY_ENDPOINT, SDN_APPLY_ENDPOINT } from './apply-endpoints.ts';
|
|
21
|
+
import { constraintsFor, formViolations } from './constraint-guard.ts';
|
|
22
|
+
import type { PveTarget } from './credentials.ts';
|
|
23
|
+
import { createBody as metricCreateForm } from './metric-server-form.ts';
|
|
24
|
+
import { createShape as targetCreateForm } from './notification-target-form.ts';
|
|
25
|
+
import { createForm as subnetCreateForm } from './sdn-subnet-form.ts';
|
|
26
|
+
|
|
27
|
+
const TARGET: PveTarget = { members: ['pve.test:8006'], mount: 'pve-test', scheme: 'pve' };
|
|
28
|
+
|
|
29
|
+
/** `formViolations` with presence — the create check, exactly as `resource-guard.ts` runs it. */
|
|
30
|
+
const onCreate = (key: string, form: Record<string, string | readonly string[]>) =>
|
|
31
|
+
formViolations(key, form, true);
|
|
32
|
+
|
|
33
|
+
describe('Proxmox.ApiToken plans clean against POST /access/users/{userid}/token/{tokenid}', () => {
|
|
34
|
+
/** ⚠️ `expire: 0` is "never", and PVE's `minimum: 0` accepts it — the bound the table carries. */
|
|
35
|
+
test('the estate-shaped token declaration has no violations', () => {
|
|
36
|
+
expect(
|
|
37
|
+
onCreate(
|
|
38
|
+
'pve:POST /access/users/{userid}/token/{tokenid}',
|
|
39
|
+
apiTokenForm({
|
|
40
|
+
comment: 'Declared by Alchemy',
|
|
41
|
+
expire: 0,
|
|
42
|
+
privsep: false,
|
|
43
|
+
target: TARGET,
|
|
44
|
+
tokenid: 'apply',
|
|
45
|
+
userid: 'iac@pve',
|
|
46
|
+
}),
|
|
47
|
+
),
|
|
48
|
+
).toEqual([]);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
/** ⛔ `{userid}` AND `{tokenid}` ARE PATH SEGMENTS. A table that kept them would refuse every
|
|
52
|
+
* create for a "missing" parameter that was never missing (codegen/emit.ts). */
|
|
53
|
+
test('neither path parameter is in the table', () => {
|
|
54
|
+
const table = constraintsFor('pve:POST /access/users/{userid}/token/{tokenid}');
|
|
55
|
+
expect(table['userid']).toBeUndefined();
|
|
56
|
+
expect(table['tokenid']).toBeUndefined();
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe('Proxmox.MetricServer plans clean against POST /cluster/metrics/server/{id}', () => {
|
|
61
|
+
/**
|
|
62
|
+
* The estate's own row, SHAPED: InfluxDB v2 over https with an api-path-prefix, an
|
|
63
|
+
* organization and a bucket, every value a placeholder.
|
|
64
|
+
*
|
|
65
|
+
* ⛔ SHAPE ONLY, FOR THE REASON lxc-harness.ts GIVES. `src` ships in the npm tarball and this
|
|
66
|
+
* repository is public, so a real metrics host, cluster name or organization here would be
|
|
67
|
+
* estate topology published forever in the git history. The proof is about which KEYS the
|
|
68
|
+
* create form sends and which bounds they face; the strings carry none of it.
|
|
69
|
+
*/
|
|
70
|
+
const declared = {
|
|
71
|
+
'api-path-prefix': '/cluster-a/',
|
|
72
|
+
bucket: 'pve',
|
|
73
|
+
disable: false,
|
|
74
|
+
id: 'metrics-example',
|
|
75
|
+
influxdbproto: 'https',
|
|
76
|
+
'max-body-size': 10_000_000,
|
|
77
|
+
organization: 'example',
|
|
78
|
+
port: 443,
|
|
79
|
+
server: 'metrics.example.invalid',
|
|
80
|
+
target: TARGET,
|
|
81
|
+
type: 'influxdb',
|
|
82
|
+
'verify-certificate': true,
|
|
83
|
+
} as const;
|
|
84
|
+
|
|
85
|
+
test('the live declaration has no violations', () => {
|
|
86
|
+
expect(onCreate('pve:POST /cluster/metrics/server/{id}', metricCreateForm(declared))).toEqual(
|
|
87
|
+
[],
|
|
88
|
+
);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
/** ⛔ PVE MARKS THREE PARAMETERS REQUIRED AND THE CREATE FORM SENDS ALL THREE. `id` is the path. */
|
|
92
|
+
test('port, server and type are required and all three are sent', () => {
|
|
93
|
+
const table = constraintsFor('pve:POST /cluster/metrics/server/{id}');
|
|
94
|
+
expect(
|
|
95
|
+
Object.entries(table)
|
|
96
|
+
.filter(([, rule]) => rule.required)
|
|
97
|
+
.map(([name]) => name),
|
|
98
|
+
).toEqual(['port', 'server', 'type']);
|
|
99
|
+
expect(Object.keys(metricCreateForm(declared))).toContain('type');
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('a port past PVE own maximum is refused rather than sent', () => {
|
|
103
|
+
expect(
|
|
104
|
+
onCreate(
|
|
105
|
+
'pve:POST /cluster/metrics/server/{id}',
|
|
106
|
+
metricCreateForm({ ...declared, port: 70_000 }),
|
|
107
|
+
),
|
|
108
|
+
).toEqual(['port: at most 65536']);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
describe('Proxmox.NotificationTarget plans clean per family', () => {
|
|
113
|
+
/** The shipped `mail-to-root`, edited — the shape every estate has. sendmail needs only a name. */
|
|
114
|
+
test('the live sendmail declaration has no violations', () => {
|
|
115
|
+
expect(
|
|
116
|
+
onCreate(
|
|
117
|
+
'pve:POST /cluster/notifications/endpoints/sendmail',
|
|
118
|
+
targetCreateForm({
|
|
119
|
+
comment: "Send mails to root@pam's email address",
|
|
120
|
+
disable: false,
|
|
121
|
+
mailto: ['someone@example.invalid'],
|
|
122
|
+
'mailto-user': ['root@pam'],
|
|
123
|
+
name: 'mail-to-root',
|
|
124
|
+
target: TARGET,
|
|
125
|
+
type: 'sendmail',
|
|
126
|
+
}),
|
|
127
|
+
),
|
|
128
|
+
).toEqual([]);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test('an smtp target that declares its server and sender has no violations', () => {
|
|
132
|
+
expect(
|
|
133
|
+
onCreate(
|
|
134
|
+
'pve:POST /cluster/notifications/endpoints/smtp',
|
|
135
|
+
targetCreateForm({
|
|
136
|
+
'from-address': 'pve@example.invalid',
|
|
137
|
+
mode: 'tls',
|
|
138
|
+
name: 'smtp-relay',
|
|
139
|
+
port: 465,
|
|
140
|
+
server: 'smtp.example.invalid',
|
|
141
|
+
target: TARGET,
|
|
142
|
+
type: 'smtp',
|
|
143
|
+
}),
|
|
144
|
+
),
|
|
145
|
+
).toEqual([]);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* ⛔ THE ONE FAMILY THAT CANNOT SATISFY ITS OWN CREATE, PROVEN RATHER THAN ASSERTED IN PROSE.
|
|
150
|
+
* PVE requires gotify's `token` and it is a write-only secret, so it is not a prop — props are
|
|
151
|
+
* persisted to the state store unencrypted. `notification-target.ts` has said so in a comment
|
|
152
|
+
* since the family was written; this is the comment made executable.
|
|
153
|
+
* ★ AND IT IS WHY THE CREATE CHECK IS CONDITIONAL. `resource-guard.ts` demands presence only
|
|
154
|
+
* when a create is really about to happen, so the documented workflow — create it out of band
|
|
155
|
+
* with its secret, then declare it — still plans clean. Asking to CREATE one fails at plan
|
|
156
|
+
* with the vendor's own word instead of a 400 half a deploy in.
|
|
157
|
+
*/
|
|
158
|
+
test('a gotify create is refused for the token it may never carry', () => {
|
|
159
|
+
expect(
|
|
160
|
+
onCreate(
|
|
161
|
+
'pve:POST /cluster/notifications/endpoints/gotify',
|
|
162
|
+
targetCreateForm({
|
|
163
|
+
name: 'gotify-house',
|
|
164
|
+
server: 'https://gotify.example.invalid',
|
|
165
|
+
target: TARGET,
|
|
166
|
+
type: 'gotify',
|
|
167
|
+
}),
|
|
168
|
+
),
|
|
169
|
+
).toEqual(['token: required']);
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
describe('Proxmox.SdnSubnet plans clean against POST /cluster/sdn/vnets/{vnet}/subnets', () => {
|
|
174
|
+
test('a declared subnet has no violations', () => {
|
|
175
|
+
expect(
|
|
176
|
+
onCreate(
|
|
177
|
+
'pve:POST /cluster/sdn/vnets/{vnet}/subnets',
|
|
178
|
+
subnetCreateForm({
|
|
179
|
+
cidr: '192.0.2.0/24',
|
|
180
|
+
gateway: '192.0.2.1',
|
|
181
|
+
target: TARGET,
|
|
182
|
+
vnet: 'vnet0',
|
|
183
|
+
zone: 'zone0',
|
|
184
|
+
}),
|
|
185
|
+
),
|
|
186
|
+
).toEqual([]);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
/** ⚠️ `subnet` and `type` are the two PVE marks required, and `createForm` sends both. */
|
|
190
|
+
test('subnet and type are required and both are sent', () => {
|
|
191
|
+
const table = constraintsFor('pve:POST /cluster/sdn/vnets/{vnet}/subnets');
|
|
192
|
+
expect(table['subnet']?.required).toBe(true);
|
|
193
|
+
expect(table['type']).toMatchObject({ enum: ['subnet'], required: true });
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* ★ AN ACTION ENDPOINT IS WIRED TOO, AND THE TABLE IS SUPPOSED TO BE EMPTY. Neither apply takes a
|
|
199
|
+
* body, so there is nothing for a length or a range to be wrong about — the key exists so that
|
|
200
|
+
* `codegen/constraints.ts` resolves it against the vendor schema at generation time. A PVE that
|
|
201
|
+
* moves or withdraws either one fails `bun run check` rather than an `ifreload -a` on three
|
|
202
|
+
* nodes at once. ⛔ An empty table is NOT the same as a missing one: a missing one throws.
|
|
203
|
+
*/
|
|
204
|
+
describe('the two applies are wired although they carry no form', () => {
|
|
205
|
+
test('both tables exist and both are empty', () => {
|
|
206
|
+
expect(constraintsFor(NETWORK_APPLY_ENDPOINT)).toEqual({});
|
|
207
|
+
expect(constraintsFor(SDN_APPLY_ENDPOINT)).toEqual({});
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
test('a key the generator never tabled is a defect that names the command', () => {
|
|
211
|
+
expect(() => constraintsFor('pve:PUT /cluster/sdn/nope')).toThrow('bun codegen/constraints.ts');
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* ⛔ `Proxmox.CephFlag` HAS NO CREATE KEY, AND THAT IS THE VENDOR'S DOING. PVE registers only PUT
|
|
217
|
+
* on `/cluster/ceph/flags/{flag}`; `POST /cluster/ceph/flags` does not exist, which is why
|
|
218
|
+
* ceph-flag.ts documents its create branch as unreachable. Naming that POST would stop the
|
|
219
|
+
* generator — correctly — so the family declares the update alone.
|
|
220
|
+
*/
|
|
221
|
+
describe('Proxmox.CephFlag is wired through its PUT alone', () => {
|
|
222
|
+
test('the update form passes, and value is the one rule PVE publishes', () => {
|
|
223
|
+
expect(formViolations('pve:PUT /cluster/ceph/flags/{flag}', { value: '1' }, false)).toEqual([]);
|
|
224
|
+
expect(constraintsFor('pve:PUT /cluster/ceph/flags/{flag}')).toEqual({
|
|
225
|
+
value: { required: true, type: 'boolean' },
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
});
|