Skip to content

Commit

Permalink
api: optional return operators as JSON object (tikv#255)
Browse files Browse the repository at this point in the history
* api: optional return operators as JSON object (tikv#7705)

close tikv#7704

api: optional return operators as JSON object

Signed-off-by: Ping Yu <yuping@pingcap.com>

Co-authored-by: ti-chi-bot[bot] <108142056+ti-chi-bot[bot]@users.noreply.github.com>
Signed-off-by: Ping Yu <yuping@pingcap.com>

* fix

Signed-off-by: Ping Yu <yuping@pingcap.com>

---------

Signed-off-by: Ping Yu <yuping@pingcap.com>
Co-authored-by: ti-chi-bot[bot] <108142056+ti-chi-bot[bot]@users.noreply.github.com>
  • Loading branch information
pingyu and ti-chi-bot[bot] authored Jan 17, 2024
1 parent f468aa6 commit 19a7726
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 5 deletions.
14 changes: 12 additions & 2 deletions pkg/mcs/scheduling/server/apis/v1/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,8 @@ func getOperatorByRegion(c *gin.Context) {

// @Tags operators
// @Summary List operators.
// @Param kind query string false "Specify the operator kind." Enums(admin, leader, region, waiting)
// @Param kind query string false "Specify the operator kind." Enums(admin, leader, region, waiting)
// @Param object query bool false "Whether to return as JSON object."
// @Produce json
// @Success 200 {array} operator.Operator
// @Failure 500 {string} string "PD server failed to proceed the request."
Expand All @@ -336,6 +337,7 @@ func getOperators(c *gin.Context) {
)

kinds := c.QueryArray("kind")
_, objectFlag := c.GetQuery("object")
if len(kinds) == 0 {
results, err = handler.GetOperators()
} else {
Expand All @@ -346,7 +348,15 @@ func getOperators(c *gin.Context) {
c.String(http.StatusInternalServerError, err.Error())
return
}
c.IndentedJSON(http.StatusOK, results)
if objectFlag {
objResults := make([]*operator.OpObject, len(results))
for i, op := range results {
objResults[i] = op.ToJSONObject()
}
c.IndentedJSON(http.StatusOK, objResults)
} else {
c.IndentedJSON(http.StatusOK, results)
}
}

// @Tags operator
Expand Down
20 changes: 18 additions & 2 deletions pkg/schedule/operator/operator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -532,8 +532,8 @@ func (suite *operatorTestSuite) TestRecord() {
func (suite *operatorTestSuite) TestToJSONObject() {
steps := []OpStep{
AddPeer{ToStore: 1, PeerID: 1},
TransferLeader{FromStore: 2, ToStore: 1},
RemovePeer{FromStore: 2},
TransferLeader{FromStore: 3, ToStore: 1},
RemovePeer{FromStore: 3},
}
op := suite.newTestOperator(101, OpLeader|OpRegion, steps...)
op.Start()
Expand All @@ -544,4 +544,20 @@ func (suite *operatorTestSuite) TestToJSONObject() {
suite.Equal(OpLeader|OpRegion, obj.Kind)
suite.Equal("12m0s", obj.Timeout)
suite.Equal(STARTED, obj.Status)

// Test SUCCESS status.
region := suite.newTestRegion(1, 1, [2]uint64{1, 1}, [2]uint64{2, 2})
suite.Nil(op.Check(region))
suite.Equal(SUCCESS, op.Status())
obj = op.ToJSONObject()
suite.Equal(SUCCESS, obj.Status)

// Test TIMEOUT status.
steps = []OpStep{TransferLeader{FromStore: 2, ToStore: 1}}
op = suite.newTestOperator(1, OpLeader, steps...)
op.Start()
op.SetStatusReachTime(STARTED, op.GetStartTime().Add(-FastStepWaitTime-time.Second))
suite.True(op.CheckTimeout())
obj = op.ToJSONObject()
suite.Equal(TIMEOUT, obj.Status)
}
3 changes: 2 additions & 1 deletion server/api/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ func (h *operatorHandler) GetOperatorsByRegion(w http.ResponseWriter, r *http.Re

// @Tags operator
// @Summary List pending operators.
// @Param kind query string false "Specify the operator kind." Enums(admin, leader, region)
// @Param kind query string false "Specify the operator kind." Enums(admin, leader, region)
// @Param object query bool false "Whether to return as JSON object."
// @Produce json
// @Success 200 {array} operator.Operator
// @Failure 500 {string} string "PD server failed to proceed the request."
Expand Down
102 changes: 102 additions & 0 deletions tests/server/api/operator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"errors"
"fmt"
"net/http"
"sort"
"strconv"
"strings"
"testing"
Expand Down Expand Up @@ -483,6 +484,107 @@ func (suite *operatorTestSuite) checkTransferRegionWithPlacementRule(cluster *te
}
}

func (suite *operatorTestSuite) TestGetOperatorsAsObject() {
// use a new environment to avoid being affected by other tests
env := tests.NewSchedulingTestEnvironment(suite.T(),
func(conf *config.Config, serverName string) {
conf.Replication.MaxReplicas = 1
})
env.RunTestInTwoModes(suite.checkGetOperatorsAsObject)
}

func (suite *operatorTestSuite) checkGetOperatorsAsObject(cluster *tests.TestCluster) {
re := suite.Require()
suite.pauseRuleChecker(cluster)
stores := []*metapb.Store{
{
Id: 1,
State: metapb.StoreState_Up,
NodeState: metapb.NodeState_Serving,
LastHeartbeat: time.Now().UnixNano(),
},
{
Id: 2,
State: metapb.StoreState_Up,
NodeState: metapb.NodeState_Serving,
LastHeartbeat: time.Now().UnixNano(),
},
{
Id: 3,
State: metapb.StoreState_Up,
NodeState: metapb.NodeState_Serving,
LastHeartbeat: time.Now().UnixNano(),
},
}

for _, store := range stores {
tests.MustPutStore(re, cluster, store)
}

urlPrefix := fmt.Sprintf("%s/pd/api/v1", cluster.GetLeaderServer().GetAddr())
objURL := fmt.Sprintf("%s/operators?object=1", urlPrefix)
resp := make([]operator.OpObject, 0)

// No operator.
err := tu.ReadGetJSON(re, testDialClient, objURL, &resp)
re.NoError(err)
re.Empty(resp)

// Merge operator.
r1 := core.NewTestRegionInfo(10, 1, []byte(""), []byte("b"), core.SetWrittenBytes(1000), core.SetReadBytes(1000), core.SetRegionConfVer(1), core.SetRegionVersion(1))
tests.MustPutRegionInfo(re, cluster, r1)
r2 := core.NewTestRegionInfo(20, 1, []byte("b"), []byte("c"), core.SetWrittenBytes(2000), core.SetReadBytes(0), core.SetRegionConfVer(2), core.SetRegionVersion(3))
tests.MustPutRegionInfo(re, cluster, r2)
r3 := core.NewTestRegionInfo(30, 1, []byte("c"), []byte("d"), core.SetWrittenBytes(500), core.SetReadBytes(800), core.SetRegionConfVer(3), core.SetRegionVersion(2))
tests.MustPutRegionInfo(re, cluster, r3)

err = tu.CheckPostJSON(testDialClient, fmt.Sprintf("%s/operators", urlPrefix), []byte(`{"name":"merge-region", "source_region_id": 10, "target_region_id": 20}`), tu.StatusOK(re))
re.NoError(err)
err = tu.ReadGetJSON(re, testDialClient, objURL, &resp)
re.NoError(err)
re.Len(resp, 2)
less := func(i, j int) bool {
return resp[i].RegionID < resp[j].RegionID
}
sort.Slice(resp, less)
re.Equal(uint64(10), resp[0].RegionID)
re.Equal("admin-merge-region", resp[0].Desc)
re.Equal("merge: region 10 to 20", resp[0].Brief)
re.Equal("10m0s", resp[0].Timeout)
re.Equal(&metapb.RegionEpoch{
ConfVer: 1,
Version: 1,
}, resp[0].RegionEpoch)
re.Equal(operator.OpAdmin|operator.OpMerge, resp[0].Kind)
re.Truef(resp[0].Status == operator.CREATED || resp[0].Status == operator.STARTED, "unexpected status %s", resp[0].Status)
re.Equal(uint64(20), resp[1].RegionID)
re.Equal("admin-merge-region", resp[1].Desc)

// Add peer operator.
peer1 := &metapb.Peer{Id: 100, StoreId: 1}
peer2 := &metapb.Peer{Id: 200, StoreId: 2}
region := &metapb.Region{
Id: 40,
Peers: []*metapb.Peer{peer1, peer2},
RegionEpoch: &metapb.RegionEpoch{
ConfVer: 1,
Version: 1,
},
StartKey: []byte("d"),
EndKey: []byte(""),
}
regionInfo := core.NewRegionInfo(region, peer1)
tests.MustPutRegionInfo(re, cluster, regionInfo)
err = tu.CheckPostJSON(testDialClient, fmt.Sprintf("%s/operators", urlPrefix), []byte(`{"name":"add-peer", "region_id": 40, "store_id": 3}`), tu.StatusOK(re))
re.NoError(err)
err = tu.ReadGetJSON(re, testDialClient, objURL, &resp)
re.NoError(err)
re.Len(resp, 3)
sort.Slice(resp, less)
re.Equal(uint64(40), resp[2].RegionID)
re.Equal("admin-add-peer", resp[2].Desc)
}

// pauseRuleChecker will pause rule checker to avoid unexpected operator.
func (suite *operatorTestSuite) pauseRuleChecker(cluster *tests.TestCluster) {
re := suite.Require()
Expand Down

0 comments on commit 19a7726

Please sign in to comment.