99 lines
3.2 KiB
Go
99 lines
3.2 KiB
Go
package kvm2
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/digitalocean/go-libvirt"
|
|
"github.com/go-logr/logr"
|
|
|
|
"gitea.home.hrajfrisbee.cz/kacerr/egress-proxies-operator/internal/provider"
|
|
)
|
|
|
|
// undefineFlags removes everything libvirt keeps for a domain beside its
|
|
// definition; UEFI domains keep NVRAM and undefine refuses without the flag.
|
|
const undefineFlags = libvirt.DomainUndefineManagedSave | libvirt.DomainUndefineSnapshotsMetadata | libvirt.DomainUndefineNvram
|
|
|
|
// Delete is spec entry 11: under the per-name lock, destroy and undefine
|
|
// the domain, then remove its overlay and seed. Everything already gone is
|
|
// success, and nothing is left when nil is returned, so Get afterwards is
|
|
// NotFound. The base image stays: it is a shared cache.
|
|
func (p *Provider) Delete(ctx context.Context, providerID string) error {
|
|
hostName, name, err := parseProviderID(providerID)
|
|
if err != nil {
|
|
return provider.Wrap(provider.ErrPermanent, "delete", p.name, providerID, err)
|
|
}
|
|
log := p.logger(ctx).WithValues("host", hostName, "name", name)
|
|
host, err := p.host(hostName)
|
|
if err != nil {
|
|
log.Info("host is not configured; delete has nothing to clean up", "id", providerID)
|
|
return nil
|
|
}
|
|
unlock, err := p.lockName(ctx, name)
|
|
if err != nil {
|
|
return p.wrapErr("delete", providerID, err)
|
|
}
|
|
defer unlock()
|
|
|
|
be, err := p.hosts.get(ctx, host)
|
|
if err != nil {
|
|
logAPIError(log, "delete", err)
|
|
return p.wrapErr("delete", providerID, err)
|
|
}
|
|
if err := removeDomain(ctx, log, be, name); err != nil {
|
|
logAPIError(log, "delete", err)
|
|
return p.wrapErr("delete", providerID, err)
|
|
}
|
|
if err := removeVolumes(ctx, log, be, host.Pool, name); err != nil {
|
|
logAPIError(log, "delete", err)
|
|
return p.wrapErr("delete", providerID, err)
|
|
}
|
|
log.V(1).Info("kvm2 domain deleted")
|
|
return nil
|
|
}
|
|
|
|
func removeDomain(ctx context.Context, log logr.Logger, be backend, name string) error {
|
|
dom, err := be.DomainLookupByName(ctx, name)
|
|
if hasCode(err, libvirt.ErrNoDomain) {
|
|
log.V(1).Info("domain already gone")
|
|
return nil
|
|
}
|
|
if err != nil {
|
|
return fmt.Errorf("looking up domain %s: %w", name, err)
|
|
}
|
|
switch err := be.DomainDestroy(ctx, dom); {
|
|
case err == nil:
|
|
log.V(1).Info("domain destroyed")
|
|
case hasCode(err, libvirt.ErrOperationInvalid):
|
|
log.V(1).Info("domain not running, destroy skipped")
|
|
case hasCode(err, libvirt.ErrNoDomain):
|
|
log.V(1).Info("domain already gone")
|
|
return nil
|
|
default:
|
|
return fmt.Errorf("destroying domain %s: %w", name, err)
|
|
}
|
|
if err := be.DomainUndefineFlags(ctx, dom, undefineFlags); err != nil && !hasCode(err, libvirt.ErrNoDomain) {
|
|
return fmt.Errorf("undefining domain %s: %w", name, err)
|
|
}
|
|
log.V(1).Info("domain undefined")
|
|
return nil
|
|
}
|
|
|
|
func removeVolumes(ctx context.Context, log logr.Logger, be backend, poolName, name string) error {
|
|
pool, err := be.StoragePoolLookupByName(ctx, poolName)
|
|
if hasCode(err, libvirt.ErrNoStoragePool) {
|
|
log.Info("pool not found, no volumes to delete", "pool", poolName)
|
|
return nil
|
|
}
|
|
if err != nil {
|
|
return fmt.Errorf("looking up storage pool %s: %w", poolName, err)
|
|
}
|
|
for _, vol := range []string{overlayVolumeName(name), seedVolumeName(name)} {
|
|
if err := deleteVolumeIfPresent(ctx, be, pool, vol); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
log.V(1).Info("per-VM volumes deleted")
|
|
return nil
|
|
}
|