// +build !nautilus // Initially, we're only providing mirroring related functions for octopus as // that version of ceph deprecated a number of the functions in nautilus. If // you need mirroring on an earlier supported version of ceph please file an // issue in our tracker. package rbd import ( "fmt" "os" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestGetMirrorUUID(t *testing.T) { conn := radosConnect(t) poolName := GetUUID() err := conn.MakePool(poolName) require.NoError(t, err) defer func() { assert.NoError(t, conn.DeletePool(poolName)) conn.Shutdown() }() ioctx, err := conn.OpenIOContext(poolName) assert.NoError(t, err) defer func() { ioctx.Destroy() }() // verify that mirroring is not enabled on this new pool m, err := GetMirrorMode(ioctx) assert.NoError(t, err) assert.Equal(t, m, MirrorModeDisabled) // enable per-image mirroring for this pool err = SetMirrorMode(ioctx, MirrorModeImage) require.NoError(t, err) name1 := GetUUID() options := NewRbdImageOptions() assert.NoError(t, options.SetUint64(ImageOptionOrder, uint64(testImageOrder))) err = CreateImage(ioctx, name1, testImageSize, options) require.NoError(t, err) t.Run("getUUID", func(t *testing.T) { img, err := OpenImage(ioctx, name1, NoSnapshot) assert.NoError(t, err) defer func() { assert.NoError(t, img.Close()) }() err = img.MirrorEnable(ImageMirrorModeSnapshot) assert.NoError(t, err) miid, err := GetMirrorUUID(ioctx) assert.NoError(t, err) assert.NotEqual(t, miid, "") }) } func TestGetMirrorMode(t *testing.T) { conn := radosConnect(t) poolName := GetUUID() err := conn.MakePool(poolName) require.NoError(t, err) defer func() { assert.NoError(t, conn.DeletePool(poolName)) conn.Shutdown() }() ioctx, err := conn.OpenIOContext(poolName) assert.NoError(t, err) defer func() { ioctx.Destroy() }() t.Run("mirrorModeDisabled", func(t *testing.T) { m, err := GetMirrorMode(ioctx) assert.NoError(t, err) assert.Equal(t, m, MirrorModeDisabled) }) t.Run("mirrorModeEnabled", func(t *testing.T) { err = SetMirrorMode(ioctx, MirrorModeImage) require.NoError(t, err) m, err := GetMirrorMode(ioctx) assert.NoError(t, err) assert.Equal(t, m, MirrorModeImage) }) t.Run("ioctxNil", func(t *testing.T) { assert.Panics(t, func() { GetMirrorMode(nil) }) }) } func TestMirroring(t *testing.T) { conn := radosConnect(t) poolName := GetUUID() err := conn.MakePool(poolName) require.NoError(t, err) defer func() { assert.NoError(t, conn.DeletePool(poolName)) conn.Shutdown() }() ioctx, err := conn.OpenIOContext(poolName) assert.NoError(t, err) defer func() { ioctx.Destroy() }() // verify that mirroring is not enabled on this new pool m, err := GetMirrorMode(ioctx) assert.NoError(t, err) assert.Equal(t, m, MirrorModeDisabled) // enable per-image mirroring for this pool err = SetMirrorMode(ioctx, MirrorModeImage) require.NoError(t, err) name1 := GetUUID() options := NewRbdImageOptions() assert.NoError(t, options.SetUint64(ImageOptionOrder, uint64(testImageOrder))) err = CreateImage(ioctx, name1, testImageSize, options) require.NoError(t, err) t.Run("enableDisable", func(t *testing.T) { img, err := OpenImage(ioctx, name1, NoSnapshot) assert.NoError(t, err) defer func() { assert.NoError(t, img.Close()) }() err = img.MirrorEnable(ImageMirrorModeSnapshot) assert.NoError(t, err) mode, err := img.GetImageMirrorMode() assert.NoError(t, err) assert.Equal(t, mode, ImageMirrorModeSnapshot) err = img.MirrorDisable(false) assert.NoError(t, err) }) t.Run("enableDisableInvalid", func(t *testing.T) { img, err := OpenImage(ioctx, name1, NoSnapshot) assert.NoError(t, err) assert.NoError(t, img.Close()) err = img.MirrorEnable(ImageMirrorModeSnapshot) assert.Error(t, err) err = img.MirrorDisable(false) assert.Error(t, err) _, err = img.GetImageMirrorMode() assert.Error(t, err) }) t.Run("promoteDemote", func(t *testing.T) { img, err := OpenImage(ioctx, name1, NoSnapshot) assert.NoError(t, err) defer func() { assert.NoError(t, img.Close()) }() err = img.MirrorEnable(ImageMirrorModeSnapshot) assert.NoError(t, err) err = img.MirrorDemote() assert.NoError(t, err) err = img.MirrorPromote(false) assert.NoError(t, err) err = img.MirrorDisable(false) assert.NoError(t, err) }) t.Run("promoteDemoteInvalid", func(t *testing.T) { img, err := OpenImage(ioctx, name1, NoSnapshot) assert.NoError(t, err) assert.NoError(t, img.Close()) err = img.MirrorDemote() assert.Error(t, err) err = img.MirrorPromote(false) assert.Error(t, err) }) t.Run("resync", func(t *testing.T) { img, err := OpenImage(ioctx, name1, NoSnapshot) assert.NoError(t, err) defer func() { assert.NoError(t, img.Close()) }() err = img.MirrorEnable(ImageMirrorModeSnapshot) assert.NoError(t, err) err = img.MirrorDemote() assert.NoError(t, err) err = img.MirrorResync() assert.NoError(t, err) err = img.MirrorDisable(true) assert.NoError(t, err) }) t.Run("resyncInvalid", func(t *testing.T) { img, err := OpenImage(ioctx, name1, NoSnapshot) assert.NoError(t, err) assert.NoError(t, img.Close()) err = img.MirrorResync() assert.Error(t, err) }) t.Run("instanceId", func(t *testing.T) { img, err := OpenImage(ioctx, name1, NoSnapshot) assert.NoError(t, err) defer func() { assert.NoError(t, img.Close()) }() err = img.MirrorEnable(ImageMirrorModeSnapshot) assert.NoError(t, err) miid, err := img.MirrorInstanceID() // this is not currently testable for the "success" case // see also the ceph tree where nothing is asserted except // that the error is raised. // TODO(?): figure out how to test this assert.Error(t, err) assert.Equal(t, "", miid) err = img.MirrorDisable(false) assert.NoError(t, err) }) t.Run("instanceIdInvalid", func(t *testing.T) { img, err := OpenImage(ioctx, name1, NoSnapshot) assert.NoError(t, err) assert.NoError(t, img.Close()) _, err = img.MirrorInstanceID() assert.Error(t, err) }) } func TestGetMirrorImageInfo(t *testing.T) { conn := radosConnect(t) poolName := GetUUID() err := conn.MakePool(poolName) require.NoError(t, err) defer func() { assert.NoError(t, conn.DeletePool(poolName)) conn.Shutdown() }() ioctx, err := conn.OpenIOContext(poolName) assert.NoError(t, err) defer func() { ioctx.Destroy() }() // enable per-image mirroring for this pool err = SetMirrorMode(ioctx, MirrorModeImage) require.NoError(t, err) imgName := GetUUID() options := NewRbdImageOptions() assert.NoError(t, options.SetUint64(ImageOptionOrder, uint64(testImageOrder))) err = CreateImage(ioctx, imgName, testImageSize, options) require.NoError(t, err) t.Run("closedImage", func(t *testing.T) { img := GetImage(ioctx, imgName) _, err = img.GetMirrorImageInfo() assert.Error(t, err) }) t.Run("getInfo", func(t *testing.T) { // open image, enable, mirroring. img, err := OpenImage(ioctx, imgName, NoSnapshot) assert.NoError(t, err) defer func() { assert.NoError(t, img.Close()) }() err = img.MirrorEnable(ImageMirrorModeSnapshot) assert.NoError(t, err) mii, err := img.GetMirrorImageInfo() assert.NoError(t, err) assert.NotNil(t, mii.GlobalID) assert.Equal(t, mii.State, MirrorImageEnabled) assert.Equal(t, mii.Primary, true) }) } func TestMirrorConstantStrings(t *testing.T) { x := []struct { s fmt.Stringer t string }{ {MirrorModeDisabled, "disabled"}, {MirrorModeImage, "image"}, {MirrorModePool, "pool"}, {MirrorMode(9999), ""}, {ImageMirrorModeJournal, "journal"}, {ImageMirrorModeSnapshot, "snapshot"}, {ImageMirrorMode(9999), ""}, {MirrorImageDisabling, "disabling"}, {MirrorImageEnabled, "enabled"}, {MirrorImageDisabled, "disabled"}, {MirrorImageState(9999), ""}, } for _, v := range x { assert.Equal(t, v.s.String(), v.t) } } func TestGetGlobalMirrorStatus(t *testing.T) { conn := radosConnect(t) poolName := GetUUID() err := conn.MakePool(poolName) require.NoError(t, err) defer func() { assert.NoError(t, conn.DeletePool(poolName)) conn.Shutdown() }() ioctx, err := conn.OpenIOContext(poolName) assert.NoError(t, err) defer func() { ioctx.Destroy() }() // enable per-image mirroring for this pool err = SetMirrorMode(ioctx, MirrorModeImage) require.NoError(t, err) imgName := GetUUID() options := NewRbdImageOptions() assert.NoError(t, options.SetUint64(ImageOptionOrder, uint64(testImageOrder))) err = CreateImage(ioctx, imgName, testImageSize, options) require.NoError(t, err) t.Run("closedImage", func(t *testing.T) { img := GetImage(ioctx, imgName) _, err = img.GetGlobalMirrorStatus() assert.Error(t, err) }) t.Run("getStatus", func(t *testing.T) { // open image, enable, mirroring. img, err := OpenImage(ioctx, imgName, NoSnapshot) assert.NoError(t, err) defer func() { assert.NoError(t, img.Close()) }() err = img.MirrorEnable(ImageMirrorModeSnapshot) assert.NoError(t, err) gms, err := img.GetGlobalMirrorStatus() assert.NoError(t, err) assert.NotEqual(t, "", gms.Name) assert.NotEqual(t, "", gms.Info.GlobalID) assert.Equal(t, gms.Info.State, MirrorImageEnabled) assert.Equal(t, gms.Info.Primary, true) if assert.Len(t, gms.SiteStatuses, 1) { ss := gms.SiteStatuses[0] assert.Equal(t, "", ss.MirrorUUID) assert.Equal(t, MirrorImageStatusStateUnknown, ss.State, ss.State) assert.Equal(t, "status not found", ss.Description) assert.Equal(t, int64(0), ss.LastUpdate) assert.False(t, ss.Up) ls, err := gms.LocalStatus() assert.NoError(t, err) assert.Equal(t, ss, ls) } }) } func mirrorConfig() string { return os.Getenv("MIRROR_CONF") } func TestGetGlobalMirrorStatusMirroredPool(t *testing.T) { mconfig := mirrorConfig() if mconfig == "" { t.Skip("no mirror config env var set") } conn := radosConnect(t) // this test assumes the rbd pool already exists and is mirrored // this must be set up previously by the CI or manually poolName := "rbd" ioctx, err := conn.OpenIOContext(poolName) assert.NoError(t, err) defer func() { ioctx.Destroy() }() imgName := GetUUID() options := NewRbdImageOptions() assert.NoError(t, options.SetUint64(ImageOptionOrder, uint64(testImageOrder))) err = CreateImage(ioctx, imgName, testImageSize, options) require.NoError(t, err) defer func() { err = RemoveImage(ioctx, imgName) assert.NoError(t, err) }() // this next section is not a t.Run because it must be unconditionally // executed. It is wrapped in a func to use defer to close the img. func() { img, err := OpenImage(ioctx, imgName, NoSnapshot) assert.NoError(t, err) defer func() { assert.NoError(t, img.Close()) }() err = img.MirrorEnable(ImageMirrorModeSnapshot) assert.NoError(t, err) mid, err := img.CreateMirrorSnapshot() assert.NoError(t, err) assert.NotEqual(t, 0, mid) // wait for site statuses to get updated for i := 0; i < 30; i++ { gms, err := img.GetGlobalMirrorStatus() assert.NoError(t, err) if len(gms.SiteStatuses) > 1 { break } time.Sleep(time.Second) } gms, err := img.GetGlobalMirrorStatus() assert.NoError(t, err) assert.NotEqual(t, "", gms.Name) assert.NotEqual(t, "", gms.Info.GlobalID) assert.Equal(t, gms.Info.State, MirrorImageEnabled) assert.Equal(t, gms.Info.Primary, true) if assert.Len(t, gms.SiteStatuses, 2) { ss1 := gms.SiteStatuses[0] assert.Equal(t, "", ss1.MirrorUUID) assert.Equal(t, MirrorImageStatusStateStopped, ss1.State, ss1.State) assert.Equal(t, "local image is primary", ss1.Description) assert.Greater(t, ss1.LastUpdate, int64(0)) assert.True(t, ss1.Up) ls, err := gms.LocalStatus() assert.NoError(t, err) assert.Equal(t, ss1, ls) ss2 := gms.SiteStatuses[1] assert.NotEqual(t, "", ss2.MirrorUUID) assert.Equal(t, MirrorImageStatusStateReplaying, ss2.State, ss2.State) assert.Contains(t, ss2.Description, "replaying,") assert.Greater(t, ss2.LastUpdate, int64(0)) assert.True(t, ss2.Up) } }() // test the results of GetGlobalMirrorStatus using the "other" // mirror+pool as a source t.Run("fromMirror", func(t *testing.T) { conn := radosConnectConfig(t, mconfig) ioctx2, err := conn.OpenIOContext(poolName) assert.NoError(t, err) defer func() { ioctx2.Destroy() }() img, err := OpenImage(ioctx2, imgName, NoSnapshot) assert.NoError(t, err) defer func() { assert.NoError(t, img.Close()) }() // wait for site statuses to get updated for i := 0; i < 30; i++ { gms, err := img.GetGlobalMirrorStatus() assert.NoError(t, err) if len(gms.SiteStatuses) > 1 { break } time.Sleep(time.Second) } gms, err := img.GetGlobalMirrorStatus() assert.NoError(t, err) assert.NotEqual(t, "", gms.Name) assert.NotEqual(t, "", gms.Info.GlobalID) assert.Equal(t, gms.Info.State, MirrorImageEnabled) assert.Equal(t, gms.Info.Primary, false) if assert.Len(t, gms.SiteStatuses, 2) { ls, err := gms.LocalStatus() assert.NoError(t, err) assert.Equal(t, "", ls.MirrorUUID) assert.Equal(t, MirrorImageStatusStateReplaying, ls.State, ls.State) assert.Contains(t, ls.Description, "replaying,") assert.Greater(t, ls.LastUpdate, int64(0)) assert.True(t, ls.Up) assert.Equal(t, ls, gms.SiteStatuses[0]) ss2 := gms.SiteStatuses[1] assert.NotEqual(t, "", ss2.MirrorUUID) assert.Equal(t, MirrorImageStatusStateStopped, ss2.State, ss2.State) assert.Equal(t, "local image is primary", ss2.Description) assert.Greater(t, ss2.LastUpdate, int64(0)) assert.True(t, ss2.Up) } }) } func TestMirrorImageStatusSummary(t *testing.T) { t.Run("ioctxNil", func(t *testing.T) { assert.Panics(t, func() { MirrorImageStatusSummary(nil) }) }) t.Run("emptyPool", func(t *testing.T) { conn := radosConnect(t) poolName := GetUUID() err := conn.MakePool(poolName) require.NoError(t, err) defer func() { assert.NoError(t, conn.DeletePool(poolName)) conn.Shutdown() }() ioctx, err := conn.OpenIOContext(poolName) assert.NoError(t, err) defer func() { ioctx.Destroy() }() ssum, err := MirrorImageStatusSummary(ioctx) assert.NoError(t, err) assert.Len(t, ssum, 0) }) t.Run("mirroredPool", testMirrorImageStatusSummaryMirroredPool) } func testMirrorImageStatusSummaryMirroredPool(t *testing.T) { mconfig := mirrorConfig() if mconfig == "" { t.Skip("no mirror config env var set") } conn := radosConnect(t) // this test assumes the rbd pool already exists and is mirrored // this must be set up previously by the CI or manually poolName := "rbd" ioctx, err := conn.OpenIOContext(poolName) assert.NoError(t, err) defer func() { ioctx.Destroy() }() imgBase := GetUUID() imgName1 := imgBase + "a" imgName2 := imgBase + "b" imgName3 := imgBase + "c" imgName4 := imgBase + "d" options := NewRbdImageOptions() assert.NoError(t, options.SetUint64(ImageOptionOrder, uint64(testImageOrder))) for _, n := range []string{imgName1, imgName2, imgName3, imgName4} { err = CreateImage(ioctx, n, testImageSize, options) require.NoError(t, err) defer func(n string) { err = RemoveImage(ioctx, n) assert.NoError(t, err) }(n) } mkMirror := func(n string) { img, err := OpenImage(ioctx, n, NoSnapshot) assert.NoError(t, err) defer func() { assert.NoError(t, img.Close()) }() err = img.MirrorEnable(ImageMirrorModeSnapshot) assert.NoError(t, err) mid, err := img.CreateMirrorSnapshot() assert.NoError(t, err) assert.NotEqual(t, 0, mid) } checkMirror := func(n string) { img, err := OpenImage(ioctx, n, NoSnapshot) assert.NoError(t, err) defer func() { assert.NoError(t, img.Close()) }() // wait for site statuses to get updated for i := 0; i < 30; i++ { gms, err := img.GetGlobalMirrorStatus() assert.NoError(t, err) if len(gms.SiteStatuses) > 1 { break } time.Sleep(time.Second) } } for _, n := range []string{imgName1, imgName3} { mkMirror(n) } for _, n := range []string{imgName1, imgName3} { checkMirror(n) } ssum, err := MirrorImageStatusSummary(ioctx) assert.NoError(t, err) if assert.Len(t, ssum, 1) { assert.Contains(t, ssum, MirrorImageStatusStateReplaying) assert.GreaterOrEqual(t, ssum[MirrorImageStatusStateReplaying], uint(2)) } // immediately going for status right after enabling mirroring and not // waiting for things to settle should give us one unknown status mkMirror(imgName2) ssum, err = MirrorImageStatusSummary(ioctx) assert.NoError(t, err) if assert.Len(t, ssum, 2) { assert.Contains(t, ssum, MirrorImageStatusStateReplaying) assert.GreaterOrEqual(t, ssum[MirrorImageStatusStateReplaying], uint(2)) assert.Contains(t, ssum, MirrorImageStatusStateUnknown) assert.GreaterOrEqual(t, ssum[MirrorImageStatusStateUnknown], uint(1)) } } func TestMirrorSiteName(t *testing.T) { t.Run("connNilGet", func(t *testing.T) { assert.Panics(t, func() { GetMirrorSiteName(nil) }) }) t.Run("connNilSet", func(t *testing.T) { assert.Panics(t, func() { SetMirrorSiteName(nil, "foo") }) }) t.Run("simple", func(t *testing.T) { conn := radosConnect(t) defer conn.Shutdown() err := SetMirrorSiteName(conn, "rbd4eva") assert.NoError(t, err) n, err := GetMirrorSiteName(conn) assert.NoError(t, err) assert.Equal(t, "rbd4eva", n) err = SetMirrorSiteName(conn, "ceph_a") assert.NoError(t, err) n, err = GetMirrorSiteName(conn) assert.NoError(t, err) assert.Equal(t, "ceph_a", n) }) t.Run("twoCluster", func(t *testing.T) { mconfig := mirrorConfig() if mconfig == "" { t.Skip("no mirror config env var set") } conn1 := radosConnect(t) defer conn1.Shutdown() conn2 := radosConnectConfig(t, mconfig) defer conn2.Shutdown() err := SetMirrorSiteName(conn1, "cluster_a") assert.NoError(t, err) err = SetMirrorSiteName(conn2, "cluster_b") assert.NoError(t, err) // verify the two conns are properly separate n1, err := GetMirrorSiteName(conn1) assert.NoError(t, err) assert.Equal(t, "cluster_a", n1) n2, err := GetMirrorSiteName(conn2) assert.NoError(t, err) assert.Equal(t, "cluster_b", n2) }) } func TestMirrorBootstrapToken(t *testing.T) { t.Run("ioctxNilCreate", func(t *testing.T) { assert.Panics(t, func() { CreateMirrorPeerBootstrapToken(nil) }) }) t.Run("ioctxNilImport", func(t *testing.T) { assert.Panics(t, func() { ImportMirrorPeerBootstrapToken(nil, MirrorPeerDirectionRxTx, "") }) }) t.Run("justCreate", func(t *testing.T) { conn := radosConnect(t) defer conn.Shutdown() poolName := GetUUID() err := conn.MakePool(poolName) require.NoError(t, err) defer func() { assert.NoError(t, conn.DeletePool(poolName)) }() ioctx, err := conn.OpenIOContext(poolName) assert.NoError(t, err) defer func() { ioctx.Destroy() }() err = SetMirrorMode(ioctx, MirrorModeImage) require.NoError(t, err) token, err := CreateMirrorPeerBootstrapToken(ioctx) assert.NoError(t, err) assert.GreaterOrEqual(t, len(token), 4) }) t.Run("roundTrip", func(t *testing.T) { mconfig := mirrorConfig() if mconfig == "" { t.Skip("no mirror config env var set") } conn1 := radosConnect(t) defer conn1.Shutdown() poolName := GetUUID() err := conn1.MakePool(poolName) require.NoError(t, err) defer func() { assert.NoError(t, conn1.DeletePool(poolName)) }() err = SetMirrorSiteName(conn1, "ceph_a") require.NoError(t, err) ioctx1, err := conn1.OpenIOContext(poolName) assert.NoError(t, err) defer func() { ioctx1.Destroy() }() err = SetMirrorMode(ioctx1, MirrorModeImage) require.NoError(t, err) token, err := CreateMirrorPeerBootstrapToken(ioctx1) assert.NoError(t, err) assert.GreaterOrEqual(t, len(token), 4) conn2 := radosConnectConfig(t, mconfig) defer conn2.Shutdown() err = conn2.MakePool(poolName) require.NoError(t, err) defer func() { assert.NoError(t, conn2.DeletePool(poolName)) }() err = SetMirrorSiteName(conn2, "ceph_b") require.NoError(t, err) ioctx2, err := conn2.OpenIOContext(poolName) assert.NoError(t, err) defer func() { ioctx2.Destroy() }() err = SetMirrorMode(ioctx2, MirrorModeImage) require.NoError(t, err) fmt.Printf("TOKEN: %s\n", string(token)) err = ImportMirrorPeerBootstrapToken( ioctx2, MirrorPeerDirectionRxTx, token) assert.NoError(t, err) }) } func TestMirrorImageLists(t *testing.T) { defer func(x int) { iterBufSize = x }(iterBufSize) // shrink the buffer size in order to trigger more of the // retry logic in the iter type iterBufSize = 4 conn := radosConnect(t) poolName := GetUUID() err := conn.MakePool(poolName) require.NoError(t, err) defer func() { assert.NoError(t, conn.DeletePool(poolName)) conn.Shutdown() }() ioctx, err := conn.OpenIOContext(poolName) assert.NoError(t, err) defer func() { ioctx.Destroy() }() // enable per-image mirroring for this pool err = SetMirrorMode(ioctx, MirrorModeImage) require.NoError(t, err) imgName := GetUUID() options := NewRbdImageOptions() assert.NoError(t, options.SetUint64(ImageOptionOrder, uint64(testImageOrder))) for i := 0; i < 5; i++ { name := fmt.Sprintf("%s%d", imgName, i) err = CreateImage(ioctx, name, testImageSize, options) require.NoError(t, err) img, err := OpenImage(ioctx, name, NoSnapshot) assert.NoError(t, err) err = img.MirrorEnable(ImageMirrorModeSnapshot) assert.NoError(t, err) require.NoError(t, img.Close()) } for i := 5; i < 10; i++ { name := fmt.Sprintf("%s%d", imgName, i) err = CreateImage(ioctx, name, testImageSize, options) require.NoError(t, err) img, err := OpenImage(ioctx, name, NoSnapshot) assert.NoError(t, err) err = img.MirrorEnable(ImageMirrorModeJournal) assert.NoError(t, err) require.NoError(t, img.Close()) } t.Run("statusIterIoctxNil", func(t *testing.T) { iter := NewMirrorImageGlobalStatusIter(nil) defer iter.Close() //nolint:errcheck assert.Panics(t, func() { iter.Next() //nolint:errcheck }) }) t.Run("infoIterIoctxNil", func(t *testing.T) { iter := NewMirrorImageInfoIter(nil, nil) assert.Panics(t, func() { iter.Next() //nolint:errcheck }) }) t.Run("getStatus", func(t *testing.T) { lst := []*GlobalMirrorImageIDAndStatus{} iter := NewMirrorImageGlobalStatusIter(ioctx) for { istatus, err := iter.Next() assert.NoError(t, err) if istatus == nil { break } lst = append(lst, istatus) } assert.Len(t, lst, 10) gms := lst[0].Status assert.NoError(t, err) assert.NotEqual(t, "", gms.Name) assert.NotEqual(t, "", gms.Info.GlobalID) assert.Equal(t, gms.Info.State, MirrorImageEnabled) assert.Equal(t, gms.Info.Primary, false) if assert.Len(t, gms.SiteStatuses, 1) { ss := gms.SiteStatuses[0] assert.Equal(t, "", ss.MirrorUUID) assert.Equal(t, MirrorImageStatusStateUnknown, ss.State, ss.State) assert.Equal(t, "status not found", ss.Description) assert.Equal(t, int64(0), ss.LastUpdate) assert.False(t, ss.Up) ls, err := gms.LocalStatus() assert.NoError(t, err) assert.Equal(t, ss, ls) } }) t.Run("getStatusSlice", func(t *testing.T) { lst, err := MirrorImageGlobalStatusList(ioctx, "", 0) assert.NoError(t, err) assert.Len(t, lst, 10) for i := 1; i < len(lst); i++ { assert.NotEqual(t, lst[i-1].ID, lst[i].ID) } for i := 1; i <= iterBufSize; i++ { lst, err := MirrorImageGlobalStatusList(ioctx, "", i) assert.NoError(t, err) assert.Len(t, lst, i) } lst, err = MirrorImageGlobalStatusList(ioctx, "", 3) assert.NoError(t, err) assert.Len(t, lst, 3) lst, err = MirrorImageGlobalStatusList(ioctx, lst[2].ID, 0) assert.NoError(t, err) assert.Len(t, lst, 7) }) t.Run("getInfo", func(t *testing.T) { get := func(iter *MirrorImageInfoIter) []*MirrorImageInfoItem { lst := []*MirrorImageInfoItem{} for { info, err := iter.Next() assert.NoError(t, err) if info == nil { break } lst = append(lst, info) } return lst } iter := NewMirrorImageInfoIter(ioctx, nil) lst := get(iter) assert.Len(t, lst, 10) item := lst[0] assert.NotEqual(t, "", item.ID) assert.NotEqual(t, "", item.Info.GlobalID) assert.Equal(t, item.Info.State, MirrorImageEnabled) assert.Equal(t, item.Info.Primary, true) for i := 1; i < 10; i++ { assert.NotEqual(t, lst[i-1].ID, lst[i].ID) } iter = NewMirrorImageInfoIter(ioctx, ImageMirrorModeJournal) lst = get(iter) assert.Len(t, lst, 5) item = lst[0] assert.Equal(t, item.Mode, ImageMirrorModeJournal) assert.NotEqual(t, "", item.ID) assert.NotEqual(t, "", item.Info.GlobalID) assert.Equal(t, item.Info.State, MirrorImageEnabled) assert.Equal(t, item.Info.Primary, true) iter = NewMirrorImageInfoIter(ioctx, ImageMirrorModeSnapshot) lst = get(iter) assert.Len(t, lst, 5) item = lst[0] assert.Equal(t, item.Mode, ImageMirrorModeSnapshot) assert.NotEqual(t, "", item.ID) assert.NotEqual(t, "", item.Info.GlobalID) assert.Equal(t, item.Info.State, MirrorImageEnabled) assert.Equal(t, item.Info.Primary, true) }) t.Run("getInfoSlice", func(t *testing.T) { lst, err := MirrorImageInfoList(ioctx, nil, "", 0) assert.NoError(t, err) assert.Len(t, lst, 10) for i := 1; i < len(lst); i++ { assert.NotEqual(t, lst[i-1].ID, lst[i].ID) } for i := 1; i <= iterBufSize; i++ { lst, err := MirrorImageInfoList(ioctx, nil, "", i) assert.NoError(t, err) assert.Len(t, lst, i) } lst, err = MirrorImageInfoList(ioctx, ImageMirrorModeJournal, "", 0) assert.NoError(t, err) assert.Len(t, lst, 5) lst, err = MirrorImageInfoList(ioctx, ImageMirrorModeSnapshot, "", 0) assert.NoError(t, err) assert.Len(t, lst, 5) lst, err = MirrorImageInfoList(ioctx, nil, "", 3) assert.NoError(t, err) assert.Len(t, lst, 3) lst, err = MirrorImageInfoList(ioctx, nil, lst[2].ID, 0) assert.NoError(t, err) assert.Len(t, lst, 7) }) } func TestMirrorImageInstanceIDLists(t *testing.T) { defer func(x int) { iterBufSize = x }(iterBufSize) // shrink the buffer size in order to trigger more of the // retry logic in the iter type iterBufSize = 4 t.Run("instanceIDIterIoctxNil", func(t *testing.T) { iter := NewMirrorImageInstanceIDIter(nil) assert.Panics(t, func() { iter.Next() //nolint:errcheck }) }) mconfig := mirrorConfig() if mconfig == "" { t.Skip("no mirror config env var set") } conn := radosConnect(t) defer conn.Shutdown() poolName := GetUUID() err := conn.MakePool(poolName) require.NoError(t, err) defer func() { assert.NoError(t, conn.DeletePool(poolName)) }() ioctx, err := conn.OpenIOContext(poolName) assert.NoError(t, err) defer func() { ioctx.Destroy() }() err = SetMirrorMode(ioctx, MirrorModeImage) require.NoError(t, err) token, err := CreateMirrorPeerBootstrapToken(ioctx) assert.NoError(t, err) assert.GreaterOrEqual(t, len(token), 4) conn2 := radosConnectConfig(t, mconfig) defer conn2.Shutdown() err = conn2.MakePool(poolName) require.NoError(t, err) defer func() { assert.NoError(t, conn2.DeletePool(poolName)) }() ioctx2, err := conn2.OpenIOContext(poolName) assert.NoError(t, err) defer func() { ioctx2.Destroy() }() err = SetMirrorMode(ioctx2, MirrorModeImage) require.NoError(t, err) err = ImportMirrorPeerBootstrapToken( ioctx2, MirrorPeerDirectionRxTx, token) assert.NoError(t, err) imgName := GetUUID() options := NewRbdImageOptions() assert.NoError(t, options.SetUint64(ImageOptionOrder, uint64(testImageOrder))) for i := 0; i < 5; i++ { name := fmt.Sprintf("%s%d", imgName, i) err = CreateImage(ioctx, name, testImageSize, options) require.NoError(t, err) img, err := OpenImage(ioctx, name, NoSnapshot) assert.NoError(t, err) err = img.MirrorEnable(ImageMirrorModeSnapshot) assert.NoError(t, err) require.NoError(t, img.Close()) } // wait for all the images to be mirrored for i := 0; i < 30; i++ { lst, err := MirrorImageInstanceIDList(ioctx, "", 0) assert.NoError(t, err) if len(lst) == 5 { break } time.Sleep(time.Second) } t.Run("getInstanceID", func(t *testing.T) { lst := []*MirrorImageInstanceIDItem{} iter := NewMirrorImageInstanceIDIter(ioctx) for { istatus, err := iter.Next() assert.NoError(t, err) if istatus == nil { break } lst = append(lst, istatus) } assert.Len(t, lst, 5) assert.NoError(t, err) }) t.Run("getInstanceIDSlice", func(t *testing.T) { lst, err := MirrorImageInstanceIDList(ioctx, "", 0) fmt.Print(lst) assert.NoError(t, err) assert.Len(t, lst, 5) for i := 1; i < len(lst); i++ { assert.NotEqual(t, lst[i-1].ID, lst[i].ID) } for i := 1; i <= iterBufSize; i++ { lst, err := MirrorImageInstanceIDList(ioctx, "", i) assert.NoError(t, err) assert.Len(t, lst, i) } lst, err = MirrorImageInstanceIDList(ioctx, "", 3) assert.NoError(t, err) assert.Len(t, lst, 3) lst, err = MirrorImageInstanceIDList(ioctx, lst[2].ID, 0) assert.NoError(t, err) assert.Len(t, lst, 2) }) }