Skip to content

Commit

Permalink
config, api: Add Service Config (#4869)
Browse files Browse the repository at this point in the history
ref #4480

add service config

Signed-off-by: Cabinfever_B <cabinfeveroier@gmail.com>

Co-authored-by: Ti Chi Robot <ti-community-prow-bot@tidb.io>
  • Loading branch information
CabinfeverB and ti-chi-bot authored May 17, 2022
1 parent 3e95051 commit dac3836
Show file tree
Hide file tree
Showing 16 changed files with 498 additions and 70 deletions.
21 changes: 0 additions & 21 deletions server/api/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -305,27 +305,6 @@ func (s *testConfigSuite) TestConfigPDServer(c *C) {
c.Assert(sc.FlowRoundByDigit, Equals, int(3))
c.Assert(sc.MinResolvedTSPersistenceInterval, Equals, typeutil.NewDuration(0))
c.Assert(sc.MaxResetTSGap.Duration, Equals, 24*time.Hour)
c.Assert(sc.EnableAudit, Equals, false)

// test update enable-audit
ms = map[string]interface{}{
"enable-audit": true,
}
postData, err = json.Marshal(ms)
c.Assert(err, IsNil)
c.Assert(tu.CheckPostJSON(testDialClient, addrPost, postData, tu.StatusOK(c)), IsNil)
sc = &config.PDServerConfig{}
c.Assert(tu.ReadGetJSON(c, testDialClient, addrGet, sc), IsNil)
c.Assert(sc.EnableAudit, Equals, true)
ms = map[string]interface{}{
"enable-audit": false,
}
postData, err = json.Marshal(ms)
c.Assert(err, IsNil)
c.Assert(tu.CheckPostJSON(testDialClient, addrPost, postData, tu.StatusOK(c)), IsNil)
sc = &config.PDServerConfig{}
c.Assert(tu.ReadGetJSON(c, testDialClient, addrGet, sc), IsNil)
c.Assert(sc.EnableAudit, Equals, false)
}

var ttlConfig = map[string]interface{}{
Expand Down
4 changes: 2 additions & 2 deletions server/api/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func newRequestInfoMiddleware(s *server.Server) negroni.Handler {
}

func (rm *requestInfoMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
if !rm.svr.GetPersistOptions().IsAuditEnabled() {
if !rm.svr.GetServiceMiddlewarePersistOptions().IsAuditEnabled() {
next(w, r)
return
}
Expand Down Expand Up @@ -116,7 +116,7 @@ func newAuditMiddleware(s *server.Server) negroni.Handler {

// ServeHTTP is used to implememt negroni.Handler for auditMiddleware
func (s *auditMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
if !s.svr.GetPersistOptions().IsAuditEnabled() {
if !s.svr.GetServiceMiddlewarePersistOptions().IsAuditEnabled() {
next(w, r)
return
}
Expand Down
2 changes: 1 addition & 1 deletion server/api/min_resolved_ts.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ type minResolvedTS struct {
PersistInterval typeutil.Duration `json:"persist_interval,omitempty"`
}

// @Tags minresolvedts
// @Tags min_resolved_ts
// @Summary Get cluster-level min resolved ts.
// @Produce json
// @Success 200 {array} minResolvedTS
Expand Down
4 changes: 4 additions & 0 deletions server/api/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,10 @@ func createRouter(prefix string, svr *server.Server) *mux.Router {
registerFunc(apiRouter, "/admin/persist-file/{file_name}", adminHandler.SavePersistFile, setMethods("POST"), setAuditBackend(localLog))
registerFunc(clusterRouter, "/admin/replication_mode/wait-async", adminHandler.UpdateWaitAsyncTime, setMethods("POST"), setAuditBackend(localLog))

serviceMiddlewareHandler := newServiceMiddlewareHandler(svr, rd)
registerFunc(apiRouter, "/service-middleware/config", serviceMiddlewareHandler.GetServiceMiddlewareConfig, setMethods("GET"))
registerFunc(apiRouter, "/service-middleware/config", serviceMiddlewareHandler.SetServiceMiddlewareConfig, setMethods("POST"), setAuditBackend(localLog))

logHandler := newLogHandler(svr, rd)
registerFunc(apiRouter, "/admin/log", logHandler.SetLogLevel, setMethods("POST"), setAuditBackend(localLog))
replicationModeHandler := newReplicationModeHandler(svr, rd)
Expand Down
4 changes: 2 additions & 2 deletions server/api/service_gc_safepoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ type listServiceGCSafepoint struct {
GCSafePoint uint64 `json:"gc_safe_point"`
}

// @Tags servicegcsafepoint
// @Tags service_gc_safepoint
// @Summary Get all service GC safepoint.
// @Produce json
// @Success 200 {array} listServiceGCSafepoint
Expand All @@ -66,7 +66,7 @@ func (h *serviceGCSafepointHandler) GetGCSafePoint(w http.ResponseWriter, r *htt
h.rd.JSON(w, http.StatusOK, list)
}

// @Tags servicegcsafepoint
// @Tags service_gc_safepoint
// @Summary Delete a service GC safepoint.
// @Param service_id path string true "Service ID"
// @Produce json
Expand Down
131 changes: 131 additions & 0 deletions server/api/service_middleware.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// Copyright 2022 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/json"
"fmt"
"io"
"net/http"
"reflect"
"strings"

"github.com/pingcap/errors"
"github.com/tikv/pd/pkg/reflectutil"
"github.com/tikv/pd/server"
"github.com/tikv/pd/server/config"

"github.com/unrolled/render"
)

type serviceMiddlewareHandler struct {
svr *server.Server
rd *render.Render
}

func newServiceMiddlewareHandler(svr *server.Server, rd *render.Render) *serviceMiddlewareHandler {
return &serviceMiddlewareHandler{
svr: svr,
rd: rd,
}
}

// @Tags service_middleware
// @Summary Get Service Middleware config.
// @Produce json
// @Success 200 {object} config.Config
// @Router /service-middleware/config [get]
func (h *serviceMiddlewareHandler) GetServiceMiddlewareConfig(w http.ResponseWriter, r *http.Request) {
h.rd.JSON(w, http.StatusOK, h.svr.GetServiceMiddlewareConfig())
}

// @Tags service_middleware
// @Summary Update some service-middleware's config items.
// @Accept json
// @Param body body object false "json params"
// @Produce json
// @Success 200 {string} string "The config is updated."
// @Failure 400 {string} string "The input is invalid."
// @Failure 500 {string} string "PD server failed to proceed the request."
// @Router /service-middleware/config [post]
func (h *serviceMiddlewareHandler) SetServiceMiddlewareConfig(w http.ResponseWriter, r *http.Request) {
cfg := h.svr.GetServiceMiddlewareConfig()
data, err := io.ReadAll(r.Body)
r.Body.Close()
if err != nil {
h.rd.JSON(w, http.StatusInternalServerError, err.Error())
return
}

conf := make(map[string]interface{})
if err := json.Unmarshal(data, &conf); err != nil {
h.rd.JSON(w, http.StatusBadRequest, err.Error())
return
}

if len(conf) == 0 {
h.rd.JSON(w, http.StatusOK, "The input is empty.")
}

for k, v := range conf {
if s := strings.Split(k, "."); len(s) > 1 {
if err := h.updateServiceMiddlewareConfig(cfg, k, v); err != nil {
h.rd.JSON(w, http.StatusBadRequest, err.Error())
return
}
continue
}
key := reflectutil.FindJSONFullTagByChildTag(reflect.TypeOf(config.ServiceMiddlewareConfig{}), k)
if key == "" {
h.rd.JSON(w, http.StatusBadRequest, fmt.Sprintf("config item %s not found", k))
return
}
if err := h.updateServiceMiddlewareConfig(cfg, key, v); err != nil {
h.rd.JSON(w, http.StatusBadRequest, err.Error())
return
}
}

h.rd.JSON(w, http.StatusOK, "The service-middleware config is updated.")
}

func (h *serviceMiddlewareHandler) updateServiceMiddlewareConfig(cfg *config.ServiceMiddlewareConfig, key string, value interface{}) error {
kp := strings.Split(key, ".")
if kp[0] == "audit" {
return h.updateAudit(cfg, kp[len(kp)-1], value)
}
return errors.Errorf("config prefix %s not found", kp[0])
}

func (h *serviceMiddlewareHandler) updateAudit(config *config.ServiceMiddlewareConfig, key string, value interface{}) error {
data, err := json.Marshal(map[string]interface{}{key: value})
if err != nil {
return err
}

updated, found, err := mergeConfig(&config.AuditConfig, data)
if err != nil {
return err
}

if !found {
return errors.Errorf("config item %s not found", key)
}

if updated {
err = h.svr.SetAuditConfig(config.AuditConfig)
}
return err
}
100 changes: 100 additions & 0 deletions server/api/service_middleware_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Copyright 2022 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/json"
"fmt"
"net/http"

. "github.com/pingcap/check"
"github.com/pingcap/failpoint"
tu "github.com/tikv/pd/pkg/testutil"
"github.com/tikv/pd/server"
"github.com/tikv/pd/server/config"
)

var _ = Suite(&testServiceMiddlewareSuite{})

type testServiceMiddlewareSuite struct {
svr *server.Server
cleanup cleanUpFunc
urlPrefix string
}

func (s *testServiceMiddlewareSuite) SetUpSuite(c *C) {
s.svr, s.cleanup = mustNewServer(c, func(cfg *config.Config) {
cfg.Replication.EnablePlacementRules = false
})
mustWaitLeader(c, []*server.Server{s.svr})

addr := s.svr.GetAddr()
s.urlPrefix = fmt.Sprintf("%s%s/api/v1", addr, apiPrefix)
}

func (s *testServiceMiddlewareSuite) TearDownSuite(c *C) {
s.cleanup()
}

func (s *testServiceMiddlewareSuite) TestConfigAudit(c *C) {
addr := fmt.Sprintf("%s/service-middleware/config", s.urlPrefix)
ms := map[string]interface{}{
"enable-audit": "true",
}
postData, err := json.Marshal(ms)
c.Assert(err, IsNil)
c.Assert(tu.CheckPostJSON(testDialClient, addr, postData, tu.StatusOK(c)), IsNil)
sc := &config.ServiceMiddlewareConfig{}
c.Assert(tu.ReadGetJSON(c, testDialClient, addr, sc), IsNil)
c.Assert(sc.EnableAudit, Equals, true)
ms = map[string]interface{}{
"audit.enable-audit": "false",
}
postData, err = json.Marshal(ms)
c.Assert(err, IsNil)
c.Assert(tu.CheckPostJSON(testDialClient, addr, postData, tu.StatusOK(c)), IsNil)
sc = &config.ServiceMiddlewareConfig{}
c.Assert(tu.ReadGetJSON(c, testDialClient, addr, sc), IsNil)
c.Assert(sc.EnableAudit, Equals, false)

// test empty
ms = map[string]interface{}{}
postData, err = json.Marshal(ms)
c.Assert(err, IsNil)
c.Assert(tu.CheckPostJSON(testDialClient, addr, postData, tu.StatusOK(c), tu.StringContain(c, "The input is empty.")), IsNil)

ms = map[string]interface{}{
"audit": "false",
}
postData, err = json.Marshal(ms)
c.Assert(err, IsNil)
c.Assert(tu.CheckPostJSON(testDialClient, addr, postData, tu.Status(c, http.StatusBadRequest), tu.StringEqual(c, "config item audit not found")), IsNil)

c.Assert(failpoint.Enable("github.com/tikv/pd/server/config/persistServiceMiddlewareFail", "return(true)"), IsNil)
ms = map[string]interface{}{
"audit.enable-audit": "true",
}
postData, err = json.Marshal(ms)
c.Assert(err, IsNil)
c.Assert(tu.CheckPostJSON(testDialClient, addr, postData, tu.Status(c, http.StatusBadRequest)), IsNil)
c.Assert(failpoint.Disable("github.com/tikv/pd/server/config/persistServiceMiddlewareFail"), IsNil)

ms = map[string]interface{}{
"audit.audit": "false",
}
postData, err = json.Marshal(ms)
c.Assert(err, IsNil)
c.Assert(tu.CheckPostJSON(testDialClient, addr, postData, tu.Status(c, http.StatusBadRequest), tu.StringEqual(c, "config item audit not found")), IsNil)
}
6 changes: 0 additions & 6 deletions server/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,6 @@ const (
maxTraceFlowRoundByDigit = 5 // 0.1 MB
defaultMaxResetTSGap = 24 * time.Hour
defaultMinResolvedTSPersistenceInterval = 0
defaultEnableAuditMiddleware = false
defaultKeyType = "table"

defaultStrictlyMatchLabel = false
Expand Down Expand Up @@ -1116,8 +1115,6 @@ type PDServerConfig struct {
FlowRoundByDigit int `toml:"flow-round-by-digit" json:"flow-round-by-digit"`
// MinResolvedTSPersistenceInterval is the interval to save the min resolved ts.
MinResolvedTSPersistenceInterval typeutil.Duration `toml:"min-resolved-ts-persistence-interval" json:"min-resolved-ts-persistence-interval"`
// EnableAudit controls the switch of the audit middleware
EnableAudit bool `toml:"enable-audit" json:"enable-audit"`
}

func (c *PDServerConfig) adjust(meta *configMetaData) error {
Expand All @@ -1143,9 +1140,6 @@ func (c *PDServerConfig) adjust(meta *configMetaData) error {
if !meta.IsDefined("min-resolved-ts-persistence-interval") {
adjustDuration(&c.MinResolvedTSPersistenceInterval, defaultMinResolvedTSPersistenceInterval)
}
if !meta.IsDefined("enable-audit") {
c.EnableAudit = defaultEnableAuditMiddleware
}
c.migrateConfigurationFromFile(meta)
return c.Validate()
}
Expand Down
5 changes: 0 additions & 5 deletions server/config/persist_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -456,11 +456,6 @@ func (o *PersistOptions) GetLeaderSchedulePolicy() core.SchedulePolicy {
return core.StringToSchedulePolicy(o.GetScheduleConfig().LeaderSchedulePolicy)
}

// IsAuditEnabled returns whether audit middleware is enabled
func (o *PersistOptions) IsAuditEnabled() bool {
return o.GetPDServerConfig().EnableAudit
}

// GetKeyType is to get key type.
func (o *PersistOptions) GetKeyType() core.KeyType {
return core.StringToKeyType(o.GetPDServerConfig().KeyType)
Expand Down
Loading

0 comments on commit dac3836

Please sign in to comment.