79 lines
2.6 KiB
Go
79 lines
2.6 KiB
Go
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 <metadata> 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 <metadata>.
|
|
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 <instance> 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
|
|
}
|