From 4e7bf41169369baf1784a9fe8f4b7c9211e48613 Mon Sep 17 00:00:00 2001 From: lhy1024 Date: Wed, 18 Dec 2024 14:05:26 +0800 Subject: [PATCH] move to api v2 Signed-off-by: lhy1024 --- server/api/health.go | 34 ----------- server/api/health_test.go | 39 ------------ server/api/router.go | 1 - server/apiv2/handlers/ready.go | 65 ++++++++++++++++++++ server/apiv2/router.go | 1 + tests/server/apiv2/handlers/ready_test.go | 72 +++++++++++++++++++++++ tests/server/apiv2/handlers/testutil.go | 1 + 7 files changed, 139 insertions(+), 74 deletions(-) create mode 100644 server/apiv2/handlers/ready.go create mode 100644 tests/server/apiv2/handlers/ready_test.go diff --git a/server/api/health.go b/server/api/health.go index 227edb634a4..dcfc21175c3 100644 --- a/server/api/health.go +++ b/server/api/health.go @@ -19,7 +19,6 @@ import ( "github.com/unrolled/render" - "github.com/tikv/pd/pkg/storage" "github.com/tikv/pd/server" "github.com/tikv/pd/server/cluster" ) @@ -78,36 +77,3 @@ func (h *healthHandler) GetHealthStatus(w http.ResponseWriter, _ *http.Request) // @Summary Ping PD servers. // @Router /ping [get] func (*healthHandler) Ping(http.ResponseWriter, *http.Request) {} - -// ReadyStatus reflects the cluster's ready status. -// NOTE: This type is exported by HTTP API. Please pay more attention when modifying it. -type ReadyStatus struct { - RegionLoaded bool `json:"region_loaded"` -} - -// @Summary It will return whether pd follower is ready to became leader. -// @Router /ready [get] -// @Param verbose query bool false "Whether to return details." -// @Success 200 -// @Failure 500 -func (h *healthHandler) Ready(w http.ResponseWriter, r *http.Request) { - s := h.svr.GetStorage() - regionLoaded := storage.AreRegionsLoaded(s) - if regionLoaded { - w.WriteHeader(http.StatusOK) - } else { - w.WriteHeader(http.StatusInternalServerError) - } - - if _, ok := r.URL.Query()["verbose"]; !ok { - return - } - resp := &ReadyStatus{ - RegionLoaded: regionLoaded, - } - if regionLoaded { - h.rd.JSON(w, http.StatusOK, resp) - } else { - h.rd.JSON(w, http.StatusInternalServerError, resp) - } -} diff --git a/server/api/health_test.go b/server/api/health_test.go index 387c7c65da1..1720b75b69c 100644 --- a/server/api/health_test.go +++ b/server/api/health_test.go @@ -17,15 +17,11 @@ package api import ( "encoding/json" "io" - "net/http" "strings" "testing" "github.com/stretchr/testify/require" - "github.com/pingcap/failpoint" - - tu "github.com/tikv/pd/pkg/utils/testutil" "github.com/tikv/pd/server" "github.com/tikv/pd/server/config" ) @@ -73,38 +69,3 @@ func TestHealthSlice(t *testing.T) { re.NoError(err) checkSliceResponse(re, buf, cfgs, follower.GetConfig().Name) } - -func TestReady(t *testing.T) { - re := require.New(t) - _, svrs, clean := mustNewCluster(re, 1) - defer clean() - mustBootstrapCluster(re, svrs[0]) - url := svrs[0].GetConfig().ClientUrls + apiPrefix + "/api/v1/ready" - failpoint.Enable("github.com/tikv/pd/pkg/storage/loadRegionSlow", `return()`) - checkReady(re, url, false) - failpoint.Disable("github.com/tikv/pd/pkg/storage/loadRegionSlow") - checkReady(re, url, true) -} - -func checkReady(re *require.Assertions, url string, isReady bool) { - expectCode := http.StatusOK - if !isReady { - expectCode = http.StatusInternalServerError - } - resp, err := testDialClient.Get(url) - re.NoError(err) - defer resp.Body.Close() - buf, err := io.ReadAll(resp.Body) - re.NoError(err) - re.Empty(buf) - re.Equal(expectCode, resp.StatusCode) - r := &ReadyStatus{} - if isReady { - r.RegionLoaded = true - } - data, err := json.Marshal(r) - re.NoError(err) - err = tu.CheckGetJSON(testDialClient, url+"?verbose", data, - tu.Status(re, expectCode)) - re.NoError(err) -} diff --git a/server/api/router.go b/server/api/router.go index 9fdd6b0f56f..fd999211542 100644 --- a/server/api/router.go +++ b/server/api/router.go @@ -333,7 +333,6 @@ func createRouter(prefix string, svr *server.Server) *mux.Router { healthHandler := newHealthHandler(svr, rd) registerFunc(apiRouter, "/health", healthHandler.GetHealthStatus, setMethods(http.MethodGet), setAuditBackend(prometheus)) registerFunc(apiRouter, "/ping", healthHandler.Ping, setMethods(http.MethodGet), setAuditBackend(prometheus)) - registerFunc(apiRouter, "/ready", healthHandler.Ready, setMethods(http.MethodGet), setAuditBackend(prometheus)) // metric query use to query metric data, the protocol is compatible with prometheus. registerFunc(apiRouter, "/metric/query", newqueryMetric(svr).queryMetric, setMethods(http.MethodGet, http.MethodPost), setAuditBackend(prometheus)) diff --git a/server/apiv2/handlers/ready.go b/server/apiv2/handlers/ready.go new file mode 100644 index 00000000000..75cf301f4e7 --- /dev/null +++ b/server/apiv2/handlers/ready.go @@ -0,0 +1,65 @@ +// Copyright 2024 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 handlers + +import ( + "net/http" + + "github.com/gin-gonic/gin" + + "github.com/tikv/pd/pkg/storage" + "github.com/tikv/pd/server" + "github.com/tikv/pd/server/apiv2/middlewares" +) + +// RegisterMicroService registers ready group handlers to the server. +func RegisterReadyRouter(r *gin.RouterGroup) { + router := r.Group("ready") + router.GET("", Ready) +} + +// ReadyStatus reflects the cluster's ready status. +// NOTE: This type is exported by HTTP API. Please pay more attention when modifying it. +type ReadyStatus struct { + RegionLoaded bool `json:"region_loaded"` +} + +// @Summary It will return whether pd follower is ready to became leader. +// @Router /ready [get] +// @Param verbose query bool false "Whether to return details." +// @Success 200 +// @Failure 500 +func Ready(c *gin.Context) { + svr := c.MustGet(middlewares.ServerContextKey).(*server.Server) + s := svr.GetStorage() + regionLoaded := storage.AreRegionsLoaded(s) + if regionLoaded { + c.Status(http.StatusOK) + } else { + c.Status(http.StatusInternalServerError) + } + + if _, ok := c.GetQuery("verbose"); !ok { + return + } + resp := &ReadyStatus{ + RegionLoaded: regionLoaded, + } + if regionLoaded { + c.IndentedJSON(http.StatusOK, resp) + } else { + c.AbortWithStatusJSON(http.StatusInternalServerError, resp) + } +} diff --git a/server/apiv2/router.go b/server/apiv2/router.go index 612ba8932ff..65f5293a4a6 100644 --- a/server/apiv2/router.go +++ b/server/apiv2/router.go @@ -56,5 +56,6 @@ func NewV2Handler(_ context.Context, svr *server.Server) (http.Handler, apiutil. handlers.RegisterKeyspace(root) handlers.RegisterTSOKeyspaceGroup(root) handlers.RegisterMicroService(root) + handlers.RegisterReadyRouter(root) return router, group, nil } diff --git a/tests/server/apiv2/handlers/ready_test.go b/tests/server/apiv2/handlers/ready_test.go new file mode 100644 index 00000000000..111fc06784d --- /dev/null +++ b/tests/server/apiv2/handlers/ready_test.go @@ -0,0 +1,72 @@ +// Copyright 2024 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 handlers + +import ( + "context" + "encoding/json" + "io" + "net/http" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/pingcap/failpoint" + + tu "github.com/tikv/pd/pkg/utils/testutil" + "github.com/tikv/pd/server/apiv2/handlers" + "github.com/tikv/pd/tests" +) + +func TestReady(t *testing.T) { + re := require.New(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + cluster, err := tests.NewTestCluster(ctx, 1) + re.NoError(err) + defer cluster.Destroy() + re.NoError(cluster.RunInitialServers()) + re.NotEmpty(cluster.WaitLeader()) + server := cluster.GetLeaderServer() + re.NoError(server.BootstrapCluster()) + url := server.GetConfig().ClientUrls + v2Prefix + "/ready" + failpoint.Enable("github.com/tikv/pd/pkg/storage/loadRegionSlow", `return()`) + checkReady(re, url, false) + failpoint.Disable("github.com/tikv/pd/pkg/storage/loadRegionSlow") + checkReady(re, url, true) +} + +func checkReady(re *require.Assertions, url string, isReady bool) { + expectCode := http.StatusOK + if !isReady { + expectCode = http.StatusInternalServerError + } + resp, err := tests.TestDialClient.Get(url) + re.NoError(err) + defer resp.Body.Close() + buf, err := io.ReadAll(resp.Body) + re.NoError(err) + re.Empty(buf) + re.Equal(expectCode, resp.StatusCode) + r := &handlers.ReadyStatus{} + if isReady { + r.RegionLoaded = true + } + data, err := json.Marshal(r) + re.NoError(err) + err = tu.CheckGetJSON(tests.TestDialClient, url+"?verbose", data, + tu.Status(re, expectCode)) + re.NoError(err) +} diff --git a/tests/server/apiv2/handlers/testutil.go b/tests/server/apiv2/handlers/testutil.go index e5280183c52..2a023338c8b 100644 --- a/tests/server/apiv2/handlers/testutil.go +++ b/tests/server/apiv2/handlers/testutil.go @@ -32,6 +32,7 @@ import ( ) const ( + v2Prefix = "/pd/api/v2" keyspacesPrefix = "/pd/api/v2/keyspaces" keyspaceGroupsPrefix = "/pd/api/v2/tso/keyspace-groups" )