mirror of
https://github.com/kubernetes-sigs/prometheus-adapter.git
synced 2026-04-06 09:47:54 +00:00
Compare commits
94 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
01919d0ef1 | ||
|
|
21ea0ab279 | ||
|
|
c2ae4cdaf1 | ||
|
|
26d05b7ae9 | ||
|
|
17cef511b1 | ||
|
|
9988fd3e91 | ||
|
|
06e1d3913e | ||
|
|
39ef9fa0e7 | ||
|
|
01b29a6578 | ||
|
|
1d31a46aa1 | ||
|
|
d3784c5725 | ||
|
|
aba25ac4aa | ||
|
|
fdde189945 | ||
|
|
63bd3e8d44 | ||
|
|
1692f124d3 | ||
|
|
11d7d2bb05 | ||
|
|
b224085e86 | ||
|
|
5d9b01a57a | ||
|
|
4d5c98d364 | ||
|
|
b48bff400e | ||
|
|
9156bf3fbc | ||
|
|
27cf936f32 | ||
|
|
ed795c1ae2 | ||
|
|
a64d132d91 | ||
|
|
a01b094a63 | ||
|
|
f588141f08 | ||
|
|
98e716c7d3 | ||
|
|
ba77337ae4 | ||
|
|
a5bcb39046 | ||
|
|
2a4a4316dd | ||
|
|
f82ee9d1dc | ||
|
|
a53ee9eed1 | ||
|
|
7ba3c13bb6 | ||
|
|
891c52fe00 | ||
|
|
e772844ed8 | ||
|
|
6a1ba321da | ||
|
|
0032610ace | ||
|
|
fda3dad49b | ||
|
|
4cc5de93cb | ||
|
|
a4100f047a | ||
|
|
cb883fb789 | ||
|
|
198c469805 | ||
|
|
7cf3ac5d90 | ||
|
|
966ef227fe | ||
|
|
74ba84b76e | ||
|
|
36fbcc78f1 | ||
|
|
0a6e74a5b3 | ||
|
|
147dacee4a | ||
|
|
f733e2f74d | ||
|
|
b50333c035 | ||
|
|
f69aae4c78 | ||
|
|
8579be6c7b | ||
|
|
e69388346f | ||
|
|
86efb37019 | ||
|
|
c8caa11da1 | ||
|
|
b3a3d97596 | ||
|
|
27eb607509 | ||
|
|
d58cdcee93 | ||
|
|
aab718746b | ||
|
|
8528f29516 | ||
|
|
9a1ffb7b17 | ||
|
|
6a7f2b5ce1 | ||
|
|
f607905cf6 | ||
|
|
7bdb7f14b9 | ||
|
|
1145dbfe93 | ||
|
|
307795482f | ||
|
|
d341e8f67b | ||
|
|
b03cc3e7c8 | ||
|
|
b233597358 | ||
|
|
5d24df6353 | ||
|
|
411763b355 | ||
|
|
062c42eccc | ||
|
|
e18cc18201 | ||
|
|
70604d2f54 | ||
|
|
03cd31007e | ||
|
|
3d590269aa | ||
|
|
09cc27e609 | ||
|
|
a5faf9f920 | ||
|
|
dc0c0058d0 | ||
|
|
8958457968 | ||
|
|
0ea1c1b8d3 | ||
|
|
85e2d2052d | ||
|
|
d5c45b27b0 | ||
|
|
fdfecc8d7f | ||
|
|
e740fee947 | ||
|
|
268b2a8ec2 | ||
|
|
372dfc9d3a | ||
|
|
3afe2c74bc | ||
|
|
dd75b55557 | ||
|
|
4767a63a67 | ||
|
|
204d5996a4 | ||
|
|
d4d0a69514 | ||
|
|
e5ad3d8903 | ||
|
|
465e4153f9 |
67 changed files with 3295 additions and 1726 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -2,3 +2,4 @@
|
||||||
*~
|
*~
|
||||||
/vendor
|
/vendor
|
||||||
/adapter
|
/adapter
|
||||||
|
.e2e
|
||||||
|
|
|
||||||
39
.golangci.yml
Normal file
39
.golangci.yml
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
run:
|
||||||
|
deadline: 5m
|
||||||
|
|
||||||
|
linters:
|
||||||
|
disable-all: true
|
||||||
|
enable:
|
||||||
|
- bodyclose
|
||||||
|
- dogsled
|
||||||
|
- dupl
|
||||||
|
- errcheck
|
||||||
|
- exportloopref
|
||||||
|
- gocritic
|
||||||
|
- gocyclo
|
||||||
|
- gofmt
|
||||||
|
- goimports
|
||||||
|
- gosec
|
||||||
|
- goprintffuncname
|
||||||
|
- gosimple
|
||||||
|
- govet
|
||||||
|
- ineffassign
|
||||||
|
- misspell
|
||||||
|
- nakedret
|
||||||
|
- nolintlint
|
||||||
|
- revive
|
||||||
|
- staticcheck
|
||||||
|
- stylecheck
|
||||||
|
- typecheck
|
||||||
|
- unconvert
|
||||||
|
- unused
|
||||||
|
- whitespace
|
||||||
|
|
||||||
|
linters-settings:
|
||||||
|
goimports:
|
||||||
|
local-prefixes: sigs.k8s.io/prometheus-adapter
|
||||||
|
revive:
|
||||||
|
rules:
|
||||||
|
- name: exported
|
||||||
|
arguments:
|
||||||
|
- disableStutteringCheck
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
ARG ARCH
|
||||||
ARG GO_VERSION
|
ARG GO_VERSION
|
||||||
|
|
||||||
FROM golang:${GO_VERSION} as build
|
FROM golang:${GO_VERSION} as build
|
||||||
|
|
@ -14,7 +15,7 @@ COPY Makefile Makefile
|
||||||
ARG ARCH
|
ARG ARCH
|
||||||
RUN make prometheus-adapter
|
RUN make prometheus-adapter
|
||||||
|
|
||||||
FROM gcr.io/distroless/static:latest
|
FROM gcr.io/distroless/static:latest-$ARCH
|
||||||
|
|
||||||
COPY --from=build /go/src/sigs.k8s.io/prometheus-adapter/adapter /
|
COPY --from=build /go/src/sigs.k8s.io/prometheus-adapter/adapter /
|
||||||
USER 65534
|
USER 65534
|
||||||
|
|
|
||||||
47
Makefile
47
Makefile
|
|
@ -2,12 +2,14 @@ REGISTRY?=gcr.io/k8s-staging-prometheus-adapter
|
||||||
IMAGE=prometheus-adapter
|
IMAGE=prometheus-adapter
|
||||||
ARCH?=$(shell go env GOARCH)
|
ARCH?=$(shell go env GOARCH)
|
||||||
ALL_ARCH=amd64 arm arm64 ppc64le s390x
|
ALL_ARCH=amd64 arm arm64 ppc64le s390x
|
||||||
|
GOPATH:=$(shell go env GOPATH)
|
||||||
|
|
||||||
VERSION=$(shell cat VERSION)
|
VERSION=$(shell cat VERSION)
|
||||||
TAG_PREFIX=v
|
TAG_PREFIX=v
|
||||||
TAG?=$(TAG_PREFIX)$(VERSION)
|
TAG?=$(TAG_PREFIX)$(VERSION)
|
||||||
|
|
||||||
GO_VERSION?=1.18.5
|
GO_VERSION?=1.22.5
|
||||||
|
GOLANGCI_VERSION?=1.56.2
|
||||||
|
|
||||||
.PHONY: all
|
.PHONY: all
|
||||||
all: prometheus-adapter
|
all: prometheus-adapter
|
||||||
|
|
@ -53,25 +55,38 @@ push-multi-arch:
|
||||||
test:
|
test:
|
||||||
CGO_ENABLED=0 go test ./cmd/... ./pkg/...
|
CGO_ENABLED=0 go test ./cmd/... ./pkg/...
|
||||||
|
|
||||||
|
.PHONY: test-e2e
|
||||||
|
test-e2e:
|
||||||
|
./test/run-e2e-tests.sh
|
||||||
|
|
||||||
|
|
||||||
# Static analysis
|
# Static analysis
|
||||||
# ---------------
|
# ---------------
|
||||||
|
|
||||||
.PHONY: verify
|
.PHONY: verify
|
||||||
verify: verify-gofmt verify-deps verify-generated test
|
verify: verify-lint verify-deps verify-generated
|
||||||
|
|
||||||
.PHONY: update
|
.PHONY: update
|
||||||
update: update-generated
|
update: update-lint update-generated
|
||||||
|
|
||||||
# Format
|
# Format and lint
|
||||||
# ------
|
# ---------------
|
||||||
|
|
||||||
.PHONY: verify-gofmt
|
HAS_GOLANGCI_VERSION:=$(shell $(GOPATH)/bin/golangci-lint version --format=short)
|
||||||
verify-gofmt:
|
.PHONY: golangci
|
||||||
./hack/gofmt-all.sh -v
|
golangci:
|
||||||
|
ifneq ($(HAS_GOLANGCI_VERSION), $(GOLANGCI_VERSION))
|
||||||
|
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(GOPATH)/bin v$(GOLANGCI_VERSION)
|
||||||
|
endif
|
||||||
|
|
||||||
|
.PHONY: verify-lint
|
||||||
|
verify-lint: golangci
|
||||||
|
$(GOPATH)/bin/golangci-lint run --modules-download-mode=readonly || (echo 'Run "make update-lint"' && exit 1)
|
||||||
|
|
||||||
|
.PHONY: update-lint
|
||||||
|
update-lint: golangci
|
||||||
|
$(GOPATH)/bin/golangci-lint run --fix --modules-download-mode=readonly
|
||||||
|
|
||||||
.PHONY: gofmt
|
|
||||||
gofmt:
|
|
||||||
./hack/gofmt-all.sh
|
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
# ------------
|
# ------------
|
||||||
|
|
@ -88,10 +103,16 @@ verify-deps:
|
||||||
generated_files=pkg/api/generated/openapi/zz_generated.openapi.go
|
generated_files=pkg/api/generated/openapi/zz_generated.openapi.go
|
||||||
|
|
||||||
.PHONY: verify-generated
|
.PHONY: verify-generated
|
||||||
verify-generated:
|
verify-generated: update-generated
|
||||||
@git diff --exit-code -- $(generated_files)
|
@git diff --exit-code -- $(generated_files)
|
||||||
|
|
||||||
.PHONY: update-generated
|
.PHONY: update-generated
|
||||||
update-generated:
|
update-generated:
|
||||||
go install -mod=readonly k8s.io/kube-openapi/cmd/openapi-gen
|
go install -mod=readonly k8s.io/kube-openapi/cmd/openapi-gen
|
||||||
$(GOPATH)/bin/openapi-gen --logtostderr -i k8s.io/metrics/pkg/apis/custom_metrics,k8s.io/metrics/pkg/apis/custom_metrics/v1beta1,k8s.io/metrics/pkg/apis/custom_metrics/v1beta2,k8s.io/metrics/pkg/apis/external_metrics,k8s.io/metrics/pkg/apis/external_metrics/v1beta1,k8s.io/metrics/pkg/apis/metrics,k8s.io/metrics/pkg/apis/metrics/v1beta1,k8s.io/apimachinery/pkg/apis/meta/v1,k8s.io/apimachinery/pkg/api/resource,k8s.io/apimachinery/pkg/version,k8s.io/api/core/v1 -h ./hack/boilerplate.go.txt -p ./pkg/api/generated/openapi -O zz_generated.openapi -o ./ -r /dev/null
|
$(GOPATH)/bin/openapi-gen --logtostderr \
|
||||||
|
--go-header-file ./hack/boilerplate.go.txt \
|
||||||
|
--output-pkg ./pkg/api/generated/openapi \
|
||||||
|
--output-file zz_generated.openapi.go \
|
||||||
|
--output-dir ./pkg/api/generated/openapi \
|
||||||
|
-r /dev/null \
|
||||||
|
"k8s.io/metrics/pkg/apis/custom_metrics" "k8s.io/metrics/pkg/apis/custom_metrics/v1beta1" "k8s.io/metrics/pkg/apis/custom_metrics/v1beta2" "k8s.io/metrics/pkg/apis/external_metrics" "k8s.io/metrics/pkg/apis/external_metrics/v1beta1" "k8s.io/metrics/pkg/apis/metrics" "k8s.io/metrics/pkg/apis/metrics/v1beta1" "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/version" "k8s.io/api/core/v1"
|
||||||
|
|
|
||||||
10
OWNERS
10
OWNERS
|
|
@ -1,11 +1,17 @@
|
||||||
# See the OWNERS docs at https://go.k8s.io/owners
|
# See the OWNERS docs at https://go.k8s.io/owners
|
||||||
approvers:
|
approvers:
|
||||||
- prometheus-adapter-approvers
|
- dgrisonnet
|
||||||
|
- logicalhan
|
||||||
|
- dashpole
|
||||||
|
|
||||||
reviewers:
|
reviewers:
|
||||||
- prometheus-adapter-approvers
|
- dgrisonnet
|
||||||
|
- olivierlemasle
|
||||||
|
- logicalhan
|
||||||
|
- dashpole
|
||||||
|
|
||||||
emeritus_approvers:
|
emeritus_approvers:
|
||||||
- brancz
|
- brancz
|
||||||
- directxman12
|
- directxman12
|
||||||
- lilic
|
- lilic
|
||||||
|
- s-urbaniak
|
||||||
|
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
# See the OWNERS docs at https://go.k8s.io/owners#owners_aliases
|
|
||||||
|
|
||||||
aliases:
|
|
||||||
prometheus-adapter-approvers:
|
|
||||||
- dgrisonnet
|
|
||||||
- s-urbaniak
|
|
||||||
10
README.md
10
README.md
|
|
@ -1,9 +1,7 @@
|
||||||
# Prometheus Adapter for Kubernetes Metrics APIs
|
# Prometheus Adapter for Kubernetes Metrics APIs
|
||||||
|
|
||||||
This repository contains an implementation of the Kubernetes
|
This repository contains an implementation of the Kubernetes Custom, Resource and External
|
||||||
[resource metrics](https://github.com/kubernetes/community/blob/master/contributors/design-proposals/instrumentation/resource-metrics-api.md),
|
[Metric APIs](https://github.com/kubernetes/metrics).
|
||||||
[custom metrics](https://github.com/kubernetes/community/blob/master/contributors/design-proposals/instrumentation/custom-metrics-api.md), and
|
|
||||||
[external metrics](https://github.com/kubernetes/community/blob/master/contributors/design-proposals/instrumentation/external-metrics-api.md) APIs.
|
|
||||||
|
|
||||||
This adapter is therefore suitable for use with the autoscaling/v2 Horizontal Pod Autoscaler in Kubernetes 1.6+.
|
This adapter is therefore suitable for use with the autoscaling/v2 Horizontal Pod Autoscaler in Kubernetes 1.6+.
|
||||||
It can also replace the [metrics server](https://github.com/kubernetes-incubator/metrics-server) on clusters that already run Prometheus and collect the appropriate metrics.
|
It can also replace the [metrics server](https://github.com/kubernetes-incubator/metrics-server) on clusters that already run Prometheus and collect the appropriate metrics.
|
||||||
|
|
@ -36,7 +34,7 @@ $ helm install my-release prometheus-community/prometheus-adapter
|
||||||
|
|
||||||
Official images
|
Official images
|
||||||
---
|
---
|
||||||
All official images for releases after v0.8.4 are available in `k8s.gcr.io/prometheus-adapter/prometheus-adapter:$VERSION`. The project also maintains a [staging registry](https://console.cloud.google.com/gcr/images/k8s-staging-prometheus-adapter/GLOBAL/) where images for each commit from the master branch are published. You can use this registry if you need to test a version from a specific commit, or if you need to deploy a patch while waiting for a new release.
|
All official images for releases after v0.8.4 are available in `registry.k8s.io/prometheus-adapter/prometheus-adapter:$VERSION`. The project also maintains a [staging registry](https://console.cloud.google.com/gcr/images/k8s-staging-prometheus-adapter/GLOBAL/) where images for each commit from the master branch are published. You can use this registry if you need to test a version from a specific commit, or if you need to deploy a patch while waiting for a new release.
|
||||||
|
|
||||||
Images for versions v0.8.4 and prior are only available in unofficial registries:
|
Images for versions v0.8.4 and prior are only available in unofficial registries:
|
||||||
* https://quay.io/repository/coreos/k8s-prometheus-adapter-amd64
|
* https://quay.io/repository/coreos/k8s-prometheus-adapter-amd64
|
||||||
|
|
@ -51,7 +49,7 @@ will attempt to using [Kubernetes in-cluster
|
||||||
config](https://kubernetes.io/docs/tasks/access-application-cluster/access-cluster/#accessing-the-api-from-a-pod)
|
config](https://kubernetes.io/docs/tasks/access-application-cluster/access-cluster/#accessing-the-api-from-a-pod)
|
||||||
to connect to the cluster.
|
to connect to the cluster.
|
||||||
|
|
||||||
It takes the following addition arguments specific to configuring how the
|
It takes the following additional arguments specific to configuring how the
|
||||||
adapter talks to Prometheus and the main Kubernetes cluster:
|
adapter talks to Prometheus and the main Kubernetes cluster:
|
||||||
|
|
||||||
- `--lister-kubeconfig=<path-to-kubeconfig>`: This configures
|
- `--lister-kubeconfig=<path-to-kubeconfig>`: This configures
|
||||||
|
|
|
||||||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
||||||
0.10.0
|
0.12.0
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"flag"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -82,7 +80,8 @@ type PrometheusAdapter struct {
|
||||||
MetricsRelistInterval time.Duration
|
MetricsRelistInterval time.Duration
|
||||||
// MetricsMaxAge is the period to query available metrics for
|
// MetricsMaxAge is the period to query available metrics for
|
||||||
MetricsMaxAge time.Duration
|
MetricsMaxAge time.Duration
|
||||||
|
// DisableHTTP2 indicates that http2 should not be enabled.
|
||||||
|
DisableHTTP2 bool
|
||||||
metricsConfig *adaptercfg.MetricsDiscoveryConfig
|
metricsConfig *adaptercfg.MetricsDiscoveryConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -93,7 +92,7 @@ func (cmd *PrometheusAdapter) makePromClient() (prom.Client, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if cmd.PrometheusVerb != http.MethodGet && cmd.PrometheusVerb != http.MethodPost {
|
if cmd.PrometheusVerb != http.MethodGet && cmd.PrometheusVerb != http.MethodPost {
|
||||||
return nil, fmt.Errorf("unsupported Prometheus HTTP verb %q. use \"GET\" or \"POST\" instead.", cmd.PrometheusVerb)
|
return nil, fmt.Errorf("unsupported Prometheus HTTP verb %q; supported verbs: \"GET\" and \"POST\"", cmd.PrometheusVerb)
|
||||||
}
|
}
|
||||||
|
|
||||||
var httpClient *http.Client
|
var httpClient *http.Client
|
||||||
|
|
@ -115,11 +114,15 @@ func (cmd *PrometheusAdapter) makePromClient() (prom.Client, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if cmd.PrometheusTokenFile != "" {
|
if cmd.PrometheusTokenFile != "" {
|
||||||
data, err := ioutil.ReadFile(cmd.PrometheusTokenFile)
|
data, err := os.ReadFile(cmd.PrometheusTokenFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to read prometheus-token-file: %v", err)
|
return nil, fmt.Errorf("failed to read prometheus-token-file: %v", err)
|
||||||
}
|
}
|
||||||
httpClient.Transport = transport.NewBearerAuthRoundTripper(string(data), httpClient.Transport)
|
wrappedTransport := http.DefaultTransport
|
||||||
|
if httpClient.Transport != nil {
|
||||||
|
wrappedTransport = httpClient.Transport
|
||||||
|
}
|
||||||
|
httpClient.Transport = transport.NewBearerAuthRoundTripper(string(data), wrappedTransport)
|
||||||
}
|
}
|
||||||
genericPromClient := prom.NewGenericAPIClient(httpClient, baseURL, parseHeaderArgs(cmd.PrometheusHeaders))
|
genericPromClient := prom.NewGenericAPIClient(httpClient, baseURL, parseHeaderArgs(cmd.PrometheusHeaders))
|
||||||
instrumentedGenericPromClient := mprom.InstrumentGenericAPIClient(genericPromClient, baseURL.String())
|
instrumentedGenericPromClient := mprom.InstrumentGenericAPIClient(genericPromClient, baseURL.String())
|
||||||
|
|
@ -148,10 +151,15 @@ func (cmd *PrometheusAdapter) addFlags() {
|
||||||
cmd.Flags().StringVar(&cmd.AdapterConfigFile, "config", cmd.AdapterConfigFile,
|
cmd.Flags().StringVar(&cmd.AdapterConfigFile, "config", cmd.AdapterConfigFile,
|
||||||
"Configuration file containing details of how to transform between Prometheus metrics "+
|
"Configuration file containing details of how to transform between Prometheus metrics "+
|
||||||
"and custom metrics API resources")
|
"and custom metrics API resources")
|
||||||
cmd.Flags().DurationVar(&cmd.MetricsRelistInterval, "metrics-relist-interval", cmd.MetricsRelistInterval, ""+
|
cmd.Flags().DurationVar(&cmd.MetricsRelistInterval, "metrics-relist-interval", cmd.MetricsRelistInterval,
|
||||||
"interval at which to re-list the set of all available metrics from Prometheus")
|
"interval at which to re-list the set of all available metrics from Prometheus")
|
||||||
cmd.Flags().DurationVar(&cmd.MetricsMaxAge, "metrics-max-age", cmd.MetricsMaxAge, ""+
|
cmd.Flags().DurationVar(&cmd.MetricsMaxAge, "metrics-max-age", cmd.MetricsMaxAge,
|
||||||
"period for which to query the set of available metrics from Prometheus")
|
"period for which to query the set of available metrics from Prometheus")
|
||||||
|
cmd.Flags().BoolVar(&cmd.DisableHTTP2, "disable-http2", cmd.DisableHTTP2,
|
||||||
|
"Disable HTTP/2 support")
|
||||||
|
|
||||||
|
// Add logging flags
|
||||||
|
logs.AddFlags(cmd.Flags())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cmd *PrometheusAdapter) loadConfig() error {
|
func (cmd *PrometheusAdapter) loadConfig() error {
|
||||||
|
|
@ -261,12 +269,24 @@ func (cmd *PrometheusAdapter) addResourceMetricsAPI(promClient prom.Client, stop
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config, err := cmd.Config()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
config.GenericConfig.EnableMetrics = false
|
||||||
|
|
||||||
server, err := cmd.Server()
|
server, err := cmd.Server()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := api.Install(provider, podInformer.Lister(), informer.Core().V1().Nodes().Lister(), server.GenericAPIServer); err != nil {
|
metricsHandler, err := mprom.MetricsHandler()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
server.GenericAPIServer.Handler.NonGoRestfulMux.HandleFunc("/metrics", metricsHandler)
|
||||||
|
|
||||||
|
if err := api.Install(provider, podInformer.Lister(), informer.Core().V1().Nodes().Lister(), server.GenericAPIServer, nil); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -287,17 +307,21 @@ func main() {
|
||||||
}
|
}
|
||||||
cmd.Name = "prometheus-metrics-adapter"
|
cmd.Name = "prometheus-metrics-adapter"
|
||||||
|
|
||||||
|
cmd.addFlags()
|
||||||
|
if err := cmd.Flags().Parse(os.Args); err != nil {
|
||||||
|
klog.Fatalf("unable to parse flags: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.OpenAPIConfig == nil {
|
||||||
cmd.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig(generatedopenapi.GetOpenAPIDefinitions, openapinamer.NewDefinitionNamer(api.Scheme, customexternalmetrics.Scheme))
|
cmd.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig(generatedopenapi.GetOpenAPIDefinitions, openapinamer.NewDefinitionNamer(api.Scheme, customexternalmetrics.Scheme))
|
||||||
cmd.OpenAPIConfig.Info.Title = "prometheus-metrics-adapter"
|
cmd.OpenAPIConfig.Info.Title = "prometheus-metrics-adapter"
|
||||||
cmd.OpenAPIConfig.Info.Version = "1.0.0"
|
cmd.OpenAPIConfig.Info.Version = "1.0.0"
|
||||||
|
}
|
||||||
|
|
||||||
cmd.addFlags()
|
if cmd.OpenAPIV3Config == nil {
|
||||||
// make sure we get klog flags
|
cmd.OpenAPIV3Config = genericapiserver.DefaultOpenAPIV3Config(generatedopenapi.GetOpenAPIDefinitions, openapinamer.NewDefinitionNamer(api.Scheme, customexternalmetrics.Scheme))
|
||||||
local := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
|
cmd.OpenAPIV3Config.Info.Title = "prometheus-metrics-adapter"
|
||||||
logs.AddGoFlags(local)
|
cmd.OpenAPIV3Config.Info.Version = "1.0.0"
|
||||||
cmd.Flags().AddGoFlagSet(local)
|
|
||||||
if err := cmd.Flags().Parse(os.Args); err != nil {
|
|
||||||
klog.Fatalf("unable to parse flags: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if --metrics-max-age is not set, make it equal to --metrics-relist-interval
|
// if --metrics-max-age is not set, make it equal to --metrics-relist-interval
|
||||||
|
|
@ -346,6 +370,14 @@ func main() {
|
||||||
klog.Fatalf("unable to install resource metrics API: %v", err)
|
klog.Fatalf("unable to install resource metrics API: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// disable HTTP/2 to mitigate CVE-2023-44487 until the Go standard library
|
||||||
|
// and golang.org/x/net are fully fixed.
|
||||||
|
server, err := cmd.Server()
|
||||||
|
if err != nil {
|
||||||
|
klog.Fatalf("unable to fetch server: %v", err)
|
||||||
|
}
|
||||||
|
server.GenericAPIServer.SecureServingInfo.DisableHTTP2 = cmd.DisableHTTP2
|
||||||
|
|
||||||
// run the server
|
// run the server
|
||||||
if err := cmd.Run(stopCh); err != nil {
|
if err := cmd.Run(stopCh); err != nil {
|
||||||
klog.Fatalf("unable to run custom metrics adapter: %v", err)
|
klog.Fatalf("unable to run custom metrics adapter: %v", err)
|
||||||
|
|
@ -388,7 +420,7 @@ func makeKubeconfigHTTPClient(inClusterAuth bool, kubeConfigPath string) (*http.
|
||||||
}
|
}
|
||||||
|
|
||||||
func makePrometheusCAClient(caFilePath string, tlsCertFilePath string, tlsKeyFilePath string) (*http.Client, error) {
|
func makePrometheusCAClient(caFilePath string, tlsCertFilePath string, tlsKeyFilePath string) (*http.Client, error) {
|
||||||
data, err := ioutil.ReadFile(caFilePath)
|
data, err := os.ReadFile(caFilePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to read prometheus-ca-file: %v", err)
|
return nil, fmt.Errorf("failed to read prometheus-ca-file: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -408,6 +440,7 @@ func makePrometheusCAClient(caFilePath string, tlsCertFilePath string, tlsKeyFil
|
||||||
TLSClientConfig: &tls.Config{
|
TLSClientConfig: &tls.Config{
|
||||||
RootCAs: pool,
|
RootCAs: pool,
|
||||||
Certificates: []tls.Certificate{tlsClientCerts},
|
Certificates: []tls.Certificate{tlsClientCerts},
|
||||||
|
MinVersion: tls.VersionTLS12,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
|
|
@ -417,6 +450,7 @@ func makePrometheusCAClient(caFilePath string, tlsCertFilePath string, tlsKeyFil
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
TLSClientConfig: &tls.Config{
|
TLSClientConfig: &tls.Config{
|
||||||
RootCAs: pool,
|
RootCAs: pool,
|
||||||
|
MinVersion: tls.VersionTLS12,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ import (
|
||||||
const certsDir = "testdata"
|
const certsDir = "testdata"
|
||||||
|
|
||||||
func TestMakeKubeconfigHTTPClient(t *testing.T) {
|
func TestMakeKubeconfigHTTPClient(t *testing.T) {
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
kubeconfigPath string
|
kubeconfigPath string
|
||||||
inClusterAuth bool
|
inClusterAuth bool
|
||||||
|
|
@ -71,16 +70,13 @@ func TestMakeKubeconfigHTTPClient(t *testing.T) {
|
||||||
t.Error("HTTP client Transport is nil, expected http.RoundTripper")
|
t.Error("HTTP client Transport is nil, expected http.RoundTripper")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else if err == nil {
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Error is nil, expected %v", err)
|
t.Errorf("Error is nil, expected %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func TestMakePrometheusCAClient(t *testing.T) {
|
func TestMakePrometheusCAClient(t *testing.T) {
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
caFilePath string
|
caFilePath string
|
||||||
tlsCertFilePath string
|
tlsCertFilePath string
|
||||||
|
|
@ -140,16 +136,13 @@ func TestMakePrometheusCAClient(t *testing.T) {
|
||||||
t.Errorf("TLS certificates is %+v, expected nil", prometheusCAClient.Transport.(*http.Transport).TLSClientConfig.Certificates)
|
t.Errorf("TLS certificates is %+v, expected nil", prometheusCAClient.Transport.(*http.Transport).TLSClientConfig.Certificates)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else if err == nil {
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Error is nil, expected %v", err)
|
t.Errorf("Error is nil, expected %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseHeaderArgs(t *testing.T) {
|
func TestParseHeaderArgs(t *testing.T) {
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
args []string
|
args []string
|
||||||
headers http.Header
|
headers http.Header
|
||||||
|
|
@ -185,3 +178,34 @@ func TestParseHeaderArgs(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFlags(t *testing.T) {
|
||||||
|
cmd := &PrometheusAdapter{
|
||||||
|
PrometheusURL: "https://localhost",
|
||||||
|
}
|
||||||
|
cmd.addFlags()
|
||||||
|
|
||||||
|
flags := cmd.FlagSet
|
||||||
|
if flags == nil {
|
||||||
|
t.Fatalf("FlagSet should not be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedFlags := []struct {
|
||||||
|
flag string
|
||||||
|
defaultValue string
|
||||||
|
}{
|
||||||
|
{flag: "v", defaultValue: "0"}, // logging flag (klog)
|
||||||
|
{flag: "prometheus-url", defaultValue: "https://localhost"}, // default is set in cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, e := range expectedFlags {
|
||||||
|
flag := flags.Lookup(e.flag)
|
||||||
|
if flag == nil {
|
||||||
|
t.Errorf("Flag %q expected to be present, was absent", e.flag)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if flag.DefValue != e.defaultValue {
|
||||||
|
t.Errorf("Expected default value %q for flag %q, got %q", e.defaultValue, e.flag, flag.DefValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import (
|
||||||
pmodel "github.com/prometheus/common/model"
|
pmodel "github.com/prometheus/common/model"
|
||||||
|
|
||||||
prom "sigs.k8s.io/prometheus-adapter/pkg/client"
|
prom "sigs.k8s.io/prometheus-adapter/pkg/client"
|
||||||
. "sigs.k8s.io/prometheus-adapter/pkg/config"
|
"sigs.k8s.io/prometheus-adapter/pkg/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DefaultConfig returns a configuration equivalent to the former
|
// DefaultConfig returns a configuration equivalent to the former
|
||||||
|
|
@ -15,55 +15,55 @@ import (
|
||||||
// will be of the form `<prefix><<.Resource>>`, cadvisor series will be
|
// will be of the form `<prefix><<.Resource>>`, cadvisor series will be
|
||||||
// of the form `container_`, and have the label `pod`. Any series ending
|
// of the form `container_`, and have the label `pod`. Any series ending
|
||||||
// in total will be treated as a rate metric.
|
// in total will be treated as a rate metric.
|
||||||
func DefaultConfig(rateInterval time.Duration, labelPrefix string) *MetricsDiscoveryConfig {
|
func DefaultConfig(rateInterval time.Duration, labelPrefix string) *config.MetricsDiscoveryConfig {
|
||||||
return &MetricsDiscoveryConfig{
|
return &config.MetricsDiscoveryConfig{
|
||||||
Rules: []DiscoveryRule{
|
Rules: []config.DiscoveryRule{
|
||||||
// container seconds rate metrics
|
// container seconds rate metrics
|
||||||
{
|
{
|
||||||
SeriesQuery: string(prom.MatchSeries("", prom.NameMatches("^container_.*"), prom.LabelNeq("container", "POD"), prom.LabelNeq("namespace", ""), prom.LabelNeq("pod", ""))),
|
SeriesQuery: string(prom.MatchSeries("", prom.NameMatches("^container_.*"), prom.LabelNeq("container", "POD"), prom.LabelNeq("namespace", ""), prom.LabelNeq("pod", ""))),
|
||||||
Resources: ResourceMapping{
|
Resources: config.ResourceMapping{
|
||||||
Overrides: map[string]GroupResource{
|
Overrides: map[string]config.GroupResource{
|
||||||
"namespace": {Resource: "namespace"},
|
"namespace": {Resource: "namespace"},
|
||||||
"pod": {Resource: "pod"},
|
"pod": {Resource: "pod"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Name: NameMapping{Matches: "^container_(.*)_seconds_total$"},
|
Name: config.NameMapping{Matches: "^container_(.*)_seconds_total$"},
|
||||||
MetricsQuery: fmt.Sprintf(`sum(rate(<<.Series>>{<<.LabelMatchers>>,container!="POD"}[%s])) by (<<.GroupBy>>)`, pmodel.Duration(rateInterval).String()),
|
MetricsQuery: fmt.Sprintf(`sum(rate(<<.Series>>{<<.LabelMatchers>>,container!="POD"}[%s])) by (<<.GroupBy>>)`, pmodel.Duration(rateInterval).String()),
|
||||||
},
|
},
|
||||||
|
|
||||||
// container rate metrics
|
// container rate metrics
|
||||||
{
|
{
|
||||||
SeriesQuery: string(prom.MatchSeries("", prom.NameMatches("^container_.*"), prom.LabelNeq("container", "POD"), prom.LabelNeq("namespace", ""), prom.LabelNeq("pod", ""))),
|
SeriesQuery: string(prom.MatchSeries("", prom.NameMatches("^container_.*"), prom.LabelNeq("container", "POD"), prom.LabelNeq("namespace", ""), prom.LabelNeq("pod", ""))),
|
||||||
SeriesFilters: []RegexFilter{{IsNot: "^container_.*_seconds_total$"}},
|
SeriesFilters: []config.RegexFilter{{IsNot: "^container_.*_seconds_total$"}},
|
||||||
Resources: ResourceMapping{
|
Resources: config.ResourceMapping{
|
||||||
Overrides: map[string]GroupResource{
|
Overrides: map[string]config.GroupResource{
|
||||||
"namespace": {Resource: "namespace"},
|
"namespace": {Resource: "namespace"},
|
||||||
"pod": {Resource: "pod"},
|
"pod": {Resource: "pod"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Name: NameMapping{Matches: "^container_(.*)_total$"},
|
Name: config.NameMapping{Matches: "^container_(.*)_total$"},
|
||||||
MetricsQuery: fmt.Sprintf(`sum(rate(<<.Series>>{<<.LabelMatchers>>,container!="POD"}[%s])) by (<<.GroupBy>>)`, pmodel.Duration(rateInterval).String()),
|
MetricsQuery: fmt.Sprintf(`sum(rate(<<.Series>>{<<.LabelMatchers>>,container!="POD"}[%s])) by (<<.GroupBy>>)`, pmodel.Duration(rateInterval).String()),
|
||||||
},
|
},
|
||||||
|
|
||||||
// container non-cumulative metrics
|
// container non-cumulative metrics
|
||||||
{
|
{
|
||||||
SeriesQuery: string(prom.MatchSeries("", prom.NameMatches("^container_.*"), prom.LabelNeq("container", "POD"), prom.LabelNeq("namespace", ""), prom.LabelNeq("pod", ""))),
|
SeriesQuery: string(prom.MatchSeries("", prom.NameMatches("^container_.*"), prom.LabelNeq("container", "POD"), prom.LabelNeq("namespace", ""), prom.LabelNeq("pod", ""))),
|
||||||
SeriesFilters: []RegexFilter{{IsNot: "^container_.*_total$"}},
|
SeriesFilters: []config.RegexFilter{{IsNot: "^container_.*_total$"}},
|
||||||
Resources: ResourceMapping{
|
Resources: config.ResourceMapping{
|
||||||
Overrides: map[string]GroupResource{
|
Overrides: map[string]config.GroupResource{
|
||||||
"namespace": {Resource: "namespace"},
|
"namespace": {Resource: "namespace"},
|
||||||
"pod": {Resource: "pod"},
|
"pod": {Resource: "pod"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Name: NameMapping{Matches: "^container_(.*)$"},
|
Name: config.NameMapping{Matches: "^container_(.*)$"},
|
||||||
MetricsQuery: `sum(<<.Series>>{<<.LabelMatchers>>,container!="POD"}) by (<<.GroupBy>>)`,
|
MetricsQuery: `sum(<<.Series>>{<<.LabelMatchers>>,container!="POD"}) by (<<.GroupBy>>)`,
|
||||||
},
|
},
|
||||||
|
|
||||||
// normal non-cumulative metrics
|
// normal non-cumulative metrics
|
||||||
{
|
{
|
||||||
SeriesQuery: string(prom.MatchSeries("", prom.LabelNeq(fmt.Sprintf("%snamespace", labelPrefix), ""), prom.NameNotMatches("^container_.*"))),
|
SeriesQuery: string(prom.MatchSeries("", prom.LabelNeq(fmt.Sprintf("%snamespace", labelPrefix), ""), prom.NameNotMatches("^container_.*"))),
|
||||||
SeriesFilters: []RegexFilter{{IsNot: ".*_total$"}},
|
SeriesFilters: []config.RegexFilter{{IsNot: ".*_total$"}},
|
||||||
Resources: ResourceMapping{
|
Resources: config.ResourceMapping{
|
||||||
Template: fmt.Sprintf("%s<<.Resource>>", labelPrefix),
|
Template: fmt.Sprintf("%s<<.Resource>>", labelPrefix),
|
||||||
},
|
},
|
||||||
MetricsQuery: "sum(<<.Series>>{<<.LabelMatchers>>}) by (<<.GroupBy>>)",
|
MetricsQuery: "sum(<<.Series>>{<<.LabelMatchers>>}) by (<<.GroupBy>>)",
|
||||||
|
|
@ -72,9 +72,9 @@ func DefaultConfig(rateInterval time.Duration, labelPrefix string) *MetricsDisco
|
||||||
// normal rate metrics
|
// normal rate metrics
|
||||||
{
|
{
|
||||||
SeriesQuery: string(prom.MatchSeries("", prom.LabelNeq(fmt.Sprintf("%snamespace", labelPrefix), ""), prom.NameNotMatches("^container_.*"))),
|
SeriesQuery: string(prom.MatchSeries("", prom.LabelNeq(fmt.Sprintf("%snamespace", labelPrefix), ""), prom.NameNotMatches("^container_.*"))),
|
||||||
SeriesFilters: []RegexFilter{{IsNot: ".*_seconds_total"}},
|
SeriesFilters: []config.RegexFilter{{IsNot: ".*_seconds_total"}},
|
||||||
Name: NameMapping{Matches: "^(.*)_total$"},
|
Name: config.NameMapping{Matches: "^(.*)_total$"},
|
||||||
Resources: ResourceMapping{
|
Resources: config.ResourceMapping{
|
||||||
Template: fmt.Sprintf("%s<<.Resource>>", labelPrefix),
|
Template: fmt.Sprintf("%s<<.Resource>>", labelPrefix),
|
||||||
},
|
},
|
||||||
MetricsQuery: fmt.Sprintf("sum(rate(<<.Series>>{<<.LabelMatchers>>}[%s])) by (<<.GroupBy>>)", pmodel.Duration(rateInterval).String()),
|
MetricsQuery: fmt.Sprintf("sum(rate(<<.Series>>{<<.LabelMatchers>>}[%s])) by (<<.GroupBy>>)", pmodel.Duration(rateInterval).String()),
|
||||||
|
|
@ -83,20 +83,20 @@ func DefaultConfig(rateInterval time.Duration, labelPrefix string) *MetricsDisco
|
||||||
// seconds rate metrics
|
// seconds rate metrics
|
||||||
{
|
{
|
||||||
SeriesQuery: string(prom.MatchSeries("", prom.LabelNeq(fmt.Sprintf("%snamespace", labelPrefix), ""), prom.NameNotMatches("^container_.*"))),
|
SeriesQuery: string(prom.MatchSeries("", prom.LabelNeq(fmt.Sprintf("%snamespace", labelPrefix), ""), prom.NameNotMatches("^container_.*"))),
|
||||||
Name: NameMapping{Matches: "^(.*)_seconds_total$"},
|
Name: config.NameMapping{Matches: "^(.*)_seconds_total$"},
|
||||||
Resources: ResourceMapping{
|
Resources: config.ResourceMapping{
|
||||||
Template: fmt.Sprintf("%s<<.Resource>>", labelPrefix),
|
Template: fmt.Sprintf("%s<<.Resource>>", labelPrefix),
|
||||||
},
|
},
|
||||||
MetricsQuery: fmt.Sprintf("sum(rate(<<.Series>>{<<.LabelMatchers>>}[%s])) by (<<.GroupBy>>)", pmodel.Duration(rateInterval).String()),
|
MetricsQuery: fmt.Sprintf("sum(rate(<<.Series>>{<<.LabelMatchers>>}[%s])) by (<<.GroupBy>>)", pmodel.Duration(rateInterval).String()),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
ResourceRules: &ResourceRules{
|
ResourceRules: &config.ResourceRules{
|
||||||
CPU: ResourceRule{
|
CPU: config.ResourceRule{
|
||||||
ContainerQuery: fmt.Sprintf("sum(rate(container_cpu_usage_seconds_total{<<.LabelMatchers>>}[%s])) by (<<.GroupBy>>)", pmodel.Duration(rateInterval).String()),
|
ContainerQuery: fmt.Sprintf("sum(rate(container_cpu_usage_seconds_total{<<.LabelMatchers>>}[%s])) by (<<.GroupBy>>)", pmodel.Duration(rateInterval).String()),
|
||||||
NodeQuery: fmt.Sprintf("sum(rate(container_cpu_usage_seconds_total{<<.LabelMatchers>>, id='/'}[%s])) by (<<.GroupBy>>)", pmodel.Duration(rateInterval).String()),
|
NodeQuery: fmt.Sprintf("sum(rate(container_cpu_usage_seconds_total{<<.LabelMatchers>>, id='/'}[%s])) by (<<.GroupBy>>)", pmodel.Duration(rateInterval).String()),
|
||||||
Resources: ResourceMapping{
|
Resources: config.ResourceMapping{
|
||||||
Overrides: map[string]GroupResource{
|
Overrides: map[string]config.GroupResource{
|
||||||
"namespace": {Resource: "namespace"},
|
"namespace": {Resource: "namespace"},
|
||||||
"pod": {Resource: "pod"},
|
"pod": {Resource: "pod"},
|
||||||
"instance": {Resource: "node"},
|
"instance": {Resource: "node"},
|
||||||
|
|
@ -104,11 +104,11 @@ func DefaultConfig(rateInterval time.Duration, labelPrefix string) *MetricsDisco
|
||||||
},
|
},
|
||||||
ContainerLabel: fmt.Sprintf("%scontainer", labelPrefix),
|
ContainerLabel: fmt.Sprintf("%scontainer", labelPrefix),
|
||||||
},
|
},
|
||||||
Memory: ResourceRule{
|
Memory: config.ResourceRule{
|
||||||
ContainerQuery: "sum(container_memory_working_set_bytes{<<.LabelMatchers>>}) by (<<.GroupBy>>)",
|
ContainerQuery: "sum(container_memory_working_set_bytes{<<.LabelMatchers>>}) by (<<.GroupBy>>)",
|
||||||
NodeQuery: "sum(container_memory_working_set_bytes{<<.LabelMatchers>>,id='/'}) by (<<.GroupBy>>)",
|
NodeQuery: "sum(container_memory_working_set_bytes{<<.LabelMatchers>>,id='/'}) by (<<.GroupBy>>)",
|
||||||
Resources: ResourceMapping{
|
Resources: config.ResourceMapping{
|
||||||
Overrides: map[string]GroupResource{
|
Overrides: map[string]config.GroupResource{
|
||||||
"namespace": {Resource: "namespace"},
|
"namespace": {Resource: "namespace"},
|
||||||
"pod": {Resource: "pod"},
|
"pod": {Resource: "pod"},
|
||||||
"instance": {Resource: "node"},
|
"instance": {Resource: "node"},
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,11 @@
|
||||||
Example Deployment
|
Example Deployment
|
||||||
==================
|
==================
|
||||||
|
|
||||||
1. Make sure you've built the included Dockerfile with `TAG=latest make container`. The image should be tagged as `gcr.io/k8s-staging-prometheus-adapter:latest`.
|
1. Make sure you've built the included Dockerfile with `TAG=latest make container`. The image should be tagged as `registry.k8s.io/prometheus-adapter/staging-prometheus-adapter:latest`.
|
||||||
|
|
||||||
2. Create a secret called `cm-adapter-serving-certs` with two values:
|
2. `kubectl create namespace monitoring` to ensure that the namespace that we're installing
|
||||||
`serving.crt` and `serving.key`. These are the serving certificates used
|
|
||||||
by the adapter for serving HTTPS traffic. For more information on how to
|
|
||||||
generate these certificates, see the [auth concepts
|
|
||||||
documentation](https://github.com/kubernetes-incubator/apiserver-builder/blob/master/docs/concepts/auth.md)
|
|
||||||
in the apiserver-builder repository.
|
|
||||||
The kube-prometheus project published two scripts [gencerts.sh](https://github.com/prometheus-operator/kube-prometheus/blob/62fff622e9900fade8aecbd02bc9c557b736ef85/experimental/custom-metrics-api/gencerts.sh)
|
|
||||||
and [deploy.sh](https://github.com/prometheus-operator/kube-prometheus/blob/62fff622e9900fade8aecbd02bc9c557b736ef85/experimental/custom-metrics-api/deploy.sh) to create the `cm-adapter-serving-certs` secret.
|
|
||||||
|
|
||||||
3. `kubectl create namespace custom-metrics` to ensure that the namespace that we're installing
|
|
||||||
the custom metrics adapter in exists.
|
the custom metrics adapter in exists.
|
||||||
|
|
||||||
4. `kubectl create -f manifests/`, modifying the Deployment as necessary to
|
3. `kubectl create -f manifests/`, modifying the Deployment as necessary to
|
||||||
point to your Prometheus server, and the ConfigMap to contain your desired
|
point to your Prometheus server, and the ConfigMap to contain your desired
|
||||||
metrics discovery configuration.
|
metrics discovery configuration.
|
||||||
|
|
|
||||||
17
deploy/manifests/api-service.yaml
Normal file
17
deploy/manifests/api-service.yaml
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
apiVersion: apiregistration.k8s.io/v1
|
||||||
|
kind: APIService
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: metrics-adapter
|
||||||
|
app.kubernetes.io/name: prometheus-adapter
|
||||||
|
app.kubernetes.io/version: 0.12.0
|
||||||
|
name: v1beta1.metrics.k8s.io
|
||||||
|
spec:
|
||||||
|
group: metrics.k8s.io
|
||||||
|
groupPriorityMinimum: 100
|
||||||
|
insecureSkipTLSVerify: true
|
||||||
|
service:
|
||||||
|
name: prometheus-adapter
|
||||||
|
namespace: monitoring
|
||||||
|
version: v1beta1
|
||||||
|
versionPriority: 100
|
||||||
22
deploy/manifests/cluster-role-aggregated-metrics-reader.yaml
Normal file
22
deploy/manifests/cluster-role-aggregated-metrics-reader.yaml
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: metrics-adapter
|
||||||
|
app.kubernetes.io/name: prometheus-adapter
|
||||||
|
app.kubernetes.io/version: 0.12.0
|
||||||
|
rbac.authorization.k8s.io/aggregate-to-admin: "true"
|
||||||
|
rbac.authorization.k8s.io/aggregate-to-edit: "true"
|
||||||
|
rbac.authorization.k8s.io/aggregate-to-view: "true"
|
||||||
|
name: system:aggregated-metrics-reader
|
||||||
|
namespace: monitoring
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- metrics.k8s.io
|
||||||
|
resources:
|
||||||
|
- pods
|
||||||
|
- nodes
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
17
deploy/manifests/cluster-role-binding-delegator.yaml
Normal file
17
deploy/manifests/cluster-role-binding-delegator.yaml
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: metrics-adapter
|
||||||
|
app.kubernetes.io/name: prometheus-adapter
|
||||||
|
app.kubernetes.io/version: 0.12.0
|
||||||
|
name: resource-metrics:system:auth-delegator
|
||||||
|
namespace: monitoring
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: ClusterRole
|
||||||
|
name: system:auth-delegator
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: prometheus-adapter
|
||||||
|
namespace: monitoring
|
||||||
|
|
@ -2,6 +2,9 @@ apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
name: hpa-controller-custom-metrics
|
name: hpa-controller-custom-metrics
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: metrics-adapter
|
||||||
|
app.kubernetes.io/name: prometheus-adapter
|
||||||
roleRef:
|
roleRef:
|
||||||
apiGroup: rbac.authorization.k8s.io
|
apiGroup: rbac.authorization.k8s.io
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
17
deploy/manifests/cluster-role-binding.yaml
Normal file
17
deploy/manifests/cluster-role-binding.yaml
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: metrics-adapter
|
||||||
|
app.kubernetes.io/name: prometheus-adapter
|
||||||
|
app.kubernetes.io/version: 0.12.0
|
||||||
|
name: prometheus-adapter
|
||||||
|
namespace: monitoring
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: ClusterRole
|
||||||
|
name: prometheus-adapter
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: prometheus-adapter
|
||||||
|
namespace: monitoring
|
||||||
15
deploy/manifests/cluster-role-metrics-server-resources.yaml
Normal file
15
deploy/manifests/cluster-role-metrics-server-resources.yaml
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: metrics-adapter
|
||||||
|
app.kubernetes.io/name: prometheus-adapter
|
||||||
|
app.kubernetes.io/version: 0.12.0
|
||||||
|
name: resource-metrics-server-resources
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- metrics.k8s.io
|
||||||
|
resources:
|
||||||
|
- '*'
|
||||||
|
verbs:
|
||||||
|
- '*'
|
||||||
20
deploy/manifests/cluster-role.yaml
Normal file
20
deploy/manifests/cluster-role.yaml
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: metrics-adapter
|
||||||
|
app.kubernetes.io/name: prometheus-adapter
|
||||||
|
app.kubernetes.io/version: 0.12.0
|
||||||
|
name: prometheus-adapter
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- nodes
|
||||||
|
- namespaces
|
||||||
|
- pods
|
||||||
|
- services
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
53
deploy/manifests/config-map.yaml
Normal file
53
deploy/manifests/config-map.yaml
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
config.yaml: |-
|
||||||
|
"resourceRules":
|
||||||
|
"cpu":
|
||||||
|
"containerLabel": "container"
|
||||||
|
"containerQuery": |
|
||||||
|
sum by (<<.GroupBy>>) (
|
||||||
|
irate (
|
||||||
|
container_cpu_usage_seconds_total{<<.LabelMatchers>>,container!="",pod!=""}[4m]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
"nodeQuery": |
|
||||||
|
sum by (<<.GroupBy>>) (
|
||||||
|
irate(
|
||||||
|
node_cpu_usage_seconds_total{<<.LabelMatchers>>}[4m]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
"resources":
|
||||||
|
"overrides":
|
||||||
|
"namespace":
|
||||||
|
"resource": "namespace"
|
||||||
|
"node":
|
||||||
|
"resource": "node"
|
||||||
|
"pod":
|
||||||
|
"resource": "pod"
|
||||||
|
"memory":
|
||||||
|
"containerLabel": "container"
|
||||||
|
"containerQuery": |
|
||||||
|
sum by (<<.GroupBy>>) (
|
||||||
|
container_memory_working_set_bytes{<<.LabelMatchers>>,container!="",pod!=""}
|
||||||
|
)
|
||||||
|
"nodeQuery": |
|
||||||
|
sum by (<<.GroupBy>>) (
|
||||||
|
node_memory_working_set_bytes{<<.LabelMatchers>>}
|
||||||
|
)
|
||||||
|
"resources":
|
||||||
|
"overrides":
|
||||||
|
"node":
|
||||||
|
"resource": "node"
|
||||||
|
"namespace":
|
||||||
|
"resource": "namespace"
|
||||||
|
"pod":
|
||||||
|
"resource": "pod"
|
||||||
|
"window": "5m"
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: metrics-adapter
|
||||||
|
app.kubernetes.io/name: prometheus-adapter
|
||||||
|
app.kubernetes.io/version: 0.12.0
|
||||||
|
name: adapter-config
|
||||||
|
namespace: monitoring
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: custom-metrics-apiserver
|
|
||||||
name: custom-metrics-apiserver
|
|
||||||
namespace: custom-metrics
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: custom-metrics-apiserver
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: custom-metrics-apiserver
|
|
||||||
name: custom-metrics-apiserver
|
|
||||||
spec:
|
|
||||||
serviceAccountName: custom-metrics-apiserver
|
|
||||||
containers:
|
|
||||||
- name: custom-metrics-apiserver
|
|
||||||
image: gcr.io/k8s-staging-prometheus-adapter-amd64
|
|
||||||
args:
|
|
||||||
- --secure-port=6443
|
|
||||||
- --tls-cert-file=/var/run/serving-cert/serving.crt
|
|
||||||
- --tls-private-key-file=/var/run/serving-cert/serving.key
|
|
||||||
- --logtostderr=true
|
|
||||||
- --prometheus-url=http://prometheus.prom.svc:9090/
|
|
||||||
- --metrics-relist-interval=1m
|
|
||||||
- --v=10
|
|
||||||
- --config=/etc/adapter/config.yaml
|
|
||||||
ports:
|
|
||||||
- containerPort: 6443
|
|
||||||
volumeMounts:
|
|
||||||
- mountPath: /var/run/serving-cert
|
|
||||||
name: volume-serving-cert
|
|
||||||
readOnly: true
|
|
||||||
- mountPath: /etc/adapter/
|
|
||||||
name: config
|
|
||||||
readOnly: true
|
|
||||||
- mountPath: /tmp
|
|
||||||
name: tmp-vol
|
|
||||||
volumes:
|
|
||||||
- name: volume-serving-cert
|
|
||||||
secret:
|
|
||||||
secretName: cm-adapter-serving-certs
|
|
||||||
- name: config
|
|
||||||
configMap:
|
|
||||||
name: adapter-config
|
|
||||||
- name: tmp-vol
|
|
||||||
emptyDir: {}
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
kind: ClusterRoleBinding
|
|
||||||
metadata:
|
|
||||||
name: custom-metrics-resource-reader
|
|
||||||
roleRef:
|
|
||||||
apiGroup: rbac.authorization.k8s.io
|
|
||||||
kind: ClusterRole
|
|
||||||
name: custom-metrics-resource-reader
|
|
||||||
subjects:
|
|
||||||
- kind: ServiceAccount
|
|
||||||
name: custom-metrics-apiserver
|
|
||||||
namespace: custom-metrics
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
kind: ServiceAccount
|
|
||||||
apiVersion: v1
|
|
||||||
metadata:
|
|
||||||
name: custom-metrics-apiserver
|
|
||||||
namespace: custom-metrics
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: custom-metrics-apiserver
|
|
||||||
namespace: custom-metrics
|
|
||||||
spec:
|
|
||||||
ports:
|
|
||||||
- port: 443
|
|
||||||
targetPort: 6443
|
|
||||||
selector:
|
|
||||||
app: custom-metrics-apiserver
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
apiVersion: apiregistration.k8s.io/v1
|
|
||||||
kind: APIService
|
|
||||||
metadata:
|
|
||||||
name: v1beta1.custom.metrics.k8s.io
|
|
||||||
spec:
|
|
||||||
service:
|
|
||||||
name: custom-metrics-apiserver
|
|
||||||
namespace: custom-metrics
|
|
||||||
group: custom.metrics.k8s.io
|
|
||||||
version: v1beta1
|
|
||||||
insecureSkipTLSVerify: true
|
|
||||||
groupPriorityMinimum: 100
|
|
||||||
versionPriority: 100
|
|
||||||
---
|
|
||||||
apiVersion: apiregistration.k8s.io/v1
|
|
||||||
kind: APIService
|
|
||||||
metadata:
|
|
||||||
name: v1beta2.custom.metrics.k8s.io
|
|
||||||
spec:
|
|
||||||
service:
|
|
||||||
name: custom-metrics-apiserver
|
|
||||||
namespace: custom-metrics
|
|
||||||
group: custom.metrics.k8s.io
|
|
||||||
version: v1beta2
|
|
||||||
insecureSkipTLSVerify: true
|
|
||||||
groupPriorityMinimum: 100
|
|
||||||
versionPriority: 200
|
|
||||||
---
|
|
||||||
apiVersion: apiregistration.k8s.io/v1
|
|
||||||
kind: APIService
|
|
||||||
metadata:
|
|
||||||
name: v1beta1.external.metrics.k8s.io
|
|
||||||
spec:
|
|
||||||
service:
|
|
||||||
name: custom-metrics-apiserver
|
|
||||||
namespace: custom-metrics
|
|
||||||
group: external.metrics.k8s.io
|
|
||||||
version: v1beta1
|
|
||||||
insecureSkipTLSVerify: true
|
|
||||||
groupPriorityMinimum: 100
|
|
||||||
versionPriority: 100
|
|
||||||
---
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
kind: ClusterRole
|
|
||||||
metadata:
|
|
||||||
name: custom-metrics-server-resources
|
|
||||||
rules:
|
|
||||||
- apiGroups:
|
|
||||||
- custom.metrics.k8s.io
|
|
||||||
- external.metrics.k8s.io
|
|
||||||
resources: ["*"]
|
|
||||||
verbs: ["*"]
|
|
||||||
|
|
@ -1,117 +0,0 @@
|
||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: adapter-config
|
|
||||||
namespace: custom-metrics
|
|
||||||
data:
|
|
||||||
config.yaml: |
|
|
||||||
rules:
|
|
||||||
- seriesQuery: '{__name__=~"^container_.*",container!="POD",namespace!="",pod!=""}'
|
|
||||||
seriesFilters: []
|
|
||||||
resources:
|
|
||||||
overrides:
|
|
||||||
namespace:
|
|
||||||
resource: namespace
|
|
||||||
pod:
|
|
||||||
resource: pod
|
|
||||||
name:
|
|
||||||
matches: ^container_(.*)_seconds_total$
|
|
||||||
as: ""
|
|
||||||
metricsQuery: sum(rate(<<.Series>>{<<.LabelMatchers>>,container!="POD"}[1m])) by (<<.GroupBy>>)
|
|
||||||
- seriesQuery: '{__name__=~"^container_.*",container!="POD",namespace!="",pod!=""}'
|
|
||||||
seriesFilters:
|
|
||||||
- isNot: ^container_.*_seconds_total$
|
|
||||||
resources:
|
|
||||||
overrides:
|
|
||||||
namespace:
|
|
||||||
resource: namespace
|
|
||||||
pod:
|
|
||||||
resource: pod
|
|
||||||
name:
|
|
||||||
matches: ^container_(.*)_total$
|
|
||||||
as: ""
|
|
||||||
metricsQuery: sum(rate(<<.Series>>{<<.LabelMatchers>>,container!="POD"}[1m])) by (<<.GroupBy>>)
|
|
||||||
- seriesQuery: '{__name__=~"^container_.*",container!="POD",namespace!="",pod!=""}'
|
|
||||||
seriesFilters:
|
|
||||||
- isNot: ^container_.*_total$
|
|
||||||
resources:
|
|
||||||
overrides:
|
|
||||||
namespace:
|
|
||||||
resource: namespace
|
|
||||||
pod:
|
|
||||||
resource: pod
|
|
||||||
name:
|
|
||||||
matches: ^container_(.*)$
|
|
||||||
as: ""
|
|
||||||
metricsQuery: sum(<<.Series>>{<<.LabelMatchers>>,container!="POD"}) by (<<.GroupBy>>)
|
|
||||||
- seriesQuery: '{namespace!="",__name__!~"^container_.*"}'
|
|
||||||
seriesFilters:
|
|
||||||
- isNot: .*_total$
|
|
||||||
resources:
|
|
||||||
template: <<.Resource>>
|
|
||||||
name:
|
|
||||||
matches: ""
|
|
||||||
as: ""
|
|
||||||
metricsQuery: sum(<<.Series>>{<<.LabelMatchers>>}) by (<<.GroupBy>>)
|
|
||||||
- seriesQuery: '{namespace!="",__name__!~"^container_.*"}'
|
|
||||||
seriesFilters:
|
|
||||||
- isNot: .*_seconds_total
|
|
||||||
resources:
|
|
||||||
template: <<.Resource>>
|
|
||||||
name:
|
|
||||||
matches: ^(.*)_total$
|
|
||||||
as: ""
|
|
||||||
metricsQuery: sum(rate(<<.Series>>{<<.LabelMatchers>>}[1m])) by (<<.GroupBy>>)
|
|
||||||
- seriesQuery: '{namespace!="",__name__!~"^container_.*"}'
|
|
||||||
seriesFilters: []
|
|
||||||
resources:
|
|
||||||
template: <<.Resource>>
|
|
||||||
name:
|
|
||||||
matches: ^(.*)_seconds_total$
|
|
||||||
as: ""
|
|
||||||
metricsQuery: sum(rate(<<.Series>>{<<.LabelMatchers>>}[1m])) by (<<.GroupBy>>)
|
|
||||||
resourceRules:
|
|
||||||
cpu:
|
|
||||||
containerQuery: sum(rate(container_cpu_usage_seconds_total{<<.LabelMatchers>>}[1m])) by (<<.GroupBy>>)
|
|
||||||
nodeQuery: sum(rate(container_cpu_usage_seconds_total{<<.LabelMatchers>>, id='/'}[1m])) by (<<.GroupBy>>)
|
|
||||||
resources:
|
|
||||||
overrides:
|
|
||||||
instance:
|
|
||||||
resource: node
|
|
||||||
namespace:
|
|
||||||
resource: namespace
|
|
||||||
pod:
|
|
||||||
resource: pod
|
|
||||||
containerLabel: container
|
|
||||||
memory:
|
|
||||||
containerQuery: sum(container_memory_working_set_bytes{<<.LabelMatchers>>}) by (<<.GroupBy>>)
|
|
||||||
nodeQuery: sum(container_memory_working_set_bytes{<<.LabelMatchers>>,id='/'}) by (<<.GroupBy>>)
|
|
||||||
resources:
|
|
||||||
overrides:
|
|
||||||
instance:
|
|
||||||
resource: node
|
|
||||||
namespace:
|
|
||||||
resource: namespace
|
|
||||||
pod:
|
|
||||||
resource: pod
|
|
||||||
containerLabel: container
|
|
||||||
window: 1m
|
|
||||||
externalRules:
|
|
||||||
- seriesQuery: '{__name__=~"^.*_queue_(length|size)$",namespace!=""}'
|
|
||||||
resources:
|
|
||||||
overrides:
|
|
||||||
namespace:
|
|
||||||
resource: namespace
|
|
||||||
name:
|
|
||||||
matches: ^.*_queue_(length|size)$
|
|
||||||
as: "$0"
|
|
||||||
metricsQuery: max(<<.Series>>{<<.LabelMatchers>>})
|
|
||||||
- seriesQuery: '{__name__=~"^.*_queue$",namespace!=""}'
|
|
||||||
resources:
|
|
||||||
overrides:
|
|
||||||
namespace:
|
|
||||||
resource: namespace
|
|
||||||
name:
|
|
||||||
matches: ^.*_queue$
|
|
||||||
as: "$0"
|
|
||||||
metricsQuery: max(<<.Series>>{<<.LabelMatchers>>})
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
kind: ClusterRole
|
|
||||||
metadata:
|
|
||||||
name: custom-metrics-resource-reader
|
|
||||||
rules:
|
|
||||||
- apiGroups:
|
|
||||||
- ""
|
|
||||||
resources:
|
|
||||||
- pods
|
|
||||||
- nodes
|
|
||||||
- nodes/stats
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- list
|
|
||||||
- watch
|
|
||||||
89
deploy/manifests/deployment.yaml
Normal file
89
deploy/manifests/deployment.yaml
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: metrics-adapter
|
||||||
|
app.kubernetes.io/name: prometheus-adapter
|
||||||
|
app.kubernetes.io/version: 0.12.0
|
||||||
|
name: prometheus-adapter
|
||||||
|
namespace: monitoring
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/component: metrics-adapter
|
||||||
|
app.kubernetes.io/name: prometheus-adapter
|
||||||
|
strategy:
|
||||||
|
rollingUpdate:
|
||||||
|
maxSurge: 1
|
||||||
|
maxUnavailable: 1
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: metrics-adapter
|
||||||
|
app.kubernetes.io/name: prometheus-adapter
|
||||||
|
app.kubernetes.io/version: 0.12.0
|
||||||
|
spec:
|
||||||
|
automountServiceAccountToken: true
|
||||||
|
containers:
|
||||||
|
- args:
|
||||||
|
- --cert-dir=/var/run/serving-cert
|
||||||
|
- --config=/etc/adapter/config.yaml
|
||||||
|
- --metrics-relist-interval=1m
|
||||||
|
- --prometheus-url=https://prometheus.monitoring.svc:9090/
|
||||||
|
- --secure-port=6443
|
||||||
|
- --tls-cipher-suites=TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA
|
||||||
|
image: registry.k8s.io/prometheus-adapter/prometheus-adapter:v0.12.0
|
||||||
|
livenessProbe:
|
||||||
|
failureThreshold: 5
|
||||||
|
httpGet:
|
||||||
|
path: /livez
|
||||||
|
port: https
|
||||||
|
scheme: HTTPS
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 5
|
||||||
|
name: prometheus-adapter
|
||||||
|
ports:
|
||||||
|
- containerPort: 6443
|
||||||
|
name: https
|
||||||
|
readinessProbe:
|
||||||
|
failureThreshold: 5
|
||||||
|
httpGet:
|
||||||
|
path: /readyz
|
||||||
|
port: https
|
||||||
|
scheme: HTTPS
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 5
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 102m
|
||||||
|
memory: 180Mi
|
||||||
|
securityContext:
|
||||||
|
allowPrivilegeEscalation: false
|
||||||
|
capabilities:
|
||||||
|
drop:
|
||||||
|
- ALL
|
||||||
|
readOnlyRootFilesystem: true
|
||||||
|
terminationMessagePolicy: FallbackToLogsOnError
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /tmp
|
||||||
|
name: tmpfs
|
||||||
|
readOnly: false
|
||||||
|
- mountPath: /var/run/serving-cert
|
||||||
|
name: volume-serving-cert
|
||||||
|
readOnly: false
|
||||||
|
- mountPath: /etc/adapter
|
||||||
|
name: config
|
||||||
|
readOnly: false
|
||||||
|
nodeSelector:
|
||||||
|
kubernetes.io/os: linux
|
||||||
|
securityContext: {}
|
||||||
|
serviceAccountName: prometheus-adapter
|
||||||
|
volumes:
|
||||||
|
- emptyDir: {}
|
||||||
|
name: tmpfs
|
||||||
|
- emptyDir: {}
|
||||||
|
name: volume-serving-cert
|
||||||
|
- configMap:
|
||||||
|
name: adapter-config
|
||||||
|
name: config
|
||||||
21
deploy/manifests/network-policy.yaml
Normal file
21
deploy/manifests/network-policy.yaml
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: NetworkPolicy
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: metrics-adapter
|
||||||
|
app.kubernetes.io/name: prometheus-adapter
|
||||||
|
app.kubernetes.io/version: 0.12.0
|
||||||
|
name: prometheus-adapter
|
||||||
|
namespace: monitoring
|
||||||
|
spec:
|
||||||
|
egress:
|
||||||
|
- {}
|
||||||
|
ingress:
|
||||||
|
- {}
|
||||||
|
podSelector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/component: metrics-adapter
|
||||||
|
app.kubernetes.io/name: prometheus-adapter
|
||||||
|
policyTypes:
|
||||||
|
- Egress
|
||||||
|
- Ingress
|
||||||
15
deploy/manifests/pod-disruption-budget.yaml
Normal file
15
deploy/manifests/pod-disruption-budget.yaml
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
apiVersion: policy/v1
|
||||||
|
kind: PodDisruptionBudget
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: metrics-adapter
|
||||||
|
app.kubernetes.io/name: prometheus-adapter
|
||||||
|
app.kubernetes.io/version: 0.12.0
|
||||||
|
name: prometheus-adapter
|
||||||
|
namespace: monitoring
|
||||||
|
spec:
|
||||||
|
minAvailable: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/component: metrics-adapter
|
||||||
|
app.kubernetes.io/name: prometheus-adapter
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: RoleBinding
|
kind: RoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
name: custom-metrics-auth-reader
|
labels:
|
||||||
|
app.kubernetes.io/component: metrics-adapter
|
||||||
|
app.kubernetes.io/name: prometheus-adapter
|
||||||
|
app.kubernetes.io/version: 0.12.0
|
||||||
|
name: resource-metrics-auth-reader
|
||||||
namespace: kube-system
|
namespace: kube-system
|
||||||
roleRef:
|
roleRef:
|
||||||
apiGroup: rbac.authorization.k8s.io
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
|
@ -9,5 +13,5 @@ roleRef:
|
||||||
name: extension-apiserver-authentication-reader
|
name: extension-apiserver-authentication-reader
|
||||||
subjects:
|
subjects:
|
||||||
- kind: ServiceAccount
|
- kind: ServiceAccount
|
||||||
name: custom-metrics-apiserver
|
name: prometheus-adapter
|
||||||
namespace: custom-metrics
|
namespace: monitoring
|
||||||
10
deploy/manifests/service-account.yaml
Normal file
10
deploy/manifests/service-account.yaml
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
apiVersion: v1
|
||||||
|
automountServiceAccountToken: false
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: metrics-adapter
|
||||||
|
app.kubernetes.io/name: prometheus-adapter
|
||||||
|
app.kubernetes.io/version: 0.12.0
|
||||||
|
name: prometheus-adapter
|
||||||
|
namespace: monitoring
|
||||||
17
deploy/manifests/service.yaml
Normal file
17
deploy/manifests/service.yaml
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: metrics-adapter
|
||||||
|
app.kubernetes.io/name: prometheus-adapter
|
||||||
|
app.kubernetes.io/version: 0.12.0
|
||||||
|
name: prometheus-adapter
|
||||||
|
namespace: monitoring
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- name: https
|
||||||
|
port: 443
|
||||||
|
targetPort: 6443
|
||||||
|
selector:
|
||||||
|
app.kubernetes.io/component: metrics-adapter
|
||||||
|
app.kubernetes.io/name: prometheus-adapter
|
||||||
|
|
@ -36,8 +36,8 @@ rules:
|
||||||
# skip specifying generic resource<->label mappings, and just
|
# skip specifying generic resource<->label mappings, and just
|
||||||
# attach only pod and namespace resources by mapping label names to group-resources
|
# attach only pod and namespace resources by mapping label names to group-resources
|
||||||
overrides:
|
overrides:
|
||||||
namespace: {resource: "namespace"},
|
namespace: {resource: "namespace"}
|
||||||
pod: {resource: "pod"},
|
pod: {resource: "pod"}
|
||||||
# specify that the `container_` and `_seconds_total` suffixes should be removed.
|
# specify that the `container_` and `_seconds_total` suffixes should be removed.
|
||||||
# this also introduces an implicit filter on metric family names
|
# this also introduces an implicit filter on metric family names
|
||||||
name:
|
name:
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@ rules:
|
||||||
# skip specifying generic resource<->label mappings, and just
|
# skip specifying generic resource<->label mappings, and just
|
||||||
# attach only pod and namespace resources by mapping label names to group-resources
|
# attach only pod and namespace resources by mapping label names to group-resources
|
||||||
overrides:
|
overrides:
|
||||||
namespace: {resource: "namespace"},
|
namespace: {resource: "namespace"}
|
||||||
pod: {resource: "pod"},
|
pod: {resource: "pod"}
|
||||||
# specify that the `container_` and `_seconds_total` suffixes should be removed.
|
# specify that the `container_` and `_seconds_total` suffixes should be removed.
|
||||||
# this also introduces an implicit filter on metric family names
|
# this also introduces an implicit filter on metric family names
|
||||||
name:
|
name:
|
||||||
|
|
@ -33,8 +33,8 @@ rules:
|
||||||
- seriesQuery: '{__name__=~"^container_.*_total",container!="POD",namespace!="",pod!=""}'
|
- seriesQuery: '{__name__=~"^container_.*_total",container!="POD",namespace!="",pod!=""}'
|
||||||
resources:
|
resources:
|
||||||
overrides:
|
overrides:
|
||||||
namespace: {resource: "namespace"},
|
namespace: {resource: "namespace"}
|
||||||
pod: {resource: "pod"},
|
pod: {resource: "pod"}
|
||||||
seriesFilters:
|
seriesFilters:
|
||||||
# since this is a superset of the query above, we introduce an additional filter here
|
# since this is a superset of the query above, we introduce an additional filter here
|
||||||
- isNot: "^container_.*_seconds_total$"
|
- isNot: "^container_.*_seconds_total$"
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,8 @@ instance, if you're on an x86_64 machine, use
|
||||||
`gcr.io/k8s-staging-prometheus-adapter/prometheus-adapter-amd64` image.
|
`gcr.io/k8s-staging-prometheus-adapter/prometheus-adapter-amd64` image.
|
||||||
|
|
||||||
There is also an official multi arch image available at
|
There is also an official multi arch image available at
|
||||||
`k8s.gcr.io/prometheus-adapter/prometheus-adapter:${VERSION}`.
|
`registry.k8s.io/prometheus-adapter/prometheus-adapter:${VERSION}`.
|
||||||
|
|
||||||
|
|
||||||
If you're feeling adventurous, you can build the latest version of
|
If you're feeling adventurous, you can build the latest version of
|
||||||
prometheus-adapter by running `make container` or get the latest image from the
|
prometheus-adapter by running `make container` or get the latest image from the
|
||||||
|
|
@ -141,11 +142,11 @@ a HorizontalPodAutoscaler like this to accomplish the autoscaling:
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
|
||||||
<summary>sample-app-hpa.yaml</summary>
|
<summary>sample-app.hpa.yaml</summary>
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
kind: HorizontalPodAutoscaler
|
kind: HorizontalPodAutoscaler
|
||||||
apiVersion: autoscaling/v2beta1
|
apiVersion: autoscaling/v2
|
||||||
metadata:
|
metadata:
|
||||||
name: sample-app
|
name: sample-app
|
||||||
spec:
|
spec:
|
||||||
|
|
@ -164,10 +165,13 @@ spec:
|
||||||
- type: Pods
|
- type: Pods
|
||||||
pods:
|
pods:
|
||||||
# use the metric that you used above: pods/http_requests
|
# use the metric that you used above: pods/http_requests
|
||||||
metricName: http_requests
|
metric:
|
||||||
|
name: http_requests
|
||||||
# target 500 milli-requests per second,
|
# target 500 milli-requests per second,
|
||||||
# which is 1 request every two seconds
|
# which is 1 request every two seconds
|
||||||
targetAverageValue: 500m
|
target:
|
||||||
|
type: Value
|
||||||
|
averageValue: 500m
|
||||||
```
|
```
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
@ -175,7 +179,7 @@ spec:
|
||||||
If you try creating that now (and take a look at your controller-manager
|
If you try creating that now (and take a look at your controller-manager
|
||||||
logs), you'll see that the that the HorizontalPodAutoscaler controller is
|
logs), you'll see that the that the HorizontalPodAutoscaler controller is
|
||||||
attempting to fetch metrics from
|
attempting to fetch metrics from
|
||||||
`/apis/custom.metrics.k8s.io/v1beta1/namespaces/default/pods/*/http_requests?selector=app%3Dsample-app`,
|
`/apis/custom.metrics.k8s.io/v1beta2/namespaces/default/pods/*/http_requests?selector=app%3Dsample-app`,
|
||||||
but right now, nothing's serving that API.
|
but right now, nothing's serving that API.
|
||||||
|
|
||||||
Before you can autoscale your application, you'll need to make sure that
|
Before you can autoscale your application, you'll need to make sure that
|
||||||
|
|
@ -196,11 +200,11 @@ First, you'll need to deploy the Prometheus Operator. Check out the
|
||||||
guide](https://github.com/prometheus-operator/prometheus-operator#quickstart)
|
guide](https://github.com/prometheus-operator/prometheus-operator#quickstart)
|
||||||
for the Operator to deploy a copy of Prometheus.
|
for the Operator to deploy a copy of Prometheus.
|
||||||
|
|
||||||
This walkthrough assumes that Prometheus is deployed in the `prom`
|
This walkthrough assumes that Prometheus is deployed in the `monitoring`
|
||||||
namespace. Most of the sample commands and files are namespace-agnostic,
|
namespace. Most of the sample commands and files are namespace-agnostic,
|
||||||
but there are a few commands or pieces of configuration that rely on that
|
but there are a few commands or pieces of configuration that rely on that
|
||||||
namespace. If you're using a different namespace, simply substitute that
|
namespace. If you're using a different namespace, simply substitute that
|
||||||
in for `prom` when it appears.
|
in for `monitoring` when it appears.
|
||||||
|
|
||||||
### Monitoring Your Application
|
### Monitoring Your Application
|
||||||
|
|
||||||
|
|
@ -212,7 +216,7 @@ service:
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
|
||||||
<summary>service-monitor.yaml</summary>
|
<summary>sample-app.monitor.yaml</summary>
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
kind: ServiceMonitor
|
kind: ServiceMonitor
|
||||||
|
|
@ -232,12 +236,12 @@ spec:
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ kubectl create -f service-monitor.yaml
|
$ kubectl create -f sample-app.monitor.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
Now, you should see your metrics appear in your Prometheus instance. Look
|
Now, you should see your metrics (`http_requests_total`) appear in your Prometheus instance. Look
|
||||||
them up via the dashboard, and make sure they have the `namespace` and
|
them up via the dashboard, and make sure they have the `namespace` and
|
||||||
`pod` labels.
|
`pod` labels. If not, check the labels on the service monitor match the ones on the Prometheus CRD.
|
||||||
|
|
||||||
### Launching the Adapter
|
### Launching the Adapter
|
||||||
|
|
||||||
|
|
@ -255,7 +259,46 @@ the steps to deploy the adapter. Note that if you're deploying on
|
||||||
a non-x86_64 (amd64) platform, you'll need to change the `image` field in
|
a non-x86_64 (amd64) platform, you'll need to change the `image` field in
|
||||||
the Deployment to be the appropriate image for your platform.
|
the Deployment to be the appropriate image for your platform.
|
||||||
|
|
||||||
The default adapter configuration should work for this walkthrough and
|
However an update to the adapter config is necessary in order to
|
||||||
|
expose custom metrics.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
|
||||||
|
<summary>prom-adapter.config.yaml</summary>
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: adapter-config
|
||||||
|
namespace: monitoring
|
||||||
|
data:
|
||||||
|
config.yaml: |-
|
||||||
|
"rules":
|
||||||
|
- "seriesQuery": |
|
||||||
|
{namespace!="",__name__!~"^container_.*"}
|
||||||
|
"resources":
|
||||||
|
"template": "<<.Resource>>"
|
||||||
|
"name":
|
||||||
|
"matches": "^(.*)_total"
|
||||||
|
"as": ""
|
||||||
|
"metricsQuery": |
|
||||||
|
sum by (<<.GroupBy>>) (
|
||||||
|
irate (
|
||||||
|
<<.Series>>{<<.LabelMatchers>>}[1m]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ kubectl apply -f prom-adapter.config.yaml
|
||||||
|
# Restart prom-adapter pods
|
||||||
|
$ kubectl rollout restart deployment prometheus-adapter -n monitoring
|
||||||
|
```
|
||||||
|
|
||||||
|
This adapter configuration should work for this walkthrough together with
|
||||||
a standard Prometheus Operator configuration, but if you've got custom
|
a standard Prometheus Operator configuration, but if you've got custom
|
||||||
relabelling rules, or your labels above weren't exactly `namespace` and
|
relabelling rules, or your labels above weren't exactly `namespace` and
|
||||||
`pod`, you may need to edit the configuration in the ConfigMap. The
|
`pod`, you may need to edit the configuration in the ConfigMap. The
|
||||||
|
|
@ -264,11 +307,36 @@ overview of how configuration works.
|
||||||
|
|
||||||
### The Registered API
|
### The Registered API
|
||||||
|
|
||||||
As part of the creation of the adapter Deployment and associated objects
|
We also need to register the custom metrics API with the API aggregator (part of
|
||||||
(performed above), we registered the API with the API aggregator (part of
|
the main Kubernetes API server). For that we need to create an APIService resource
|
||||||
the main Kubernetes API server).
|
|
||||||
|
|
||||||
The API is registered as `custom.metrics.k8s.io/v1beta1`, and you can find
|
<details>
|
||||||
|
|
||||||
|
<summary>api-service.yaml</summary>
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: apiregistration.k8s.io/v1
|
||||||
|
kind: APIService
|
||||||
|
metadata:
|
||||||
|
name: v1beta2.custom.metrics.k8s.io
|
||||||
|
spec:
|
||||||
|
group: custom.metrics.k8s.io
|
||||||
|
groupPriorityMinimum: 100
|
||||||
|
insecureSkipTLSVerify: true
|
||||||
|
service:
|
||||||
|
name: prometheus-adapter
|
||||||
|
namespace: monitoring
|
||||||
|
version: v1beta2
|
||||||
|
versionPriority: 100
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ kubectl create -f api-service.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
The API is registered as `custom.metrics.k8s.io/v1beta2`, and you can find
|
||||||
more information about aggregation at [Concepts:
|
more information about aggregation at [Concepts:
|
||||||
Aggregation](https://github.com/kubernetes-incubator/apiserver-builder/blob/master/docs/concepts/aggregation.md).
|
Aggregation](https://github.com/kubernetes-incubator/apiserver-builder/blob/master/docs/concepts/aggregation.md).
|
||||||
|
|
||||||
|
|
@ -279,7 +347,7 @@ With that all set, your custom metrics API should show up in discovery.
|
||||||
Try fetching the discovery information for it:
|
Try fetching the discovery information for it:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1
|
$ kubectl get --raw /apis/custom.metrics.k8s.io/v1beta2
|
||||||
```
|
```
|
||||||
|
|
||||||
Since you've set up Prometheus to collect your app's metrics, you should
|
Since you've set up Prometheus to collect your app's metrics, you should
|
||||||
|
|
@ -293,12 +361,12 @@ sends a raw GET request to the Kubernetes API server, automatically
|
||||||
injecting auth information:
|
injecting auth information:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/default/pods/*/http_requests?selector=app%3Dsample-app"
|
$ kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta2/namespaces/default/pods/*/http_requests?selector=app%3Dsample-app"
|
||||||
```
|
```
|
||||||
|
|
||||||
Because of the adapter's configuration, the cumulative metric
|
Because of the adapter's configuration, the cumulative metric
|
||||||
`http_requests_total` has been converted into a rate metric,
|
`http_requests_total` has been converted into a rate metric,
|
||||||
`pods/http_requests`, which measures requests per second over a 2 minute
|
`pods/http_requests`, which measures requests per second over a 1 minute
|
||||||
interval. The value should currently be close to zero, since there's no
|
interval. The value should currently be close to zero, since there's no
|
||||||
traffic to your app, except for the regular metrics collection from
|
traffic to your app, except for the regular metrics collection from
|
||||||
Prometheus.
|
Prometheus.
|
||||||
|
|
@ -349,7 +417,7 @@ and make decisions based on it.
|
||||||
If you didn't create the HorizontalPodAutoscaler above, create it now:
|
If you didn't create the HorizontalPodAutoscaler above, create it now:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ kubectl create -f sample-app-hpa.yaml
|
$ kubectl create -f sample-app.hpa.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
Wait a little bit, and then examine the HPA:
|
Wait a little bit, and then examine the HPA:
|
||||||
|
|
@ -395,4 +463,4 @@ setting different labels or using the `Object` metric source type.
|
||||||
|
|
||||||
For more information on how metrics are exposed by the Prometheus adapter,
|
For more information on how metrics are exposed by the Prometheus adapter,
|
||||||
see [config documentation](/docs/config.md), and check the [default
|
see [config documentation](/docs/config.md), and check the [default
|
||||||
configuration](/deploy/manifests/custom-metrics-config-map.yaml).
|
configuration](/deploy/manifests/config-map.yaml).
|
||||||
|
|
|
||||||
172
go.mod
172
go.mod
|
|
@ -1,110 +1,118 @@
|
||||||
module sigs.k8s.io/prometheus-adapter
|
module sigs.k8s.io/prometheus-adapter
|
||||||
|
|
||||||
go 1.18
|
go 1.22.1
|
||||||
|
|
||||||
|
toolchain go1.22.2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/onsi/ginkgo v1.16.5
|
github.com/onsi/ginkgo v1.16.5
|
||||||
github.com/onsi/gomega v1.20.0
|
github.com/onsi/gomega v1.33.1
|
||||||
github.com/prometheus/client_golang v1.12.1
|
github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.73.2
|
||||||
github.com/prometheus/common v0.32.1
|
github.com/prometheus-operator/prometheus-operator/pkg/client v0.73.2
|
||||||
github.com/spf13/cobra v1.5.0
|
github.com/prometheus/client_golang v1.18.0
|
||||||
github.com/stretchr/testify v1.8.0
|
github.com/prometheus/common v0.46.0
|
||||||
|
github.com/spf13/cobra v1.8.0
|
||||||
|
github.com/stretchr/testify v1.9.0
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
k8s.io/api v0.24.3
|
k8s.io/api v0.30.0
|
||||||
k8s.io/apimachinery v0.24.3
|
k8s.io/apimachinery v0.30.0
|
||||||
k8s.io/apiserver v0.24.3
|
k8s.io/apiserver v0.30.0
|
||||||
k8s.io/client-go v0.24.3
|
k8s.io/client-go v0.30.0
|
||||||
k8s.io/component-base v0.24.3
|
k8s.io/component-base v0.30.0
|
||||||
k8s.io/klog/v2 v2.70.1
|
k8s.io/klog/v2 v2.120.1
|
||||||
k8s.io/kube-openapi v0.0.0-20220413171646-5e7f5fdc6da6
|
k8s.io/kube-openapi v0.0.0-20240430033511-f0e62f92d13f
|
||||||
k8s.io/metrics v0.24.3
|
k8s.io/metrics v0.30.0
|
||||||
sigs.k8s.io/custom-metrics-apiserver v1.24.0
|
sigs.k8s.io/custom-metrics-apiserver v1.30.0
|
||||||
sigs.k8s.io/metrics-server v0.6.1
|
sigs.k8s.io/metrics-server v0.7.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/NYTimes/gziphandler v1.1.1 // indirect
|
github.com/NYTimes/gziphandler v1.1.1 // indirect
|
||||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df // indirect
|
||||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||||
github.com/coreos/go-semver v0.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
|
github.com/coreos/go-semver v0.3.1 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||||
github.com/emicklei/go-restful v2.16.0+incompatible // indirect
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
github.com/emicklei/go-restful-swagger12 v0.0.0-20201014110547-68ccff494617 // indirect
|
github.com/emicklei/go-restful/v3 v3.12.0 // indirect
|
||||||
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
|
github.com/evanphx/json-patch v5.9.0+incompatible // indirect
|
||||||
github.com/felixge/httpsnoop v1.0.1 // indirect
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||||
github.com/go-logr/logr v1.2.0 // indirect
|
github.com/go-logr/logr v1.4.1 // indirect
|
||||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-openapi/jsonreference v0.19.6 // indirect
|
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||||
github.com/go-openapi/swag v0.19.15 // indirect
|
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||||
|
github.com/go-openapi/swag v0.23.0 // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
github.com/golang/protobuf v1.5.2 // indirect
|
github.com/golang/protobuf v1.5.4 // indirect
|
||||||
github.com/google/gnostic v0.6.9 // indirect
|
github.com/google/cel-go v0.17.8 // indirect
|
||||||
github.com/google/go-cmp v0.5.8 // indirect
|
github.com/google/gnostic-models v0.6.8 // indirect
|
||||||
github.com/google/gofuzz v1.1.0 // indirect
|
github.com/google/go-cmp v0.6.0 // indirect
|
||||||
github.com/google/uuid v1.1.2 // indirect
|
github.com/google/gofuzz v1.2.0 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
|
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1 // indirect
|
||||||
github.com/imdario/mergo v0.3.5 // indirect
|
github.com/imdario/mergo v0.3.16 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/mailru/easyjson v0.7.7 // indirect
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/nxadm/tail v1.4.8 // indirect
|
github.com/nxadm/tail v1.4.8 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
github.com/prometheus/client_model v0.2.0 // indirect
|
github.com/prometheus/client_model v0.5.0 // indirect
|
||||||
github.com/prometheus/procfs v0.7.3 // indirect
|
github.com/prometheus/procfs v0.12.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
go.etcd.io/etcd/api/v3 v3.5.1 // indirect
|
github.com/stoewer/go-strcase v1.3.0 // indirect
|
||||||
go.etcd.io/etcd/client/pkg/v3 v3.5.1 // indirect
|
go.etcd.io/etcd/api/v3 v3.5.11 // indirect
|
||||||
go.etcd.io/etcd/client/v3 v3.5.1 // indirect
|
go.etcd.io/etcd/client/pkg/v3 v3.5.11 // indirect
|
||||||
go.opentelemetry.io/contrib v0.20.0 // indirect
|
go.etcd.io/etcd/client/v3 v3.5.11 // indirect
|
||||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0 // indirect
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 // indirect
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0 // indirect
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect
|
||||||
go.opentelemetry.io/otel v0.20.0 // indirect
|
go.opentelemetry.io/otel v1.21.0 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/otlp v0.20.0 // indirect
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect
|
||||||
go.opentelemetry.io/otel/metric v0.20.0 // indirect
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 // indirect
|
||||||
go.opentelemetry.io/otel/sdk v0.20.0 // indirect
|
go.opentelemetry.io/otel/metric v1.21.0 // indirect
|
||||||
go.opentelemetry.io/otel/sdk/export/metric v0.20.0 // indirect
|
go.opentelemetry.io/otel/sdk v1.21.0 // indirect
|
||||||
go.opentelemetry.io/otel/sdk/metric v0.20.0 // indirect
|
go.opentelemetry.io/otel/trace v1.21.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace v0.20.0 // indirect
|
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
|
||||||
go.opentelemetry.io/proto/otlp v0.7.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.uber.org/atomic v1.7.0 // indirect
|
go.uber.org/zap v1.26.0 // indirect
|
||||||
go.uber.org/multierr v1.6.0 // indirect
|
golang.org/x/crypto v0.31.0 // indirect
|
||||||
go.uber.org/zap v1.19.0 // indirect
|
golang.org/x/exp v0.0.0-20231226003508-02704c960a9b // indirect
|
||||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 // indirect
|
golang.org/x/mod v0.17.0 // indirect
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
|
golang.org/x/net v0.25.0 // indirect
|
||||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 // indirect
|
golang.org/x/oauth2 v0.18.0 // indirect
|
||||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
|
golang.org/x/sync v0.10.0 // indirect
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
golang.org/x/sys v0.28.0 // indirect
|
||||||
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 // indirect
|
golang.org/x/term v0.27.0 // indirect
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
golang.org/x/text v0.21.0 // indirect
|
||||||
golang.org/x/text v0.3.7 // indirect
|
golang.org/x/time v0.5.0 // indirect
|
||||||
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
|
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
||||||
golang.org/x/tools v0.1.10 // indirect
|
google.golang.org/appengine v1.6.8 // indirect
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
google.golang.org/genproto v0.0.0-20231212172506-995d672761c0 // indirect
|
||||||
google.golang.org/appengine v1.6.7 // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 // indirect
|
||||||
google.golang.org/grpc v1.40.0 // indirect
|
google.golang.org/grpc v1.60.1 // indirect
|
||||||
google.golang.org/protobuf v1.28.0 // indirect
|
google.golang.org/protobuf v1.33.0 // indirect
|
||||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
k8s.io/gengo v0.0.0-20211129171323-c02415ce4185 // indirect
|
k8s.io/apiextensions-apiserver v0.29.3 // indirect
|
||||||
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect
|
k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70 // indirect
|
||||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.30 // indirect
|
k8s.io/kms v0.30.0 // indirect
|
||||||
sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect
|
k8s.io/utils v0.0.0-20240423183400-0849a56e8f22 // indirect
|
||||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
|
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.29.0 // indirect
|
||||||
sigs.k8s.io/yaml v1.2.0 // indirect
|
sigs.k8s.io/controller-runtime v0.17.2 // indirect
|
||||||
|
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||||
|
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
|
||||||
|
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Copyright 2017 The Kubernetes 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.
|
|
||||||
|
|
||||||
set -o errexit
|
|
||||||
set -o nounset
|
|
||||||
set -o pipefail
|
|
||||||
|
|
||||||
verify=0
|
|
||||||
if [[ ${1:-} = "--verify" || ${1:-} = "-v" ]]; then
|
|
||||||
verify=1
|
|
||||||
fi
|
|
||||||
|
|
||||||
find_files() {
|
|
||||||
find . -not \( \( \
|
|
||||||
-wholename './_output' \
|
|
||||||
-o -wholename './vendor' \
|
|
||||||
\) -prune \) -name '*.go'
|
|
||||||
}
|
|
||||||
|
|
||||||
if [[ $verify -eq 1 ]]; then
|
|
||||||
diff=$(find_files | xargs gofmt -s -d 2>&1)
|
|
||||||
if [[ -n "${diff}" ]]; then
|
|
||||||
echo "gofmt -s -w $(echo "${diff}" | awk '/^diff / { print $2 }' | tr '\n' ' ')"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
find_files | xargs gofmt -s -w
|
|
||||||
fi
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -21,7 +21,6 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path"
|
"path"
|
||||||
|
|
@ -61,11 +60,10 @@ func (c *httpAPIClient) Do(ctx context.Context, verb, endpoint string, query url
|
||||||
reqBody = strings.NewReader(query.Encode())
|
reqBody = strings.NewReader(query.Encode())
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequest(verb, u.String(), reqBody)
|
req, err := http.NewRequestWithContext(ctx, verb, u.String(), reqBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return APIResponse{}, fmt.Errorf("error constructing HTTP request to Prometheus: %v", err)
|
return APIResponse{}, fmt.Errorf("error constructing HTTP request to Prometheus: %v", err)
|
||||||
}
|
}
|
||||||
req.WithContext(ctx)
|
|
||||||
for key, values := range c.headers {
|
for key, values := range c.headers {
|
||||||
for _, value := range values {
|
for _, value := range values {
|
||||||
req.Header.Add(key, value)
|
req.Header.Add(key, value)
|
||||||
|
|
@ -102,7 +100,7 @@ func (c *httpAPIClient) Do(ctx context.Context, verb, endpoint string, query url
|
||||||
|
|
||||||
var body io.Reader = resp.Body
|
var body io.Reader = resp.Body
|
||||||
if klog.V(8).Enabled() {
|
if klog.V(8).Enabled() {
|
||||||
data, err := ioutil.ReadAll(body)
|
data, err := io.ReadAll(body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return APIResponse{}, fmt.Errorf("unable to log response body: %v", err)
|
return APIResponse{}, fmt.Errorf("unable to log response body: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -237,7 +235,7 @@ func (h *queryClient) QueryRange(ctx context.Context, r Range, query Selector) (
|
||||||
// when present
|
// when present
|
||||||
func timeoutFromContext(ctx context.Context) (time.Duration, bool) {
|
func timeoutFromContext(ctx context.Context) (time.Duration, bool) {
|
||||||
if deadline, hasDeadline := ctx.Deadline(); hasDeadline {
|
if deadline, hasDeadline := ctx.Deadline(); hasDeadline {
|
||||||
return time.Now().Sub(deadline), true
|
return time.Since(deadline), true
|
||||||
}
|
}
|
||||||
|
|
||||||
return time.Duration(0), false
|
return time.Duration(0), false
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
||||||
|
|
@ -13,15 +13,21 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package metrics
|
package metrics
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
|
||||||
|
apimetrics "k8s.io/apiserver/pkg/endpoints/metrics"
|
||||||
|
"k8s.io/component-base/metrics"
|
||||||
|
"k8s.io/component-base/metrics/legacyregistry"
|
||||||
|
|
||||||
"sigs.k8s.io/prometheus-adapter/pkg/client"
|
"sigs.k8s.io/prometheus-adapter/pkg/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -29,18 +35,29 @@ var (
|
||||||
// queryLatency is the total latency of any query going through the
|
// queryLatency is the total latency of any query going through the
|
||||||
// various endpoints (query, range-query, series). It includes some deserialization
|
// various endpoints (query, range-query, series). It includes some deserialization
|
||||||
// overhead and HTTP overhead.
|
// overhead and HTTP overhead.
|
||||||
queryLatency = prometheus.NewHistogramVec(
|
queryLatency = metrics.NewHistogramVec(
|
||||||
prometheus.HistogramOpts{
|
&metrics.HistogramOpts{
|
||||||
Name: "cmgateway_prometheus_query_latency_seconds",
|
Namespace: "prometheus_adapter",
|
||||||
|
Subsystem: "prometheus_client",
|
||||||
|
Name: "request_duration_seconds",
|
||||||
Help: "Prometheus client query latency in seconds. Broken down by target prometheus endpoint and target server",
|
Help: "Prometheus client query latency in seconds. Broken down by target prometheus endpoint and target server",
|
||||||
Buckets: prometheus.ExponentialBuckets(0.0001, 2, 10),
|
Buckets: prometheus.DefBuckets,
|
||||||
},
|
},
|
||||||
[]string{"endpoint", "server"},
|
[]string{"path", "server"},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func MetricsHandler() (http.HandlerFunc, error) {
|
||||||
prometheus.MustRegister(queryLatency)
|
registry := metrics.NewKubeRegistry()
|
||||||
|
err := registry.Register(queryLatency)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
apimetrics.Register()
|
||||||
|
return func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
legacyregistry.Handler().ServeHTTP(w, req)
|
||||||
|
metrics.HandlerFor(registry, metrics.HandlerOpts{}).ServeHTTP(w, req)
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// instrumentedClient is a client.GenericAPIClient which instruments calls to Do,
|
// instrumentedClient is a client.GenericAPIClient which instruments calls to Do,
|
||||||
|
|
@ -62,7 +79,7 @@ func (c *instrumentedGenericClient) Do(ctx context.Context, verb, endpoint strin
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
queryLatency.With(prometheus.Labels{"endpoint": endpoint, "server": c.serverName}).Observe(endTime.Sub(startTime).Seconds())
|
queryLatency.With(prometheus.Labels{"path": endpoint, "server": c.serverName}).Observe(endTime.Sub(startTime).Seconds())
|
||||||
}()
|
}()
|
||||||
|
|
||||||
var resp client.APIResponse
|
var resp client.APIResponse
|
||||||
|
|
|
||||||
|
|
@ -25,10 +25,10 @@ type ErrorType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ErrBadData ErrorType = "bad_data"
|
ErrBadData ErrorType = "bad_data"
|
||||||
ErrTimeout = "timeout"
|
ErrTimeout ErrorType = "timeout"
|
||||||
ErrCanceled = "canceled"
|
ErrCanceled ErrorType = "canceled"
|
||||||
ErrExec = "execution"
|
ErrExec ErrorType = "execution"
|
||||||
ErrBadResponse = "bad_response"
|
ErrBadResponse ErrorType = "bad_response"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Error is an error returned by the API.
|
// Error is an error returned by the API.
|
||||||
|
|
@ -46,7 +46,7 @@ type ResponseStatus string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ResponseSucceeded ResponseStatus = "succeeded"
|
ResponseSucceeded ResponseStatus = "succeeded"
|
||||||
ResponseError = "error"
|
ResponseError ResponseStatus = "error"
|
||||||
)
|
)
|
||||||
|
|
||||||
// APIResponse represents the raw response returned by the API.
|
// APIResponse represents the raw response returned by the API.
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
yaml "gopkg.in/yaml.v2"
|
yaml "gopkg.in/yaml.v2"
|
||||||
|
|
@ -15,7 +15,7 @@ func FromFile(filename string) (*MetricsDiscoveryConfig, error) {
|
||||||
return nil, fmt.Errorf("unable to load metrics discovery config file: %v", err)
|
return nil, fmt.Errorf("unable to load metrics discovery config file: %v", err)
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
contents, err := ioutil.ReadAll(file)
|
contents, err := io.ReadAll(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to load metrics discovery config file: %v", err)
|
return nil, fmt.Errorf("unable to load metrics discovery config file: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ func (p *prometheusProvider) metricFor(value pmodel.SampleValue, name types.Name
|
||||||
Name: info.Metric,
|
Name: info.Metric,
|
||||||
},
|
},
|
||||||
// TODO(directxman12): use the right timestamp
|
// TODO(directxman12): use the right timestamp
|
||||||
Timestamp: metav1.Time{time.Now()},
|
Timestamp: metav1.Time{Time: time.Now()},
|
||||||
Value: *q,
|
Value: *q,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -256,7 +256,7 @@ func (l *cachingMetricsLister) updateMetrics() error {
|
||||||
}
|
}
|
||||||
selectors[sel] = struct{}{}
|
selectors[sel] = struct{}{}
|
||||||
go func() {
|
go func() {
|
||||||
series, err := l.promClient.Series(context.TODO(), pmodel.Interval{startTime, 0}, sel)
|
series, err := l.promClient.Series(context.TODO(), pmodel.Interval{Start: startTime, End: 0}, sel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs <- fmt.Errorf("unable to fetch metrics for query %q: %v", sel, err)
|
errs <- fmt.Errorf("unable to fetch metrics for query %q: %v", sel, err)
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ var _ = Describe("Custom Metrics Provider", func() {
|
||||||
By("ensuring that no metrics are present before we start listing")
|
By("ensuring that no metrics are present before we start listing")
|
||||||
Expect(prov.ListAllMetrics()).To(BeEmpty())
|
Expect(prov.ListAllMetrics()).To(BeEmpty())
|
||||||
|
|
||||||
By("setting the acceptible interval to now until the next update, with a bit of wiggle room")
|
By("setting the acceptable interval to now until the next update, with a bit of wiggle room")
|
||||||
startTime := pmodel.Now().Add(-1*fakeProviderUpdateInterval - fakeProviderUpdateInterval/10)
|
startTime := pmodel.Now().Add(-1*fakeProviderUpdateInterval - fakeProviderUpdateInterval/10)
|
||||||
fakeProm.AcceptableInterval = pmodel.Interval{Start: startTime, End: 0}
|
fakeProm.AcceptableInterval = pmodel.Interval{Start: startTime, End: 0}
|
||||||
|
|
||||||
|
|
@ -98,16 +98,16 @@ var _ = Describe("Custom Metrics Provider", func() {
|
||||||
|
|
||||||
By("listing all metrics, and checking that they contain the expected results")
|
By("listing all metrics, and checking that they contain the expected results")
|
||||||
Expect(prov.ListAllMetrics()).To(ConsistOf(
|
Expect(prov.ListAllMetrics()).To(ConsistOf(
|
||||||
provider.CustomMetricInfo{schema.GroupResource{Resource: "services"}, true, "ingress_hits"},
|
provider.CustomMetricInfo{GroupResource: schema.GroupResource{Resource: "services"}, Namespaced: true, Metric: "ingress_hits"},
|
||||||
provider.CustomMetricInfo{schema.GroupResource{Group: "extensions", Resource: "ingresses"}, true, "ingress_hits"},
|
provider.CustomMetricInfo{GroupResource: schema.GroupResource{Group: "extensions", Resource: "ingresses"}, Namespaced: true, Metric: "ingress_hits"},
|
||||||
provider.CustomMetricInfo{schema.GroupResource{Resource: "pods"}, true, "ingress_hits"},
|
provider.CustomMetricInfo{GroupResource: schema.GroupResource{Resource: "pods"}, Namespaced: true, Metric: "ingress_hits"},
|
||||||
provider.CustomMetricInfo{schema.GroupResource{Resource: "namespaces"}, false, "ingress_hits"},
|
provider.CustomMetricInfo{GroupResource: schema.GroupResource{Resource: "namespaces"}, Namespaced: false, Metric: "ingress_hits"},
|
||||||
provider.CustomMetricInfo{schema.GroupResource{Resource: "services"}, true, "service_proxy_packets"},
|
provider.CustomMetricInfo{GroupResource: schema.GroupResource{Resource: "services"}, Namespaced: true, Metric: "service_proxy_packets"},
|
||||||
provider.CustomMetricInfo{schema.GroupResource{Resource: "namespaces"}, false, "service_proxy_packets"},
|
provider.CustomMetricInfo{GroupResource: schema.GroupResource{Resource: "namespaces"}, Namespaced: false, Metric: "service_proxy_packets"},
|
||||||
provider.CustomMetricInfo{schema.GroupResource{Group: "extensions", Resource: "deployments"}, true, "work_queue_wait"},
|
provider.CustomMetricInfo{GroupResource: schema.GroupResource{Group: "extensions", Resource: "deployments"}, Namespaced: true, Metric: "work_queue_wait"},
|
||||||
provider.CustomMetricInfo{schema.GroupResource{Resource: "namespaces"}, false, "work_queue_wait"},
|
provider.CustomMetricInfo{GroupResource: schema.GroupResource{Resource: "namespaces"}, Namespaced: false, Metric: "work_queue_wait"},
|
||||||
provider.CustomMetricInfo{schema.GroupResource{Resource: "namespaces"}, false, "some_usage"},
|
provider.CustomMetricInfo{GroupResource: schema.GroupResource{Resource: "namespaces"}, Namespaced: false, Metric: "some_usage"},
|
||||||
provider.CustomMetricInfo{schema.GroupResource{Resource: "pods"}, true, "some_usage"},
|
provider.CustomMetricInfo{GroupResource: schema.GroupResource{Resource: "pods"}, Namespaced: true, Metric: "some_usage"},
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ var seriesRegistryTestSeries = [][]prom.Series{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// guage metrics
|
// gauge metrics
|
||||||
{
|
{
|
||||||
Name: "node_gigawatts",
|
Name: "node_gigawatts",
|
||||||
Labels: pmodel.LabelSet{"kube_node": "somenode"},
|
Labels: pmodel.LabelSet{"kube_node": "somenode"},
|
||||||
|
|
@ -159,7 +159,7 @@ var _ = Describe("Series Registry", func() {
|
||||||
// container metrics
|
// container metrics
|
||||||
{
|
{
|
||||||
title: "container metrics gauge / multiple resource names",
|
title: "container metrics gauge / multiple resource names",
|
||||||
info: provider.CustomMetricInfo{schema.GroupResource{Resource: "pods"}, true, "some_usage"},
|
info: provider.CustomMetricInfo{GroupResource: schema.GroupResource{Resource: "pods"}, Namespaced: true, Metric: "some_usage"},
|
||||||
namespace: "somens",
|
namespace: "somens",
|
||||||
resourceNames: []string{"somepod1", "somepod2"},
|
resourceNames: []string{"somepod1", "somepod2"},
|
||||||
metricSelector: labels.Everything(),
|
metricSelector: labels.Everything(),
|
||||||
|
|
@ -168,7 +168,7 @@ var _ = Describe("Series Registry", func() {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "container metrics counter",
|
title: "container metrics counter",
|
||||||
info: provider.CustomMetricInfo{schema.GroupResource{Resource: "pods"}, true, "some_count"},
|
info: provider.CustomMetricInfo{GroupResource: schema.GroupResource{Resource: "pods"}, Namespaced: true, Metric: "some_count"},
|
||||||
namespace: "somens",
|
namespace: "somens",
|
||||||
resourceNames: []string{"somepod1", "somepod2"},
|
resourceNames: []string{"somepod1", "somepod2"},
|
||||||
metricSelector: labels.Everything(),
|
metricSelector: labels.Everything(),
|
||||||
|
|
@ -177,7 +177,7 @@ var _ = Describe("Series Registry", func() {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "container metrics seconds counter",
|
title: "container metrics seconds counter",
|
||||||
info: provider.CustomMetricInfo{schema.GroupResource{Resource: "pods"}, true, "some_time"},
|
info: provider.CustomMetricInfo{GroupResource: schema.GroupResource{Resource: "pods"}, Namespaced: true, Metric: "some_time"},
|
||||||
namespace: "somens",
|
namespace: "somens",
|
||||||
resourceNames: []string{"somepod1", "somepod2"},
|
resourceNames: []string{"somepod1", "somepod2"},
|
||||||
metricSelector: labels.Everything(),
|
metricSelector: labels.Everything(),
|
||||||
|
|
@ -187,7 +187,7 @@ var _ = Describe("Series Registry", func() {
|
||||||
// namespaced metrics
|
// namespaced metrics
|
||||||
{
|
{
|
||||||
title: "namespaced metrics counter / multidimensional (service)",
|
title: "namespaced metrics counter / multidimensional (service)",
|
||||||
info: provider.CustomMetricInfo{schema.GroupResource{Resource: "service"}, true, "ingress_hits"},
|
info: provider.CustomMetricInfo{GroupResource: schema.GroupResource{Resource: "service"}, Namespaced: true, Metric: "ingress_hits"},
|
||||||
namespace: "somens",
|
namespace: "somens",
|
||||||
resourceNames: []string{"somesvc"},
|
resourceNames: []string{"somesvc"},
|
||||||
metricSelector: labels.Everything(),
|
metricSelector: labels.Everything(),
|
||||||
|
|
@ -196,7 +196,7 @@ var _ = Describe("Series Registry", func() {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "namespaced metrics counter / multidimensional (service) / selection using labels",
|
title: "namespaced metrics counter / multidimensional (service) / selection using labels",
|
||||||
info: provider.CustomMetricInfo{schema.GroupResource{Resource: "service"}, true, "ingress_hits"},
|
info: provider.CustomMetricInfo{GroupResource: schema.GroupResource{Resource: "service"}, Namespaced: true, Metric: "ingress_hits"},
|
||||||
namespace: "somens",
|
namespace: "somens",
|
||||||
resourceNames: []string{"somesvc"},
|
resourceNames: []string{"somesvc"},
|
||||||
metricSelector: labels.NewSelector().Add(
|
metricSelector: labels.NewSelector().Add(
|
||||||
|
|
@ -206,7 +206,7 @@ var _ = Describe("Series Registry", func() {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "namespaced metrics counter / multidimensional (ingress)",
|
title: "namespaced metrics counter / multidimensional (ingress)",
|
||||||
info: provider.CustomMetricInfo{schema.GroupResource{Group: "extensions", Resource: "ingress"}, true, "ingress_hits"},
|
info: provider.CustomMetricInfo{GroupResource: schema.GroupResource{Group: "extensions", Resource: "ingress"}, Namespaced: true, Metric: "ingress_hits"},
|
||||||
namespace: "somens",
|
namespace: "somens",
|
||||||
resourceNames: []string{"someingress"},
|
resourceNames: []string{"someingress"},
|
||||||
metricSelector: labels.Everything(),
|
metricSelector: labels.Everything(),
|
||||||
|
|
@ -215,7 +215,7 @@ var _ = Describe("Series Registry", func() {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "namespaced metrics counter / multidimensional (pod)",
|
title: "namespaced metrics counter / multidimensional (pod)",
|
||||||
info: provider.CustomMetricInfo{schema.GroupResource{Resource: "pod"}, true, "ingress_hits"},
|
info: provider.CustomMetricInfo{GroupResource: schema.GroupResource{Resource: "pod"}, Namespaced: true, Metric: "ingress_hits"},
|
||||||
namespace: "somens",
|
namespace: "somens",
|
||||||
resourceNames: []string{"somepod"},
|
resourceNames: []string{"somepod"},
|
||||||
metricSelector: labels.Everything(),
|
metricSelector: labels.Everything(),
|
||||||
|
|
@ -224,7 +224,7 @@ var _ = Describe("Series Registry", func() {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "namespaced metrics gauge",
|
title: "namespaced metrics gauge",
|
||||||
info: provider.CustomMetricInfo{schema.GroupResource{Resource: "service"}, true, "service_proxy_packets"},
|
info: provider.CustomMetricInfo{GroupResource: schema.GroupResource{Resource: "service"}, Namespaced: true, Metric: "service_proxy_packets"},
|
||||||
namespace: "somens",
|
namespace: "somens",
|
||||||
resourceNames: []string{"somesvc"},
|
resourceNames: []string{"somesvc"},
|
||||||
metricSelector: labels.Everything(),
|
metricSelector: labels.Everything(),
|
||||||
|
|
@ -233,7 +233,7 @@ var _ = Describe("Series Registry", func() {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "namespaced metrics seconds counter",
|
title: "namespaced metrics seconds counter",
|
||||||
info: provider.CustomMetricInfo{schema.GroupResource{Group: "extensions", Resource: "deployment"}, true, "work_queue_wait"},
|
info: provider.CustomMetricInfo{GroupResource: schema.GroupResource{Group: "extensions", Resource: "deployment"}, Namespaced: true, Metric: "work_queue_wait"},
|
||||||
namespace: "somens",
|
namespace: "somens",
|
||||||
resourceNames: []string{"somedep"},
|
resourceNames: []string{"somedep"},
|
||||||
metricSelector: labels.Everything(),
|
metricSelector: labels.Everything(),
|
||||||
|
|
@ -243,7 +243,7 @@ var _ = Describe("Series Registry", func() {
|
||||||
// non-namespaced series
|
// non-namespaced series
|
||||||
{
|
{
|
||||||
title: "root scoped metrics gauge",
|
title: "root scoped metrics gauge",
|
||||||
info: provider.CustomMetricInfo{schema.GroupResource{Resource: "node"}, false, "node_gigawatts"},
|
info: provider.CustomMetricInfo{GroupResource: schema.GroupResource{Resource: "node"}, Namespaced: false, Metric: "node_gigawatts"},
|
||||||
resourceNames: []string{"somenode"},
|
resourceNames: []string{"somenode"},
|
||||||
metricSelector: labels.Everything(),
|
metricSelector: labels.Everything(),
|
||||||
|
|
||||||
|
|
@ -251,7 +251,7 @@ var _ = Describe("Series Registry", func() {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "root scoped metrics counter",
|
title: "root scoped metrics counter",
|
||||||
info: provider.CustomMetricInfo{schema.GroupResource{Resource: "persistentvolume"}, false, "volume_claims"},
|
info: provider.CustomMetricInfo{GroupResource: schema.GroupResource{Resource: "persistentvolume"}, Namespaced: false, Metric: "volume_claims"},
|
||||||
resourceNames: []string{"somepv"},
|
resourceNames: []string{"somepv"},
|
||||||
metricSelector: labels.Everything(),
|
metricSelector: labels.Everything(),
|
||||||
|
|
||||||
|
|
@ -259,7 +259,7 @@ var _ = Describe("Series Registry", func() {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "root scoped metrics seconds counter",
|
title: "root scoped metrics seconds counter",
|
||||||
info: provider.CustomMetricInfo{schema.GroupResource{Resource: "node"}, false, "node_fan"},
|
info: provider.CustomMetricInfo{GroupResource: schema.GroupResource{Resource: "node"}, Namespaced: false, Metric: "node_fan"},
|
||||||
resourceNames: []string{"somenode"},
|
resourceNames: []string{"somenode"},
|
||||||
metricSelector: labels.Everything(),
|
metricSelector: labels.Everything(),
|
||||||
|
|
||||||
|
|
@ -281,23 +281,23 @@ var _ = Describe("Series Registry", func() {
|
||||||
|
|
||||||
It("should list all metrics", func() {
|
It("should list all metrics", func() {
|
||||||
Expect(registry.ListAllMetrics()).To(ConsistOf(
|
Expect(registry.ListAllMetrics()).To(ConsistOf(
|
||||||
provider.CustomMetricInfo{schema.GroupResource{Resource: "pods"}, true, "some_count"},
|
provider.CustomMetricInfo{GroupResource: schema.GroupResource{Resource: "pods"}, Namespaced: true, Metric: "some_count"},
|
||||||
provider.CustomMetricInfo{schema.GroupResource{Resource: "namespaces"}, false, "some_count"},
|
provider.CustomMetricInfo{GroupResource: schema.GroupResource{Resource: "namespaces"}, Namespaced: false, Metric: "some_count"},
|
||||||
provider.CustomMetricInfo{schema.GroupResource{Resource: "pods"}, true, "some_time"},
|
provider.CustomMetricInfo{GroupResource: schema.GroupResource{Resource: "pods"}, Namespaced: true, Metric: "some_time"},
|
||||||
provider.CustomMetricInfo{schema.GroupResource{Resource: "namespaces"}, false, "some_time"},
|
provider.CustomMetricInfo{GroupResource: schema.GroupResource{Resource: "namespaces"}, Namespaced: false, Metric: "some_time"},
|
||||||
provider.CustomMetricInfo{schema.GroupResource{Resource: "pods"}, true, "some_usage"},
|
provider.CustomMetricInfo{GroupResource: schema.GroupResource{Resource: "pods"}, Namespaced: true, Metric: "some_usage"},
|
||||||
provider.CustomMetricInfo{schema.GroupResource{Resource: "namespaces"}, false, "some_usage"},
|
provider.CustomMetricInfo{GroupResource: schema.GroupResource{Resource: "namespaces"}, Namespaced: false, Metric: "some_usage"},
|
||||||
provider.CustomMetricInfo{schema.GroupResource{Resource: "services"}, true, "ingress_hits"},
|
provider.CustomMetricInfo{GroupResource: schema.GroupResource{Resource: "services"}, Namespaced: true, Metric: "ingress_hits"},
|
||||||
provider.CustomMetricInfo{schema.GroupResource{Group: "extensions", Resource: "ingresses"}, true, "ingress_hits"},
|
provider.CustomMetricInfo{GroupResource: schema.GroupResource{Group: "extensions", Resource: "ingresses"}, Namespaced: true, Metric: "ingress_hits"},
|
||||||
provider.CustomMetricInfo{schema.GroupResource{Resource: "pods"}, true, "ingress_hits"},
|
provider.CustomMetricInfo{GroupResource: schema.GroupResource{Resource: "pods"}, Namespaced: true, Metric: "ingress_hits"},
|
||||||
provider.CustomMetricInfo{schema.GroupResource{Resource: "namespaces"}, false, "ingress_hits"},
|
provider.CustomMetricInfo{GroupResource: schema.GroupResource{Resource: "namespaces"}, Namespaced: false, Metric: "ingress_hits"},
|
||||||
provider.CustomMetricInfo{schema.GroupResource{Resource: "services"}, true, "service_proxy_packets"},
|
provider.CustomMetricInfo{GroupResource: schema.GroupResource{Resource: "services"}, Namespaced: true, Metric: "service_proxy_packets"},
|
||||||
provider.CustomMetricInfo{schema.GroupResource{Resource: "namespaces"}, false, "service_proxy_packets"},
|
provider.CustomMetricInfo{GroupResource: schema.GroupResource{Resource: "namespaces"}, Namespaced: false, Metric: "service_proxy_packets"},
|
||||||
provider.CustomMetricInfo{schema.GroupResource{Group: "extensions", Resource: "deployments"}, true, "work_queue_wait"},
|
provider.CustomMetricInfo{GroupResource: schema.GroupResource{Group: "extensions", Resource: "deployments"}, Namespaced: true, Metric: "work_queue_wait"},
|
||||||
provider.CustomMetricInfo{schema.GroupResource{Resource: "namespaces"}, false, "work_queue_wait"},
|
provider.CustomMetricInfo{GroupResource: schema.GroupResource{Resource: "namespaces"}, Namespaced: false, Metric: "work_queue_wait"},
|
||||||
provider.CustomMetricInfo{schema.GroupResource{Resource: "nodes"}, false, "node_gigawatts"},
|
provider.CustomMetricInfo{GroupResource: schema.GroupResource{Resource: "nodes"}, Namespaced: false, Metric: "node_gigawatts"},
|
||||||
provider.CustomMetricInfo{schema.GroupResource{Resource: "persistentvolumes"}, false, "volume_claims"},
|
provider.CustomMetricInfo{GroupResource: schema.GroupResource{Resource: "persistentvolumes"}, Namespaced: false, Metric: "volume_claims"},
|
||||||
provider.CustomMetricInfo{schema.GroupResource{Resource: "nodes"}, false, "node_fan"},
|
provider.CustomMetricInfo{GroupResource: schema.GroupResource{Resource: "nodes"}, Namespaced: false, Metric: "node_fan"},
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@ func (l *basicMetricLister) ListAllMetrics() (MetricUpdateResult, error) {
|
||||||
}
|
}
|
||||||
selectors[sel] = struct{}{}
|
selectors[sel] = struct{}{}
|
||||||
go func() {
|
go func() {
|
||||||
series, err := l.promClient.Series(context.TODO(), pmodel.Interval{startTime, 0}, sel)
|
series, err := l.promClient.Series(context.TODO(), pmodel.Interval{Start: startTime, End: 0}, sel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs <- fmt.Errorf("unable to fetch metrics for query %q: %v", sel, err)
|
errs <- fmt.Errorf("unable to fetch metrics for query %q: %v", sel, err)
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,6 @@ func (r *externalSeriesRegistry) filterAndStoreMetrics(result MetricUpdateResult
|
||||||
|
|
||||||
r.metrics = apiMetricsCache
|
r.metrics = apiMetricsCache
|
||||||
r.metricsInfo = rawMetricsCache
|
r.metricsInfo = rawMetricsCache
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *externalSeriesRegistry) ListAllMetrics() []provider.ExternalMetricInfo {
|
func (r *externalSeriesRegistry) ListAllMetrics() []provider.ExternalMetricInfo {
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ func (c *metricConverter) convertSample(info provider.ExternalMetricInfo, sample
|
||||||
singleMetric := external_metrics.ExternalMetricValue{
|
singleMetric := external_metrics.ExternalMetricValue{
|
||||||
MetricName: info.Metric,
|
MetricName: info.Metric,
|
||||||
Timestamp: metav1.Time{
|
Timestamp: metav1.Time{
|
||||||
sample.Timestamp.Time(),
|
Time: sample.Timestamp.Time(),
|
||||||
},
|
},
|
||||||
Value: *resource.NewMilliQuantity(int64(sample.Value*1000.0), resource.DecimalSI),
|
Value: *resource.NewMilliQuantity(int64(sample.Value*1000.0), resource.DecimalSI),
|
||||||
MetricLabels: labels,
|
MetricLabels: labels,
|
||||||
|
|
@ -133,7 +133,7 @@ func (c *metricConverter) convertScalar(info provider.ExternalMetricInfo, queryR
|
||||||
{
|
{
|
||||||
MetricName: info.Metric,
|
MetricName: info.Metric,
|
||||||
Timestamp: metav1.Time{
|
Timestamp: metav1.Time{
|
||||||
toConvert.Timestamp.Time(),
|
Time: toConvert.Timestamp.Time(),
|
||||||
},
|
},
|
||||||
Value: *resource.NewMilliQuantity(int64(toConvert.Value*1000.0), resource.DecimalSI),
|
Value: *resource.NewMilliQuantity(int64(toConvert.Value*1000.0), resource.DecimalSI),
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -85,5 +85,7 @@ func (l *periodicMetricLister) notifyListeners() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *periodicMetricLister) UpdateNow() {
|
func (l *periodicMetricLister) UpdateNow() {
|
||||||
l.updateMetrics()
|
if err := l.updateMetrics(); err != nil {
|
||||||
|
utilruntime.HandleError(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
|
||||||
pmodel "github.com/prometheus/common/model"
|
pmodel "github.com/prometheus/common/model"
|
||||||
|
|
@ -34,7 +33,6 @@ type labelGroupResExtractor struct {
|
||||||
|
|
||||||
resourceInd int
|
resourceInd int
|
||||||
groupInd *int
|
groupInd *int
|
||||||
mapper apimeta.RESTMapper
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// newLabelGroupResExtractor creates a new labelGroupResExtractor for labels whose form
|
// newLabelGroupResExtractor creates a new labelGroupResExtractor for labels whose form
|
||||||
|
|
@ -42,7 +40,10 @@ type labelGroupResExtractor struct {
|
||||||
// so anything in the template which limits resource or group name length will cause issues.
|
// so anything in the template which limits resource or group name length will cause issues.
|
||||||
func newLabelGroupResExtractor(labelTemplate *template.Template) (*labelGroupResExtractor, error) {
|
func newLabelGroupResExtractor(labelTemplate *template.Template) (*labelGroupResExtractor, error) {
|
||||||
labelRegexBuff := new(bytes.Buffer)
|
labelRegexBuff := new(bytes.Buffer)
|
||||||
if err := labelTemplate.Execute(labelRegexBuff, schema.GroupResource{"(?P<group>.+?)", "(?P<resource>.+?)"}); err != nil {
|
if err := labelTemplate.Execute(labelRegexBuff, schema.GroupResource{
|
||||||
|
Group: "(?P<group>.+?)",
|
||||||
|
Resource: "(?P<resource>.+?)"},
|
||||||
|
); err != nil {
|
||||||
return nil, fmt.Errorf("unable to convert label template to matcher: %v", err)
|
return nil, fmt.Errorf("unable to convert label template to matcher: %v", err)
|
||||||
}
|
}
|
||||||
if labelRegexBuff.Len() == 0 {
|
if labelRegexBuff.Len() == 0 {
|
||||||
|
|
|
||||||
|
|
@ -194,13 +194,14 @@ func NamersFromConfig(cfg []config.DiscoveryRule, mapper apimeta.RESTMapper) ([]
|
||||||
if nameAs == "" {
|
if nameAs == "" {
|
||||||
// check if we have an obvious default
|
// check if we have an obvious default
|
||||||
subexpNames := nameMatches.SubexpNames()
|
subexpNames := nameMatches.SubexpNames()
|
||||||
if len(subexpNames) == 1 {
|
switch len(subexpNames) {
|
||||||
|
case 1:
|
||||||
// no capture groups, use the whole thing
|
// no capture groups, use the whole thing
|
||||||
nameAs = "$0"
|
nameAs = "$0"
|
||||||
} else if len(subexpNames) == 2 {
|
case 2:
|
||||||
// one capture group, use that
|
// one capture group, use that
|
||||||
nameAs = "$1"
|
nameAs = "$1"
|
||||||
} else {
|
default:
|
||||||
return nil, fmt.Errorf("must specify an 'as' value for name matcher %q associated with series query %q", rule.Name.Matches, rule.SeriesQuery)
|
return nil, fmt.Errorf("must specify an 'as' value for name matcher %q associated with series query %q", rule.Name.Matches, rule.SeriesQuery)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -283,9 +283,8 @@ func (q *metricsQuery) processQueryParts(queryParts []queryPart) ([]string, map[
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *metricsQuery) selectMatcher(operator selection.Operator, values []string) (func(string, string) string, error) {
|
func (q *metricsQuery) selectMatcher(operator selection.Operator, values []string) (func(string, string) string, error) {
|
||||||
|
switch len(values) {
|
||||||
numValues := len(values)
|
case 0:
|
||||||
if numValues == 0 {
|
|
||||||
switch operator {
|
switch operator {
|
||||||
case selection.Exists:
|
case selection.Exists:
|
||||||
return prom.LabelNeq, nil
|
return prom.LabelNeq, nil
|
||||||
|
|
@ -294,7 +293,7 @@ func (q *metricsQuery) selectMatcher(operator selection.Operator, values []strin
|
||||||
case selection.Equals, selection.DoubleEquals, selection.NotEquals, selection.In, selection.NotIn:
|
case selection.Equals, selection.DoubleEquals, selection.NotEquals, selection.In, selection.NotIn:
|
||||||
return nil, ErrMalformedQuery
|
return nil, ErrMalformedQuery
|
||||||
}
|
}
|
||||||
} else if numValues == 1 {
|
case 1:
|
||||||
switch operator {
|
switch operator {
|
||||||
case selection.Equals, selection.DoubleEquals:
|
case selection.Equals, selection.DoubleEquals:
|
||||||
return prom.LabelEq, nil
|
return prom.LabelEq, nil
|
||||||
|
|
@ -305,7 +304,7 @@ func (q *metricsQuery) selectMatcher(operator selection.Operator, values []strin
|
||||||
case selection.DoesNotExist, selection.NotIn:
|
case selection.DoesNotExist, selection.NotIn:
|
||||||
return prom.LabelNotMatches, nil
|
return prom.LabelNotMatches, nil
|
||||||
}
|
}
|
||||||
} else {
|
default:
|
||||||
// Since labels can only have one value, providing multiple
|
// Since labels can only have one value, providing multiple
|
||||||
// values results in a regex match, even if that's not what the user
|
// values results in a regex match, even if that's not what the user
|
||||||
// asked for.
|
// asked for.
|
||||||
|
|
@ -321,8 +320,8 @@ func (q *metricsQuery) selectMatcher(operator selection.Operator, values []strin
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *metricsQuery) selectTargetValue(operator selection.Operator, values []string) (string, error) {
|
func (q *metricsQuery) selectTargetValue(operator selection.Operator, values []string) (string, error) {
|
||||||
numValues := len(values)
|
switch len(values) {
|
||||||
if numValues == 0 {
|
case 0:
|
||||||
switch operator {
|
switch operator {
|
||||||
case selection.Exists, selection.DoesNotExist:
|
case selection.Exists, selection.DoesNotExist:
|
||||||
// Return an empty string when values are equal to 0
|
// Return an empty string when values are equal to 0
|
||||||
|
|
@ -334,7 +333,7 @@ func (q *metricsQuery) selectTargetValue(operator selection.Operator, values []s
|
||||||
case selection.Equals, selection.DoubleEquals, selection.NotEquals, selection.In, selection.NotIn:
|
case selection.Equals, selection.DoubleEquals, selection.NotEquals, selection.In, selection.NotIn:
|
||||||
return "", ErrMalformedQuery
|
return "", ErrMalformedQuery
|
||||||
}
|
}
|
||||||
} else if numValues == 1 {
|
case 1:
|
||||||
switch operator {
|
switch operator {
|
||||||
case selection.Equals, selection.DoubleEquals, selection.NotEquals, selection.In, selection.NotIn:
|
case selection.Equals, selection.DoubleEquals, selection.NotEquals, selection.In, selection.NotIn:
|
||||||
// Pass the value through as-is.
|
// Pass the value through as-is.
|
||||||
|
|
@ -347,7 +346,7 @@ func (q *metricsQuery) selectTargetValue(operator selection.Operator, values []s
|
||||||
case selection.Exists, selection.DoesNotExist:
|
case selection.Exists, selection.DoesNotExist:
|
||||||
return "", ErrQueryUnsupportedValues
|
return "", ErrQueryUnsupportedValues
|
||||||
}
|
}
|
||||||
} else {
|
default:
|
||||||
switch operator {
|
switch operator {
|
||||||
case selection.Equals, selection.DoubleEquals, selection.NotEquals, selection.In, selection.NotIn:
|
case selection.Equals, selection.DoubleEquals, selection.NotEquals, selection.In, selection.NotIn:
|
||||||
// Pass the value through as-is.
|
// Pass the value through as-is.
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,6 @@ import (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
nodeResource = schema.GroupResource{Resource: "nodes"}
|
nodeResource = schema.GroupResource{Resource: "nodes"}
|
||||||
nsResource = schema.GroupResource{Resource: "ns"}
|
|
||||||
podResource = schema.GroupResource{Resource: "pods"}
|
podResource = schema.GroupResource{Resource: "pods"}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -72,7 +71,6 @@ func newResourceQuery(cfg config.ResourceRule, mapper apimeta.RESTMapper) (resou
|
||||||
nodeQuery: nodeQuery,
|
nodeQuery: nodeQuery,
|
||||||
containerLabel: cfg.ContainerLabel,
|
containerLabel: cfg.ContainerLabel,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// resourceQuery represents query information for querying resource metrics for some resource,
|
// resourceQuery represents query information for querying resource metrics for some resource,
|
||||||
|
|
@ -200,7 +198,7 @@ func (p *resourceProvider) assignForPod(pod *metav1.PartialObjectMetadata, resul
|
||||||
}
|
}
|
||||||
|
|
||||||
containerMetrics := make(map[string]metrics.ContainerMetrics)
|
containerMetrics := make(map[string]metrics.ContainerMetrics)
|
||||||
earliestTs := pmodel.Latest
|
earliestTS := pmodel.Latest
|
||||||
|
|
||||||
// organize all the CPU results
|
// organize all the CPU results
|
||||||
for _, cpu := range cpuRes {
|
for _, cpu := range cpuRes {
|
||||||
|
|
@ -212,8 +210,8 @@ func (p *resourceProvider) assignForPod(pod *metav1.PartialObjectMetadata, resul
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
containerMetrics[containerName].Usage[corev1.ResourceCPU] = *resource.NewMilliQuantity(int64(cpu.Value*1000.0), resource.DecimalSI)
|
containerMetrics[containerName].Usage[corev1.ResourceCPU] = *resource.NewMilliQuantity(int64(cpu.Value*1000.0), resource.DecimalSI)
|
||||||
if cpu.Timestamp.Before(earliestTs) {
|
if cpu.Timestamp.Before(earliestTS) {
|
||||||
earliestTs = cpu.Timestamp
|
earliestTS = cpu.Timestamp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -227,8 +225,8 @@ func (p *resourceProvider) assignForPod(pod *metav1.PartialObjectMetadata, resul
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
containerMetrics[containerName].Usage[corev1.ResourceMemory] = *resource.NewMilliQuantity(int64(mem.Value*1000.0), resource.BinarySI)
|
containerMetrics[containerName].Usage[corev1.ResourceMemory] = *resource.NewMilliQuantity(int64(mem.Value*1000.0), resource.BinarySI)
|
||||||
if mem.Timestamp.Before(earliestTs) {
|
if mem.Timestamp.Before(earliestTS) {
|
||||||
earliestTs = mem.Timestamp
|
earliestTS = mem.Timestamp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -251,7 +249,7 @@ func (p *resourceProvider) assignForPod(pod *metav1.PartialObjectMetadata, resul
|
||||||
CreationTimestamp: metav1.Now(),
|
CreationTimestamp: metav1.Now(),
|
||||||
},
|
},
|
||||||
// store the time in the final format
|
// store the time in the final format
|
||||||
Timestamp: metav1.NewTime(earliestTs.Time()),
|
Timestamp: metav1.NewTime(earliestTS.Time()),
|
||||||
Window: metav1.Duration{Duration: p.window},
|
Window: metav1.Duration{Duration: p.window},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
41
test/README.md
Normal file
41
test/README.md
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
# End-to-end tests
|
||||||
|
|
||||||
|
## With [kind](https://kind.sigs.k8s.io/)
|
||||||
|
|
||||||
|
[`kind`](https://kind.sigs.k8s.io/) and `kubectl` are automatically downloaded
|
||||||
|
except if `SKIP_INSTALL=true` is set.
|
||||||
|
A `kind` cluster is automatically created before the tests, and deleted after
|
||||||
|
the tests.
|
||||||
|
The `prometheus-adapter` container image is build locally and imported
|
||||||
|
into the cluster.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
KIND_E2E=true make test-e2e
|
||||||
|
```
|
||||||
|
|
||||||
|
## With an existing Kubernetes cluster
|
||||||
|
|
||||||
|
If you already have a Kubernetes cluster, you can use:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
KUBECONFIG="/path/to/kube/config" REGISTRY="my.registry/prefix" make test-e2e
|
||||||
|
```
|
||||||
|
|
||||||
|
- The cluster should not have a namespace `prometheus-adapter-e2e`.
|
||||||
|
The namespace will be created and deleted as part of the E2E tests.
|
||||||
|
- `KUBECONFIG` is the path of the [`kubeconfig` file].
|
||||||
|
**Optional**, defaults to `${HOME}/.kube/config`
|
||||||
|
- `REGISTRY` is the image registry where the container image should be pushed.
|
||||||
|
**Required**.
|
||||||
|
|
||||||
|
[`kubeconfig` file]: https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/
|
||||||
|
|
||||||
|
## Additional environment variables
|
||||||
|
|
||||||
|
These environment variables may also be used (with any non-empty value):
|
||||||
|
|
||||||
|
- `SKIP_INSTALL`: skip the installation of `kind` and `kubectl` binaries;
|
||||||
|
- `SKIP_CLEAN_AFTER`: skip the deletion of resources (`Kind` cluster or
|
||||||
|
Kubernetes namespace) and of the temporary directory `.e2e`;
|
||||||
|
- `CLEAN_BEFORE`: clean before running the tests, e.g. if `SKIP_CLEAN_AFTER`
|
||||||
|
was used on the previous run.
|
||||||
213
test/e2e/e2e_test.go
Normal file
213
test/e2e/e2e_test.go
Normal file
|
|
@ -0,0 +1,213 @@
|
||||||
|
/*
|
||||||
|
Copyright 2022 The Kubernetes 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 e2e
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
|
||||||
|
monitoring "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
|
metricsv1beta1 "k8s.io/metrics/pkg/apis/metrics/v1beta1"
|
||||||
|
metrics "k8s.io/metrics/pkg/client/clientset/versioned"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ns = "prometheus-adapter-e2e"
|
||||||
|
prometheusInstance = "prometheus"
|
||||||
|
deployment = "prometheus-adapter"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
client clientset.Interface
|
||||||
|
promOpClient monitoring.Interface
|
||||||
|
metricsClient metrics.Interface
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
kubeconfig := os.Getenv("KUBECONFIG")
|
||||||
|
if len(kubeconfig) == 0 {
|
||||||
|
log.Fatal("KUBECONFIG not provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
client, promOpClient, metricsClient, err = initializeClients(kubeconfig)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Cannot create clients: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
err = waitForPrometheusReady(ctx, ns, prometheusInstance)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Prometheus instance 'prometheus' not ready: %v", err)
|
||||||
|
}
|
||||||
|
err = waitForDeploymentReady(ctx, ns, deployment)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Deployment prometheus-adapter not ready: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
exitVal := m.Run()
|
||||||
|
os.Exit(exitVal)
|
||||||
|
}
|
||||||
|
|
||||||
|
func initializeClients(kubeconfig string) (clientset.Interface, monitoring.Interface, metrics.Interface, error) {
|
||||||
|
cfg, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, fmt.Errorf("Error during client configuration with %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
clientSet, err := clientset.NewForConfig(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, fmt.Errorf("Error during client creation with %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
promOpClient, err := monitoring.NewForConfig(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, fmt.Errorf("Error during dynamic client creation with %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
metricsClientSet, err := metrics.NewForConfig(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, fmt.Errorf("Error during metrics client creation with %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return clientSet, promOpClient, metricsClientSet, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitForPrometheusReady(ctx context.Context, namespace string, name string) error {
|
||||||
|
return wait.PollUntilContextTimeout(ctx, 5*time.Second, 120*time.Second, true, func(ctx context.Context) (bool, error) {
|
||||||
|
prom, err := promOpClient.MonitoringV1().Prometheuses(ns).Get(ctx, name, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var reconciled, available *monitoringv1.Condition
|
||||||
|
for _, condition := range prom.Status.Conditions {
|
||||||
|
cond := condition
|
||||||
|
if cond.Type == monitoringv1.Reconciled {
|
||||||
|
reconciled = &cond
|
||||||
|
} else if cond.Type == monitoringv1.Available {
|
||||||
|
available = &cond
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if reconciled == nil {
|
||||||
|
log.Printf("Prometheus instance '%s': Waiting for reconciliation status...", name)
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
if reconciled.Status != monitoringv1.ConditionTrue {
|
||||||
|
log.Printf("Prometheus instance '%s': Reconciiled = %v. Waiting for reconciliation (reason %s, %q)...", name, reconciled.Status, reconciled.Reason, reconciled.Message)
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
specReplicas := *prom.Spec.Replicas
|
||||||
|
availableReplicas := prom.Status.AvailableReplicas
|
||||||
|
if specReplicas != availableReplicas {
|
||||||
|
log.Printf("Prometheus instance '%s': %v/%v pods are ready. Waiting for all pods to be ready...", name, availableReplicas, specReplicas)
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if available == nil {
|
||||||
|
log.Printf("Prometheus instance '%s': Waiting for Available status...", name)
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
if available.Status != monitoringv1.ConditionTrue {
|
||||||
|
log.Printf("Prometheus instance '%s': Available = %v. Waiting for Available status... (reason %s, %q)", name, available.Status, available.Reason, available.Message)
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Prometheus instance '%s': Ready.", name)
|
||||||
|
return true, nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitForDeploymentReady(ctx context.Context, namespace string, name string) error {
|
||||||
|
return wait.PollUntilContextTimeout(ctx, 5*time.Second, 30*time.Second, true, func(ctx context.Context) (bool, error) {
|
||||||
|
sts, err := client.AppsV1().Deployments(namespace).Get(ctx, name, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if sts.Status.ReadyReplicas == *sts.Spec.Replicas {
|
||||||
|
log.Printf("Deployment %s: %v/%v pods are ready.", name, sts.Status.ReadyReplicas, *sts.Spec.Replicas)
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
log.Printf("Deployment %s: %v/%v pods are ready. Waiting for all pods to be ready...", name, sts.Status.ReadyReplicas, *sts.Spec.Replicas)
|
||||||
|
return false, nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNodeMetrics(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
var nodeMetrics *metricsv1beta1.NodeMetricsList
|
||||||
|
err := wait.PollUntilContextTimeout(ctx, 2*time.Second, 30*time.Second, true, func(ctx context.Context) (bool, error) {
|
||||||
|
var err error
|
||||||
|
nodeMetrics, err = metricsClient.MetricsV1beta1().NodeMetricses().List(ctx, metav1.ListOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
nonEmptyNodeMetrics := len(nodeMetrics.Items) > 0
|
||||||
|
if !nonEmptyNodeMetrics {
|
||||||
|
t.Logf("Node metrics empty... Retrying.")
|
||||||
|
}
|
||||||
|
return nonEmptyNodeMetrics, nil
|
||||||
|
})
|
||||||
|
require.NoErrorf(t, err, "Node metrics should not be empty")
|
||||||
|
|
||||||
|
for _, nodeMetric := range nodeMetrics.Items {
|
||||||
|
positiveMemory := nodeMetric.Usage.Memory().CmpInt64(0)
|
||||||
|
assert.Positivef(t, positiveMemory, "Memory usage for node %s is %v, should be > 0", nodeMetric.Name, nodeMetric.Usage.Memory())
|
||||||
|
|
||||||
|
positiveCPU := nodeMetric.Usage.Cpu().CmpInt64(0)
|
||||||
|
assert.Positivef(t, positiveCPU, "CPU usage for node %s is %v, should be > 0", nodeMetric.Name, nodeMetric.Usage.Cpu())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPodMetrics(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
var podMetrics *metricsv1beta1.PodMetricsList
|
||||||
|
err := wait.PollUntilContextTimeout(ctx, 2*time.Second, 30*time.Second, true, func(ctx context.Context) (bool, error) {
|
||||||
|
var err error
|
||||||
|
podMetrics, err = metricsClient.MetricsV1beta1().PodMetricses(ns).List(ctx, metav1.ListOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
nonEmptyNodeMetrics := len(podMetrics.Items) > 0
|
||||||
|
if !nonEmptyNodeMetrics {
|
||||||
|
t.Logf("Pod metrics empty... Retrying.")
|
||||||
|
}
|
||||||
|
return nonEmptyNodeMetrics, nil
|
||||||
|
})
|
||||||
|
require.NoErrorf(t, err, "Pod metrics should not be empty")
|
||||||
|
|
||||||
|
for _, pod := range podMetrics.Items {
|
||||||
|
for _, containerMetric := range pod.Containers {
|
||||||
|
positiveMemory := containerMetric.Usage.Memory().CmpInt64(0)
|
||||||
|
assert.Positivef(t, positiveMemory, "Memory usage for pod %s/%s is %v, should be > 0", pod.Name, containerMetric.Name, containerMetric.Usage.Memory())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
name: custom-metrics:system:auth-delegator
|
name: prometheus
|
||||||
roleRef:
|
roleRef:
|
||||||
apiGroup: rbac.authorization.k8s.io
|
apiGroup: rbac.authorization.k8s.io
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
name: system:auth-delegator
|
name: prometheus
|
||||||
subjects:
|
subjects:
|
||||||
- kind: ServiceAccount
|
- kind: ServiceAccount
|
||||||
name: custom-metrics-apiserver
|
name: prometheus
|
||||||
namespace: custom-metrics
|
namespace: prometheus-adapter-e2e
|
||||||
24
test/prometheus-manifests/cluster-role.yaml
Normal file
24
test/prometheus-manifests/cluster-role.yaml
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
name: prometheus
|
||||||
|
rules:
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources:
|
||||||
|
- nodes
|
||||||
|
- nodes/metrics
|
||||||
|
- services
|
||||||
|
- endpoints
|
||||||
|
- pods
|
||||||
|
verbs: ["get", "list", "watch"]
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources:
|
||||||
|
- configmaps
|
||||||
|
verbs: ["get"]
|
||||||
|
- apiGroups:
|
||||||
|
- networking.k8s.io
|
||||||
|
resources:
|
||||||
|
- ingresses
|
||||||
|
verbs: ["get", "list", "watch"]
|
||||||
|
- nonResourceURLs: ["/metrics"]
|
||||||
|
verbs: ["get"]
|
||||||
9
test/prometheus-manifests/prometheus.yaml
Normal file
9
test/prometheus-manifests/prometheus.yaml
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
apiVersion: monitoring.coreos.com/v1
|
||||||
|
kind: Prometheus
|
||||||
|
metadata:
|
||||||
|
name: prometheus
|
||||||
|
namespace: prometheus-adapter-e2e
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
serviceAccountName: prometheus
|
||||||
|
serviceMonitorSelector: {}
|
||||||
5
test/prometheus-manifests/service-account.yaml
Normal file
5
test/prometheus-manifests/service-account.yaml
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: prometheus
|
||||||
|
namespace: prometheus-adapter-e2e
|
||||||
25
test/prometheus-manifests/service-monitor-kubelet.yaml
Normal file
25
test/prometheus-manifests/service-monitor-kubelet.yaml
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
apiVersion: monitoring.coreos.com/v1
|
||||||
|
kind: ServiceMonitor
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: kubelet
|
||||||
|
name: kubelet
|
||||||
|
namespace: prometheus-adapter-e2e
|
||||||
|
spec:
|
||||||
|
endpoints:
|
||||||
|
- bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
|
||||||
|
honorLabels: true
|
||||||
|
honorTimestamps: false
|
||||||
|
interval: 10s
|
||||||
|
path: /metrics/resource
|
||||||
|
port: https-metrics
|
||||||
|
scheme: https
|
||||||
|
tlsConfig:
|
||||||
|
insecureSkipVerify: true
|
||||||
|
jobLabel: app.kubernetes.io/name
|
||||||
|
namespaceSelector:
|
||||||
|
matchNames:
|
||||||
|
- kube-system
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: kubelet
|
||||||
14
test/prometheus-manifests/service.yaml
Normal file
14
test/prometheus-manifests/service.yaml
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: prometheus
|
||||||
|
namespace: prometheus-adapter-e2e
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- name: web
|
||||||
|
port: 9090
|
||||||
|
targetPort: web
|
||||||
|
selector:
|
||||||
|
app.kubernetes.io/instance: prometheus
|
||||||
|
app.kubernetes.io/name: prometheus
|
||||||
|
sessionAffinity: ClientIP
|
||||||
134
test/run-e2e-tests.sh
Executable file
134
test/run-e2e-tests.sh
Executable file
|
|
@ -0,0 +1,134 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Copyright 2022 The Kubernetes 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.
|
||||||
|
|
||||||
|
set -x
|
||||||
|
set -o errexit
|
||||||
|
set -o nounset
|
||||||
|
|
||||||
|
# Tool versions
|
||||||
|
K8S_VERSION=${KUBERNETES_VERSION:-v1.30.0} # cf https://hub.docker.com/r/kindest/node/tags
|
||||||
|
KIND_VERSION=${KIND_VERSION:-v0.23.0} # cf https://github.com/kubernetes-sigs/kind/releases
|
||||||
|
PROM_OPERATOR_VERSION=${PROM_OPERATOR_VERSION:-v0.73.2} # cf https://github.com/prometheus-operator/prometheus-operator/releases
|
||||||
|
|
||||||
|
# Variables; set to empty if unbound/empty
|
||||||
|
REGISTRY=${REGISTRY:-}
|
||||||
|
KIND_E2E=${KIND_E2E:-}
|
||||||
|
SKIP_INSTALL=${SKIP_INSTALL:-}
|
||||||
|
SKIP_CLEAN_AFTER=${SKIP_CLEAN_AFTER:-}
|
||||||
|
CLEAN_BEFORE=${CLEAN_BEFORE:-}
|
||||||
|
|
||||||
|
# KUBECONFIG - will be overriden if a cluster is deployed with Kind
|
||||||
|
KUBECONFIG=${KUBECONFIG:-"${HOME}/.kube/config"}
|
||||||
|
|
||||||
|
# A temporary directory used by the tests
|
||||||
|
E2E_DIR="${PWD}/.e2e"
|
||||||
|
|
||||||
|
# The namespace where prometheus-adapter is deployed
|
||||||
|
NAMESPACE="prometheus-adapter-e2e"
|
||||||
|
|
||||||
|
if [[ -z "${REGISTRY}" && -z "${KIND_E2E}" ]]; then
|
||||||
|
echo -e "Either REGISTRY or KIND_E2E should be set."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
function clean {
|
||||||
|
if [[ -n "${KIND_E2E}" ]]; then
|
||||||
|
kind delete cluster || true
|
||||||
|
else
|
||||||
|
kubectl delete -f ./deploy/manifests || true
|
||||||
|
kubectl delete -f ./test/prometheus-manifests || true
|
||||||
|
kubectl delete namespace "${NAMESPACE}" || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -rf "${E2E_DIR}"
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ -n "${CLEAN_BEFORE}" ]]; then
|
||||||
|
clean
|
||||||
|
fi
|
||||||
|
|
||||||
|
function on_exit {
|
||||||
|
local error_code="$?"
|
||||||
|
|
||||||
|
echo "Obtaining prometheus-adapter pod logs..."
|
||||||
|
kubectl logs -l app.kubernetes.io/name=prometheus-adapter -n "${NAMESPACE}" || true
|
||||||
|
|
||||||
|
if [[ -z "${SKIP_CLEAN_AFTER}" ]]; then
|
||||||
|
clean
|
||||||
|
fi
|
||||||
|
|
||||||
|
test "${error_code}" == 0 && return;
|
||||||
|
}
|
||||||
|
trap on_exit EXIT
|
||||||
|
|
||||||
|
if [[ -d "${E2E_DIR}" ]]; then
|
||||||
|
echo -e "${E2E_DIR} already exists."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
mkdir -p "${E2E_DIR}"
|
||||||
|
|
||||||
|
if [[ -n "${KIND_E2E}" ]]; then
|
||||||
|
# Install kubectl and kind, if we did not set SKIP_INSTALL
|
||||||
|
if [[ -z "${SKIP_INSTALL}" ]]; then
|
||||||
|
BIN="${E2E_DIR}/bin"
|
||||||
|
mkdir -p "${BIN}"
|
||||||
|
curl -Lo "${BIN}/kubectl" "https://dl.k8s.io/release/${K8S_VERSION}/bin/linux/amd64/kubectl" && chmod +x "${BIN}/kubectl"
|
||||||
|
curl -Lo "${BIN}/kind" "https://kind.sigs.k8s.io/dl/${KIND_VERSION}/kind-linux-amd64" && chmod +x "${BIN}/kind"
|
||||||
|
export PATH="${BIN}:${PATH}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
kind create cluster --image "kindest/node:${K8S_VERSION}"
|
||||||
|
|
||||||
|
REGISTRY="localhost"
|
||||||
|
|
||||||
|
KUBECONFIG="${E2E_DIR}/kubeconfig"
|
||||||
|
kind get kubeconfig > "${KUBECONFIG}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create the test namespace
|
||||||
|
kubectl create namespace "${NAMESPACE}"
|
||||||
|
|
||||||
|
export REGISTRY
|
||||||
|
IMAGE_NAME="${REGISTRY}/prometheus-adapter-$(go env GOARCH)"
|
||||||
|
IMAGE_TAG="v$(cat VERSION)"
|
||||||
|
|
||||||
|
if [[ -n "${KIND_E2E}" ]]; then
|
||||||
|
make container
|
||||||
|
kind load docker-image "${IMAGE_NAME}:${IMAGE_TAG}"
|
||||||
|
else
|
||||||
|
make push
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install prometheus-operator
|
||||||
|
kubectl apply -f "https://github.com/prometheus-operator/prometheus-operator/releases/download/${PROM_OPERATOR_VERSION}/bundle.yaml" --server-side
|
||||||
|
|
||||||
|
# Install and setup prometheus
|
||||||
|
kubectl apply -f ./test/prometheus-manifests --server-side
|
||||||
|
|
||||||
|
# Customize prometheus-adapter manifests
|
||||||
|
# TODO: use Kustomize or generate manifests from Jsonnet
|
||||||
|
cp -r ./deploy/manifests "${E2E_DIR}/manifests"
|
||||||
|
prom_url="http://prometheus.${NAMESPACE}.svc:9090/"
|
||||||
|
sed -i -e "s|--prometheus-url=.*$|--prometheus-url=${prom_url}|g" "${E2E_DIR}/manifests/deployment.yaml"
|
||||||
|
sed -i -e "s|image: .*$|image: ${IMAGE_NAME}:${IMAGE_TAG}|g" "${E2E_DIR}/manifests/deployment.yaml"
|
||||||
|
find "${E2E_DIR}/manifests" -type f -exec sed -i -e "s|namespace: monitoring|namespace: ${NAMESPACE}|g" {} \;
|
||||||
|
|
||||||
|
# Deploy prometheus-adapter
|
||||||
|
kubectl apply -f "${E2E_DIR}/manifests" --server-side
|
||||||
|
|
||||||
|
PROJECT_PREFIX="sigs.k8s.io/prometheus-adapter"
|
||||||
|
export KUBECONFIG
|
||||||
|
go test "${PROJECT_PREFIX}/test/e2e/" -v -count=1
|
||||||
Loading…
Add table
Add a link
Reference in a new issue