Files
egress-proxies-operator/internal/provider/proxmox/id.go
T
kacerrandClaude Opus 5.5 6e996ae2c7 proxmox: config block, IDs, marks, state mapping, classify, registration
Phase 2 of the proxmox provider plan. Adds the proxmox: config block with
its validation, registers the type in the config allow-list and the
constructor map, and starts internal/provider/proxmox: the api seam and
its data types, providerIDs (vms/<vmid>/<name>) with the UID-derived VMID,
the description mark, state mapping, and error classification.

Tests read the responses captured from pv1-1 in Phase 0 rather than
repeating Proxmox's wording, and every matcher is run against every
captured error so none fires on the wrong message. The contract methods
are Transient stubs until Phases 5 to 7.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-23 09:42:46 +00:00

43 lines
1.5 KiB
Go

package proxmox
import (
"hash/fnv"
"strconv"
"strings"
"gitea.home.hrajfrisbee.cz/kacerr/egress-proxies-operator/internal/provider"
)
const idPrefix = "vms"
// formatProviderID gives vms/<vmid>/<name>: location-free, because VMs can
// migrate between nodes (spec entry 1).
func formatProviderID(vmid int, name string) string {
return idPrefix + "/" + strconv.Itoa(vmid) + "/" + name
}
// parseProviderID is strict: exactly three segments, the literal "vms", a
// decimal VMID without leading zeros in Proxmox's range, and a non-empty
// name. Anything else is ErrPermanent.
func parseProviderID(id string) (int, string, error) {
parts := strings.Split(id, "/")
if len(parts) != 3 || parts[0] != idPrefix || parts[2] == "" {
return 0, "", permanentf("malformed providerID %q, want vms/<vmid>/<name>", id)
}
vmid, err := strconv.Atoi(parts[1])
if err != nil || strconv.Itoa(vmid) != parts[1] || vmid < provider.ProxmoxMinVMID || vmid > provider.ProxmoxMaxVMID {
return 0, "", permanentf("malformed providerID %q: %q is not a VMID", id, parts[1])
}
return vmid, parts[2], nil
}
// vmidFor is the attempt-th VMID candidate for a Proxy UID: a stable hash of
// the UID placed in [min, max], moving forward and wrapping as attempt grows,
// so a retry of the same Create lands on the same VMID (spec entry 2).
func vmidFor(uid string, min, max, attempt int) int {
h := fnv.New64a()
_, _ = h.Write([]byte(uid))
span := uint64(max - min + 1)
return min + int((h.Sum64()%span+uint64(attempt)%span)%span)
}