mirror of https://github.com/ceph/go-ceph
424 lines
9.3 KiB
Go
424 lines
9.3 KiB
Go
|
// +build !nautilus,ceph_preview
|
||
|
|
||
|
package admin
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"testing"
|
||
|
"time"
|
||
|
|
||
|
"github.com/ceph/go-ceph/internal/commands"
|
||
|
"github.com/ceph/go-ceph/rbd"
|
||
|
"github.com/stretchr/testify/assert"
|
||
|
"github.com/stretchr/testify/require"
|
||
|
)
|
||
|
|
||
|
var tr1 = `
|
||
|
{
|
||
|
"sequence":1,
|
||
|
"id":"id-1",
|
||
|
"message":"Removing image pool/test from trash",
|
||
|
"refs":{
|
||
|
"action":"trash remove",
|
||
|
"pool_name":"pool",
|
||
|
"pool_namespace":"",
|
||
|
"image_id":"12345"
|
||
|
},
|
||
|
"retry_attempts":1,
|
||
|
"retry_time":"2021-09-13T05:58:24.826408"
|
||
|
}
|
||
|
`
|
||
|
|
||
|
var tr2 = `
|
||
|
{
|
||
|
"sequence":2,
|
||
|
"id":"id-2",
|
||
|
"message":"Removing image pool/test",
|
||
|
"refs":{
|
||
|
"action":"remove",
|
||
|
"pool_name":"pool",
|
||
|
"pool_namespace":"",
|
||
|
"image_name":"test",
|
||
|
"image_id":"123456"
|
||
|
},
|
||
|
"in_progress":true,
|
||
|
"progress":0.70
|
||
|
}
|
||
|
`
|
||
|
|
||
|
var trList = `[
|
||
|
{
|
||
|
"sequence":1,
|
||
|
"id":"id-1",
|
||
|
"message":"Removing image pool/test from trash",
|
||
|
"refs":{
|
||
|
"action":"trash remove",
|
||
|
"pool_name":"pool",
|
||
|
"pool_namespace":"",
|
||
|
"image_id":"12345"
|
||
|
},
|
||
|
"retry_attempts":1,
|
||
|
"retry_time":"2021-09-13T05:58:24.826408"
|
||
|
},
|
||
|
{
|
||
|
"sequence":2,
|
||
|
"id":"id-2",
|
||
|
"message":"Removing image pool/test",
|
||
|
"refs":{
|
||
|
"action":"remove",
|
||
|
"pool_name":"pool",
|
||
|
"pool_namespace":"",
|
||
|
"image_name":"test",
|
||
|
"image_id":"123456"
|
||
|
},
|
||
|
"in_progress":true,
|
||
|
"progress":0.70
|
||
|
}
|
||
|
]`
|
||
|
|
||
|
func TestParseTaskResponse(t *testing.T) {
|
||
|
type args struct {
|
||
|
res commands.Response
|
||
|
}
|
||
|
tests := []struct {
|
||
|
name string
|
||
|
args args
|
||
|
want TaskResponse
|
||
|
wantErr bool
|
||
|
}{
|
||
|
{
|
||
|
name: "",
|
||
|
args: args{
|
||
|
res: commands.NewResponse([]byte(tr1), "", nil),
|
||
|
},
|
||
|
want: TaskResponse{
|
||
|
Sequence: 1,
|
||
|
ID: "id-1",
|
||
|
Message: "Removing image pool/test from trash",
|
||
|
Refs: TaskRefs{
|
||
|
Action: "trash remove",
|
||
|
PoolName: "pool",
|
||
|
PoolNamespace: "",
|
||
|
ImageName: "",
|
||
|
ImageID: "12345",
|
||
|
},
|
||
|
InProgress: false,
|
||
|
Progress: 0,
|
||
|
RetryAttempts: 1,
|
||
|
RetryTime: "2021-09-13T05:58:24.826408",
|
||
|
RetryMessage: "",
|
||
|
},
|
||
|
wantErr: false,
|
||
|
},
|
||
|
{
|
||
|
name: "",
|
||
|
args: args{
|
||
|
res: commands.NewResponse([]byte(tr2), "", nil),
|
||
|
},
|
||
|
want: TaskResponse{
|
||
|
Sequence: 2,
|
||
|
ID: "id-2",
|
||
|
Message: "Removing image pool/test",
|
||
|
Refs: TaskRefs{
|
||
|
Action: "remove",
|
||
|
PoolName: "pool",
|
||
|
PoolNamespace: "",
|
||
|
ImageName: "test",
|
||
|
ImageID: "123456",
|
||
|
},
|
||
|
InProgress: true,
|
||
|
Progress: 0.70,
|
||
|
RetryAttempts: 0,
|
||
|
RetryTime: "",
|
||
|
RetryMessage: "",
|
||
|
},
|
||
|
wantErr: false,
|
||
|
},
|
||
|
}
|
||
|
for _, tt := range tests {
|
||
|
t.Run(tt.name, func(t *testing.T) {
|
||
|
got, err := parseTaskResponse(tt.args.res)
|
||
|
assert.NoError(t, err)
|
||
|
assert.Equal(t, tt.want, got)
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TesParseTaskResponseList(t *testing.T) {
|
||
|
type args struct {
|
||
|
res commands.Response
|
||
|
}
|
||
|
res1, err := parseTaskResponse(commands.NewResponse([]byte(tr1), "", nil))
|
||
|
assert.NoError(t, err)
|
||
|
res2, err := parseTaskResponse(commands.NewResponse([]byte(tr2), "", nil))
|
||
|
assert.NoError(t, err)
|
||
|
|
||
|
tests := []struct {
|
||
|
name string
|
||
|
args args
|
||
|
want []TaskResponse
|
||
|
wantErr bool
|
||
|
}{
|
||
|
{
|
||
|
name: "emptyList",
|
||
|
args: args{
|
||
|
res: commands.NewResponse([]byte(`[]`), "", nil),
|
||
|
},
|
||
|
want: []TaskResponse{},
|
||
|
wantErr: false,
|
||
|
},
|
||
|
{
|
||
|
name: "twoItemList",
|
||
|
args: args{
|
||
|
res: commands.NewResponse([]byte(trList), "", nil),
|
||
|
},
|
||
|
want: []TaskResponse{res1, res2},
|
||
|
wantErr: false,
|
||
|
},
|
||
|
}
|
||
|
for _, tt := range tests {
|
||
|
t.Run(tt.name, func(t *testing.T) {
|
||
|
got, err := parseTaskResponseList(tt.args.res)
|
||
|
assert.NoError(t, err)
|
||
|
assert.Equal(t, tt.want, got)
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
imageName = "img"
|
||
|
testNS = "test"
|
||
|
)
|
||
|
|
||
|
type args struct {
|
||
|
pool string
|
||
|
namespace string
|
||
|
imageName string
|
||
|
}
|
||
|
|
||
|
var tests = []struct {
|
||
|
name string
|
||
|
args args
|
||
|
}{
|
||
|
{
|
||
|
name: "onlyImageName",
|
||
|
args: args{
|
||
|
pool: "",
|
||
|
namespace: "",
|
||
|
imageName: imageName,
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "Image&PoolName",
|
||
|
args: args{
|
||
|
pool: defaultPoolName,
|
||
|
namespace: "",
|
||
|
imageName: imageName,
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "AllArgs",
|
||
|
args: args{
|
||
|
pool: defaultPoolName,
|
||
|
namespace: testNS,
|
||
|
imageName: imageName,
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
func TestTaskAdminAddRemove(t *testing.T) {
|
||
|
ensureDefaultPool(t)
|
||
|
conn := getConn(t)
|
||
|
|
||
|
ioctx, err := conn.OpenIOContext(defaultPoolName)
|
||
|
require.NoError(t, err)
|
||
|
defer ioctx.Destroy()
|
||
|
|
||
|
assert.NoError(t, rbd.NamespaceCreate(ioctx, testNS))
|
||
|
defer func() {
|
||
|
assert.NoError(t, rbd.NamespaceRemove(ioctx, testNS))
|
||
|
}()
|
||
|
|
||
|
ta := getAdmin(t).Task()
|
||
|
options := rbd.NewRbdImageOptions()
|
||
|
assert.NoError(t,
|
||
|
options.SetUint64(rbd.ImageOptionOrder, uint64(testImageOrder)))
|
||
|
|
||
|
for _, tt := range tests {
|
||
|
t.Run(tt.name, func(t *testing.T) {
|
||
|
|
||
|
ioctx.SetNamespace(tt.args.namespace)
|
||
|
err = rbd.CreateImage(ioctx, tt.args.imageName, testImageSize, options)
|
||
|
assert.NoError(t, err)
|
||
|
|
||
|
tr, err := ta.AddRemove(NewImageSpec(tt.args.pool, tt.args.namespace, tt.args.imageName))
|
||
|
assert.NoError(t, err)
|
||
|
|
||
|
assert.Equal(t, tt.args.imageName, tr.Refs.ImageName)
|
||
|
assert.Equal(t, defaultPoolName, tr.Refs.PoolName)
|
||
|
assert.Equal(t, tt.args.namespace, tr.Refs.PoolNamespace)
|
||
|
assert.Equal(t, "remove", tr.Refs.Action)
|
||
|
|
||
|
found := false
|
||
|
// wait for the image to be deleted
|
||
|
for i := 0; i < 35; i++ {
|
||
|
imgList, err := rbd.GetImageNames(ioctx)
|
||
|
assert.NoError(t, err)
|
||
|
|
||
|
found = false
|
||
|
for _, img := range imgList {
|
||
|
if img == imageName {
|
||
|
found = true
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
if !found {
|
||
|
break
|
||
|
}
|
||
|
time.Sleep(time.Second)
|
||
|
}
|
||
|
assert.Equal(t, false, found)
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestTaskAdminAddTrashRemove(t *testing.T) {
|
||
|
ensureDefaultPool(t)
|
||
|
conn := getConn(t)
|
||
|
|
||
|
ioctx, err := conn.OpenIOContext(defaultPoolName)
|
||
|
require.NoError(t, err)
|
||
|
defer ioctx.Destroy()
|
||
|
|
||
|
assert.NoError(t, rbd.NamespaceCreate(ioctx, testNS))
|
||
|
defer func() {
|
||
|
assert.NoError(t, rbd.NamespaceRemove(ioctx, testNS))
|
||
|
}()
|
||
|
|
||
|
ta := getAdmin(t).Task()
|
||
|
options := rbd.NewRbdImageOptions()
|
||
|
assert.NoError(t,
|
||
|
options.SetUint64(rbd.ImageOptionOrder, uint64(testImageOrder)))
|
||
|
|
||
|
for _, tt := range tests {
|
||
|
t.Run(tt.name, func(t *testing.T) {
|
||
|
|
||
|
ioctx.SetNamespace(tt.args.namespace)
|
||
|
err = rbd.CreateImage(ioctx, tt.args.imageName, testImageSize, options)
|
||
|
assert.NoError(t, err)
|
||
|
|
||
|
img, err := rbd.OpenImage(ioctx, tt.args.imageName, rbd.NoSnapshot)
|
||
|
assert.NoError(t, err)
|
||
|
|
||
|
imageID, err := img.GetId()
|
||
|
assert.NoError(t, err)
|
||
|
|
||
|
assert.NoError(t, img.Trash(0))
|
||
|
assert.NoError(t, img.Close())
|
||
|
|
||
|
tr, err := ta.AddTrashRemove(NewImageSpec(tt.args.pool, tt.args.namespace, imageID))
|
||
|
assert.NoError(t, err)
|
||
|
|
||
|
assert.Equal(t, imageID, tr.Refs.ImageID)
|
||
|
assert.Equal(t, defaultPoolName, tr.Refs.PoolName)
|
||
|
assert.Equal(t, tt.args.namespace, tr.Refs.PoolNamespace)
|
||
|
assert.Equal(t, "trash remove", tr.Refs.Action)
|
||
|
|
||
|
trashList := []rbd.TrashInfo{}
|
||
|
// wait for the image to be deleted
|
||
|
for i := 0; i < 35; i++ {
|
||
|
trashList, err = rbd.GetTrashList(ioctx)
|
||
|
assert.NoError(t, err)
|
||
|
|
||
|
if len(trashList) == 0 {
|
||
|
break
|
||
|
}
|
||
|
time.Sleep(time.Second)
|
||
|
}
|
||
|
assert.Equal(t, 0, len(trashList))
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestTaskAdminAddFlatten(t *testing.T) {
|
||
|
parentImageName := "parent"
|
||
|
|
||
|
ensureDefaultPool(t)
|
||
|
conn := getConn(t)
|
||
|
|
||
|
ioctx, err := conn.OpenIOContext(defaultPoolName)
|
||
|
require.NoError(t, err)
|
||
|
defer ioctx.Destroy()
|
||
|
|
||
|
assert.NoError(t, rbd.NamespaceCreate(ioctx, testNS))
|
||
|
defer func() {
|
||
|
assert.NoError(t, rbd.NamespaceRemove(ioctx, testNS))
|
||
|
}()
|
||
|
|
||
|
ta := getAdmin(t).Task()
|
||
|
for _, tt := range tests {
|
||
|
t.Run(tt.name, func(t *testing.T) {
|
||
|
ioctx.SetNamespace(tt.args.namespace)
|
||
|
|
||
|
options := rbd.NewRbdImageOptions()
|
||
|
assert.NoError(t,
|
||
|
options.SetUint64(rbd.ImageOptionOrder, uint64(testImageOrder)))
|
||
|
err = rbd.CreateImage(ioctx, parentImageName, testImageSize, options)
|
||
|
assert.NoError(t, err)
|
||
|
|
||
|
parentImage, err := rbd.OpenImage(ioctx, parentImageName, rbd.NoSnapshot)
|
||
|
assert.NoError(t, err)
|
||
|
defer func() {
|
||
|
assert.NoError(t, parentImage.Close())
|
||
|
assert.NoError(t, parentImage.Remove())
|
||
|
}()
|
||
|
|
||
|
snap, err := parentImage.CreateSnapshot(tt.args.imageName)
|
||
|
assert.NoError(t, err)
|
||
|
|
||
|
err = snap.Protect()
|
||
|
assert.NoError(t, err)
|
||
|
defer func() {
|
||
|
assert.NoError(t, snap.Unprotect())
|
||
|
assert.NoError(t, snap.Remove())
|
||
|
}()
|
||
|
assert.NoError(t, options.SetUint64(rbd.ImageOptionFormat, uint64(2)))
|
||
|
|
||
|
assert.NoError(t, rbd.CloneImage(ioctx, parentImageName, tt.args.imageName, ioctx, tt.args.imageName, options))
|
||
|
|
||
|
childImage, err := rbd.OpenImage(ioctx, tt.args.imageName, rbd.NoSnapshot)
|
||
|
assert.NoError(t, err)
|
||
|
defer func() {
|
||
|
assert.NoError(t, childImage.Close())
|
||
|
assert.NoError(t, childImage.Remove())
|
||
|
}()
|
||
|
|
||
|
parentInfo, err := childImage.GetParent()
|
||
|
assert.NoError(t, err)
|
||
|
assert.Equal(t, parentImageName, parentInfo.Image.ImageName)
|
||
|
assert.Equal(t, tt.args.imageName, parentInfo.Snap.SnapName)
|
||
|
|
||
|
tr, err := ta.AddFlatten(NewImageSpec(tt.args.pool, tt.args.namespace, tt.args.imageName))
|
||
|
assert.NoError(t, err)
|
||
|
|
||
|
assert.Equal(t, tt.args.imageName, tr.Refs.ImageName)
|
||
|
assert.Equal(t, defaultPoolName, tr.Refs.PoolName)
|
||
|
assert.Equal(t, tt.args.namespace, tr.Refs.PoolNamespace)
|
||
|
assert.Equal(t, "flatten", tr.Refs.Action)
|
||
|
|
||
|
// wait for the image to be flattened
|
||
|
for i := 0; i < 35; i++ {
|
||
|
_, err = childImage.GetParent()
|
||
|
if errors.Is(err, rbd.RbdErrorNotFound) {
|
||
|
break
|
||
|
}
|
||
|
assert.NoError(t, err)
|
||
|
|
||
|
time.Sleep(time.Second)
|
||
|
}
|
||
|
assert.Error(t, err)
|
||
|
})
|
||
|
}
|
||
|
|
||
|
}
|