From 2950a5f1ea4f7d44ab1897c978bfa3919f1de9f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20S=CC=8Ctibrany=CC=81?= Date: Thu, 10 Dec 2020 13:42:35 +0100 Subject: [PATCH 1/6] Memberlist client can now keep buffer with sent and received messages, and display them in the admin UI. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Peter Štibraný --- pkg/ring/kv/memberlist/kv_init_service.go | 184 ++++++++++++++---- .../kv/memberlist/kv_init_service_test.go | 32 ++- pkg/ring/kv/memberlist/memberlist_client.go | 111 ++++++++++- .../kv/memberlist/memberlist_client_test.go | 18 ++ 4 files changed, 293 insertions(+), 52 deletions(-) diff --git a/pkg/ring/kv/memberlist/kv_init_service.go b/pkg/ring/kv/memberlist/kv_init_service.go index cdb20fd0a6e..c07200020ba 100644 --- a/pkg/ring/kv/memberlist/kv_init_service.go +++ b/pkg/ring/kv/memberlist/kv_init_service.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/memberlist" "go.uber.org/atomic" + "github.com/cortexproject/cortex/pkg/ring/kv/codec" "github.com/cortexproject/cortex/pkg/util" "github.com/cortexproject/cortex/pkg/util/services" ) @@ -86,64 +87,103 @@ func (kvs *KVInitService) stopping(_ error) error { func (kvs *KVInitService) ServeHTTP(w http.ResponseWriter, req *http.Request) { kv := kvs.getKV() - var ml *memberlist.Memberlist - var store map[string]valueDesc - - if kv != nil { - ml = kv.memberlist - store = kv.storeCopy() + if kv == nil { + http.Error(w, "This Cortex instance doesn't use memberlist.", http.StatusOK) + return } const ( - downloadParam = "download" - viewParam = "view" - viewFormat = "format" + downloadKeyParam = "downloadKey" + viewKeyParam = "viewKey" + viewMsgParam = "viewMsg" ) if err := req.ParseForm(); err == nil { - if req.Form[downloadParam] != nil { - download(w, store, req.Form[downloadParam][0]) // Use first value, ignore the rest. + if req.Form[downloadKeyParam] != nil { + downloadKey(w, kv.storeCopy(), req.Form[downloadKeyParam][0]) // Use first value, ignore the rest. + return + } + + if req.Form[viewKeyParam] != nil { + viewKey(w, kv, kv.storeCopy(), req.Form[viewKeyParam][0], getFormat(req)) return } - if req.Form[viewParam] != nil { - format := "" - if len(req.Form[viewFormat]) > 0 { - format = req.Form[viewFormat][0] + if req.Form[viewMsgParam] != nil { + msgID, err := strconv.Atoi(req.Form[viewMsgParam][0]) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return } - view(w, kv, store, req.Form[viewParam][0], format) + sent, received := kv.getSentAndReceivedMessages() + + for _, m := range append(sent, received...) { + if m.Id == msgID { + viewMessage(w, kv, m, getFormat(req)) + return + } + } + + http.Error(w, "message not found", http.StatusNotFound) return } } - members := ml.Members() + members := kv.memberlist.Members() sort.Slice(members, func(i, j int) bool { return members[i].Name < members[j].Name }) + sent, received := kv.getSentAndReceivedMessages() + util.RenderHTTPResponse(w, pageData{ - Now: time.Now(), - Initialized: kv != nil, - Memberlist: ml, - SortedMembers: members, - Store: store, + Now: time.Now(), + Memberlist: kv.memberlist, + SortedMembers: members, + Store: kv.storeCopy(), + SentMessages: sent, + ReceivedMessages: received, }, pageTemplate, req) } -func view(w http.ResponseWriter, kv *KV, store map[string]valueDesc, key string, format string) { - if kv == nil || store == nil || store[key].value == nil { +func getFormat(req *http.Request) string { + const viewFormat = "format" + + format := "" + if len(req.Form[viewFormat]) > 0 { + format = req.Form[viewFormat][0] + } + return format +} + +func viewMessage(w http.ResponseWriter, kv *KV, msg message, format string) { + c := kv.GetCodec(msg.Pair.Codec) + if c == nil { + http.Error(w, "codec not found", http.StatusNotFound) + return + } + + formatValue(w, c, msg.Pair.Value, format) +} + +func viewKey(w http.ResponseWriter, kv *KV, store map[string]valueDesc, key string, format string) { + if store[key].value == nil { http.Error(w, "value not found", http.StatusNotFound) return } - codec := kv.GetCodec(store[key].codecID) - if codec == nil { + c := kv.GetCodec(store[key].codecID) + if c == nil { http.Error(w, "codec not found", http.StatusNotFound) return } - val, err := codec.Decode(store[key].value) + formatValue(w, c, store[key].value, format) +} + +func formatValue(w http.ResponseWriter, codec codec.Codec, value []byte, format string) { + val, err := codec.Decode(value) if err != nil { http.Error(w, fmt.Sprintf("failed to decode: %v", err), http.StatusInternalServerError) return @@ -169,8 +209,8 @@ func view(w http.ResponseWriter, kv *KV, store map[string]valueDesc, key string, } } -func download(w http.ResponseWriter, store map[string]valueDesc, key string) { - if store == nil || store[key].value == nil { +func downloadKey(w http.ResponseWriter, store map[string]valueDesc, key string) { + if store[key].value == nil { http.Error(w, "value not found", http.StatusNotFound) return } @@ -188,11 +228,12 @@ func download(w http.ResponseWriter, store map[string]valueDesc, key string) { } type pageData struct { - Now time.Time - Initialized bool - Memberlist *memberlist.Memberlist - SortedMembers []*memberlist.Node - Store map[string]valueDesc + Now time.Time + Memberlist *memberlist.Memberlist + SortedMembers []*memberlist.Node + Store map[string]valueDesc + SentMessages []message + ReceivedMessages []message } var pageTemplate = template.Must(template.New("webpage").Parse(pageContent)) @@ -208,7 +249,6 @@ const pageContent = `

Cortex Memberlist Status

Current time: {{ .Now }}

- {{ if .Initialized }}