Skip to content

Commit d08fd48

Browse files
ctl: support to query the members and primary by using pdctl (tikv#9564) (tikv#10019)
close tikv#9567 Signed-off-by: ti-chi-bot <ti-community-prow-bot@tidb.io> Signed-off-by: 童剑 <1045931706@qq.com> Co-authored-by: tongjian <1045931706@qq.com> Co-authored-by: 童剑 <1045931706@qq.com>
1 parent dff8871 commit d08fd48

File tree

6 files changed

+345
-116
lines changed

6 files changed

+345
-116
lines changed

tests/cluster.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import (
3434
"github.com/tikv/pd/pkg/id"
3535
"github.com/tikv/pd/pkg/keyspace"
3636
scheduling "github.com/tikv/pd/pkg/mcs/scheduling/server"
37+
tsoServer "github.com/tikv/pd/pkg/mcs/tso/server"
3738
"github.com/tikv/pd/pkg/mcs/utils/constant"
3839
"github.com/tikv/pd/pkg/schedule/schedulers"
3940
"github.com/tikv/pd/pkg/swaggerserver"
@@ -458,6 +459,7 @@ type TestCluster struct {
458459
pool map[uint64]struct{}
459460
}
460461
schedulingCluster *TestSchedulingCluster
462+
tsoCluster *TestTSOCluster
461463
}
462464

463465
// ConfigOption is used to define customize settings in test.
@@ -851,6 +853,9 @@ func (c *TestCluster) Destroy() {
851853
if c.schedulingCluster != nil {
852854
c.schedulingCluster.Destroy()
853855
}
856+
if c.tsoCluster != nil {
857+
c.tsoCluster.Destroy()
858+
}
854859
}
855860

856861
// CheckClusterDCLocation will force the cluster to do the dc-location check in order to speed up the test.
@@ -885,11 +890,24 @@ func (c *TestCluster) GetSchedulingPrimaryServer() *scheduling.Server {
885890
return c.schedulingCluster.GetPrimaryServer()
886891
}
887892

893+
// GetDefaultTSOPrimaryServer returns the primary TSO server for the default keyspace.
894+
func (c *TestCluster) GetDefaultTSOPrimaryServer() *tsoServer.Server {
895+
if c.tsoCluster == nil {
896+
return nil
897+
}
898+
return c.tsoCluster.GetPrimaryServer(constant.DefaultKeyspaceID, constant.DefaultKeyspaceGroupID)
899+
}
900+
888901
// SetSchedulingCluster sets the scheduling cluster.
889902
func (c *TestCluster) SetSchedulingCluster(cluster *TestSchedulingCluster) {
890903
c.schedulingCluster = cluster
891904
}
892905

906+
// SetTSOCluster sets the TSO cluster.
907+
func (c *TestCluster) SetTSOCluster(cluster *TestTSOCluster) {
908+
c.tsoCluster = cluster
909+
}
910+
893911
// WaitOp represent the wait configuration
894912
type WaitOp struct {
895913
retryTimes int
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// Copyright 2025 TiKV Project Authors.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package command
16+
17+
import (
18+
"fmt"
19+
"net/http"
20+
21+
"github.com/spf13/cobra"
22+
)
23+
24+
var (
25+
msPrimaryPrefix = "/pd/api/v2/ms/primary/%s"
26+
msMembersPrefix = "/pd/api/v2/ms/members/%s"
27+
)
28+
29+
// NewMicroServicesCommand return a microservice subcommand of rootCmd
30+
func NewMicroServicesCommand() *cobra.Command {
31+
m := &cobra.Command{
32+
Use: "microservice <tso|scheduling>",
33+
Short: "microservice commands",
34+
}
35+
m.AddCommand(newMSTsoCommand())
36+
m.AddCommand(newMSSchedulerCommand())
37+
return m
38+
}
39+
40+
func newMSTsoCommand() *cobra.Command {
41+
d := &cobra.Command{
42+
Use: "tso <primary|members>",
43+
Short: "tso microservice commands",
44+
}
45+
d.AddCommand(&cobra.Command{
46+
Use: "primary",
47+
Short: "show the tso primary status",
48+
Run: getPrimaryCommandFunc,
49+
})
50+
d.AddCommand(&cobra.Command{
51+
Use: "members",
52+
Short: "show the tso members status",
53+
Run: getMembersCommandFunc,
54+
})
55+
return d
56+
}
57+
58+
func newMSSchedulerCommand() *cobra.Command {
59+
c := &cobra.Command{
60+
Use: "scheduling <primary|members>",
61+
Short: "scheduling microservice commands",
62+
}
63+
c.AddCommand(&cobra.Command{
64+
Use: "primary",
65+
Short: "show the scheduling primary member status",
66+
Run: getPrimaryCommandFunc,
67+
})
68+
c.AddCommand(&cobra.Command{
69+
Use: "members",
70+
Short: "show the scheduling members status",
71+
Run: getMembersCommandFunc,
72+
})
73+
return c
74+
}
75+
76+
func getMembersCommandFunc(cmd *cobra.Command, _ []string) {
77+
parent := cmd.Parent().Name()
78+
uri := fmt.Sprintf(msMembersPrefix, parent)
79+
r, err := doRequest(cmd, uri, http.MethodGet, http.Header{})
80+
if err != nil {
81+
cmd.Printf("Failed to get the %s microservice members: %s\n", parent, err)
82+
return
83+
}
84+
cmd.Println(r)
85+
}
86+
87+
func getPrimaryCommandFunc(cmd *cobra.Command, _ []string) {
88+
parent := cmd.Parent().Name()
89+
uri := fmt.Sprintf(msPrimaryPrefix, parent)
90+
r, err := doRequest(cmd, uri, http.MethodGet, http.Header{})
91+
if err != nil {
92+
cmd.Printf("Failed to get the %s microservice primary: %s\n", parent, err)
93+
return
94+
}
95+
cmd.Println(r)
96+
}

tools/pd-ctl/pdctl/ctl.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ func GetRootCmd() *cobra.Command {
7373
command.NewKeyspaceGroupCommand(),
7474
command.NewKeyspaceCommand(),
7575
command.NewResourceManagerCommand(),
76+
command.NewMicroServicesCommand(),
7677
)
7778

7879
return rootCmd

tools/pd-ctl/tests/helper.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package tests
1616

1717
import (
1818
"bytes"
19+
"encoding/json"
1920
"sort"
2021

2122
"github.com/spf13/cobra"
@@ -91,3 +92,14 @@ func CheckRegionsInfoWithoutSort(re *require.Assertions, output *response.Region
9192
CheckRegionInfo(re, &got[i], region)
9293
}
9394
}
95+
96+
// MustExec is a helper function to execute a command and unmarshal the output into the provided variable.
97+
func MustExec(re *require.Assertions, cmd *cobra.Command, args []string, v any) string {
98+
output, err := ExecuteCommand(cmd, args...)
99+
re.NoError(err)
100+
if v == nil {
101+
return string(output)
102+
}
103+
re.NoError(json.Unmarshal(output, v), string(output))
104+
return ""
105+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
// Copyright 2025 TiKV Project Authors.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package microservice_test
16+
17+
import (
18+
"context"
19+
"strings"
20+
"testing"
21+
22+
"github.com/stretchr/testify/suite"
23+
24+
mcs "github.com/tikv/pd/pkg/mcs/utils/constant"
25+
"github.com/tikv/pd/pkg/utils/testutil"
26+
pdTests "github.com/tikv/pd/tests"
27+
ctl "github.com/tikv/pd/tools/pd-ctl/pdctl"
28+
"github.com/tikv/pd/tools/pd-ctl/tests"
29+
)
30+
31+
// microServiceSuite is a test suite for microservice related tests.
32+
type microServiceSuite struct {
33+
suite.Suite
34+
cluster *pdTests.TestCluster
35+
cancels []testutil.CleanupFunc
36+
}
37+
38+
func TestMicroServiceSuite(t *testing.T) {
39+
suite.Run(t, new(microServiceSuite))
40+
}
41+
42+
func (suite *microServiceSuite) SetupSuite() {
43+
suite.startCluster()
44+
}
45+
46+
func (suite *microServiceSuite) TearDownSuite() {
47+
for _, fn := range suite.cancels {
48+
fn()
49+
}
50+
suite.cluster.Destroy()
51+
}
52+
53+
func (suite *microServiceSuite) TestMicroService() {
54+
cluster := suite.cluster
55+
re := suite.Require()
56+
cmd := ctl.GetRootCmd()
57+
pdAddr := cluster.GetConfig().GetClientURL()
58+
if primaryServer := cluster.GetSchedulingPrimaryServer(); primaryServer != nil {
59+
address := primaryServer.GetAddr()
60+
res := tests.MustExec(re, cmd, []string{"-u", pdAddr, "microservice", "scheduling", "primary"}, nil)
61+
primaryAddress := strings.Trim(res, "\"\n")
62+
suite.Equal(address, primaryAddress)
63+
64+
v := make([]any, 0)
65+
tests.MustExec(re, cmd, []string{"-u", pdAddr, "microservice", "scheduling", "members"}, &v)
66+
re.Len(v, 2)
67+
}
68+
if primaryServer := cluster.GetDefaultTSOPrimaryServer(); primaryServer != nil {
69+
address := primaryServer.GetAddr()
70+
res := tests.MustExec(re, cmd, []string{"-u", pdAddr, "microservice", "tso", "primary"}, nil)
71+
primaryAddress := strings.Trim(res, "\"\n")
72+
suite.Equal(address, primaryAddress)
73+
74+
v := make([]any, 0)
75+
tests.MustExec(re, cmd, []string{"-u", pdAddr, "microservice", "tso", "members"}, &v)
76+
re.Len(v, 2)
77+
}
78+
}
79+
80+
func (suite *microServiceSuite) startCluster() {
81+
re := suite.Require()
82+
ctx, cancel := context.WithCancel(context.Background())
83+
suite.cancels = append(suite.cancels, func() {
84+
cancel()
85+
})
86+
87+
cluster, err := pdTests.NewTestAPICluster(ctx, 1)
88+
re.NoError(err)
89+
err = cluster.RunInitialServers()
90+
re.NoError(err)
91+
re.NotEmpty(cluster.WaitLeader())
92+
leaderServer := cluster.GetServer(cluster.GetLeader())
93+
re.NoError(leaderServer.BootstrapCluster())
94+
leaderServer.GetRaftCluster().SetPrepared()
95+
// start scheduling cluster
96+
tc, err := pdTests.NewTestSchedulingCluster(ctx, 2, cluster.GetConfig().GetClientURL())
97+
re.NoError(err)
98+
tc.WaitForPrimaryServing(re)
99+
tc.GetPrimaryServer().GetCluster().SetPrepared()
100+
cluster.SetSchedulingCluster(tc)
101+
testutil.Eventually(re, func() bool {
102+
return cluster.GetLeaderServer().GetServer().IsServiceIndependent(mcs.SchedulingServiceName)
103+
})
104+
// start tso cluster
105+
ts, err := pdTests.NewTestTSOCluster(ctx, 2, leaderServer.GetAddr())
106+
ts.WaitForDefaultPrimaryServing(re)
107+
re.NoError(err)
108+
cluster.SetTSOCluster(ts)
109+
suite.cluster = cluster
110+
}

0 commit comments

Comments
 (0)