| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- /*
- Copyright 2014 CoreOS 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,
- 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 etcdserver
- import (
- "encoding/json"
- "fmt"
- "net/http"
- "net/url"
- "path/filepath"
- "strconv"
- "strings"
- "github.com/coreos/etcd/conf"
- "github.com/coreos/etcd/store"
- )
- const (
- stateFollower = "follower"
- stateCandidate = "candidate"
- stateLeader = "leader"
- )
- // machineMessage represents information about a peer or standby in the registry.
- type machineMessage struct {
- Name string `json:"name"`
- State string `json:"state"`
- ClientURL string `json:"clientURL"`
- PeerURL string `json:"peerURL"`
- }
- type context struct {
- MinVersion int `json:"minVersion"`
- MaxVersion int `json:"maxVersion"`
- ClientURL string `json:"clientURL"`
- PeerURL string `json:"peerURL"`
- }
- func (p *participant) serveAdminConfig(w http.ResponseWriter, r *http.Request) error {
- switch r.Method {
- case "GET":
- case "PUT":
- if !p.node.IsLeader() {
- return p.redirect(w, r, p.node.Leader())
- }
- c := p.clusterConfig()
- if err := json.NewDecoder(r.Body).Decode(c); err != nil {
- return err
- }
- c.Sanitize()
- if err := p.setClusterConfig(c); err != nil {
- return err
- }
- default:
- return allow(w, "GET", "PUT")
- }
- w.Header().Set("Content-Type", "application/json")
- json.NewEncoder(w).Encode(p.clusterConfig())
- return nil
- }
- func (p *participant) serveAdminMachines(w http.ResponseWriter, r *http.Request) error {
- name := strings.TrimPrefix(r.URL.Path, v2adminMachinesPrefix)
- switch r.Method {
- case "GET":
- var info interface{}
- var err error
- if name != "" {
- info, err = p.machineMessage(name)
- } else {
- info, err = p.allMachineMessages()
- }
- if err != nil {
- return err
- }
- w.Header().Set("Content-Type", "application/json")
- json.NewEncoder(w).Encode(info)
- case "PUT":
- if !p.node.IsLeader() {
- return p.redirect(w, r, p.node.Leader())
- }
- id, err := strconv.ParseInt(name, 0, 64)
- if err != nil {
- return err
- }
- info := &context{}
- if err := json.NewDecoder(r.Body).Decode(info); err != nil {
- return err
- }
- return p.add(id, info.PeerURL, info.ClientURL)
- case "DELETE":
- if !p.node.IsLeader() {
- return p.redirect(w, r, p.node.Leader())
- }
- id, err := strconv.ParseInt(name, 0, 64)
- if err != nil {
- return err
- }
- return p.remove(id)
- default:
- return allow(w, "GET", "PUT", "DELETE")
- }
- return nil
- }
- func (p *participant) clusterConfig() *conf.ClusterConfig {
- c := conf.NewClusterConfig()
- // This is used for backward compatibility because it doesn't
- // set cluster config in older version.
- if e, err := p.Store.Get(v2configKVPrefix, false, false); err == nil {
- json.Unmarshal([]byte(*e.Node.Value), c)
- }
- return c
- }
- func (p *participant) setClusterConfig(c *conf.ClusterConfig) error {
- b, err := json.Marshal(c)
- if err != nil {
- return err
- }
- if _, err := p.Set(v2configKVPrefix, false, string(b), store.Permanent); err != nil {
- return err
- }
- return nil
- }
- // machineMessage returns the machineMessage of the given name
- func (p *participant) machineMessage(name string) (*machineMessage, error) {
- pp := filepath.Join(v2machineKVPrefix, name)
- e, err := p.Store.Get(pp, false, false)
- if err != nil {
- return nil, err
- }
- lead := fmt.Sprint(p.node.Leader())
- return newMachineMessage(e.Node, lead), nil
- }
- func (p *participant) allMachineMessages() ([]*machineMessage, error) {
- e, err := p.Store.Get(v2machineKVPrefix, false, false)
- if err != nil {
- return nil, err
- }
- lead := fmt.Sprint(p.node.Leader())
- ms := make([]*machineMessage, len(e.Node.Nodes))
- for i, n := range e.Node.Nodes {
- ms[i] = newMachineMessage(n, lead)
- }
- return ms, nil
- }
- func newMachineMessage(n *store.NodeExtern, lead string) *machineMessage {
- _, name := filepath.Split(n.Key)
- q, err := url.ParseQuery(*n.Value)
- if err != nil {
- panic("fail to parse the info for machine " + name)
- }
- m := &machineMessage{
- Name: name,
- State: stateFollower,
- ClientURL: q["etcd"][0],
- PeerURL: q["raft"][0],
- }
- if name == lead {
- m.State = stateLeader
- }
- return m
- }
|