From 0dfc784e55e2d299b5150ae6f6f4d54a934c19dd Mon Sep 17 00:00:00 2001 From: lhy1024 Date: Tue, 21 Nov 2023 18:24:49 +0800 Subject: [PATCH] move test Signed-off-by: lhy1024 --- pkg/mcs/scheduling/server/apis/v1/api.go | 9 +- pkg/utils/apiutil/serverapi/middleware.go | 4 +- server/api/region_test.go | 222 ----------------- tests/server/api/operator_test.go | 4 +- tests/server/api/region_test.go | 287 ++++++++++++++++++++++ 5 files changed, 297 insertions(+), 229 deletions(-) create mode 100644 tests/server/api/region_test.go diff --git a/pkg/mcs/scheduling/server/apis/v1/api.go b/pkg/mcs/scheduling/server/apis/v1/api.go index de613f86044..b59780b7a61 100644 --- a/pkg/mcs/scheduling/server/apis/v1/api.go +++ b/pkg/mcs/scheduling/server/apis/v1/api.go @@ -1318,8 +1318,13 @@ func splitRegions(c *gin.Context) { // @Router /regions/replicated [get] func checkRegionsReplicated(c *gin.Context) { handler := c.MustGet(handlerKey).(*handler.Handler) - rawStartKey, _ := c.GetQuery("start_key") - rawEndKey, _ := c.GetQuery("end_key") + rawStartKey, ok1 := c.GetQuery("startKey") + rawEndKey, ok2 := c.GetQuery("endKey") + if !ok1 || !ok2 { + c.String(http.StatusBadRequest, "there is no start_key or end_key") + return + } + state, err := handler.CheckRegionsReplicated(rawStartKey, rawEndKey) if err != nil { c.String(http.StatusBadRequest, err.Error()) diff --git a/pkg/utils/apiutil/serverapi/middleware.go b/pkg/utils/apiutil/serverapi/middleware.go index f257efb34f9..335680d7da0 100644 --- a/pkg/utils/apiutil/serverapi/middleware.go +++ b/pkg/utils/apiutil/serverapi/middleware.go @@ -151,8 +151,8 @@ func (h *redirector) matchMicroServiceRedirectRules(r *http.Request) (bool, stri } else { r.URL.Path = rule.targetPath } - log.Debug("redirect to micro service", zap.String("path", r.URL.Path), zap.String("origin-path", origin), - zap.String("target", addr), zap.String("method", r.Method)) + log.Info("redirect to micro service", zap.String("path", r.URL.Path), zap.String("origin-path", origin), + zap.String("target", addr), zap.String("method", r.Method), zap.Any("query", r.URL.Query())) return true, addr } } diff --git a/server/api/region_test.go b/server/api/region_test.go index 4f6c80f228e..83d9dff402b 100644 --- a/server/api/region_test.go +++ b/server/api/region_test.go @@ -28,13 +28,11 @@ import ( "time" "github.com/docker/go-units" - "github.com/pingcap/failpoint" "github.com/pingcap/kvproto/pkg/metapb" "github.com/pingcap/kvproto/pkg/pdpb" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/tikv/pd/pkg/core" - "github.com/tikv/pd/pkg/schedule/placement" "github.com/tikv/pd/pkg/utils/apiutil" tu "github.com/tikv/pd/pkg/utils/testutil" "github.com/tikv/pd/server" @@ -337,99 +335,6 @@ func (suite *regionTestSuite) TestTop() { suite.checkTopRegions(fmt.Sprintf("%s/regions/cpu", suite.urlPrefix), []uint64{3, 2, 1}) } -func (suite *regionTestSuite) TestAccelerateRegionsScheduleInRange() { - re := suite.Require() - r1 := core.NewTestRegionInfo(557, 13, []byte("a1"), []byte("a2")) - r2 := core.NewTestRegionInfo(558, 14, []byte("a2"), []byte("a3")) - r3 := core.NewTestRegionInfo(559, 15, []byte("a3"), []byte("a4")) - mustRegionHeartbeat(re, suite.svr, r1) - mustRegionHeartbeat(re, suite.svr, r2) - mustRegionHeartbeat(re, suite.svr, r3) - body := fmt.Sprintf(`{"start_key":"%s", "end_key": "%s"}`, hex.EncodeToString([]byte("a1")), hex.EncodeToString([]byte("a3"))) - - err := tu.CheckPostJSON(testDialClient, fmt.Sprintf("%s/regions/accelerate-schedule", suite.urlPrefix), []byte(body), tu.StatusOK(re)) - suite.NoError(err) - idList := suite.svr.GetRaftCluster().GetSuspectRegions() - suite.Len(idList, 2) -} - -func (suite *regionTestSuite) TestAccelerateRegionsScheduleInRanges() { - re := suite.Require() - r1 := core.NewTestRegionInfo(557, 13, []byte("a1"), []byte("a2")) - r2 := core.NewTestRegionInfo(558, 14, []byte("a2"), []byte("a3")) - r3 := core.NewTestRegionInfo(559, 15, []byte("a3"), []byte("a4")) - r4 := core.NewTestRegionInfo(560, 16, []byte("a4"), []byte("a5")) - r5 := core.NewTestRegionInfo(561, 17, []byte("a5"), []byte("a6")) - mustRegionHeartbeat(re, suite.svr, r1) - mustRegionHeartbeat(re, suite.svr, r2) - mustRegionHeartbeat(re, suite.svr, r3) - mustRegionHeartbeat(re, suite.svr, r4) - mustRegionHeartbeat(re, suite.svr, r5) - body := fmt.Sprintf(`[{"start_key":"%s", "end_key": "%s"}, {"start_key":"%s", "end_key": "%s"}]`, hex.EncodeToString([]byte("a1")), hex.EncodeToString([]byte("a3")), hex.EncodeToString([]byte("a4")), hex.EncodeToString([]byte("a6"))) - - err := tu.CheckPostJSON(testDialClient, fmt.Sprintf("%s/regions/accelerate-schedule/batch", suite.urlPrefix), []byte(body), tu.StatusOK(re)) - suite.NoError(err) - idList := suite.svr.GetRaftCluster().GetSuspectRegions() - suite.Len(idList, 4) -} - -func (suite *regionTestSuite) TestScatterRegions() { - re := suite.Require() - r1 := core.NewTestRegionInfo(601, 13, []byte("b1"), []byte("b2")) - r1.GetMeta().Peers = append(r1.GetMeta().Peers, &metapb.Peer{Id: 5, StoreId: 14}, &metapb.Peer{Id: 6, StoreId: 15}) - r2 := core.NewTestRegionInfo(602, 13, []byte("b2"), []byte("b3")) - r2.GetMeta().Peers = append(r2.GetMeta().Peers, &metapb.Peer{Id: 7, StoreId: 14}, &metapb.Peer{Id: 8, StoreId: 15}) - r3 := core.NewTestRegionInfo(603, 13, []byte("b4"), []byte("b4")) - r3.GetMeta().Peers = append(r3.GetMeta().Peers, &metapb.Peer{Id: 9, StoreId: 14}, &metapb.Peer{Id: 10, StoreId: 15}) - mustRegionHeartbeat(re, suite.svr, r1) - mustRegionHeartbeat(re, suite.svr, r2) - mustRegionHeartbeat(re, suite.svr, r3) - mustPutStore(re, suite.svr, 13, metapb.StoreState_Up, metapb.NodeState_Serving, []*metapb.StoreLabel{}) - mustPutStore(re, suite.svr, 14, metapb.StoreState_Up, metapb.NodeState_Serving, []*metapb.StoreLabel{}) - mustPutStore(re, suite.svr, 15, metapb.StoreState_Up, metapb.NodeState_Serving, []*metapb.StoreLabel{}) - mustPutStore(re, suite.svr, 16, metapb.StoreState_Up, metapb.NodeState_Serving, []*metapb.StoreLabel{}) - body := fmt.Sprintf(`{"start_key":"%s", "end_key": "%s"}`, hex.EncodeToString([]byte("b1")), hex.EncodeToString([]byte("b3"))) - - err := tu.CheckPostJSON(testDialClient, fmt.Sprintf("%s/regions/scatter", suite.urlPrefix), []byte(body), tu.StatusOK(re)) - suite.NoError(err) - op1 := suite.svr.GetRaftCluster().GetOperatorController().GetOperator(601) - op2 := suite.svr.GetRaftCluster().GetOperatorController().GetOperator(602) - op3 := suite.svr.GetRaftCluster().GetOperatorController().GetOperator(603) - // At least one operator used to scatter region - suite.True(op1 != nil || op2 != nil || op3 != nil) - - body = `{"regions_id": [601, 602, 603]}` - err = tu.CheckPostJSON(testDialClient, fmt.Sprintf("%s/regions/scatter", suite.urlPrefix), []byte(body), tu.StatusOK(re)) - suite.NoError(err) -} - -func (suite *regionTestSuite) TestSplitRegions() { - re := suite.Require() - r1 := core.NewTestRegionInfo(601, 13, []byte("aaa"), []byte("ggg")) - r1.GetMeta().Peers = append(r1.GetMeta().Peers, &metapb.Peer{Id: 5, StoreId: 14}, &metapb.Peer{Id: 6, StoreId: 15}) - mustRegionHeartbeat(re, suite.svr, r1) - mustPutStore(re, suite.svr, 13, metapb.StoreState_Up, metapb.NodeState_Serving, []*metapb.StoreLabel{}) - newRegionID := uint64(11) - body := fmt.Sprintf(`{"retry_limit":%v, "split_keys": ["%s","%s","%s"]}`, 3, - hex.EncodeToString([]byte("bbb")), - hex.EncodeToString([]byte("ccc")), - hex.EncodeToString([]byte("ddd"))) - checkOpt := func(res []byte, code int, _ http.Header) { - s := &struct { - ProcessedPercentage int `json:"processed-percentage"` - NewRegionsID []uint64 `json:"regions-id"` - }{} - err := json.Unmarshal(res, s) - suite.NoError(err) - suite.Equal(100, s.ProcessedPercentage) - suite.Equal([]uint64{newRegionID}, s.NewRegionsID) - } - suite.NoError(failpoint.Enable("github.com/tikv/pd/pkg/schedule/handler/splitResponses", fmt.Sprintf("return(%v)", newRegionID))) - err := tu.CheckPostJSON(testDialClient, fmt.Sprintf("%s/regions/split", suite.urlPrefix), []byte(body), checkOpt) - suite.NoError(failpoint.Disable("github.com/tikv/pd/pkg/schedule/handler/splitResponses")) - suite.NoError(err) -} - func (suite *regionTestSuite) checkTopRegions(url string, regionIDs []uint64) { regions := &RegionsInfo{} err := tu.ReadGetJSON(suite.Require(), testDialClient, url, regions) @@ -652,133 +557,6 @@ func (suite *getRegionRangeHolesTestSuite) TestRegionRangeHoles() { }, *rangeHoles) } -type regionsReplicatedTestSuite struct { - suite.Suite - svr *server.Server - cleanup tu.CleanupFunc - urlPrefix string -} - -func TestRegionsReplicatedTestSuite(t *testing.T) { - suite.Run(t, new(regionsReplicatedTestSuite)) -} - -func (suite *regionsReplicatedTestSuite) SetupSuite() { - re := suite.Require() - suite.svr, suite.cleanup = mustNewServer(re) - server.MustWaitLeader(re, []*server.Server{suite.svr}) - - addr := suite.svr.GetAddr() - suite.urlPrefix = fmt.Sprintf("%s%s/api/v1", addr, apiPrefix) - - mustBootstrapCluster(re, suite.svr) -} - -func (suite *regionsReplicatedTestSuite) TearDownSuite() { - suite.cleanup() -} - -func (suite *regionsReplicatedTestSuite) TestCheckRegionsReplicated() { - re := suite.Require() - // enable placement rule - suite.NoError(tu.CheckPostJSON(testDialClient, suite.urlPrefix+"/config", []byte(`{"enable-placement-rules":"true"}`), tu.StatusOK(re))) - defer func() { - suite.NoError(tu.CheckPostJSON(testDialClient, suite.urlPrefix+"/config", []byte(`{"enable-placement-rules":"false"}`), tu.StatusOK(re))) - }() - - // add test region - r1 := core.NewTestRegionInfo(2, 1, []byte("a"), []byte("b")) - mustRegionHeartbeat(re, suite.svr, r1) - - // set the bundle - bundle := []placement.GroupBundle{ - { - ID: "5", - Index: 5, - Rules: []*placement.Rule{ - { - ID: "foo", Index: 1, Role: placement.Voter, Count: 1, - }, - }, - }, - } - - status := "" - - // invalid url - url := fmt.Sprintf(`%s/regions/replicated?startKey=%s&endKey=%s`, suite.urlPrefix, "_", "t") - err := tu.CheckGetJSON(testDialClient, url, nil, tu.Status(re, http.StatusBadRequest)) - suite.NoError(err) - - url = fmt.Sprintf(`%s/regions/replicated?startKey=%s&endKey=%s`, suite.urlPrefix, hex.EncodeToString(r1.GetStartKey()), "_") - err = tu.CheckGetJSON(testDialClient, url, nil, tu.Status(re, http.StatusBadRequest)) - suite.NoError(err) - - // correct test - url = fmt.Sprintf(`%s/regions/replicated?startKey=%s&endKey=%s`, suite.urlPrefix, hex.EncodeToString(r1.GetStartKey()), hex.EncodeToString(r1.GetEndKey())) - err = tu.CheckGetJSON(testDialClient, url, nil, tu.StatusOK(re)) - suite.NoError(err) - - // test one rule - data, err := json.Marshal(bundle) - suite.NoError(err) - err = tu.CheckPostJSON(testDialClient, suite.urlPrefix+"/config/placement-rule", data, tu.StatusOK(re)) - suite.NoError(err) - - err = tu.ReadGetJSON(re, testDialClient, url, &status) - suite.NoError(err) - suite.Equal("REPLICATED", status) - - suite.NoError(failpoint.Enable("github.com/tikv/pd/pkg/schedule/handler/mockPending", "return(true)")) - err = tu.ReadGetJSON(re, testDialClient, url, &status) - suite.NoError(err) - suite.Equal("PENDING", status) - suite.NoError(failpoint.Disable("github.com/tikv/pd/pkg/schedule/handler/mockPending")) - // test multiple rules - r1 = core.NewTestRegionInfo(2, 1, []byte("a"), []byte("b")) - r1.GetMeta().Peers = append(r1.GetMeta().Peers, &metapb.Peer{Id: 5, StoreId: 1}) - mustRegionHeartbeat(re, suite.svr, r1) - - bundle[0].Rules = append(bundle[0].Rules, &placement.Rule{ - ID: "bar", Index: 1, Role: placement.Voter, Count: 1, - }) - data, err = json.Marshal(bundle) - suite.NoError(err) - err = tu.CheckPostJSON(testDialClient, suite.urlPrefix+"/config/placement-rule", data, tu.StatusOK(re)) - suite.NoError(err) - - err = tu.ReadGetJSON(re, testDialClient, url, &status) - suite.NoError(err) - suite.Equal("REPLICATED", status) - - // test multiple bundles - bundle = append(bundle, placement.GroupBundle{ - ID: "6", - Index: 6, - Rules: []*placement.Rule{ - { - ID: "foo", Index: 1, Role: placement.Voter, Count: 2, - }, - }, - }) - data, err = json.Marshal(bundle) - suite.NoError(err) - err = tu.CheckPostJSON(testDialClient, suite.urlPrefix+"/config/placement-rule", data, tu.StatusOK(re)) - suite.NoError(err) - - err = tu.ReadGetJSON(re, testDialClient, url, &status) - suite.NoError(err) - suite.Equal("INPROGRESS", status) - - r1 = core.NewTestRegionInfo(2, 1, []byte("a"), []byte("b")) - r1.GetMeta().Peers = append(r1.GetMeta().Peers, &metapb.Peer{Id: 5, StoreId: 1}, &metapb.Peer{Id: 6, StoreId: 1}, &metapb.Peer{Id: 7, StoreId: 1}) - mustRegionHeartbeat(re, suite.svr, r1) - - err = tu.ReadGetJSON(re, testDialClient, url, &status) - suite.NoError(err) - suite.Equal("REPLICATED", status) -} - func TestRegionsInfoMarshal(t *testing.T) { re := require.New(t) regionWithNilPeer := core.NewRegionInfo(&metapb.Region{Id: 1}, &metapb.Peer{Id: 1}) diff --git a/tests/server/api/operator_test.go b/tests/server/api/operator_test.go index 14b8618f6a6..c27ebbe7ee8 100644 --- a/tests/server/api/operator_test.go +++ b/tests/server/api/operator_test.go @@ -477,9 +477,7 @@ func (suite *operatorTestSuite) checkTransferRegionWithPlacementRule(cluster *te suite.NoError(err) err = tu.CheckDelete(testDialClient, regionURL, tu.StatusOK(re)) } else { - // FIXME: we should check the delete result, which should be failed, - // but the delete operator may be success because the cluster create a new operator to remove ophan peer. - err = tu.CheckDelete(testDialClient, regionURL) + err = tu.CheckDelete(testDialClient, regionURL, tu.StatusNotOK(re)) } suite.NoError(err) } diff --git a/tests/server/api/region_test.go b/tests/server/api/region_test.go new file mode 100644 index 00000000000..452ef365a6d --- /dev/null +++ b/tests/server/api/region_test.go @@ -0,0 +1,287 @@ +// Copyright 2023 TiKV Project Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package api + +import ( + "encoding/hex" + "encoding/json" + "fmt" + "net/http" + "testing" + + "github.com/pingcap/failpoint" + "github.com/pingcap/kvproto/pkg/metapb" + "github.com/stretchr/testify/suite" + "github.com/tikv/pd/pkg/core" + "github.com/tikv/pd/pkg/schedule/placement" + tu "github.com/tikv/pd/pkg/utils/testutil" + "github.com/tikv/pd/server/config" + "github.com/tikv/pd/tests" +) + +type regionTestSuite struct { + suite.Suite +} + +func TestRegionTestSuite(t *testing.T) { + suite.Run(t, new(regionTestSuite)) +} +func (suite *regionTestSuite) TestSplitRegions() { + env := tests.NewSchedulingTestEnvironment(suite.T()) + env.RunTestInTwoModes(suite.checkSplitRegions) +} + +func (suite *regionTestSuite) checkSplitRegions(cluster *tests.TestCluster) { + leader := cluster.GetLeaderServer() + urlPrefix := leader.GetAddr() + "/pd/api/v1" + re := suite.Require() + r1 := core.NewTestRegionInfo(601, 13, []byte("aaa"), []byte("ggg")) + r1.GetMeta().Peers = append(r1.GetMeta().Peers, &metapb.Peer{Id: 5, StoreId: 14}, &metapb.Peer{Id: 6, StoreId: 15}) + tests.MustPutRegionInfo(re, cluster, r1) + s1 := &metapb.Store{ + Id: 13, + State: metapb.StoreState_Up, + NodeState: metapb.NodeState_Serving, + } + tests.MustPutStore(re, cluster, s1) + newRegionID := uint64(11) + body := fmt.Sprintf(`{"retry_limit":%v, "split_keys": ["%s","%s","%s"]}`, 3, + hex.EncodeToString([]byte("bbb")), + hex.EncodeToString([]byte("ccc")), + hex.EncodeToString([]byte("ddd"))) + checkOpt := func(res []byte, code int, _ http.Header) { + s := &struct { + ProcessedPercentage int `json:"processed-percentage"` + NewRegionsID []uint64 `json:"regions-id"` + }{} + err := json.Unmarshal(res, s) + suite.NoError(err) + suite.Equal(100, s.ProcessedPercentage) + suite.Equal([]uint64{newRegionID}, s.NewRegionsID) + } + suite.NoError(failpoint.Enable("github.com/tikv/pd/pkg/schedule/handler/splitResponses", fmt.Sprintf("return(%v)", newRegionID))) + err := tu.CheckPostJSON(testDialClient, fmt.Sprintf("%s/regions/split", urlPrefix), []byte(body), checkOpt) + suite.NoError(failpoint.Disable("github.com/tikv/pd/pkg/schedule/handler/splitResponses")) + suite.NoError(err) +} + +func (suite *regionTestSuite) TestAccelerateRegionsScheduleInRange() { + env := tests.NewSchedulingTestEnvironment(suite.T()) + env.RunTestInTwoModes(suite.checkAccelerateRegionsScheduleInRange) +} + +func (suite *regionTestSuite) checkAccelerateRegionsScheduleInRange(cluster *tests.TestCluster) { + leader := cluster.GetLeaderServer() + urlPrefix := leader.GetAddr() + "/pd/api/v1" + re := suite.Require() + r1 := core.NewTestRegionInfo(557, 13, []byte("a1"), []byte("a2")) + r2 := core.NewTestRegionInfo(558, 14, []byte("a2"), []byte("a3")) + r3 := core.NewTestRegionInfo(559, 15, []byte("a3"), []byte("a4")) + tests.MustPutRegionInfo(re, cluster, r1) + tests.MustPutRegionInfo(re, cluster, r2) + tests.MustPutRegionInfo(re, cluster, r3) + body := fmt.Sprintf(`{"start_key":"%s", "end_key": "%s"}`, hex.EncodeToString([]byte("a1")), hex.EncodeToString([]byte("a3"))) + + err := tu.CheckPostJSON(testDialClient, fmt.Sprintf("%s/regions/accelerate-schedule", urlPrefix), []byte(body), tu.StatusOK(re)) + suite.NoError(err) + idList := leader.GetRaftCluster().GetSuspectRegions() + if sche := cluster.GetSchedulingPrimaryServer(); sche != nil { + idList = sche.GetCluster().GetCoordinator().GetCheckerController().GetSuspectRegions() + } + suite.Len(idList, 2) +} + +func (suite *regionTestSuite) TestAccelerateRegionsScheduleInRanges() { + env := tests.NewSchedulingTestEnvironment(suite.T()) + env.RunTestInTwoModes(suite.checkAccelerateRegionsScheduleInRanges) +} + +func (suite *regionTestSuite) checkAccelerateRegionsScheduleInRanges(cluster *tests.TestCluster) { + leader := cluster.GetLeaderServer() + urlPrefix := leader.GetAddr() + "/pd/api/v1" + re := suite.Require() + r1 := core.NewTestRegionInfo(557, 13, []byte("a1"), []byte("a2")) + r2 := core.NewTestRegionInfo(558, 14, []byte("a2"), []byte("a3")) + r3 := core.NewTestRegionInfo(559, 15, []byte("a3"), []byte("a4")) + r4 := core.NewTestRegionInfo(560, 16, []byte("a4"), []byte("a5")) + r5 := core.NewTestRegionInfo(561, 17, []byte("a5"), []byte("a6")) + tests.MustPutRegionInfo(re, cluster, r1) + tests.MustPutRegionInfo(re, cluster, r2) + tests.MustPutRegionInfo(re, cluster, r3) + tests.MustPutRegionInfo(re, cluster, r4) + tests.MustPutRegionInfo(re, cluster, r5) + body := fmt.Sprintf(`[{"start_key":"%s", "end_key": "%s"}, {"start_key":"%s", "end_key": "%s"}]`, hex.EncodeToString([]byte("a1")), hex.EncodeToString([]byte("a3")), hex.EncodeToString([]byte("a4")), hex.EncodeToString([]byte("a6"))) + + err := tu.CheckPostJSON(testDialClient, fmt.Sprintf("%s/regions/accelerate-schedule/batch", urlPrefix), []byte(body), tu.StatusOK(re)) + suite.NoError(err) + idList := leader.GetRaftCluster().GetSuspectRegions() + if sche := cluster.GetSchedulingPrimaryServer(); sche != nil { + idList = sche.GetCluster().GetCoordinator().GetCheckerController().GetSuspectRegions() + } + suite.Len(idList, 4) +} + +func (suite *regionTestSuite) TestScatterRegions() { + env := tests.NewSchedulingTestEnvironment(suite.T()) + env.RunTestInTwoModes(suite.checkScatterRegions) +} + +func (suite *regionTestSuite) checkScatterRegions(cluster *tests.TestCluster) { + leader := cluster.GetLeaderServer() + urlPrefix := leader.GetAddr() + "/pd/api/v1" + re := suite.Require() + r1 := core.NewTestRegionInfo(601, 13, []byte("b1"), []byte("b2")) + r1.GetMeta().Peers = append(r1.GetMeta().Peers, &metapb.Peer{Id: 5, StoreId: 14}, &metapb.Peer{Id: 6, StoreId: 15}) + r2 := core.NewTestRegionInfo(602, 13, []byte("b2"), []byte("b3")) + r2.GetMeta().Peers = append(r2.GetMeta().Peers, &metapb.Peer{Id: 7, StoreId: 14}, &metapb.Peer{Id: 8, StoreId: 15}) + r3 := core.NewTestRegionInfo(603, 13, []byte("b4"), []byte("b4")) + r3.GetMeta().Peers = append(r3.GetMeta().Peers, &metapb.Peer{Id: 9, StoreId: 14}, &metapb.Peer{Id: 10, StoreId: 15}) + tests.MustPutRegionInfo(re, cluster, r1) + tests.MustPutRegionInfo(re, cluster, r2) + tests.MustPutRegionInfo(re, cluster, r3) + for i := 13; i <= 16; i++ { + s1 := &metapb.Store{ + Id: uint64(i), + State: metapb.StoreState_Up, + NodeState: metapb.NodeState_Serving, + } + tests.MustPutStore(re, cluster, s1) + } + body := fmt.Sprintf(`{"start_key":"%s", "end_key": "%s"}`, hex.EncodeToString([]byte("b1")), hex.EncodeToString([]byte("b3"))) + + err := tu.CheckPostJSON(testDialClient, fmt.Sprintf("%s/regions/scatter", urlPrefix), []byte(body), tu.StatusOK(re)) + suite.NoError(err) + oc := leader.GetRaftCluster().GetOperatorController() + if sche := cluster.GetSchedulingPrimaryServer(); sche != nil { + oc = sche.GetCoordinator().GetOperatorController() + } + + op1 := oc.GetOperator(601) + op2 := oc.GetOperator(602) + op3 := oc.GetOperator(603) + // At least one operator used to scatter region + suite.True(op1 != nil || op2 != nil || op3 != nil) + + body = `{"regions_id": [601, 602, 603]}` + err = tu.CheckPostJSON(testDialClient, fmt.Sprintf("%s/regions/scatter", urlPrefix), []byte(body), tu.StatusOK(re)) + suite.NoError(err) +} + +func (suite *regionTestSuite) TestCheckRegionsReplicated() { + env := tests.NewSchedulingTestEnvironment(suite.T(), + func(conf *config.Config, serverName string) { + conf.Replication.EnablePlacementRules = true + }) + // FIXME: enable this test in two modes. + env.RunTestInPDMode(suite.checkRegionsReplicated) +} + +func (suite *regionTestSuite) checkRegionsReplicated(cluster *tests.TestCluster) { + leader := cluster.GetLeaderServer() + urlPrefix := leader.GetAddr() + "/pd/api/v1" + re := suite.Require() + + // add test region + r1 := core.NewTestRegionInfo(2, 1, []byte("a"), []byte("b")) + tests.MustPutRegionInfo(re, cluster, r1) + + // set the bundle + bundle := []placement.GroupBundle{ + { + ID: "5", + Index: 5, + Rules: []*placement.Rule{ + { + ID: "foo", Index: 1, Role: placement.Voter, Count: 1, + }, + }, + }, + } + + status := "" + + // invalid url + url := fmt.Sprintf(`%s/regions/replicated?startKey=%s&endKey=%s`, urlPrefix, "_", "t") + err := tu.CheckGetJSON(testDialClient, url, nil, tu.Status(re, http.StatusBadRequest)) + suite.NoError(err) + + url = fmt.Sprintf(`%s/regions/replicated?startKey=%s&endKey=%s`, urlPrefix, hex.EncodeToString(r1.GetStartKey()), "_") + err = tu.CheckGetJSON(testDialClient, url, nil, tu.Status(re, http.StatusBadRequest)) + suite.NoError(err) + + // correct test + url = fmt.Sprintf(`%s/regions/replicated?startKey=%s&endKey=%s`, urlPrefix, hex.EncodeToString(r1.GetStartKey()), hex.EncodeToString(r1.GetEndKey())) + err = tu.CheckGetJSON(testDialClient, url, nil, tu.StatusOK(re)) + suite.NoError(err) + + // test one rule + data, err := json.Marshal(bundle) + suite.NoError(err) + err = tu.CheckPostJSON(testDialClient, urlPrefix+"/config/placement-rule", data, tu.StatusOK(re)) + suite.NoError(err) + + err = tu.ReadGetJSON(re, testDialClient, url, &status) + suite.NoError(err) + suite.Equal("REPLICATED", status) + + suite.NoError(failpoint.Enable("github.com/tikv/pd/pkg/schedule/handler/mockPending", "return(true)")) + err = tu.ReadGetJSON(re, testDialClient, url, &status) + suite.NoError(err) + suite.Equal("PENDING", status) + suite.NoError(failpoint.Disable("github.com/tikv/pd/pkg/schedule/handler/mockPending")) + // test multiple rules + r1 = core.NewTestRegionInfo(2, 1, []byte("a"), []byte("b")) + r1.GetMeta().Peers = append(r1.GetMeta().Peers, &metapb.Peer{Id: 5, StoreId: 1}) + tests.MustPutRegionInfo(re, cluster, r1) + + bundle[0].Rules = append(bundle[0].Rules, &placement.Rule{ + ID: "bar", Index: 1, Role: placement.Voter, Count: 1, + }) + data, err = json.Marshal(bundle) + suite.NoError(err) + err = tu.CheckPostJSON(testDialClient, urlPrefix+"/config/placement-rule", data, tu.StatusOK(re)) + suite.NoError(err) + + err = tu.ReadGetJSON(re, testDialClient, url, &status) + suite.NoError(err) + suite.Equal("REPLICATED", status) + + // test multiple bundles + bundle = append(bundle, placement.GroupBundle{ + ID: "6", + Index: 6, + Rules: []*placement.Rule{ + { + ID: "foo", Index: 1, Role: placement.Voter, Count: 2, + }, + }, + }) + data, err = json.Marshal(bundle) + suite.NoError(err) + err = tu.CheckPostJSON(testDialClient, urlPrefix+"/config/placement-rule", data, tu.StatusOK(re)) + suite.NoError(err) + + err = tu.ReadGetJSON(re, testDialClient, url, &status) + suite.NoError(err) + suite.Equal("INPROGRESS", status) + + r1 = core.NewTestRegionInfo(2, 1, []byte("a"), []byte("b")) + r1.GetMeta().Peers = append(r1.GetMeta().Peers, &metapb.Peer{Id: 5, StoreId: 1}, &metapb.Peer{Id: 6, StoreId: 1}, &metapb.Peer{Id: 7, StoreId: 1}) + tests.MustPutRegionInfo(re, cluster, r1) + + err = tu.ReadGetJSON(re, testDialClient, url, &status) + suite.NoError(err) + suite.Equal("REPLICATED", status) +}