Merge pull request #14 from DirectXMan12/build/initial-travis-setup

[build] set up Travis CI to run the unit tests
This commit is contained in:
Solly Ross 2017-06-27 19:07:18 -04:00 committed by GitHub
commit 895183e503
15 changed files with 350 additions and 256 deletions

23
.travis.yml Normal file
View file

@ -0,0 +1,23 @@
language: go
go:
- 1.8
# blech, Travis downloads with capitals in DirectXMan12, which confuses go
go_import_path: github.com/directxman12/k8s-prometheus-adapter
addons:
apt:
sources:
- sourceline: 'ppa:masterminds/glide'
packages:
- glide
install:
- make -B vendor
script: make verify
cache:
directories:
- ~/.glide

View file

@ -24,7 +24,7 @@ ifeq ($(ARCH),s390x)
BASEIMAGE?=s390x/busybox BASEIMAGE?=s390x/busybox
endif endif
.PHONY: all build docker-build push-% push test .PHONY: all build docker-build push-% push test verify-gofmt gofmt verify
all: build all: build
build: vendor build: vendor
@ -56,3 +56,11 @@ vendor: glide.lock
test: vendor test: vendor
CGO_ENABLED=0 go test ./pkg/... CGO_ENABLED=0 go test ./pkg/...
verify-gofmt:
./hack/gofmt-all.sh -v
gofmt:
./hack/gofmt-all.sh
verify: verify-gofmt test

View file

@ -17,23 +17,23 @@ limitations under the License.
package app package app
import ( import (
"net/http"
"net/url"
"fmt" "fmt"
"io" "io"
"net/http"
"net/url"
"time" "time"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/client-go/rest"
"k8s.io/client-go/pkg/api"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/discovery" "k8s.io/client-go/discovery"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/pkg/api"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"github.com/directxman12/custom-metrics-boilerplate/pkg/cmd/server" "github.com/directxman12/custom-metrics-boilerplate/pkg/cmd/server"
cmprov "github.com/directxman12/k8s-prometheus-adapter/pkg/custom-provider"
prom "github.com/directxman12/k8s-prometheus-adapter/pkg/client" prom "github.com/directxman12/k8s-prometheus-adapter/pkg/client"
mprom "github.com/directxman12/k8s-prometheus-adapter/pkg/client/metrics" mprom "github.com/directxman12/k8s-prometheus-adapter/pkg/client/metrics"
cmprov "github.com/directxman12/k8s-prometheus-adapter/pkg/custom-provider"
) )
// NewCommandStartPrometheusAdapterServer provides a CLI handler for 'start master' command // NewCommandStartPrometheusAdapterServer provides a CLI handler for 'start master' command

9
glide.lock generated
View file

@ -1,5 +1,5 @@
hash: 5301bfa390fea808bce92640a9c3ea2165b8484a75a394144d242d49f4a8e6e6 hash: 4432fd0d13b3a79f1febb9040cad9576793e44ab1c4d6e40abc664ad0582999f
updated: 2017-06-23T21:03:10.447897871-04:00 updated: 2017-06-27T18:59:14.961201169-04:00
imports: imports:
- name: bitbucket.org/ww/goautoneg - name: bitbucket.org/ww/goautoneg
version: 75cd24fc2f2c2a2088577d12123ddee5f54e0675 version: 75cd24fc2f2c2a2088577d12123ddee5f54e0675
@ -386,10 +386,12 @@ imports:
- plugin/pkg/authenticator/token/webhook - plugin/pkg/authenticator/token/webhook
- plugin/pkg/authorizer/webhook - plugin/pkg/authorizer/webhook
- name: k8s.io/client-go - name: k8s.io/client-go
version: 450baa5d60f8d6a251c7682cb6f86e939b750b2d version: b932a6d0e02c2b5afe156f4dd193a095efd8e954
repo: https://github.com/directxman12/client-go.git
subpackages: subpackages:
- discovery - discovery
- dynamic - dynamic
- dynamic/fake
- informers - informers
- informers/apps - informers/apps
- informers/apps/v1beta1 - informers/apps/v1beta1
@ -488,6 +490,7 @@ imports:
- pkg/version - pkg/version
- rest - rest
- rest/watch - rest/watch
- testing
- tools/auth - tools/auth
- tools/cache - tools/cache
- tools/clientcmd - tools/clientcmd

View file

@ -8,6 +8,8 @@ import:
subpackages: subpackages:
- pkg/util/logs - pkg/util/logs
- package: k8s.io/client-go - package: k8s.io/client-go
repo: https://github.com/directxman12/client-go.git
version: feature/fake-dynamic-client
subpackages: subpackages:
- kubernetes/typed/core/v1 - kubernetes/typed/core/v1
- rest - rest

41
hack/gofmt-all.sh Executable file
View file

@ -0,0 +1,41 @@
#!/bin/bash
# Copyright 2017 The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -o errexit
set -o nounset
set -o pipefail
verify=0
if [[ ${1:-} = "--verify" || ${1:-} = "-v" ]]; then
verify=1
fi
find_files() {
find . -not \( \( \
-wholename './_output' \
-o -wholename './vendor' \
\) -prune \) -name '*.go'
}
if [[ $verify -eq 1 ]]; then
diff=$(find_files | xargs gofmt -s -d 2>&1)
if [[ -n "${diff}" ]]; then
echo "gofmt -s -w $(echo "${diff}" | awk '/^diff / { print $2 }' | tr '\n' ' ')"
exit 1
fi
else
find_files | xargs gofmt -s -w
fi

View file

@ -20,14 +20,14 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io"
"io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
"path" "path"
"io/ioutil"
"io"
"github.com/prometheus/common/model"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/prometheus/common/model"
) )
// APIClient is a raw client to the Prometheus Query API. // APIClient is a raw client to the Prometheus Query API.

View file

@ -43,6 +43,7 @@ func (e *Error) Error() string {
// ResponseStatus is the type of response from the API: succeeded or error. // ResponseStatus is the type of response from the API: succeeded or error.
type ResponseStatus string type ResponseStatus string
const ( const (
ResponseSucceeded ResponseStatus = "succeeded" ResponseSucceeded ResponseStatus = "succeeded"
ResponseError = "error" ResponseError = "error"

View file

@ -21,12 +21,12 @@ import (
"strings" "strings"
"sync" "sync"
"k8s.io/apimachinery/pkg/runtime/schema"
apimeta "k8s.io/apimachinery/pkg/api/meta"
"github.com/directxman12/custom-metrics-boilerplate/pkg/provider" "github.com/directxman12/custom-metrics-boilerplate/pkg/provider"
apimeta "k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime/schema"
"github.com/golang/glog"
prom "github.com/directxman12/k8s-prometheus-adapter/pkg/client" prom "github.com/directxman12/k8s-prometheus-adapter/pkg/client"
"github.com/golang/glog"
pmodel "github.com/prometheus/common/model" pmodel "github.com/prometheus/common/model"
) )
@ -36,6 +36,7 @@ import (
// SeriesType represents the kind of series backing a metric. // SeriesType represents the kind of series backing a metric.
type SeriesType int type SeriesType int
const ( const (
CounterSeries SeriesType = iota CounterSeries SeriesType = iota
SecondsCounterSeries SecondsCounterSeries
@ -97,7 +98,7 @@ func (r *basicSeriesRegistry) SetSeries(newSeries []prom.Series) error {
} }
newMetrics := make([]provider.MetricInfo, 0, len(newInfo)) newMetrics := make([]provider.MetricInfo, 0, len(newInfo))
for info, _ := range newInfo { for info := range newInfo {
newMetrics = append(newMetrics, info) newMetrics = append(newMetrics, info)
} }
@ -324,7 +325,7 @@ func (n *metricNamer) processRootScopedSeries(series prom.Series, infos map[prov
func (n *metricNamer) groupResourcesFromSeries(series prom.Series) ([]schema.GroupResource, error) { func (n *metricNamer) groupResourcesFromSeries(series prom.Series) ([]schema.GroupResource, error) {
// TODO: do we need to cache this, or is ResourceFor good enough? // TODO: do we need to cache this, or is ResourceFor good enough?
var res []schema.GroupResource var res []schema.GroupResource
for label, _ := range series.Labels { for label := range series.Labels {
// TODO: figure out a way to let people specify a fully-qualified name in label-form // TODO: figure out a way to let people specify a fully-qualified name in label-form
// TODO: will this work when missing a group? // TODO: will this work when missing a group?
gvr, err := n.mapper.ResourceFor(schema.GroupVersionResource{Resource: string(label)}) gvr, err := n.mapper.ResourceFor(schema.GroupVersionResource{Resource: string(label)})

View file

@ -20,11 +20,12 @@ import (
"sort" "sort"
"testing" "testing"
"k8s.io/client-go/pkg/api" "github.com/directxman12/custom-metrics-boilerplate/pkg/provider"
pmodel "github.com/prometheus/common/model"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"github.com/directxman12/custom-metrics-boilerplate/pkg/provider" "k8s.io/client-go/pkg/api"
// install extensions so that our RESTMapper knows about it // install extensions so that our RESTMapper knows about it
_ "k8s.io/client-go/pkg/apis/extensions/install" _ "k8s.io/client-go/pkg/apis/extensions/install"
@ -35,7 +36,7 @@ import (
func setupMetricNamer(t *testing.T) *metricNamer { func setupMetricNamer(t *testing.T) *metricNamer {
return &metricNamer{ return &metricNamer{
overrides: map[string]seriesSpec{ overrides: map[string]seriesSpec{
"container_actually_gauge_seconds_total": seriesSpec{ "container_actually_gauge_seconds_total": {
metricName: "actually_gauge", metricName: "actually_gauge",
kind: GaugeSeries, kind: GaugeSeries,
}, },
@ -45,7 +46,7 @@ func setupMetricNamer(t *testing.T) *metricNamer {
} }
func TestMetricNamerContainerSeries(t *testing.T) { func TestMetricNamerContainerSeries(t *testing.T) {
testCases := []struct{ testCases := []struct {
input prom.Series input prom.Series
outputMetricName string outputMetricName string
outputInfo seriesInfo outputInfo seriesInfo
@ -53,7 +54,7 @@ func TestMetricNamerContainerSeries(t *testing.T) {
{ {
input: prom.Series{ input: prom.Series{
Name: "container_actually_gauge_seconds_total", Name: "container_actually_gauge_seconds_total",
Labels: map[string]string{"pod_name": "somepod", "namespace": "somens", "container_name": "somecont"}, Labels: pmodel.LabelSet{"pod_name": "somepod", "namespace": "somens", "container_name": "somecont"},
}, },
outputMetricName: "actually_gauge", outputMetricName: "actually_gauge",
outputInfo: seriesInfo{ outputInfo: seriesInfo{
@ -65,7 +66,7 @@ func TestMetricNamerContainerSeries(t *testing.T) {
{ {
input: prom.Series{ input: prom.Series{
Name: "container_some_usage", Name: "container_some_usage",
Labels: map[string]string{"pod_name": "somepod", "namespace": "somens", "container_name": "somecont"}, Labels: pmodel.LabelSet{"pod_name": "somepod", "namespace": "somens", "container_name": "somecont"},
}, },
outputMetricName: "some_usage", outputMetricName: "some_usage",
outputInfo: seriesInfo{ outputInfo: seriesInfo{
@ -77,7 +78,7 @@ func TestMetricNamerContainerSeries(t *testing.T) {
{ {
input: prom.Series{ input: prom.Series{
Name: "container_some_count_total", Name: "container_some_count_total",
Labels: map[string]string{"pod_name": "somepod", "namespace": "somens", "container_name": "somecont"}, Labels: pmodel.LabelSet{"pod_name": "somepod", "namespace": "somens", "container_name": "somecont"},
}, },
outputMetricName: "some_count", outputMetricName: "some_count",
outputInfo: seriesInfo{ outputInfo: seriesInfo{
@ -89,7 +90,7 @@ func TestMetricNamerContainerSeries(t *testing.T) {
{ {
input: prom.Series{ input: prom.Series{
Name: "container_some_time_seconds_total", Name: "container_some_time_seconds_total",
Labels: map[string]string{"pod_name": "somepod", "namespace": "somens", "container_name": "somecont"}, Labels: pmodel.LabelSet{"pod_name": "somepod", "namespace": "somens", "container_name": "somecont"},
}, },
outputMetricName: "some_time", outputMetricName: "some_time",
outputInfo: seriesInfo{ outputInfo: seriesInfo{
@ -131,63 +132,63 @@ func TestSeriesRegistry(t *testing.T) {
// container series // container series
{ {
Name: "container_actually_gauge_seconds_total", Name: "container_actually_gauge_seconds_total",
Labels: map[string]string{"pod_name": "somepod", "namespace": "somens", "container_name": "somecont"}, Labels: pmodel.LabelSet{"pod_name": "somepod", "namespace": "somens", "container_name": "somecont"},
}, },
{ {
Name: "container_some_usage", Name: "container_some_usage",
Labels: map[string]string{"pod_name": "somepod", "namespace": "somens", "container_name": "somecont"}, Labels: pmodel.LabelSet{"pod_name": "somepod", "namespace": "somens", "container_name": "somecont"},
}, },
{ {
Name: "container_some_count_total", Name: "container_some_count_total",
Labels: map[string]string{"pod_name": "somepod", "namespace": "somens", "container_name": "somecont"}, Labels: pmodel.LabelSet{"pod_name": "somepod", "namespace": "somens", "container_name": "somecont"},
}, },
{ {
Name: "container_some_time_seconds_total", Name: "container_some_time_seconds_total",
Labels: map[string]string{"pod_name": "somepod", "namespace": "somens", "container_name": "somecont"}, Labels: pmodel.LabelSet{"pod_name": "somepod", "namespace": "somens", "container_name": "somecont"},
}, },
// namespaced series // namespaced series
// a series that should turn into multiple metrics // a series that should turn into multiple metrics
{ {
Name: "ingress_hits_total", Name: "ingress_hits_total",
Labels: map[string]string{"ingress": "someingress", "service": "somesvc", "pod": "backend1", "namespace": "somens"}, Labels: pmodel.LabelSet{"ingress": "someingress", "service": "somesvc", "pod": "backend1", "namespace": "somens"},
}, },
{ {
Name: "ingress_hits_total", Name: "ingress_hits_total",
Labels: map[string]string{"ingress": "someingress", "service": "somesvc", "pod": "backend2", "namespace": "somens"}, Labels: pmodel.LabelSet{"ingress": "someingress", "service": "somesvc", "pod": "backend2", "namespace": "somens"},
}, },
{ {
Name: "service_proxy_packets", Name: "service_proxy_packets",
Labels: map[string]string{"service": "somesvc", "namespace": "somens"}, Labels: pmodel.LabelSet{"service": "somesvc", "namespace": "somens"},
}, },
{ {
Name: "work_queue_wait_seconds_total", Name: "work_queue_wait_seconds_total",
Labels: map[string]string{"deployment": "somedep", "namespace": "somens"}, Labels: pmodel.LabelSet{"deployment": "somedep", "namespace": "somens"},
}, },
// non-namespaced series // non-namespaced series
{ {
Name: "node_gigawatts", Name: "node_gigawatts",
Labels: map[string]string{"node": "somenode"}, Labels: pmodel.LabelSet{"node": "somenode"},
}, },
{ {
Name: "volume_claims_total", Name: "volume_claims_total",
Labels: map[string]string{"persistentvolume": "somepv"}, Labels: pmodel.LabelSet{"persistentvolume": "somepv"},
}, },
{ {
Name: "node_fan_seconds_total", Name: "node_fan_seconds_total",
Labels: map[string]string{"node": "somenode"}, Labels: pmodel.LabelSet{"node": "somenode"},
}, },
// unrelated series // unrelated series
{ {
Name: "admin_coffee_liters_total", Name: "admin_coffee_liters_total",
Labels: map[string]string{"admin": "some-admin"}, Labels: pmodel.LabelSet{"admin": "some-admin"},
}, },
{ {
Name: "admin_unread_emails", Name: "admin_unread_emails",
Labels: map[string]string{"admin": "some-admin"}, Labels: pmodel.LabelSet{"admin": "some-admin"},
}, },
{ {
Name: "admin_reddit_seconds_total", Name: "admin_reddit_seconds_total",
Labels: map[string]string{"admin": "some-admin"}, Labels: pmodel.LabelSet{"admin": "some-admin"},
}, },
} }
@ -195,7 +196,7 @@ func TestSeriesRegistry(t *testing.T) {
require.NoError(registry.SetSeries(inputSeries)) require.NoError(registry.SetSeries(inputSeries))
// make sure each metric got registered and can form queries // make sure each metric got registered and can form queries
testCases := []struct{ testCases := []struct {
title string title string
info provider.MetricInfo info provider.MetricInfo
namespace string namespace string
@ -203,6 +204,7 @@ func TestSeriesRegistry(t *testing.T) {
expectedKind SeriesType expectedKind SeriesType
expectedQuery string expectedQuery string
expectedGroupBy string
}{ }{
// container metrics // container metrics
{ {
@ -213,6 +215,7 @@ func TestSeriesRegistry(t *testing.T) {
expectedKind: GaugeSeries, expectedKind: GaugeSeries,
expectedQuery: "container_actually_gauge_seconds_total{pod_name=\"somepod\",container_name!=\"POD\",namespace=\"somens\"}", expectedQuery: "container_actually_gauge_seconds_total{pod_name=\"somepod\",container_name!=\"POD\",namespace=\"somens\"}",
expectedGroupBy: "pod_name",
}, },
{ {
title: "container metrics gauge / multiple resource names", title: "container metrics gauge / multiple resource names",
@ -222,6 +225,7 @@ func TestSeriesRegistry(t *testing.T) {
expectedKind: GaugeSeries, expectedKind: GaugeSeries,
expectedQuery: "container_some_usage{pod_name=~\"somepod1|somepod2\",container_name!=\"POD\",namespace=\"somens\"}", expectedQuery: "container_some_usage{pod_name=~\"somepod1|somepod2\",container_name!=\"POD\",namespace=\"somens\"}",
expectedGroupBy: "pod_name",
}, },
{ {
title: "container metrics counter", title: "container metrics counter",
@ -231,6 +235,7 @@ func TestSeriesRegistry(t *testing.T) {
expectedKind: CounterSeries, expectedKind: CounterSeries,
expectedQuery: "container_some_count_total{pod_name=~\"somepod1|somepod2\",container_name!=\"POD\",namespace=\"somens\"}", expectedQuery: "container_some_count_total{pod_name=~\"somepod1|somepod2\",container_name!=\"POD\",namespace=\"somens\"}",
expectedGroupBy: "pod_name",
}, },
{ {
title: "container metrics seconds counter", title: "container metrics seconds counter",
@ -240,6 +245,7 @@ func TestSeriesRegistry(t *testing.T) {
expectedKind: SecondsCounterSeries, expectedKind: SecondsCounterSeries,
expectedQuery: "container_some_time_seconds_total{pod_name=~\"somepod1|somepod2\",container_name!=\"POD\",namespace=\"somens\"}", expectedQuery: "container_some_time_seconds_total{pod_name=~\"somepod1|somepod2\",container_name!=\"POD\",namespace=\"somens\"}",
expectedGroupBy: "pod_name",
}, },
// namespaced metrics // namespaced metrics
{ {
@ -315,32 +321,38 @@ func TestSeriesRegistry(t *testing.T) {
} }
for _, testCase := range testCases { for _, testCase := range testCases {
outputKind, outputQuery, found := registry.QueryForMetric(testCase.info, testCase.namespace, testCase.resourceNames...) outputKind, outputQuery, groupBy, found := registry.QueryForMetric(testCase.info, testCase.namespace, testCase.resourceNames...)
if !assert.True(found, "%s: metric %v should available", testCase.title, testCase.info) { if !assert.True(found, "%s: metric %v should available", testCase.title, testCase.info) {
continue continue
} }
assert.Equal(testCase.expectedKind, outputKind, "%s: metric %v should have had the right series type", testCase.title, testCase.info) assert.Equal(testCase.expectedKind, outputKind, "%s: metric %v should have had the right series type", testCase.title, testCase.info)
assert.Equal(prom.Selector(testCase.expectedQuery), outputQuery, "%s: metric %v should have produced the correct query for %v in namespace %s", testCase.title, testCase.info, testCase.resourceNames, testCase.namespace) assert.Equal(prom.Selector(testCase.expectedQuery), outputQuery, "%s: metric %v should have produced the correct query for %v in namespace %s", testCase.title, testCase.info, testCase.resourceNames, testCase.namespace)
expectedGroupBy := testCase.expectedGroupBy
if expectedGroupBy == "" {
expectedGroupBy = testCase.info.GroupResource.Resource
}
assert.Equal(expectedGroupBy, groupBy, "%s: metric %v should have produced the correct groupBy clause", testCase.title)
} }
allMetrics := registry.ListAllMetrics() allMetrics := registry.ListAllMetrics()
expectedMetrics := []provider.MetricInfo{ expectedMetrics := []provider.MetricInfo{
provider.MetricInfo{schema.GroupResource{Resource: "pods"}, true, "actually_gauge"}, {schema.GroupResource{Resource: "pods"}, true, "actually_gauge"},
provider.MetricInfo{schema.GroupResource{Resource: "pods"}, true, "some_usage"}, {schema.GroupResource{Resource: "pods"}, true, "some_usage"},
provider.MetricInfo{schema.GroupResource{Resource: "pods"}, true, "some_count"}, {schema.GroupResource{Resource: "pods"}, true, "some_count"},
provider.MetricInfo{schema.GroupResource{Resource: "pods"}, true, "some_time"}, {schema.GroupResource{Resource: "pods"}, true, "some_time"},
provider.MetricInfo{schema.GroupResource{Resource: "services"}, true, "ingress_hits"}, {schema.GroupResource{Resource: "services"}, true, "ingress_hits"},
provider.MetricInfo{schema.GroupResource{Group: "extensions", Resource: "ingresses"}, true, "ingress_hits"}, {schema.GroupResource{Group: "extensions", Resource: "ingresses"}, true, "ingress_hits"},
provider.MetricInfo{schema.GroupResource{Resource: "pods"}, true, "ingress_hits"}, {schema.GroupResource{Resource: "pods"}, true, "ingress_hits"},
provider.MetricInfo{schema.GroupResource{Resource: "namespaces"}, false, "ingress_hits"}, {schema.GroupResource{Resource: "namespaces"}, false, "ingress_hits"},
provider.MetricInfo{schema.GroupResource{Resource: "services"}, true, "service_proxy_packets"}, {schema.GroupResource{Resource: "services"}, true, "service_proxy_packets"},
provider.MetricInfo{schema.GroupResource{Resource: "namespaces"}, false, "service_proxy_packets"}, {schema.GroupResource{Resource: "namespaces"}, false, "service_proxy_packets"},
provider.MetricInfo{schema.GroupResource{Group: "extensions", Resource: "deployments"}, true, "work_queue_wait"}, {schema.GroupResource{Group: "extensions", Resource: "deployments"}, true, "work_queue_wait"},
provider.MetricInfo{schema.GroupResource{Resource: "namespaces"}, false, "work_queue_wait"}, {schema.GroupResource{Resource: "namespaces"}, false, "work_queue_wait"},
provider.MetricInfo{schema.GroupResource{Resource: "nodes"}, false, "node_gigawatts"}, {schema.GroupResource{Resource: "nodes"}, false, "node_gigawatts"},
provider.MetricInfo{schema.GroupResource{Resource: "persistentvolumes"}, false, "volume_claims"}, {schema.GroupResource{Resource: "persistentvolumes"}, false, "volume_claims"},
provider.MetricInfo{schema.GroupResource{Resource: "nodes"}, false, "node_fan"}, {schema.GroupResource{Resource: "nodes"}, false, "node_fan"},
} }
// sort both for easy comparison // sort both for easy comparison

View file

@ -18,27 +18,27 @@ package provider
import ( import (
"context" "context"
"time"
"fmt" "fmt"
"net/http"
"github.com/golang/glog" "github.com/golang/glog"
"net/http"
"time"
"github.com/directxman12/custom-metrics-boilerplate/pkg/provider"
pmodel "github.com/prometheus/common/model"
apierr "k8s.io/apimachinery/pkg/api/errors" apierr "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime/schema"
apimeta "k8s.io/apimachinery/pkg/api/meta" apimeta "k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/metrics/pkg/apis/custom_metrics" "k8s.io/apimachinery/pkg/runtime/schema"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/pkg/api" "k8s.io/client-go/pkg/api"
_ "k8s.io/client-go/pkg/api/install" _ "k8s.io/client-go/pkg/api/install"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/metrics/pkg/apis/custom_metrics"
"k8s.io/client-go/dynamic"
"github.com/directxman12/custom-metrics-boilerplate/pkg/provider"
"k8s.io/apimachinery/pkg/util/wait"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
pmodel "github.com/prometheus/common/model"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
prom "github.com/directxman12/k8s-prometheus-adapter/pkg/client" prom "github.com/directxman12/k8s-prometheus-adapter/pkg/client"
) )
@ -111,14 +111,14 @@ func (p *prometheusProvider) metricFor(value pmodel.SampleValue, groupResource s
return &custom_metrics.MetricValue{ return &custom_metrics.MetricValue{
DescribedObject: api.ObjectReference{ DescribedObject: api.ObjectReference{
APIVersion: groupResource.Group+"/"+runtime.APIVersionInternal, APIVersion: groupResource.Group + "/" + runtime.APIVersionInternal,
Kind: kind.Kind, Kind: kind.Kind,
Name: name, Name: name,
Namespace: namespace, Namespace: namespace,
}, },
MetricName: metricName, MetricName: metricName,
Timestamp: metav1.Time{time.Now()}, Timestamp: metav1.Time{time.Now()},
Value: *resource.NewMilliQuantity(int64(value * 1000.0), resource.DecimalSI), Value: *resource.NewMilliQuantity(int64(value*1000.0), resource.DecimalSI),
}, nil }, nil
} }
@ -274,7 +274,6 @@ func (p *prometheusProvider) GetRootScopedMetricByName(groupResource schema.Grou
return p.getSingle(info, "", name) return p.getSingle(info, "", name)
} }
func (p *prometheusProvider) GetRootScopedMetricBySelector(groupResource schema.GroupResource, selector labels.Selector, metricName string) (*custom_metrics.MetricValueList, error) { func (p *prometheusProvider) GetRootScopedMetricBySelector(groupResource schema.GroupResource, selector labels.Selector, metricName string) (*custom_metrics.MetricValueList, error) {
info := provider.MetricInfo{ info := provider.MetricInfo{
GroupResource: groupResource, GroupResource: groupResource,
@ -311,7 +310,7 @@ type cachingMetricsLister struct {
} }
func (l *cachingMetricsLister) Run() { func (l *cachingMetricsLister) Run() {
go wait.Forever(func () { go wait.Forever(func() {
if err := l.updateMetrics(); err != nil { if err := l.updateMetrics(); err != nil {
utilruntime.HandleError(err) utilruntime.HandleError(err)
} }
@ -319,7 +318,7 @@ func (l *cachingMetricsLister) Run() {
} }
func (l *cachingMetricsLister) updateMetrics() error { func (l *cachingMetricsLister) updateMetrics() error {
startTime := pmodel.Now().Add(-1*l.updateInterval) startTime := pmodel.Now().Add(-1 * l.updateInterval)
// TODO: figure out a good way to add all Kubernetes-related metrics at once // TODO: figure out a good way to add all Kubernetes-related metrics at once
// (i.e. how do we determine if something is a Kubernetes-related metric?) // (i.e. how do we determine if something is a Kubernetes-related metric?)

View file

@ -17,17 +17,18 @@ limitations under the License.
package provider package provider
import ( import (
"context"
"fmt" "fmt"
"sort" "sort"
"time"
"testing" "testing"
"time"
fakedyn "k8s.io/client-go/dynamic/fake" "github.com/directxman12/custom-metrics-boilerplate/pkg/provider"
"k8s.io/client-go/pkg/api"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"github.com/directxman12/custom-metrics-boilerplate/pkg/provider" fakedyn "k8s.io/client-go/dynamic/fake"
"k8s.io/client-go/pkg/api"
// install extensions so that our RESTMapper knows about it // install extensions so that our RESTMapper knows about it
_ "k8s.io/client-go/pkg/apis/extensions/install" _ "k8s.io/client-go/pkg/apis/extensions/install"
@ -36,7 +37,7 @@ import (
pmodel "github.com/prometheus/common/model" pmodel "github.com/prometheus/common/model"
) )
const fakeProviderUpdateInterval = 2*time.Second const fakeProviderUpdateInterval = 2 * time.Second
// fakePromClient is a fake instance of prom.Client // fakePromClient is a fake instance of prom.Client
type fakePromClient struct { type fakePromClient struct {
@ -50,7 +51,7 @@ type fakePromClient struct {
queryResults map[prom.Selector]prom.QueryResult queryResults map[prom.Selector]prom.QueryResult
} }
func (c *fakePromClient) Series(interval pmodel.Interval, selectors ...prom.Selector) ([]prom.Series, error) { func (c *fakePromClient) Series(_ context.Context, interval pmodel.Interval, selectors ...prom.Selector) ([]prom.Series, error) {
if (interval.Start != 0 && interval.Start < c.acceptibleInterval.Start) || (interval.End != 0 && interval.End > c.acceptibleInterval.End) { if (interval.Start != 0 && interval.Start < c.acceptibleInterval.Start) || (interval.End != 0 && interval.End > c.acceptibleInterval.End) {
return nil, fmt.Errorf("interval [%v, %v] for query is outside range [%v, %v]", interval.Start, interval.End, c.acceptibleInterval.Start, c.acceptibleInterval.End) return nil, fmt.Errorf("interval [%v, %v] for query is outside range [%v, %v]", interval.Start, interval.End, c.acceptibleInterval.Start, c.acceptibleInterval.End)
} }
@ -67,7 +68,7 @@ func (c *fakePromClient) Series(interval pmodel.Interval, selectors ...prom.Sele
return res, nil return res, nil
} }
func (c *fakePromClient) Query(t pmodel.Time, query prom.Selector) (prom.QueryResult, error) { func (c *fakePromClient) Query(_ context.Context, t pmodel.Time, query prom.Selector) (prom.QueryResult, error) {
if t < c.acceptibleInterval.Start || t > c.acceptibleInterval.End { if t < c.acceptibleInterval.Start || t > c.acceptibleInterval.End {
return prom.QueryResult{}, fmt.Errorf("time %v for query is outside range [%v, %v]", t, c.acceptibleInterval.Start, c.acceptibleInterval.End) return prom.QueryResult{}, fmt.Errorf("time %v for query is outside range [%v, %v]", t, c.acceptibleInterval.Start, c.acceptibleInterval.End)
} }
@ -85,6 +86,9 @@ func (c *fakePromClient) Query(t pmodel.Time, query prom.Selector) (prom.QueryRe
Vector: &pmodel.Vector{}, Vector: &pmodel.Vector{},
}, nil }, nil
} }
func (c *fakePromClient) QueryRange(_ context.Context, r prom.Range, query prom.Selector) (prom.QueryResult, error) {
return prom.QueryResult{}, nil
}
func setupPrometheusProvider(t *testing.T) (provider.CustomMetricsProvider, *fakePromClient) { func setupPrometheusProvider(t *testing.T) (provider.CustomMetricsProvider, *fakePromClient) {
fakeProm := &fakePromClient{} fakeProm := &fakePromClient{}
@ -95,32 +99,32 @@ func setupPrometheusProvider(t *testing.T) (provider.CustomMetricsProvider, *fak
containerSel := prom.MatchSeries("", prom.NameMatches("^container_.*"), prom.LabelNeq("container_name", "POD"), prom.LabelNeq("namespace", ""), prom.LabelNeq("pod_name", "")) containerSel := prom.MatchSeries("", prom.NameMatches("^container_.*"), prom.LabelNeq("container_name", "POD"), prom.LabelNeq("namespace", ""), prom.LabelNeq("pod_name", ""))
namespacedSel := prom.MatchSeries("", prom.LabelNeq("namespace", ""), prom.NameNotMatches("^container_.*")) namespacedSel := prom.MatchSeries("", prom.LabelNeq("namespace", ""), prom.NameNotMatches("^container_.*"))
fakeProm.series = map[prom.Selector][]prom.Series{ fakeProm.series = map[prom.Selector][]prom.Series{
containerSel: []prom.Series{ containerSel: {
{ {
Name: "container_actually_gauge_seconds_total", Name: "container_actually_gauge_seconds_total",
Labels: map[string]string{"pod_name": "somepod", "namespace": "somens", "container_name": "somecont"}, Labels: pmodel.LabelSet{"pod_name": "somepod", "namespace": "somens", "container_name": "somecont"},
}, },
{ {
Name: "container_some_usage", Name: "container_some_usage",
Labels: map[string]string{"pod_name": "somepod", "namespace": "somens", "container_name": "somecont"}, Labels: pmodel.LabelSet{"pod_name": "somepod", "namespace": "somens", "container_name": "somecont"},
}, },
}, },
namespacedSel: []prom.Series{ namespacedSel: {
{ {
Name: "ingress_hits_total", Name: "ingress_hits_total",
Labels: map[string]string{"ingress": "someingress", "service": "somesvc", "pod": "backend1", "namespace": "somens"}, Labels: pmodel.LabelSet{"ingress": "someingress", "service": "somesvc", "pod": "backend1", "namespace": "somens"},
}, },
{ {
Name: "ingress_hits_total", Name: "ingress_hits_total",
Labels: map[string]string{"ingress": "someingress", "service": "somesvc", "pod": "backend2", "namespace": "somens"}, Labels: pmodel.LabelSet{"ingress": "someingress", "service": "somesvc", "pod": "backend2", "namespace": "somens"},
}, },
{ {
Name: "service_proxy_packets", Name: "service_proxy_packets",
Labels: map[string]string{"service": "somesvc", "namespace": "somens"}, Labels: pmodel.LabelSet{"service": "somesvc", "namespace": "somens"},
}, },
{ {
Name: "work_queue_wait_seconds_total", Name: "work_queue_wait_seconds_total",
Labels: map[string]string{"deployment": "somedep", "namespace": "somens"}, Labels: pmodel.LabelSet{"deployment": "somedep", "namespace": "somens"},
}, },
}, },
} }
@ -148,16 +152,16 @@ func TestListAllMetrics(t *testing.T) {
sort.Sort(metricInfoSorter(actualMetrics)) sort.Sort(metricInfoSorter(actualMetrics))
expectedMetrics := []provider.MetricInfo{ expectedMetrics := []provider.MetricInfo{
provider.MetricInfo{schema.GroupResource{Resource: "pods"}, true, "actually_gauge"}, {schema.GroupResource{Resource: "pods"}, true, "actually_gauge"},
provider.MetricInfo{schema.GroupResource{Resource: "pods"}, true, "some_usage"}, {schema.GroupResource{Resource: "pods"}, true, "some_usage"},
provider.MetricInfo{schema.GroupResource{Resource: "services"}, true, "ingress_hits"}, {schema.GroupResource{Resource: "services"}, true, "ingress_hits"},
provider.MetricInfo{schema.GroupResource{Group: "extensions", Resource: "ingresses"}, true, "ingress_hits"}, {schema.GroupResource{Group: "extensions", Resource: "ingresses"}, true, "ingress_hits"},
provider.MetricInfo{schema.GroupResource{Resource: "pods"}, true, "ingress_hits"}, {schema.GroupResource{Resource: "pods"}, true, "ingress_hits"},
provider.MetricInfo{schema.GroupResource{Resource: "namespaces"}, false, "ingress_hits"}, {schema.GroupResource{Resource: "namespaces"}, false, "ingress_hits"},
provider.MetricInfo{schema.GroupResource{Resource: "services"}, true, "service_proxy_packets"}, {schema.GroupResource{Resource: "services"}, true, "service_proxy_packets"},
provider.MetricInfo{schema.GroupResource{Resource: "namespaces"}, false, "service_proxy_packets"}, {schema.GroupResource{Resource: "namespaces"}, false, "service_proxy_packets"},
provider.MetricInfo{schema.GroupResource{Group: "extensions", Resource: "deployments"}, true, "work_queue_wait"}, {schema.GroupResource{Group: "extensions", Resource: "deployments"}, true, "work_queue_wait"},
provider.MetricInfo{schema.GroupResource{Resource: "namespaces"}, false, "work_queue_wait"}, {schema.GroupResource{Resource: "namespaces"}, false, "work_queue_wait"},
} }
sort.Sort(metricInfoSorter(expectedMetrics)) sort.Sort(metricInfoSorter(expectedMetrics))