Compare commits

..
2 Commits
Author SHA1 Message Date
Mikkel Oscar Lyderik Larsen c614b57a63 Serve OpenAPI spec
Signed-off-by: Mikkel Oscar Lyderik Larsen <mikkel.larsen@zalando.de>
2020-06-26 19:19:15 +02:00
Mikkel Oscar Lyderik Larsen 5bd527b698 Update client-go
Signed-off-by: Mikkel Oscar Lyderik Larsen <mikkel.larsen@zalando.de>
2020-06-26 18:22:38 +02:00
18 changed files with 14196 additions and 193 deletions
+35
View File
@@ -0,0 +1,35 @@
# 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
@@ -1,14 +0,0 @@
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
View File
@@ -1,2 +1,3 @@
Mikkel Larsen <mikkel.larsen@zalando.de>
Arjun Naik <arjun.naik@zalando.de>
Team Teapot <team-teapot@zalando.de>
+22 -12
View File
@@ -1,19 +1,22 @@
.PHONY: clean test check build.local build.linux build.osx build.docker build.push
BINARY ?= kube-metrics-adapter
VERSION ?= $(shell git describe --tags --always --dirty)
IMAGE ?= registry-write.opensource.zalan.do/teapot/$(BINARY)
TAG ?= $(VERSION)
SOURCES = $(shell find . -name '*.go')
DOCKERFILE ?= Dockerfile
GOPKGS = $(shell go list ./...)
BUILD_FLAGS ?= -v
LDFLAGS ?= -X main.version=$(VERSION) -w -s
BINARY ?= kube-metrics-adapter
VERSION ?= $(shell git describe --tags --always --dirty)
IMAGE ?= registry-write.opensource.zalan.do/teapot/$(BINARY)
TAG ?= $(VERSION)
SOURCES = $(shell find . -name '*.go')
DOCKERFILE ?= Dockerfile
GOPKGS = $(shell go list ./...)
GO_OPENAPI_GEN = ./build/openapi-gen
OPENAPI_GEN = pkg/apiserver/generated/openapi/zz_generated.openapi.go
BUILD_FLAGS ?= -v
LDFLAGS ?= -X main.version=$(VERSION) -w -s
default: build.local
clean:
rm -rf build
rm -rf $(OPENAPI_GEN)
test:
go test -v $(GOPKGS)
@@ -22,17 +25,24 @@ check:
go mod download
golangci-lint run --timeout=2m ./...
$(GO_OPENAPI_GEN):
mkdir -p build
GOBIN=$(shell pwd)/build go install k8s.io/kube-openapi/cmd/openapi-gen
$(OPENAPI_GEN): $(GO_OPENAPI_GEN)
$(GO_OPENAPI_GEN) -o . --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/apimachinery/pkg/apis/meta/v1,k8s.io/apimachinery/pkg/api/resource,k8s.io/apimachinery/pkg/version,k8s.io/api/core/v1 -p pkg/apiserver/generated/openapi -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 $(OPENAPI_GEN) $(SOURCES)
CGO_ENABLED=0 go build -o build/$(BINARY) $(BUILD_FLAGS) -ldflags "$(LDFLAGS)" .
build/linux/$(BINARY): go.mod $(SOURCES)
build/linux/$(BINARY): go.mod $(OPENAPI_GEN) $(SOURCES)
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 $(OPENAPI_GEN) $(SOURCES)
GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 go build $(BUILD_FLAGS) -o build/osx/$(BINARY) -ldflags "$(LDFLAGS)" .
build.docker: build.linux
+1 -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/spyzhov/ajson](https://github.com/spyzhov/ajson) library.
[github.com/oliveagle/jsonpath](https://github.com/oliveagle/jsonpath) library.
See the README for possible queries. It's expected that the metric you query
returns something that can be turned into a `float64`.
+13 -9
View File
@@ -2,30 +2,34 @@ module github.com/zalando-incubator/kube-metrics-adapter
require (
github.com/NYTimes/gziphandler v1.0.1 // indirect
github.com/aws/aws-sdk-go v1.33.5
github.com/aws/aws-sdk-go v1.31.7
github.com/go-openapi/spec v0.19.3
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-20200618121405-54026617ec44
github.com/lib/pq v1.2.0 // indirect
github.com/mattn/go-colorable v0.1.4 // indirect
github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852
github.com/onsi/gomega v1.8.1 // indirect
github.com/prometheus/client_golang v1.7.1
github.com/prometheus/client_golang v1.6.0
github.com/prometheus/common v0.10.0
github.com/sirupsen/logrus v1.6.0
github.com/spf13/cobra v0.0.7
github.com/spyzhov/ajson v0.4.2
github.com/stretchr/testify v1.6.1
github.com/stretchr/testify v1.6.0
github.com/zalando-incubator/cluster-lifecycle-manager v0.0.0-20180921141935-824b77fb1f84
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
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.18.5
k8s.io/apimachinery v0.18.5
k8s.io/client-go v0.18.5
k8s.io/component-base v0.18.5
k8s.io/api v0.18.4
k8s.io/apiextensions-apiserver v0.18.4
k8s.io/apimachinery v0.18.4
k8s.io/apiserver v0.18.4
k8s.io/client-go v0.18.4
k8s.io/component-base v0.18.4
k8s.io/klog v1.0.0
k8s.io/metrics v0.18.5
k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6
k8s.io/metrics v0.18.4
)
go 1.13
+104 -29
View File
File diff suppressed because it is too large Load Diff
+15
View File
@@ -0,0 +1,15 @@
/*
Copyright YEAR 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.
*/
File diff suppressed because it is too large Load Diff
+8 -8
View File
@@ -7,6 +7,7 @@ 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"
@@ -35,12 +36,14 @@ 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 := value
jsonPath, err := jsonpath.Compile(value)
if err != nil {
return nil, fmt.Errorf("failed to parse json path: %v", err)
}
collector.jsonPath = jsonPath
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
@@ -62,16 +65,13 @@ func (p *HTTPCollectorPlugin) NewCollector(_ *v2beta2.HorizontalPodAutoscaler, c
return nil, err
}
}
jsonPathGetter, err := httpmetrics.NewJSONPathMetricsGetter(httpmetrics.DefaultMetricsHTTPClient(), aggFunc, jsonPath)
if err != nil {
return nil, err
}
collector.metricsGetter = jsonPathGetter
collector.metricsGetter = httpmetrics.NewJSONPathMetricsGetter(httpmetrics.DefaultMetricsHTTPClient(), aggFunc, jsonPath)
return collector, nil
}
type HTTPCollector struct {
endpoint *url.URL
jsonPath *jsonpath.Compiled
interval time.Duration
metricType v2beta2.MetricSourceType
metricsGetter *httpmetrics.JSONPathMetricsGetter
+42 -34
View File
@@ -1,6 +1,7 @@
package httpmetrics
import (
"encoding/json"
"fmt"
"io/ioutil"
"net"
@@ -8,26 +9,21 @@ import (
"net/url"
"time"
"github.com/spyzhov/ajson"
"github.com/oliveagle/jsonpath"
)
// 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 string
jsonPath *jsonpath.Compiled
aggregator AggregatorFunc
client *http.Client
}
// NewJSONPathMetricsGetter initializes a new JSONPathMetricsGetter.
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 NewJSONPathMetricsGetter(httpClient *http.Client, aggregatorFunc AggregatorFunc, compiledPath *jsonpath.Compiled) *JSONPathMetricsGetter {
return &JSONPathMetricsGetter{client: httpClient, aggregator: aggregatorFunc, jsonPath: compiledPath}
}
var DefaultRequestTimeout = 15 * time.Second
@@ -62,45 +58,57 @@ func (g *JSONPathMetricsGetter) GetMetric(metricsURL url.URL) (float64, error) {
}
// parse data
root, err := ajson.Unmarshal(data)
var jsonData interface{}
err = json.Unmarshal(data, &jsonData)
if err != nil {
return 0, err
}
nodes, err := root.JSONPath(g.jsonPath)
res, err := g.jsonPath.Lookup(jsonData)
if err != nil {
return 0, err
}
if len(nodes) > 1 {
return 0, fmt.Errorf("unexpected json: expected single numeric or array value")
}
node := nodes[0]
if node.IsArray() {
switch res := res.(type) {
case int:
return float64(res), nil
case float32:
return float64(res), nil
case float64:
return res, nil
case []interface{}:
if g.aggregator == nil {
return 0, fmt.Errorf("no aggregator function has been specified")
}
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)
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)
}
return g.aggregator(values...), nil
} else if node.IsNumeric() {
res, _ := node.GetNumeric()
return res, 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)
return out, nil
}
func (g *JSONPathMetricsGetter) fetchMetrics(metricsURL url.URL) ([]byte, error) {
+53 -29
View File
@@ -1,20 +1,59 @@
package httpmetrics
import (
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"testing"
"github.com/oliveagle/jsonpath"
"github.com/stretchr/testify/require"
)
func makeTestHTTPServer(t *testing.T, response []byte) *httptest.Server {
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 {
h := func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, r.URL.Path, "/metrics")
w.Header().Set("Content-Type", "application/json")
_, err := w.Write(response)
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)
require.NoError(t, err)
}
return httptest.NewServer(http.HandlerFunc(h))
@@ -22,44 +61,29 @@ func makeTestHTTPServer(t *testing.T, response []byte) *httptest.Server {
func TestJSONPathMetricsGetter(t *testing.T) {
for _, tc := range []struct {
name string
jsonResponse []byte
jsonPath string
result float64
aggregator AggregatorFunc
name string
input []int64
output float64
aggregator AggregatorFunc
}{
{
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: "basic average",
input: []int64{3, 4, 5},
output: 4,
aggregator: Average,
},
} {
t.Run(tc.name, func(t *testing.T) {
server := makeTestHTTPServer(t, tc.jsonResponse)
server := makeTestHTTPServer(t, tc.input...)
defer server.Close()
getter, err := NewJSONPathMetricsGetter(DefaultMetricsHTTPClient(), tc.aggregator, tc.jsonPath)
path, err := jsonpath.Compile("$.value")
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)
require.NoError(t, err)
require.Equal(t, tc.result, metric)
require.Equal(t, tc.output, metric)
})
}
}
+9 -7
View File
@@ -6,6 +6,7 @@ import (
"strconv"
"time"
"github.com/oliveagle/jsonpath"
v1 "k8s.io/api/core/v1"
)
@@ -32,13 +33,18 @@ func (g PodMetricsJSONPathGetter) GetMetric(pod *v1.Pod) (float64, error) {
func NewPodMetricsJSONPathGetter(config map[string]string) (*PodMetricsJSONPathGetter, error) {
getter := PodMetricsJSONPathGetter{}
var (
jsonPath string
jsonPath *jsonpath.Compiled
aggregator AggregatorFunc
err error
)
if v, ok := config["json-key"]; ok {
jsonPath = v
path, err := jsonpath.Compile(v)
if err != nil {
return nil, fmt.Errorf("failed to parse json path definition: %v", err)
}
jsonPath = path
}
if v, ok := config["scheme"]; ok {
@@ -93,11 +99,7 @@ func NewPodMetricsJSONPathGetter(config map[string]string) (*PodMetricsJSONPathG
connectTimeout = d
}
jsonPathGetter, err := NewJSONPathMetricsGetter(CustomMetricsHTTPClient(requestTimeout, connectTimeout), aggregator, jsonPath)
if err != nil {
return nil, err
}
getter.metricGetter = jsonPathGetter
getter.metricGetter = NewJSONPathMetricsGetter(CustomMetricsHTTPClient(requestTimeout, connectTimeout), aggregator, jsonPath)
return &getter, nil
}
+32 -24
View File
@@ -5,6 +5,7 @@ import (
"testing"
"time"
"github.com/oliveagle/jsonpath"
"github.com/stretchr/testify/require"
)
@@ -22,11 +23,12 @@ 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: configNoAggregator["json-key"]},
metricGetter: &JSONPathMetricsGetter{jsonPath: jpath1},
scheme: "http",
path: "/metrics",
port: 9090,
@@ -39,11 +41,12 @@ 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: configAggregator["json-key"], aggregator: Average},
metricGetter: &JSONPathMetricsGetter{jsonPath: jpath2, aggregator: Average},
scheme: "http",
path: "/metrics",
port: 9090,
@@ -76,11 +79,12 @@ 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: configWithRawQuery["json-key"]},
metricGetter: &JSONPathMetricsGetter{jsonPath: jpath5},
scheme: "http",
path: "/metrics",
port: 9090,
@@ -103,6 +107,8 @@ 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)
@@ -117,6 +123,8 @@ 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)
@@ -132,10 +140,10 @@ func TestCustomTimeouts(t *testing.T) {
// Test no custom options results in default timeouts
defaultConfig := map[string]string{
"json-key": "$.value",
"scheme": scheme,
"path": path,
"port": port,
"json-key": "$.value",
"scheme": scheme,
"path": path,
"port": port,
}
defaultTime := time.Duration(15000) * time.Millisecond
@@ -145,10 +153,10 @@ func TestCustomTimeouts(t *testing.T) {
// Test with custom request timeout
configWithRequestTimeout := map[string]string{
"json-key": "$.value",
"scheme": scheme,
"path": path,
"port": port,
"json-key": "$.value",
"scheme": scheme,
"path": path,
"port": port,
"request-timeout": "978ms",
}
exectedTimeout := time.Duration(978) * time.Millisecond
@@ -159,30 +167,30 @@ func TestCustomTimeouts(t *testing.T) {
// 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,
"json-key": "$.value",
"scheme": scheme,
"path": path,
"port": port,
"connect-timeout": "512ms",
}
_, err3 := NewPodMetricsJSONPathGetter(configWithConnectTimeout)
customRequestGetter, err3 := NewPodMetricsJSONPathGetter(configWithConnectTimeout)
require.NoError(t, err3)
configWithInvalidTimeout := map[string]string{
"json-key": "$.value",
"scheme": scheme,
"path": path,
"port": port,
"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,
"json-key": "$.value",
"scheme": scheme,
"path": path,
"port": port,
"connect-timeout": "-256ms",
}
_, err5 := NewPodMetricsJSONPathGetter(configWithInvalidTimeout)
+6 -10
View File
@@ -88,16 +88,16 @@ func NewSkipperCollector(client kubernetes.Interface, plugin CollectorPlugin, hp
}, nil
}
func getAnnotationWeight(backendWeights string, backend string) (float64, error) {
var weightsMap map[string]float64
func getAnnotationWeight(backendWeights string, backend string) float64 {
var weightsMap map[string]int
err := json.Unmarshal([]byte(backendWeights), &weightsMap)
if err != nil {
return 0, err
return 0
}
if weight, ok := weightsMap[backend]; ok {
return float64(weight) / 100, nil
return float64(weight) / 100
}
return 0, nil
return 0
}
func getWeights(ingressAnnotations map[string]string, backendAnnotations []string, backend string) (float64, error) {
@@ -107,11 +107,7 @@ func getWeights(ingressAnnotations map[string]string, backendAnnotations []strin
for _, anno := range backendAnnotations {
if weightsMap, ok := ingressAnnotations[anno]; ok {
annotationsPresent = true
weight, err := getAnnotationWeight(weightsMap, backend)
if err != nil {
return 0.0, err
}
maxWeight = math.Max(maxWeight, weight)
maxWeight = math.Max(maxWeight, getAnnotationWeight(weightsMap, backend))
}
}
+16 -16
View File
@@ -112,7 +112,7 @@ func TestSkipperCollector(t *testing.T) {
expectError bool
fakedAverage bool
namespace string
backendWeights map[string]map[string]float64
backendWeights map[string]map[string]int
replicas int32
readyReplicas int32
backendAnnotations []string
@@ -138,7 +138,7 @@ func TestSkipperCollector(t *testing.T) {
collectedMetric: 1000,
namespace: "default",
backend: "backend1",
backendWeights: map[string]map[string]float64{testBackendWeightsAnnotation: {"backend2": 60.0, "backend1": 40}},
backendWeights: map[string]map[string]int{testBackendWeightsAnnotation: {"backend2": 60, "backend1": 40}},
replicas: 1,
readyReplicas: 1,
backendAnnotations: []string{testBackendWeightsAnnotation},
@@ -152,7 +152,7 @@ func TestSkipperCollector(t *testing.T) {
collectedMetric: 1000,
namespace: "default",
backend: "backend1",
backendWeights: map[string]map[string]float64{testBackendWeightsAnnotation: {"backend2": 60, "backend1": 40}},
backendWeights: map[string]map[string]int{testBackendWeightsAnnotation: {"backend2": 60, "backend1": 40}},
replicas: 1,
readyReplicas: 1,
backendAnnotations: []string{testBackendWeightsAnnotation},
@@ -167,7 +167,7 @@ func TestSkipperCollector(t *testing.T) {
fakedAverage: true,
namespace: "default",
backend: "backend1",
backendWeights: map[string]map[string]float64{testBackendWeightsAnnotation: {"backend2": 50, "backend1": 50}},
backendWeights: map[string]map[string]int{testBackendWeightsAnnotation: {"backend2": 50, "backend1": 50}},
replicas: 5,
readyReplicas: 5,
backendAnnotations: []string{testBackendWeightsAnnotation},
@@ -181,7 +181,7 @@ func TestSkipperCollector(t *testing.T) {
collectedMetric: 1500,
namespace: "default",
backend: "backend1",
backendWeights: map[string]map[string]float64{testBackendWeightsAnnotation: {"backend2": 50, "backend1": 50}},
backendWeights: map[string]map[string]int{testBackendWeightsAnnotation: {"backend2": 50, "backend1": 50}},
replicas: 5, // this is not taken into account
readyReplicas: 5,
backendAnnotations: []string{testBackendWeightsAnnotation},
@@ -195,7 +195,7 @@ func TestSkipperCollector(t *testing.T) {
collectedMetric: 0,
namespace: "default",
backend: "backend1",
backendWeights: map[string]map[string]float64{testBackendWeightsAnnotation: {"backend2": 100, "backend1": 0}},
backendWeights: map[string]map[string]int{testBackendWeightsAnnotation: {"backend2": 100, "backend1": 0}},
replicas: 5,
readyReplicas: 5,
backendAnnotations: []string{testBackendWeightsAnnotation},
@@ -210,7 +210,7 @@ func TestSkipperCollector(t *testing.T) {
fakedAverage: true,
namespace: "default",
backend: "backend1",
backendWeights: map[string]map[string]float64{
backendWeights: map[string]map[string]int{
testBackendWeightsAnnotation: {"backend2": 20, "backend1": 80},
testStacksetWeightsAnnotation: {"backend2": 0, "backend1": 100},
},
@@ -227,7 +227,7 @@ func TestSkipperCollector(t *testing.T) {
collectedMetric: 1500,
namespace: "default",
backend: "backend1",
backendWeights: map[string]map[string]float64{
backendWeights: map[string]map[string]int{
testBackendWeightsAnnotation: {"backend2": 20, "backend1": 80},
testStacksetWeightsAnnotation: {"backend2": 0, "backend1": 100},
},
@@ -244,7 +244,7 @@ func TestSkipperCollector(t *testing.T) {
collectedMetric: 0,
namespace: "default",
backend: "backend3",
backendWeights: map[string]map[string]float64{testBackendWeightsAnnotation: {"backend2": 100, "backend1": 0}},
backendWeights: map[string]map[string]int{testBackendWeightsAnnotation: {"backend2": 100, "backend1": 0}},
replicas: 1,
readyReplicas: 1,
backendAnnotations: []string{testBackendWeightsAnnotation},
@@ -258,7 +258,7 @@ func TestSkipperCollector(t *testing.T) {
collectedMetric: 1500,
namespace: "default",
backend: "backend3",
backendWeights: map[string]map[string]float64{},
backendWeights: map[string]map[string]int{},
replicas: 1,
readyReplicas: 1,
backendAnnotations: []string{testBackendWeightsAnnotation},
@@ -272,7 +272,7 @@ func TestSkipperCollector(t *testing.T) {
expectError: true,
namespace: "default",
backend: "",
backendWeights: map[string]map[string]float64{testBackendWeightsAnnotation: {"backend2": 100, "backend1": 0}},
backendWeights: map[string]map[string]int{testBackendWeightsAnnotation: {"backend2": 100, "backend1": 0}},
replicas: 1,
readyReplicas: 1,
backendAnnotations: []string{testBackendWeightsAnnotation},
@@ -301,7 +301,7 @@ func TestSkipperCollector(t *testing.T) {
fakedAverage: true,
namespace: "default",
backend: "backend2",
backendWeights: map[string]map[string]float64{
backendWeights: map[string]map[string]int{
testBackendWeightsAnnotation: {"backend2": 20, "backend1": 80},
testStacksetWeightsAnnotation: {"backend1": 100},
},
@@ -314,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.2050)`,
expectedQuery: `scalar(sum(rate(skipper_serve_host_duration_seconds_count{host=~"example_org"}[1m])) * 0.2000)`,
collectedMetric: 1500,
namespace: "default",
backend: "backend2",
backendWeights: map[string]map[string]float64{
testBackendWeightsAnnotation: {"backend2": 20.5, "backend1": 79.5},
backendWeights: map[string]map[string]int{
testBackendWeightsAnnotation: {"backend2": 20, "backend1": 80},
testStacksetWeightsAnnotation: {"backend1": 100},
},
replicas: 5,
@@ -352,7 +352,7 @@ func TestSkipperCollector(t *testing.T) {
}
}
func makeIngress(client kubernetes.Interface, namespace, ingressName, backend string, hostnames []string, backendWeights map[string]map[string]float64) error {
func makeIngress(client kubernetes.Interface, namespace, ingressName, backend string, hostnames []string, backendWeights map[string]map[string]int) error {
annotations := make(map[string]string)
for anno, weights := range backendWeights {
sWeights, err := json.Marshal(weights)
+8
View File
@@ -29,10 +29,14 @@ import (
"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/apiserver/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"
"k8s.io/apiextensions-apiserver/pkg/apiserver"
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}
+7
View File
@@ -0,0 +1,7 @@
// +build tools
package main
import (
_ "k8s.io/kube-openapi/cmd/openapi-gen"
)