package volume_move import ( "context" "strings" "testing" "github.com/seaweedfs/seaweedfs/weed/pb" "github.com/seaweedfs/seaweedfs/weed/pb/volume_server_pb" "github.com/seaweedfs/seaweedfs/weed/storage/erasure_coding" ) func ecMove(shardIds ...erasure_coding.ShardId) EcShardMove { return EcShardMove{ VolumeId: 7, Collection: "c1", ShardIds: shardIds, Source: srcAddr, Target: dstAddr, TargetDisk: 2, } } func dstShards(shardIds ...uint32) []*volume_server_pb.EcShardInfo { var infos []*volume_server_pb.EcShardInfo for _, sid := range shardIds { infos = append(infos, &volume_server_pb.EcShardInfo{VolumeId: 7, ShardId: sid, Size: 1024}) } return infos } func TestMoveEcShardsSequence(t *testing.T) { cluster := newFakeCluster() cluster.ecShards[string(dstAddr)] = dstShards(3, 4) err := cluster.mover().MoveEcShards(context.Background(), ecMove(3, 4), EcMoveOptions{IoBytePerSecond: 77}) if err != nil { t.Fatalf("MoveEcShards: %v", err) } assertCalls(t, cluster.callList(), []string{ "dst:8080 VolumeEcShardsCopy", "dst:8080 VolumeEcShardsMount", "dst:8080 VolumeEcShardsInfo", "src:8080 VolumeEcShardsUnmount", "src:8080 VolumeEcShardsDelete", }) copyReq := cluster.ecCopyReqs[0] if !copyReq.CopyEcxFile || !copyReq.CopyEcjFile || !copyReq.CopyVifFile || !copyReq.CopyEcsumFile { t.Errorf("shard sidecars not all copied: %+v", copyReq) } if copyReq.DiskId != 2 || copyReq.SourceDataNode != string(srcAddr) || copyReq.Collection != "c1" || copyReq.IoBytePerSecond != 77 { t.Errorf("copy request not propagated: %+v", copyReq) } } func TestMoveEcShardsVerifyFailureKeepsSource(t *testing.T) { cluster := newFakeCluster() cluster.ecShards[string(dstAddr)] = dstShards(3) // shard 4 didn't register err := cluster.mover().MoveEcShards(context.Background(), ecMove(3, 4), EcMoveOptions{}) if err == nil || !strings.Contains(err.Error(), "missing EC shard 7.4") { t.Fatalf("expected missing-shard error, got: %v", err) } for _, call := range cluster.callList() { if call == "src:8080 VolumeEcShardsUnmount" || call == "src:8080 VolumeEcShardsDelete" { t.Fatalf("source touched despite verification failure: %v", cluster.callList()) } } } func TestMoveEcShardsZeroSizedDestinationKeepsSource(t *testing.T) { // A zero-sized shard on the destination is residue of a failed operation // (seaweedfs issue 10730); verification must not count it as a delivered // shard, or the source is deleted behind a broken copy. cluster := newFakeCluster() cluster.ecShards[string(dstAddr)] = dstShards(3, 4) cluster.ecShards[string(dstAddr)][1].Size = 0 // shard 4 landed as an empty file err := cluster.mover().MoveEcShards(context.Background(), ecMove(3, 4), EcMoveOptions{}) if err == nil || !strings.Contains(err.Error(), "zero-sized EC shard 7.4") { t.Fatalf("expected zero-sized-shard rejection, got: %v", err) } for _, call := range cluster.callList() { if call == "src:8080 VolumeEcShardsUnmount" || call == "src:8080 VolumeEcShardsDelete" { t.Fatalf("source touched despite zero-sized destination shard: %v", cluster.callList()) } } } func TestMoveEcShardsRejectsSameServer(t *testing.T) { // The second target is the same server written with an explicit grpc port; // the guard must see through the representation difference. for _, target := range []pb.ServerAddress{srcAddr, pb.ServerAddress("src:8080.18080")} { cluster := newFakeCluster() move := ecMove(3) move.Target = target err := cluster.mover().MoveEcShards(context.Background(), move, EcMoveOptions{}) if err == nil || !strings.Contains(err.Error(), "its own server") { t.Fatalf("target %q: expected same-server rejection, got: %v", target, err) } if len(cluster.callList()) != 0 { t.Fatalf("target %q: RPCs issued for a rejected move: %v", target, cluster.callList()) } } } func TestRemoveEcShards(t *testing.T) { cluster := newFakeCluster() err := cluster.mover().RemoveEcShards(context.Background(), 7, "c1", srcAddr, []erasure_coding.ShardId{3}) if err != nil { t.Fatalf("RemoveEcShards: %v", err) } assertCalls(t, cluster.callList(), []string{ "src:8080 VolumeEcShardsUnmount", "src:8080 VolumeEcShardsDelete", }) } func TestCopyAndMountEcShardsSameAddressMountsOnly(t *testing.T) { cluster := newFakeCluster() err := cluster.mover().CopyAndMountEcShards(context.Background(), 7, "c1", []erasure_coding.ShardId{3}, srcAddr, srcAddr, 0, 0, nil) if err != nil { t.Fatalf("CopyAndMountEcShards: %v", err) } assertCalls(t, cluster.callList(), []string{ "src:8080 VolumeEcShardsMount", }) }