Run 3 on the real host got past Create and failed in Get: libvirt 11.6 selects the <instance> element by our URI in DomainGetMetadata but hands it back with no namespace declaration, and the parser required the URI on the element. The lookup already guarantees the namespace, so match on the local name only. The fake now strips the declaration the way libvirt does, and a new test pins the bare form. Execution log updated. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
64 lines
2.2 KiB
Go
64 lines
2.2 KiB
Go
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
|
|
// <metadata>. 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 selects the element by namespace URI but serialises it without the
|
|
// declaration (observed on libvirt 11.6: a bare <instance>), and other
|
|
// versions may keep a default or prefixed form. The URI is already checked
|
|
// by the lookup, so only the local name is matched here.
|
|
func parseInstanceMetadata(s string) (instanceMetadata, error) {
|
|
var raw struct {
|
|
XMLName xml.Name `xml:"instance"`
|
|
Managed string `xml:"managed"`
|
|
UID string `xml:"uid"`
|
|
CreatedAt string `xml:"createdAt"`
|
|
}
|
|
if err := xml.Unmarshal([]byte(s), &raw); err != nil {
|
|
return instanceMetadata{}, fmt.Errorf("parsing domain metadata: %w", err)
|
|
}
|
|
return instanceMetadata{Managed: raw.Managed, UID: raw.UID, CreatedAt: raw.CreatedAt}, 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
|
|
}
|