From 04ae8b3be22664ee39496a5556c7507e5ffeea9f Mon Sep 17 00:00:00 2001 From: Joel Takvorian Date: Wed, 1 Mar 2023 08:08:48 +0100 Subject: [PATCH] NETOBSERV-870 implement TokenReview (#283) * NETOBSERV-870 implement TokenReview Requires operator PR to grant permission for TokenReviews - 3 auth modes: check for cluster-admin, check for any user, no check (insecure; only for debugging/dev mode) - add tests - fix broken dev mode * Merge check implementations * Add 'auto' mode Auto mode acts either as admin or authenticated depending on the loki authtoken mode (forward => authenticated) Also improve loki errors hanfling --- Makefile | 2 +- cmd/plugin-backend.go | 25 +- go.mod | 2 +- pkg/handler/auth/check_auth.go | 66 --- pkg/handler/loki.go | 23 +- pkg/handler/resources.go | 4 +- pkg/kubernetes/auth/check_auth.go | 123 +++++ pkg/kubernetes/auth/check_auth_test.go | 212 +++++++ pkg/kubernetes/client/client.go | 38 ++ pkg/server/routes.go | 2 +- pkg/server/server.go | 2 +- pkg/server/server_test.go | 2 +- .../prometheus/promhttp/delegator.go | 368 ------------- .../client_golang/prometheus/promhttp/http.go | 383 ------------- .../prometheus/promhttp/instrument_client.go | 235 -------- .../prometheus/promhttp/instrument_server.go | 517 ------------------ .../prometheus/promhttp/option.go | 31 -- vendor/modules.txt | 1 - 18 files changed, 415 insertions(+), 1621 deletions(-) delete mode 100644 pkg/handler/auth/check_auth.go create mode 100644 pkg/kubernetes/auth/check_auth.go create mode 100644 pkg/kubernetes/auth/check_auth_test.go create mode 100644 pkg/kubernetes/client/client.go delete mode 100644 vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator.go delete mode 100644 vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go delete mode 100644 vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go delete mode 100644 vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go delete mode 100644 vendor/github.com/prometheus/client_golang/prometheus/promhttp/option.go diff --git a/Makefile b/Makefile index 6aee16c72..cec8406d5 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ GOLANGCI_LINT_VERSION = v1.50.1 COVERPROFILE = coverage.out NPM_INSTALL ?= install -CMDLINE_ARGS ?= --loglevel trace --loki-tenant-id netobserv --frontend-config config/sample-frontend-config.yaml +CMDLINE_ARGS ?= --loglevel trace --loki-tenant-id netobserv --frontend-config config/sample-frontend-config.yaml --auth-check none ifeq (,$(shell which podman 2>/dev/null)) OCI_BIN ?= docker diff --git a/cmd/plugin-backend.go b/cmd/plugin-backend.go index 5775998fd..f39215ec0 100644 --- a/cmd/plugin-backend.go +++ b/cmd/plugin-backend.go @@ -10,7 +10,8 @@ import ( "github.com/sirupsen/logrus" - "github.com/netobserv/network-observability-console-plugin/pkg/handler/auth" + "github.com/netobserv/network-observability-console-plugin/pkg/kubernetes/auth" + "github.com/netobserv/network-observability-console-plugin/pkg/kubernetes/client" "github.com/netobserv/network-observability-console-plugin/pkg/loki" "github.com/netobserv/network-observability-console-plugin/pkg/server" ) @@ -39,6 +40,7 @@ var ( lokiMock = flag.Bool("loki-mock", false, "Fake loki results using saved mocks") logLevel = flag.String("loglevel", "info", "log level (default: info)") frontendConfig = flag.String("frontend-config", "", "path to the console plugin config file") + authCheck = flag.String("auth-check", "auto", "type of authentication check: authenticated, admin, auto or none (default is auto, based on loki auth mode)") versionFlag = flag.Bool("v", false, "print version") log = logrus.WithField("module", "main") ) @@ -81,6 +83,25 @@ func main() { log.Fatal("labels cannot be empty") } + var checkType auth.CheckType + if *authCheck == "auto" { + if *lokiForwardUserToken { + checkType = auth.CheckAuthenticated + } else { + checkType = auth.CheckAdmin + } + log.Info(fmt.Sprintf("auth-check 'auto' resolved to '%s'", checkType)) + } else { + checkType = auth.CheckType(*authCheck) + } + if checkType == auth.CheckNone { + log.Warn("INSECURE: auth checker is disabled") + } + checker, err := auth.NewChecker(checkType, client.NewInCluster) + if err != nil { + log.WithError(err).Fatal("auth checker error") + } + server.Start(&server.Config{ Port: *port, CertFile: *cert, @@ -91,5 +112,5 @@ func main() { CORSMaxAge: *corsMaxAge, Loki: loki.NewConfig(lURL, lStatusURL, *lokiTimeout, *lokiTenantID, *lokiTokenPath, *lokiForwardUserToken, *lokiSkipTLS, *lokiCAPath, *lokiMock, strings.Split(lLabels, ",")), FrontendConfig: *frontendConfig, - }, &auth.BearerTokenChecker{}) + }, checker) } diff --git a/go.mod b/go.mod index 65e3da72b..8805c5671 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/sirupsen/logrus v1.8.1 github.com/stretchr/testify v1.8.0 gopkg.in/yaml.v3 v3.0.1 + k8s.io/api v0.26.1 k8s.io/apimachinery v0.26.1 k8s.io/client-go v0.26.1 ) @@ -48,7 +49,6 @@ require ( google.golang.org/protobuf v1.28.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - k8s.io/api v0.26.1 // indirect k8s.io/klog/v2 v2.80.1 // indirect k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect k8s.io/utils v0.0.0-20221107191617-1a15be271d1d // indirect diff --git a/pkg/handler/auth/check_auth.go b/pkg/handler/auth/check_auth.go deleted file mode 100644 index cb4376289..000000000 --- a/pkg/handler/auth/check_auth.go +++ /dev/null @@ -1,66 +0,0 @@ -package auth - -import ( - "context" - "errors" - "net/http" - "strings" - - "github.com/sirupsen/logrus" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" -) - -var hlog = logrus.WithField("module", "handler.auth") - -const AuthHeader = "Authorization" - -type Checker interface { - CheckAuth(ctx context.Context, header http.Header) error -} - -type BearerTokenChecker struct { - Checker -} - -func getUserToken(header http.Header) (string, error) { - authValue := header.Get(AuthHeader) - if authValue != "" { - parts := strings.Split(authValue, "Bearer ") - if len(parts) != 2 { - return "", errors.New("missing Bearer token in Authorization header") - } - return parts[1], nil - } - return "", errors.New("missing Authorization header") -} - -func (b *BearerTokenChecker) CheckAuth(ctx context.Context, header http.Header) error { - hlog.Debug("Checking authenticated user") - token, err := getUserToken(header) - if err != nil { - return err - } - hlog.Debug("Checking auth: token found") - config, err := rest.InClusterConfig() - if err != nil { - return err - } - config.BearerToken = token - config.BearerTokenFile = "" - - client, err := kubernetes.NewForConfig(config) - if err != nil { - return err - } - hlog.Debug("Checking auth: kube config created") - - _, err = client.CoreV1().Namespaces().List(ctx, v1.ListOptions{}) - if err != nil { - return err - } - - hlog.Debug("Checking auth: passed") - return nil -} diff --git a/pkg/handler/loki.go b/pkg/handler/loki.go index 02d716dc2..8bdbe8b40 100644 --- a/pkg/handler/loki.go +++ b/pkg/handler/loki.go @@ -2,7 +2,6 @@ package handler import ( "encoding/json" - "errors" "fmt" "net/http" "strings" @@ -12,9 +11,9 @@ import ( "github.com/sirupsen/logrus" "gopkg.in/yaml.v3" - "github.com/netobserv/network-observability-console-plugin/pkg/handler/auth" "github.com/netobserv/network-observability-console-plugin/pkg/handler/lokiclientmock" "github.com/netobserv/network-observability-console-plugin/pkg/httpclient" + "github.com/netobserv/network-observability-console-plugin/pkg/kubernetes/auth" "github.com/netobserv/network-observability-console-plugin/pkg/loki" "github.com/netobserv/network-observability-console-plugin/pkg/metrics" "github.com/netobserv/network-observability-console-plugin/pkg/model" @@ -66,21 +65,24 @@ func EncodeQuery(url string) string { return unspaced } -func getLokiError(resp []byte, code int) string { +func getLokiError(resp []byte, code int) (int, string) { var f map[string]string if code == http.StatusBadRequest { - return fmt.Sprintf("Loki message: %s", resp) + return code, fmt.Sprintf("Loki message: %s", resp) + } + if code == http.StatusForbidden { + return code, fmt.Sprintf("Forbidden: %s", resp) } err := json.Unmarshal(resp, &f) if err != nil { hlog.WithError(err).Errorf("cannot unmarshal, response was: %v", string(resp)) - return fmt.Sprintf("Unknown error from Loki\ncannot unmarshal\n%s", resp) + return http.StatusBadRequest, fmt.Sprintf("Unknown error from Loki\ncannot unmarshal\n%s", resp) } message, ok := f["message"] if !ok { - return "Unknown error from Loki\nno message found" + return http.StatusBadRequest, "Unknown error from Loki\nno message found" } - return fmt.Sprintf("Loki message: %s", message) + return http.StatusBadRequest, fmt.Sprintf("Loki message: %s", message) } func executeLokiQuery(flowsURL string, lokiClient httpclient.Caller) ([]byte, int, error) { @@ -96,11 +98,10 @@ func executeLokiQuery(flowsURL string, lokiClient httpclient.Caller) ([]byte, in return nil, http.StatusServiceUnavailable, err } if code != http.StatusOK { - msg := getLokiError(resp, code) - return nil, http.StatusBadRequest, errors.New(msg) + newCode, msg := getLokiError(resp, code) + return nil, newCode, fmt.Errorf("[%d] %s", code, msg) } - code = http.StatusOK - return resp, code, nil + return resp, http.StatusOK, nil } func fetchSingle(lokiClient httpclient.Caller, flowsURL string, merger loki.Merger) (int, error) { diff --git a/pkg/handler/resources.go b/pkg/handler/resources.go index 89a68e73b..b18eef6eb 100644 --- a/pkg/handler/resources.go +++ b/pkg/handler/resources.go @@ -61,8 +61,8 @@ func getLabelValues(cfg *loki.Config, lokiClient httpclient.Caller, label string return nil, http.StatusServiceUnavailable, err } if code != http.StatusOK { - msg := getLokiError(resp, code) - return nil, http.StatusBadRequest, errors.New(msg) + newCode, msg := getLokiError(resp, code) + return nil, newCode, errors.New(msg) } hlog.Tracef("GetFlows raw response: %s", resp) var lvr model.LabelValuesResponse diff --git a/pkg/kubernetes/auth/check_auth.go b/pkg/kubernetes/auth/check_auth.go new file mode 100644 index 000000000..54036decb --- /dev/null +++ b/pkg/kubernetes/auth/check_auth.go @@ -0,0 +1,123 @@ +package auth + +import ( + "context" + "errors" + "fmt" + "net/http" + "strings" + + "github.com/netobserv/network-observability-console-plugin/pkg/kubernetes/client" + "github.com/sirupsen/logrus" + authv1 "k8s.io/api/authentication/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var hlog = logrus.WithField("module", "handler.auth") + +type CheckType string + +const ( + AuthHeader = "Authorization" + CheckAuthenticated CheckType = "authenticated" + CheckAdmin CheckType = "admin" + CheckNone CheckType = "none" +) + +type Checker interface { + CheckAuth(ctx context.Context, header http.Header) error +} + +func NewChecker(typez CheckType, apiProvider client.APIProvider) (Checker, error) { + switch typez { + case CheckNone: + return &NoopChecker{}, nil + case CheckAuthenticated: + return &BearerTokenChecker{apiProvider: apiProvider, predicates: []tokenReviewPredicate{mustBeAuthenticated}}, nil + case CheckAdmin: + return &BearerTokenChecker{apiProvider: apiProvider, predicates: []tokenReviewPredicate{mustBeAuthenticated, mustBeClusterAdmin}}, nil + } + return nil, fmt.Errorf("auth checker type unknown: %s. Must be one of %s, %s, %s", typez, CheckAdmin, CheckAuthenticated, CheckNone) +} + +type NoopChecker struct { + Checker +} + +func (b *NoopChecker) CheckAuth(ctx context.Context, header http.Header) error { + hlog.Debug("noop auth checker: ignore auth") + return nil +} + +func getUserToken(header http.Header) (string, error) { + authValue := header.Get(AuthHeader) + if authValue != "" { + parts := strings.Split(authValue, "Bearer ") + if len(parts) != 2 { + return "", errors.New("missing Bearer token in Authorization header") + } + return parts[1], nil + } + return "", errors.New("missing Authorization header") +} + +func runTokenReview(ctx context.Context, apiProvider client.APIProvider, token string, preds []tokenReviewPredicate) error { + client, err := apiProvider() + if err != nil { + return err + } + + rvw, err := client.CreateTokenReview(ctx, &authv1.TokenReview{ + Spec: authv1.TokenReviewSpec{ + Token: token, + }, + }, &metav1.CreateOptions{}) + if err != nil { + return err + } + for _, predFunc := range preds { + if err = predFunc(rvw); err != nil { + return err + } + } + return nil +} + +type tokenReviewPredicate func(*authv1.TokenReview) error + +func mustBeAuthenticated(rvw *authv1.TokenReview) error { + if !rvw.Status.Authenticated { + return errors.New("user not authenticated") + } + return nil +} + +func mustBeClusterAdmin(rvw *authv1.TokenReview) error { + for _, group := range rvw.Status.User.Groups { + if group == "system:cluster-admins" { + return nil + } + } + return errors.New("user not in cluster-admins group") +} + +type BearerTokenChecker struct { + Checker + apiProvider client.APIProvider + predicates []tokenReviewPredicate +} + +func (c *BearerTokenChecker) CheckAuth(ctx context.Context, header http.Header) error { + hlog.Debug("Checking authenticated user") + token, err := getUserToken(header) + if err != nil { + return err + } + hlog.Debug("Checking auth: token found") + if err = runTokenReview(ctx, c.apiProvider, token, c.predicates); err != nil { + return err + } + + hlog.Debug("Checking auth: passed") + return nil +} diff --git a/pkg/kubernetes/auth/check_auth_test.go b/pkg/kubernetes/auth/check_auth_test.go new file mode 100644 index 000000000..511de1877 --- /dev/null +++ b/pkg/kubernetes/auth/check_auth_test.go @@ -0,0 +1,212 @@ +package auth + +import ( + "context" + "errors" + "net/http" + "testing" + + "github.com/netobserv/network-observability-console-plugin/pkg/kubernetes/client" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + authv1 "k8s.io/api/authentication/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func setupChecker(typez CheckType, m *TokenReviewMock) Checker { + checker, _ := NewChecker(typez, func() (client.KubeAPI, error) { return m, nil }) + return checker +} + +func TestCheckAuth_NoAuth(t *testing.T) { + m := TokenReviewMock{} + m.mockNoAuth() + + // Any user authenticated mode + checkAny := setupChecker(CheckAuthenticated, &m) + + // No header => fail + err := checkAny.CheckAuth(context.TODO(), http.Header{}) + require.Error(t, err) + require.Equal(t, "missing Authorization header", err.Error()) + + err = checkAny.CheckAuth(context.TODO(), http.Header{"Authorization": []string{"Bearer abcdef"}}) + require.Error(t, err) + require.Equal(t, "user not authenticated", err.Error()) + + // Admin mode + checkerAdmin := setupChecker(CheckAdmin, &m) + + // No header => fail + err = checkerAdmin.CheckAuth(context.TODO(), http.Header{}) + require.Error(t, err) + require.Equal(t, "missing Authorization header", err.Error()) + + err = checkerAdmin.CheckAuth(context.TODO(), http.Header{"Authorization": []string{"Bearer abcdef"}}) + require.Error(t, err) + require.Equal(t, "user not authenticated", err.Error()) + + // Noop mode + checkerNoop := NoopChecker{} + + // No header => success + err = checkerNoop.CheckAuth(context.TODO(), http.Header{}) + require.NoError(t, err) +} + +func TestCheckAuth_NormalUser(t *testing.T) { + m := TokenReviewMock{} + m.mockNormalUser() + + // Any user authenticated mode + checkAny := setupChecker(CheckAuthenticated, &m) + + // No header => fail + err := checkAny.CheckAuth(context.TODO(), http.Header{}) + require.Error(t, err) + require.Equal(t, "missing Authorization header", err.Error()) + + err = checkAny.CheckAuth(context.TODO(), http.Header{"Authorization": []string{"Bearer abcdef"}}) + require.NoError(t, err) + + // Admin mode + checkerAdmin := setupChecker(CheckAdmin, &m) + + // No header => fail + err = checkerAdmin.CheckAuth(context.TODO(), http.Header{}) + require.Error(t, err) + require.Equal(t, "missing Authorization header", err.Error()) + + err = checkerAdmin.CheckAuth(context.TODO(), http.Header{"Authorization": []string{"Bearer abcdef"}}) + require.Error(t, err) + require.Equal(t, "user not in cluster-admins group", err.Error()) + + // Noop mode + checkerNoop := NoopChecker{} + + // No header => success + err = checkerNoop.CheckAuth(context.TODO(), http.Header{}) + require.NoError(t, err) +} + +func TestCheckAuth_Admin(t *testing.T) { + m := TokenReviewMock{} + m.mockAdmin() + + // Any user authenticated mode + checkAny := setupChecker(CheckAuthenticated, &m) + + // No header => fail + err := checkAny.CheckAuth(context.TODO(), http.Header{}) + require.Error(t, err) + require.Equal(t, "missing Authorization header", err.Error()) + + err = checkAny.CheckAuth(context.TODO(), http.Header{"Authorization": []string{"Bearer abcdef"}}) + require.NoError(t, err) + + // Admin mode + checkerAdmin := setupChecker(CheckAdmin, &m) + + // No header => fail + err = checkerAdmin.CheckAuth(context.TODO(), http.Header{}) + require.Error(t, err) + require.Equal(t, "missing Authorization header", err.Error()) + + err = checkerAdmin.CheckAuth(context.TODO(), http.Header{"Authorization": []string{"Bearer abcdef"}}) + require.NoError(t, err) + + // Noop mode + checkerNoop := NoopChecker{} + + // No header => success + err = checkerNoop.CheckAuth(context.TODO(), http.Header{}) + require.NoError(t, err) +} + +const fakeError = "an error occured" + +func TestCheckAuth_APIError(t *testing.T) { + m := TokenReviewMock{} + m.mockError() + + // Any user authenticated mode + checkAny := setupChecker(CheckAuthenticated, &m) + + // No header => fail + err := checkAny.CheckAuth(context.TODO(), http.Header{}) + require.Error(t, err) + require.Equal(t, "missing Authorization header", err.Error()) + + err = checkAny.CheckAuth(context.TODO(), http.Header{"Authorization": []string{"Bearer abcdef"}}) + require.Error(t, err) + require.Equal(t, fakeError, err.Error()) + + // Admin mode + checkerAdmin := setupChecker(CheckAdmin, &m) + + // No header => fail + err = checkerAdmin.CheckAuth(context.TODO(), http.Header{}) + require.Error(t, err) + require.Equal(t, "missing Authorization header", err.Error()) + + err = checkerAdmin.CheckAuth(context.TODO(), http.Header{"Authorization": []string{"Bearer abcdef"}}) + require.Error(t, err) + require.Equal(t, fakeError, err.Error()) + + // Noop mode + checkerNoop := NoopChecker{} + + // No header => success + err = checkerNoop.CheckAuth(context.TODO(), http.Header{}) + require.NoError(t, err) +} + +type TokenReviewMock struct { + mock.Mock + client.KubeAPI +} + +func (m *TokenReviewMock) CreateTokenReview(ctx context.Context, tr *authv1.TokenReview, opts *metav1.CreateOptions) (*authv1.TokenReview, error) { + args := m.Called(ctx, tr, opts) + return args.Get(0).(*authv1.TokenReview), args.Error(1) +} + +func (m *TokenReviewMock) mockError() { + m.On("CreateTokenReview", mock.Anything, mock.Anything, mock.Anything).Return(&authv1.TokenReview{}, errors.New(fakeError)) +} + +func (m *TokenReviewMock) mockNoAuth() { + m.On("CreateTokenReview", mock.Anything, mock.Anything, mock.Anything).Return(&authv1.TokenReview{ + Status: mockedTokenReviewStatus(false, true), + }, nil) +} + +func (m *TokenReviewMock) mockNormalUser() { + m.On("CreateTokenReview", mock.Anything, mock.Anything, mock.Anything).Return(&authv1.TokenReview{ + Status: mockedTokenReviewStatus(true, false), + }, nil) +} + +func (m *TokenReviewMock) mockAdmin() { + m.On("CreateTokenReview", mock.Anything, mock.Anything, mock.Anything).Return(&authv1.TokenReview{ + Status: mockedTokenReviewStatus(true, true), + }, nil) +} + +func mockedTokenReviewStatus(isAuth, isAdmin bool) authv1.TokenReviewStatus { + st := authv1.TokenReviewStatus{ + Authenticated: isAuth, + User: authv1.UserInfo{ + Username: "user1", + Groups: []string{"group1"}, + }, + Audiences: []string{"https://kubernetes.default.svc"}, + } + if isAuth { + st.User.Groups = append(st.User.Groups, "system:authenticated") + } + if isAdmin { + st.User.Groups = append(st.User.Groups, "system:cluster-admins") + } + return st +} diff --git a/pkg/kubernetes/client/client.go b/pkg/kubernetes/client/client.go new file mode 100644 index 000000000..80a3410cc --- /dev/null +++ b/pkg/kubernetes/client/client.go @@ -0,0 +1,38 @@ +package client + +import ( + "context" + + authv1 "k8s.io/api/authentication/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" +) + +// Interface for mocking +type KubeAPI interface { + CreateTokenReview(ctx context.Context, tr *authv1.TokenReview, opts *metav1.CreateOptions) (*authv1.TokenReview, error) +} + +type APIProvider func() (KubeAPI, error) + +type InCluster struct { + KubeAPI + client *kubernetes.Clientset +} + +func (c *InCluster) CreateTokenReview(ctx context.Context, tr *authv1.TokenReview, opts *metav1.CreateOptions) (*authv1.TokenReview, error) { + return c.client.AuthenticationV1().TokenReviews().Create(ctx, tr, *opts) +} + +func NewInCluster() (KubeAPI, error) { + config, err := rest.InClusterConfig() + if err != nil { + return nil, err + } + client, err := kubernetes.NewForConfig(config) + if err != nil { + return nil, err + } + return &InCluster{client: client}, nil +} diff --git a/pkg/server/routes.go b/pkg/server/routes.go index b16faf487..bdbbfdadd 100644 --- a/pkg/server/routes.go +++ b/pkg/server/routes.go @@ -8,7 +8,7 @@ import ( "github.com/sirupsen/logrus" "github.com/netobserv/network-observability-console-plugin/pkg/handler" - "github.com/netobserv/network-observability-console-plugin/pkg/handler/auth" + "github.com/netobserv/network-observability-console-plugin/pkg/kubernetes/auth" ) func setupRoutes(cfg *Config, authChecker auth.Checker) *mux.Router { diff --git a/pkg/server/server.go b/pkg/server/server.go index 85d8cb8a0..e9d7d4dcc 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -8,7 +8,7 @@ import ( "github.com/sirupsen/logrus" - "github.com/netobserv/network-observability-console-plugin/pkg/handler/auth" + "github.com/netobserv/network-observability-console-plugin/pkg/kubernetes/auth" "github.com/netobserv/network-observability-console-plugin/pkg/loki" ) diff --git a/pkg/server/server_test.go b/pkg/server/server_test.go index b49c210cf..10eb40281 100644 --- a/pkg/server/server_test.go +++ b/pkg/server/server_test.go @@ -27,7 +27,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/netobserv/network-observability-console-plugin/pkg/handler/auth" + "github.com/netobserv/network-observability-console-plugin/pkg/kubernetes/auth" "github.com/netobserv/network-observability-console-plugin/pkg/loki" "github.com/netobserv/network-observability-console-plugin/pkg/model" ) diff --git a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator.go b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator.go deleted file mode 100644 index e7c0d0546..000000000 --- a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator.go +++ /dev/null @@ -1,368 +0,0 @@ -// Copyright 2017 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 promhttp - -import ( - "bufio" - "io" - "net" - "net/http" -) - -const ( - closeNotifier = 1 << iota - flusher - hijacker - readerFrom - pusher -) - -type delegator interface { - http.ResponseWriter - - Status() int - Written() int64 -} - -type responseWriterDelegator struct { - http.ResponseWriter - - status int - written int64 - wroteHeader bool - observeWriteHeader func(int) -} - -func (r *responseWriterDelegator) Status() int { - return r.status -} - -func (r *responseWriterDelegator) Written() int64 { - return r.written -} - -func (r *responseWriterDelegator) WriteHeader(code int) { - if r.observeWriteHeader != nil && !r.wroteHeader { - // Only call observeWriteHeader for the 1st time. It's a bug if - // WriteHeader is called more than once, but we want to protect - // against it here. Note that we still delegate the WriteHeader - // to the original ResponseWriter to not mask the bug from it. - r.observeWriteHeader(code) - } - r.status = code - r.wroteHeader = true - r.ResponseWriter.WriteHeader(code) -} - -func (r *responseWriterDelegator) Write(b []byte) (int, error) { - // If applicable, call WriteHeader here so that observeWriteHeader is - // handled appropriately. - if !r.wroteHeader { - r.WriteHeader(http.StatusOK) - } - n, err := r.ResponseWriter.Write(b) - r.written += int64(n) - return n, err -} - -type closeNotifierDelegator struct{ *responseWriterDelegator } -type flusherDelegator struct{ *responseWriterDelegator } -type hijackerDelegator struct{ *responseWriterDelegator } -type readerFromDelegator struct{ *responseWriterDelegator } -type pusherDelegator struct{ *responseWriterDelegator } - -func (d closeNotifierDelegator) CloseNotify() <-chan bool { - //nolint:staticcheck // Ignore SA1019. http.CloseNotifier is deprecated but we keep it here to not break existing users. - return d.ResponseWriter.(http.CloseNotifier).CloseNotify() -} -func (d flusherDelegator) Flush() { - // If applicable, call WriteHeader here so that observeWriteHeader is - // handled appropriately. - if !d.wroteHeader { - d.WriteHeader(http.StatusOK) - } - d.ResponseWriter.(http.Flusher).Flush() -} -func (d hijackerDelegator) Hijack() (net.Conn, *bufio.ReadWriter, error) { - return d.ResponseWriter.(http.Hijacker).Hijack() -} -func (d readerFromDelegator) ReadFrom(re io.Reader) (int64, error) { - // If applicable, call WriteHeader here so that observeWriteHeader is - // handled appropriately. - if !d.wroteHeader { - d.WriteHeader(http.StatusOK) - } - n, err := d.ResponseWriter.(io.ReaderFrom).ReadFrom(re) - d.written += n - return n, err -} -func (d pusherDelegator) Push(target string, opts *http.PushOptions) error { - return d.ResponseWriter.(http.Pusher).Push(target, opts) -} - -var pickDelegator = make([]func(*responseWriterDelegator) delegator, 32) - -func init() { - // TODO(beorn7): Code generation would help here. - pickDelegator[0] = func(d *responseWriterDelegator) delegator { // 0 - return d - } - pickDelegator[closeNotifier] = func(d *responseWriterDelegator) delegator { // 1 - return closeNotifierDelegator{d} - } - pickDelegator[flusher] = func(d *responseWriterDelegator) delegator { // 2 - return flusherDelegator{d} - } - pickDelegator[flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 3 - return struct { - *responseWriterDelegator - http.Flusher - http.CloseNotifier - }{d, flusherDelegator{d}, closeNotifierDelegator{d}} - } - pickDelegator[hijacker] = func(d *responseWriterDelegator) delegator { // 4 - return hijackerDelegator{d} - } - pickDelegator[hijacker+closeNotifier] = func(d *responseWriterDelegator) delegator { // 5 - return struct { - *responseWriterDelegator - http.Hijacker - http.CloseNotifier - }{d, hijackerDelegator{d}, closeNotifierDelegator{d}} - } - pickDelegator[hijacker+flusher] = func(d *responseWriterDelegator) delegator { // 6 - return struct { - *responseWriterDelegator - http.Hijacker - http.Flusher - }{d, hijackerDelegator{d}, flusherDelegator{d}} - } - pickDelegator[hijacker+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 7 - return struct { - *responseWriterDelegator - http.Hijacker - http.Flusher - http.CloseNotifier - }{d, hijackerDelegator{d}, flusherDelegator{d}, closeNotifierDelegator{d}} - } - pickDelegator[readerFrom] = func(d *responseWriterDelegator) delegator { // 8 - return readerFromDelegator{d} - } - pickDelegator[readerFrom+closeNotifier] = func(d *responseWriterDelegator) delegator { // 9 - return struct { - *responseWriterDelegator - io.ReaderFrom - http.CloseNotifier - }{d, readerFromDelegator{d}, closeNotifierDelegator{d}} - } - pickDelegator[readerFrom+flusher] = func(d *responseWriterDelegator) delegator { // 10 - return struct { - *responseWriterDelegator - io.ReaderFrom - http.Flusher - }{d, readerFromDelegator{d}, flusherDelegator{d}} - } - pickDelegator[readerFrom+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 11 - return struct { - *responseWriterDelegator - io.ReaderFrom - http.Flusher - http.CloseNotifier - }{d, readerFromDelegator{d}, flusherDelegator{d}, closeNotifierDelegator{d}} - } - pickDelegator[readerFrom+hijacker] = func(d *responseWriterDelegator) delegator { // 12 - return struct { - *responseWriterDelegator - io.ReaderFrom - http.Hijacker - }{d, readerFromDelegator{d}, hijackerDelegator{d}} - } - pickDelegator[readerFrom+hijacker+closeNotifier] = func(d *responseWriterDelegator) delegator { // 13 - return struct { - *responseWriterDelegator - io.ReaderFrom - http.Hijacker - http.CloseNotifier - }{d, readerFromDelegator{d}, hijackerDelegator{d}, closeNotifierDelegator{d}} - } - pickDelegator[readerFrom+hijacker+flusher] = func(d *responseWriterDelegator) delegator { // 14 - return struct { - *responseWriterDelegator - io.ReaderFrom - http.Hijacker - http.Flusher - }{d, readerFromDelegator{d}, hijackerDelegator{d}, flusherDelegator{d}} - } - pickDelegator[readerFrom+hijacker+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 15 - return struct { - *responseWriterDelegator - io.ReaderFrom - http.Hijacker - http.Flusher - http.CloseNotifier - }{d, readerFromDelegator{d}, hijackerDelegator{d}, flusherDelegator{d}, closeNotifierDelegator{d}} - } - pickDelegator[pusher] = func(d *responseWriterDelegator) delegator { // 16 - return pusherDelegator{d} - } - pickDelegator[pusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 17 - return struct { - *responseWriterDelegator - http.Pusher - http.CloseNotifier - }{d, pusherDelegator{d}, closeNotifierDelegator{d}} - } - pickDelegator[pusher+flusher] = func(d *responseWriterDelegator) delegator { // 18 - return struct { - *responseWriterDelegator - http.Pusher - http.Flusher - }{d, pusherDelegator{d}, flusherDelegator{d}} - } - pickDelegator[pusher+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 19 - return struct { - *responseWriterDelegator - http.Pusher - http.Flusher - http.CloseNotifier - }{d, pusherDelegator{d}, flusherDelegator{d}, closeNotifierDelegator{d}} - } - pickDelegator[pusher+hijacker] = func(d *responseWriterDelegator) delegator { // 20 - return struct { - *responseWriterDelegator - http.Pusher - http.Hijacker - }{d, pusherDelegator{d}, hijackerDelegator{d}} - } - pickDelegator[pusher+hijacker+closeNotifier] = func(d *responseWriterDelegator) delegator { // 21 - return struct { - *responseWriterDelegator - http.Pusher - http.Hijacker - http.CloseNotifier - }{d, pusherDelegator{d}, hijackerDelegator{d}, closeNotifierDelegator{d}} - } - pickDelegator[pusher+hijacker+flusher] = func(d *responseWriterDelegator) delegator { // 22 - return struct { - *responseWriterDelegator - http.Pusher - http.Hijacker - http.Flusher - }{d, pusherDelegator{d}, hijackerDelegator{d}, flusherDelegator{d}} - } - pickDelegator[pusher+hijacker+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { //23 - return struct { - *responseWriterDelegator - http.Pusher - http.Hijacker - http.Flusher - http.CloseNotifier - }{d, pusherDelegator{d}, hijackerDelegator{d}, flusherDelegator{d}, closeNotifierDelegator{d}} - } - pickDelegator[pusher+readerFrom] = func(d *responseWriterDelegator) delegator { // 24 - return struct { - *responseWriterDelegator - http.Pusher - io.ReaderFrom - }{d, pusherDelegator{d}, readerFromDelegator{d}} - } - pickDelegator[pusher+readerFrom+closeNotifier] = func(d *responseWriterDelegator) delegator { // 25 - return struct { - *responseWriterDelegator - http.Pusher - io.ReaderFrom - http.CloseNotifier - }{d, pusherDelegator{d}, readerFromDelegator{d}, closeNotifierDelegator{d}} - } - pickDelegator[pusher+readerFrom+flusher] = func(d *responseWriterDelegator) delegator { // 26 - return struct { - *responseWriterDelegator - http.Pusher - io.ReaderFrom - http.Flusher - }{d, pusherDelegator{d}, readerFromDelegator{d}, flusherDelegator{d}} - } - pickDelegator[pusher+readerFrom+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 27 - return struct { - *responseWriterDelegator - http.Pusher - io.ReaderFrom - http.Flusher - http.CloseNotifier - }{d, pusherDelegator{d}, readerFromDelegator{d}, flusherDelegator{d}, closeNotifierDelegator{d}} - } - pickDelegator[pusher+readerFrom+hijacker] = func(d *responseWriterDelegator) delegator { // 28 - return struct { - *responseWriterDelegator - http.Pusher - io.ReaderFrom - http.Hijacker - }{d, pusherDelegator{d}, readerFromDelegator{d}, hijackerDelegator{d}} - } - pickDelegator[pusher+readerFrom+hijacker+closeNotifier] = func(d *responseWriterDelegator) delegator { // 29 - return struct { - *responseWriterDelegator - http.Pusher - io.ReaderFrom - http.Hijacker - http.CloseNotifier - }{d, pusherDelegator{d}, readerFromDelegator{d}, hijackerDelegator{d}, closeNotifierDelegator{d}} - } - pickDelegator[pusher+readerFrom+hijacker+flusher] = func(d *responseWriterDelegator) delegator { // 30 - return struct { - *responseWriterDelegator - http.Pusher - io.ReaderFrom - http.Hijacker - http.Flusher - }{d, pusherDelegator{d}, readerFromDelegator{d}, hijackerDelegator{d}, flusherDelegator{d}} - } - pickDelegator[pusher+readerFrom+hijacker+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 31 - return struct { - *responseWriterDelegator - http.Pusher - io.ReaderFrom - http.Hijacker - http.Flusher - http.CloseNotifier - }{d, pusherDelegator{d}, readerFromDelegator{d}, hijackerDelegator{d}, flusherDelegator{d}, closeNotifierDelegator{d}} - } -} - -func newDelegator(w http.ResponseWriter, observeWriteHeaderFunc func(int)) delegator { - d := &responseWriterDelegator{ - ResponseWriter: w, - observeWriteHeader: observeWriteHeaderFunc, - } - - id := 0 - //nolint:staticcheck // Ignore SA1019. http.CloseNotifier is deprecated but we keep it here to not break existing users. - if _, ok := w.(http.CloseNotifier); ok { - id += closeNotifier - } - if _, ok := w.(http.Flusher); ok { - id += flusher - } - if _, ok := w.(http.Hijacker); ok { - id += hijacker - } - if _, ok := w.(io.ReaderFrom); ok { - id += readerFrom - } - if _, ok := w.(http.Pusher); ok { - id += pusher - } - - return pickDelegator[id](d) -} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go deleted file mode 100644 index d86d0cf4b..000000000 --- a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go +++ /dev/null @@ -1,383 +0,0 @@ -// Copyright 2016 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 promhttp provides tooling around HTTP servers and clients. -// -// First, the package allows the creation of http.Handler instances to expose -// Prometheus metrics via HTTP. promhttp.Handler acts on the -// prometheus.DefaultGatherer. With HandlerFor, you can create a handler for a -// custom registry or anything that implements the Gatherer interface. It also -// allows the creation of handlers that act differently on errors or allow to -// log errors. -// -// Second, the package provides tooling to instrument instances of http.Handler -// via middleware. Middleware wrappers follow the naming scheme -// InstrumentHandlerX, where X describes the intended use of the middleware. -// See each function's doc comment for specific details. -// -// Finally, the package allows for an http.RoundTripper to be instrumented via -// middleware. Middleware wrappers follow the naming scheme -// InstrumentRoundTripperX, where X describes the intended use of the -// middleware. See each function's doc comment for specific details. -package promhttp - -import ( - "compress/gzip" - "fmt" - "io" - "net/http" - "strings" - "sync" - "time" - - "github.com/prometheus/common/expfmt" - - "github.com/prometheus/client_golang/prometheus" -) - -const ( - contentTypeHeader = "Content-Type" - contentEncodingHeader = "Content-Encoding" - acceptEncodingHeader = "Accept-Encoding" -) - -var gzipPool = sync.Pool{ - New: func() interface{} { - return gzip.NewWriter(nil) - }, -} - -// Handler returns an http.Handler for the prometheus.DefaultGatherer, using -// default HandlerOpts, i.e. it reports the first error as an HTTP error, it has -// no error logging, and it applies compression if requested by the client. -// -// The returned http.Handler is already instrumented using the -// InstrumentMetricHandler function and the prometheus.DefaultRegisterer. If you -// create multiple http.Handlers by separate calls of the Handler function, the -// metrics used for instrumentation will be shared between them, providing -// global scrape counts. -// -// This function is meant to cover the bulk of basic use cases. If you are doing -// anything that requires more customization (including using a non-default -// Gatherer, different instrumentation, and non-default HandlerOpts), use the -// HandlerFor function. See there for details. -func Handler() http.Handler { - return InstrumentMetricHandler( - prometheus.DefaultRegisterer, HandlerFor(prometheus.DefaultGatherer, HandlerOpts{}), - ) -} - -// HandlerFor returns an uninstrumented http.Handler for the provided -// Gatherer. The behavior of the Handler is defined by the provided -// HandlerOpts. Thus, HandlerFor is useful to create http.Handlers for custom -// Gatherers, with non-default HandlerOpts, and/or with custom (or no) -// instrumentation. Use the InstrumentMetricHandler function to apply the same -// kind of instrumentation as it is used by the Handler function. -func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler { - var ( - inFlightSem chan struct{} - errCnt = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "promhttp_metric_handler_errors_total", - Help: "Total number of internal errors encountered by the promhttp metric handler.", - }, - []string{"cause"}, - ) - ) - - if opts.MaxRequestsInFlight > 0 { - inFlightSem = make(chan struct{}, opts.MaxRequestsInFlight) - } - if opts.Registry != nil { - // Initialize all possibilities that can occur below. - errCnt.WithLabelValues("gathering") - errCnt.WithLabelValues("encoding") - if err := opts.Registry.Register(errCnt); err != nil { - if are, ok := err.(prometheus.AlreadyRegisteredError); ok { - errCnt = are.ExistingCollector.(*prometheus.CounterVec) - } else { - panic(err) - } - } - } - - h := http.HandlerFunc(func(rsp http.ResponseWriter, req *http.Request) { - if inFlightSem != nil { - select { - case inFlightSem <- struct{}{}: // All good, carry on. - defer func() { <-inFlightSem }() - default: - http.Error(rsp, fmt.Sprintf( - "Limit of concurrent requests reached (%d), try again later.", opts.MaxRequestsInFlight, - ), http.StatusServiceUnavailable) - return - } - } - mfs, err := reg.Gather() - if err != nil { - if opts.ErrorLog != nil { - opts.ErrorLog.Println("error gathering metrics:", err) - } - errCnt.WithLabelValues("gathering").Inc() - switch opts.ErrorHandling { - case PanicOnError: - panic(err) - case ContinueOnError: - if len(mfs) == 0 { - // Still report the error if no metrics have been gathered. - httpError(rsp, err) - return - } - case HTTPErrorOnError: - httpError(rsp, err) - return - } - } - - var contentType expfmt.Format - if opts.EnableOpenMetrics { - contentType = expfmt.NegotiateIncludingOpenMetrics(req.Header) - } else { - contentType = expfmt.Negotiate(req.Header) - } - header := rsp.Header() - header.Set(contentTypeHeader, string(contentType)) - - w := io.Writer(rsp) - if !opts.DisableCompression && gzipAccepted(req.Header) { - header.Set(contentEncodingHeader, "gzip") - gz := gzipPool.Get().(*gzip.Writer) - defer gzipPool.Put(gz) - - gz.Reset(w) - defer gz.Close() - - w = gz - } - - enc := expfmt.NewEncoder(w, contentType) - - // handleError handles the error according to opts.ErrorHandling - // and returns true if we have to abort after the handling. - handleError := func(err error) bool { - if err == nil { - return false - } - if opts.ErrorLog != nil { - opts.ErrorLog.Println("error encoding and sending metric family:", err) - } - errCnt.WithLabelValues("encoding").Inc() - switch opts.ErrorHandling { - case PanicOnError: - panic(err) - case HTTPErrorOnError: - // We cannot really send an HTTP error at this - // point because we most likely have written - // something to rsp already. But at least we can - // stop sending. - return true - } - // Do nothing in all other cases, including ContinueOnError. - return false - } - - for _, mf := range mfs { - if handleError(enc.Encode(mf)) { - return - } - } - if closer, ok := enc.(expfmt.Closer); ok { - // This in particular takes care of the final "# EOF\n" line for OpenMetrics. - if handleError(closer.Close()) { - return - } - } - }) - - if opts.Timeout <= 0 { - return h - } - return http.TimeoutHandler(h, opts.Timeout, fmt.Sprintf( - "Exceeded configured timeout of %v.\n", - opts.Timeout, - )) -} - -// InstrumentMetricHandler is usually used with an http.Handler returned by the -// HandlerFor function. It instruments the provided http.Handler with two -// metrics: A counter vector "promhttp_metric_handler_requests_total" to count -// scrapes partitioned by HTTP status code, and a gauge -// "promhttp_metric_handler_requests_in_flight" to track the number of -// simultaneous scrapes. This function idempotently registers collectors for -// both metrics with the provided Registerer. It panics if the registration -// fails. The provided metrics are useful to see how many scrapes hit the -// monitored target (which could be from different Prometheus servers or other -// scrapers), and how often they overlap (which would result in more than one -// scrape in flight at the same time). Note that the scrapes-in-flight gauge -// will contain the scrape by which it is exposed, while the scrape counter will -// only get incremented after the scrape is complete (as only then the status -// code is known). For tracking scrape durations, use the -// "scrape_duration_seconds" gauge created by the Prometheus server upon each -// scrape. -func InstrumentMetricHandler(reg prometheus.Registerer, handler http.Handler) http.Handler { - cnt := prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "promhttp_metric_handler_requests_total", - Help: "Total number of scrapes by HTTP status code.", - }, - []string{"code"}, - ) - // Initialize the most likely HTTP status codes. - cnt.WithLabelValues("200") - cnt.WithLabelValues("500") - cnt.WithLabelValues("503") - if err := reg.Register(cnt); err != nil { - if are, ok := err.(prometheus.AlreadyRegisteredError); ok { - cnt = are.ExistingCollector.(*prometheus.CounterVec) - } else { - panic(err) - } - } - - gge := prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "promhttp_metric_handler_requests_in_flight", - Help: "Current number of scrapes being served.", - }) - if err := reg.Register(gge); err != nil { - if are, ok := err.(prometheus.AlreadyRegisteredError); ok { - gge = are.ExistingCollector.(prometheus.Gauge) - } else { - panic(err) - } - } - - return InstrumentHandlerCounter(cnt, InstrumentHandlerInFlight(gge, handler)) -} - -// HandlerErrorHandling defines how a Handler serving metrics will handle -// errors. -type HandlerErrorHandling int - -// These constants cause handlers serving metrics to behave as described if -// errors are encountered. -const ( - // Serve an HTTP status code 500 upon the first error - // encountered. Report the error message in the body. Note that HTTP - // errors cannot be served anymore once the beginning of a regular - // payload has been sent. Thus, in the (unlikely) case that encoding the - // payload into the negotiated wire format fails, serving the response - // will simply be aborted. Set an ErrorLog in HandlerOpts to detect - // those errors. - HTTPErrorOnError HandlerErrorHandling = iota - // Ignore errors and try to serve as many metrics as possible. However, - // if no metrics can be served, serve an HTTP status code 500 and the - // last error message in the body. Only use this in deliberate "best - // effort" metrics collection scenarios. In this case, it is highly - // recommended to provide other means of detecting errors: By setting an - // ErrorLog in HandlerOpts, the errors are logged. By providing a - // Registry in HandlerOpts, the exposed metrics include an error counter - // "promhttp_metric_handler_errors_total", which can be used for - // alerts. - ContinueOnError - // Panic upon the first error encountered (useful for "crash only" apps). - PanicOnError -) - -// Logger is the minimal interface HandlerOpts needs for logging. Note that -// log.Logger from the standard library implements this interface, and it is -// easy to implement by custom loggers, if they don't do so already anyway. -type Logger interface { - Println(v ...interface{}) -} - -// HandlerOpts specifies options how to serve metrics via an http.Handler. The -// zero value of HandlerOpts is a reasonable default. -type HandlerOpts struct { - // ErrorLog specifies an optional Logger for errors collecting and - // serving metrics. If nil, errors are not logged at all. Note that the - // type of a reported error is often prometheus.MultiError, which - // formats into a multi-line error string. If you want to avoid the - // latter, create a Logger implementation that detects a - // prometheus.MultiError and formats the contained errors into one line. - ErrorLog Logger - // ErrorHandling defines how errors are handled. Note that errors are - // logged regardless of the configured ErrorHandling provided ErrorLog - // is not nil. - ErrorHandling HandlerErrorHandling - // If Registry is not nil, it is used to register a metric - // "promhttp_metric_handler_errors_total", partitioned by "cause". A - // failed registration causes a panic. Note that this error counter is - // different from the instrumentation you get from the various - // InstrumentHandler... helpers. It counts errors that don't necessarily - // result in a non-2xx HTTP status code. There are two typical cases: - // (1) Encoding errors that only happen after streaming of the HTTP body - // has already started (and the status code 200 has been sent). This - // should only happen with custom collectors. (2) Collection errors with - // no effect on the HTTP status code because ErrorHandling is set to - // ContinueOnError. - Registry prometheus.Registerer - // If DisableCompression is true, the handler will never compress the - // response, even if requested by the client. - DisableCompression bool - // The number of concurrent HTTP requests is limited to - // MaxRequestsInFlight. Additional requests are responded to with 503 - // Service Unavailable and a suitable message in the body. If - // MaxRequestsInFlight is 0 or negative, no limit is applied. - MaxRequestsInFlight int - // If handling a request takes longer than Timeout, it is responded to - // with 503 ServiceUnavailable and a suitable Message. No timeout is - // applied if Timeout is 0 or negative. Note that with the current - // implementation, reaching the timeout simply ends the HTTP requests as - // described above (and even that only if sending of the body hasn't - // started yet), while the bulk work of gathering all the metrics keeps - // running in the background (with the eventual result to be thrown - // away). Until the implementation is improved, it is recommended to - // implement a separate timeout in potentially slow Collectors. - Timeout time.Duration - // If true, the experimental OpenMetrics encoding is added to the - // possible options during content negotiation. Note that Prometheus - // 2.5.0+ will negotiate OpenMetrics as first priority. OpenMetrics is - // the only way to transmit exemplars. However, the move to OpenMetrics - // is not completely transparent. Most notably, the values of "quantile" - // labels of Summaries and "le" labels of Histograms are formatted with - // a trailing ".0" if they would otherwise look like integer numbers - // (which changes the identity of the resulting series on the Prometheus - // server). - EnableOpenMetrics bool -} - -// gzipAccepted returns whether the client will accept gzip-encoded content. -func gzipAccepted(header http.Header) bool { - a := header.Get(acceptEncodingHeader) - parts := strings.Split(a, ",") - for _, part := range parts { - part = strings.TrimSpace(part) - if part == "gzip" || strings.HasPrefix(part, "gzip;") { - return true - } - } - return false -} - -// httpError removes any content-encoding header and then calls http.Error with -// the provided error and http.StatusInternalServerError. Error contents is -// supposed to be uncompressed plain text. Same as with a plain http.Error, this -// must not be called if the header or any payload has already been sent. -func httpError(rsp http.ResponseWriter, err error) { - rsp.Header().Del(contentEncodingHeader) - http.Error( - rsp, - "An error has occurred while serving metrics:\n\n"+err.Error(), - http.StatusInternalServerError, - ) -} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go deleted file mode 100644 index 861b4d21c..000000000 --- a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright 2017 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 promhttp - -import ( - "crypto/tls" - "net/http" - "net/http/httptrace" - "time" - - "github.com/prometheus/client_golang/prometheus" -) - -// The RoundTripperFunc type is an adapter to allow the use of ordinary -// functions as RoundTrippers. If f is a function with the appropriate -// signature, RountTripperFunc(f) is a RoundTripper that calls f. -type RoundTripperFunc func(req *http.Request) (*http.Response, error) - -// RoundTrip implements the RoundTripper interface. -func (rt RoundTripperFunc) RoundTrip(r *http.Request) (*http.Response, error) { - return rt(r) -} - -// InstrumentRoundTripperInFlight is a middleware that wraps the provided -// http.RoundTripper. It sets the provided prometheus.Gauge to the number of -// requests currently handled by the wrapped http.RoundTripper. -// -// See the example for ExampleInstrumentRoundTripperDuration for example usage. -func InstrumentRoundTripperInFlight(gauge prometheus.Gauge, next http.RoundTripper) RoundTripperFunc { - return RoundTripperFunc(func(r *http.Request) (*http.Response, error) { - gauge.Inc() - defer gauge.Dec() - return next.RoundTrip(r) - }) -} - -// InstrumentRoundTripperCounter is a middleware that wraps the provided -// http.RoundTripper to observe the request result with the provided CounterVec. -// The CounterVec must have zero, one, or two non-const non-curried labels. For -// those, the only allowed label names are "code" and "method". The function -// panics otherwise. For the "method" label a predefined default label value set -// is used to filter given values. Values besides predefined values will count -// as `unknown` method.`WithExtraMethods` can be used to add more -// methods to the set. Partitioning of the CounterVec happens by HTTP status code -// and/or HTTP method if the respective instance label names are present in the -// CounterVec. For unpartitioned counting, use a CounterVec with zero labels. -// -// If the wrapped RoundTripper panics or returns a non-nil error, the Counter -// is not incremented. -// -// See the example for ExampleInstrumentRoundTripperDuration for example usage. -func InstrumentRoundTripperCounter(counter *prometheus.CounterVec, next http.RoundTripper, opts ...Option) RoundTripperFunc { - rtOpts := &option{} - for _, o := range opts { - o(rtOpts) - } - - code, method := checkLabels(counter) - - return RoundTripperFunc(func(r *http.Request) (*http.Response, error) { - resp, err := next.RoundTrip(r) - if err == nil { - counter.With(labels(code, method, r.Method, resp.StatusCode, rtOpts.extraMethods...)).Inc() - } - return resp, err - }) -} - -// InstrumentRoundTripperDuration is a middleware that wraps the provided -// http.RoundTripper to observe the request duration with the provided -// ObserverVec. The ObserverVec must have zero, one, or two non-const -// non-curried labels. For those, the only allowed label names are "code" and -// "method". The function panics otherwise. For the "method" label a predefined -// default label value set is used to filter given values. Values besides -// predefined values will count as `unknown` method. `WithExtraMethods` -// can be used to add more methods to the set. The Observe method of the Observer -// in the ObserverVec is called with the request duration in -// seconds. Partitioning happens by HTTP status code and/or HTTP method if the -// respective instance label names are present in the ObserverVec. For -// unpartitioned observations, use an ObserverVec with zero labels. Note that -// partitioning of Histograms is expensive and should be used judiciously. -// -// If the wrapped RoundTripper panics or returns a non-nil error, no values are -// reported. -// -// Note that this method is only guaranteed to never observe negative durations -// if used with Go1.9+. -func InstrumentRoundTripperDuration(obs prometheus.ObserverVec, next http.RoundTripper, opts ...Option) RoundTripperFunc { - rtOpts := &option{} - for _, o := range opts { - o(rtOpts) - } - - code, method := checkLabels(obs) - - return RoundTripperFunc(func(r *http.Request) (*http.Response, error) { - start := time.Now() - resp, err := next.RoundTrip(r) - if err == nil { - obs.With(labels(code, method, r.Method, resp.StatusCode, rtOpts.extraMethods...)).Observe(time.Since(start).Seconds()) - } - return resp, err - }) -} - -// InstrumentTrace is used to offer flexibility in instrumenting the available -// httptrace.ClientTrace hook functions. Each function is passed a float64 -// representing the time in seconds since the start of the http request. A user -// may choose to use separately buckets Histograms, or implement custom -// instance labels on a per function basis. -type InstrumentTrace struct { - GotConn func(float64) - PutIdleConn func(float64) - GotFirstResponseByte func(float64) - Got100Continue func(float64) - DNSStart func(float64) - DNSDone func(float64) - ConnectStart func(float64) - ConnectDone func(float64) - TLSHandshakeStart func(float64) - TLSHandshakeDone func(float64) - WroteHeaders func(float64) - Wait100Continue func(float64) - WroteRequest func(float64) -} - -// InstrumentRoundTripperTrace is a middleware that wraps the provided -// RoundTripper and reports times to hook functions provided in the -// InstrumentTrace struct. Hook functions that are not present in the provided -// InstrumentTrace struct are ignored. Times reported to the hook functions are -// time since the start of the request. Only with Go1.9+, those times are -// guaranteed to never be negative. (Earlier Go versions are not using a -// monotonic clock.) Note that partitioning of Histograms is expensive and -// should be used judiciously. -// -// For hook functions that receive an error as an argument, no observations are -// made in the event of a non-nil error value. -// -// See the example for ExampleInstrumentRoundTripperDuration for example usage. -func InstrumentRoundTripperTrace(it *InstrumentTrace, next http.RoundTripper) RoundTripperFunc { - return RoundTripperFunc(func(r *http.Request) (*http.Response, error) { - start := time.Now() - - trace := &httptrace.ClientTrace{ - GotConn: func(_ httptrace.GotConnInfo) { - if it.GotConn != nil { - it.GotConn(time.Since(start).Seconds()) - } - }, - PutIdleConn: func(err error) { - if err != nil { - return - } - if it.PutIdleConn != nil { - it.PutIdleConn(time.Since(start).Seconds()) - } - }, - DNSStart: func(_ httptrace.DNSStartInfo) { - if it.DNSStart != nil { - it.DNSStart(time.Since(start).Seconds()) - } - }, - DNSDone: func(_ httptrace.DNSDoneInfo) { - if it.DNSDone != nil { - it.DNSDone(time.Since(start).Seconds()) - } - }, - ConnectStart: func(_, _ string) { - if it.ConnectStart != nil { - it.ConnectStart(time.Since(start).Seconds()) - } - }, - ConnectDone: func(_, _ string, err error) { - if err != nil { - return - } - if it.ConnectDone != nil { - it.ConnectDone(time.Since(start).Seconds()) - } - }, - GotFirstResponseByte: func() { - if it.GotFirstResponseByte != nil { - it.GotFirstResponseByte(time.Since(start).Seconds()) - } - }, - Got100Continue: func() { - if it.Got100Continue != nil { - it.Got100Continue(time.Since(start).Seconds()) - } - }, - TLSHandshakeStart: func() { - if it.TLSHandshakeStart != nil { - it.TLSHandshakeStart(time.Since(start).Seconds()) - } - }, - TLSHandshakeDone: func(_ tls.ConnectionState, err error) { - if err != nil { - return - } - if it.TLSHandshakeDone != nil { - it.TLSHandshakeDone(time.Since(start).Seconds()) - } - }, - WroteHeaders: func() { - if it.WroteHeaders != nil { - it.WroteHeaders(time.Since(start).Seconds()) - } - }, - Wait100Continue: func() { - if it.Wait100Continue != nil { - it.Wait100Continue(time.Since(start).Seconds()) - } - }, - WroteRequest: func(_ httptrace.WroteRequestInfo) { - if it.WroteRequest != nil { - it.WroteRequest(time.Since(start).Seconds()) - } - }, - } - r = r.WithContext(httptrace.WithClientTrace(r.Context(), trace)) - - return next.RoundTrip(r) - }) -} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go deleted file mode 100644 index a23f0edc6..000000000 --- a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go +++ /dev/null @@ -1,517 +0,0 @@ -// Copyright 2017 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 promhttp - -import ( - "errors" - "net/http" - "strconv" - "strings" - "time" - - dto "github.com/prometheus/client_model/go" - - "github.com/prometheus/client_golang/prometheus" -) - -// magicString is used for the hacky label test in checkLabels. Remove once fixed. -const magicString = "zZgWfBxLqvG8kc8IMv3POi2Bb0tZI3vAnBx+gBaFi9FyPzB/CzKUer1yufDa" - -// InstrumentHandlerInFlight is a middleware that wraps the provided -// http.Handler. It sets the provided prometheus.Gauge to the number of -// requests currently handled by the wrapped http.Handler. -// -// See the example for InstrumentHandlerDuration for example usage. -func InstrumentHandlerInFlight(g prometheus.Gauge, next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - g.Inc() - defer g.Dec() - next.ServeHTTP(w, r) - }) -} - -// InstrumentHandlerDuration is a middleware that wraps the provided -// http.Handler to observe the request duration with the provided ObserverVec. -// The ObserverVec must have valid metric and label names and must have zero, -// one, or two non-const non-curried labels. For those, the only allowed label -// names are "code" and "method". The function panics otherwise. For the "method" -// label a predefined default label value set is used to filter given values. -// Values besides predefined values will count as `unknown` method. -//`WithExtraMethods` can be used to add more methods to the set. The Observe -// method of the Observer in the ObserverVec is called with the request duration -// in seconds. Partitioning happens by HTTP status code and/or HTTP method if -// the respective instance label names are present in the ObserverVec. For -// unpartitioned observations, use an ObserverVec with zero labels. Note that -// partitioning of Histograms is expensive and should be used judiciously. -// -// If the wrapped Handler does not set a status code, a status code of 200 is assumed. -// -// If the wrapped Handler panics, no values are reported. -// -// Note that this method is only guaranteed to never observe negative durations -// if used with Go1.9+. -func InstrumentHandlerDuration(obs prometheus.ObserverVec, next http.Handler, opts ...Option) http.HandlerFunc { - mwOpts := &option{} - for _, o := range opts { - o(mwOpts) - } - - code, method := checkLabels(obs) - - if code { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - now := time.Now() - d := newDelegator(w, nil) - next.ServeHTTP(d, r) - - obs.With(labels(code, method, r.Method, d.Status(), mwOpts.extraMethods...)).Observe(time.Since(now).Seconds()) - }) - } - - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - now := time.Now() - next.ServeHTTP(w, r) - obs.With(labels(code, method, r.Method, 0, mwOpts.extraMethods...)).Observe(time.Since(now).Seconds()) - }) -} - -// InstrumentHandlerCounter is a middleware that wraps the provided http.Handler -// to observe the request result with the provided CounterVec. The CounterVec -// must have valid metric and label names and must have zero, one, or two -// non-const non-curried labels. For those, the only allowed label names are -// "code" and "method". The function panics otherwise. For the "method" -// label a predefined default label value set is used to filter given values. -// Values besides predefined values will count as `unknown` method. -// `WithExtraMethods` can be used to add more methods to the set. Partitioning of the -// CounterVec happens by HTTP status code and/or HTTP method if the respective -// instance label names are present in the CounterVec. For unpartitioned -// counting, use a CounterVec with zero labels. -// -// If the wrapped Handler does not set a status code, a status code of 200 is assumed. -// -// If the wrapped Handler panics, the Counter is not incremented. -// -// See the example for InstrumentHandlerDuration for example usage. -func InstrumentHandlerCounter(counter *prometheus.CounterVec, next http.Handler, opts ...Option) http.HandlerFunc { - mwOpts := &option{} - for _, o := range opts { - o(mwOpts) - } - - code, method := checkLabels(counter) - - if code { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - d := newDelegator(w, nil) - next.ServeHTTP(d, r) - counter.With(labels(code, method, r.Method, d.Status(), mwOpts.extraMethods...)).Inc() - }) - } - - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - next.ServeHTTP(w, r) - counter.With(labels(code, method, r.Method, 0, mwOpts.extraMethods...)).Inc() - }) -} - -// InstrumentHandlerTimeToWriteHeader is a middleware that wraps the provided -// http.Handler to observe with the provided ObserverVec the request duration -// until the response headers are written. The ObserverVec must have valid -// metric and label names and must have zero, one, or two non-const non-curried -// labels. For those, the only allowed label names are "code" and "method". The -// function panics otherwise. For the "method" label a predefined default label -// value set is used to filter given values. Values besides predefined values -// will count as `unknown` method.`WithExtraMethods` can be used to add more -// methods to the set. The Observe method of the Observer in the -// ObserverVec is called with the request duration in seconds. Partitioning -// happens by HTTP status code and/or HTTP method if the respective instance -// label names are present in the ObserverVec. For unpartitioned observations, -// use an ObserverVec with zero labels. Note that partitioning of Histograms is -// expensive and should be used judiciously. -// -// If the wrapped Handler panics before calling WriteHeader, no value is -// reported. -// -// Note that this method is only guaranteed to never observe negative durations -// if used with Go1.9+. -// -// See the example for InstrumentHandlerDuration for example usage. -func InstrumentHandlerTimeToWriteHeader(obs prometheus.ObserverVec, next http.Handler, opts ...Option) http.HandlerFunc { - mwOpts := &option{} - for _, o := range opts { - o(mwOpts) - } - - code, method := checkLabels(obs) - - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - now := time.Now() - d := newDelegator(w, func(status int) { - obs.With(labels(code, method, r.Method, status, mwOpts.extraMethods...)).Observe(time.Since(now).Seconds()) - }) - next.ServeHTTP(d, r) - }) -} - -// InstrumentHandlerRequestSize is a middleware that wraps the provided -// http.Handler to observe the request size with the provided ObserverVec. The -// ObserverVec must have valid metric and label names and must have zero, one, -// or two non-const non-curried labels. For those, the only allowed label names -// are "code" and "method". The function panics otherwise. For the "method" -// label a predefined default label value set is used to filter given values. -// Values besides predefined values will count as `unknown` method. -// `WithExtraMethods` can be used to add more methods to the set. The Observe -// method of the Observer in the ObserverVec is called with the request size in -// bytes. Partitioning happens by HTTP status code and/or HTTP method if the -// respective instance label names are present in the ObserverVec. For -// unpartitioned observations, use an ObserverVec with zero labels. Note that -// partitioning of Histograms is expensive and should be used judiciously. -// -// If the wrapped Handler does not set a status code, a status code of 200 is assumed. -// -// If the wrapped Handler panics, no values are reported. -// -// See the example for InstrumentHandlerDuration for example usage. -func InstrumentHandlerRequestSize(obs prometheus.ObserverVec, next http.Handler, opts ...Option) http.HandlerFunc { - mwOpts := &option{} - for _, o := range opts { - o(mwOpts) - } - - code, method := checkLabels(obs) - - if code { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - d := newDelegator(w, nil) - next.ServeHTTP(d, r) - size := computeApproximateRequestSize(r) - obs.With(labels(code, method, r.Method, d.Status(), mwOpts.extraMethods...)).Observe(float64(size)) - }) - } - - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - next.ServeHTTP(w, r) - size := computeApproximateRequestSize(r) - obs.With(labels(code, method, r.Method, 0, mwOpts.extraMethods...)).Observe(float64(size)) - }) -} - -// InstrumentHandlerResponseSize is a middleware that wraps the provided -// http.Handler to observe the response size with the provided ObserverVec. The -// ObserverVec must have valid metric and label names and must have zero, one, -// or two non-const non-curried labels. For those, the only allowed label names -// are "code" and "method". The function panics otherwise. For the "method" -// label a predefined default label value set is used to filter given values. -// Values besides predefined values will count as `unknown` method. -// `WithExtraMethods` can be used to add more methods to the set. The Observe -// method of the Observer in the ObserverVec is called with the response size in -// bytes. Partitioning happens by HTTP status code and/or HTTP method if the -// respective instance label names are present in the ObserverVec. For -// unpartitioned observations, use an ObserverVec with zero labels. Note that -// partitioning of Histograms is expensive and should be used judiciously. -// -// If the wrapped Handler does not set a status code, a status code of 200 is assumed. -// -// If the wrapped Handler panics, no values are reported. -// -// See the example for InstrumentHandlerDuration for example usage. -func InstrumentHandlerResponseSize(obs prometheus.ObserverVec, next http.Handler, opts ...Option) http.Handler { - mwOpts := &option{} - for _, o := range opts { - o(mwOpts) - } - - code, method := checkLabels(obs) - - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - d := newDelegator(w, nil) - next.ServeHTTP(d, r) - obs.With(labels(code, method, r.Method, d.Status(), mwOpts.extraMethods...)).Observe(float64(d.Written())) - }) -} - -// checkLabels returns whether the provided Collector has a non-const, -// non-curried label named "code" and/or "method". It panics if the provided -// Collector does not have a Desc or has more than one Desc or its Desc is -// invalid. It also panics if the Collector has any non-const, non-curried -// labels that are not named "code" or "method". -func checkLabels(c prometheus.Collector) (code bool, method bool) { - // TODO(beorn7): Remove this hacky way to check for instance labels - // once Descriptors can have their dimensionality queried. - var ( - desc *prometheus.Desc - m prometheus.Metric - pm dto.Metric - lvs []string - ) - - // Get the Desc from the Collector. - descc := make(chan *prometheus.Desc, 1) - c.Describe(descc) - - select { - case desc = <-descc: - default: - panic("no description provided by collector") - } - select { - case <-descc: - panic("more than one description provided by collector") - default: - } - - close(descc) - - // Make sure the Collector has a valid Desc by registering it with a - // temporary registry. - prometheus.NewRegistry().MustRegister(c) - - // Create a ConstMetric with the Desc. Since we don't know how many - // variable labels there are, try for as long as it needs. - for err := errors.New("dummy"); err != nil; lvs = append(lvs, magicString) { - m, err = prometheus.NewConstMetric(desc, prometheus.UntypedValue, 0, lvs...) - } - - // Write out the metric into a proto message and look at the labels. - // If the value is not the magicString, it is a constLabel, which doesn't interest us. - // If the label is curried, it doesn't interest us. - // In all other cases, only "code" or "method" is allowed. - if err := m.Write(&pm); err != nil { - panic("error checking metric for labels") - } - for _, label := range pm.Label { - name, value := label.GetName(), label.GetValue() - if value != magicString || isLabelCurried(c, name) { - continue - } - switch name { - case "code": - code = true - case "method": - method = true - default: - panic("metric partitioned with non-supported labels") - } - } - return -} - -func isLabelCurried(c prometheus.Collector, label string) bool { - // This is even hackier than the label test above. - // We essentially try to curry again and see if it works. - // But for that, we need to type-convert to the two - // types we use here, ObserverVec or *CounterVec. - switch v := c.(type) { - case *prometheus.CounterVec: - if _, err := v.CurryWith(prometheus.Labels{label: "dummy"}); err == nil { - return false - } - case prometheus.ObserverVec: - if _, err := v.CurryWith(prometheus.Labels{label: "dummy"}); err == nil { - return false - } - default: - panic("unsupported metric vec type") - } - return true -} - -// emptyLabels is a one-time allocation for non-partitioned metrics to avoid -// unnecessary allocations on each request. -var emptyLabels = prometheus.Labels{} - -func labels(code, method bool, reqMethod string, status int, extraMethods ...string) prometheus.Labels { - if !(code || method) { - return emptyLabels - } - labels := prometheus.Labels{} - - if code { - labels["code"] = sanitizeCode(status) - } - if method { - labels["method"] = sanitizeMethod(reqMethod, extraMethods...) - } - - return labels -} - -func computeApproximateRequestSize(r *http.Request) int { - s := 0 - if r.URL != nil { - s += len(r.URL.String()) - } - - s += len(r.Method) - s += len(r.Proto) - for name, values := range r.Header { - s += len(name) - for _, value := range values { - s += len(value) - } - } - s += len(r.Host) - - // N.B. r.Form and r.MultipartForm are assumed to be included in r.URL. - - if r.ContentLength != -1 { - s += int(r.ContentLength) - } - return s -} - -// If the wrapped http.Handler has a known method, it will be sanitized and returned. -// Otherwise, "unknown" will be returned. The known method list can be extended -// as needed by using extraMethods parameter. -func sanitizeMethod(m string, extraMethods ...string) string { - // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods for - // the methods chosen as default. - switch m { - case "GET", "get": - return "get" - case "PUT", "put": - return "put" - case "HEAD", "head": - return "head" - case "POST", "post": - return "post" - case "DELETE", "delete": - return "delete" - case "CONNECT", "connect": - return "connect" - case "OPTIONS", "options": - return "options" - case "NOTIFY", "notify": - return "notify" - case "TRACE", "trace": - return "trace" - case "PATCH", "patch": - return "patch" - default: - for _, method := range extraMethods { - if strings.EqualFold(m, method) { - return strings.ToLower(m) - } - } - return "unknown" - } -} - -// If the wrapped http.Handler has not set a status code, i.e. the value is -// currently 0, sanitizeCode will return 200, for consistency with behavior in -// the stdlib. -func sanitizeCode(s int) string { - // See for accepted codes https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml - switch s { - case 100: - return "100" - case 101: - return "101" - - case 200, 0: - return "200" - case 201: - return "201" - case 202: - return "202" - case 203: - return "203" - case 204: - return "204" - case 205: - return "205" - case 206: - return "206" - - case 300: - return "300" - case 301: - return "301" - case 302: - return "302" - case 304: - return "304" - case 305: - return "305" - case 307: - return "307" - - case 400: - return "400" - case 401: - return "401" - case 402: - return "402" - case 403: - return "403" - case 404: - return "404" - case 405: - return "405" - case 406: - return "406" - case 407: - return "407" - case 408: - return "408" - case 409: - return "409" - case 410: - return "410" - case 411: - return "411" - case 412: - return "412" - case 413: - return "413" - case 414: - return "414" - case 415: - return "415" - case 416: - return "416" - case 417: - return "417" - case 418: - return "418" - - case 500: - return "500" - case 501: - return "501" - case 502: - return "502" - case 503: - return "503" - case 504: - return "504" - case 505: - return "505" - - case 428: - return "428" - case 429: - return "429" - case 431: - return "431" - case 511: - return "511" - - default: - if s >= 100 && s <= 599 { - return strconv.Itoa(s) - } - return "unknown" - } -} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/option.go b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/option.go deleted file mode 100644 index 35e41bd1e..000000000 --- a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/option.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2022 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 promhttp - -// Option are used to configure a middleware or round tripper.. -type Option func(*option) - -type option struct { - extraMethods []string -} - -// WithExtraMethods adds additional HTTP methods to the list of allowed methods. -// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods for the default list. -// -// See the example for ExampleInstrumentHandlerWithExtraMethods for example usage. -func WithExtraMethods(methods ...string) Option { - return func(o *option) { - o.extraMethods = methods - } -} diff --git a/vendor/modules.txt b/vendor/modules.txt index eedf0e080..0ab7f5fd9 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -86,7 +86,6 @@ github.com/pmezard/go-difflib/difflib github.com/prometheus/client_golang/prometheus github.com/prometheus/client_golang/prometheus/internal github.com/prometheus/client_golang/prometheus/promauto -github.com/prometheus/client_golang/prometheus/promhttp # github.com/prometheus/client_model v0.2.0 ## explicit; go 1.9 github.com/prometheus/client_model/go