blob: f0d3b0bd3d2aff12dce86d71a25217179738d1c9 [file] [log] [blame]
khenaidooffe076b2019-01-15 16:08:08 -05001// 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
15package etcdhttp
16
17import (
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
33var (
34 plog = capnslog.NewPackageLogger("github.com/coreos/etcd", "etcdserver/api/etcdhttp")
35 mlog = logutil.NewMergeLogger(plog)
36)
37
38const (
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.
46func 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
53func 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
64func 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
81func 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
105func 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
123func 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
135func 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}