Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pd-ctl: add config replicate and show all config #573

Merged
merged 13 commits into from
Mar 27, 2017
75 changes: 41 additions & 34 deletions pdctl/command/config_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ import (
)

var (
configPrefix = "pd/api/v1/config"
schedulePrefix = "pd/api/v1/config/schedule"
configPrefix = "pd/api/v1/config"
schedulePrefix = "pd/api/v1/config/schedule"
replicatePrefix = "pd/api/v1/config/replicate"
)

// NewConfigCommand return a config subcommand of rootCmd
Expand All @@ -46,6 +47,17 @@ func NewShowConfigCommand() *cobra.Command {
Short: "show config of PD",
Run: showConfigCommandFunc,
}
sc.AddCommand(NewShowAllConfigCommand())
return sc
}

// NewShowAllConfigCommand return a show all subcommand of show subcommand
func NewShowAllConfigCommand() *cobra.Command {
sc := &cobra.Command{
Use: "all",
Short: "show all config of PD",
Run: showAllConfigCommandFunc,
}
return sc
}

Expand All @@ -68,50 +80,45 @@ func showConfigCommandFunc(cmd *cobra.Command, args []string) {
fmt.Println(r)
}

func setConfigCommandFunc(cmd *cobra.Command, args []string) {
if len(args) != 2 {
fmt.Println(cmd.UsageString())
func showAllConfigCommandFunc(cmd *cobra.Command, args []string) {
r, err := doRequest(cmd, configPrefix, http.MethodGet)
if err != nil {
fmt.Printf("Failed to get config: %s", err)
return
}
fmt.Println(r)
}

url := getAddressFromCmd(cmd, schedulePrefix)
var value interface{}
func postConfigDataWithPath(cmd *cobra.Command, key, value, path string) error {
var val interface{}
data := make(map[string]interface{})

r, err := http.Get(url)
val, err := strconv.ParseFloat(value, 64)
if err != nil {
fmt.Printf("Failed to set config:[%s]\n", err)
return
val = value
}
if r.StatusCode != http.StatusOK {
printResponseError(r)
r.Body.Close()
return
data[key] = val
reqData, err := json.Marshal(data)
req, err := getRequest(cmd, path, http.MethodPost, "application/json", bytes.NewBuffer(reqData))
if err != nil {
return err
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use schedulePrefix?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the API setSchedule and getSchedule are not consistent. considering compatible I just adjust it in here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's ok to give up supporting old pd-server.


json.NewDecoder(r.Body).Decode(&data)
r.Body.Close()
value, err = strconv.ParseFloat(args[1], 64)
_, err = dail(req)
if err != nil {
value = args[1]
return err
}
data[args[0]] = value
return nil
}

req, err := json.Marshal(data)
if err != nil {
fmt.Printf("Failed to set config:[%s]\n", err)
func setConfigCommandFunc(cmd *cobra.Command, args []string) {
if len(args) != 2 {
fmt.Println(cmd.UsageString())
return
}

url = getAddressFromCmd(cmd, configPrefix)
r, err = http.Post(url, "application/json", bytes.NewBuffer(req))
opt, val := args[0], args[1]
err := postConfigDataWithPath(cmd, opt, val, configPrefix)
if err != nil {
fmt.Printf("Failed to set config:[%s]\n", err)
}
defer r.Body.Close()
if r.StatusCode == http.StatusOK {
fmt.Println("Success!")
} else {
printResponseError(r)
fmt.Printf("Failed to set config: %s", err)
return
}
fmt.Println("Success!")
}
21 changes: 17 additions & 4 deletions pdctl/command/global.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,21 @@ var (
errInvalidAddr = errors.New("Invalid pd address, Cannot get connect to it")
)

func doRequest(cmd *cobra.Command, prefix string, method string) (string, error) {
var res string
func getRequest(cmd *cobra.Command, prefix string, method string, bodyType string, body io.Reader) (*http.Request, error) {
if method == "" {
method = http.MethodGet
}
url := getAddressFromCmd(cmd, prefix)
req, err := http.NewRequest(method, url, nil)
req, err := http.NewRequest(method, url, body)
if err != nil {
return res, err
return nil, err
}
req.Header.Set("Content-Type", bodyType)
return req, err
}

func dail(req *http.Request) (string, error) {
var res string
reps, err := dailClient.Do(req)
if err != nil {
return res, err
Expand All @@ -64,6 +69,14 @@ func doRequest(cmd *cobra.Command, prefix string, method string) (string, error)
return res, nil
}

func doRequest(cmd *cobra.Command, prefix string, method string) (string, error) {
req, err := getRequest(cmd, prefix, method, "", nil)
if err != nil {
return "", err
}
return dail(req)
}

func genResponseError(r *http.Response) error {
res, _ := ioutil.ReadAll(r.Body)
return errors.Errorf("[%d] %s", r.StatusCode, res)
Expand Down
25 changes: 25 additions & 0 deletions server/api/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
package api

import (
"encoding/json"
"io/ioutil"
"net/http"

"github.com/pingcap/pd/server"
Expand All @@ -36,6 +38,29 @@ func (h *confHandler) Get(w http.ResponseWriter, r *http.Request) {
h.rd.JSON(w, http.StatusOK, h.svr.GetConfig())
}

func (h *confHandler) Post(w http.ResponseWriter, r *http.Request) {
config := h.svr.GetConfig()
data, err := ioutil.ReadAll(r.Body)
r.Body.Close()
if err != nil {
h.rd.JSON(w, http.StatusInternalServerError, err.Error())
return
}
err = json.Unmarshal(data, &config.Schedule)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we support json.Unmarshal(data, &config) later?

IMO, I prefer check key-value one by one.

Copy link
Contributor Author

@nolouch nolouch Mar 27, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a more easy way :). the other config no need to dynamic adjust.

if err != nil {
h.rd.JSON(w, http.StatusInternalServerError, err.Error())
return
}
err = json.Unmarshal(data, &config.Replication)
if err != nil {
h.rd.JSON(w, http.StatusInternalServerError, err.Error())
return
}
h.svr.SetScheduleConfig(config.Schedule)
h.svr.SetReplicationConfig(config.Replication)
h.rd.JSON(w, http.StatusOK, nil)
}

func (h *confHandler) GetSchedule(w http.ResponseWriter, r *http.Request) {
h.rd.JSON(w, http.StatusOK, &h.svr.GetConfig().Schedule)
}
Expand Down
30 changes: 26 additions & 4 deletions server/api/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ package api

import (
"encoding/json"
"io/ioutil"
"math/rand"
"net/http"
"strings"
Expand All @@ -41,7 +40,7 @@ func checkConfigResponse(c *C, body []byte, cfgs []*server.Config) {
c.Assert(err, IsNil)
}

func (s *testConfigSuite) TestConfigList(c *C) {
func (s *testConfigSuite) TestConfigAll(c *C) {
numbers := []int{1, 3}
for _, num := range numbers {
cfgs, _, clean := mustNewCluster(c, num)
Expand All @@ -51,9 +50,32 @@ func (s *testConfigSuite) TestConfigList(c *C) {
addr := mustUnixAddrToHTTPAddr(c, strings.Join(parts, ""))
resp, err := s.hc.Get(addr)
c.Assert(err, IsNil)
buf, err := ioutil.ReadAll(resp.Body)
cfg := &server.Config{}
err = readJSON(resp.Body, cfg)
c.Assert(err, IsNil)
checkConfigResponse(c, buf, cfgs)

r := map[string]int{"max-replicas": 5}
postData, err := json.Marshal(r)
c.Assert(err, IsNil)
err = postJSON(s.hc, addr, postData)
c.Assert(err, IsNil)
l := map[string]interface{}{
"location-labels": "zone,rack",
"region-schedule-limit": 10,
}
postData, err = json.Marshal(l)
c.Assert(err, IsNil)
err = postJSON(s.hc, addr, postData)
c.Assert(err, IsNil)

resp, err = s.hc.Get(addr)
newCfg := &server.Config{}
err = readJSON(resp.Body, newCfg)
c.Assert(err, IsNil)
cfg.Replication.MaxReplicas = 5
cfg.Replication.LocationLabels = []string{"zone", "rack"}
cfg.Schedule.RegionScheduleLimit = 10
c.Assert(cfg, DeepEquals, newCfg)
}
}

Expand Down
1 change: 1 addition & 0 deletions server/api/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ func createRouter(prefix string, svr *server.Server) *mux.Router {

confHandler := newConfHandler(svr, rd)
router.HandleFunc("/api/v1/config", confHandler.Get).Methods("GET")
router.HandleFunc("/api/v1/config", confHandler.Post).Methods("POST")
router.HandleFunc("/api/v1/config/schedule", confHandler.SetSchedule).Methods("POST")
router.HandleFunc("/api/v1/config/schedule", confHandler.GetSchedule).Methods("GET")
router.HandleFunc("/api/v1/config/replicate", confHandler.SetReplication).Methods("POST")
Expand Down
9 changes: 7 additions & 2 deletions server/api/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ func readJSON(r io.ReadCloser, data interface{}) error {
if err != nil {
return errors.Trace(err)
}

err = json.Unmarshal(b, data)
if err != nil {
return errors.Trace(err)
Expand All @@ -45,8 +44,14 @@ func postJSON(cli *http.Client, url string, data []byte) error {
if err != nil {
return errors.Trace(err)
}
ioutil.ReadAll(resp.Body)
res, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
return err
}
if resp.StatusCode != http.StatusOK {
return errors.New(string(res))
}
return nil
}

Expand Down
2 changes: 1 addition & 1 deletion server/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ func (c *RaftCluster) isRunning() bool {
return c.running
}

// GetConfig gets config information.
// GetConfig gets the config information.
func (s *Server) GetConfig() *Config {
cfg := s.cfg.clone()
cfg.Schedule = *s.scheduleOpt.load()
Expand Down