Skip to content

Commit

Permalink
reimplement dynamic config
Browse files Browse the repository at this point in the history
Signed-off-by: Ryan Leung <[email protected]>
  • Loading branch information
rleungx committed Apr 14, 2020
1 parent 8438f3f commit 6283bb5
Show file tree
Hide file tree
Showing 34 changed files with 384 additions and 2,961 deletions.
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ require (
github.com/gorilla/mux v1.7.3
github.com/gorilla/websocket v1.2.0 // indirect
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
github.com/grpc-ecosystem/grpc-gateway v1.12.1
github.com/json-iterator/go v1.1.9 // indirect
github.com/juju/ratelimit v1.0.1
github.com/kevinburke/go-bindata v3.18.0+incompatible
Expand Down
103 changes: 103 additions & 0 deletions pkg/component/manager.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Copyright 2020 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package component

import (
"fmt"
"net"
"strings"
"sync"

"github.com/pingcap/log"
"go.uber.org/zap"
)

// Manager is used to manage components.
type Manager struct {
sync.RWMutex
// component -> addresses
Addresses map[string][]string
}

// NewManager creates a new component manager.
func NewManager() *Manager {
return &Manager{
Addresses: make(map[string][]string),
}
}

// GetComponentAddrs returns component addresses for a given component.
func (c *Manager) GetComponentAddrs(component string) []string {
c.RLock()
defer c.RUnlock()
var addresses []string
if ca, ok := c.Addresses[component]; ok {
addresses = append(addresses, ca...)
}
return addresses
}

// GetAllComponentAddrs returns all components' addresses.
func (c *Manager) GetAllComponentAddrs() map[string][]string {
c.RLock()
defer c.RUnlock()
return c.Addresses
}

// GetComponent returns the component from a given component ID.
func (c *Manager) GetComponent(addr string) string {
c.RLock()
defer c.RUnlock()
for component, ca := range c.Addresses {
if contains(ca, addr) {
return component
}
}
return ""
}

// Register is used for registering a component with an address to PD.
func (c *Manager) Register(component, addr string) error {
c.Lock()
defer c.Unlock()

str := strings.Split(addr, ":")
if len(str) != 0 {
ip := net.ParseIP(str[0])
if ip == nil {
return fmt.Errorf("failed to parse address %s of component %s", addr, component)
}
}

ca, ok := c.Addresses[component]
if ok && contains(ca, addr) {
log.Info("address has already been registered", zap.String("component", component), zap.String("address", addr))
return fmt.Errorf("component %s address %s has already been registered", component, addr)
}

ca = append(ca, addr)
c.Addresses[component] = ca
log.Info("address registers successfully", zap.String("component", component), zap.String("address", addr))
return nil
}

func contains(slice []string, item string) bool {
for _, s := range slice {
if s == item {
return true
}
}

return false
}
52 changes: 52 additions & 0 deletions pkg/component/manager_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright 2020 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package component

import (
"strings"
"testing"

. "github.com/pingcap/check"
)

func Test(t *testing.T) {
TestingT(t)
}

var _ = Suite(&testManagerSuite{})

type testManagerSuite struct{}

func (s *testManagerSuite) TestManager(c *C) {
m := NewManager()
// register legal address
c.Assert(m.Register("c1", "127.0.0.1:1"), IsNil)
c.Assert(m.Register("c1", "127.0.0.1:2"), IsNil)
// register repeatedly
c.Assert(strings.Contains(m.Register("c1", "127.0.0.1:2").Error(), "already"), IsTrue)
c.Assert(m.Register("c2", "127.0.0.1:3"), IsNil)
// register illegal address
c.Assert(m.Register("c2", "abcde"), NotNil)

all := map[string][]string{
"c1": {"127.0.0.1:1", "127.0.0.1:2"},
"c2": {"127.0.0.1:3"},
}
c.Assert(m.GetAllComponentAddrs(), DeepEquals, all)
c.Assert(m.GetComponentAddrs("c1"), DeepEquals, all["c1"])
c.Assert(m.GetComponentAddrs("c2"), DeepEquals, all["c2"])
c.Assert(m.GetComponent("127.0.0.1:1"), Equals, "c1")
c.Assert(m.GetComponent("127.0.0.1:2"), Equals, "c1")
c.Assert(m.GetComponent("127.0.0.1:3"), Equals, "c2")
}
18 changes: 5 additions & 13 deletions pkg/dashboard/adapter/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,11 @@ type Manager struct {
func NewManager(srv *server.Server, s *apiserver.Service, redirector *Redirector) *Manager {
ctx, cancel := context.WithCancel(srv.Context())
return &Manager{
ctx: ctx,
cancel: cancel,
srv: srv,
service: s,
redirector: redirector,
enableDynamic: srv.GetConfig().EnableDynamicConfig,
ctx: ctx,
cancel: cancel,
srv: srv,
service: s,
redirector: redirector,
}
}

Expand Down Expand Up @@ -195,13 +194,6 @@ func (m *Manager) setNewAddress() {
}
}
}
// set new dashboard address
if m.enableDynamic {
if err := m.srv.UpdateConfigManager("pd-server.dashboard-address", addr); err != nil {
log.Error("failed to update the dashboard address in config manager", zap.Error(err))
}
return
}
cfg := m.srv.GetScheduleOption().GetPDServerConfig().Clone()
cfg.DashboardAddress = addr
m.srv.SetPDServerConfig(*cfg)
Expand Down
100 changes: 100 additions & 0 deletions server/api/component.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Copyright 2020 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package api

import (
"net/http"

"github.com/gorilla/mux"
"github.com/pingcap/errcode"
"github.com/pingcap/pd/v4/pkg/apiutil"
"github.com/pingcap/pd/v4/server"
"github.com/pkg/errors"
"github.com/unrolled/render"
)

// Addresses is mapping from component to addresses.
type Addresses map[string][]string

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

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

// @Tags component
// @Summary Register component address.
// @Produce json
// @Success 200 {string} string
// @Failure 400 {string} string "PD server failed to proceed the request."
// @Router /component/register [post]
func (h *componentHandler) Register(w http.ResponseWriter, r *http.Request) {
input := make(map[string]string)
if err := apiutil.ReadJSONRespondError(h.rd, w, r.Body, &input); err != nil {
return
}
component, ok := input["component"]
if !ok {
apiutil.ErrorResp(h.rd, w, errcode.NewInvalidInputErr(errors.New("not set component")))
return
}
addr, ok := input["addr"]
if !ok {
apiutil.ErrorResp(h.rd, w, errcode.NewInvalidInputErr(errors.New("not set addr")))
return
}
m := h.svr.GetComponentManager()
err := m.Register(component, addr)
if err != nil {
h.rd.JSON(w, http.StatusBadRequest, err.Error())
return
}
h.rd.JSON(w, http.StatusOK, nil)
}

// @Tags component
// @Summary List all component addresses
// @Produce json
// @Success 200 {object} Addresses
// @Router /component [get]
func (h *componentHandler) GetAllAddress(w http.ResponseWriter, r *http.Request) {
m := h.svr.GetComponentManager()
addrs := m.GetAllComponentAddrs()
h.rd.JSON(w, http.StatusOK, addrs)
}

// @Tags component
// @Summary List component addresses
// @Produce json
// @Success 200 {array} string
// @Failure 404 {string} string "The component does not exist."
// @Router /component/{type} [get]
func (h *componentHandler) GetAddress(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
component := vars["type"]
m := h.svr.GetComponentManager()
addrs := m.GetComponentAddrs(component)

if len(addrs) == 0 {
h.rd.JSON(w, http.StatusNotFound, "component not found")
return
}
h.rd.JSON(w, http.StatusOK, addrs)
}
93 changes: 93 additions & 0 deletions server/api/component_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Copyright 2020 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package api

import (
"encoding/json"
"fmt"
"strings"

. "github.com/pingcap/check"
"github.com/pingcap/pd/v4/server"
)

var _ = Suite(&testComponentSuite{})

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

func (s *testComponentSuite) SetUpSuite(c *C) {
s.svr, s.cleanup = mustNewServer(c)
mustWaitLeader(c, []*server.Server{s.svr})

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

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

func (s *testComponentSuite) TestRegister(c *C) {
addr := fmt.Sprintf("%s/component", s.urlPrefix)
output := make(map[string][]string)
err := readJSON(addr, &output)
c.Assert(err, IsNil)
c.Assert(len(output), Equals, 0)

addr1 := fmt.Sprintf("%s/component/c1", s.urlPrefix)
var output1 []string
err = readJSON(addr1, &output)
c.Assert(strings.Contains(err.Error(), "404"), IsTrue)
c.Assert(len(output1), Equals, 0)

addr2 := fmt.Sprintf("%s/component/register", s.urlPrefix)
reqs := []map[string]string{
{"component": "c1", "addr": "127.0.0.1:1"},
{"component": "c1", "addr": "127.0.0.1:2"},
{"component": "c2", "addr": "127.0.0.1:3"},
}
for _, req := range reqs {
postData, err := json.Marshal(req)
c.Assert(err, IsNil)
err = postJSON(addr2, postData)
c.Assert(err, IsNil)
}

expected := map[string][]string{
"c1": {"127.0.0.1:1", "127.0.0.1:2"},
"c2": {"127.0.0.1:3"},
}

output = make(map[string][]string)
err = readJSON(addr, &output)
c.Assert(err, IsNil)
c.Assert(output, DeepEquals, expected)

expected1 := []string{"127.0.0.1:1", "127.0.0.1:2"}
var output2 []string
err = readJSON(addr1, &output2)
c.Assert(err, IsNil)
c.Assert(output2, DeepEquals, expected1)

addr3 := fmt.Sprintf("%s/component/c2", s.urlPrefix)
expected2 := []string{"127.0.0.1:3"}
var output3 []string
err = readJSON(addr3, &output3)
c.Assert(err, IsNil)
c.Assert(output3, DeepEquals, expected2)
}
Loading

0 comments on commit 6283bb5

Please sign in to comment.