Repository: ceph/go-ceph
Pull request #509 (2021-05-27 14:20:38)

rbd: add DeepCopy()
With rbd_deep_copy() an image can be copied with selected options. This
can be used to create a copy and flatten the image in one go.

Updates: ceph/ceph-csi#2077

## Checklist
- [x] Added tests for features and functional changes
- [x] Public functions and types are documented
- [x] Standard formatting is applied to Go code


phlogistonjohn: The CI issue ought to resolve when ceph-container pushes a new container image matching yesterday's [ceph release](https://ceph.io/releases/v15-2-13-octopus-released/). We can restart the CI run then and it should stop hitting the rpm error.
nixpanic: @mergifyio rebase
mergify[bot]: **Command `rebase`: success**
> **Branch has been successfully rebased**
> 

phlogistonjohn: @mergifyio rebase
mergify[bot]: **Command `rebase`: success**
> **Branch has been successfully rebased**
> 


Patch:
diff --git a/rbd/rbd.go b/rbd/rbd.go
index f827bd2..b0091e9 100644
--- a/rbd/rbd.go
+++ b/rbd/rbd.go
@@ -531,6 +531,33 @@ func (image *Image) Copy2(dest *Image) error {
 	return getError(C.rbd_copy2(image.image, dest.image))
 }
 
+// DeepCopy an rbd image to a new image with specific options.
+//
+// Implements:
+//  int rbd_deep_copy(rbd_image_t src, rados_ioctx_t dest_io_ctx,
+//          const char *destname, rbd_image_options_t dest_opts);
+func (image *Image) DeepCopy(ioctx *rados.IOContext, destname string, rio *ImageOptions) error {
+	if err := image.validate(imageIsOpen); err != nil {
+		return err
+	}
+	if ioctx == nil {
+		return ErrNoIOContext
+	}
+	if destname == "" {
+		return ErrNoName
+	}
+	if rio == nil {
+		return rbdError(C.EINVAL)
+	}
+
+	cDestname := C.CString(destname)
+	defer C.free(unsafe.Pointer(cDestname))
+
+	ret := C.rbd_deep_copy(image.image, cephIoctx(ioctx), cDestname,
+		C.rbd_image_options_t(rio.options))
+	return getError(ret)
+}
+
 // Flatten removes snapshot references from the image.
 //
 // Implements:


Test patch:
diff --git a/rbd/rbd_test.go b/rbd/rbd_test.go
index 80fa6c0..62dee55 100644
--- a/rbd/rbd_test.go
+++ b/rbd/rbd_test.go
@@ -958,6 +958,87 @@ func TestImageCopy(t *testing.T) {
 	conn.Shutdown()
 }
 
+func TestImageDeepCopy(t *testing.T) {
+	conn := radosConnect(t)
+
+	poolname := GetUUID()
+	err := conn.MakePool(poolname)
+	assert.NoError(t, err)
+
+	ioctx, err := conn.OpenIOContext(poolname)
+	require.NoError(t, err)
+
+	t.Run("invalidParameters", func(t *testing.T) {
+		name := GetUUID()
+		options := NewRbdImageOptions()
+		defer options.Destroy()
+		err = options.SetUint64(ImageOptionOrder, uint64(testImageOrder))
+		assert.NoError(t, err)
+		err = CreateImage(ioctx, name, testImageSize, options)
+		require.NoError(t, err)
+
+		// img not open, should fail
+		img := GetImage(ioctx, name)
+		err = img.DeepCopy(nil, "", nil)
+		assert.Equal(t, err, ErrImageNotOpen)
+
+		img, err := OpenImage(ioctx, name, NoSnapshot)
+		require.NoError(t, err)
+
+		// pass invalid parameters
+		err = img.DeepCopy(nil, "", nil)
+		assert.Error(t, err) // order of errors not enforced
+
+		err = img.DeepCopy(ioctx, "", options)
+		assert.Equal(t, err, ErrNoName)
+
+		err = img.DeepCopy(nil, "duplicate", options)
+		assert.Equal(t, err, ErrNoIOContext)
+
+		err = img.DeepCopy(ioctx, "copied", nil)
+		assert.Error(t, err) // rbdError(C.EINVAL), but can not use C in tests
+
+		err = img.Close()
+		assert.NoError(t, err)
+		err = RemoveImage(ioctx, name)
+		assert.NoError(t, err)
+	})
+
+	// try successful copying
+	t.Run("successfulDeepCopy", func(t *testing.T) {
+		name := GetUUID()
+		options := NewRbdImageOptions()
+		defer options.Destroy()
+		err = options.SetUint64(ImageOptionOrder, uint64(testImageOrder))
+		assert.NoError(t, err)
+		err = CreateImage(ioctx, name, testImageSize, options)
+		require.NoError(t, err)
+
+		img, err := OpenImage(ioctx, name, NoSnapshot)
+		require.NoError(t, err)
+
+		name2 := GetUUID()
+		err = img.DeepCopy(ioctx, name2, options)
+		require.NoError(t, err)
+
+		img2, err := OpenImage(ioctx, name2, NoSnapshot)
+		require.NoError(t, err)
+
+		err = img2.Close()
+		assert.NoError(t, err)
+
+		err = img2.Remove()
+		assert.NoError(t, err)
+
+		err = img.Close()
+		assert.NoError(t, err)
+	})
+
+	ioctx.Destroy()
+	conn.DeletePool(poolname)
+	conn.Shutdown()
+}
+
 func TestParentInfo(t *testing.T) {
 	conn := radosConnect(t)