mirror of
https://github.com/zalando-incubator/kube-metrics-adapter.git
synced 2025-01-09 18:01:36 +00:00
Compare commits
50 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
b7aa886546 | ||
|
942e753f87 | ||
|
86ed6ec102 | ||
|
b313c963b6 | ||
|
ca20ef3a3b | ||
|
f2179846c5 | ||
|
85089c4773 | ||
|
2ac9dd29d8 | ||
|
2bfc85ff3d | ||
|
d55a551bd8 | ||
|
3174100f5a | ||
|
16808f012d | ||
|
2531ddcbe3 | ||
|
8ff2e3985b | ||
|
dab5a66556 | ||
|
995ec54aa5 | ||
|
6f71e9c6b0 | ||
|
ed16830a77 | ||
|
7f84bc75f4 | ||
|
b5fbb1beae | ||
|
4506701b7c | ||
|
df0ed1fca4 | ||
|
bb107b678c | ||
|
8d7dc13bbd | ||
|
20f0042017 | ||
|
c732851b6b | ||
|
2e09132f95 | ||
|
95f22dc398 | ||
|
eed5c78fb3 | ||
|
e61071a36d | ||
|
c108fab55a | ||
|
be7567efea | ||
|
b677e814be | ||
|
deec4727ee | ||
|
cf369639d8 | ||
|
262a35c2ec | ||
|
607386834b | ||
|
4bdce4da4d | ||
|
801e5d7a47 | ||
|
4df21ae2b4 | ||
|
7f639baeee | ||
|
88ddb6f10e | ||
|
d9cf2e0858 | ||
|
c3b18e784b | ||
|
c3a03fd758 | ||
|
05704c0a6b | ||
|
f3a608fcf7 | ||
|
223ec9fd89 | ||
|
81255aa956 | ||
|
dac44965e3 |
35
.github/CODEOWNERS
vendored
35
.github/CODEOWNERS
vendored
@ -1,35 +0,0 @@
|
||||
# These owners will be the default owners for everything in
|
||||
# the repo.
|
||||
* @arjunrn
|
||||
|
||||
|
||||
|
||||
# Samples for assigning codeowners below:
|
||||
# Order is important; the last matching pattern takes the most
|
||||
# precedence. When someone opens a pull request that only
|
||||
# modifies JS files, only @js-owner and not the global
|
||||
# owner(s) will be requested for a review.
|
||||
# *.js @js-owner
|
||||
|
||||
# You can also use email addresses if you prefer. They'll be
|
||||
# used to look up users just like we do for commit author
|
||||
# emails.
|
||||
# *.go docs@example.com
|
||||
|
||||
# In this example, @doctocat owns any files in the build/logs
|
||||
# directory at the root of the repository and any of its
|
||||
# subdirectories.
|
||||
# /build/logs/ @doctocat
|
||||
|
||||
# The `docs/*` pattern will match files like
|
||||
# `docs/getting-started.md` but not further nested files like
|
||||
# `docs/build-app/troubleshooting.md`.
|
||||
# docs/* docs@example.com
|
||||
|
||||
# In this example, @octocat owns any file in an apps directory
|
||||
# anywhere in your repository.
|
||||
# apps/ @octocat
|
||||
|
||||
# In this example, @doctocat owns any file in the `/docs`
|
||||
# directory in the root of your repository.
|
||||
# /docs/ @doctocat
|
14
.github/dependabot.yml
vendored
Normal file
14
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: gomod
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: monthly
|
||||
time: "07:00"
|
||||
open-pull-requests-limit: 10
|
||||
- package-ecosystem: docker
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: monthly
|
||||
time: "07:00"
|
||||
open-pull-requests-limit: 10
|
25
.github/workflows/ci.yaml
vendored
Normal file
25
.github/workflows/ci.yaml
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
name: ci
|
||||
on: [push, pull_request]
|
||||
env:
|
||||
GO111MODULE: on
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '^1.16'
|
||||
- run: go version
|
||||
- run: go get github.com/mattn/goveralls
|
||||
env:
|
||||
GO111MODULE: off
|
||||
- run: curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin ${GOLANGCI_RELEASE}
|
||||
env:
|
||||
GOLANGCI_RELEASE: v1.37.0
|
||||
- run: make build.docker
|
||||
- run: make test
|
||||
- run: make check
|
||||
- run: goveralls -coverprofile=profile.cov -service=github
|
||||
env:
|
||||
COVERALLS_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,4 +1,5 @@
|
||||
build/
|
||||
apiserver.local.config/
|
||||
.idea/
|
||||
profile.cov
|
||||
vendor/
|
||||
|
20
.travis.yml
20
.travis.yml
@ -1,20 +0,0 @@
|
||||
language: go
|
||||
dist: xenial
|
||||
|
||||
go:
|
||||
- "1.14.x"
|
||||
|
||||
env:
|
||||
- GO111MODULE=on GOLANGCI_RELEASE="v1.21.0"
|
||||
|
||||
before_install:
|
||||
- GO111MODULE=off go get github.com/mattn/goveralls
|
||||
- GO111MODULE=off go get github.com/lawrencewoodman/roveralls
|
||||
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin ${GOLANGCI_RELEASE}
|
||||
|
||||
script:
|
||||
- make test
|
||||
- make build.docker
|
||||
- make check
|
||||
- roveralls
|
||||
- goveralls -v -coverprofile=roveralls.coverprofile -service=travis-ci
|
@ -1,5 +1,5 @@
|
||||
FROM registry.opensource.zalan.do/stups/alpine:latest
|
||||
MAINTAINER Team Teapot @ Zalando SE <team-teapot@zalando.de>
|
||||
FROM registry.opensource.zalan.do/library/alpine-3.12:latest
|
||||
LABEL maintainer="Team Teapot @ Zalando SE <team-teapot@zalando.de>"
|
||||
|
||||
# add binary
|
||||
ADD build/linux/kube-metrics-adapter /
|
||||
|
@ -1,3 +1,2 @@
|
||||
Mikkel Larsen <mikkel.larsen@zalando.de>
|
||||
Arjun Naik <arjun.naik@zalando.de>
|
||||
Team Teapot <team-teapot@zalando.de>
|
||||
|
22
Makefile
22
Makefile
@ -8,31 +8,45 @@ SOURCES = $(shell find . -name '*.go')
|
||||
DOCKERFILE ?= Dockerfile
|
||||
GOPKGS = $(shell go list ./...)
|
||||
BUILD_FLAGS ?= -v
|
||||
OPENAPI ?= pkg/api/generated/openapi/zz_generated.openapi.go
|
||||
LDFLAGS ?= -X main.version=$(VERSION) -w -s
|
||||
|
||||
|
||||
default: build.local
|
||||
|
||||
clean:
|
||||
rm -rf build
|
||||
rm -rf $(OPENAPI)
|
||||
|
||||
test:
|
||||
go test -v $(GOPKGS)
|
||||
go test -v -coverprofile=profile.cov $(GOPKGS)
|
||||
|
||||
check:
|
||||
go mod download
|
||||
golangci-lint run --timeout=2m ./...
|
||||
|
||||
|
||||
$(OPENAPI): go.mod
|
||||
go run k8s.io/kube-openapi/cmd/openapi-gen \
|
||||
--go-header-file hack/boilerplate.go.txt \
|
||||
--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 \
|
||||
-p pkg/api/generated/openapi \
|
||||
-o . \
|
||||
-O zz_generated.openapi \
|
||||
-r /dev/null
|
||||
|
||||
build.local: build/$(BINARY)
|
||||
build.linux: build/linux/$(BINARY)
|
||||
build.osx: build/osx/$(BINARY)
|
||||
|
||||
build/$(BINARY): go.mod $(SOURCES)
|
||||
build/$(BINARY): go.mod $(SOURCES) $(OPENAPI)
|
||||
CGO_ENABLED=0 go build -o build/$(BINARY) $(BUILD_FLAGS) -ldflags "$(LDFLAGS)" .
|
||||
|
||||
build/linux/$(BINARY): go.mod $(SOURCES)
|
||||
build/linux/$(BINARY): go.mod $(SOURCES) $(OPENAPI)
|
||||
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build $(BUILD_FLAGS) -o build/linux/$(BINARY) -ldflags "$(LDFLAGS)" .
|
||||
|
||||
build/osx/$(BINARY): go.mod $(SOURCES)
|
||||
build/osx/$(BINARY): go.mod $(SOURCES) $(OPENAPI)
|
||||
GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 go build $(BUILD_FLAGS) -o build/osx/$(BINARY) -ldflags "$(LDFLAGS)" .
|
||||
|
||||
build.docker: build.linux
|
||||
|
88
README.md
88
README.md
@ -10,7 +10,7 @@ It supports scaling based on [Prometheus metrics](https://prometheus.io/), [SQS
|
||||
|
||||
It discovers Horizontal Pod Autoscaling resources and starts to collect the
|
||||
requested metrics and stores them in memory. It's implemented using the
|
||||
[custom-metrics-apiserver](https://github.com/kubernetes-incubator/custom-metrics-apiserver)
|
||||
[custom-metrics-apiserver](https://github.com/kubernetes-sigs/custom-metrics-apiserver)
|
||||
library.
|
||||
|
||||
Here's an example of a `HorizontalPodAutoscaler` resource configured to get
|
||||
@ -22,7 +22,7 @@ kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: myapp-hpa
|
||||
annotations:
|
||||
# metric-config.<metricType>.<metricName>.<collectorName>/<configKey>
|
||||
# metric-config.<metricType>.<metricName>.<collectorType>/<configKey>
|
||||
metric-config.pods.requests-per-second.json-path/json-key: "$.http_server.rps"
|
||||
metric-config.pods.requests-per-second.json-path/path: /metrics
|
||||
metric-config.pods.requests-per-second.json-path/port: "9090"
|
||||
@ -104,7 +104,7 @@ kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: myapp-hpa
|
||||
annotations:
|
||||
# metric-config.<metricType>.<metricName>.<collectorName>/<configKey>
|
||||
# metric-config.<metricType>.<metricName>.<collectorType>/<configKey>
|
||||
metric-config.pods.requests-per-second.json-path/json-key: "$.http_server.rps"
|
||||
metric-config.pods.requests-per-second.json-path/path: /metrics
|
||||
metric-config.pods.requests-per-second.json-path/port: "9090"
|
||||
@ -142,7 +142,7 @@ example the following JSON data would be expected:
|
||||
```
|
||||
|
||||
The json-path query support depends on the
|
||||
[github.com/oliveagle/jsonpath](https://github.com/oliveagle/jsonpath) library.
|
||||
[github.com/spyzhov/ajson](https://github.com/spyzhov/ajson) library.
|
||||
See the README for possible queries. It's expected that the metric you query
|
||||
returns something that can be turned into a `float64`.
|
||||
|
||||
@ -202,15 +202,24 @@ the trade-offs between the two approaches.
|
||||
| `prometheus-query` | Generic metric which requires a user defined query. | External | | `>=1.12` |
|
||||
| *custom* | No predefined metrics. Metrics are generated from user defined queries. | Object | *any* | `>=1.12` |
|
||||
|
||||
### Configure Scrape Interval
|
||||
|
||||
There is a way to set the interval via an annotation like:
|
||||
|
||||
```yaml
|
||||
metric-config.external.prometheus-query.prometheus/interval: 30s
|
||||
```
|
||||
|
||||
Default is 1 minute.
|
||||
|
||||
### Example: External Metric
|
||||
|
||||
This is an example of an HPA configured to get metrics based on a Prometheus
|
||||
query. The query is defined in the annotation
|
||||
`metric-config.external.prometheus-query.prometheus/processed-events-per-second`
|
||||
`metric-config.external.processed-events-per-second.prometheus/query`
|
||||
where `processed-events-per-second` is the query name which will be associated
|
||||
with the result of the query. A matching `query-name` label must be defined in
|
||||
the `matchLabels` of the metric definition. This allows having multiple
|
||||
prometheus queries associated with a single HPA.
|
||||
with the result of the query.
|
||||
This allows having multiple prometheus queries associated with a single HPA.
|
||||
|
||||
```yaml
|
||||
apiVersion: autoscaling/v2beta2
|
||||
@ -221,10 +230,9 @@ metadata:
|
||||
# This annotation is optional.
|
||||
# If specified, then this prometheus server is used,
|
||||
# instead of the prometheus server specified as the CLI argument `--prometheus-server`.
|
||||
metric-config.external.prometheus-query.prometheus/prometheus-server: http://prometheus.my-namespace.svc
|
||||
# metric-config.<metricType>.<metricName>.<collectorName>/<configKey>
|
||||
# <configKey> == query-name
|
||||
metric-config.external.prometheus-query.prometheus/processed-events-per-second: |
|
||||
metric-config.external.processed-events-per-second.prometheus/prometheus-server: http://prometheus.my-namespace.svc
|
||||
# metric-config.<metricType>.<metricName>.<collectorType>/<configKey>
|
||||
metric-config.external.processed-events-per-second.prometheus/query: |
|
||||
scalar(sum(rate(event-service_events_count{application="event-service",processed="true"}[1m])))
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
@ -237,10 +245,10 @@ spec:
|
||||
- type: External
|
||||
external:
|
||||
metric:
|
||||
name: prometheus-query
|
||||
name: processed-events-per-second
|
||||
selector:
|
||||
matchLabels:
|
||||
query-name: processed-events-per-second
|
||||
type: prometheus
|
||||
target:
|
||||
type: AverageValue
|
||||
averageValue: "10"
|
||||
@ -271,7 +279,7 @@ kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: myapp-hpa
|
||||
annotations:
|
||||
# metric-config.<metricType>.<metricName>.<collectorName>/<configKey>
|
||||
# metric-config.<metricType>.<metricName>.<collectorType>/<configKey>
|
||||
metric-config.object.processed-events-per-second.prometheus/query: |
|
||||
scalar(sum(rate(event-service_events_count{application="event-service",processed="true"}[1m])))
|
||||
metric-config.object.processed-events-per-second.prometheus/per-replica: "true"
|
||||
@ -372,10 +380,9 @@ we only support Flux instead of InfluxQL.
|
||||
|
||||
This is an example of an HPA configured to get metrics based on a Flux query.
|
||||
The query is defined in the annotation
|
||||
`metric-config.external.flux-query.influxdb/queue_depth`
|
||||
where `queue_depth` is the query name which will be associated with the result of the query.
|
||||
A matching `query-name` label must be defined in the `matchLabels` of the metric definition.
|
||||
This allows having multiple flux queries associated with a single HPA.
|
||||
`metric-config.external.<metricName>.influxdb/query` where `<metricName>` is
|
||||
the query name which will be associated with the result of the query. This
|
||||
allows having multiple flux queries associated with a single HPA.
|
||||
|
||||
```yaml
|
||||
apiVersion: autoscaling/v2beta2
|
||||
@ -389,13 +396,13 @@ metadata:
|
||||
# - --influxdb-address
|
||||
# - --influxdb-token
|
||||
# - --influxdb-org
|
||||
metric-config.external.flux-query.influxdb/address: "http://influxdbv2.my-namespace.svc"
|
||||
metric-config.external.flux-query.influxdb/token: "secret-token"
|
||||
metric-config.external.queue-depth.influxdb/address: "http://influxdbv2.my-namespace.svc"
|
||||
metric-config.external.queue-depth.influxdb/token: "secret-token"
|
||||
# This could be either the organization name or the ID.
|
||||
metric-config.external.flux-query.influxdb/org: "deadbeef"
|
||||
# metric-config.<metricType>.<metricName>.<collectorName>/<configKey>
|
||||
metric-config.external.queue-depth.influxdb/org: "deadbeef"
|
||||
# metric-config.<metricType>.<metricName>.<collectorType>/<configKey>
|
||||
# <configKey> == query-name
|
||||
metric-config.external.flux-query.influxdb/queue_depth: |
|
||||
metric-config.external.queue-depth.influxdb/query: |
|
||||
from(bucket: "apps")
|
||||
|> range(start: -30s)
|
||||
|> filter(fn: (r) => r._measurement == "queue_depth")
|
||||
@ -415,10 +422,10 @@ spec:
|
||||
- type: External
|
||||
external:
|
||||
metric:
|
||||
name: flux-query
|
||||
name: queue-depth
|
||||
selector:
|
||||
matchLabels:
|
||||
query-name: queue_depth
|
||||
type: influxdb
|
||||
target:
|
||||
type: Value
|
||||
value: "1"
|
||||
@ -480,9 +487,10 @@ spec:
|
||||
- type: External
|
||||
external:
|
||||
metric:
|
||||
name: sqs-queue-length
|
||||
name: my-sqs
|
||||
selector:
|
||||
matchLabels:
|
||||
type: sqs-queue-length
|
||||
queue-name: foobar
|
||||
region: eu-central-1
|
||||
target:
|
||||
@ -521,9 +529,9 @@ kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: myapp-hpa
|
||||
annotations:
|
||||
# metric-config.<metricType>.<metricName>.<collectorName>/<configKey>
|
||||
metric-config.external.zmon-check.zmon/key: "custom.*"
|
||||
metric-config.external.zmon-check.zmon/tag-application: "my-custom-app-*"
|
||||
# metric-config.<metricType>.<metricName>.<collectorType>/<configKey>
|
||||
metric-config.external.my-zmon-check.zmon/key: "custom.*"
|
||||
metric-config.external.my-zmon-check.zmon/tag-application: "my-custom-app-*"
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
@ -535,9 +543,10 @@ spec:
|
||||
- type: External
|
||||
external:
|
||||
metric:
|
||||
name: zmon-check
|
||||
name: my-zmon-check
|
||||
selector:
|
||||
matchLabels:
|
||||
type: zmon
|
||||
check-id: "1234" # the ZMON check to query for metrics
|
||||
key: "custom.value"
|
||||
tag-application: my-custom-app
|
||||
@ -587,8 +596,8 @@ specify a duration of `5m` then the query will return metric points for the
|
||||
last 5 minutes and apply the specified aggregation with the same duration .e.g
|
||||
`max(5m)`.
|
||||
|
||||
The annotations `metric-config.external.zmon-check.zmon/key` and
|
||||
`metric-config.external.zmon-check.zmon/tag-<name>` can be optionally used if
|
||||
The annotations `metric-config.external.my-zmon-check.zmon/key` and
|
||||
`metric-config.external.my-zmon-check.zmon/tag-<name>` can be optionally used if
|
||||
you need to define a `key` or other `tag` with a "star" query syntax like
|
||||
`values.*`. This *hack* is in place because it's not allowed to use `*` in the
|
||||
metric label definitions. If both annotations and corresponding label is
|
||||
@ -616,10 +625,10 @@ kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: myapp-hpa
|
||||
annotations:
|
||||
# metric-config.<metricType>.<metricName>.<collectorName>/<configKey>
|
||||
metric-config.external.http.json/json-key: "$.some-metric.value"
|
||||
metric-config.external.http.json/endpoint: "http://metric-source.app-namespace:8080/metrics"
|
||||
metric-config.external.http.json/aggregator: "max"
|
||||
# metric-config.<metricType>.<metricName>.<collectorType>/<configKey>
|
||||
metric-config.external.unique-metric-name.json-path/json-key: "$.some-metric.value"
|
||||
metric-config.external.unique-metric-name.json-path/endpoint: "http://metric-source.app-namespace:8080/metrics"
|
||||
metric-config.external.unique-metric-name.json-path/aggregator: "max"
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
@ -631,10 +640,10 @@ spec:
|
||||
- type: External
|
||||
external:
|
||||
metric:
|
||||
name: http
|
||||
name: unique-metric-name
|
||||
selector:
|
||||
matchLabels:
|
||||
identifier: unique-metric-name
|
||||
type: json-path
|
||||
target:
|
||||
averageValue: 1
|
||||
type: AverageValue
|
||||
@ -649,4 +658,3 @@ target. The following configuration values are supported:
|
||||
in the namespace `app-namespace` is called.
|
||||
- `aggregator` is only required if the metric is an array of values and specifies how the values
|
||||
are aggregated. Currently this option can support the values: `sum`, `max`, `min`, `avg`.
|
||||
|
||||
|
9
docs/helm/Chart.yaml
Normal file
9
docs/helm/Chart.yaml
Normal file
@ -0,0 +1,9 @@
|
||||
apiVersion: v2
|
||||
name: kube-metrics-adapter
|
||||
version: 0.1.9
|
||||
description: kube-metrics-adapter helm chart
|
||||
home: https://github.com/zalando-incubator/kube-metrics-adapter
|
||||
maintainers:
|
||||
- name: The Zalando Incubator
|
||||
email: opensource@zalando.de
|
||||
url: https://github.com/zalando-incubator
|
15
docs/helm/templates/custom-metrics-apiservice.yaml
Normal file
15
docs/helm/templates/custom-metrics-apiservice.yaml
Normal file
@ -0,0 +1,15 @@
|
||||
{{- if .Values.enableCustomMetricsApi }}
|
||||
apiVersion: apiregistration.k8s.io/v1beta1
|
||||
kind: APIService
|
||||
metadata:
|
||||
name: v1beta1.custom.metrics.k8s.io
|
||||
spec:
|
||||
service:
|
||||
name: kube-metrics-adapter
|
||||
namespace: {{ .Values.namespace }}
|
||||
group: custom.metrics.k8s.io
|
||||
version: v1beta1
|
||||
insecureSkipTLSVerify: {{ .Values.tls.skipTLSVerify }}
|
||||
groupPriorityMinimum: 100
|
||||
versionPriority: 100
|
||||
{{- end}}
|
191
docs/helm/templates/deployment.yaml
Normal file
191
docs/helm/templates/deployment.yaml
Normal file
@ -0,0 +1,191 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: kube-metrics-adapter
|
||||
namespace: {{ .Values.namespace }}
|
||||
labels:
|
||||
application: kube-metrics-adapter
|
||||
version: {{ .Values.registry.imageTag }}
|
||||
spec:
|
||||
replicas: {{ .Values.replicas }}
|
||||
selector:
|
||||
matchLabels:
|
||||
application: kube-metrics-adapter
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
application: kube-metrics-adapter
|
||||
version: {{ .Values.registry.imageTag }}
|
||||
spec:
|
||||
serviceAccountName: kube-metrics-adapter
|
||||
containers:
|
||||
- name: kube-metrics-adapter
|
||||
image: {{ .Values.registry.image}}:{{ .Values.registry.imageTag }}
|
||||
args:
|
||||
{{- if .Values.addDirectoryHeader }}
|
||||
- --add_dir_header={{ .Values.addDirectoryHeader }}
|
||||
{{- end}}
|
||||
{{- if .Values.log.alsoToStderr }}
|
||||
- --alsologtostderr={{ .Values.log.alsoToStderr }}
|
||||
{{- end}}
|
||||
{{- if .Values.authentication.kubeConfig }}
|
||||
- --authentication-kubeconfig={{ .Values.authentication.kubeConfig }}
|
||||
{{- end}}
|
||||
{{- if .Values.authentication.skipLookup }}
|
||||
- --authentication-skip-lookup={{ .Values.authentication.skipLookup }}
|
||||
{{- end}}
|
||||
{{- if .Values.authentication.tokenWebhookCacheTtl }}
|
||||
- --authentication-token-webhook-cache-ttl={{ .Values.authentication.tokenWebhookCacheTtl }}
|
||||
{{- end}}
|
||||
{{- if .Values.authentication.tolerateLookupFailure }}
|
||||
- --authentication-tolerate-lookup-failure={{ .Values.authentication.tolerateLookupFailure }}
|
||||
{{- end}}
|
||||
{{- if .Values.authorization.alwaysAllowPaths }}
|
||||
- --authorization-always-allow-paths={{ .Values.authorization.alwaysAllowPaths }}
|
||||
{{- end}}
|
||||
{{- if .Values.authorization.kubeConfig }}
|
||||
- --authorization-kubeconfig={{ .Values.authorization.kubeConfig }}
|
||||
{{- end}}
|
||||
{{- if .Values.authorization.webhookCache.authorizedTtl }}
|
||||
- --authorization-webhook-cache-authorized-ttl={{ .Values.authorization.webhookCache.authorizedTtl }}
|
||||
{{- end}}
|
||||
{{- if .Values.authorization.webhookCache.unauthorizedTtl }}
|
||||
- --authorization-webhook-cache-unauthorized-ttl={{ .Values.authorization.webhookCache.unauthorizedTtl }}
|
||||
{{- end}}
|
||||
{{- if .Values.aws.externalMetrics }}
|
||||
- --aws-external-metrics={{ .Values.aws.externalMetrics }}
|
||||
{{- end}}
|
||||
{{- if .Values.aws.region }}
|
||||
- --aws-region={{ .Values.aws.region }}
|
||||
{{- end}}
|
||||
{{- if .Values.tls.certificateDirectory }}
|
||||
- --cert-dir={{ .Values.tls.certificateDirectory }}
|
||||
{{- end}}
|
||||
{{- if .Values.tls.clientCaFile }}
|
||||
- --client-ca-file={{ .Values.tls.clientCaFile }}
|
||||
{{- end}}
|
||||
{{- if .Values.contentionProfiling }}
|
||||
- --contention-profiling={{ .Values.contentionProfiling }}
|
||||
{{- end}}
|
||||
{{- if .Values.credentialsDirectory }}
|
||||
- --credentials-dir={{ .Values.credentialsDirectory }}
|
||||
{{- end}}
|
||||
{{- if .Values.disregardIncompatibleHPAs }}
|
||||
- --disregard-incompatible-hpas={{ .Values.disregardIncompatibleHPAs }}
|
||||
{{- end}}
|
||||
{{- if .Values.enableCustomMetricsApi }}
|
||||
- --enable-custom-metrics-api={{ .Values.enableCustomMetricsApi }}
|
||||
{{- end}}
|
||||
{{- if .Values.enableExternalMetricsApi }}
|
||||
- --enable-external-metrics-api={{ .Values.enableExternalMetricsApi }}
|
||||
{{- end}}
|
||||
{{- if .Values.http2MaxStreamsPerConnection }}
|
||||
- --http2-max-streams-per-connection={{ .Values.http2MaxStreamsPerConnection }}
|
||||
{{- end}}
|
||||
{{- if .Values.influxDB.address }}
|
||||
- --influxdb-address={{ .Values.influxDB.address }}
|
||||
{{- end}}
|
||||
{{- if .Values.influxDB.organization }}
|
||||
- --influxdb-org={{ .Values.influxDB.organization }}
|
||||
{{- end}}
|
||||
{{- if .Values.influxDB.token }}
|
||||
- --influxdb-token={{ .Values.influxDB.token }}
|
||||
{{- end}}
|
||||
{{- if .Values.listerKubeConfig }}
|
||||
- --lister-kubeconfig={{ .Values.listerKubeConfig }}
|
||||
{{- end}}
|
||||
{{- if .Values.log.flushFrequency }}
|
||||
- --log-flush-frequency={{ .Values.log.flushFrequency }}
|
||||
{{- end}}
|
||||
{{- if .Values.log.backtraceAtTraceLocation }}
|
||||
- --log_backtrace_at={{ .Values.log.backtraceAtTraceLocation }}
|
||||
{{- end}}
|
||||
{{- if .Values.log.directory }}
|
||||
- --log_dir={{ .Values.log.directory }}
|
||||
{{- end}}
|
||||
{{- if .Values.log.file }}
|
||||
- --log_file={{ .Values.log.file }}
|
||||
{{- end}}
|
||||
{{- if .Values.log.fileMaxSize }}
|
||||
- --log_file_max_size={{ .Values.log.fileMaxSize }}
|
||||
{{- end}}
|
||||
{{- if .Values.log.toStderr }}
|
||||
- --logtostderr={{ .Values.log.toStderr }}
|
||||
{{- end}}
|
||||
{{- if .Values.prometheus.metricsAddress }}
|
||||
- --metrics-address={{ .Values.prometheus.metricsAddress }}
|
||||
{{- end}}
|
||||
{{- if .Values.profiling }}
|
||||
- --profiling={{ .Values.profiling }}
|
||||
{{- end}}
|
||||
{{- if .Values.prometheus.server }}
|
||||
- --prometheus-server={{ .Values.prometheus.server }}
|
||||
{{- end}}
|
||||
{{- if .Values.requestHeader.allowedNames }}
|
||||
- --requestheader-allowed-names={{ .Values.requestHeader.allowedNames }}
|
||||
{{- end}}
|
||||
{{- if .Values.requestHeader.clientCaFile }}
|
||||
- --requestheader-client-ca-file={{ .Values.requestHeader.clientCaFile }}
|
||||
{{- end}}
|
||||
{{- if .Values.requestHeader.extraHeadersPrefix }}
|
||||
- --requestheader-extra-headers-prefix={{ .Values.requestHeader.extraHeadersPrefix }}
|
||||
{{- end}}
|
||||
{{- if .Values.requestHeader.groupHeaders }}
|
||||
- --requestheader-group-headers={{ .Values.requestHeader.groupHeaders }}
|
||||
{{- end}}
|
||||
{{- if .Values.requestHeader.usernameHeaders }}
|
||||
- --requestheader-username-headers={{ .Values.requestHeader.usernameHeaders }}
|
||||
{{- end}}
|
||||
- --secure-port={{ .Values.service.internalPort }}
|
||||
{{- if .Values.log.skipHeaders }}
|
||||
- --skip_headers={{ .Values.log.skipHeaders }}
|
||||
{{- end}}
|
||||
{{- if .Values.log.skipLogHeaders }}
|
||||
- --skip_log_headers={{ .Values.log.skipLogHeaders }}
|
||||
{{- end}}
|
||||
{{- if .Values.skipperBackendsAnnotation }}
|
||||
- --skipper-backends-annotation={{ .Values.skipperBackendsAnnotation }}
|
||||
{{- end}}
|
||||
{{- if .Values.skipperIngressMetrics }}
|
||||
- --skipper-ingress-metrics={{ .Values.skipperIngressMetrics }}
|
||||
{{- end}}
|
||||
{{- if .Values.log.stderrThreshold }}
|
||||
- --stderrthreshold={{ .Values.log.stderrThreshold }}
|
||||
{{- end}}
|
||||
{{- if .Values.tls.certFile }}
|
||||
- --tls-cert-file={{ .Values.tls.certFile }}
|
||||
{{- end}}
|
||||
{{- if .Values.tls.cipherSuites }}
|
||||
- --tls-cipher-suites={{ .Values.tls.cipherSuites }}
|
||||
{{- end}}
|
||||
{{- if .Values.tls.minVersion }}
|
||||
- --tls-min-version={{ .Values.tls.minVersion }}
|
||||
{{- end}}
|
||||
{{- if .Values.tls.privateKeyFile }}
|
||||
- --tls-private-key-file={{ .Values.tls.privateKeyFile }}
|
||||
{{- end}}
|
||||
{{- if .Values.tls.sniCertKey }}
|
||||
- --tls-sni-cert-key={{ .Values.tls.sniCertKey }}
|
||||
{{- end}}
|
||||
{{- if .Values.token }}
|
||||
- --token={{ .Values.token }}
|
||||
{{- end}}
|
||||
{{- if .Values.log.level }}
|
||||
- --v={{ .Values.log.level }}
|
||||
{{- end}}
|
||||
{{- if .Values.vmodule }}
|
||||
- --vmodule={{ .Values.vmodule }}
|
||||
{{- end}}
|
||||
{{- if .Values.zmon.kariosdbEndpoint }}
|
||||
- --zmon-kariosdb-endpoint={{ .Values.zmon.kariosdbEndpoint }}
|
||||
{{- end}}
|
||||
{{- if .Values.zmon.tokenName }}
|
||||
- --zmon-token-name={{ .Values.zmon.tokenName }}
|
||||
{{- end}}
|
||||
resources:
|
||||
limits:
|
||||
cpu: {{ .Values.resources.limits.cpu }}
|
||||
memory: {{ .Values.resources.limits.memory }}
|
||||
requests:
|
||||
cpu: {{ .Values.resources.requests.cpu }}
|
||||
memory: {{ .Values.resources.requests.memory }}
|
15
docs/helm/templates/external-metrics-apiservice.yaml
Normal file
15
docs/helm/templates/external-metrics-apiservice.yaml
Normal file
@ -0,0 +1,15 @@
|
||||
{{- if .Values.enableExternalMetricsApi }}
|
||||
apiVersion: apiregistration.k8s.io/v1beta1
|
||||
kind: APIService
|
||||
metadata:
|
||||
name: v1beta1.external.metrics.k8s.io
|
||||
spec:
|
||||
service:
|
||||
name: kube-metrics-adapter
|
||||
namespace: {{ .Values.namespace }}
|
||||
group: external.metrics.k8s.io
|
||||
version: v1beta1
|
||||
insecureSkipTLSVerify: {{ .Values.tls.skipTLSVerify }}
|
||||
groupPriorityMinimum: 100
|
||||
versionPriority: 100
|
||||
{{- end}}
|
154
docs/helm/templates/rbac.yaml
Normal file
154
docs/helm/templates/rbac.yaml
Normal file
@ -0,0 +1,154 @@
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: kube-metrics-adapter-server-resources
|
||||
rules:
|
||||
- apiGroups:
|
||||
- custom.metrics.k8s.io
|
||||
resources: ["*"]
|
||||
verbs: ["*"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: external-metrics-server-resources
|
||||
rules:
|
||||
- apiGroups:
|
||||
- external.metrics.k8s.io
|
||||
resources: ["*"]
|
||||
verbs: ["*"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: kube-metrics-adapter-resource-reader
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- namespaces
|
||||
- pods
|
||||
- services
|
||||
- configmaps
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: kube-metrics-adapter-resource-collector
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- events
|
||||
verbs:
|
||||
- create
|
||||
- patch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- pods
|
||||
verbs:
|
||||
- list
|
||||
- apiGroups:
|
||||
- apps
|
||||
resources:
|
||||
- deployments
|
||||
- statefulsets
|
||||
verbs:
|
||||
- get
|
||||
- apiGroups:
|
||||
- extensions
|
||||
resources:
|
||||
- ingresses
|
||||
verbs:
|
||||
- get
|
||||
- apiGroups:
|
||||
- autoscaling
|
||||
resources:
|
||||
- horizontalpodautoscalers
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: hpa-controller-custom-metrics
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: kube-metrics-adapter-server-resources
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: horizontal-pod-autoscaler
|
||||
namespace: kube-system
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: hpa-controller-external-metrics
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: external-metrics-server-resources
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: horizontal-pod-autoscaler
|
||||
namespace: kube-system
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: kube-metrics-adapter-auth-reader
|
||||
namespace: kube-system
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: extension-apiserver-authentication-reader
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: kube-metrics-adapter
|
||||
namespace: {{ .Values.namespace }}
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: custom-metrics:system:auth-delegator
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:auth-delegator
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: kube-metrics-adapter
|
||||
namespace: {{ .Values.namespace }}
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: kube-metrics-adapter-resource-collector
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: kube-metrics-adapter-resource-collector
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: kube-metrics-adapter
|
||||
namespace: {{ .Values.namespace }}
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: kube-metrics-adapter-resource-reader
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: kube-metrics-adapter-resource-reader
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: kube-metrics-adapter
|
||||
namespace: {{ .Values.namespace }}
|
5
docs/helm/templates/service-account.yaml
Normal file
5
docs/helm/templates/service-account.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
kind: ServiceAccount
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: kube-metrics-adapter
|
||||
namespace: {{ .Values.namespace }}
|
11
docs/helm/templates/service.yaml
Normal file
11
docs/helm/templates/service.yaml
Normal file
@ -0,0 +1,11 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: kube-metrics-adapter
|
||||
namespace: {{ .Values.namespace }}
|
||||
spec:
|
||||
ports:
|
||||
- port: {{ .Values.service.port }}
|
||||
targetPort: {{ .Values.service.internalPort }}
|
||||
selector:
|
||||
application: kube-metrics-adapter
|
94
docs/helm/values.yaml
Normal file
94
docs/helm/values.yaml
Normal file
@ -0,0 +1,94 @@
|
||||
namespace: kube-system
|
||||
|
||||
replicas: 1
|
||||
|
||||
registry:
|
||||
image: registry.opensource.zalan.do/teapot/kube-metrics-adapter
|
||||
imageTag: v0.1.9
|
||||
imagePullPolicy: IfNotPresent
|
||||
|
||||
service:
|
||||
port: 443
|
||||
internalPort: 443
|
||||
|
||||
addDirectoryHeader:
|
||||
contentionProfiling:
|
||||
profiling:
|
||||
enableCustomMetricsApi:
|
||||
enableExternalMetricsApi:
|
||||
credentialsDirectory:
|
||||
disregardIncompatibleHPAs:
|
||||
http2MaxStreamsPerConnection:
|
||||
listerKubeConfig:
|
||||
skipperBackendsAnnotation:
|
||||
skipperIngressMetrics:
|
||||
token:
|
||||
vmodule:
|
||||
|
||||
authentication:
|
||||
kubeConfig:
|
||||
skipLookup:
|
||||
tokenWebhookCacheTtl:
|
||||
tolerateLookupFailure:
|
||||
|
||||
authorization:
|
||||
kubeConfig:
|
||||
alwaysAllowPaths:
|
||||
webhookCache:
|
||||
authorizedTtl:
|
||||
unauthorizedTtl:
|
||||
|
||||
aws:
|
||||
externalMetrics:
|
||||
region:
|
||||
|
||||
influxDB:
|
||||
address:
|
||||
organization:
|
||||
token:
|
||||
|
||||
log:
|
||||
alsoToStderr:
|
||||
toStderr:
|
||||
flushFrequency:
|
||||
backtraceAtTraceLocation:
|
||||
directory:
|
||||
file:
|
||||
fileMaxSize:
|
||||
level:
|
||||
stderrThreshold:
|
||||
skipHeaders:
|
||||
skipLogHeaders:
|
||||
|
||||
prometheus:
|
||||
server: http://prometheus.kube-system.svc.cluster.local
|
||||
metricsAddress:
|
||||
|
||||
requestHeader:
|
||||
allowedNames:
|
||||
clientCaFile:
|
||||
extraHeadersPrefix:
|
||||
groupHeaders:
|
||||
usernameHeaders:
|
||||
|
||||
tls:
|
||||
skipTLSVerify: true
|
||||
certificateDirectory:
|
||||
clientCaFile:
|
||||
certFile:
|
||||
cipherSuites:
|
||||
minVersion:
|
||||
privateKeyFile:
|
||||
sniCertKey:
|
||||
|
||||
zmon:
|
||||
kariosdbEndpoint:
|
||||
tokenName:
|
||||
|
||||
resources:
|
||||
limits:
|
||||
cpu: 100m
|
||||
memory: 100Mi
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 100Mi
|
@ -6,7 +6,7 @@ metadata:
|
||||
labels:
|
||||
application: custom-metrics-consumer
|
||||
annotations:
|
||||
# metric-config.<metricType>.<metricName>.<collectorName>/<configKey>
|
||||
# metric-config.<metricType>.<metricName>.<collectorType>/<configKey>
|
||||
metric-config.pods.queue-length.json-path/json-key: "$.queue.length"
|
||||
metric-config.pods.queue-length.json-path/path: /metrics
|
||||
metric-config.pods.queue-length.json-path/port: "9090"
|
||||
@ -50,9 +50,10 @@ spec:
|
||||
- type: External
|
||||
external:
|
||||
metric:
|
||||
name: sqs-queue-length
|
||||
name: app-queue-length
|
||||
selector:
|
||||
matchLabels:
|
||||
type: sqs-queue-length
|
||||
queue-name: foobar
|
||||
region: eu-central-1
|
||||
target:
|
||||
|
43
go.mod
43
go.mod
@ -2,31 +2,32 @@ module github.com/zalando-incubator/kube-metrics-adapter
|
||||
|
||||
require (
|
||||
github.com/NYTimes/gziphandler v1.0.1 // indirect
|
||||
github.com/aws/aws-sdk-go v1.30.24
|
||||
github.com/googleapis/gnostic v0.2.0 // indirect
|
||||
github.com/influxdata/influxdb-client-go v0.1.5
|
||||
github.com/kubernetes-incubator/custom-metrics-apiserver v0.0.0-20200323093244-5046ce1afe6b
|
||||
github.com/lib/pq v1.2.0 // indirect
|
||||
github.com/aws/aws-sdk-go v1.37.1
|
||||
github.com/go-openapi/spec v0.20.2
|
||||
github.com/influxdata/influxdb-client-go v0.2.0
|
||||
github.com/influxdata/line-protocol v0.0.0-20201012155213-5f565037cbc9 // indirect
|
||||
github.com/kubernetes-sigs/custom-metrics-apiserver v0.0.0-20201216091021-1b9fa998bbaa
|
||||
github.com/mattn/go-colorable v0.1.4 // indirect
|
||||
github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852
|
||||
github.com/onsi/ginkgo v1.11.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.10 // indirect
|
||||
github.com/onsi/gomega v1.8.1 // indirect
|
||||
github.com/prometheus/client_golang v1.6.0
|
||||
github.com/prometheus/common v0.9.1
|
||||
github.com/sirupsen/logrus v1.6.0
|
||||
github.com/prometheus/client_golang v1.9.0
|
||||
github.com/prometheus/common v0.15.0
|
||||
github.com/sirupsen/logrus v1.7.0
|
||||
github.com/spf13/cobra v0.0.7
|
||||
github.com/stretchr/testify v1.5.1
|
||||
github.com/spyzhov/ajson v0.4.2
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/zalando-incubator/cluster-lifecycle-manager v0.0.0-20180921141935-824b77fb1f84
|
||||
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
|
||||
golang.org/x/tools v0.0.0-20200204192400-7124308813f3 // indirect
|
||||
honnef.co/go/tools v0.0.1-2020.1.3 // indirect
|
||||
k8s.io/api v0.17.3
|
||||
k8s.io/apimachinery v0.17.4
|
||||
k8s.io/client-go v0.17.3
|
||||
k8s.io/component-base v0.17.3
|
||||
golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
k8s.io/api v0.20.0
|
||||
k8s.io/apimachinery v0.20.0
|
||||
k8s.io/apiserver v0.20.0
|
||||
k8s.io/client-go v0.20.0
|
||||
k8s.io/component-base v0.20.0
|
||||
k8s.io/klog v1.0.0
|
||||
k8s.io/metrics v0.17.3
|
||||
k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd
|
||||
k8s.io/metrics v0.20.0
|
||||
)
|
||||
|
||||
go 1.13
|
||||
go 1.15
|
||||
|
16
hack/boilerplate.go.txt
Normal file
16
hack/boilerplate.go.txt
Normal file
@ -0,0 +1,16 @@
|
||||
/*
|
||||
Copyright 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.
|
||||
*/
|
||||
|
6
hack/tools.go
Normal file
6
hack/tools.go
Normal file
@ -0,0 +1,6 @@
|
||||
// +build tools
|
||||
|
||||
// This package imports things required by build scripts, to force `go mod` to see them as dependencies
|
||||
package tools
|
||||
|
||||
import _ "k8s.io/kube-openapi/cmd/openapi-gen"
|
@ -15,7 +15,7 @@ const (
|
||||
)
|
||||
|
||||
type AnnotationConfigs struct {
|
||||
CollectorName string
|
||||
CollectorType string
|
||||
Configs map[string]string
|
||||
PerReplica bool
|
||||
Interval time.Duration
|
||||
@ -64,14 +64,14 @@ func (m AnnotationConfigMap) Parse(annotations map[string]string) error {
|
||||
config, ok := m[key]
|
||||
if !ok {
|
||||
config = &AnnotationConfigs{
|
||||
CollectorName: metricCollector,
|
||||
CollectorType: metricCollector,
|
||||
Configs: map[string]string{},
|
||||
}
|
||||
m[key] = config
|
||||
}
|
||||
|
||||
// TODO: fail if collector name doesn't match
|
||||
if config.CollectorName != metricCollector {
|
||||
if config.CollectorType != metricCollector {
|
||||
continue
|
||||
}
|
||||
|
||||
|
14904
pkg/api/generated/openapi/zz_generated.openapi.go
Normal file
14904
pkg/api/generated/openapi/zz_generated.openapi.go
Normal file
File diff suppressed because it is too large
Load Diff
@ -33,12 +33,7 @@ func NewAWSCollectorPlugin(sessions map[string]*session.Session) *AWSCollectorPl
|
||||
|
||||
// NewCollector initializes a new skipper collector from the specified HPA.
|
||||
func (c *AWSCollectorPlugin) NewCollector(hpa *autoscalingv2.HorizontalPodAutoscaler, config *MetricConfig, interval time.Duration) (Collector, error) {
|
||||
switch config.Metric.Name {
|
||||
case AWSSQSQueueLengthMetric:
|
||||
return NewAWSSQSCollector(c.sessions, config, interval)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("metric '%s' not supported", config.Metric.Name)
|
||||
return NewAWSSQSCollector(c.sessions, hpa, config, interval)
|
||||
}
|
||||
|
||||
type AWSSQSCollector struct {
|
||||
@ -47,11 +42,12 @@ type AWSSQSCollector struct {
|
||||
region string
|
||||
queueURL string
|
||||
queueName string
|
||||
namespace string
|
||||
metric autoscalingv2.MetricIdentifier
|
||||
metricType autoscalingv2.MetricSourceType
|
||||
}
|
||||
|
||||
func NewAWSSQSCollector(sessions map[string]*session.Session, config *MetricConfig, interval time.Duration) (*AWSSQSCollector, error) {
|
||||
func NewAWSSQSCollector(sessions map[string]*session.Session, hpa *autoscalingv2.HorizontalPodAutoscaler, config *MetricConfig, interval time.Duration) (*AWSSQSCollector, error) {
|
||||
if config.Metric.Selector == nil {
|
||||
return nil, fmt.Errorf("selector for queue is not specified")
|
||||
}
|
||||
@ -85,6 +81,7 @@ func NewAWSSQSCollector(sessions map[string]*session.Session, config *MetricConf
|
||||
interval: interval,
|
||||
queueURL: aws.StringValue(resp.QueueUrl),
|
||||
queueName: name,
|
||||
namespace: hpa.Namespace,
|
||||
metric: config.Metric,
|
||||
metricType: config.Type,
|
||||
}, nil
|
||||
@ -108,7 +105,8 @@ func (c *AWSSQSCollector) GetMetrics() ([]CollectedMetric, error) {
|
||||
}
|
||||
|
||||
metricValue := CollectedMetric{
|
||||
Type: c.metricType,
|
||||
Namespace: c.namespace,
|
||||
Type: c.metricType,
|
||||
External: external_metrics.ExternalMetricValue{
|
||||
MetricName: c.metric.Name,
|
||||
MetricLabels: c.metric.Selector.MatchLabels,
|
||||
|
@ -4,12 +4,17 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/zalando-incubator/kube-metrics-adapter/pkg/annotations"
|
||||
autoscalingv2 "k8s.io/api/autoscaling/v2beta2"
|
||||
"k8s.io/metrics/pkg/apis/custom_metrics"
|
||||
"k8s.io/metrics/pkg/apis/external_metrics"
|
||||
)
|
||||
|
||||
const (
|
||||
typeLabelKey = "type"
|
||||
)
|
||||
|
||||
type ObjectReference struct {
|
||||
autoscalingv2.CrossVersionObjectReference
|
||||
Namespace string
|
||||
@ -19,6 +24,7 @@ type CollectorFactory struct {
|
||||
podsPlugins pluginMap
|
||||
objectPlugins objectPluginMap
|
||||
externalPlugins map[string]CollectorPlugin
|
||||
logger *log.Entry
|
||||
}
|
||||
|
||||
type objectPluginMap struct {
|
||||
@ -39,6 +45,7 @@ func NewCollectorFactory() *CollectorFactory {
|
||||
Named: map[string]*pluginMap{},
|
||||
},
|
||||
externalPlugins: map[string]CollectorPlugin{},
|
||||
logger: log.WithFields(log.Fields{"collector": "true"}),
|
||||
}
|
||||
}
|
||||
|
||||
@ -112,7 +119,7 @@ func (c *CollectorFactory) NewCollector(hpa *autoscalingv2.HorizontalPodAutoscal
|
||||
switch config.Type {
|
||||
case autoscalingv2.PodsMetricSourceType:
|
||||
// first try to find a plugin by format
|
||||
if plugin, ok := c.podsPlugins.Named[config.CollectorName]; ok {
|
||||
if plugin, ok := c.podsPlugins.Named[config.CollectorType]; ok {
|
||||
return plugin.NewCollector(hpa, config, interval)
|
||||
}
|
||||
|
||||
@ -123,7 +130,7 @@ func (c *CollectorFactory) NewCollector(hpa *autoscalingv2.HorizontalPodAutoscal
|
||||
case autoscalingv2.ObjectMetricSourceType:
|
||||
// first try to find a plugin by kind
|
||||
if kinds, ok := c.objectPlugins.Named[config.ObjectReference.Kind]; ok {
|
||||
if plugin, ok := kinds.Named[config.CollectorName]; ok {
|
||||
if plugin, ok := kinds.Named[config.CollectorType]; ok {
|
||||
return plugin.NewCollector(hpa, config, interval)
|
||||
}
|
||||
|
||||
@ -134,7 +141,7 @@ func (c *CollectorFactory) NewCollector(hpa *autoscalingv2.HorizontalPodAutoscal
|
||||
}
|
||||
|
||||
// else try to find a default plugin for this kind
|
||||
if plugin, ok := c.objectPlugins.Any.Named[config.CollectorName]; ok {
|
||||
if plugin, ok := c.objectPlugins.Any.Named[config.CollectorType]; ok {
|
||||
return plugin.NewCollector(hpa, config, interval)
|
||||
}
|
||||
|
||||
@ -142,7 +149,21 @@ func (c *CollectorFactory) NewCollector(hpa *autoscalingv2.HorizontalPodAutoscal
|
||||
return c.objectPlugins.Any.Any.NewCollector(hpa, config, interval)
|
||||
}
|
||||
case autoscalingv2.ExternalMetricSourceType:
|
||||
if plugin, ok := c.externalPlugins[config.Metric.Name]; ok {
|
||||
// First type to get metric type from the `type` label,
|
||||
// otherwise fall back to the legacy metric name based mapping.
|
||||
var pluginKey string
|
||||
if config.Metric.Selector != nil && config.Metric.Selector.MatchLabels != nil {
|
||||
if typ, ok := config.Metric.Selector.MatchLabels[typeLabelKey]; ok {
|
||||
pluginKey = typ
|
||||
}
|
||||
}
|
||||
|
||||
if pluginKey == "" {
|
||||
pluginKey = config.Metric.Name
|
||||
c.logger.Warnf("HPA %s/%s is using deprecated metric type identifier '%s'", hpa.Namespace, hpa.Name, config.Metric.Name)
|
||||
}
|
||||
|
||||
if plugin, ok := c.externalPlugins[pluginKey]; ok {
|
||||
return plugin.NewCollector(hpa, config, interval)
|
||||
}
|
||||
}
|
||||
@ -156,9 +177,10 @@ type MetricTypeName struct {
|
||||
}
|
||||
|
||||
type CollectedMetric struct {
|
||||
Type autoscalingv2.MetricSourceType
|
||||
Custom custom_metrics.MetricValue
|
||||
External external_metrics.ExternalMetricValue
|
||||
Type autoscalingv2.MetricSourceType
|
||||
Namespace string
|
||||
Custom custom_metrics.MetricValue
|
||||
External external_metrics.ExternalMetricValue
|
||||
}
|
||||
|
||||
type Collector interface {
|
||||
@ -168,7 +190,7 @@ type Collector interface {
|
||||
|
||||
type MetricConfig struct {
|
||||
MetricTypeName
|
||||
CollectorName string
|
||||
CollectorType string
|
||||
Config map[string]string
|
||||
ObjectReference custom_metrics.ObjectReference
|
||||
PerReplica bool
|
||||
@ -228,7 +250,7 @@ func ParseHPAMetrics(hpa *autoscalingv2.HorizontalPodAutoscaler) ([]*MetricConfi
|
||||
|
||||
annotationConfigs, present := parser.GetAnnotationConfig(typeName.Metric.Name, typeName.Type)
|
||||
if present {
|
||||
config.CollectorName = annotationConfigs.CollectorName
|
||||
config.CollectorType = annotationConfigs.CollectorType
|
||||
config.Interval = annotationConfigs.Interval
|
||||
config.PerReplica = annotationConfigs.PerReplica
|
||||
// configs specified in annotations takes precedence
|
||||
|
129
pkg/collector/collector_test.go
Normal file
129
pkg/collector/collector_test.go
Normal file
@ -0,0 +1,129 @@
|
||||
package collector
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
autoscalingv2 "k8s.io/api/autoscaling/v2beta2"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type mockCollectorPlugin struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (c *mockCollectorPlugin) NewCollector(hpa *autoscalingv2.HorizontalPodAutoscaler, config *MetricConfig, interval time.Duration) (Collector, error) {
|
||||
return &mockCollector{Name: c.Name}, nil
|
||||
}
|
||||
|
||||
type mockCollector struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (c *mockCollector) GetMetrics() ([]CollectedMetric, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c *mockCollector) Interval() time.Duration {
|
||||
return 0
|
||||
}
|
||||
|
||||
func TestNewCollector(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
msg string
|
||||
hpa *autoscalingv2.HorizontalPodAutoscaler
|
||||
expectedCollector string
|
||||
}{
|
||||
{
|
||||
msg: "should get create collector type from legacy metric name",
|
||||
hpa: &autoscalingv2.HorizontalPodAutoscaler{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{},
|
||||
},
|
||||
Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
|
||||
Metrics: []autoscalingv2.MetricSpec{
|
||||
{
|
||||
Type: autoscalingv2.ExternalMetricSourceType,
|
||||
External: &autoscalingv2.ExternalMetricSource{
|
||||
Metric: autoscalingv2.MetricIdentifier{
|
||||
Name: "external-1",
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{"x": "y"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedCollector: "external-1",
|
||||
},
|
||||
{
|
||||
msg: "should get create collector type from type label (ignore legacy metric name)",
|
||||
hpa: &autoscalingv2.HorizontalPodAutoscaler{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{},
|
||||
},
|
||||
Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
|
||||
Metrics: []autoscalingv2.MetricSpec{
|
||||
{
|
||||
Type: autoscalingv2.ExternalMetricSourceType,
|
||||
External: &autoscalingv2.ExternalMetricSource{
|
||||
Metric: autoscalingv2.MetricIdentifier{
|
||||
Name: "external-1",
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{"type": "external-2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedCollector: "external-2",
|
||||
},
|
||||
{
|
||||
msg: "should not find collector when no collector matches",
|
||||
hpa: &autoscalingv2.HorizontalPodAutoscaler{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{},
|
||||
},
|
||||
Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
|
||||
Metrics: []autoscalingv2.MetricSpec{
|
||||
{
|
||||
Type: autoscalingv2.ExternalMetricSourceType,
|
||||
External: &autoscalingv2.ExternalMetricSource{
|
||||
Metric: autoscalingv2.MetricIdentifier{
|
||||
Name: "external-3",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedCollector: "",
|
||||
},
|
||||
} {
|
||||
t.Run(tc.msg, func(t *testing.T) {
|
||||
collectorFactory := NewCollectorFactory()
|
||||
for _, collector := range []string{"1", "2"} {
|
||||
collectorFactory.RegisterExternalCollector([]string{"external-" + collector}, &mockCollectorPlugin{Name: "external-" + collector})
|
||||
}
|
||||
configs, err := ParseHPAMetrics(tc.hpa)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, configs, 1)
|
||||
|
||||
collector, err := collectorFactory.NewCollector(tc.hpa, configs[0], 0)
|
||||
if tc.expectedCollector == "" {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
|
||||
c, ok := collector.(*mockCollector)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, tc.expectedCollector, c.Name)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -7,7 +7,6 @@ import (
|
||||
|
||||
"github.com/zalando-incubator/kube-metrics-adapter/pkg/collector/httpmetrics"
|
||||
|
||||
"github.com/oliveagle/jsonpath"
|
||||
"k8s.io/api/autoscaling/v2beta2"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@ -15,7 +14,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
HTTPMetricName = "http"
|
||||
HTTPJSONPathType = "json-path"
|
||||
HTTPMetricNameLegacy = "http"
|
||||
HTTPEndpointAnnotationKey = "endpoint"
|
||||
HTTPJsonPathAnnotationKey = "json-key"
|
||||
identifierLabel = "identifier"
|
||||
@ -27,8 +27,10 @@ func NewHTTPCollectorPlugin() (*HTTPCollectorPlugin, error) {
|
||||
return &HTTPCollectorPlugin{}, nil
|
||||
}
|
||||
|
||||
func (p *HTTPCollectorPlugin) NewCollector(_ *v2beta2.HorizontalPodAutoscaler, config *MetricConfig, interval time.Duration) (Collector, error) {
|
||||
collector := &HTTPCollector{}
|
||||
func (p *HTTPCollectorPlugin) NewCollector(hpa *v2beta2.HorizontalPodAutoscaler, config *MetricConfig, interval time.Duration) (Collector, error) {
|
||||
collector := &HTTPCollector{
|
||||
namespace: hpa.Namespace,
|
||||
}
|
||||
var (
|
||||
value string
|
||||
ok bool
|
||||
@ -36,14 +38,12 @@ func (p *HTTPCollectorPlugin) NewCollector(_ *v2beta2.HorizontalPodAutoscaler, c
|
||||
if value, ok = config.Config[HTTPJsonPathAnnotationKey]; !ok {
|
||||
return nil, fmt.Errorf("config value %s not found", HTTPJsonPathAnnotationKey)
|
||||
}
|
||||
jsonPath, err := jsonpath.Compile(value)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse json path: %v", err)
|
||||
}
|
||||
collector.jsonPath = jsonPath
|
||||
jsonPath := value
|
||||
|
||||
if value, ok = config.Config[HTTPEndpointAnnotationKey]; !ok {
|
||||
return nil, fmt.Errorf("config value %s not found", HTTPEndpointAnnotationKey)
|
||||
}
|
||||
var err error
|
||||
collector.endpoint, err = url.Parse(value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -65,14 +65,18 @@ func (p *HTTPCollectorPlugin) NewCollector(_ *v2beta2.HorizontalPodAutoscaler, c
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
collector.metricsGetter = httpmetrics.NewJSONPathMetricsGetter(httpmetrics.DefaultMetricsHTTPClient(), aggFunc, jsonPath)
|
||||
jsonPathGetter, err := httpmetrics.NewJSONPathMetricsGetter(httpmetrics.DefaultMetricsHTTPClient(), aggFunc, jsonPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
collector.metricsGetter = jsonPathGetter
|
||||
return collector, nil
|
||||
}
|
||||
|
||||
type HTTPCollector struct {
|
||||
endpoint *url.URL
|
||||
jsonPath *jsonpath.Compiled
|
||||
interval time.Duration
|
||||
namespace string
|
||||
metricType v2beta2.MetricSourceType
|
||||
metricsGetter *httpmetrics.JSONPathMetricsGetter
|
||||
metric v2beta2.MetricIdentifier
|
||||
@ -85,7 +89,8 @@ func (c *HTTPCollector) GetMetrics() ([]CollectedMetric, error) {
|
||||
}
|
||||
|
||||
value := CollectedMetric{
|
||||
Type: c.metricType,
|
||||
Namespace: c.namespace,
|
||||
Type: c.metricType,
|
||||
External: external_metrics.ExternalMetricValue{
|
||||
MetricName: c.metric.Name,
|
||||
MetricLabels: c.metric.Selector.MatchLabels,
|
||||
|
@ -8,6 +8,8 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/api/autoscaling/v2beta2"
|
||||
autoscalingv2 "k8s.io/api/autoscaling/v2beta2"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
@ -60,7 +62,12 @@ func TestHTTPCollector(t *testing.T) {
|
||||
plugin, err := NewHTTPCollectorPlugin()
|
||||
require.NoError(t, err)
|
||||
testConfig := makeTestHTTPCollectorConfig(testServer, tc.aggregator)
|
||||
collector, err := plugin.NewCollector(nil, testConfig, testInterval)
|
||||
hpa := &autoscalingv2.HorizontalPodAutoscaler{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "default",
|
||||
},
|
||||
}
|
||||
collector, err := plugin.NewCollector(hpa, testConfig, testInterval)
|
||||
require.NoError(t, err)
|
||||
metrics, err := collector.GetMetrics()
|
||||
require.NoError(t, err)
|
||||
|
@ -1,7 +1,6 @@
|
||||
package httpmetrics
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
@ -9,21 +8,26 @@ import (
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/oliveagle/jsonpath"
|
||||
"github.com/spyzhov/ajson"
|
||||
)
|
||||
|
||||
// JSONPathMetricsGetter is a metrics getter which looks up pod metrics by
|
||||
// querying the pods metrics endpoint and lookup the metric value as defined by
|
||||
// the json path query.
|
||||
type JSONPathMetricsGetter struct {
|
||||
jsonPath *jsonpath.Compiled
|
||||
jsonPath string
|
||||
aggregator AggregatorFunc
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
// NewJSONPathMetricsGetter initializes a new JSONPathMetricsGetter.
|
||||
func NewJSONPathMetricsGetter(httpClient *http.Client, aggregatorFunc AggregatorFunc, compiledPath *jsonpath.Compiled) *JSONPathMetricsGetter {
|
||||
return &JSONPathMetricsGetter{client: httpClient, aggregator: aggregatorFunc, jsonPath: compiledPath}
|
||||
func NewJSONPathMetricsGetter(httpClient *http.Client, aggregatorFunc AggregatorFunc, jsonPath string) (*JSONPathMetricsGetter, error) {
|
||||
// check that jsonPath parses
|
||||
_, err := ajson.ParseJSONPath(jsonPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &JSONPathMetricsGetter{client: httpClient, aggregator: aggregatorFunc, jsonPath: jsonPath}, nil
|
||||
}
|
||||
|
||||
var DefaultRequestTimeout = 15 * time.Second
|
||||
@ -58,57 +62,45 @@ func (g *JSONPathMetricsGetter) GetMetric(metricsURL url.URL) (float64, error) {
|
||||
}
|
||||
|
||||
// parse data
|
||||
var jsonData interface{}
|
||||
err = json.Unmarshal(data, &jsonData)
|
||||
root, err := ajson.Unmarshal(data)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
res, err := g.jsonPath.Lookup(jsonData)
|
||||
nodes, err := root.JSONPath(g.jsonPath)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
switch res := res.(type) {
|
||||
case int:
|
||||
return float64(res), nil
|
||||
case float32:
|
||||
return float64(res), nil
|
||||
case float64:
|
||||
return res, nil
|
||||
case []interface{}:
|
||||
if len(nodes) != 1 {
|
||||
return 0, fmt.Errorf("unexpected json: expected single numeric or array value")
|
||||
}
|
||||
|
||||
node := nodes[0]
|
||||
if node.IsArray() {
|
||||
if g.aggregator == nil {
|
||||
return 0, fmt.Errorf("no aggregator function has been specified")
|
||||
}
|
||||
s, err := castSlice(res)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return g.aggregator(s...), nil
|
||||
default:
|
||||
return 0, fmt.Errorf("unsupported type %T", res)
|
||||
}
|
||||
}
|
||||
|
||||
// castSlice takes a slice of interface and returns a slice of float64 if all
|
||||
// values in slice were castable, else returns an error
|
||||
func castSlice(in []interface{}) ([]float64, error) {
|
||||
var out []float64
|
||||
|
||||
for _, v := range in {
|
||||
switch v := v.(type) {
|
||||
case int:
|
||||
out = append(out, float64(v))
|
||||
case float32:
|
||||
out = append(out, float64(v))
|
||||
case float64:
|
||||
out = append(out, v)
|
||||
default:
|
||||
return nil, fmt.Errorf("slice was returned by JSONPath, but value inside is unsupported: %T", v)
|
||||
values := make([]float64, 0, len(nodes))
|
||||
items, _ := node.GetArray()
|
||||
for _, item := range items {
|
||||
value, err := item.GetNumeric()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("did not find numeric type: %w", err)
|
||||
}
|
||||
values = append(values, value)
|
||||
}
|
||||
return g.aggregator(values...), nil
|
||||
} else if node.IsNumeric() {
|
||||
res, _ := node.GetNumeric()
|
||||
return res, nil
|
||||
}
|
||||
|
||||
return out, nil
|
||||
value, err := node.Value()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to check value of jsonPath result: %w", err)
|
||||
}
|
||||
return 0, fmt.Errorf("unsupported type %T", value)
|
||||
}
|
||||
|
||||
func (g *JSONPathMetricsGetter) fetchMetrics(metricsURL url.URL) ([]byte, error) {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user