mirror of
https://github.com/kubernetes-sigs/prometheus-adapter.git
synced 2026-04-06 01:38:10 +00:00
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:
commit
895183e503
15 changed files with 350 additions and 256 deletions
23
.travis.yml
Normal file
23
.travis.yml
Normal 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
|
||||||
10
Makefile
10
Makefile
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -17,27 +17,27 @@ limitations under the License.
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"k8s.io/apiserver/pkg/util/logs"
|
"k8s.io/apiserver/pkg/util/logs"
|
||||||
|
|
||||||
"github.com/directxman12/k8s-prometheus-adapter/cmd/adapter/app"
|
"github.com/directxman12/k8s-prometheus-adapter/cmd/adapter/app"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
logs.InitLogs()
|
logs.InitLogs()
|
||||||
defer logs.FlushLogs()
|
defer logs.FlushLogs()
|
||||||
|
|
||||||
if len(os.Getenv("GOMAXPROCS")) == 0 {
|
if len(os.Getenv("GOMAXPROCS")) == 0 {
|
||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := app.NewCommandStartPrometheusAdapterServer(os.Stdout, os.Stderr, wait.NeverStop)
|
cmd := app.NewCommandStartPrometheusAdapterServer(os.Stdout, os.Stderr, wait.NeverStop)
|
||||||
cmd.Flags().AddGoFlagSet(flag.CommandLine)
|
cmd.Flags().AddGoFlagSet(flag.CommandLine)
|
||||||
if err := cmd.Execute(); err != nil {
|
if err := cmd.Execute(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -41,9 +41,9 @@ func NewCommandStartPrometheusAdapterServer(out, errOut io.Writer, stopCh <-chan
|
||||||
baseOpts := server.NewCustomMetricsAdapterServerOptions(out, errOut)
|
baseOpts := server.NewCustomMetricsAdapterServerOptions(out, errOut)
|
||||||
o := PrometheusAdapterServerOptions{
|
o := PrometheusAdapterServerOptions{
|
||||||
CustomMetricsAdapterServerOptions: baseOpts,
|
CustomMetricsAdapterServerOptions: baseOpts,
|
||||||
MetricsRelistInterval: 10 * time.Minute,
|
MetricsRelistInterval: 10 * time.Minute,
|
||||||
RateInterval: 5 * time.Minute,
|
RateInterval: 5 * time.Minute,
|
||||||
PrometheusURL: "https://localhost",
|
PrometheusURL: "https://localhost",
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
|
|
|
||||||
9
glide.lock
generated
9
glide.lock
generated
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
41
hack/gofmt-all.sh
Executable 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
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -44,7 +44,7 @@ type GenericAPIClient interface {
|
||||||
|
|
||||||
// httpAPIClient is a GenericAPIClient implemented in terms of an underlying http.Client.
|
// httpAPIClient is a GenericAPIClient implemented in terms of an underlying http.Client.
|
||||||
type httpAPIClient struct {
|
type httpAPIClient struct {
|
||||||
client *http.Client
|
client *http.Client
|
||||||
baseURL *url.URL
|
baseURL *url.URL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -80,7 +80,7 @@ func (c *httpAPIClient) Do(ctx context.Context, verb, endpoint string, query url
|
||||||
if code/100 != 2 && code != 400 && code != 422 && code != 503 {
|
if code/100 != 2 && code != 400 && code != 422 && code != 503 {
|
||||||
return APIResponse{}, &Error{
|
return APIResponse{}, &Error{
|
||||||
Type: ErrBadResponse,
|
Type: ErrBadResponse,
|
||||||
Msg: fmt.Sprintf("unknown response code %d", code),
|
Msg: fmt.Sprintf("unknown response code %d", code),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,7 +106,7 @@ func (c *httpAPIClient) Do(ctx context.Context, verb, endpoint string, query url
|
||||||
if res.Status == ResponseError {
|
if res.Status == ResponseError {
|
||||||
return res, &Error{
|
return res, &Error{
|
||||||
Type: res.ErrorType,
|
Type: res.ErrorType,
|
||||||
Msg: res.Error,
|
Msg: res.Error,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -116,15 +116,15 @@ func (c *httpAPIClient) Do(ctx context.Context, verb, endpoint string, query url
|
||||||
// NewGenericAPIClient builds a new generic Prometheus API client for the given base URL and HTTP Client.
|
// NewGenericAPIClient builds a new generic Prometheus API client for the given base URL and HTTP Client.
|
||||||
func NewGenericAPIClient(client *http.Client, baseURL *url.URL) GenericAPIClient {
|
func NewGenericAPIClient(client *http.Client, baseURL *url.URL) GenericAPIClient {
|
||||||
return &httpAPIClient{
|
return &httpAPIClient{
|
||||||
client: client,
|
client: client,
|
||||||
baseURL: baseURL,
|
baseURL: baseURL,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
queryURL = "/api/v1/query"
|
queryURL = "/api/v1/query"
|
||||||
queryRangeURL = "/api/v1/query_range"
|
queryRangeURL = "/api/v1/query_range"
|
||||||
seriesURL = "/api/v1/series"
|
seriesURL = "/api/v1/series"
|
||||||
)
|
)
|
||||||
|
|
||||||
// queryClient is a Client that connects to the Prometheus HTTP API.
|
// queryClient is a Client that connects to the Prometheus HTTP API.
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ func (qr *QueryResult) UnmarshalJSON(b []byte) error {
|
||||||
// Series is roughly equivalent to model.Metrics, but has easy access to name
|
// Series is roughly equivalent to model.Metrics, but has easy access to name
|
||||||
// and the set of non-name labels.
|
// and the set of non-name labels.
|
||||||
type Series struct {
|
type Series struct {
|
||||||
Name string
|
Name string
|
||||||
Labels model.LabelSet
|
Labels model.LabelSet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,8 @@ var (
|
||||||
// overhead and HTTP overhead.
|
// overhead and HTTP overhead.
|
||||||
queryLatency = prometheus.NewHistogramVec(
|
queryLatency = prometheus.NewHistogramVec(
|
||||||
prometheus.HistogramOpts{
|
prometheus.HistogramOpts{
|
||||||
Name: "cmgateway_prometheus_query_latency_seconds",
|
Name: "cmgateway_prometheus_query_latency_seconds",
|
||||||
Help: "Prometheus client query latency in seconds. Broken down by target prometheus endpoint and target server",
|
Help: "Prometheus client query latency in seconds. Broken down by target prometheus endpoint and target server",
|
||||||
Buckets: prometheus.ExponentialBuckets(0.0001, 2, 10),
|
Buckets: prometheus.ExponentialBuckets(0.0001, 2, 10),
|
||||||
},
|
},
|
||||||
[]string{"endpoint", "server"},
|
[]string{"endpoint", "server"},
|
||||||
|
|
@ -47,7 +47,7 @@ func init() {
|
||||||
// capturing request latency.
|
// capturing request latency.
|
||||||
type instrumentedGenericClient struct {
|
type instrumentedGenericClient struct {
|
||||||
serverName string
|
serverName string
|
||||||
client client.GenericAPIClient
|
client client.GenericAPIClient
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *instrumentedGenericClient) Do(ctx context.Context, verb, endpoint string, query url.Values) (client.APIResponse, error) {
|
func (c *instrumentedGenericClient) Do(ctx context.Context, verb, endpoint string, query url.Values) (client.APIResponse, error) {
|
||||||
|
|
@ -73,6 +73,6 @@ func (c *instrumentedGenericClient) Do(ctx context.Context, verb, endpoint strin
|
||||||
func InstrumentGenericAPIClient(client client.GenericAPIClient, serverName string) client.GenericAPIClient {
|
func InstrumentGenericAPIClient(client client.GenericAPIClient, serverName string) client.GenericAPIClient {
|
||||||
return &instrumentedGenericClient{
|
return &instrumentedGenericClient{
|
||||||
serverName: serverName,
|
serverName: serverName,
|
||||||
client: client,
|
client: client,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -248,13 +249,13 @@ func (n *metricNamer) processContainerSeries(series prom.Series, infos map[provi
|
||||||
info := provider.MetricInfo{
|
info := provider.MetricInfo{
|
||||||
// TODO: is the plural correct?
|
// TODO: is the plural correct?
|
||||||
GroupResource: schema.GroupResource{Resource: "pods"},
|
GroupResource: schema.GroupResource{Resource: "pods"},
|
||||||
Namespaced: true,
|
Namespaced: true,
|
||||||
Metric: name,
|
Metric: name,
|
||||||
}
|
}
|
||||||
|
|
||||||
infos[info] = seriesInfo{
|
infos[info] = seriesInfo{
|
||||||
kind: metricKind,
|
kind: metricKind,
|
||||||
baseSeries: prom.Series{Name: originalName},
|
baseSeries: prom.Series{Name: originalName},
|
||||||
isContainer: true,
|
isContainer: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -272,8 +273,8 @@ func (n *metricNamer) processNamespacedSeries(series prom.Series, infos map[prov
|
||||||
for _, resource := range resources {
|
for _, resource := range resources {
|
||||||
info := provider.MetricInfo{
|
info := provider.MetricInfo{
|
||||||
GroupResource: resource,
|
GroupResource: resource,
|
||||||
Namespaced: true,
|
Namespaced: true,
|
||||||
Metric: name,
|
Metric: name,
|
||||||
}
|
}
|
||||||
|
|
||||||
// metrics describing namespaces aren't considered to be namespaced
|
// metrics describing namespaces aren't considered to be namespaced
|
||||||
|
|
@ -282,7 +283,7 @@ func (n *metricNamer) processNamespacedSeries(series prom.Series, infos map[prov
|
||||||
}
|
}
|
||||||
|
|
||||||
infos[info] = seriesInfo{
|
infos[info] = seriesInfo{
|
||||||
kind: metricKind,
|
kind: metricKind,
|
||||||
baseSeries: prom.Series{Name: series.Name},
|
baseSeries: prom.Series{Name: series.Name},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -303,12 +304,12 @@ func (n *metricNamer) processRootScopedSeries(series prom.Series, infos map[prov
|
||||||
for _, resource := range resources {
|
for _, resource := range resources {
|
||||||
info := provider.MetricInfo{
|
info := provider.MetricInfo{
|
||||||
GroupResource: resource,
|
GroupResource: resource,
|
||||||
Namespaced: false,
|
Namespaced: false,
|
||||||
Metric: name,
|
Metric: name,
|
||||||
}
|
}
|
||||||
|
|
||||||
infos[info] = seriesInfo{
|
infos[info] = seriesInfo{
|
||||||
kind: metricKind,
|
kind: metricKind,
|
||||||
baseSeries: prom.Series{Name: series.Name},
|
baseSeries: prom.Series{Name: series.Name},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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)})
|
||||||
|
|
|
||||||
|
|
@ -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,9 +36,9 @@ 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,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mapper: api.Registry.RESTMapper(),
|
mapper: api.Registry.RESTMapper(),
|
||||||
|
|
@ -45,56 +46,56 @@ 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
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
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{
|
||||||
baseSeries: prom.Series{Name: "container_actually_gauge_seconds_total"},
|
baseSeries: prom.Series{Name: "container_actually_gauge_seconds_total"},
|
||||||
kind: GaugeSeries,
|
kind: GaugeSeries,
|
||||||
isContainer: true,
|
isContainer: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
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{
|
||||||
baseSeries: prom.Series{Name: "container_some_usage"},
|
baseSeries: prom.Series{Name: "container_some_usage"},
|
||||||
kind: GaugeSeries,
|
kind: GaugeSeries,
|
||||||
isContainer: true,
|
isContainer: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
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{
|
||||||
baseSeries: prom.Series{Name: "container_some_count_total"},
|
baseSeries: prom.Series{Name: "container_some_count_total"},
|
||||||
kind: CounterSeries,
|
kind: CounterSeries,
|
||||||
isContainer: true,
|
isContainer: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
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{
|
||||||
baseSeries: prom.Series{Name: "container_some_time_seconds_total"},
|
baseSeries: prom.Series{Name: "container_some_time_seconds_total"},
|
||||||
kind: SecondsCounterSeries,
|
kind: SecondsCounterSeries,
|
||||||
isContainer: true,
|
isContainer: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -108,9 +109,9 @@ func TestMetricNamerContainerSeries(t *testing.T) {
|
||||||
for _, test := range testCases {
|
for _, test := range testCases {
|
||||||
namer.processContainerSeries(test.input, resMap)
|
namer.processContainerSeries(test.input, resMap)
|
||||||
metric := provider.MetricInfo{
|
metric := provider.MetricInfo{
|
||||||
Metric: test.outputMetricName,
|
Metric: test.outputMetricName,
|
||||||
GroupResource: schema.GroupResource{Resource: "pods"},
|
GroupResource: schema.GroupResource{Resource: "pods"},
|
||||||
Namespaced: true,
|
Namespaced: true,
|
||||||
}
|
}
|
||||||
if assert.Contains(resMap, metric) {
|
if assert.Contains(resMap, metric) {
|
||||||
assert.Equal(test.outputInfo, resMap[metric])
|
assert.Equal(test.outputInfo, resMap[metric])
|
||||||
|
|
@ -130,64 +131,64 @@ func TestSeriesRegistry(t *testing.T) {
|
||||||
inputSeries := []prom.Series{
|
inputSeries := []prom.Series{
|
||||||
// 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,152 +196,163 @@ 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
|
||||||
resourceNames []string
|
resourceNames []string
|
||||||
|
|
||||||
expectedKind SeriesType
|
expectedKind SeriesType
|
||||||
expectedQuery string
|
expectedQuery string
|
||||||
|
expectedGroupBy string
|
||||||
}{
|
}{
|
||||||
// container metrics
|
// container metrics
|
||||||
{
|
{
|
||||||
title: "container metrics overrides / single resource name",
|
title: "container metrics overrides / single resource name",
|
||||||
info: provider.MetricInfo{schema.GroupResource{Resource: "pods"}, true, "actually_gauge"},
|
info: provider.MetricInfo{schema.GroupResource{Resource: "pods"}, true, "actually_gauge"},
|
||||||
namespace: "somens",
|
namespace: "somens",
|
||||||
resourceNames: []string{"somepod"},
|
resourceNames: []string{"somepod"},
|
||||||
|
|
||||||
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",
|
||||||
info: provider.MetricInfo{schema.GroupResource{Resource: "pods"}, true, "some_usage"},
|
info: provider.MetricInfo{schema.GroupResource{Resource: "pods"}, true, "some_usage"},
|
||||||
namespace: "somens",
|
namespace: "somens",
|
||||||
resourceNames: []string{"somepod1", "somepod2"},
|
resourceNames: []string{"somepod1", "somepod2"},
|
||||||
|
|
||||||
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",
|
||||||
info: provider.MetricInfo{schema.GroupResource{Resource: "pods"}, true, "some_count"},
|
info: provider.MetricInfo{schema.GroupResource{Resource: "pods"}, true, "some_count"},
|
||||||
namespace: "somens",
|
namespace: "somens",
|
||||||
resourceNames: []string{"somepod1", "somepod2"},
|
resourceNames: []string{"somepod1", "somepod2"},
|
||||||
|
|
||||||
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",
|
||||||
info: provider.MetricInfo{schema.GroupResource{Resource: "pods"}, true, "some_time"},
|
info: provider.MetricInfo{schema.GroupResource{Resource: "pods"}, true, "some_time"},
|
||||||
namespace: "somens",
|
namespace: "somens",
|
||||||
resourceNames: []string{"somepod1", "somepod2"},
|
resourceNames: []string{"somepod1", "somepod2"},
|
||||||
|
|
||||||
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
|
||||||
{
|
{
|
||||||
title: "namespaced metrics counter / multidimensional (service)",
|
title: "namespaced metrics counter / multidimensional (service)",
|
||||||
info: provider.MetricInfo{schema.GroupResource{Resource: "service"}, true, "ingress_hits"},
|
info: provider.MetricInfo{schema.GroupResource{Resource: "service"}, true, "ingress_hits"},
|
||||||
namespace: "somens",
|
namespace: "somens",
|
||||||
resourceNames: []string{"somesvc"},
|
resourceNames: []string{"somesvc"},
|
||||||
|
|
||||||
expectedKind: CounterSeries,
|
expectedKind: CounterSeries,
|
||||||
expectedQuery: "ingress_hits_total{service=\"somesvc\",namespace=\"somens\"}",
|
expectedQuery: "ingress_hits_total{service=\"somesvc\",namespace=\"somens\"}",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "namespaced metrics counter / multidimensional (ingress)",
|
title: "namespaced metrics counter / multidimensional (ingress)",
|
||||||
info: provider.MetricInfo{schema.GroupResource{Group: "extensions", Resource: "ingress"}, true, "ingress_hits"},
|
info: provider.MetricInfo{schema.GroupResource{Group: "extensions", Resource: "ingress"}, true, "ingress_hits"},
|
||||||
namespace: "somens",
|
namespace: "somens",
|
||||||
resourceNames: []string{"someingress"},
|
resourceNames: []string{"someingress"},
|
||||||
|
|
||||||
expectedKind: CounterSeries,
|
expectedKind: CounterSeries,
|
||||||
expectedQuery: "ingress_hits_total{ingress=\"someingress\",namespace=\"somens\"}",
|
expectedQuery: "ingress_hits_total{ingress=\"someingress\",namespace=\"somens\"}",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "namespaced metrics counter / multidimensional (pod)",
|
title: "namespaced metrics counter / multidimensional (pod)",
|
||||||
info: provider.MetricInfo{schema.GroupResource{Resource: "pod"}, true, "ingress_hits"},
|
info: provider.MetricInfo{schema.GroupResource{Resource: "pod"}, true, "ingress_hits"},
|
||||||
namespace: "somens",
|
namespace: "somens",
|
||||||
resourceNames: []string{"somepod"},
|
resourceNames: []string{"somepod"},
|
||||||
|
|
||||||
expectedKind: CounterSeries,
|
expectedKind: CounterSeries,
|
||||||
expectedQuery: "ingress_hits_total{pod=\"somepod\",namespace=\"somens\"}",
|
expectedQuery: "ingress_hits_total{pod=\"somepod\",namespace=\"somens\"}",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "namespaced metrics gauge",
|
title: "namespaced metrics gauge",
|
||||||
info: provider.MetricInfo{schema.GroupResource{Resource: "service"}, true, "service_proxy_packets"},
|
info: provider.MetricInfo{schema.GroupResource{Resource: "service"}, true, "service_proxy_packets"},
|
||||||
namespace: "somens",
|
namespace: "somens",
|
||||||
resourceNames: []string{"somesvc"},
|
resourceNames: []string{"somesvc"},
|
||||||
|
|
||||||
expectedKind: GaugeSeries,
|
expectedKind: GaugeSeries,
|
||||||
expectedQuery: "service_proxy_packets{service=\"somesvc\",namespace=\"somens\"}",
|
expectedQuery: "service_proxy_packets{service=\"somesvc\",namespace=\"somens\"}",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "namespaced metrics seconds counter",
|
title: "namespaced metrics seconds counter",
|
||||||
info: provider.MetricInfo{schema.GroupResource{Group: "extensions", Resource: "deployment"}, true, "work_queue_wait"},
|
info: provider.MetricInfo{schema.GroupResource{Group: "extensions", Resource: "deployment"}, true, "work_queue_wait"},
|
||||||
namespace: "somens",
|
namespace: "somens",
|
||||||
resourceNames: []string{"somedep"},
|
resourceNames: []string{"somedep"},
|
||||||
|
|
||||||
expectedKind: SecondsCounterSeries,
|
expectedKind: SecondsCounterSeries,
|
||||||
expectedQuery: "work_queue_wait_seconds_total{deployment=\"somedep\",namespace=\"somens\"}",
|
expectedQuery: "work_queue_wait_seconds_total{deployment=\"somedep\",namespace=\"somens\"}",
|
||||||
},
|
},
|
||||||
// non-namespaced series
|
// non-namespaced series
|
||||||
{
|
{
|
||||||
title: "root scoped metrics gauge",
|
title: "root scoped metrics gauge",
|
||||||
info: provider.MetricInfo{schema.GroupResource{Resource: "node"}, false, "node_gigawatts"},
|
info: provider.MetricInfo{schema.GroupResource{Resource: "node"}, false, "node_gigawatts"},
|
||||||
resourceNames: []string{"somenode"},
|
resourceNames: []string{"somenode"},
|
||||||
|
|
||||||
expectedKind: GaugeSeries,
|
expectedKind: GaugeSeries,
|
||||||
expectedQuery: "node_gigawatts{node=\"somenode\"}",
|
expectedQuery: "node_gigawatts{node=\"somenode\"}",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "root scoped metrics counter",
|
title: "root scoped metrics counter",
|
||||||
info: provider.MetricInfo{schema.GroupResource{Resource: "persistentvolume"}, false, "volume_claims"},
|
info: provider.MetricInfo{schema.GroupResource{Resource: "persistentvolume"}, false, "volume_claims"},
|
||||||
resourceNames: []string{"somepv"},
|
resourceNames: []string{"somepv"},
|
||||||
|
|
||||||
expectedKind: CounterSeries,
|
expectedKind: CounterSeries,
|
||||||
expectedQuery: "volume_claims_total{persistentvolume=\"somepv\"}",
|
expectedQuery: "volume_claims_total{persistentvolume=\"somepv\"}",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "root scoped metrics seconds counter",
|
title: "root scoped metrics seconds counter",
|
||||||
info: provider.MetricInfo{schema.GroupResource{Resource: "node"}, false, "node_fan"},
|
info: provider.MetricInfo{schema.GroupResource{Resource: "node"}, false, "node_fan"},
|
||||||
resourceNames: []string{"somenode"},
|
resourceNames: []string{"somenode"},
|
||||||
|
|
||||||
expectedKind: SecondsCounterSeries,
|
expectedKind: SecondsCounterSeries,
|
||||||
expectedQuery: "node_fan_seconds_total{node=\"somenode\"}",
|
expectedQuery: "node_fan_seconds_total{node=\"somenode\"}",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
@ -47,9 +47,9 @@ import (
|
||||||
// It is similar to NewNotFound, but more specialized
|
// It is similar to NewNotFound, but more specialized
|
||||||
func newMetricNotFoundError(resource schema.GroupResource, metricName string) *apierr.StatusError {
|
func newMetricNotFoundError(resource schema.GroupResource, metricName string) *apierr.StatusError {
|
||||||
return &apierr.StatusError{metav1.Status{
|
return &apierr.StatusError{metav1.Status{
|
||||||
Status: metav1.StatusFailure,
|
Status: metav1.StatusFailure,
|
||||||
Code: int32(http.StatusNotFound),
|
Code: int32(http.StatusNotFound),
|
||||||
Reason: metav1.StatusReasonNotFound,
|
Reason: metav1.StatusReasonNotFound,
|
||||||
Message: fmt.Sprintf("the server could not find the metric %s for %s", metricName, resource.String()),
|
Message: fmt.Sprintf("the server could not find the metric %s for %s", metricName, resource.String()),
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
@ -58,15 +58,15 @@ func newMetricNotFoundError(resource schema.GroupResource, metricName string) *a
|
||||||
// the given named object. It is similar to NewNotFound, but more specialized
|
// the given named object. It is similar to NewNotFound, but more specialized
|
||||||
func newMetricNotFoundForError(resource schema.GroupResource, metricName string, resourceName string) *apierr.StatusError {
|
func newMetricNotFoundForError(resource schema.GroupResource, metricName string, resourceName string) *apierr.StatusError {
|
||||||
return &apierr.StatusError{metav1.Status{
|
return &apierr.StatusError{metav1.Status{
|
||||||
Status: metav1.StatusFailure,
|
Status: metav1.StatusFailure,
|
||||||
Code: int32(http.StatusNotFound),
|
Code: int32(http.StatusNotFound),
|
||||||
Reason: metav1.StatusReasonNotFound,
|
Reason: metav1.StatusReasonNotFound,
|
||||||
Message: fmt.Sprintf("the server could not find the metric %s for %s %s", metricName, resource.String(), resourceName),
|
Message: fmt.Sprintf("the server could not find the metric %s for %s %s", metricName, resource.String(), resourceName),
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
type prometheusProvider struct {
|
type prometheusProvider struct {
|
||||||
mapper apimeta.RESTMapper
|
mapper apimeta.RESTMapper
|
||||||
kubeClient dynamic.ClientPool
|
kubeClient dynamic.ClientPool
|
||||||
promClient prom.Client
|
promClient prom.Client
|
||||||
|
|
||||||
|
|
@ -78,13 +78,13 @@ type prometheusProvider struct {
|
||||||
func NewPrometheusProvider(mapper apimeta.RESTMapper, kubeClient dynamic.ClientPool, promClient prom.Client, updateInterval time.Duration, rateInterval time.Duration) provider.CustomMetricsProvider {
|
func NewPrometheusProvider(mapper apimeta.RESTMapper, kubeClient dynamic.ClientPool, promClient prom.Client, updateInterval time.Duration, rateInterval time.Duration) provider.CustomMetricsProvider {
|
||||||
lister := &cachingMetricsLister{
|
lister := &cachingMetricsLister{
|
||||||
updateInterval: updateInterval,
|
updateInterval: updateInterval,
|
||||||
promClient: promClient,
|
promClient: promClient,
|
||||||
|
|
||||||
SeriesRegistry: &basicSeriesRegistry{
|
SeriesRegistry: &basicSeriesRegistry{
|
||||||
namer: metricNamer{
|
namer: metricNamer{
|
||||||
// TODO: populate this...
|
// TODO: populate this...
|
||||||
overrides: nil,
|
overrides: nil,
|
||||||
mapper: mapper,
|
mapper: mapper,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -93,7 +93,7 @@ func NewPrometheusProvider(mapper apimeta.RESTMapper, kubeClient dynamic.ClientP
|
||||||
lister.Run()
|
lister.Run()
|
||||||
|
|
||||||
return &prometheusProvider{
|
return &prometheusProvider{
|
||||||
mapper: mapper,
|
mapper: mapper,
|
||||||
kubeClient: kubeClient,
|
kubeClient: kubeClient,
|
||||||
promClient: promClient,
|
promClient: promClient,
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -227,7 +227,7 @@ func (p *prometheusProvider) getMultiple(info provider.MetricInfo, namespace str
|
||||||
// we can construct a this APIResource ourself, since the dynamic client only uses Name and Namespaced
|
// we can construct a this APIResource ourself, since the dynamic client only uses Name and Namespaced
|
||||||
// TODO: use discovery information instead
|
// TODO: use discovery information instead
|
||||||
apiRes := &metav1.APIResource{
|
apiRes := &metav1.APIResource{
|
||||||
Name: info.GroupResource.Resource,
|
Name: info.GroupResource.Resource,
|
||||||
Namespaced: info.Namespaced,
|
Namespaced: info.Namespaced,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -267,19 +267,18 @@ func (p *prometheusProvider) getMultiple(info provider.MetricInfo, namespace str
|
||||||
func (p *prometheusProvider) GetRootScopedMetricByName(groupResource schema.GroupResource, name string, metricName string) (*custom_metrics.MetricValue, error) {
|
func (p *prometheusProvider) GetRootScopedMetricByName(groupResource schema.GroupResource, name string, metricName string) (*custom_metrics.MetricValue, error) {
|
||||||
info := provider.MetricInfo{
|
info := provider.MetricInfo{
|
||||||
GroupResource: groupResource,
|
GroupResource: groupResource,
|
||||||
Metric: metricName,
|
Metric: metricName,
|
||||||
Namespaced: false,
|
Namespaced: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
Metric: metricName,
|
Metric: metricName,
|
||||||
Namespaced: false,
|
Namespaced: false,
|
||||||
}
|
}
|
||||||
return p.getMultiple(info, "", selector)
|
return p.getMultiple(info, "", selector)
|
||||||
}
|
}
|
||||||
|
|
@ -287,8 +286,8 @@ func (p *prometheusProvider) GetRootScopedMetricBySelector(groupResource schema.
|
||||||
func (p *prometheusProvider) GetNamespacedMetricByName(groupResource schema.GroupResource, namespace string, name string, metricName string) (*custom_metrics.MetricValue, error) {
|
func (p *prometheusProvider) GetNamespacedMetricByName(groupResource schema.GroupResource, namespace string, name string, metricName string) (*custom_metrics.MetricValue, error) {
|
||||||
info := provider.MetricInfo{
|
info := provider.MetricInfo{
|
||||||
GroupResource: groupResource,
|
GroupResource: groupResource,
|
||||||
Metric: metricName,
|
Metric: metricName,
|
||||||
Namespaced: true,
|
Namespaced: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
return p.getSingle(info, namespace, name)
|
return p.getSingle(info, namespace, name)
|
||||||
|
|
@ -297,8 +296,8 @@ func (p *prometheusProvider) GetNamespacedMetricByName(groupResource schema.Grou
|
||||||
func (p *prometheusProvider) GetNamespacedMetricBySelector(groupResource schema.GroupResource, namespace string, selector labels.Selector, metricName string) (*custom_metrics.MetricValueList, error) {
|
func (p *prometheusProvider) GetNamespacedMetricBySelector(groupResource schema.GroupResource, namespace string, selector labels.Selector, metricName string) (*custom_metrics.MetricValueList, error) {
|
||||||
info := provider.MetricInfo{
|
info := provider.MetricInfo{
|
||||||
GroupResource: groupResource,
|
GroupResource: groupResource,
|
||||||
Metric: metricName,
|
Metric: metricName,
|
||||||
Namespaced: true,
|
Namespaced: true,
|
||||||
}
|
}
|
||||||
return p.getMultiple(info, namespace, selector)
|
return p.getMultiple(info, namespace, selector)
|
||||||
}
|
}
|
||||||
|
|
@ -306,12 +305,12 @@ func (p *prometheusProvider) GetNamespacedMetricBySelector(groupResource schema.
|
||||||
type cachingMetricsLister struct {
|
type cachingMetricsLister struct {
|
||||||
SeriesRegistry
|
SeriesRegistry
|
||||||
|
|
||||||
promClient prom.Client
|
promClient prom.Client
|
||||||
updateInterval time.Duration
|
updateInterval time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
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?)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
@ -81,10 +82,13 @@ func (c *fakePromClient) Query(t pmodel.Time, query prom.Selector) (prom.QueryRe
|
||||||
}
|
}
|
||||||
|
|
||||||
return prom.QueryResult{
|
return prom.QueryResult{
|
||||||
Type: pmodel.ValVector,
|
Type: pmodel.ValVector,
|
||||||
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))
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue