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.V(1).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.V(1).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 }