package kvm2 import ( "encoding/xml" "strings" "time" "gitea.home.hrajfrisbee.cz/kacerr/egress-proxies-operator/internal/provider" ) // The GC mark lives in the domain's as one element in kvm2's own // namespace (spec entry 8). The namespace is what selects it in // DomainGetMetadata; libvirt 11.6 hands the element back without the xmlns // declaration, so parsing matches on the local name only. const ( metadataURI = "https://gitea.home.hrajfrisbee.cz/kacerr/egress-proxies-operator/kvm2" metadataLocalName = "instance" ) // instanceMetadata is the mark as it is written and read back. The fields // are the element's text as is; managed and createdAt interpret them. type instanceMetadata struct { XMLName xml.Name Managed string `xml:"managed"` UID string `xml:"uid"` CreatedAt string `xml:"createdAt"` } func newInstanceMetadata(uid string, createdAt time.Time) instanceMetadata { return instanceMetadata{ XMLName: xml.Name{Space: metadataURI, Local: metadataLocalName}, Managed: provider.LabelManagedYes, UID: uid, CreatedAt: createdAt.UTC().Format(time.RFC3339), } } func (m instanceMetadata) managed() bool { return m.Managed == provider.LabelManagedYes } // createdAt returns the zero time when the element carries no parsable // timestamp; GC treats that as old, which the spec accepts. func (m instanceMetadata) createdAt() time.Time { t, err := time.Parse(time.RFC3339, m.CreatedAt) if err != nil { return time.Time{} } return t } // marshalInstanceMetadata renders the namespaced element that goes under // the domain's . func marshalInstanceMetadata(m instanceMetadata) (string, error) { m.XMLName = xml.Name{Space: metadataURI, Local: metadataLocalName} out, err := xml.Marshal(m) if err != nil { return "", permanentf("marshalling instance metadata: %v", err) } return string(out), nil } // parseInstanceMetadata reads the element back, with or without its xmlns // declaration. Anything that is not an element is ErrPermanent: // the domain's metadata is corrupt and no retry fixes it. func parseInstanceMetadata(s string) (instanceMetadata, error) { var m instanceMetadata if strings.TrimSpace(s) == "" { return instanceMetadata{}, permanentf("parsing instance metadata: empty document") } if err := xml.Unmarshal([]byte(s), &m); err != nil { return instanceMetadata{}, permanentf("parsing instance metadata: %v", err) } if m.XMLName.Local != metadataLocalName { return instanceMetadata{}, permanentf("parsing instance metadata: root element is <%s>, want <%s>", m.XMLName.Local, metadataLocalName) } return m, nil }