Files
kubesphere/vendor/github.com/prometheus-community/prom-label-proxy/injectproxy/silences.go
zackzhangkai 745ede8519 upgrade prometheus client-go
Signed-off-by: zackzhangkai <zackzhang@yunify.com>
2020-11-17 18:13:43 +08:00

177 lines
4.8 KiB
Go

// Copyright 2020 The Prometheus Authors
// 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 injectproxy
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"path"
"strconv"
"strings"
runtimeclient "github.com/go-openapi/runtime/client"
"github.com/go-openapi/strfmt"
"github.com/prometheus/alertmanager/api/v2/client"
"github.com/prometheus/alertmanager/api/v2/client/silence"
"github.com/prometheus/alertmanager/api/v2/models"
"github.com/prometheus/alertmanager/pkg/labels"
)
func (r *routes) silences(w http.ResponseWriter, req *http.Request) {
switch req.Method {
case "GET":
r.listSilences(w, req)
case "POST":
r.postSilence(w, req)
default:
http.NotFound(w, req)
}
}
func (r *routes) listSilences(w http.ResponseWriter, req *http.Request) {
var (
q = req.URL.Query()
proxyLabelMatch = labels.Matcher{
Type: labels.MatchEqual,
Name: r.label,
Value: mustLabelValue(req.Context()),
}
modified = []string{proxyLabelMatch.String()}
)
for _, filter := range q["filter"] {
m, err := labels.ParseMatcher(filter)
if err != nil {
http.Error(w, fmt.Sprintf("bad request: can't parse filter %q: %v", filter, err), http.StatusBadRequest)
return
}
if m.Name == r.label {
continue
}
modified = append(modified, filter)
}
q["filter"] = modified
q.Del(r.label)
req.URL.RawQuery = q.Encode()
r.handler.ServeHTTP(w, req)
}
func (r *routes) postSilence(w http.ResponseWriter, req *http.Request) {
var (
sil models.PostableSilence
lvalue = mustLabelValue(req.Context())
)
if err := json.NewDecoder(req.Body).Decode(&sil); err != nil {
http.Error(w, fmt.Sprintf("bad request: can't decode: %v", err), http.StatusBadRequest)
return
}
if sil.ID != "" {
// This is an update for an existing silence.
existing, err := r.getSilenceByID(req.Context(), sil.ID)
if err != nil {
http.Error(w, fmt.Sprintf("proxy error: can't get silence: %v", err), http.StatusBadGateway)
return
}
if !hasMatcherForLabel(existing.Matchers, r.label, lvalue) {
http.Error(w, "forbidden", http.StatusForbidden)
return
}
}
var falsy bool
modified := models.Matchers{
&models.Matcher{Name: &(r.label), Value: &lvalue, IsRegex: &falsy},
}
for _, m := range sil.Matchers {
if m.Name != nil && *m.Name == r.label {
continue
}
modified = append(modified, m)
}
// At least one matcher in addition to the enforced label is required,
// otherwise all alerts would be silenced
if len(modified) < 2 {
http.Error(w, "need at least one matcher, got none", http.StatusBadRequest)
return
}
sil.Matchers = modified
var buf bytes.Buffer
if err := json.NewEncoder(&buf).Encode(&sil); err != nil {
http.Error(w, fmt.Sprintf("can't encode: %v", err), http.StatusInternalServerError)
return
}
req = req.Clone(req.Context())
req.Body = ioutil.NopCloser(&buf)
req.URL.RawQuery = ""
req.Header["Content-Length"] = []string{strconv.Itoa(buf.Len())}
req.ContentLength = int64(buf.Len())
r.handler.ServeHTTP(w, req)
}
func (r *routes) deleteSilence(w http.ResponseWriter, req *http.Request) {
silID := strings.TrimPrefix(req.URL.Path, "/api/v2/silence/")
if silID == "" || silID == req.URL.Path {
http.Error(w, "bad request", http.StatusBadRequest)
return
}
// Get the silence by ID and verify that it has the expected label.
sil, err := r.getSilenceByID(req.Context(), silID)
if err != nil {
http.Error(w, fmt.Sprintf("proxy error: %v", err), http.StatusBadGateway)
return
}
if !hasMatcherForLabel(sil.Matchers, r.label, mustLabelValue(req.Context())) {
http.Error(w, "forbidden", http.StatusForbidden)
return
}
req.URL.RawQuery = ""
r.handler.ServeHTTP(w, req)
}
func (r *routes) getSilenceByID(ctx context.Context, id string) (*models.GettableSilence, error) {
amc := client.New(
runtimeclient.New(r.upstream.Host, path.Join(r.upstream.Path, "/api/v2"), []string{r.upstream.Scheme}),
strfmt.Default,
)
params := silence.NewGetSilenceParams().WithContext(ctx)
params.SetSilenceID(strfmt.UUID(id))
sil, err := amc.Silence.GetSilence(params)
if err != nil {
return nil, err
}
return sil.Payload, nil
}
func hasMatcherForLabel(matchers models.Matchers, name, value string) bool {
for _, m := range matchers {
if *m.Name == name && !*m.IsRegex && *m.Value == value {
return true
}
}
return false
}