go.etcd.io/etcd@v3.3.27+incompatible/etcdserver/api/etcdhttp/base.go (about) 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 15 package etcdhttp 16 17 import ( 18 "encoding/json" 19 "expvar" 20 "fmt" 21 "net/http" 22 "strings" 23 24 etcdErr "github.com/coreos/etcd/error" 25 "github.com/coreos/etcd/etcdserver" 26 "github.com/coreos/etcd/etcdserver/api" 27 "github.com/coreos/etcd/etcdserver/api/v2http/httptypes" 28 "github.com/coreos/etcd/pkg/logutil" 29 "github.com/coreos/etcd/version" 30 "github.com/coreos/pkg/capnslog" 31 ) 32 33 var ( 34 plog = capnslog.NewPackageLogger("github.com/coreos/etcd", "etcdserver/api/etcdhttp") 35 mlog = logutil.NewMergeLogger(plog) 36 ) 37 38 const ( 39 configPath = "/config" 40 varsPath = "/debug/vars" 41 versionPath = "/version" 42 ) 43 44 // HandleBasic adds handlers to a mux for serving JSON etcd client requests 45 // that do not access the v2 store. 46 func HandleBasic(mux *http.ServeMux, server etcdserver.ServerPeer) { 47 mux.HandleFunc(varsPath, serveVars) 48 mux.HandleFunc(configPath+"/local/log", logHandleFunc) 49 HandleMetricsHealth(mux, server) 50 mux.HandleFunc(versionPath, versionHandler(server.Cluster(), serveVersion)) 51 } 52 53 func versionHandler(c api.Cluster, fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc { 54 return func(w http.ResponseWriter, r *http.Request) { 55 v := c.Version() 56 if v != nil { 57 fn(w, r, v.String()) 58 } else { 59 fn(w, r, "not_decided") 60 } 61 } 62 } 63 64 func serveVersion(w http.ResponseWriter, r *http.Request, clusterV string) { 65 if !allowMethod(w, r, "GET") { 66 return 67 } 68 vs := version.Versions{ 69 Server: version.Version, 70 Cluster: clusterV, 71 } 72 73 w.Header().Set("Content-Type", "application/json") 74 b, err := json.Marshal(&vs) 75 if err != nil { 76 plog.Panicf("cannot marshal versions to json (%v)", err) 77 } 78 w.Write(b) 79 } 80 81 func logHandleFunc(w http.ResponseWriter, r *http.Request) { 82 if !allowMethod(w, r, "PUT") { 83 return 84 } 85 86 in := struct{ Level string }{} 87 88 d := json.NewDecoder(r.Body) 89 if err := d.Decode(&in); err != nil { 90 WriteError(w, r, httptypes.NewHTTPError(http.StatusBadRequest, "Invalid json body")) 91 return 92 } 93 94 logl, err := capnslog.ParseLevel(strings.ToUpper(in.Level)) 95 if err != nil { 96 WriteError(w, r, httptypes.NewHTTPError(http.StatusBadRequest, "Invalid log level "+in.Level)) 97 return 98 } 99 100 plog.Noticef("globalLogLevel set to %q", logl.String()) 101 capnslog.SetGlobalLogLevel(logl) 102 w.WriteHeader(http.StatusNoContent) 103 } 104 105 func serveVars(w http.ResponseWriter, r *http.Request) { 106 if !allowMethod(w, r, "GET") { 107 return 108 } 109 110 w.Header().Set("Content-Type", "application/json; charset=utf-8") 111 fmt.Fprintf(w, "{\n") 112 first := true 113 expvar.Do(func(kv expvar.KeyValue) { 114 if !first { 115 fmt.Fprintf(w, ",\n") 116 } 117 first = false 118 fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value) 119 }) 120 fmt.Fprintf(w, "\n}\n") 121 } 122 123 func allowMethod(w http.ResponseWriter, r *http.Request, m string) bool { 124 if m == r.Method { 125 return true 126 } 127 w.Header().Set("Allow", m) 128 http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) 129 return false 130 } 131 132 // WriteError logs and writes the given Error to the ResponseWriter 133 // If Error is an etcdErr, it is rendered to the ResponseWriter 134 // Otherwise, it is assumed to be a StatusInternalServerError 135 func WriteError(w http.ResponseWriter, r *http.Request, err error) { 136 if err == nil { 137 return 138 } 139 switch e := err.(type) { 140 case *etcdErr.Error: 141 e.WriteTo(w) 142 case *httptypes.HTTPError: 143 if et := e.WriteTo(w); et != nil { 144 plog.Debugf("error writing HTTPError (%v) to %s", et, r.RemoteAddr) 145 } 146 default: 147 switch err { 148 case etcdserver.ErrTimeoutDueToLeaderFail, etcdserver.ErrTimeoutDueToConnectionLost, etcdserver.ErrNotEnoughStartedMembers, etcdserver.ErrUnhealthy: 149 mlog.MergeError(err) 150 default: 151 mlog.MergeErrorf("got unexpected response error (%v)", err) 152 } 153 herr := httptypes.NewHTTPError(http.StatusInternalServerError, "Internal Server Error") 154 if et := herr.WriteTo(w); et != nil { 155 plog.Debugf("error writing HTTPError (%v) to %s", et, r.RemoteAddr) 156 } 157 } 158 }