@homeflare/alchemy 0.15.0 → 0.19.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 +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 +1554 -0
- package/dist/linux/index.js.map +37 -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 +44 -0
- package/dist/linux/unit-lifecycle.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 +186 -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 +193 -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 +220 -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 +188 -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,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The whole live PBS estate, through its own create and update tables, expecting NOTHING.
|
|
3
|
+
*
|
|
4
|
+
* ⛔ THIS IS THE RISK THE CONSTRAINT GUARD CARRIES, NOT THE ONE IT FIXES. A table that refuses a
|
|
5
|
+
* declaration the vendor would have ACCEPTED blocks a deploy that was always legal, and the
|
|
6
|
+
* operator cannot tell that from a genuine violation. `guardForms` runs in `diff` AND in
|
|
7
|
+
* `reconcile`, and `reconcile` runs for an ADOPTED row — so every object the estate has already
|
|
8
|
+
* adopted faces these tables on its next plan. A false positive here is a broken plan for ten
|
|
9
|
+
* live objects, including a datastore holding the offsite copies.
|
|
10
|
+
*
|
|
11
|
+
* ★ THE VALUES ARE THE LIVE ONES, read from homeflare-proxmox's own inventory fixture
|
|
12
|
+
* (`tests/fixtures/pbs-live-2026-09-21.json`, captured read-only 2026-09-21): the ten objects
|
|
13
|
+
* PBS actually holds, with their real ids, stores, schedules and retention numbers.
|
|
14
|
+
* ⚠️ EXCEPT THE COMMENT TEXT, WHICH IS REPLACED BY A STRING OF THE SAME CHARACTER LENGTH. This
|
|
15
|
+
* package is public and the live comments name hosts and runbook paths. Length and character
|
|
16
|
+
* class are the whole of what a `maxLength`/`pattern` rule sees, so the substitution costs the
|
|
17
|
+
* test nothing — and the lengths (106, 116, 88, 107, 120) are the live ones, asserted below.
|
|
18
|
+
*/
|
|
19
|
+
import { describe, expect, test } from 'bun:test';
|
|
20
|
+
import { formViolations } from './constraint-guard.ts';
|
|
21
|
+
import type { PbsTarget } from './credentials.ts';
|
|
22
|
+
import { createForm as datastoreCreateForm, updateForm } from './pbs-datastore-form.ts';
|
|
23
|
+
import { createBody as pruneCreateBody, updateBody as pruneUpdate } from './pbs-prune-job-form.ts';
|
|
24
|
+
import { createBody as syncCreateBody, updateBody as syncUpdate } from './pbs-sync-job-form.ts';
|
|
25
|
+
import type { PbsVerifyJobProps } from './pbs-verify-job.ts';
|
|
26
|
+
import { shape as verifyShape } from './pbs-verify-job-form.ts';
|
|
27
|
+
|
|
28
|
+
const PBS: PbsTarget = { api: 'https://pbs.test:8007/api2/json', mount: 'pbs-test', scheme: 'pbs' };
|
|
29
|
+
const filler = (length: number) => 'c'.repeat(length);
|
|
30
|
+
|
|
31
|
+
/** What `pbs-verify-job.ts` builds inline for a create: the update shape plus the required `id`. */
|
|
32
|
+
const verifyCreate = (props: PbsVerifyJobProps) => ({ ...verifyShape(props), id: props.id });
|
|
33
|
+
|
|
34
|
+
describe('every live PBS object passes its own create table', () => {
|
|
35
|
+
test('the three prune jobs, including the parked one', () => {
|
|
36
|
+
for (const job of [
|
|
37
|
+
{ id: 'prune-cluster-all', keep: 3, schedule: 'Sat 06:00', store: 'cluster' },
|
|
38
|
+
{ comment: filler(88), id: 'prune-datacenter-all', keep: 3, store: 'datacenter' },
|
|
39
|
+
{ id: 'prune-r2-offsite-all', keep: 3, schedule: 'Sat 07:30', store: 'r2-offsite' },
|
|
40
|
+
]) {
|
|
41
|
+
const form = pruneCreateBody({
|
|
42
|
+
...job,
|
|
43
|
+
'keep-daily': 7,
|
|
44
|
+
'keep-last': job.keep,
|
|
45
|
+
'keep-monthly': 3,
|
|
46
|
+
'keep-weekly': 4,
|
|
47
|
+
'max-depth': 7,
|
|
48
|
+
schedule: job.schedule ?? 'Sat 08:00',
|
|
49
|
+
target: PBS,
|
|
50
|
+
});
|
|
51
|
+
expect([job.id, formViolations('pbs:POST /config/prune', form, true)]).toEqual([job.id, []]);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* ⚠️ `sync-all-to-dc` HAS NO `schedule` AND `v-datacenter` HAS NEITHER `schedule` NOR `ns`. Both
|
|
57
|
+
* are parked, and both are legal: PBS marks `schedule` optional on sync and verify jobs. If
|
|
58
|
+
* the presence check ever read `required` from the wrong place, these two rows are what fails.
|
|
59
|
+
*/
|
|
60
|
+
test('the two sync jobs, one of them parked with no schedule', () => {
|
|
61
|
+
const parked = syncCreateBody({
|
|
62
|
+
comment: filler(107),
|
|
63
|
+
id: 'sync-all-to-dc',
|
|
64
|
+
'max-depth': 7,
|
|
65
|
+
'remote-store': 'cluster',
|
|
66
|
+
'remove-vanished': false,
|
|
67
|
+
store: 'datacenter',
|
|
68
|
+
target: PBS,
|
|
69
|
+
'verified-only': true,
|
|
70
|
+
});
|
|
71
|
+
expect(formViolations('pbs:POST /config/sync', parked, true)).toEqual([]);
|
|
72
|
+
const live = syncCreateBody({
|
|
73
|
+
id: 'sync-all-to-r2',
|
|
74
|
+
owner: 'root@pam',
|
|
75
|
+
'remote-store': 'cluster',
|
|
76
|
+
schedule: '08:30',
|
|
77
|
+
store: 'r2-offsite',
|
|
78
|
+
target: PBS,
|
|
79
|
+
});
|
|
80
|
+
expect(formViolations('pbs:POST /config/sync', live, true)).toEqual([]);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* ⚠️ `{...shape(props), id}` IS THE CREATE FORM, SPELLED OUT. `pbs-verify-job.ts` builds it
|
|
85
|
+
* inline and exports only `shape`, which is the UPDATE form and carries no `id` — and `id` is
|
|
86
|
+
* one of the two parameters PBS marks required on this endpoint. Calling `shape` here and
|
|
87
|
+
* expecting no violations reported `id: required`, which is the presence check working, not a
|
|
88
|
+
* defect. If that inline `id` is ever dropped, this test is what says so.
|
|
89
|
+
* ⚠️ `ns: ''` IS A LIVE VALUE AND `shape` DROPS IT rather than sending the empty string, so the
|
|
90
|
+
* table never sees it. The vendor's pattern would accept it either way — it ends `)?$`.
|
|
91
|
+
*/
|
|
92
|
+
test('the two verify jobs, one with an empty namespace', () => {
|
|
93
|
+
const running = verifyCreate({
|
|
94
|
+
id: 'v-b7d7ff81-17d6',
|
|
95
|
+
'ignore-verified': true,
|
|
96
|
+
ns: '',
|
|
97
|
+
'outdated-after': 30,
|
|
98
|
+
schedule: '06:30',
|
|
99
|
+
store: 'cluster',
|
|
100
|
+
target: PBS,
|
|
101
|
+
});
|
|
102
|
+
expect(formViolations('pbs:POST /config/verify', running, true)).toEqual([]);
|
|
103
|
+
const parked = verifyCreate({
|
|
104
|
+
comment: filler(120),
|
|
105
|
+
id: 'v-datacenter',
|
|
106
|
+
'ignore-verified': true,
|
|
107
|
+
'outdated-after': 30,
|
|
108
|
+
schedule: null,
|
|
109
|
+
store: 'datacenter',
|
|
110
|
+
target: PBS,
|
|
111
|
+
});
|
|
112
|
+
expect(formViolations('pbs:POST /config/verify', parked, true)).toEqual([]);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('the three datastores, on create and on update', () => {
|
|
116
|
+
for (const store of [
|
|
117
|
+
{ comment: filler(106), name: 'cluster', path: '/mnt/datastore/cluster' },
|
|
118
|
+
{ comment: filler(116), name: 'datacenter', path: '/mnt/cluster_pbs' },
|
|
119
|
+
{ comment: filler(106), name: 'r2-offsite', path: '/mnt/datastore/r2-cache' },
|
|
120
|
+
]) {
|
|
121
|
+
const props = { ...store, target: PBS };
|
|
122
|
+
expect([
|
|
123
|
+
store.name,
|
|
124
|
+
formViolations('pbs:POST /config/datastore', datastoreCreateForm(props), true),
|
|
125
|
+
]).toEqual([store.name, []]);
|
|
126
|
+
// ⚠️ THE UPDATE FORM IS PARTIAL BY DESIGN, so presence is off — value rules still apply.
|
|
127
|
+
expect([
|
|
128
|
+
store.name,
|
|
129
|
+
formViolations('pbs:PUT /config/datastore/{name}', updateForm(props), false),
|
|
130
|
+
]).toEqual([store.name, []]);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test('the prune and sync update forms, which send only what changed', () => {
|
|
135
|
+
const prune = pruneUpdate({
|
|
136
|
+
id: 'prune-cluster-all',
|
|
137
|
+
'keep-daily': 7,
|
|
138
|
+
'keep-last': 3,
|
|
139
|
+
'max-depth': 7,
|
|
140
|
+
schedule: 'Sat 06:00',
|
|
141
|
+
store: 'cluster',
|
|
142
|
+
target: PBS,
|
|
143
|
+
});
|
|
144
|
+
expect(formViolations('pbs:PUT /config/prune/{id}', prune, false)).toEqual([]);
|
|
145
|
+
const sync = syncUpdate({
|
|
146
|
+
id: 'sync-all-to-r2',
|
|
147
|
+
owner: 'root@pam',
|
|
148
|
+
'remote-store': 'cluster',
|
|
149
|
+
schedule: '08:30',
|
|
150
|
+
store: 'r2-offsite',
|
|
151
|
+
target: PBS,
|
|
152
|
+
});
|
|
153
|
+
expect(formViolations('pbs:PUT /config/sync/{id}', sync, false)).toEqual([]);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The node-scoped families' REAL create forms, against the vendor's own tables — and PBS's
|
|
3
|
+
* notification targets, which carry the very rule the 2026-09-22 incident was about.
|
|
4
|
+
*
|
|
5
|
+
* ⛔ SAME PROOF AS constraints-forms.test.ts, AND THE SAME REASON FOR IT: a table that refuses a
|
|
6
|
+
* declaration the vendor would have ACCEPTED is worse than the 400 it replaces, because the
|
|
7
|
+
* operator cannot tell it from a genuine violation. Each family's own builder, through
|
|
8
|
+
* `formViolations(key, form, true)`, empty result required.
|
|
9
|
+
*/
|
|
10
|
+
import { describe, expect, test } from 'bun:test';
|
|
11
|
+
import { createForm as daemonCreateForm } from './ceph-daemon-form.ts';
|
|
12
|
+
import { createForm as cephFsCreateForm } from './ceph-fs-wire.ts';
|
|
13
|
+
import { createOsdForm } from './ceph-osd-write.ts';
|
|
14
|
+
import { createBody as cephPoolCreateForm } from './ceph-pool-form.ts';
|
|
15
|
+
import { constraintsFor, formViolations } from './constraint-guard.ts';
|
|
16
|
+
import type { PveTarget } from './credentials.ts';
|
|
17
|
+
import { TARGET as LXC_TARGET, pasted } from './lxc-harness.ts';
|
|
18
|
+
import { createForm as lxcCreateForm } from './lxc-create-form.ts';
|
|
19
|
+
import { createForm as vmCreateForm } from './qemu.ts';
|
|
20
|
+
|
|
21
|
+
const TARGET: PveTarget = { members: ['pve.test:8006'], mount: 'pve-test', scheme: 'pve' };
|
|
22
|
+
|
|
23
|
+
const onCreate = (key: string, form: Record<string, string | readonly string[]>) =>
|
|
24
|
+
formViolations(key, form, true);
|
|
25
|
+
|
|
26
|
+
describe('the Ceph families plan clean against their own creates', () => {
|
|
27
|
+
/** ⚠️ Three kinds, three endpoints, three parameter schemas — see `DAEMON_ENDPOINTS`. */
|
|
28
|
+
test('a mon, an mds and a mgr each pass their own table', () => {
|
|
29
|
+
expect(
|
|
30
|
+
onCreate(
|
|
31
|
+
'pve:POST /nodes/{node}/ceph/mon/{monid}',
|
|
32
|
+
daemonCreateForm({ kind: 'mon', 'mon-address': '192.0.2.11', node: 'n2', target: TARGET }),
|
|
33
|
+
),
|
|
34
|
+
).toEqual([]);
|
|
35
|
+
expect(
|
|
36
|
+
onCreate(
|
|
37
|
+
'pve:POST /nodes/{node}/ceph/mds/{name}',
|
|
38
|
+
daemonCreateForm({ hotstandby: true, kind: 'mds', node: 'n2', target: TARGET }),
|
|
39
|
+
),
|
|
40
|
+
).toEqual([]);
|
|
41
|
+
expect(
|
|
42
|
+
onCreate(
|
|
43
|
+
'pve:POST /nodes/{node}/ceph/mgr/{id}',
|
|
44
|
+
daemonCreateForm({ kind: 'mgr', node: 'n2', target: TARGET }),
|
|
45
|
+
),
|
|
46
|
+
).toEqual([]);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('a CephFS create passes, and pg_num carries the vendor own 8..32768', () => {
|
|
50
|
+
expect(
|
|
51
|
+
onCreate(
|
|
52
|
+
'pve:POST /nodes/{node}/ceph/fs/{name}',
|
|
53
|
+
cephFsCreateForm({ 'add-storage': true, name: 'cephfs-c1', node: 'n2', target: TARGET }),
|
|
54
|
+
),
|
|
55
|
+
).toEqual([]);
|
|
56
|
+
expect(constraintsFor('pve:POST /nodes/{node}/ceph/fs/{name}')['pg_num']).toMatchObject({
|
|
57
|
+
maximum: 32_768,
|
|
58
|
+
minimum: 8,
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('an OSD create passes, and dev is the parameter PVE requires', () => {
|
|
63
|
+
expect(
|
|
64
|
+
onCreate(
|
|
65
|
+
'pve:POST /nodes/{node}/ceph/osd',
|
|
66
|
+
createOsdForm({ node: 'n2', osdid: 4, target: TARGET }, '/dev/nvme1n1'),
|
|
67
|
+
),
|
|
68
|
+
).toEqual([]);
|
|
69
|
+
expect(constraintsFor('pve:POST /nodes/{node}/ceph/osd')['dev']?.required).toBe(true);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
/** The estate's own pools: `cephtb4` and the two `cephfs-tb4_*` halves, size 3 / min_size 2. */
|
|
73
|
+
test('the live CephPool declarations have no violations', () => {
|
|
74
|
+
for (const name of ['cephtb4', 'cephfs-tb4_data', 'cephfs-tb4_metadata']) {
|
|
75
|
+
expect(
|
|
76
|
+
onCreate(
|
|
77
|
+
'pve:POST /nodes/{node}/ceph/pool',
|
|
78
|
+
cephPoolCreateForm({
|
|
79
|
+
crush_rule: 'replicated_rule',
|
|
80
|
+
min_size: 2,
|
|
81
|
+
name,
|
|
82
|
+
node: 'n2',
|
|
83
|
+
pg_autoscale_mode: 'on',
|
|
84
|
+
size: 3,
|
|
85
|
+
target: TARGET,
|
|
86
|
+
}),
|
|
87
|
+
),
|
|
88
|
+
name,
|
|
89
|
+
).toEqual([]);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test('a pool asking for size 8 is refused: PVE own maximum is 7', () => {
|
|
94
|
+
expect(
|
|
95
|
+
onCreate(
|
|
96
|
+
'pve:POST /nodes/{node}/ceph/pool',
|
|
97
|
+
cephPoolCreateForm({ name: 'toobig', node: 'n2', size: 8, target: TARGET }),
|
|
98
|
+
),
|
|
99
|
+
).toEqual(['size: at most 7']);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe('the guest families plan clean against their own creates', () => {
|
|
104
|
+
/**
|
|
105
|
+
* ⚠️ THE HARNESS DECLARATION, PLUS THE ONE CREATE-ONLY KEY IT CANNOT CARRY. `pasted()` is the
|
|
106
|
+
* production-SHAPED config with placeholder values (lxc-harness.ts); `ostemplate` is absent
|
|
107
|
+
* from it because a live config never reports one, and PVE requires it on create.
|
|
108
|
+
*/
|
|
109
|
+
test('a full LXC declaration with its template has no violations', () => {
|
|
110
|
+
const form = lxcCreateForm(
|
|
111
|
+
pasted({ ostemplate: 'local:vztmpl/debian-13-standard_13.1-2_amd64.tar.zst' }),
|
|
112
|
+
);
|
|
113
|
+
expect(form['vmid']).toBe('900');
|
|
114
|
+
expect(onCreate('pve:POST /nodes/{node}/lxc', form)).toEqual([]);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
/** ⛔ `ostemplate` AND `vmid` ARE THE TWO PVE MARKS REQUIRED, and `{node}` is the path. */
|
|
118
|
+
test('ostemplate and vmid are the required pair, and node is not in the table', () => {
|
|
119
|
+
const table = constraintsFor('pve:POST /nodes/{node}/lxc');
|
|
120
|
+
expect(
|
|
121
|
+
Object.entries(table)
|
|
122
|
+
.filter(([, rule]) => rule.required)
|
|
123
|
+
.map(([name]) => name),
|
|
124
|
+
).toEqual(['ostemplate', 'vmid']);
|
|
125
|
+
expect(table['node']).toBeUndefined();
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
/** ⚠️ The same harness props, against the SECOND endpoint this family writes. */
|
|
129
|
+
test('a resize form passes its own table', () => {
|
|
130
|
+
expect(
|
|
131
|
+
formViolations(
|
|
132
|
+
'pve:PUT /nodes/{node}/lxc/{vmid}/resize',
|
|
133
|
+
{ disk: 'rootfs', size: '+8G' },
|
|
134
|
+
true,
|
|
135
|
+
),
|
|
136
|
+
).toEqual([]);
|
|
137
|
+
expect(LXC_TARGET.scheme).toBe('pve');
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* ⛔ THE ONE VALUE THIS PACKAGE ECHOES BACK TO PVE UNCHANGED, AND IT HAS A BOUND. `updateGuest`
|
|
142
|
+
* carries the `digest` of the config it was judged against so PVE can refuse a write over an
|
|
143
|
+
* edit that landed meanwhile — and PVE's own schema caps that parameter at 40 characters,
|
|
144
|
+
* which is exactly a sha1_hex. A version that started returning a longer digest would refuse
|
|
145
|
+
* its own echo server-side; the point of the assertion is that 40 is not refused HERE.
|
|
146
|
+
*/
|
|
147
|
+
test('a config PUT carrying a 40-character digest is not refused', () => {
|
|
148
|
+
expect(
|
|
149
|
+
formViolations(
|
|
150
|
+
'pve:PUT /nodes/{node}/lxc/{vmid}/config',
|
|
151
|
+
{ digest: 'a'.repeat(40), memory: '8192' },
|
|
152
|
+
false,
|
|
153
|
+
),
|
|
154
|
+
).toEqual([]);
|
|
155
|
+
expect(constraintsFor('pve:PUT /nodes/{node}/lxc/{vmid}/config')['digest']).toMatchObject({
|
|
156
|
+
maxLength: 40,
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test('a VM create passes, and vmid carries PVE own 100..999999999', () => {
|
|
161
|
+
expect(
|
|
162
|
+
onCreate(
|
|
163
|
+
'pve:POST /nodes/{node}/qemu',
|
|
164
|
+
vmCreateForm({
|
|
165
|
+
cores: 2,
|
|
166
|
+
memory: 2048,
|
|
167
|
+
name: 'vm-example',
|
|
168
|
+
node: 'n2',
|
|
169
|
+
target: TARGET,
|
|
170
|
+
vmid: 150,
|
|
171
|
+
}),
|
|
172
|
+
),
|
|
173
|
+
).toEqual([]);
|
|
174
|
+
expect(constraintsFor('pve:POST /nodes/{node}/qemu')['vmid']).toMatchObject({
|
|
175
|
+
maximum: 999_999_999,
|
|
176
|
+
minimum: 100,
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test('a vmid below PVE own minimum is refused rather than sent', () => {
|
|
181
|
+
expect(
|
|
182
|
+
onCreate(
|
|
183
|
+
'pve:POST /nodes/{node}/qemu',
|
|
184
|
+
vmCreateForm({ node: 'n2', target: TARGET, vmid: 99 }),
|
|
185
|
+
),
|
|
186
|
+
).toEqual(['vmid: at least 100']);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the create check will actually DEMAND, committed — plus the families PR 102 wired without
|
|
3
|
+
* a create-form proof of their own.
|
|
4
|
+
*
|
|
5
|
+
* ⛔ THE OBJECTION THIS ANSWERS. Since 2026-09-22 the create form's required parameters are
|
|
6
|
+
* demanded only when a create is really about to happen (resource-guard.ts), which is correct —
|
|
7
|
+
* refusing a plan over a request that will never be made is a false positive by construction —
|
|
8
|
+
* but it moves some coverage out of the runtime and into here. So the required set is written
|
|
9
|
+
* down: a schema refresh that adds a required parameter to an endpoint this package writes to
|
|
10
|
+
* is EXACTLY the change that would start refusing creates, and it fails here, with a diff a
|
|
11
|
+
* reviewer can read, instead of on the first deploy that creates one.
|
|
12
|
+
*
|
|
13
|
+
* ⚠️ THE LIST IS THE VENDOR'S, NOT OURS. Nothing here asserts that a rule is sensible, only that
|
|
14
|
+
* it is what the two products publish today (pve-manager 9.2.11, proxmox-backup-server 4.2.6-1).
|
|
15
|
+
*/
|
|
16
|
+
import { describe, expect, test } from 'bun:test';
|
|
17
|
+
import { constraintsFor, formViolations } from './constraint-guard.ts';
|
|
18
|
+
import type { PveTarget } from './credentials.ts';
|
|
19
|
+
import { body as aliasBody } from './firewall-alias-form.ts';
|
|
20
|
+
import { PROXMOX_CONSTRAINTS } from './generated/constraints/index.ts';
|
|
21
|
+
import { body as haRuleBody } from './ha-rule-form.ts';
|
|
22
|
+
import { matcherCreateForm } from './notification-matcher-form.ts';
|
|
23
|
+
import { poolCreateForm } from './pool.ts';
|
|
24
|
+
import { createBody as replicationCreateBody } from './replication-job-form.ts';
|
|
25
|
+
|
|
26
|
+
const TARGET: PveTarget = { members: ['pve.test:8006'], mount: 'pve-test', scheme: 'pve' };
|
|
27
|
+
|
|
28
|
+
const onCreate = (key: string, form: Record<string, string | readonly string[]>) =>
|
|
29
|
+
formViolations(key, form, true);
|
|
30
|
+
|
|
31
|
+
const requiredOf = (key: string) =>
|
|
32
|
+
Object.entries(constraintsFor(key))
|
|
33
|
+
.filter(([, rule]) => rule.required === true)
|
|
34
|
+
.map(([name]) => name)
|
|
35
|
+
.sort();
|
|
36
|
+
|
|
37
|
+
describe('every create this package makes, and what the vendor requires of it', () => {
|
|
38
|
+
/**
|
|
39
|
+
* ⚠️ AN ENDPOINT WITH NO REQUIRED PARAMETER IS NOT A MISTAKE. `POST /cluster/backup` genuinely
|
|
40
|
+
* marks nothing required; recording the empty set is the same information as recording a full
|
|
41
|
+
* one. ⛔ `POST /cluster/ha/rules` LOOKED LIKE ONE OF THOSE until PR 113 taught the reader to
|
|
42
|
+
* follow PVE's `allOf`/`oneOf`, which is exactly the class of change this census exists to
|
|
43
|
+
* make visible in a diff.
|
|
44
|
+
*/
|
|
45
|
+
test('the required set of every tabled POST is exactly this', () => {
|
|
46
|
+
const creates = Object.keys(PROXMOX_CONSTRAINTS)
|
|
47
|
+
.filter((key) => key.includes(':POST '))
|
|
48
|
+
.sort();
|
|
49
|
+
const census = Object.fromEntries(
|
|
50
|
+
creates.map((key) => [key, requiredOf(key)] as const).filter(([, names]) => names.length > 0),
|
|
51
|
+
);
|
|
52
|
+
expect(census).toEqual({
|
|
53
|
+
'pbs:POST /config/datastore': ['name', 'path'],
|
|
54
|
+
'pbs:POST /config/notifications/endpoints/sendmail': ['name'],
|
|
55
|
+
'pbs:POST /config/notifications/endpoints/smtp': ['from-address', 'name', 'server'],
|
|
56
|
+
'pbs:POST /config/notifications/endpoints/webhook': ['method', 'name', 'url'],
|
|
57
|
+
'pbs:POST /config/notifications/matchers': ['name'],
|
|
58
|
+
'pbs:POST /config/prune': ['id', 'schedule', 'store'],
|
|
59
|
+
'pbs:POST /config/sync': ['id', 'remote-store', 'store'],
|
|
60
|
+
'pbs:POST /config/verify': ['id', 'store'],
|
|
61
|
+
'pve:POST /access/groups': ['groupid'],
|
|
62
|
+
'pve:POST /access/roles': ['roleid'],
|
|
63
|
+
'pve:POST /access/users': ['userid'],
|
|
64
|
+
'pve:POST /cluster/firewall/aliases': ['cidr', 'name'],
|
|
65
|
+
'pve:POST /cluster/ha/resources': ['sid'],
|
|
66
|
+
'pve:POST /cluster/ha/rules': ['resources', 'rule'],
|
|
67
|
+
'pve:POST /cluster/metrics/server/{id}': ['port', 'server', 'type'],
|
|
68
|
+
'pve:POST /cluster/notifications/endpoints/gotify': ['name', 'server', 'token'],
|
|
69
|
+
'pve:POST /cluster/notifications/endpoints/sendmail': ['name'],
|
|
70
|
+
'pve:POST /cluster/notifications/endpoints/smtp': ['from-address', 'name', 'server'],
|
|
71
|
+
'pve:POST /cluster/notifications/endpoints/webhook': ['method', 'name', 'url'],
|
|
72
|
+
'pve:POST /cluster/notifications/matchers': ['name'],
|
|
73
|
+
'pve:POST /cluster/replication': ['id', 'target', 'type'],
|
|
74
|
+
'pve:POST /cluster/sdn/vnets': ['vnet', 'zone'],
|
|
75
|
+
'pve:POST /cluster/sdn/vnets/{vnet}/subnets': ['subnet', 'type'],
|
|
76
|
+
'pve:POST /cluster/sdn/zones': ['type', 'zone'],
|
|
77
|
+
'pve:POST /nodes/{node}/ceph/osd': ['dev'],
|
|
78
|
+
'pve:POST /nodes/{node}/ceph/pool': ['name'],
|
|
79
|
+
'pve:POST /nodes/{node}/disks/zfs': ['devices', 'name', 'raidlevel'],
|
|
80
|
+
'pve:POST /nodes/{node}/lxc': ['ostemplate', 'vmid'],
|
|
81
|
+
'pve:POST /nodes/{node}/network': ['iface', 'type'],
|
|
82
|
+
'pve:POST /nodes/{node}/qemu': ['vmid'],
|
|
83
|
+
'pve:POST /pools': ['poolid'],
|
|
84
|
+
'pve:POST /storage': ['storage', 'type'],
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* ⛔ `PUT /access/acl` IS A CREATE HERE. PVE registers no POST on the ACL collection, so acl.ts
|
|
90
|
+
* names the same PUT for both — which means its required pair IS demanded on the create path.
|
|
91
|
+
*/
|
|
92
|
+
test('the ACL PUT requires a path and a role list, and both are its own props', () => {
|
|
93
|
+
expect(requiredOf('pve:PUT /access/acl')).toEqual(['path', 'roles']);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
/** ⚠️ No POST/PUT this package writes requires a parameter it sends only as a path segment. */
|
|
97
|
+
test('no required parameter is also a path segment of its own endpoint', () => {
|
|
98
|
+
for (const key of Object.keys(PROXMOX_CONSTRAINTS)) {
|
|
99
|
+
const segments = new Set([...key.matchAll(/\{([^}]+)\}/g)].map((m) => m[1]));
|
|
100
|
+
for (const name of requiredOf(key)) expect(segments.has(name), `${key} ${name}`).toBe(false);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* ★ THE FAMILIES PR 102 WIRED, WITH THE PROOF IT DID NOT WRITE. Five of them build their create
|
|
107
|
+
* form from a builder that is already exported, so covering them costs nothing but this file.
|
|
108
|
+
* The rest build theirs inline in a spec literal and are covered by the required-set census
|
|
109
|
+
* above instead — recorded honestly rather than claimed.
|
|
110
|
+
*/
|
|
111
|
+
describe('the families wired before this sweep pass their own create tables', () => {
|
|
112
|
+
test('Proxmox.Pool', () => {
|
|
113
|
+
expect(
|
|
114
|
+
onCreate(
|
|
115
|
+
'pve:POST /pools',
|
|
116
|
+
poolCreateForm({ comment: 'house', poolid: 'house', target: TARGET }),
|
|
117
|
+
),
|
|
118
|
+
).toEqual([]);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test('Proxmox.FirewallAlias', () => {
|
|
122
|
+
const form = {
|
|
123
|
+
...aliasBody({ cidr: '192.0.2.0/24', name: 'lab', target: TARGET }),
|
|
124
|
+
name: 'lab',
|
|
125
|
+
};
|
|
126
|
+
expect(onCreate('pve:POST /cluster/firewall/aliases', form)).toEqual([]);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test('Proxmox.HaRule', () => {
|
|
130
|
+
const form = {
|
|
131
|
+
...haRuleBody({
|
|
132
|
+
affinity: 'positive',
|
|
133
|
+
nodes: ['n2', 'n3'],
|
|
134
|
+
resources: ['ct:101', 'ct:102'],
|
|
135
|
+
rule: 'keep-together',
|
|
136
|
+
strict: true,
|
|
137
|
+
target: TARGET,
|
|
138
|
+
type: 'node-affinity',
|
|
139
|
+
}),
|
|
140
|
+
rule: 'keep-together',
|
|
141
|
+
};
|
|
142
|
+
expect(onCreate('pve:POST /cluster/ha/rules', form)).toEqual([]);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test('Proxmox.ReplicationJob', () => {
|
|
146
|
+
expect(
|
|
147
|
+
onCreate(
|
|
148
|
+
'pve:POST /cluster/replication',
|
|
149
|
+
replicationCreateBody({
|
|
150
|
+
guest: 150,
|
|
151
|
+
jobnum: 0,
|
|
152
|
+
schedule: '*/15',
|
|
153
|
+
target: TARGET,
|
|
154
|
+
targetNode: 'n3',
|
|
155
|
+
}),
|
|
156
|
+
),
|
|
157
|
+
).toEqual([]);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
/** ⚠️ One builder, two products: both matcher families share `matcherCreateForm`. */
|
|
161
|
+
test('Proxmox.NotificationMatcher and Pbs.NotificationMatcher', () => {
|
|
162
|
+
const form = matcherCreateForm({
|
|
163
|
+
comment: 'route everything to mail-to-root',
|
|
164
|
+
'match-severity': ['warning', 'error'],
|
|
165
|
+
mode: 'all',
|
|
166
|
+
name: 'default-matcher',
|
|
167
|
+
targets: ['mail-to-root'],
|
|
168
|
+
});
|
|
169
|
+
expect(onCreate('pve:POST /cluster/notifications/matchers', form)).toEqual([]);
|
|
170
|
+
expect(onCreate('pbs:POST /config/notifications/matchers', form)).toEqual([]);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
@@ -14,13 +14,11 @@
|
|
|
14
14
|
import { describe, expect, test } from 'bun:test';
|
|
15
15
|
import * as Layer from 'effect/Layer';
|
|
16
16
|
import { engineOver } from '../verify/fake-engine.ts';
|
|
17
|
+
// ⚠️ The estate's own declarations live in constraints-live.test.ts — split at the 250-line cap.
|
|
17
18
|
import { constraintsFor, formViolations } from './constraint-guard.ts';
|
|
18
19
|
import { type EndpointConstraints, refusal, violations } from './constraints.ts';
|
|
19
20
|
import type { PbsTarget } from './credentials.ts';
|
|
20
21
|
import { fakePve, withoutBao } from './fake-pve.ts';
|
|
21
|
-
import { createForm as datastoreCreateForm } from './pbs-datastore-form.ts';
|
|
22
|
-
import { createBody as pruneCreateBody } from './pbs-prune-job-form.ts';
|
|
23
|
-
import { createBody as syncCreateBody } from './pbs-sync-job-form.ts';
|
|
24
22
|
import { PROXMOX_CONSTRAINTS, PROXMOX_CONSTRAINTS_DIGEST } from './generated/constraints/index.ts';
|
|
25
23
|
import { PbsVerifyJob, PbsVerifyJobProvider } from './pbs-verify-job.ts';
|
|
26
24
|
|
|
@@ -71,11 +69,14 @@ describe('the v-r2-offsite failure, refused at plan instead of by the server', (
|
|
|
71
69
|
// ⛔ THE ASSERTION THE INCIDENT IS ABOUT. A refusal after the POST is the server's own 400.
|
|
72
70
|
expect(writes).toEqual([]);
|
|
73
71
|
/**
|
|
74
|
-
* ⚠️
|
|
75
|
-
* `read` handler
|
|
76
|
-
*
|
|
72
|
+
* ⚠️ EVERY CALL IS A READ, AND THERE ARE NOW TWO OF THEM. Measured 2026-09-22: Alchemy runs
|
|
73
|
+
* the provider's `read` handler first, and `reconcile` reads again — the guard moved to
|
|
74
|
+
* AFTER that second read the same day, because the read is the only thing that says whether
|
|
75
|
+
* a create or an update is about to be made, and demanding the create form's required
|
|
76
|
+
* parameters on an update refuses edits that were always legal (resource-guard.ts).
|
|
77
|
+
* ⛔ THE COUNT IS NOT THE ASSERTION; THE METHOD IS. Nothing is created or changed.
|
|
77
78
|
*/
|
|
78
|
-
expect(calls.map((call) => call.method)).toEqual(['GET']);
|
|
79
|
+
expect(calls.map((call) => call.method)).toEqual(['GET', 'GET']);
|
|
79
80
|
});
|
|
80
81
|
|
|
81
82
|
test('128 characters is accepted and the create goes through', async () => {
|
|
@@ -202,59 +203,3 @@ describe('every rule kind, at and past its boundary', () => {
|
|
|
202
203
|
expect(refusal(VERIFY, ['comment: at most 128 characters'])).toContain('generated/constraints');
|
|
203
204
|
});
|
|
204
205
|
});
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* ★ THE OTHER THREE PBS FAMILIES, WITH THE ESTATE'S OWN DECLARATIONS, AGAINST THEIR CREATE TABLE.
|
|
208
|
-
* The risk this feature carries is not the one it fixes: a table that refuses a declaration the
|
|
209
|
-
* vendor would have ACCEPTED blocks a deploy that was always legal, and the operator cannot tell
|
|
210
|
-
* that from a genuine violation. These are the jobs `homeflare-proxmox` actually declares
|
|
211
|
-
* (`prune-cluster-all`, `sync-all-to-r2`, the `r2-offsite` datastore), so a false positive in the
|
|
212
|
-
* presence check or a mistranslated pattern fails here rather than on a Sunday morning.
|
|
213
|
-
*/
|
|
214
|
-
describe('the live PBS declarations pass their own create tables', () => {
|
|
215
|
-
test('prune, sync and datastore create forms have no violations', () => {
|
|
216
|
-
expect(
|
|
217
|
-
formViolations(
|
|
218
|
-
'pbs:POST /config/prune',
|
|
219
|
-
pruneCreateBody({
|
|
220
|
-
comment: 'cluster retention',
|
|
221
|
-
id: 'prune-cluster-all',
|
|
222
|
-
'keep-daily': 7,
|
|
223
|
-
'keep-last': 3,
|
|
224
|
-
'keep-monthly': 6,
|
|
225
|
-
'keep-weekly': 4,
|
|
226
|
-
schedule: 'sat 03:00',
|
|
227
|
-
store: 'cluster',
|
|
228
|
-
target: PBS,
|
|
229
|
-
}),
|
|
230
|
-
true,
|
|
231
|
-
),
|
|
232
|
-
).toEqual([]);
|
|
233
|
-
expect(
|
|
234
|
-
formViolations(
|
|
235
|
-
'pbs:POST /config/sync',
|
|
236
|
-
syncCreateBody({
|
|
237
|
-
comment: 'offsite copy to R2',
|
|
238
|
-
id: 'sync-all-to-r2',
|
|
239
|
-
'remote-store': 'r2-offsite',
|
|
240
|
-
schedule: 'mon 08:30',
|
|
241
|
-
store: 'cluster',
|
|
242
|
-
target: PBS,
|
|
243
|
-
}),
|
|
244
|
-
true,
|
|
245
|
-
),
|
|
246
|
-
).toEqual([]);
|
|
247
|
-
expect(
|
|
248
|
-
formViolations(
|
|
249
|
-
'pbs:POST /config/datastore',
|
|
250
|
-
datastoreCreateForm({
|
|
251
|
-
comment: 'offsite',
|
|
252
|
-
name: 'r2-offsite',
|
|
253
|
-
path: '/mnt/datastore/r2-offsite',
|
|
254
|
-
target: PBS,
|
|
255
|
-
}),
|
|
256
|
-
true,
|
|
257
|
-
),
|
|
258
|
-
).toEqual([]);
|
|
259
|
-
});
|
|
260
|
-
});
|