package kvm import ( "encoding/xml" "fmt" "time" "gitea.home.hrajfrisbee.cz/kacerr/egress-proxies-operator/internal/provider" ) // metadataURI namespaces the operator's element inside the domain's // . libvirt has no labels or tags, but it stores any namespaced XML // there and hands it back through DomainGetMetadata, which is how the GC // contract (managed flag + owning UID) and the creation time survive across // operator restarts. const metadataURI = "https://gitea.home.hrajfrisbee.cz/kacerr/egress-proxies-operator/kvm" type instanceMetadata struct { XMLName xml.Name `xml:"https://gitea.home.hrajfrisbee.cz/kacerr/egress-proxies-operator/kvm instance"` Managed string `xml:"managed"` UID string `xml:"uid"` CreatedAt string `xml:"createdAt"` } func newInstanceMetadata(uid string, createdAt time.Time) instanceMetadata { return instanceMetadata{ Managed: provider.LabelManagedYes, UID: uid, CreatedAt: createdAt.UTC().Format(time.RFC3339), } } // parseInstanceMetadata decodes the element as DomainGetMetadata returns it. // libvirt may re-serialise it with a namespace prefix rather than the default // namespace it was defined with; encoding/xml matches on namespace URI, so // both spellings decode. func parseInstanceMetadata(s string) (instanceMetadata, error) { var m instanceMetadata if err := xml.Unmarshal([]byte(s), &m); err != nil { return instanceMetadata{}, fmt.Errorf("parsing domain metadata: %w", err) } return m, nil } func (m instanceMetadata) managed() bool { return m.Managed == provider.LabelManagedYes } // createdAt returns the zero time on a malformed timestamp: orphan GC treats // zero as "old", so a corrupt value can never protect a domain forever. func (m instanceMetadata) createdAt() time.Time { t, err := time.Parse(time.RFC3339, m.CreatedAt) if err != nil { return time.Time{} } return t }