peer.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. // Copyright 2015 The etcd Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package etcdhttp
  15. import (
  16. "encoding/json"
  17. "fmt"
  18. "net/http"
  19. "strconv"
  20. "strings"
  21. "go.etcd.io/etcd/etcdserver"
  22. "go.etcd.io/etcd/etcdserver/api"
  23. "go.etcd.io/etcd/etcdserver/api/membership"
  24. "go.etcd.io/etcd/etcdserver/api/rafthttp"
  25. "go.etcd.io/etcd/lease/leasehttp"
  26. "go.etcd.io/etcd/pkg/types"
  27. "go.uber.org/zap"
  28. )
  29. const (
  30. peerMembersPath = "/members"
  31. peerMemberPromotePrefix = "/members/promote/"
  32. )
  33. // NewPeerHandler generates an http.Handler to handle etcd peer requests.
  34. func NewPeerHandler(lg *zap.Logger, s etcdserver.ServerPeer) http.Handler {
  35. return newPeerHandler(lg, s, s.RaftHandler(), s.LeaseHandler())
  36. }
  37. func newPeerHandler(lg *zap.Logger, s etcdserver.Server, raftHandler http.Handler, leaseHandler http.Handler) http.Handler {
  38. peerMembersHandler := newPeerMembersHandler(lg, s.Cluster())
  39. peerMemberPromoteHandler := newPeerMemberPromoteHandler(lg, s)
  40. mux := http.NewServeMux()
  41. mux.HandleFunc("/", http.NotFound)
  42. mux.Handle(rafthttp.RaftPrefix, raftHandler)
  43. mux.Handle(rafthttp.RaftPrefix+"/", raftHandler)
  44. mux.Handle(peerMembersPath, peerMembersHandler)
  45. mux.Handle(peerMemberPromotePrefix, peerMemberPromoteHandler)
  46. if leaseHandler != nil {
  47. mux.Handle(leasehttp.LeasePrefix, leaseHandler)
  48. mux.Handle(leasehttp.LeaseInternalPrefix, leaseHandler)
  49. }
  50. mux.HandleFunc(versionPath, versionHandler(s.Cluster(), serveVersion))
  51. return mux
  52. }
  53. func newPeerMembersHandler(lg *zap.Logger, cluster api.Cluster) http.Handler {
  54. return &peerMembersHandler{
  55. lg: lg,
  56. cluster: cluster,
  57. }
  58. }
  59. type peerMembersHandler struct {
  60. lg *zap.Logger
  61. cluster api.Cluster
  62. }
  63. func newPeerMemberPromoteHandler(lg *zap.Logger, s etcdserver.Server) http.Handler {
  64. return &peerMemberPromoteHandler{
  65. lg: lg,
  66. cluster: s.Cluster(),
  67. server: s,
  68. }
  69. }
  70. type peerMemberPromoteHandler struct {
  71. lg *zap.Logger
  72. cluster api.Cluster
  73. server etcdserver.Server
  74. }
  75. func (h *peerMembersHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  76. if !allowMethod(w, r, "GET") {
  77. return
  78. }
  79. w.Header().Set("X-Etcd-Cluster-ID", h.cluster.ID().String())
  80. if r.URL.Path != peerMembersPath {
  81. http.Error(w, "bad path", http.StatusBadRequest)
  82. return
  83. }
  84. ms := h.cluster.Members()
  85. w.Header().Set("Content-Type", "application/json")
  86. if err := json.NewEncoder(w).Encode(ms); err != nil {
  87. if h.lg != nil {
  88. h.lg.Warn("failed to encode membership members", zap.Error(err))
  89. } else {
  90. plog.Warningf("failed to encode members response (%v)", err)
  91. }
  92. }
  93. }
  94. func (h *peerMemberPromoteHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  95. if !allowMethod(w, r, "POST") {
  96. return
  97. }
  98. w.Header().Set("X-Etcd-Cluster-ID", h.cluster.ID().String())
  99. if !strings.HasPrefix(r.URL.Path, peerMemberPromotePrefix) {
  100. http.Error(w, "bad path", http.StatusBadRequest)
  101. return
  102. }
  103. idStr := strings.TrimPrefix(r.URL.Path, peerMemberPromotePrefix)
  104. id, err := strconv.ParseUint(idStr, 10, 64)
  105. if err != nil {
  106. http.Error(w, fmt.Sprintf("member %s not found in cluster", idStr), http.StatusNotFound)
  107. return
  108. }
  109. resp, err := h.server.PromoteMember(r.Context(), id)
  110. if err != nil {
  111. switch err {
  112. case membership.ErrIDNotFound:
  113. http.Error(w, err.Error(), http.StatusNotFound)
  114. case membership.ErrMemberNotLearner:
  115. http.Error(w, err.Error(), http.StatusPreconditionFailed)
  116. case etcdserver.ErrLearnerNotReady:
  117. http.Error(w, err.Error(), http.StatusPreconditionFailed)
  118. default:
  119. WriteError(h.lg, w, r, err)
  120. }
  121. if h.lg != nil {
  122. h.lg.Warn(
  123. "failed to promote a member",
  124. zap.String("member-id", types.ID(id).String()),
  125. zap.Error(err),
  126. )
  127. } else {
  128. plog.Errorf("error promoting member %s (%v)", types.ID(id).String(), err)
  129. }
  130. return
  131. }
  132. w.Header().Set("Content-Type", "application/json")
  133. w.WriteHeader(http.StatusOK)
  134. if err := json.NewEncoder(w).Encode(resp); err != nil {
  135. if h.lg != nil {
  136. h.lg.Warn("failed to encode members response", zap.Error(err))
  137. } else {
  138. plog.Warningf("failed to encode members response (%v)", err)
  139. }
  140. }
  141. }