rbd: fix broken Image.Copy() and add an exhaustive test for it

The arguments passed to Copy() would normally be pointers, just like it
is done with other similar functions. This never worked correctly, as
the arguments were not validated correctly (not recognized) and the
error "Must specify either destination pool or destination image" always
got returned.

Modifying Image.Copy() into something usable would result in a complete
rewrite. Instead of a single Image.Copy() function that tries to cover
rbd_copy() and rbd_copy2(), provide Image.Copy() and Image.Copy2() so
that the API matches the librbd.so interface.

Signed-off-by: Niels de Vos <ndevos@redhat.com>
This commit is contained in:
Niels de Vos 2019-12-11 11:05:32 +01:00 committed by John Mulligan
parent 4589af9dfd
commit 2b2fcc121d
2 changed files with 109 additions and 27 deletions

View File

@ -597,37 +597,31 @@ func (image *Image) GetOverlap() (overlap uint64, err error) {
}
// int rbd_copy(rbd_image_t image, rados_ioctx_t dest_io_ctx, const char *destname);
// int rbd_copy2(rbd_image_t src, rbd_image_t dest);
// int rbd_copy_with_progress(rbd_image_t image, rados_ioctx_t dest_p, const char *destname,
// librbd_progress_fn_t cb, void *cbdata);
// int rbd_copy_with_progress2(rbd_image_t src, rbd_image_t dest,
// librbd_progress_fn_t cb, void *cbdata);
func (image *Image) Copy(args ...interface{}) error {
func (image *Image) Copy(ioctx *rados.IOContext, destname string) error {
if err := image.validate(imageIsOpen); err != nil {
return err
} else if ioctx == nil {
return ErrNoIOContext
} else if len(destname) == 0 {
return ErrNoName
}
c_destname := C.CString(destname)
defer C.free(unsafe.Pointer(c_destname))
return GetError(C.rbd_copy(image.image,
C.rados_ioctx_t(ioctx.Pointer()), c_destname))
}
// int rbd_copy2(rbd_image_t src, rbd_image_t dest);
func (image *Image) Copy2(dest *Image) error {
if err := image.validate(imageIsOpen); err != nil {
return err
} else if err := dest.validate(imageIsOpen); err != nil {
return err
}
switch t := args[0].(type) {
case rados.IOContext:
switch t2 := args[1].(type) {
case string:
c_destname := C.CString(t2)
defer C.free(unsafe.Pointer(c_destname))
return GetError(C.rbd_copy(image.image,
C.rados_ioctx_t(t.Pointer()),
c_destname))
default:
return errors.New("Must specify destname")
}
case Image:
var dest Image = t
if dest.image == nil {
return errors.New(fmt.Sprintf("RBD image %s is not open", dest.name))
}
return GetError(C.rbd_copy2(image.image, dest.image))
default:
return errors.New("Must specify either destination pool or destination image")
}
return GetError(C.rbd_copy2(image.image, dest.image))
}
// int rbd_flatten(rbd_image_t image);

View File

@ -576,6 +576,94 @@ func TestIOReaderWriter(t *testing.T) {
conn.Shutdown()
}
func TestImageCopy(t *testing.T) {
conn, _ := rados.NewConn()
conn.ReadDefaultConfigFile()
conn.Connect()
poolname := GetUUID()
err := conn.MakePool(poolname)
assert.NoError(t, err)
ioctx, err := conn.OpenIOContext(poolname)
require.NoError(t, err)
name := GetUUID()
img, err := Create(ioctx, name, 1<<22, 22)
require.NoError(t, err)
// img not open, should fail
err = img.Copy(nil, "")
assert.Equal(t, err, ErrImageNotOpen)
err = img.Open()
require.NoError(t, err)
// pass invalid parameters
err = img.Copy(nil, "")
assert.Error(t, err) // order of errors not enforced
err = img.Copy(ioctx, "")
assert.Equal(t, err, ErrNoName)
err = img.Copy(nil, "duplicate")
assert.Equal(t, err, ErrNoIOContext)
// try successful copying
name = GetUUID()
err = img.Copy(ioctx, name)
require.NoError(t, err)
img2 := GetImage(ioctx, name)
err = img2.Open()
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)
// test with Image as parameter
name = GetUUID()
img2, err = Create(ioctx, name, 1<<22, 22)
require.NoError(t, err)
err = img.Copy2(img2)
assert.Equal(t, err, ErrImageNotOpen)
err = img.Open()
assert.NoError(t, err)
err = img.Copy2(img2)
assert.Equal(t, err, ErrImageNotOpen)
err = img2.Open()
require.NoError(t, err)
err = img.Copy2(img2)
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)
err = img.Remove()
assert.NoError(t, err)
ioctx.Destroy()
conn.DeletePool(poolname)
conn.Shutdown()
}
func TestCreateSnapshot(t *testing.T) {
conn, _ := rados.NewConn()
conn.ReadDefaultConfigFile()