From 1257d817db6e1e211558a78d02535545c7783ebe Mon Sep 17 00:00:00 2001 From: Niels de Vos Date: Mon, 31 Jul 2023 15:14:21 +0200 Subject: [PATCH] rbd: include details for parent image in the trash When a parent image has been removed, it will linger in the trash until all siblings are gone. The image is not accessible through it's name anymore, only through its ID. The ImageSpec that is returned by Image.GetParent() now contains the Trash boolean and the ImageID to identify if the image is in the trash, and use OpenImageById() to access the removed parent image. Related-to: ceph/ceph-csi#4013 Signed-off-by: Niels de Vos --- rbd/rbd_nautilus_test.go | 50 ++++++++++++++++++++++++++++++++++++++++ rbd/snapshot_nautilus.go | 16 +++++++++---- 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/rbd/rbd_nautilus_test.go b/rbd/rbd_nautilus_test.go index da6d9ee..2ae4306 100644 --- a/rbd/rbd_nautilus_test.go +++ b/rbd/rbd_nautilus_test.go @@ -2,6 +2,7 @@ package rbd import ( "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -185,6 +186,55 @@ func TestGetParent(t *testing.T) { assert.Equal(t, parentInfo.Image.ImageName, imgName) assert.Equal(t, parentInfo.Snap.SnapName, snapName) assert.Equal(t, parentInfo.Image.PoolName, poolName) + assert.False(t, parentInfo.Image.Trash) + // TODO: add a comaprison for snap ID + }) + + t.Run("ParentInTrash", func(t *testing.T) { + trashName := "trashed" + _, err := Create(ioctx, trashName, testImageSize, testImageOrder, 1) + assert.NoError(t, err) + + imgTrash, err := OpenImage(ioctx, trashName, NoSnapshot) + assert.NoError(t, err) + defer func() { assert.NoError(t, imgTrash.Close()) }() + imgTrashID, err := imgTrash.GetId() + assert.NoError(t, err) + + snapNameTrash := "snapTrash" + snapTrash, err := imgTrash.CreateSnapshot(snapNameTrash) + assert.NoError(t, err) + defer func() { + assert.NoError(t, snapTrash.Remove()) + }() + + cloneName := "childWithTrash" + optionsClone := NewRbdImageOptions() + defer optionsClone.Destroy() + err = optionsClone.SetUint64(ImageOptionCloneFormat, 2) + assert.NoError(t, err) + + // Create a clone of the image. + err = CloneImage(ioctx, trashName, snapNameTrash, ioctx, cloneName, optionsClone) + assert.NoError(t, err) + defer func() { assert.NoError(t, RemoveImage(ioctx, cloneName)) }() + + // Move the parent image to the trash, won't be deleted until the clone is removed. + assert.NoError(t, imgTrash.Trash(15*time.Second)) + + imgNew, err := OpenImage(ioctx, cloneName, NoSnapshot) + assert.NoError(t, err) + defer func() { + assert.NoError(t, imgNew.Close()) + }() + + parentInfo, err := imgNew.GetParent() + assert.NoError(t, err) + assert.Equal(t, parentInfo.Image.ImageName, trashName) + assert.Equal(t, parentInfo.Image.ImageID, imgTrashID) + assert.Equal(t, parentInfo.Image.PoolName, poolName) + assert.True(t, parentInfo.Image.Trash) + assert.Equal(t, parentInfo.Snap.SnapName, snapNameTrash) // TODO: add a comaprison for snap ID }) diff --git a/rbd/snapshot_nautilus.go b/rbd/snapshot_nautilus.go index dc28db2..f81e8d9 100644 --- a/rbd/snapshot_nautilus.go +++ b/rbd/snapshot_nautilus.go @@ -67,8 +67,12 @@ func (image *Image) GetParentInfo(pool, name, snapname []byte) error { // ImageSpec represents the image information. type ImageSpec struct { - ImageName string - PoolName string + ImageName string + ImageID string + PoolName string + PoolNamespace string + PoolID uint64 + Trash bool } // SnapSpec represents the snapshot infomation. @@ -104,8 +108,12 @@ func (image *Image) GetParent() (*ParentInfo, error) { defer C.rbd_snap_spec_cleanup(&parentSnap) imageSpec := ImageSpec{ - ImageName: C.GoString(parentImage.image_name), - PoolName: C.GoString(parentImage.pool_name), + ImageName: C.GoString(parentImage.image_name), + ImageID: C.GoString(parentImage.image_id), + PoolName: C.GoString(parentImage.pool_name), + PoolNamespace: C.GoString(parentImage.pool_namespace), + PoolID: uint64(parentImage.pool_id), + Trash: bool(parentImage.trash), } snapSpec := SnapSpec{