From 07eb8b5f8d7ddfbbde7ee889437976577f291197 Mon Sep 17 00:00:00 2001 From: Wesley Hershberger Date: Fri, 22 Nov 2024 22:49:12 -0600 Subject: [PATCH] lxd: Prevent instance delete if root volume is in use Signed-off-by: Wesley Hershberger --- lxd/instance_delete.go | 43 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/lxd/instance_delete.go b/lxd/instance_delete.go index f09f1c2fccc2..7f4f44b40e44 100644 --- a/lxd/instance_delete.go +++ b/lxd/instance_delete.go @@ -1,18 +1,22 @@ package main import ( + "context" + "errors" "fmt" "net/http" "net/url" "github.com/gorilla/mux" + "github.com/canonical/lxd/lxd/db" "github.com/canonical/lxd/lxd/db/operationtype" "github.com/canonical/lxd/lxd/instance" "github.com/canonical/lxd/lxd/instance/instancetype" "github.com/canonical/lxd/lxd/operations" "github.com/canonical/lxd/lxd/request" "github.com/canonical/lxd/lxd/response" + "github.com/canonical/lxd/lxd/storage" "github.com/canonical/lxd/shared" "github.com/canonical/lxd/shared/api" "github.com/canonical/lxd/shared/version" @@ -84,6 +88,45 @@ func instanceDelete(d *Daemon, r *http.Request) response.Response { return response.BadRequest(fmt.Errorf("Instance is running")) } + // Make sure that the instance's root volume is not attached to another instance + poolName, err := inst.StoragePool() + if err != nil { + return response.SmartError(err) + } + + pool, err := storage.LoadByName(s, poolName) + if err != nil { + return response.SmartError(err) + } + + rootVolumeType, err := storage.InstanceTypeToVolumeType(inst.Type()) + if err != nil { + return response.SmartError(err) + } + + rootVolumeDBType, err := storage.VolumeTypeToDBType(rootVolumeType) + if err != nil { + return nil + } + + var dbVolume *db.StorageVolume + err = s.DB.Cluster.Transaction(r.Context(), func(ctx context.Context, tx *db.ClusterTx) error { + dbVolume, err = tx.GetStoragePoolVolume(ctx, pool.ID(), projectName, rootVolumeDBType, inst.Name(), true) + return err + }) + if err != nil { + return response.SmartError(err) + } + + volumeUsedBy, err := storagePoolVolumeUsedByGet(s, projectName, dbVolume) + if err != nil { + return response.SmartError(err) + } + + if len(volumeUsedBy) > 1 { + return response.BadRequest(errors.New("Instance's root volume is in use")) + } + rmct := func(op *operations.Operation) error { return inst.Delete(false) }