Repository: ceph/go-ceph
Pull request #352 (2020-08-26 07:27:54)

rbd: add a new version of GetParentInfo()
GetParentInfo() currently requires the caller to specify the
string length for various attributes it returns. If the
specified string length is less than required, it can return
ERANGE error. Replacing it with a new function which avoids
this.

Signed-off-by: Mudit Agarwal <muagarwa@redhat.com>

Fixes: #347 

## Checklist
- [ ] Added tests for features and functional changes
- [ ] Public functions and types are documented
- [ ] Standard formatting is applied to Go code


agarwal-mudit: @nixpanic Made the changes, PTAL

Patch:
diff --git a/rbd/snapshot_nautilus.go b/rbd/snapshot_nautilus.go
index f0eed0c..3245457 100644
--- a/rbd/snapshot_nautilus.go
+++ b/rbd/snapshot_nautilus.go
@@ -65,6 +65,60 @@ func (image *Image) GetParentInfo(pool, name, snapname []byte) error {
 	return nil
 }
 
+// ImageSpec represents the image information.
+type ImageSpec struct {
+	ImageName string
+	PoolName  string
+}
+
+// SnapSpec represents the snapshot infomation.
+type SnapSpec struct {
+	ID       uint64
+	SnapName string
+}
+
+// ParentInfo represents the parent image and the parent snapshot information.
+type ParentInfo struct {
+	Image ImageSpec
+	Snap  SnapSpec
+}
+
+// GetParent looks for the parent of the image and returns the parent image
+// information which includes the image name, the pool name and
+// the snapshot information.
+//
+// Implements:
+// int rbd_get_parent(rbd_image_t image, rbd_linked_image_spec_t *parent_image, rbd_snap_spec_t *parent_snap)
+func (image *Image) GetParent() (*ParentInfo, error) {
+	if err := image.validate(imageIsOpen); err != nil {
+		return nil, err
+	}
+
+	parentImage := C.rbd_linked_image_spec_t{}
+	parentSnap := C.rbd_snap_spec_t{}
+	ret := C.rbd_get_parent(image.image, &parentImage, &parentSnap)
+	if ret != 0 {
+		return nil, getError(ret)
+	}
+	defer C.rbd_linked_image_spec_cleanup(&parentImage)
+	defer C.rbd_snap_spec_cleanup(&parentSnap)
+
+	imageSpec := ImageSpec{
+		ImageName: C.GoString(parentImage.image_name),
+		PoolName:  C.GoString(parentImage.pool_name),
+	}
+
+	snapSpec := SnapSpec{
+		ID:       uint64(parentSnap.id),
+		SnapName: C.GoString(parentSnap.name),
+	}
+
+	return &ParentInfo{
+		Image: imageSpec,
+		Snap:  snapSpec,
+	}, nil
+}
+
 // ListChildren returns arrays with the pools and names of the images that are
 // children of the given image. The index of the pools and images arrays can be
 // used to link the two items together.


Test patch:
diff --git a/rbd/rbd_nautilus_test.go b/rbd/rbd_nautilus_test.go
index 4e1cc12..39f3724 100644
--- a/rbd/rbd_nautilus_test.go
+++ b/rbd/rbd_nautilus_test.go
@@ -123,3 +123,80 @@ func TestSparsify(t *testing.T) {
 		assert.Error(t, err)
 	})
 }
+
+func TestGetParent(t *testing.T) {
+	conn := radosConnect(t)
+	poolName := GetUUID()
+	err := conn.MakePool(poolName)
+	assert.NoError(t, err)
+	ioctx, err := conn.OpenIOContext(poolName)
+	assert.NoError(t, err)
+
+	defer func() {
+		ioctx.Destroy()
+		assert.NoError(t, conn.DeletePool(poolName))
+		conn.Shutdown()
+	}()
+
+	imgName := "parent"
+	img, err := Create(ioctx, imgName, testImageSize, testImageOrder, 1)
+	assert.NoError(t, err)
+	defer func() {
+		assert.NoError(t, img.Remove())
+	}()
+
+	img, err = OpenImage(ioctx, imgName, NoSnapshot)
+	assert.NoError(t, err)
+	defer func() {
+		assert.NoError(t, img.Close())
+	}()
+
+	snapName := "mysnap"
+	snapshot, err := img.CreateSnapshot(snapName)
+	assert.NoError(t, err)
+	defer func() {
+		assert.NoError(t, snapshot.Remove())
+	}()
+
+	t.Run("ParentNotAvailable", func(t *testing.T) {
+		_, err := img.GetParent()
+		assert.Error(t, err)
+		assert.Equal(t, err, ErrNotFound)
+	})
+
+	t.Run("ParentAvailable", func(t *testing.T) {
+		cloneName := "child"
+		optionsClone := NewRbdImageOptions()
+		defer optionsClone.Destroy()
+		err := optionsClone.SetUint64(ImageOptionCloneFormat, 2)
+		assert.NoError(t, err)
+
+		// Create a clone of the image using the snapshot.
+		err = CloneImage(ioctx, imgName, snapName, ioctx, cloneName, optionsClone)
+		assert.NoError(t, err)
+		defer func() { assert.NoError(t, RemoveImage(ioctx, cloneName)) }()
+
+		imgNew, err := OpenImage(ioctx, cloneName, NoSnapshot)
+		defer func() {
+			assert.NoError(t, imgNew.Close())
+		}()
+		assert.NoError(t, err)
+
+		parentInfo, err := imgNew.GetParent()
+		assert.NoError(t, err)
+		assert.Equal(t, parentInfo.Image.ImageName, imgName)
+		assert.Equal(t, parentInfo.Snap.SnapName, snapName)
+		assert.Equal(t, parentInfo.Image.PoolName, poolName)
+		// TODO: add a comaprison for snap ID
+	})
+
+	t.Run("ClosedImage", func(t *testing.T) {
+		closedImg, err := Create(ioctx, "someImage", testImageSize, testImageOrder, 1)
+		assert.NoError(t, err)
+		defer func() {
+			assert.NoError(t, closedImg.Remove())
+		}()
+		_, err = closedImg.GetParent()
+		assert.Error(t, err)
+	})
+}