Compare commits

...

50 Commits

Author SHA1 Message Date
Mikkel Oscar Lyderik Larsen 8d7dc13bbd Update dependencies
Signed-off-by: Mikkel Oscar Lyderik Larsen <mikkel.larsen@zalando.de>
2020-11-02 09:04:08 +01:00
Mikkel Oscar Lyderik Larsen 20f0042017 Merge pull request #217 from zalando-incubator/fix-openapi
Fix openapi serving
2020-10-27 11:27:07 +01:00
Mikkel Oscar Lyderik Larsen c732851b6b Fix openapi serving
Signed-off-by: Mikkel Oscar Lyderik Larsen <mikkel.larsen@zalando.de>
2020-10-27 10:43:16 +01:00
Muaaz Saleem 2e09132f95 [prom collector ] Documenting scrape interval (#215)
There is a way to configure scrape interval

Signed-off-by: Muaaz Saleem <muhammad.muaaz.saleem@zalando.de>
2020-10-26 17:37:24 +01:00
dependabot[bot] 95f22dc398 Bump github.com/aws/aws-sdk-go from 1.35.0 to 1.35.13 (#212)
Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.35.0 to 1.35.13.
- [Release notes](https://github.com/aws/aws-sdk-go/releases)
- [Changelog](https://github.com/aws/aws-sdk-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-go/compare/v1.35.0...v1.35.13)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-10-26 10:31:24 +01:00
Mikkel Oscar Lyderik Larsen eed5c78fb3 Use upstream version of custom-metrics-apiserver (#211)
Signed-off-by: Mikkel Oscar Lyderik Larsen <mikkel.larsen@zalando.de>
2020-10-23 10:51:10 +02:00
Mikkel Oscar Lyderik Larsen e61071a36d Merge pull request #164 from zalando-incubator/openapi-fork
Use OpenAPI spec fork
2020-10-06 22:30:46 +02:00
Mikkel Oscar Lyderik Larsen c108fab55a Use OpenAPI spec fork
Signed-off-by: Mikkel Oscar Lyderik Larsen <mikkel.larsen@zalando.de>
2020-10-02 11:06:21 +02:00
Mikkel Oscar Lyderik Larsen be7567efea Fix handling of invalid/empty json response (#209)
Signed-off-by: Mikkel Oscar Lyderik Larsen <mikkel.larsen@zalando.de>
2020-10-02 10:57:53 +02:00
Mikkel Oscar Lyderik Larsen b677e814be Update dependencies (#200)
Signed-off-by: Mikkel Oscar Lyderik Larsen <mikkel.larsen@zalando.de>
2020-10-02 10:55:42 +02:00
Mikkel Oscar Lyderik Larsen deec4727ee Merge pull request #192 from zalando-incubator/extend-jsonpath-support
Support bracket notation in jsonPath
2020-09-30 12:18:39 +02:00
Mikkel Oscar Lyderik Larsen cf369639d8 Support bracket notation in jsonPath
Signed-off-by: Mikkel Oscar Lyderik Larsen <mikkel.larsen@zalando.de>
2020-08-13 09:51:05 +02:00
Rodrigo Reis 262a35c2ec Merge pull request #177 from zalando-incubator/update-deps
Update dependencies
2020-07-15 17:45:01 +02:00
Mikkel Oscar Lyderik Larsen 607386834b Update dependencies
Signed-off-by: Mikkel Oscar Lyderik Larsen <mikkel.larsen@zalando.de>
2020-07-15 17:00:13 +02:00
Mikkel Oscar Lyderik Larsen 4bdce4da4d Merge pull request #176 from zalando-incubator/ingress-weights-float64
Correctly treat ingress weight values as float64
2020-07-15 16:56:33 +02:00
Mikkel Oscar Lyderik Larsen 801e5d7a47 Correctly treat ingress weight values as float64
Signed-off-by: Mikkel Oscar Lyderik Larsen <mikkel.larsen@zalando.de>
2020-07-15 15:42:59 +02:00
Mikkel Oscar Lyderik Larsen 4df21ae2b4 Merge pull request #174 from zalando-incubator/dependabot/add-v2-config-file
Create Dependabot config file
2020-07-10 13:31:55 +02:00
dependabot-preview[bot] 7f639baeee Create Dependabot config file 2020-07-10 09:49:34 +00:00
Rodrigo Reis 88ddb6f10e Merge pull request #173 from zalando-incubator/arjun-has-left-the-zalando
Update MAINTAINERS
2020-07-01 15:14:25 +02:00
Arjun Naik d9cf2e0858 Update MAINTAINERS
Signed-off-by: Arjun Naik <arjun@arjunnaik.in>
2020-07-01 15:04:31 +02:00
Mikkel Oscar Lyderik Larsen c3b18e784b Update client-go (#162)
Signed-off-by: Mikkel Oscar Lyderik Larsen <mikkel.larsen@zalando.de>
2020-06-27 18:39:12 +02:00
Mikkel Oscar Lyderik Larsen c3a03fd758 Merge pull request #157 from zalando-incubator/dependabot/go_modules/github.com/aws/aws-sdk-go-1.31.7
Bump github.com/aws/aws-sdk-go from 1.30.24 to 1.31.7
2020-06-02 09:43:49 +02:00
Mikkel Oscar Lyderik Larsen 05704c0a6b Merge pull request #158 from zalando-incubator/dependabot/go_modules/github.com/stretchr/testify-1.6.0
Bump github.com/stretchr/testify from 1.5.1 to 1.6.0
2020-06-02 09:43:27 +02:00
Mikkel Oscar Lyderik Larsen f3a608fcf7 Merge pull request #159 from zalando-incubator/dependabot/go_modules/github.com/prometheus/common-0.10.0
Bump github.com/prometheus/common from 0.9.1 to 0.10.0
2020-06-02 09:43:03 +02:00
dependabot-preview[bot] 223ec9fd89 Bump github.com/prometheus/common from 0.9.1 to 0.10.0
Bumps [github.com/prometheus/common](https://github.com/prometheus/common) from 0.9.1 to 0.10.0.
- [Release notes](https://github.com/prometheus/common/releases)
- [Commits](https://github.com/prometheus/common/compare/v0.9.1...v0.10.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-01 10:47:03 +00:00
dependabot-preview[bot] 81255aa956 Bump github.com/stretchr/testify from 1.5.1 to 1.6.0
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.5.1 to 1.6.0.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.5.1...v1.6.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-01 10:46:34 +00:00
dependabot-preview[bot] dac44965e3 Bump github.com/aws/aws-sdk-go from 1.30.24 to 1.31.7
Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.30.24 to 1.31.7.
- [Release notes](https://github.com/aws/aws-sdk-go/releases)
- [Changelog](https://github.com/aws/aws-sdk-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-go/compare/v1.30.24...v1.31.7)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-01 10:46:23 +00:00
Mikkel Oscar Lyderik Larsen dbed1570ba Merge pull request #156 from zalando-incubator/networking.k8s.io
Add support for Ingress of API Group networking.k8s.io
2020-05-26 15:09:18 +02:00
Mikkel Oscar Lyderik Larsen ef24244074 Add support for networking.k8s.io
Signed-off-by: Mikkel Oscar Lyderik Larsen <mikkel.larsen@zalando.de>
2020-05-26 14:47:46 +02:00
Arjun cadf2dff3c Merge pull request #153 from doyshinda/custom_timeouts
Add support for custom timeouts
2020-05-18 10:27:17 +02:00
Arjun db83268343 Merge pull request #150 from zalando-incubator/dependabot/go_modules/github.com/aws/aws-sdk-go-1.30.24
Bump github.com/aws/aws-sdk-go from 1.30.19 to 1.30.24
2020-05-18 10:25:50 +02:00
Mikkel Oscar Lyderik Larsen 78c2ef742b Merge pull request #152 from doyshinda/parallel_get_metrics
Fetch pod metrics in parallel
2020-05-18 09:26:14 +02:00
Abe Friesen f0b817629c Add support for custom timeouts
Allow configuration per metric for connect and request timeouts
when querying pods for JSON metrics.

Signed-off-by: Abe Friesen <2319792+doyshinda@users.noreply.github.com>
2020-05-16 07:15:58 -06:00
Abe Friesen 3e7b66070c Fetch pod metrics in parallel
Fetching metrics from pods sequentially, with a large number of
pods, can result in poor performance when some of those pods have
been terminated by the HPA in a normal scale down event.

Signed-off-by: Abe Friesen <2319792+doyshinda@users.noreply.github.com>
2020-05-16 06:47:13 -06:00
dependabot-preview[bot] f3d33b6132 Bump github.com/aws/aws-sdk-go from 1.30.19 to 1.30.24
Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.30.19 to 1.30.24.
- [Release notes](https://github.com/aws/aws-sdk-go/releases)
- [Changelog](https://github.com/aws/aws-sdk-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-go/compare/v1.30.19...v1.30.24)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-11 10:18:26 +00:00
Arjun 7b7af889fb Merge pull request #148 from zalando-incubator/dependabot/go_modules/github.com/aws/aws-sdk-go-1.30.19
Bump github.com/aws/aws-sdk-go from 1.30.14 to 1.30.19
2020-05-06 12:45:54 +02:00
Arjun 2bcaa80ce0 Merge pull request #147 from zalando-incubator/dependabot/go_modules/github.com/prometheus/client_golang-1.6.0
Bump github.com/prometheus/client_golang from 1.5.1 to 1.6.0
2020-05-06 12:45:46 +02:00
Arjun a2e6980b25 Merge pull request #146 from zalando-incubator/dependabot/go_modules/github.com/sirupsen/logrus-1.6.0
Bump github.com/sirupsen/logrus from 1.5.0 to 1.6.0
2020-05-06 12:45:23 +02:00
dependabot-preview[bot] f0783dcdd8 Bump github.com/aws/aws-sdk-go from 1.30.14 to 1.30.19
Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.30.14 to 1.30.19.
- [Release notes](https://github.com/aws/aws-sdk-go/releases)
- [Changelog](https://github.com/aws/aws-sdk-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-go/compare/v1.30.14...v1.30.19)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-04 09:59:27 +00:00
dependabot-preview[bot] 560fb40dae Bump github.com/prometheus/client_golang from 1.5.1 to 1.6.0
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.5.1 to 1.6.0.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/master/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.5.1...v1.6.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-04 09:58:05 +00:00
dependabot-preview[bot] 41229e370d Bump github.com/sirupsen/logrus from 1.5.0 to 1.6.0
Bumps [github.com/sirupsen/logrus](https://github.com/sirupsen/logrus) from 1.5.0 to 1.6.0.
- [Release notes](https://github.com/sirupsen/logrus/releases)
- [Changelog](https://github.com/sirupsen/logrus/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sirupsen/logrus/compare/v1.5.0...v1.6.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-04 09:57:46 +00:00
Arjun 338393c11e Merge pull request #145 from zalando-incubator/dependabot/go_modules/github.com/aws/aws-sdk-go-1.30.14
Bump github.com/aws/aws-sdk-go from 1.30.9 to 1.30.14
2020-05-02 21:28:27 +02:00
dependabot-preview[bot] 8e7d1bc0d6 Bump github.com/aws/aws-sdk-go from 1.30.9 to 1.30.14
Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.30.9 to 1.30.14.
- [Release notes](https://github.com/aws/aws-sdk-go/releases)
- [Changelog](https://github.com/aws/aws-sdk-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-go/compare/v1.30.9...v1.30.14)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-04-27 10:18:59 +00:00
Arjun dc7fb5875b Merge pull request #143 from zalando-incubator/dependabot/go_modules/github.com/aws/aws-sdk-go-1.30.9
Bump github.com/aws/aws-sdk-go from 1.30.7 to 1.30.9
2020-04-21 06:20:06 +02:00
dependabot-preview[bot] 8f70d3493e Bump github.com/aws/aws-sdk-go from 1.30.7 to 1.30.9
Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.30.7 to 1.30.9.
- [Release notes](https://github.com/aws/aws-sdk-go/releases)
- [Changelog](https://github.com/aws/aws-sdk-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-go/compare/v1.30.7...v1.30.9)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-04-20 10:00:12 +00:00
Gábor Lipták 97ec13d010 Update Travis to Go 1.14 (#140)
Signed-off-by: Gábor Lipták <gliptak@gmail.com>
2020-04-12 22:54:49 +02:00
Martin Linkhorst 5e20dcd961 Merge pull request #139 from zalando-incubator/dependabot/go_modules/github.com/aws/aws-sdk-go-1.30.7
Bump github.com/aws/aws-sdk-go from 1.30.3 to 1.30.7
2020-04-09 15:52:18 +02:00
dependabot-preview[bot] f27dbc3939 Bump github.com/aws/aws-sdk-go from 1.30.3 to 1.30.7
Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.30.3 to 1.30.7.
- [Release notes](https://github.com/aws/aws-sdk-go/releases)
- [Changelog](https://github.com/aws/aws-sdk-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-go/compare/v1.30.3...v1.30.7)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-04-09 08:48:10 +00:00
Mikkel Oscar Lyderik Larsen 6443613930 Merge pull request #135 from zalando-incubator/dependabot/go_modules/github.com/aws/aws-sdk-go-1.30.3
Bump github.com/aws/aws-sdk-go from 1.30.2 to 1.30.3
2020-04-03 12:35:00 +02:00
dependabot-preview[bot] d9c2e50f2c Bump github.com/aws/aws-sdk-go from 1.30.2 to 1.30.3
Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.30.2 to 1.30.3.
- [Release notes](https://github.com/aws/aws-sdk-go/releases)
- [Changelog](https://github.com/aws/aws-sdk-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-go/compare/v1.30.2...v1.30.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-04-03 08:48:22 +00:00
24 changed files with 14793 additions and 310 deletions
-35
View File
@@ -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
View 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
+1 -1
View File
@@ -2,7 +2,7 @@ language: go
dist: xenial
go:
- "1.13.x"
- "1.14.x"
env:
- GO111MODULE=on GOLANGCI_RELEASE="v1.21.0"
-1
View File
@@ -1,3 +1,2 @@
Mikkel Larsen <mikkel.larsen@zalando.de>
Arjun Naik <arjun.naik@zalando.de>
Team Teapot <team-teapot@zalando.de>
+17 -3
View File
@@ -8,12 +8,15 @@ 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)
@@ -22,17 +25,28 @@ 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
+19 -1
View File
@@ -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`.
@@ -166,6 +166,14 @@ will create a URL like this:
http://<podIP>:9090/metrics?foo=bar&baz=bop
```
There are also configuration options for custom (connect and request) timeouts when querying pods for metrics:
```yaml
metric-config.pods.requests-per-second.json-path/request-timeout: 2s
metric-config.pods.requests-per-second.json-path/connect-timeout: 500ms
```
The default for both of the above values is 15 seconds.
## Prometheus collector
The Prometheus collector is a generic collector which can map Prometheus
@@ -194,6 +202,16 @@ 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
+20 -15
View File
@@ -2,31 +2,36 @@ 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.2
github.com/aws/aws-sdk-go v1.35.19
github.com/go-openapi/spec v0.19.12
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/kubernetes-incubator/custom-metrics-apiserver v0.0.0-20201023074945-51cc7b53320e
github.com/lib/pq v1.2.0 // indirect
github.com/mailru/easyjson v0.7.6 // indirect
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/onsi/gomega v1.8.1 // indirect
github.com/prometheus/client_golang v1.5.1
github.com/prometheus/common v0.9.1
github.com/sirupsen/logrus v1.5.0
github.com/prometheus/client_golang v1.8.0
github.com/prometheus/common v0.14.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.6.1
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/net v0.0.0-20201031054903-ff519b6c9102 // indirect
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
golang.org/x/text v0.3.4 // indirect
golang.org/x/tools v0.0.0-20200204192400-7124308813f3 // indirect
gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e // 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
k8s.io/api v0.18.8
k8s.io/apimachinery v0.18.8
k8s.io/apiserver v0.18.8
k8s.io/client-go v0.18.8
k8s.io/component-base v0.18.8
k8s.io/klog v1.0.0
k8s.io/metrics v0.17.3
k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6
k8s.io/metrics v0.18.8
)
go 1.13
go 1.15
+308 -60
View File
File diff suppressed because it is too large Load Diff
+16
View 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
View 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"
File diff suppressed because it is too large Load Diff
+8 -8
View File
@@ -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"
@@ -36,14 +35,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,13 +62,16 @@ 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
metricType v2beta2.MetricSourceType
metricsGetter *httpmetrics.JSONPathMetricsGetter
+44 -45
View File
@@ -1,7 +1,6 @@
package httpmetrics
import (
"encoding/json"
"fmt"
"io/ioutil"
"net"
@@ -9,38 +8,50 @@ 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
}
func DefaultMetricsHTTPClient() *http.Client {
var DefaultRequestTimeout = 15 * time.Second
var DefaultConnectTimeout = 15 * time.Second
func CustomMetricsHTTPClient(requestTimeout time.Duration, connectTimeout time.Duration) *http.Client {
client := &http.Client{
Transport: &http.Transport{
DialContext: (&net.Dialer{
Timeout: 15 * time.Second,
Timeout: connectTimeout,
}).DialContext,
MaxIdleConns: 50,
IdleConnTimeout: 90 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
},
Timeout: 15 * time.Second,
Timeout: requestTimeout,
}
return client
}
func DefaultMetricsHTTPClient() *http.Client {
return CustomMetricsHTTPClient(DefaultRequestTimeout, DefaultConnectTimeout)
}
// GetMetric gets metric from pod by fetching json metrics from the pods metric
// endpoint and extracting the desired value using the specified json path
// query.
@@ -51,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) {
+48 -53
View File
@@ -1,59 +1,21 @@
package httpmetrics
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"testing"
"github.com/oliveagle/jsonpath"
"github.com/stretchr/testify/require"
)
func TestCastSlice(t *testing.T) {
res1, err1 := castSlice([]interface{}{1, 2, 3})
require.NoError(t, err1)
require.Equal(t, []float64{1.0, 2.0, 3.0}, res1)
res2, err2 := castSlice([]interface{}{float32(1.0), float32(2.0), float32(3.0)})
require.NoError(t, err2)
require.Equal(t, []float64{1.0, 2.0, 3.0}, res2)
res3, err3 := castSlice([]interface{}{float64(1.0), float64(2.0), float64(3.0)})
require.NoError(t, err3)
require.Equal(t, []float64{1.0, 2.0, 3.0}, res3)
res4, err4 := castSlice([]interface{}{1, 2, "some string"})
require.Errorf(t, err4, "slice was returned by JSONPath, but value inside is unsupported: %T", "string")
require.Equal(t, []float64(nil), res4)
}
type testValueResponse struct {
Value int64 `json:"value"`
}
type testValueArrayResponse struct {
Value []int64 `json:"value"`
}
func makeTestHTTPServer(t *testing.T, values ...int64) *httptest.Server {
func makeTestHTTPServer(t *testing.T, response []byte) *httptest.Server {
h := func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, r.URL.Path, "/metrics")
w.Header().Set("Content-Type", "application/json")
var (
response []byte
err error
)
if len(values) == 1 {
response, err = json.Marshal(testValueResponse{Value: values[0]})
require.NoError(t, err)
} else {
response, err = json.Marshal(testValueArrayResponse{Value: values})
require.NoError(t, err)
}
_, err = w.Write(response)
_, err := w.Write(response)
require.NoError(t, err)
}
return httptest.NewServer(http.HandlerFunc(h))
@@ -61,29 +23,62 @@ func makeTestHTTPServer(t *testing.T, values ...int64) *httptest.Server {
func TestJSONPathMetricsGetter(t *testing.T) {
for _, tc := range []struct {
name string
input []int64
output float64
aggregator AggregatorFunc
name string
jsonResponse []byte
jsonPath string
result float64
aggregator AggregatorFunc
err error
}{
{
name: "basic average",
input: []int64{3, 4, 5},
output: 4,
aggregator: Average,
name: "basic single value",
jsonResponse: []byte(`{"value":3}`),
jsonPath: "$.value",
result: 3,
aggregator: Average,
},
{
name: "basic average",
jsonResponse: []byte(`{"value":[3,4,5]}`),
jsonPath: "$.value",
result: 4,
aggregator: Average,
},
{
name: "dotted key",
jsonResponse: []byte(`{"metric.value":5}`),
jsonPath: "$['metric.value']",
result: 5,
aggregator: Average,
},
{
name: "json path not resulting in array or number should lead to error",
jsonResponse: []byte(`{"metric.value":5}`),
jsonPath: "$['invalid.metric.values']",
err: errors.New("unexpected json: expected single numeric or array value"),
},
{
name: "invalid json should error",
jsonResponse: []byte(`{`),
jsonPath: "$['invalid.metric.values']",
err: errors.New("unexpected end of file"),
},
} {
t.Run(tc.name, func(t *testing.T) {
server := makeTestHTTPServer(t, tc.input...)
server := makeTestHTTPServer(t, tc.jsonResponse)
defer server.Close()
path, err := jsonpath.Compile("$.value")
getter, err := NewJSONPathMetricsGetter(DefaultMetricsHTTPClient(), tc.aggregator, tc.jsonPath)
require.NoError(t, err)
getter := NewJSONPathMetricsGetter(DefaultMetricsHTTPClient(), tc.aggregator, path)
url, err := url.Parse(fmt.Sprintf("%s/metrics", server.URL))
require.NoError(t, err)
metric, err := getter.GetMetric(*url)
if tc.err != nil {
require.Error(t, err)
require.Equal(t, tc.err.Error(), err.Error())
return
}
require.NoError(t, err)
require.Equal(t, tc.output, metric)
require.Equal(t, tc.result, metric)
})
}
}
+34 -9
View File
@@ -4,8 +4,8 @@ import (
"fmt"
"net/url"
"strconv"
"time"
"github.com/oliveagle/jsonpath"
v1 "k8s.io/api/core/v1"
)
@@ -32,18 +32,13 @@ func (g PodMetricsJSONPathGetter) GetMetric(pod *v1.Pod) (float64, error) {
func NewPodMetricsJSONPathGetter(config map[string]string) (*PodMetricsJSONPathGetter, error) {
getter := PodMetricsJSONPathGetter{}
var (
jsonPath *jsonpath.Compiled
jsonPath string
aggregator AggregatorFunc
err error
)
if v, ok := config["json-key"]; ok {
path, err := jsonpath.Compile(v)
if err != nil {
return nil, fmt.Errorf("failed to parse json path definition: %v", err)
}
jsonPath = path
jsonPath = v
}
if v, ok := config["scheme"]; ok {
@@ -72,7 +67,37 @@ func NewPodMetricsJSONPathGetter(config map[string]string) (*PodMetricsJSONPathG
return nil, err
}
}
getter.metricGetter = NewJSONPathMetricsGetter(DefaultMetricsHTTPClient(), aggregator, jsonPath)
requestTimeout := DefaultRequestTimeout
connectTimeout := DefaultConnectTimeout
if v, ok := config["request-timeout"]; ok {
d, err := time.ParseDuration(v)
if err != nil {
return nil, err
}
if d < 0 {
return nil, fmt.Errorf("Invalid request-timeout config value: %s", v)
}
requestTimeout = d
}
if v, ok := config["connect-timeout"]; ok {
d, err := time.ParseDuration(v)
if err != nil {
return nil, err
}
if d < 0 {
return nil, fmt.Errorf("Invalid connect-timeout config value: %s", v)
}
connectTimeout = d
}
jsonPathGetter, err := NewJSONPathMetricsGetter(CustomMetricsHTTPClient(requestTimeout, connectTimeout), aggregator, jsonPath)
if err != nil {
return nil, err
}
getter.metricGetter = jsonPathGetter
return &getter, nil
}
+68 -11
View File
@@ -3,8 +3,8 @@ package httpmetrics
import (
"fmt"
"testing"
"time"
"github.com/oliveagle/jsonpath"
"github.com/stretchr/testify/require"
)
@@ -22,12 +22,11 @@ func TestNewPodJSONPathMetricsGetter(t *testing.T) {
"path": "/metrics",
"port": "9090",
}
jpath1, _ := jsonpath.Compile(configNoAggregator["json-key"])
getterNoAggregator, err1 := NewPodMetricsJSONPathGetter(configNoAggregator)
require.NoError(t, err1)
compareMetricsGetter(t, &PodMetricsJSONPathGetter{
metricGetter: &JSONPathMetricsGetter{jsonPath: jpath1},
metricGetter: &JSONPathMetricsGetter{jsonPath: configNoAggregator["json-key"]},
scheme: "http",
path: "/metrics",
port: 9090,
@@ -40,12 +39,11 @@ func TestNewPodJSONPathMetricsGetter(t *testing.T) {
"port": "9090",
"aggregator": "avg",
}
jpath2, _ := jsonpath.Compile(configAggregator["json-key"])
getterAggregator, err2 := NewPodMetricsJSONPathGetter(configAggregator)
require.NoError(t, err2)
compareMetricsGetter(t, &PodMetricsJSONPathGetter{
metricGetter: &JSONPathMetricsGetter{jsonPath: jpath2, aggregator: Average},
metricGetter: &JSONPathMetricsGetter{jsonPath: configAggregator["json-key"], aggregator: Average},
scheme: "http",
path: "/metrics",
port: 9090,
@@ -78,12 +76,11 @@ func TestNewPodJSONPathMetricsGetter(t *testing.T) {
"port": "9090",
"raw-query": "foo=bar&baz=bop",
}
jpath5, _ := jsonpath.Compile(configWithRawQuery["json-key"])
getterWithRawQuery, err5 := NewPodMetricsJSONPathGetter(configWithRawQuery)
require.NoError(t, err5)
compareMetricsGetter(t, &PodMetricsJSONPathGetter{
metricGetter: &JSONPathMetricsGetter{jsonPath: jpath5},
metricGetter: &JSONPathMetricsGetter{jsonPath: configWithRawQuery["json-key"]},
scheme: "http",
path: "/metrics",
port: 9090,
@@ -106,8 +103,6 @@ func TestBuildMetricsURL(t *testing.T) {
"port": port,
"raw-query": rawQuery,
}
_, err := jsonpath.Compile(configWithRawQuery["json-key"])
require.NoError(t, err)
getterWithRawQuery, err1 := NewPodMetricsJSONPathGetter(configWithRawQuery)
require.NoError(t, err1)
@@ -122,8 +117,6 @@ func TestBuildMetricsURL(t *testing.T) {
"path": path,
"port": port,
}
_, err2 := jsonpath.Compile(configWithNoQuery["json-key"])
require.NoError(t, err2)
getterWithNoQuery, err3 := NewPodMetricsJSONPathGetter(configWithNoQuery)
require.NoError(t, err3)
@@ -131,3 +124,67 @@ func TestBuildMetricsURL(t *testing.T) {
receivedURLNoQuery := getterWithNoQuery.buildMetricsURL(ip)
require.Equal(t, receivedURLNoQuery.String(), expectedURLNoQuery)
}
func TestCustomTimeouts(t *testing.T) {
scheme := "http"
port := "9090"
path := "/v1/test/"
// Test no custom options results in default timeouts
defaultConfig := map[string]string{
"json-key": "$.value",
"scheme": scheme,
"path": path,
"port": port,
}
defaultTime := time.Duration(15000) * time.Millisecond
defaultGetter, err1 := NewPodMetricsJSONPathGetter(defaultConfig)
require.NoError(t, err1)
require.Equal(t, defaultGetter.metricGetter.client.Timeout, defaultTime)
// Test with custom request timeout
configWithRequestTimeout := map[string]string{
"json-key": "$.value",
"scheme": scheme,
"path": path,
"port": port,
"request-timeout": "978ms",
}
exectedTimeout := time.Duration(978) * time.Millisecond
customRequestGetter, err2 := NewPodMetricsJSONPathGetter(configWithRequestTimeout)
require.NoError(t, err2)
require.Equal(t, customRequestGetter.metricGetter.client.Timeout, exectedTimeout)
// Test with custom connect timeout. Unfortunately, it seems there's no way to access the
// connect timeout of the client struct to actually verify it's set :/
configWithConnectTimeout := map[string]string{
"json-key": "$.value",
"scheme": scheme,
"path": path,
"port": port,
"connect-timeout": "512ms",
}
_, err3 := NewPodMetricsJSONPathGetter(configWithConnectTimeout)
require.NoError(t, err3)
configWithInvalidTimeout := map[string]string{
"json-key": "$.value",
"scheme": scheme,
"path": path,
"port": port,
"request-timeout": "-256ms",
}
_, err4 := NewPodMetricsJSONPathGetter(configWithInvalidTimeout)
require.Error(t, err4)
configWithInvalidTimeout = map[string]string{
"json-key": "$.value",
"scheme": scheme,
"path": path,
"port": port,
"connect-timeout": "-256ms",
}
_, err5 := NewPodMetricsJSONPathGetter(configWithInvalidTimeout)
require.Error(t, err5)
}
+39 -26
View File
@@ -1,6 +1,7 @@
package collector
import (
"context"
"fmt"
"net/http"
"time"
@@ -8,6 +9,7 @@ import (
log "github.com/sirupsen/logrus"
"github.com/zalando-incubator/kube-metrics-adapter/pkg/collector/httpmetrics"
autoscalingv2 "k8s.io/api/autoscaling/v2beta2"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
@@ -80,37 +82,25 @@ func (c *PodCollector) GetMetrics() ([]CollectedMetric, error) {
LabelSelector: labels.Set(c.podLabelSelector.MatchLabels).String(),
}
pods, err := c.client.CoreV1().Pods(c.namespace).List(opts)
pods, err := c.client.CoreV1().Pods(c.namespace).List(context.TODO(), opts)
if err != nil {
return nil, err
}
values := make([]CollectedMetric, 0, len(pods.Items))
// TODO: get metrics in parallel
ch := make(chan CollectedMetric)
errCh := make(chan error)
for _, pod := range pods.Items {
value, err := c.Getter.GetMetric(&pod)
if err != nil {
c.logger.Errorf("Failed to get metrics from pod '%s/%s': %v", pod.Namespace, pod.Name, err)
continue
}
go c.getPodMetric(pod, ch, errCh)
}
metricValue := CollectedMetric{
Type: c.metricType,
Custom: custom_metrics.MetricValue{
DescribedObject: custom_metrics.ObjectReference{
APIVersion: "v1",
Kind: "Pod",
Name: pod.Name,
Namespace: pod.Namespace,
},
Metric: custom_metrics.MetricIdentifier{Name: c.metric.Name, Selector: c.podLabelSelector},
Timestamp: metav1.Time{Time: time.Now().UTC()},
Value: *resource.NewMilliQuantity(int64(value*1000), resource.DecimalSI),
},
values := make([]CollectedMetric, 0, len(pods.Items))
for i := 0; i < len(pods.Items); i++ {
select {
case err := <-errCh:
c.logger.Error(err)
case resp := <-ch:
values = append(values, resp)
}
values = append(values, metricValue)
}
return values, nil
@@ -120,16 +110,39 @@ func (c *PodCollector) Interval() time.Duration {
return c.interval
}
func (c *PodCollector) getPodMetric(pod corev1.Pod, ch chan CollectedMetric, errCh chan error) {
value, err := c.Getter.GetMetric(&pod)
if err != nil {
errCh <- fmt.Errorf("Failed to get metrics from pod '%s/%s': %v", pod.Namespace, pod.Name, err)
return
}
ch <- CollectedMetric{
Type: c.metricType,
Custom: custom_metrics.MetricValue{
DescribedObject: custom_metrics.ObjectReference{
APIVersion: "v1",
Kind: "Pod",
Name: pod.Name,
Namespace: pod.Namespace,
},
Metric: custom_metrics.MetricIdentifier{Name: c.metric.Name, Selector: c.podLabelSelector},
Timestamp: metav1.Time{Time: time.Now().UTC()},
Value: *resource.NewMilliQuantity(int64(value*1000), resource.DecimalSI),
},
}
}
func getPodLabelSelector(client kubernetes.Interface, hpa *autoscalingv2.HorizontalPodAutoscaler) (*metav1.LabelSelector, error) {
switch hpa.Spec.ScaleTargetRef.Kind {
case "Deployment":
deployment, err := client.AppsV1().Deployments(hpa.Namespace).Get(hpa.Spec.ScaleTargetRef.Name, metav1.GetOptions{})
deployment, err := client.AppsV1().Deployments(hpa.Namespace).Get(context.TODO(), hpa.Spec.ScaleTargetRef.Name, metav1.GetOptions{})
if err != nil {
return nil, err
}
return deployment.Spec.Selector, nil
case "StatefulSet":
sts, err := client.AppsV1().StatefulSets(hpa.Namespace).Get(hpa.Spec.ScaleTargetRef.Name, metav1.GetOptions{})
sts, err := client.AppsV1().StatefulSets(hpa.Namespace).Get(context.TODO(), hpa.Spec.ScaleTargetRef.Name, metav1.GetOptions{})
if err != nil {
return nil, err
}
+10 -4
View File
@@ -1,11 +1,13 @@
package collector
import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"sync"
"testing"
"time"
@@ -55,7 +57,7 @@ func TestPodCollector(t *testing.T) {
for _, m := range metrics {
values = append(values, m.Custom.Value.Value())
}
require.Equal(t, tc.result, values)
require.ElementsMatch(t, tc.result, values)
})
}
}
@@ -68,9 +70,13 @@ type testMetricsHandler struct {
calledCounter uint
t *testing.T
metricsPath string
sync.RWMutex
}
func (h *testMetricsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h.Lock()
defer h.Unlock()
require.Equal(h.t, h.metricsPath, r.URL.Path)
require.Less(h.t, int(h.calledCounter), len(h.values))
response, err := json.Marshal(testMetricResponse{Values: h.values[h.calledCounter]})
@@ -110,7 +116,7 @@ func makeTestPods(t *testing.T, testServer string, metricName string, port strin
PodIP: testServer,
},
}
_, err := client.CoreV1().Pods(testNamespace).Create(testPod)
_, err := client.CoreV1().Pods(testNamespace).Create(context.TODO(), testPod, v1.CreateOptions{})
require.NoError(t, err)
}
}
@@ -124,7 +130,7 @@ func makeTestDeployment(t *testing.T, client kubernetes.Interface) *appsv1.Deplo
},
},
}
_, err := client.AppsV1().Deployments(testNamespace).Create(&deployment)
_, err := client.AppsV1().Deployments(testNamespace).Create(context.TODO(), &deployment, v1.CreateOptions{})
require.NoError(t, err)
return &deployment
@@ -144,7 +150,7 @@ func makeTestHPA(t *testing.T, client kubernetes.Interface) *autoscalingv2.Horiz
},
},
}
_, err := client.AutoscalingV2beta2().HorizontalPodAutoscalers("test-namespace").Create(hpa)
_, err := client.AutoscalingV2beta2().HorizontalPodAutoscalers("test-namespace").Create(context.TODO(), hpa, v1.CreateOptions{})
require.NoError(t, err)
return hpa
}
+14 -9
View File
@@ -1,6 +1,7 @@
package collector
import (
"context"
"encoding/json"
"errors"
"fmt"
@@ -87,16 +88,16 @@ func NewSkipperCollector(client kubernetes.Interface, plugin CollectorPlugin, hp
}, nil
}
func getAnnotationWeight(backendWeights string, backend string) float64 {
var weightsMap map[string]int
func getAnnotationWeight(backendWeights string, backend string) (float64, error) {
var weightsMap map[string]float64
err := json.Unmarshal([]byte(backendWeights), &weightsMap)
if err != nil {
return 0
return 0, err
}
if weight, ok := weightsMap[backend]; ok {
return float64(weight) / 100
return float64(weight) / 100, nil
}
return 0
return 0, nil
}
func getWeights(ingressAnnotations map[string]string, backendAnnotations []string, backend string) (float64, error) {
@@ -106,7 +107,11 @@ func getWeights(ingressAnnotations map[string]string, backendAnnotations []strin
for _, anno := range backendAnnotations {
if weightsMap, ok := ingressAnnotations[anno]; ok {
annotationsPresent = true
maxWeight = math.Max(maxWeight, getAnnotationWeight(weightsMap, backend))
weight, err := getAnnotationWeight(weightsMap, backend)
if err != nil {
return 0.0, err
}
maxWeight = math.Max(maxWeight, weight)
}
}
@@ -125,7 +130,7 @@ func getWeights(ingressAnnotations map[string]string, backendAnnotations []strin
// getCollector returns a collector for getting the metrics.
func (c *SkipperCollector) getCollector() (Collector, error) {
ingress, err := c.client.ExtensionsV1beta1().Ingresses(c.objectReference.Namespace).Get(c.objectReference.Name, metav1.GetOptions{})
ingress, err := c.client.NetworkingV1beta1().Ingresses(c.objectReference.Namespace).Get(context.TODO(), c.objectReference.Name, metav1.GetOptions{})
if err != nil {
return nil, err
}
@@ -207,13 +212,13 @@ func targetRefReplicas(client kubernetes.Interface, hpa *autoscalingv2.Horizonta
var replicas int32
switch hpa.Spec.ScaleTargetRef.Kind {
case "Deployment":
deployment, err := client.AppsV1().Deployments(hpa.Namespace).Get(hpa.Spec.ScaleTargetRef.Name, metav1.GetOptions{})
deployment, err := client.AppsV1().Deployments(hpa.Namespace).Get(context.TODO(), hpa.Spec.ScaleTargetRef.Name, metav1.GetOptions{})
if err != nil {
return 0, err
}
replicas = deployment.Status.Replicas
case "StatefulSet":
sts, err := client.AppsV1().StatefulSets(hpa.Namespace).Get(hpa.Spec.ScaleTargetRef.Name, metav1.GetOptions{})
sts, err := client.AppsV1().StatefulSets(hpa.Namespace).Get(context.TODO(), hpa.Spec.ScaleTargetRef.Name, metav1.GetOptions{})
if err != nil {
return 0, err
}
+25 -24
View File
@@ -1,6 +1,7 @@
package collector
import (
"context"
"encoding/json"
"fmt"
"testing"
@@ -10,7 +11,7 @@ import (
appsv1 "k8s.io/api/apps/v1"
autoscalingv2 "k8s.io/api/autoscaling/v2beta2"
corev1 "k8s.io/api/core/v1"
"k8s.io/api/extensions/v1beta1"
v1beta1 "k8s.io/api/networking/v1beta1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
@@ -32,7 +33,7 @@ func TestTargetRefReplicasDeployments(t *testing.T) {
// Create an HPA with the deployment as ref
hpa, err := client.AutoscalingV2beta2().HorizontalPodAutoscalers(deployment.Namespace).
Create(newHPA(defaultNamespace, name, "Deployment"))
Create(context.TODO(), newHPA(defaultNamespace, name, "Deployment"), metav1.CreateOptions{})
require.NoError(t, err)
replicas, err := targetRefReplicas(client, hpa)
@@ -49,7 +50,7 @@ func TestTargetRefReplicasStatefulSets(t *testing.T) {
// Create an HPA with the statefulSet as ref
hpa, err := client.AutoscalingV2beta2().HorizontalPodAutoscalers(statefulSet.Namespace).
Create(newHPA(defaultNamespace, name, "StatefulSet"))
Create(context.TODO(), newHPA(defaultNamespace, name, "StatefulSet"), metav1.CreateOptions{})
require.NoError(t, err)
replicas, err := targetRefReplicas(client, hpa)
@@ -73,7 +74,7 @@ func newHPA(namesapce string, refName string, refKind string) *autoscalingv2.Hor
}
func newDeployment(client *fake.Clientset, namespace string, name string, replicas, readyReplicas int32) (*appsv1.Deployment, error) {
return client.AppsV1().Deployments(namespace).Create(&appsv1.Deployment{
return client.AppsV1().Deployments(namespace).Create(context.TODO(), &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
@@ -83,11 +84,11 @@ func newDeployment(client *fake.Clientset, namespace string, name string, replic
ReadyReplicas: replicas,
Replicas: readyReplicas,
},
})
}, metav1.CreateOptions{})
}
func newStatefulSet(client *fake.Clientset, namespace string, name string) (*appsv1.StatefulSet, error) {
return client.AppsV1().StatefulSets(namespace).Create(&appsv1.StatefulSet{
return client.AppsV1().StatefulSets(namespace).Create(context.TODO(), &appsv1.StatefulSet{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
@@ -96,7 +97,7 @@ func newStatefulSet(client *fake.Clientset, namespace string, name string) (*app
ReadyReplicas: 1,
Replicas: 2,
},
})
}, metav1.CreateOptions{})
}
func TestSkipperCollector(t *testing.T) {
@@ -111,7 +112,7 @@ func TestSkipperCollector(t *testing.T) {
expectError bool
fakedAverage bool
namespace string
backendWeights map[string]map[string]int
backendWeights map[string]map[string]float64
replicas int32
readyReplicas int32
backendAnnotations []string
@@ -137,7 +138,7 @@ func TestSkipperCollector(t *testing.T) {
collectedMetric: 1000,
namespace: "default",
backend: "backend1",
backendWeights: map[string]map[string]int{testBackendWeightsAnnotation: {"backend2": 60, "backend1": 40}},
backendWeights: map[string]map[string]float64{testBackendWeightsAnnotation: {"backend2": 60.0, "backend1": 40}},
replicas: 1,
readyReplicas: 1,
backendAnnotations: []string{testBackendWeightsAnnotation},
@@ -151,7 +152,7 @@ func TestSkipperCollector(t *testing.T) {
collectedMetric: 1000,
namespace: "default",
backend: "backend1",
backendWeights: map[string]map[string]int{testBackendWeightsAnnotation: {"backend2": 60, "backend1": 40}},
backendWeights: map[string]map[string]float64{testBackendWeightsAnnotation: {"backend2": 60, "backend1": 40}},
replicas: 1,
readyReplicas: 1,
backendAnnotations: []string{testBackendWeightsAnnotation},
@@ -166,7 +167,7 @@ func TestSkipperCollector(t *testing.T) {
fakedAverage: true,
namespace: "default",
backend: "backend1",
backendWeights: map[string]map[string]int{testBackendWeightsAnnotation: {"backend2": 50, "backend1": 50}},
backendWeights: map[string]map[string]float64{testBackendWeightsAnnotation: {"backend2": 50, "backend1": 50}},
replicas: 5,
readyReplicas: 5,
backendAnnotations: []string{testBackendWeightsAnnotation},
@@ -180,7 +181,7 @@ func TestSkipperCollector(t *testing.T) {
collectedMetric: 1500,
namespace: "default",
backend: "backend1",
backendWeights: map[string]map[string]int{testBackendWeightsAnnotation: {"backend2": 50, "backend1": 50}},
backendWeights: map[string]map[string]float64{testBackendWeightsAnnotation: {"backend2": 50, "backend1": 50}},
replicas: 5, // this is not taken into account
readyReplicas: 5,
backendAnnotations: []string{testBackendWeightsAnnotation},
@@ -194,7 +195,7 @@ func TestSkipperCollector(t *testing.T) {
collectedMetric: 0,
namespace: "default",
backend: "backend1",
backendWeights: map[string]map[string]int{testBackendWeightsAnnotation: {"backend2": 100, "backend1": 0}},
backendWeights: map[string]map[string]float64{testBackendWeightsAnnotation: {"backend2": 100, "backend1": 0}},
replicas: 5,
readyReplicas: 5,
backendAnnotations: []string{testBackendWeightsAnnotation},
@@ -209,7 +210,7 @@ func TestSkipperCollector(t *testing.T) {
fakedAverage: true,
namespace: "default",
backend: "backend1",
backendWeights: map[string]map[string]int{
backendWeights: map[string]map[string]float64{
testBackendWeightsAnnotation: {"backend2": 20, "backend1": 80},
testStacksetWeightsAnnotation: {"backend2": 0, "backend1": 100},
},
@@ -226,7 +227,7 @@ func TestSkipperCollector(t *testing.T) {
collectedMetric: 1500,
namespace: "default",
backend: "backend1",
backendWeights: map[string]map[string]int{
backendWeights: map[string]map[string]float64{
testBackendWeightsAnnotation: {"backend2": 20, "backend1": 80},
testStacksetWeightsAnnotation: {"backend2": 0, "backend1": 100},
},
@@ -243,7 +244,7 @@ func TestSkipperCollector(t *testing.T) {
collectedMetric: 0,
namespace: "default",
backend: "backend3",
backendWeights: map[string]map[string]int{testBackendWeightsAnnotation: {"backend2": 100, "backend1": 0}},
backendWeights: map[string]map[string]float64{testBackendWeightsAnnotation: {"backend2": 100, "backend1": 0}},
replicas: 1,
readyReplicas: 1,
backendAnnotations: []string{testBackendWeightsAnnotation},
@@ -257,7 +258,7 @@ func TestSkipperCollector(t *testing.T) {
collectedMetric: 1500,
namespace: "default",
backend: "backend3",
backendWeights: map[string]map[string]int{},
backendWeights: map[string]map[string]float64{},
replicas: 1,
readyReplicas: 1,
backendAnnotations: []string{testBackendWeightsAnnotation},
@@ -271,7 +272,7 @@ func TestSkipperCollector(t *testing.T) {
expectError: true,
namespace: "default",
backend: "",
backendWeights: map[string]map[string]int{testBackendWeightsAnnotation: {"backend2": 100, "backend1": 0}},
backendWeights: map[string]map[string]float64{testBackendWeightsAnnotation: {"backend2": 100, "backend1": 0}},
replicas: 1,
readyReplicas: 1,
backendAnnotations: []string{testBackendWeightsAnnotation},
@@ -300,7 +301,7 @@ func TestSkipperCollector(t *testing.T) {
fakedAverage: true,
namespace: "default",
backend: "backend2",
backendWeights: map[string]map[string]int{
backendWeights: map[string]map[string]float64{
testBackendWeightsAnnotation: {"backend2": 20, "backend1": 80},
testStacksetWeightsAnnotation: {"backend1": 100},
},
@@ -313,12 +314,12 @@ func TestSkipperCollector(t *testing.T) {
metric: 1500,
ingressName: "dummy-ingress",
hostnames: []string{"example.org"},
expectedQuery: `scalar(sum(rate(skipper_serve_host_duration_seconds_count{host=~"example_org"}[1m])) * 0.2000)`,
expectedQuery: `scalar(sum(rate(skipper_serve_host_duration_seconds_count{host=~"example_org"}[1m])) * 0.2050)`,
collectedMetric: 1500,
namespace: "default",
backend: "backend2",
backendWeights: map[string]map[string]int{
testBackendWeightsAnnotation: {"backend2": 20, "backend1": 80},
backendWeights: map[string]map[string]float64{
testBackendWeightsAnnotation: {"backend2": 20.5, "backend1": 79.5},
testStacksetWeightsAnnotation: {"backend1": 100},
},
replicas: 5,
@@ -351,7 +352,7 @@ func TestSkipperCollector(t *testing.T) {
}
}
func makeIngress(client kubernetes.Interface, namespace, ingressName, backend string, hostnames []string, backendWeights map[string]map[string]int) error {
func makeIngress(client kubernetes.Interface, namespace, ingressName, backend string, hostnames []string, backendWeights map[string]map[string]float64) error {
annotations := make(map[string]string)
for anno, weights := range backendWeights {
sWeights, err := json.Marshal(weights)
@@ -382,7 +383,7 @@ func makeIngress(client kubernetes.Interface, namespace, ingressName, backend st
Host: hostname,
})
}
_, err := client.ExtensionsV1beta1().Ingresses(namespace).Create(ingress)
_, err := client.NetworkingV1beta1().Ingresses(namespace).Create(context.TODO(), ingress, metav1.CreateOptions{})
return err
}
+1 -1
View File
@@ -119,7 +119,7 @@ func (p *HPAProvider) Run(ctx context.Context) {
func (p *HPAProvider) updateHPAs() error {
p.logger.Info("Looking for HPAs")
hpas, err := p.client.AutoscalingV2beta2().HorizontalPodAutoscalers(metav1.NamespaceAll).List(metav1.ListOptions{})
hpas, err := p.client.AutoscalingV2beta2().HorizontalPodAutoscalers(metav1.NamespaceAll).List(context.TODO(), metav1.ListOptions{})
if err != nil {
return err
}
+3 -3
View File
@@ -70,7 +70,7 @@ func TestUpdateHPAs(t *testing.T) {
fakeClient := fake.NewSimpleClientset()
var err error
hpa, err = fakeClient.AutoscalingV2beta2().HorizontalPodAutoscalers("default").Create(hpa)
hpa, err = fakeClient.AutoscalingV2beta2().HorizontalPodAutoscalers("default").Create(context.TODO(), hpa, metav1.CreateOptions{})
require.NoError(t, err)
collectorFactory := collector.NewCollectorFactory()
@@ -86,7 +86,7 @@ func TestUpdateHPAs(t *testing.T) {
// update HPA
hpa.Annotations["metric-config.pods.requests-per-second.json-path/port"] = "8080"
_, err = fakeClient.AutoscalingV2beta2().HorizontalPodAutoscalers("default").Update(hpa)
_, err = fakeClient.AutoscalingV2beta2().HorizontalPodAutoscalers("default").Update(context.TODO(), hpa, metav1.UpdateOptions{})
require.NoError(t, err)
err = provider.updateHPAs()
@@ -134,7 +134,7 @@ func TestUpdateHPAsDisregardingIncompatibleHPA(t *testing.T) {
fakeClient := fake.NewSimpleClientset()
var err error
_, err = fakeClient.AutoscalingV2beta2().HorizontalPodAutoscalers("default").Create(hpa)
_, err = fakeClient.AutoscalingV2beta2().HorizontalPodAutoscalers("default").Create(context.TODO(), hpa, metav1.CreateOptions{})
require.NoError(t, err)
collectorFactory := collector.NewCollectorFactory()
+7 -1
View File
@@ -69,9 +69,15 @@ func (s *MetricStore) insertCustomMetric(value custom_metrics.MetricValue) {
Resource: "pods",
}
case "Ingress":
// group can be either `extentions` or `networking.k8s.io`
group := "extensions"
gv, err := schema.ParseGroupVersion(value.DescribedObject.APIVersion)
if err == nil {
group = gv.Group
}
groupResource = schema.GroupResource{
Resource: "ingresses",
Group: "extensions",
Group: group,
}
}
+8
View File
@@ -25,14 +25,18 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/apiserver"
"github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/cmd/server"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/spf13/cobra"
"github.com/zalando-incubator/cluster-lifecycle-manager/pkg/credentials-loader/platformiam"
generatedopenapi "github.com/zalando-incubator/kube-metrics-adapter/pkg/api/generated/openapi"
"github.com/zalando-incubator/kube-metrics-adapter/pkg/collector"
"github.com/zalando-incubator/kube-metrics-adapter/pkg/provider"
"github.com/zalando-incubator/kube-metrics-adapter/pkg/zmon"
"golang.org/x/oauth2"
openapinamer "k8s.io/apiserver/pkg/endpoints/openapi"
genericapiserver "k8s.io/apiserver/pkg/server"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
@@ -126,6 +130,10 @@ func (o AdapterServerOptions) RunCustomMetricsAdapterServer(stopCh <-chan struct
return err
}
config.GenericConfig.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig(generatedopenapi.GetOpenAPIDefinitions, openapinamer.NewDefinitionNamer(apiserver.Scheme))
config.GenericConfig.OpenAPIConfig.Info.Title = "kube-metrics-adapter"
config.GenericConfig.OpenAPIConfig.Info.Version = "1.0.0"
var clientConfig *rest.Config
if len(o.RemoteKubeConfigFile) > 0 {
loadingRules := &clientcmd.ClientConfigLoadingRules{ExplicitPath: o.RemoteKubeConfigFile}