update prometheus dependencies (#5520)
Signed-off-by: junot <junotxiang@kubesphere.io>
This commit is contained in:
25
vendor/github.com/prometheus-community/prom-label-proxy/injectproxy/alerts.go
generated
vendored
Normal file
25
vendor/github.com/prometheus-community/prom-label-proxy/injectproxy/alerts.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
// 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 "net/http"
|
||||
|
||||
func (r *routes) alerts(w http.ResponseWriter, req *http.Request) {
|
||||
switch req.Method {
|
||||
case "GET":
|
||||
r.enforceFilterParameter(w, req)
|
||||
default:
|
||||
http.NotFound(w, req)
|
||||
}
|
||||
}
|
||||
49
vendor/github.com/prometheus-community/prom-label-proxy/injectproxy/enforce.go
generated
vendored
49
vendor/github.com/prometheus-community/prom-label-proxy/injectproxy/enforce.go
generated
vendored
@@ -16,15 +16,16 @@ package injectproxy
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/prometheus/prometheus/pkg/labels"
|
||||
"github.com/prometheus/prometheus/model/labels"
|
||||
"github.com/prometheus/prometheus/promql/parser"
|
||||
)
|
||||
|
||||
type Enforcer struct {
|
||||
labelMatchers map[string]*labels.Matcher
|
||||
labelMatchers map[string]*labels.Matcher
|
||||
errorOnReplace bool
|
||||
}
|
||||
|
||||
func NewEnforcer(ms ...*labels.Matcher) *Enforcer {
|
||||
func NewEnforcer(errorOnReplace bool, ms ...*labels.Matcher) *Enforcer {
|
||||
entries := make(map[string]*labels.Matcher)
|
||||
|
||||
for _, matcher := range ms {
|
||||
@@ -32,7 +33,20 @@ func NewEnforcer(ms ...*labels.Matcher) *Enforcer {
|
||||
}
|
||||
|
||||
return &Enforcer{
|
||||
labelMatchers: entries,
|
||||
labelMatchers: entries,
|
||||
errorOnReplace: errorOnReplace,
|
||||
}
|
||||
}
|
||||
|
||||
type IllegalLabelMatcherError struct {
|
||||
msg string
|
||||
}
|
||||
|
||||
func (e IllegalLabelMatcherError) Error() string { return e.msg }
|
||||
|
||||
func newIllegalLabelMatcherError(existing string, replacement string) IllegalLabelMatcherError {
|
||||
return IllegalLabelMatcherError{
|
||||
msg: fmt.Sprintf("label matcher value (%s) conflicts with injected value (%s)", existing, replacement),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,12 +111,20 @@ func (ms Enforcer) EnforceNode(node parser.Node) error {
|
||||
case *parser.MatrixSelector:
|
||||
// inject labelselector
|
||||
if vs, ok := n.VectorSelector.(*parser.VectorSelector); ok {
|
||||
vs.LabelMatchers = ms.enforceMatchers(vs.LabelMatchers)
|
||||
var err error
|
||||
vs.LabelMatchers, err = ms.EnforceMatchers(vs.LabelMatchers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
case *parser.VectorSelector:
|
||||
// inject labelselector
|
||||
n.LabelMatchers = ms.enforceMatchers(n.LabelMatchers)
|
||||
var err error
|
||||
n.LabelMatchers, err = ms.EnforceMatchers(n.LabelMatchers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
default:
|
||||
panic(fmt.Errorf("parser.Walk: unhandled node type %T", n))
|
||||
@@ -111,11 +133,20 @@ func (ms Enforcer) EnforceNode(node parser.Node) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ms Enforcer) enforceMatchers(targets []*labels.Matcher) []*labels.Matcher {
|
||||
// EnforceMatchers appends the configured label matcher if not present.
|
||||
// If the label matcher that is to be injected is present (by labelname) but
|
||||
// different (either by match type or value) the behavior depends on the
|
||||
// errorOnReplace variable. If errorOnReplace is true an error is returned,
|
||||
// otherwise the label matcher is silently replaced.
|
||||
func (ms Enforcer) EnforceMatchers(targets []*labels.Matcher) ([]*labels.Matcher, error) {
|
||||
var res []*labels.Matcher
|
||||
|
||||
for _, target := range targets {
|
||||
if _, ok := ms.labelMatchers[target.Name]; ok {
|
||||
if matcher, ok := ms.labelMatchers[target.Name]; ok {
|
||||
// matcher.String() returns something like "labelfoo=value"
|
||||
if ms.errorOnReplace && matcher.String() != target.String() {
|
||||
return res, newIllegalLabelMatcherError(matcher.String(), target.String())
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -126,5 +157,5 @@ func (ms Enforcer) enforceMatchers(targets []*labels.Matcher) []*labels.Matcher
|
||||
res = append(res, enforcedMatcher)
|
||||
}
|
||||
|
||||
return res
|
||||
return res, nil
|
||||
}
|
||||
|
||||
531
vendor/github.com/prometheus-community/prom-label-proxy/injectproxy/routes.go
generated
vendored
531
vendor/github.com/prometheus-community/prom-label-proxy/injectproxy/routes.go
generated
vendored
@@ -15,61 +15,338 @@ package injectproxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/prometheus/prometheus/pkg/labels"
|
||||
"github.com/efficientgo/tools/core/pkg/merrors"
|
||||
"github.com/metalmatze/signal/server/signalhttp"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/prometheus/model/labels"
|
||||
"github.com/prometheus/prometheus/promql/parser"
|
||||
)
|
||||
|
||||
const (
|
||||
queryParam = "query"
|
||||
matchersParam = "match[]"
|
||||
)
|
||||
|
||||
type routes struct {
|
||||
upstream *url.URL
|
||||
handler http.Handler
|
||||
label string
|
||||
mux *http.ServeMux
|
||||
modifiers map[string]func(*http.Response) error
|
||||
upstream *url.URL
|
||||
handler http.Handler
|
||||
label string
|
||||
el ExtractLabeler
|
||||
|
||||
mux http.Handler
|
||||
modifiers map[string]func(*http.Response) error
|
||||
errorOnReplace bool
|
||||
}
|
||||
|
||||
func NewRoutes(upstream *url.URL, label string) *routes {
|
||||
type options struct {
|
||||
enableLabelAPIs bool
|
||||
passthroughPaths []string
|
||||
errorOnReplace bool
|
||||
registerer prometheus.Registerer
|
||||
}
|
||||
|
||||
type Option interface {
|
||||
apply(*options)
|
||||
}
|
||||
|
||||
type optionFunc func(*options)
|
||||
|
||||
func (f optionFunc) apply(o *options) {
|
||||
f(o)
|
||||
}
|
||||
|
||||
// WithPrometheusRegistry configures the proxy to use the given registerer.
|
||||
func WithPrometheusRegistry(reg prometheus.Registerer) Option {
|
||||
return optionFunc(func(o *options) {
|
||||
o.registerer = reg
|
||||
})
|
||||
}
|
||||
|
||||
// WithEnabledLabelsAPI enables proxying to labels API. If false, "501 Not implemented" will be return for those.
|
||||
func WithEnabledLabelsAPI() Option {
|
||||
return optionFunc(func(o *options) {
|
||||
o.enableLabelAPIs = true
|
||||
})
|
||||
}
|
||||
|
||||
// WithPassthroughPaths configures routes to register given paths as passthrough handlers for all HTTP methods.
|
||||
// that, if requested, will be forwarded without enforcing label. Use with care.
|
||||
// NOTE: Passthrough "all" paths like "/" or "" and regex are not allowed.
|
||||
func WithPassthroughPaths(paths []string) Option {
|
||||
return optionFunc(func(o *options) {
|
||||
o.passthroughPaths = paths
|
||||
})
|
||||
}
|
||||
|
||||
// WithErrorOnReplace causes the proxy to return 400 if a label matcher we want to
|
||||
// inject is present in the query already and matches something different
|
||||
func WithErrorOnReplace() Option {
|
||||
return optionFunc(func(o *options) {
|
||||
o.errorOnReplace = true
|
||||
})
|
||||
}
|
||||
|
||||
// mux abstracts away the behavior we expect from the http.ServeMux type in this package.
|
||||
type mux interface {
|
||||
http.Handler
|
||||
Handle(string, http.Handler)
|
||||
}
|
||||
|
||||
// strictMux is a mux that wraps standard HTTP handler with safer handler that allows safe user provided handler registrations.
|
||||
type strictMux struct {
|
||||
mux
|
||||
seen map[string]struct{}
|
||||
}
|
||||
|
||||
func newStrictMux(m mux) *strictMux {
|
||||
return &strictMux{
|
||||
m,
|
||||
map[string]struct{}{},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Handle is like HTTP mux handle but it does not allow to register paths that are shared with previously registered paths.
|
||||
// It also makes sure the trailing / is registered too.
|
||||
// For example if /api/v1/federate was registered consequent registrations like /api/v1/federate/ or /api/v1/federate/some will
|
||||
// return error. In the mean time request with both /api/v1/federate and /api/v1/federate/ will point to the handled passed by /api/v1/federate
|
||||
// registration.
|
||||
// This allows to de-risk ability for user to mis-configure and leak inject isolation.
|
||||
func (s *strictMux) Handle(pattern string, handler http.Handler) error {
|
||||
sanitized := pattern
|
||||
for next := strings.TrimSuffix(sanitized, "/"); next != sanitized; sanitized = next {
|
||||
}
|
||||
|
||||
if _, ok := s.seen[sanitized]; ok {
|
||||
return errors.Errorf("pattern %q was already registered", sanitized)
|
||||
}
|
||||
|
||||
for p := range s.seen {
|
||||
if strings.HasPrefix(sanitized+"/", p+"/") {
|
||||
return errors.Errorf("pattern %q is registered, cannot register path %q that shares it", p, sanitized)
|
||||
}
|
||||
}
|
||||
|
||||
s.mux.Handle(sanitized, handler)
|
||||
s.mux.Handle(sanitized+"/", handler)
|
||||
s.seen[sanitized] = struct{}{}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// instrumentedMux wraps a mux and instruments it.
|
||||
type instrumentedMux struct {
|
||||
mux
|
||||
i signalhttp.HandlerInstrumenter
|
||||
}
|
||||
|
||||
func newInstrumentedMux(m mux, r prometheus.Registerer) *instrumentedMux {
|
||||
return &instrumentedMux{
|
||||
m,
|
||||
signalhttp.NewHandlerInstrumenter(r, []string{"handler"}),
|
||||
}
|
||||
}
|
||||
|
||||
// Handle implements the mux interface.
|
||||
func (i *instrumentedMux) Handle(pattern string, handler http.Handler) {
|
||||
i.mux.Handle(pattern, i.i.NewHandler(prometheus.Labels{"handler": pattern}, handler))
|
||||
}
|
||||
|
||||
// ExtractLabeler is an HTTP handler that extract the label value to be
|
||||
// enforced from the HTTP request. If a valid label value is found, it should
|
||||
// store it in the request's context. Otherwise it should return an error in
|
||||
// the HTTP response (usually 400 or 500).
|
||||
type ExtractLabeler interface {
|
||||
ExtractLabel(next http.HandlerFunc) http.Handler
|
||||
}
|
||||
|
||||
// HTTPFormEnforcer enforces a label value extracted from the HTTP form and query parameters.
|
||||
type HTTPFormEnforcer struct {
|
||||
ParameterName string
|
||||
}
|
||||
|
||||
// ExtractLabel implements the ExtractLabeler interface.
|
||||
func (hff HTTPFormEnforcer) ExtractLabel(next http.HandlerFunc) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
labelValue, err := hff.getLabelValue(r)
|
||||
if err != nil {
|
||||
prometheusAPIError(w, humanFriendlyErrorMessage(err), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Remove the proxy label from the query parameters.
|
||||
q := r.URL.Query()
|
||||
q.Del(hff.ParameterName)
|
||||
r.URL.RawQuery = q.Encode()
|
||||
|
||||
// Remove the param from the PostForm.
|
||||
if r.Method == http.MethodPost {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
prometheusAPIError(w, fmt.Sprintf("Failed to parse the PostForm: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if r.PostForm.Get(hff.ParameterName) != "" {
|
||||
r.PostForm.Del(hff.ParameterName)
|
||||
newBody := r.PostForm.Encode()
|
||||
// We are replacing request body, close previous one (r.FormValue ensures it is read fully and not nil).
|
||||
_ = r.Body.Close()
|
||||
r.Body = io.NopCloser(strings.NewReader(newBody))
|
||||
r.ContentLength = int64(len(newBody))
|
||||
}
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r.WithContext(WithLabelValue(r.Context(), labelValue)))
|
||||
})
|
||||
}
|
||||
|
||||
func (hff HTTPFormEnforcer) getLabelValue(r *http.Request) (string, error) {
|
||||
formValue := r.FormValue(hff.ParameterName)
|
||||
if formValue == "" {
|
||||
return "", fmt.Errorf("the %q query parameter must be provided", hff.ParameterName)
|
||||
}
|
||||
|
||||
return formValue, nil
|
||||
}
|
||||
|
||||
// HTTPHeaderEnforcer enforces a label value extracted from the HTTP headers.
|
||||
type HTTPHeaderEnforcer struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
// ExtractLabel implements the ExtractLabeler interface.
|
||||
func (hhe HTTPHeaderEnforcer) ExtractLabel(next http.HandlerFunc) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
labelValue, err := hhe.getLabelValue(r)
|
||||
if err != nil {
|
||||
prometheusAPIError(w, humanFriendlyErrorMessage(err), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r.WithContext(WithLabelValue(r.Context(), labelValue)))
|
||||
})
|
||||
}
|
||||
|
||||
func (hhe HTTPHeaderEnforcer) getLabelValue(r *http.Request) (string, error) {
|
||||
headerValues := r.Header[hhe.Name]
|
||||
|
||||
if len(headerValues) == 0 {
|
||||
return "", fmt.Errorf("missing HTTP header %q", hhe.Name)
|
||||
}
|
||||
|
||||
if len(headerValues) > 1 {
|
||||
return "", fmt.Errorf("multiple values for the http header %q", hhe.Name)
|
||||
}
|
||||
|
||||
return headerValues[0], nil
|
||||
}
|
||||
|
||||
// StaticLabelEnforcer enforces a static label value.
|
||||
type StaticLabelEnforcer string
|
||||
|
||||
// ExtractLabel implements the ExtractLabeler interface.
|
||||
func (sle StaticLabelEnforcer) ExtractLabel(next http.HandlerFunc) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
next(w, r.WithContext(WithLabelValue(r.Context(), string(sle))))
|
||||
})
|
||||
}
|
||||
|
||||
func NewRoutes(upstream *url.URL, label string, extractLabeler ExtractLabeler, opts ...Option) (*routes, error) {
|
||||
opt := options{}
|
||||
for _, o := range opts {
|
||||
o.apply(&opt)
|
||||
}
|
||||
|
||||
if opt.registerer == nil {
|
||||
opt.registerer = prometheus.NewRegistry()
|
||||
}
|
||||
|
||||
proxy := httputil.NewSingleHostReverseProxy(upstream)
|
||||
|
||||
r := &routes{
|
||||
upstream: upstream,
|
||||
handler: proxy,
|
||||
label: label,
|
||||
upstream: upstream,
|
||||
handler: proxy,
|
||||
label: label,
|
||||
el: extractLabeler,
|
||||
errorOnReplace: opt.errorOnReplace,
|
||||
}
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("/federate", enforceMethods(r.federate, "GET"))
|
||||
mux.Handle("/api/v1/query", enforceMethods(r.query, "GET", "POST"))
|
||||
mux.Handle("/api/v1/query_range", enforceMethods(r.query, "GET", "POST"))
|
||||
mux.Handle("/api/v1/alerts", enforceMethods(r.noop, "GET"))
|
||||
mux.Handle("/api/v1/rules", enforceMethods(r.noop, "GET"))
|
||||
mux.Handle("/api/v2/silences", enforceMethods(r.silences, "GET", "POST"))
|
||||
mux.Handle("/api/v2/silences/", enforceMethods(r.silences, "GET", "POST"))
|
||||
mux.Handle("/api/v2/silence/", enforceMethods(r.deleteSilence, "DELETE"))
|
||||
mux := newStrictMux(newInstrumentedMux(http.NewServeMux(), opt.registerer))
|
||||
|
||||
errs := merrors.New(
|
||||
mux.Handle("/federate", r.el.ExtractLabel(enforceMethods(r.matcher, "GET"))),
|
||||
mux.Handle("/api/v1/query", r.el.ExtractLabel(enforceMethods(r.query, "GET", "POST"))),
|
||||
mux.Handle("/api/v1/query_range", r.el.ExtractLabel(enforceMethods(r.query, "GET", "POST"))),
|
||||
mux.Handle("/api/v1/alerts", r.el.ExtractLabel(enforceMethods(r.passthrough, "GET"))),
|
||||
mux.Handle("/api/v1/rules", r.el.ExtractLabel(enforceMethods(r.passthrough, "GET"))),
|
||||
mux.Handle("/api/v1/series", r.el.ExtractLabel(enforceMethods(r.matcher, "GET", "POST"))),
|
||||
mux.Handle("/api/v1/query_exemplars", r.el.ExtractLabel(enforceMethods(r.query, "GET", "POST"))),
|
||||
)
|
||||
|
||||
if opt.enableLabelAPIs {
|
||||
errs.Add(
|
||||
mux.Handle("/api/v1/labels", r.el.ExtractLabel(enforceMethods(r.matcher, "GET", "POST"))),
|
||||
// Full path is /api/v1/label/<label_name>/values but http mux does not support patterns.
|
||||
// This is fine though as we don't care about name for matcher injector.
|
||||
mux.Handle("/api/v1/label/", r.el.ExtractLabel(enforceMethods(r.matcher, "GET"))),
|
||||
)
|
||||
}
|
||||
|
||||
errs.Add(
|
||||
mux.Handle("/api/v2/silences", r.el.ExtractLabel(enforceMethods(r.silences, "GET", "POST"))),
|
||||
mux.Handle("/api/v2/silence/", r.el.ExtractLabel(enforceMethods(r.deleteSilence, "DELETE"))),
|
||||
mux.Handle("/api/v2/alerts/groups", r.el.ExtractLabel(enforceMethods(r.enforceFilterParameter, "GET"))),
|
||||
mux.Handle("/api/v2/alerts", r.el.ExtractLabel(enforceMethods(r.alerts, "GET"))),
|
||||
)
|
||||
|
||||
errs.Add(
|
||||
mux.Handle("/healthz", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
_ = json.NewEncoder(w).Encode(map[string]bool{"ok": true})
|
||||
})),
|
||||
)
|
||||
|
||||
if err := errs.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Validate paths.
|
||||
for _, path := range opt.passthroughPaths {
|
||||
u, err := url.Parse(fmt.Sprintf("http://example.com%v", path))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("path %q is not a valid URI path, got %v", path, opt.passthroughPaths)
|
||||
}
|
||||
if u.Path != path {
|
||||
return nil, fmt.Errorf("path %q is not a valid URI path, got %v", path, opt.passthroughPaths)
|
||||
}
|
||||
if u.Path == "" || u.Path == "/" {
|
||||
return nil, fmt.Errorf("path %q is not allowed, got %v", u.Path, opt.passthroughPaths)
|
||||
}
|
||||
}
|
||||
|
||||
// Register optional passthrough paths.
|
||||
for _, path := range opt.passthroughPaths {
|
||||
if err := mux.Handle(path, http.HandlerFunc(r.passthrough)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
r.mux = mux
|
||||
r.modifiers = map[string]func(*http.Response) error{
|
||||
"/api/v1/rules": modifyAPIResponse(r.filterRules),
|
||||
"/api/v1/alerts": modifyAPIResponse(r.filterAlerts),
|
||||
}
|
||||
proxy.ModifyResponse = r.ModifyResponse
|
||||
return r
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (r *routes) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
lvalue := req.URL.Query().Get(r.label)
|
||||
if lvalue == "" {
|
||||
http.Error(w, fmt.Sprintf("Bad request. The %q query parameter must be provided.", r.label), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
req = req.WithContext(withLabelValue(req.Context(), lvalue))
|
||||
// Remove the proxy label from the query parameters.
|
||||
q := req.URL.Query()
|
||||
q.Del(r.label)
|
||||
req.URL.RawQuery = q.Encode()
|
||||
|
||||
r.mux.ServeHTTP(w, req)
|
||||
}
|
||||
|
||||
@@ -82,8 +359,8 @@ func (r *routes) ModifyResponse(resp *http.Response) error {
|
||||
return m(resp)
|
||||
}
|
||||
|
||||
func enforceMethods(h http.HandlerFunc, methods ...string) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
func enforceMethods(h http.HandlerFunc, methods ...string) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, req *http.Request) {
|
||||
for _, m := range methods {
|
||||
if m == req.Method {
|
||||
h(w, req)
|
||||
@@ -91,14 +368,17 @@ func enforceMethods(h http.HandlerFunc, methods ...string) http.Handler {
|
||||
}
|
||||
}
|
||||
http.NotFound(w, req)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type ctxKey int
|
||||
|
||||
const keyLabel ctxKey = iota
|
||||
|
||||
func mustLabelValue(ctx context.Context) string {
|
||||
// MustLabelValue returns a label (previously stored using WithLabelValue())
|
||||
// from the given context.
|
||||
// It will panic if no label is found or the value is empty.
|
||||
func MustLabelValue(ctx context.Context) string {
|
||||
label, ok := ctx.Value(keyLabel).(string)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("can't find the %q value in the context", keyLabel))
|
||||
@@ -109,48 +389,187 @@ func mustLabelValue(ctx context.Context) string {
|
||||
return label
|
||||
}
|
||||
|
||||
func withLabelValue(ctx context.Context, label string) context.Context {
|
||||
// WithLabelValue stores a label in the given context.
|
||||
func WithLabelValue(ctx context.Context, label string) context.Context {
|
||||
return context.WithValue(ctx, keyLabel, label)
|
||||
}
|
||||
|
||||
func (r *routes) noop(w http.ResponseWriter, req *http.Request) {
|
||||
func (r *routes) passthrough(w http.ResponseWriter, req *http.Request) {
|
||||
r.handler.ServeHTTP(w, req)
|
||||
}
|
||||
|
||||
func (r *routes) query(w http.ResponseWriter, req *http.Request) {
|
||||
expr, err := parser.ParseExpr(req.FormValue("query"))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
e := NewEnforcer([]*labels.Matcher{
|
||||
{
|
||||
e := NewEnforcer(r.errorOnReplace,
|
||||
[]*labels.Matcher{{
|
||||
Name: r.label,
|
||||
Type: labels.MatchEqual,
|
||||
Value: mustLabelValue(req.Context()),
|
||||
},
|
||||
}...)
|
||||
if err := e.EnforceNode(expr); err != nil {
|
||||
Value: MustLabelValue(req.Context()),
|
||||
}}...)
|
||||
|
||||
// The `query` can come in the URL query string and/or the POST body.
|
||||
// For this reason, we need to try to enforcing in both places.
|
||||
// Note: a POST request may include some values in the URL query string
|
||||
// and others in the body. If both locations include a `query`, then
|
||||
// enforce in both places.
|
||||
q, found1, err := enforceQueryValues(e, req.URL.Query())
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case IllegalLabelMatcherError:
|
||||
prometheusAPIError(w, err.Error(), http.StatusBadRequest)
|
||||
case queryParseError:
|
||||
prometheusAPIError(w, err.Error(), http.StatusBadRequest)
|
||||
case enforceLabelError:
|
||||
prometheusAPIError(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
return
|
||||
}
|
||||
req.URL.RawQuery = q
|
||||
|
||||
q := req.URL.Query()
|
||||
q.Set("query", expr.String())
|
||||
req.URL.RawQuery = q.Encode()
|
||||
var found2 bool
|
||||
// Enforce the query in the POST body if needed.
|
||||
if req.Method == http.MethodPost {
|
||||
if err := req.ParseForm(); err != nil {
|
||||
prometheusAPIError(w, err.Error(), http.StatusBadRequest)
|
||||
}
|
||||
q, found2, err = enforceQueryValues(e, req.PostForm)
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case IllegalLabelMatcherError:
|
||||
prometheusAPIError(w, err.Error(), http.StatusBadRequest)
|
||||
case queryParseError:
|
||||
prometheusAPIError(w, err.Error(), http.StatusBadRequest)
|
||||
case enforceLabelError:
|
||||
prometheusAPIError(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
return
|
||||
}
|
||||
// We are replacing request body, close previous one (ParseForm ensures it is read fully and not nil).
|
||||
_ = req.Body.Close()
|
||||
req.Body = io.NopCloser(strings.NewReader(q))
|
||||
req.ContentLength = int64(len(q))
|
||||
}
|
||||
|
||||
// If no query was found, return early.
|
||||
if !found1 && !found2 {
|
||||
return
|
||||
}
|
||||
|
||||
r.handler.ServeHTTP(w, req)
|
||||
}
|
||||
|
||||
func (r *routes) federate(w http.ResponseWriter, req *http.Request) {
|
||||
func enforceQueryValues(e *Enforcer, v url.Values) (values string, noQuery bool, err error) {
|
||||
// If no values were given or no query is present,
|
||||
// e.g. because the query came in the POST body
|
||||
// but the URL query string was passed, then finish early.
|
||||
if v.Get(queryParam) == "" {
|
||||
return v.Encode(), false, nil
|
||||
}
|
||||
expr, err := parser.ParseExpr(v.Get(queryParam))
|
||||
if err != nil {
|
||||
queryParseError := newQueryParseError(err)
|
||||
return "", true, queryParseError
|
||||
}
|
||||
|
||||
if err := e.EnforceNode(expr); err != nil {
|
||||
if _, ok := err.(IllegalLabelMatcherError); ok {
|
||||
return "", true, err
|
||||
}
|
||||
enforceLabelError := newEnforceLabelError(err)
|
||||
return "", true, enforceLabelError
|
||||
}
|
||||
|
||||
v.Set(queryParam, expr.String())
|
||||
return v.Encode(), true, nil
|
||||
}
|
||||
|
||||
// matcher ensures all the provided match[] if any has label injected. If none was provided, single matcher is injected.
|
||||
// This works for non-query Prometheus APIs like: /api/v1/series, /api/v1/label/<name>/values, /api/v1/labels and /federate support multiple matchers.
|
||||
// See e.g https://prometheus.io/docs/prometheus/latest/querying/api/#querying-metadata
|
||||
func (r *routes) matcher(w http.ResponseWriter, req *http.Request) {
|
||||
matcher := &labels.Matcher{
|
||||
Name: r.label,
|
||||
Type: labels.MatchEqual,
|
||||
Value: mustLabelValue(req.Context()),
|
||||
Value: MustLabelValue(req.Context()),
|
||||
}
|
||||
|
||||
q := req.URL.Query()
|
||||
q.Set("match[]", "{"+matcher.String()+"}")
|
||||
req.URL.RawQuery = q.Encode()
|
||||
|
||||
if err := injectMatcher(q, matcher); err != nil {
|
||||
return
|
||||
}
|
||||
req.URL.RawQuery = q.Encode()
|
||||
if req.Method == http.MethodPost {
|
||||
if err := req.ParseForm(); err != nil {
|
||||
return
|
||||
}
|
||||
q = req.PostForm
|
||||
if err := injectMatcher(q, matcher); err != nil {
|
||||
return
|
||||
}
|
||||
// We are replacing request body, close previous one (ParseForm ensures it is read fully and not nil).
|
||||
_ = req.Body.Close()
|
||||
newBody := q.Encode()
|
||||
req.Body = io.NopCloser(strings.NewReader(newBody))
|
||||
req.ContentLength = int64(len(newBody))
|
||||
}
|
||||
r.handler.ServeHTTP(w, req)
|
||||
}
|
||||
|
||||
func injectMatcher(q url.Values, matcher *labels.Matcher) error {
|
||||
matchers := q[matchersParam]
|
||||
if len(matchers) == 0 {
|
||||
q.Set(matchersParam, matchersToString(matcher))
|
||||
} else {
|
||||
// Inject label to existing matchers.
|
||||
for i, m := range matchers {
|
||||
ms, err := parser.ParseMetricSelector(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
matchers[i] = matchersToString(append(ms, matcher)...)
|
||||
}
|
||||
q[matchersParam] = matchers
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func matchersToString(ms ...*labels.Matcher) string {
|
||||
var el []string
|
||||
for _, m := range ms {
|
||||
el = append(el, m.String())
|
||||
}
|
||||
return fmt.Sprintf("{%v}", strings.Join(el, ","))
|
||||
}
|
||||
|
||||
type queryParseError struct {
|
||||
msg string
|
||||
}
|
||||
|
||||
func (e queryParseError) Error() string {
|
||||
return e.msg
|
||||
}
|
||||
|
||||
func newQueryParseError(err error) queryParseError {
|
||||
return queryParseError{msg: fmt.Sprintf("error parsing query string %q", err.Error())}
|
||||
}
|
||||
|
||||
type enforceLabelError struct {
|
||||
msg string
|
||||
}
|
||||
|
||||
func (e enforceLabelError) Error() string {
|
||||
return e.msg
|
||||
}
|
||||
|
||||
func newEnforceLabelError(err error) enforceLabelError {
|
||||
return enforceLabelError{msg: fmt.Sprintf("error enforcing label %q", err.Error())}
|
||||
}
|
||||
|
||||
// humanFriendlyErrorMessage returns an error message with a capitalized first letter
|
||||
// and a punctuation at the end.
|
||||
func humanFriendlyErrorMessage(err error) string {
|
||||
if err == nil {
|
||||
return ""
|
||||
}
|
||||
errMsg := err.Error()
|
||||
return fmt.Sprintf("%s%s.", strings.ToUpper(errMsg[:1]), errMsg[1:])
|
||||
}
|
||||
|
||||
8
vendor/github.com/prometheus-community/prom-label-proxy/injectproxy/rules.go
generated
vendored
8
vendor/github.com/prometheus-community/prom-label-proxy/injectproxy/rules.go
generated
vendored
@@ -18,12 +18,12 @@ import (
|
||||
"compress/gzip"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prometheus/prometheus/pkg/labels"
|
||||
"github.com/prometheus/prometheus/model/labels"
|
||||
)
|
||||
|
||||
type apiResponse struct {
|
||||
@@ -175,7 +175,7 @@ func modifyAPIResponse(f func(string, *apiResponse) (interface{}, error)) func(*
|
||||
return errors.Wrap(err, "can't decode API response")
|
||||
}
|
||||
|
||||
v, err := f(mustLabelValue(resp.Request.Context()), apir)
|
||||
v, err := f(MustLabelValue(resp.Request.Context()), apir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -190,7 +190,7 @@ func modifyAPIResponse(f func(string, *apiResponse) (interface{}, error)) func(*
|
||||
if err = json.NewEncoder(&buf).Encode(apir); err != nil {
|
||||
return errors.Wrap(err, "can't encode API response")
|
||||
}
|
||||
resp.Body = ioutil.NopCloser(&buf)
|
||||
resp.Body = io.NopCloser(&buf)
|
||||
resp.Header["Content-Length"] = []string{fmt.Sprint(buf.Len())}
|
||||
|
||||
return nil
|
||||
|
||||
32
vendor/github.com/prometheus-community/prom-label-proxy/injectproxy/silences.go
generated
vendored
32
vendor/github.com/prometheus-community/prom-label-proxy/injectproxy/silences.go
generated
vendored
@@ -18,7 +18,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"path"
|
||||
"strconv"
|
||||
@@ -35,7 +35,7 @@ import (
|
||||
func (r *routes) silences(w http.ResponseWriter, req *http.Request) {
|
||||
switch req.Method {
|
||||
case "GET":
|
||||
r.listSilences(w, req)
|
||||
r.enforceFilterParameter(w, req)
|
||||
case "POST":
|
||||
r.postSilence(w, req)
|
||||
default:
|
||||
@@ -43,20 +43,20 @@ func (r *routes) silences(w http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
func (r *routes) listSilences(w http.ResponseWriter, req *http.Request) {
|
||||
func (r *routes) enforceFilterParameter(w http.ResponseWriter, req *http.Request) {
|
||||
var (
|
||||
q = req.URL.Query()
|
||||
proxyLabelMatch = labels.Matcher{
|
||||
Type: labels.MatchEqual,
|
||||
Name: r.label,
|
||||
Value: mustLabelValue(req.Context()),
|
||||
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)
|
||||
prometheusAPIError(w, fmt.Sprintf("bad request: can't parse filter %q: %v", filter, err), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if m.Name == r.label {
|
||||
@@ -75,10 +75,10 @@ func (r *routes) listSilences(w http.ResponseWriter, req *http.Request) {
|
||||
func (r *routes) postSilence(w http.ResponseWriter, req *http.Request) {
|
||||
var (
|
||||
sil models.PostableSilence
|
||||
lvalue = mustLabelValue(req.Context())
|
||||
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)
|
||||
prometheusAPIError(w, fmt.Sprintf("bad request: can't decode: %v", err), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -86,12 +86,12 @@ func (r *routes) postSilence(w http.ResponseWriter, req *http.Request) {
|
||||
// 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)
|
||||
prometheusAPIError(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)
|
||||
prometheusAPIError(w, "forbidden", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -109,19 +109,19 @@ func (r *routes) postSilence(w http.ResponseWriter, req *http.Request) {
|
||||
// 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)
|
||||
prometheusAPIError(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)
|
||||
prometheusAPIError(w, fmt.Sprintf("can't encode: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
req = req.Clone(req.Context())
|
||||
req.Body = ioutil.NopCloser(&buf)
|
||||
req.Body = io.NopCloser(&buf)
|
||||
req.URL.RawQuery = ""
|
||||
req.Header["Content-Length"] = []string{strconv.Itoa(buf.Len())}
|
||||
req.ContentLength = int64(buf.Len())
|
||||
@@ -132,19 +132,19 @@ func (r *routes) postSilence(w http.ResponseWriter, req *http.Request) {
|
||||
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)
|
||||
prometheusAPIError(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)
|
||||
prometheusAPIError(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)
|
||||
if !hasMatcherForLabel(sil.Matchers, r.label, MustLabelValue(req.Context())) {
|
||||
prometheusAPIError(w, "forbidden", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
32
vendor/github.com/prometheus-community/prom-label-proxy/injectproxy/utils.go
generated
vendored
Normal file
32
vendor/github.com/prometheus-community/prom-label-proxy/injectproxy/utils.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
// 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 (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func prometheusAPIError(w http.ResponseWriter, errorMessage string, code int) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
w.Header().Set("X-Content-Type-Options", "nosniff")
|
||||
w.WriteHeader(code)
|
||||
|
||||
res := map[string]string{"status": "error", "errorType": "prom-label-proxy", "error": errorMessage}
|
||||
|
||||
if err := json.NewEncoder(w).Encode(res); err != nil {
|
||||
log.Printf("error: Failed to encode json: %v", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user