mirror of
https://github.com/kubernetes-sigs/prometheus-adapter.git
synced 2026-04-06 01:38:10 +00:00
Merge pull request #46 from DirectXMan12/feature/advanced-config
[WIP] Advanced Configuration
This commit is contained in:
commit
7b606a79fc
23 changed files with 2398 additions and 1234 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -2,3 +2,4 @@
|
||||||
*~
|
*~
|
||||||
vendor
|
vendor
|
||||||
_output
|
_output
|
||||||
|
deploy/adapter
|
||||||
|
|
|
||||||
13
.travis.yml
13
.travis.yml
|
|
@ -1,17 +1,14 @@
|
||||||
language: go
|
language: go
|
||||||
|
|
||||||
go:
|
go:
|
||||||
- 1.8
|
- '1.10'
|
||||||
|
|
||||||
# blech, Travis downloads with capitals in DirectXMan12, which confuses go
|
# blech, Travis downloads with capitals in DirectXMan12, which confuses go
|
||||||
go_import_path: github.com/directxman12/k8s-prometheus-adapter
|
go_import_path: github.com/directxman12/k8s-prometheus-adapter
|
||||||
|
|
||||||
addons:
|
before_install:
|
||||||
apt:
|
- curl -L -s https://github.com/golang/dep/releases/download/v0.4.1/dep-linux-amd64 -o $GOPATH/bin/dep
|
||||||
sources:
|
- chmod +x $GOPATH/bin/dep
|
||||||
- sourceline: 'ppa:masterminds/glide'
|
|
||||||
packages:
|
|
||||||
- glide
|
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- make -B vendor
|
- make -B vendor
|
||||||
|
|
@ -20,7 +17,7 @@ script: make verify
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
directories:
|
directories:
|
||||||
- ~/.glide
|
- ~/gopath/pkg/dep/
|
||||||
|
|
||||||
sudo: required
|
sudo: required
|
||||||
services:
|
services:
|
||||||
|
|
|
||||||
807
Gopkg.lock
generated
Normal file
807
Gopkg.lock
generated
Normal file
|
|
@ -0,0 +1,807 @@
|
||||||
|
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||||
|
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "default"
|
||||||
|
name = "bitbucket.org/ww/goautoneg"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "75cd24fc2f2c2a2088577d12123ddee5f54e0675"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/NYTimes/gziphandler"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "2600fb119af974220d3916a5916d6e31176aac1b"
|
||||||
|
version = "v1.0.1"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/PuerkitoBio/purell"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "0bcb03f4b4d0a9428594752bd2a3b9aa0a9d4bd4"
|
||||||
|
version = "v1.1.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/PuerkitoBio/urlesc"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "de5bf2ad457846296e2031421a34e2568e304e35"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/beorn7/perks"
|
||||||
|
packages = ["quantile"]
|
||||||
|
revision = "3a771d992973f24aa725d07868b467d1ddfceafb"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/coreos/etcd"
|
||||||
|
packages = [
|
||||||
|
"auth/authpb",
|
||||||
|
"client",
|
||||||
|
"clientv3",
|
||||||
|
"etcdserver/api/v3rpc/rpctypes",
|
||||||
|
"etcdserver/etcdserverpb",
|
||||||
|
"mvcc/mvccpb",
|
||||||
|
"pkg/pathutil",
|
||||||
|
"pkg/srv",
|
||||||
|
"pkg/tlsutil",
|
||||||
|
"pkg/transport",
|
||||||
|
"pkg/types",
|
||||||
|
"version"
|
||||||
|
]
|
||||||
|
revision = "33245c6b5b49130ca99280408fadfab01aac0e48"
|
||||||
|
version = "v3.3.8"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/coreos/go-semver"
|
||||||
|
packages = ["semver"]
|
||||||
|
revision = "8ab6407b697782a06568d4b7f1db25550ec2e4c6"
|
||||||
|
version = "v0.2.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/coreos/go-systemd"
|
||||||
|
packages = ["daemon"]
|
||||||
|
revision = "39ca1b05acc7ad1220e09f133283b8859a8b71ab"
|
||||||
|
version = "v17"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/davecgh/go-spew"
|
||||||
|
packages = ["spew"]
|
||||||
|
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
|
||||||
|
version = "v1.1.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/elazarl/go-bindata-assetfs"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "30f82fa23fd844bd5bb1e5f216db87fd77b5eb43"
|
||||||
|
version = "v1.0.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/emicklei/go-restful"
|
||||||
|
packages = [
|
||||||
|
".",
|
||||||
|
"log"
|
||||||
|
]
|
||||||
|
revision = "3658237ded108b4134956c1b3050349d93e7b895"
|
||||||
|
version = "v2.7.1"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/emicklei/go-restful-swagger12"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "dcef7f55730566d41eae5db10e7d6981829720f6"
|
||||||
|
version = "1.0.1"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/evanphx/json-patch"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "afac545df32f2287a079e2dfb7ba2745a643747e"
|
||||||
|
version = "v3.0.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/ghodss/yaml"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "0ca9ea5df5451ffdf184b4428c902747c2c11cd7"
|
||||||
|
version = "v1.0.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/go-openapi/jsonpointer"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "3a0015ad55fa9873f41605d3e8f28cd279c32ab2"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/go-openapi/jsonreference"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "3fb327e6747da3043567ee86abd02bb6376b6be2"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/go-openapi/spec"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "bcff419492eeeb01f76e77d2ebc714dc97b607f5"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/go-openapi/swag"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "811b1089cde9dad18d4d0c2d09fbdbf28dbd27a5"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/gogo/protobuf"
|
||||||
|
packages = [
|
||||||
|
"gogoproto",
|
||||||
|
"proto",
|
||||||
|
"protoc-gen-gogo/descriptor",
|
||||||
|
"sortkeys"
|
||||||
|
]
|
||||||
|
revision = "1adfc126b41513cc696b209667c8656ea7aac67c"
|
||||||
|
version = "v1.0.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/golang/glog"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "23def4e6c14b4da8ac2ed8007337bc5eb5007998"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/golang/protobuf"
|
||||||
|
packages = [
|
||||||
|
"proto",
|
||||||
|
"ptypes",
|
||||||
|
"ptypes/any",
|
||||||
|
"ptypes/duration",
|
||||||
|
"ptypes/timestamp"
|
||||||
|
]
|
||||||
|
revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265"
|
||||||
|
version = "v1.1.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/google/btree"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "e89373fe6b4a7413d7acd6da1725b83ef713e6e4"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/google/gofuzz"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "24818f796faf91cd76ec7bddd72458fbced7a6c1"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/googleapis/gnostic"
|
||||||
|
packages = [
|
||||||
|
"OpenAPIv2",
|
||||||
|
"compiler",
|
||||||
|
"extensions"
|
||||||
|
]
|
||||||
|
revision = "7c663266750e7d82587642f65e60bc4083f1f84e"
|
||||||
|
version = "v0.2.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/gregjones/httpcache"
|
||||||
|
packages = [
|
||||||
|
".",
|
||||||
|
"diskcache"
|
||||||
|
]
|
||||||
|
revision = "9cad4c3443a7200dd6400aef47183728de563a38"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/hashicorp/golang-lru"
|
||||||
|
packages = [
|
||||||
|
".",
|
||||||
|
"simplelru"
|
||||||
|
]
|
||||||
|
revision = "0fb14efe8c47ae851c0034ed7a448854d3d34cf3"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/imdario/mergo"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "9316a62528ac99aaecb4e47eadd6dc8aa6533d58"
|
||||||
|
version = "v0.3.5"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/inconshreveable/mousetrap"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
|
||||||
|
version = "v1.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/json-iterator/go"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "ca39e5af3ece67bbcda3d0f4f56a8e24d9f2dad4"
|
||||||
|
version = "1.1.3"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/kubernetes-incubator/custom-metrics-apiserver"
|
||||||
|
packages = [
|
||||||
|
"pkg/apiserver",
|
||||||
|
"pkg/apiserver/installer",
|
||||||
|
"pkg/cmd/server",
|
||||||
|
"pkg/dynamicmapper",
|
||||||
|
"pkg/provider",
|
||||||
|
"pkg/registry/custom_metrics",
|
||||||
|
"pkg/registry/external_metrics"
|
||||||
|
]
|
||||||
|
revision = "d8f23423aa1d0ff2bc9656da863d721725b3c68a"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/mailru/easyjson"
|
||||||
|
packages = [
|
||||||
|
"buffer",
|
||||||
|
"jlexer",
|
||||||
|
"jwriter"
|
||||||
|
]
|
||||||
|
revision = "3fdea8d05856a0c8df22ed4bc71b3219245e4485"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/matttproud/golang_protobuf_extensions"
|
||||||
|
packages = ["pbutil"]
|
||||||
|
revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c"
|
||||||
|
version = "v1.0.1"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/modern-go/concurrent"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94"
|
||||||
|
version = "1.0.3"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/modern-go/reflect2"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "1df9eeb2bb81f327b96228865c5687bc2194af3f"
|
||||||
|
version = "1.0.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/pborman/uuid"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "e790cca94e6cc75c7064b1332e63811d4aae1a53"
|
||||||
|
version = "v1.1"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/petar/GoLLRB"
|
||||||
|
packages = ["llrb"]
|
||||||
|
revision = "53be0d36a84c2a886ca057d34b6aa4468df9ccb4"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/peterbourgon/diskv"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "5f041e8faa004a95c88a202771f4cc3e991971e6"
|
||||||
|
version = "v2.0.1"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/pmezard/go-difflib"
|
||||||
|
packages = ["difflib"]
|
||||||
|
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
|
||||||
|
version = "v1.0.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/prometheus/client_golang"
|
||||||
|
packages = ["prometheus"]
|
||||||
|
revision = "c5b7fccd204277076155f10851dad72b76a49317"
|
||||||
|
version = "v0.8.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/prometheus/client_model"
|
||||||
|
packages = ["go"]
|
||||||
|
revision = "99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/prometheus/common"
|
||||||
|
packages = [
|
||||||
|
"expfmt",
|
||||||
|
"internal/bitbucket.org/ww/goautoneg",
|
||||||
|
"model"
|
||||||
|
]
|
||||||
|
revision = "7600349dcfe1abd18d72d3a1770870d9800a7801"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/prometheus/procfs"
|
||||||
|
packages = [
|
||||||
|
".",
|
||||||
|
"internal/util",
|
||||||
|
"nfs",
|
||||||
|
"xfs"
|
||||||
|
]
|
||||||
|
revision = "7d6f385de8bea29190f15ba9931442a0eaef9af7"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/spf13/cobra"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "ef82de70bb3f60c65fb8eebacbb2d122ef517385"
|
||||||
|
version = "v0.0.3"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/spf13/pflag"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "583c0c0531f06d5278b7d917446061adc344b5cd"
|
||||||
|
version = "v1.0.1"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/stretchr/testify"
|
||||||
|
packages = [
|
||||||
|
"assert",
|
||||||
|
"require"
|
||||||
|
]
|
||||||
|
revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686"
|
||||||
|
version = "v1.2.2"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/ugorji/go"
|
||||||
|
packages = ["codec"]
|
||||||
|
revision = "b4c50a2b199d93b13dc15e78929cfb23bfdf21ab"
|
||||||
|
version = "v1.1.1"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "golang.org/x/crypto"
|
||||||
|
packages = ["ssh/terminal"]
|
||||||
|
revision = "a49355c7e3f8fe157a85be2f77e6e269a0f89602"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "golang.org/x/net"
|
||||||
|
packages = [
|
||||||
|
"context",
|
||||||
|
"http/httpguts",
|
||||||
|
"http2",
|
||||||
|
"http2/hpack",
|
||||||
|
"idna",
|
||||||
|
"internal/timeseries",
|
||||||
|
"trace",
|
||||||
|
"websocket"
|
||||||
|
]
|
||||||
|
revision = "afe8f62b1d6bbd81f31868121a50b06d8188e1f9"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "golang.org/x/sys"
|
||||||
|
packages = [
|
||||||
|
"unix",
|
||||||
|
"windows"
|
||||||
|
]
|
||||||
|
revision = "63fc586f45fe72d95d5240a5d5eb95e6503907d3"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "golang.org/x/text"
|
||||||
|
packages = [
|
||||||
|
"collate",
|
||||||
|
"collate/build",
|
||||||
|
"internal/colltab",
|
||||||
|
"internal/gen",
|
||||||
|
"internal/tag",
|
||||||
|
"internal/triegen",
|
||||||
|
"internal/ucd",
|
||||||
|
"language",
|
||||||
|
"secure/bidirule",
|
||||||
|
"transform",
|
||||||
|
"unicode/bidi",
|
||||||
|
"unicode/cldr",
|
||||||
|
"unicode/norm",
|
||||||
|
"unicode/rangetable",
|
||||||
|
"width"
|
||||||
|
]
|
||||||
|
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
|
||||||
|
version = "v0.3.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "golang.org/x/time"
|
||||||
|
packages = ["rate"]
|
||||||
|
revision = "fbb02b2291d28baffd63558aa44b4b56f178d650"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "google.golang.org/genproto"
|
||||||
|
packages = ["googleapis/rpc/status"]
|
||||||
|
revision = "80063a038e333bbe006c878e4c5ce4c74d055498"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "google.golang.org/grpc"
|
||||||
|
packages = [
|
||||||
|
".",
|
||||||
|
"balancer",
|
||||||
|
"balancer/base",
|
||||||
|
"balancer/roundrobin",
|
||||||
|
"codes",
|
||||||
|
"connectivity",
|
||||||
|
"credentials",
|
||||||
|
"encoding",
|
||||||
|
"encoding/proto",
|
||||||
|
"grpclog",
|
||||||
|
"health/grpc_health_v1",
|
||||||
|
"internal",
|
||||||
|
"internal/backoff",
|
||||||
|
"internal/channelz",
|
||||||
|
"internal/grpcrand",
|
||||||
|
"keepalive",
|
||||||
|
"metadata",
|
||||||
|
"naming",
|
||||||
|
"peer",
|
||||||
|
"resolver",
|
||||||
|
"resolver/dns",
|
||||||
|
"resolver/passthrough",
|
||||||
|
"stats",
|
||||||
|
"status",
|
||||||
|
"tap",
|
||||||
|
"transport"
|
||||||
|
]
|
||||||
|
revision = "168a6198bcb0ef175f7dacec0b8691fc141dc9b8"
|
||||||
|
version = "v1.13.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "gopkg.in/inf.v0"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "d2d2541c53f18d2a059457998ce2876cc8e67cbf"
|
||||||
|
version = "v0.9.1"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "gopkg.in/natefinch/lumberjack.v2"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "a96e63847dc3c67d17befa69c303767e2f84e54f"
|
||||||
|
version = "v2.1"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "gopkg.in/yaml.v2"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
|
||||||
|
version = "v2.2.1"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "k8s.io/api"
|
||||||
|
packages = [
|
||||||
|
"admission/v1beta1",
|
||||||
|
"admissionregistration/v1alpha1",
|
||||||
|
"admissionregistration/v1beta1",
|
||||||
|
"apps/v1",
|
||||||
|
"apps/v1beta1",
|
||||||
|
"apps/v1beta2",
|
||||||
|
"authentication/v1",
|
||||||
|
"authentication/v1beta1",
|
||||||
|
"authorization/v1",
|
||||||
|
"authorization/v1beta1",
|
||||||
|
"autoscaling/v1",
|
||||||
|
"autoscaling/v2beta1",
|
||||||
|
"batch/v1",
|
||||||
|
"batch/v1beta1",
|
||||||
|
"batch/v2alpha1",
|
||||||
|
"certificates/v1beta1",
|
||||||
|
"core/v1",
|
||||||
|
"events/v1beta1",
|
||||||
|
"extensions/v1beta1",
|
||||||
|
"networking/v1",
|
||||||
|
"policy/v1beta1",
|
||||||
|
"rbac/v1",
|
||||||
|
"rbac/v1alpha1",
|
||||||
|
"rbac/v1beta1",
|
||||||
|
"scheduling/v1alpha1",
|
||||||
|
"scheduling/v1beta1",
|
||||||
|
"settings/v1alpha1",
|
||||||
|
"storage/v1",
|
||||||
|
"storage/v1alpha1",
|
||||||
|
"storage/v1beta1"
|
||||||
|
]
|
||||||
|
revision = "91b2d7a92a8930454bf5020e0595b8ea0f2a5047"
|
||||||
|
version = "kubernetes-1.11.0-rc.1"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "k8s.io/apimachinery"
|
||||||
|
packages = [
|
||||||
|
"pkg/api/equality",
|
||||||
|
"pkg/api/errors",
|
||||||
|
"pkg/api/meta",
|
||||||
|
"pkg/api/resource",
|
||||||
|
"pkg/api/validation",
|
||||||
|
"pkg/api/validation/path",
|
||||||
|
"pkg/apis/meta/internalversion",
|
||||||
|
"pkg/apis/meta/v1",
|
||||||
|
"pkg/apis/meta/v1/unstructured",
|
||||||
|
"pkg/apis/meta/v1/validation",
|
||||||
|
"pkg/apis/meta/v1beta1",
|
||||||
|
"pkg/conversion",
|
||||||
|
"pkg/conversion/queryparams",
|
||||||
|
"pkg/fields",
|
||||||
|
"pkg/labels",
|
||||||
|
"pkg/runtime",
|
||||||
|
"pkg/runtime/schema",
|
||||||
|
"pkg/runtime/serializer",
|
||||||
|
"pkg/runtime/serializer/json",
|
||||||
|
"pkg/runtime/serializer/protobuf",
|
||||||
|
"pkg/runtime/serializer/recognizer",
|
||||||
|
"pkg/runtime/serializer/streaming",
|
||||||
|
"pkg/runtime/serializer/versioning",
|
||||||
|
"pkg/selection",
|
||||||
|
"pkg/types",
|
||||||
|
"pkg/util/cache",
|
||||||
|
"pkg/util/clock",
|
||||||
|
"pkg/util/diff",
|
||||||
|
"pkg/util/errors",
|
||||||
|
"pkg/util/framer",
|
||||||
|
"pkg/util/intstr",
|
||||||
|
"pkg/util/json",
|
||||||
|
"pkg/util/mergepatch",
|
||||||
|
"pkg/util/net",
|
||||||
|
"pkg/util/rand",
|
||||||
|
"pkg/util/runtime",
|
||||||
|
"pkg/util/sets",
|
||||||
|
"pkg/util/strategicpatch",
|
||||||
|
"pkg/util/uuid",
|
||||||
|
"pkg/util/validation",
|
||||||
|
"pkg/util/validation/field",
|
||||||
|
"pkg/util/wait",
|
||||||
|
"pkg/util/waitgroup",
|
||||||
|
"pkg/util/yaml",
|
||||||
|
"pkg/version",
|
||||||
|
"pkg/watch",
|
||||||
|
"third_party/forked/golang/json",
|
||||||
|
"third_party/forked/golang/reflect"
|
||||||
|
]
|
||||||
|
revision = "fda675fbe85280c4550452dae2a5ebf74e4a59b7"
|
||||||
|
version = "kubernetes-1.11.0-rc.1"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "k8s.io/apiserver"
|
||||||
|
packages = [
|
||||||
|
"pkg/admission",
|
||||||
|
"pkg/admission/configuration",
|
||||||
|
"pkg/admission/initializer",
|
||||||
|
"pkg/admission/metrics",
|
||||||
|
"pkg/admission/plugin/initialization",
|
||||||
|
"pkg/admission/plugin/namespace/lifecycle",
|
||||||
|
"pkg/admission/plugin/webhook/config",
|
||||||
|
"pkg/admission/plugin/webhook/config/apis/webhookadmission",
|
||||||
|
"pkg/admission/plugin/webhook/config/apis/webhookadmission/v1alpha1",
|
||||||
|
"pkg/admission/plugin/webhook/errors",
|
||||||
|
"pkg/admission/plugin/webhook/generic",
|
||||||
|
"pkg/admission/plugin/webhook/mutating",
|
||||||
|
"pkg/admission/plugin/webhook/namespace",
|
||||||
|
"pkg/admission/plugin/webhook/request",
|
||||||
|
"pkg/admission/plugin/webhook/rules",
|
||||||
|
"pkg/admission/plugin/webhook/validating",
|
||||||
|
"pkg/apis/apiserver",
|
||||||
|
"pkg/apis/apiserver/install",
|
||||||
|
"pkg/apis/apiserver/v1alpha1",
|
||||||
|
"pkg/apis/audit",
|
||||||
|
"pkg/apis/audit/install",
|
||||||
|
"pkg/apis/audit/v1alpha1",
|
||||||
|
"pkg/apis/audit/v1beta1",
|
||||||
|
"pkg/apis/audit/validation",
|
||||||
|
"pkg/audit",
|
||||||
|
"pkg/audit/policy",
|
||||||
|
"pkg/authentication/authenticator",
|
||||||
|
"pkg/authentication/authenticatorfactory",
|
||||||
|
"pkg/authentication/group",
|
||||||
|
"pkg/authentication/request/anonymous",
|
||||||
|
"pkg/authentication/request/bearertoken",
|
||||||
|
"pkg/authentication/request/headerrequest",
|
||||||
|
"pkg/authentication/request/union",
|
||||||
|
"pkg/authentication/request/websocket",
|
||||||
|
"pkg/authentication/request/x509",
|
||||||
|
"pkg/authentication/serviceaccount",
|
||||||
|
"pkg/authentication/token/tokenfile",
|
||||||
|
"pkg/authentication/user",
|
||||||
|
"pkg/authorization/authorizer",
|
||||||
|
"pkg/authorization/authorizerfactory",
|
||||||
|
"pkg/authorization/union",
|
||||||
|
"pkg/endpoints",
|
||||||
|
"pkg/endpoints/discovery",
|
||||||
|
"pkg/endpoints/filters",
|
||||||
|
"pkg/endpoints/handlers",
|
||||||
|
"pkg/endpoints/handlers/negotiation",
|
||||||
|
"pkg/endpoints/handlers/responsewriters",
|
||||||
|
"pkg/endpoints/metrics",
|
||||||
|
"pkg/endpoints/openapi",
|
||||||
|
"pkg/endpoints/request",
|
||||||
|
"pkg/features",
|
||||||
|
"pkg/registry/generic",
|
||||||
|
"pkg/registry/generic/registry",
|
||||||
|
"pkg/registry/rest",
|
||||||
|
"pkg/server",
|
||||||
|
"pkg/server/filters",
|
||||||
|
"pkg/server/healthz",
|
||||||
|
"pkg/server/httplog",
|
||||||
|
"pkg/server/mux",
|
||||||
|
"pkg/server/options",
|
||||||
|
"pkg/server/resourceconfig",
|
||||||
|
"pkg/server/routes",
|
||||||
|
"pkg/server/routes/data/swagger",
|
||||||
|
"pkg/server/storage",
|
||||||
|
"pkg/storage",
|
||||||
|
"pkg/storage/errors",
|
||||||
|
"pkg/storage/etcd",
|
||||||
|
"pkg/storage/etcd/metrics",
|
||||||
|
"pkg/storage/etcd/util",
|
||||||
|
"pkg/storage/etcd3",
|
||||||
|
"pkg/storage/etcd3/preflight",
|
||||||
|
"pkg/storage/names",
|
||||||
|
"pkg/storage/storagebackend",
|
||||||
|
"pkg/storage/storagebackend/factory",
|
||||||
|
"pkg/storage/value",
|
||||||
|
"pkg/util/feature",
|
||||||
|
"pkg/util/flag",
|
||||||
|
"pkg/util/flushwriter",
|
||||||
|
"pkg/util/logs",
|
||||||
|
"pkg/util/openapi",
|
||||||
|
"pkg/util/trace",
|
||||||
|
"pkg/util/webhook",
|
||||||
|
"pkg/util/wsstream",
|
||||||
|
"plugin/pkg/audit/buffered",
|
||||||
|
"plugin/pkg/audit/log",
|
||||||
|
"plugin/pkg/audit/truncate",
|
||||||
|
"plugin/pkg/audit/webhook",
|
||||||
|
"plugin/pkg/authenticator/token/webhook",
|
||||||
|
"plugin/pkg/authorizer/webhook"
|
||||||
|
]
|
||||||
|
revision = "44b612291bb7545430c499a3882c610c727f37b0"
|
||||||
|
version = "kubernetes-1.11.0-rc.1"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "k8s.io/client-go"
|
||||||
|
packages = [
|
||||||
|
"discovery",
|
||||||
|
"dynamic",
|
||||||
|
"dynamic/fake",
|
||||||
|
"informers",
|
||||||
|
"informers/admissionregistration",
|
||||||
|
"informers/admissionregistration/v1alpha1",
|
||||||
|
"informers/admissionregistration/v1beta1",
|
||||||
|
"informers/apps",
|
||||||
|
"informers/apps/v1",
|
||||||
|
"informers/apps/v1beta1",
|
||||||
|
"informers/apps/v1beta2",
|
||||||
|
"informers/autoscaling",
|
||||||
|
"informers/autoscaling/v1",
|
||||||
|
"informers/autoscaling/v2beta1",
|
||||||
|
"informers/batch",
|
||||||
|
"informers/batch/v1",
|
||||||
|
"informers/batch/v1beta1",
|
||||||
|
"informers/batch/v2alpha1",
|
||||||
|
"informers/certificates",
|
||||||
|
"informers/certificates/v1beta1",
|
||||||
|
"informers/core",
|
||||||
|
"informers/core/v1",
|
||||||
|
"informers/events",
|
||||||
|
"informers/events/v1beta1",
|
||||||
|
"informers/extensions",
|
||||||
|
"informers/extensions/v1beta1",
|
||||||
|
"informers/internalinterfaces",
|
||||||
|
"informers/networking",
|
||||||
|
"informers/networking/v1",
|
||||||
|
"informers/policy",
|
||||||
|
"informers/policy/v1beta1",
|
||||||
|
"informers/rbac",
|
||||||
|
"informers/rbac/v1",
|
||||||
|
"informers/rbac/v1alpha1",
|
||||||
|
"informers/rbac/v1beta1",
|
||||||
|
"informers/scheduling",
|
||||||
|
"informers/scheduling/v1alpha1",
|
||||||
|
"informers/scheduling/v1beta1",
|
||||||
|
"informers/settings",
|
||||||
|
"informers/settings/v1alpha1",
|
||||||
|
"informers/storage",
|
||||||
|
"informers/storage/v1",
|
||||||
|
"informers/storage/v1alpha1",
|
||||||
|
"informers/storage/v1beta1",
|
||||||
|
"kubernetes",
|
||||||
|
"kubernetes/scheme",
|
||||||
|
"kubernetes/typed/admissionregistration/v1alpha1",
|
||||||
|
"kubernetes/typed/admissionregistration/v1beta1",
|
||||||
|
"kubernetes/typed/apps/v1",
|
||||||
|
"kubernetes/typed/apps/v1beta1",
|
||||||
|
"kubernetes/typed/apps/v1beta2",
|
||||||
|
"kubernetes/typed/authentication/v1",
|
||||||
|
"kubernetes/typed/authentication/v1beta1",
|
||||||
|
"kubernetes/typed/authorization/v1",
|
||||||
|
"kubernetes/typed/authorization/v1beta1",
|
||||||
|
"kubernetes/typed/autoscaling/v1",
|
||||||
|
"kubernetes/typed/autoscaling/v2beta1",
|
||||||
|
"kubernetes/typed/batch/v1",
|
||||||
|
"kubernetes/typed/batch/v1beta1",
|
||||||
|
"kubernetes/typed/batch/v2alpha1",
|
||||||
|
"kubernetes/typed/certificates/v1beta1",
|
||||||
|
"kubernetes/typed/core/v1",
|
||||||
|
"kubernetes/typed/events/v1beta1",
|
||||||
|
"kubernetes/typed/extensions/v1beta1",
|
||||||
|
"kubernetes/typed/networking/v1",
|
||||||
|
"kubernetes/typed/policy/v1beta1",
|
||||||
|
"kubernetes/typed/rbac/v1",
|
||||||
|
"kubernetes/typed/rbac/v1alpha1",
|
||||||
|
"kubernetes/typed/rbac/v1beta1",
|
||||||
|
"kubernetes/typed/scheduling/v1alpha1",
|
||||||
|
"kubernetes/typed/scheduling/v1beta1",
|
||||||
|
"kubernetes/typed/settings/v1alpha1",
|
||||||
|
"kubernetes/typed/storage/v1",
|
||||||
|
"kubernetes/typed/storage/v1alpha1",
|
||||||
|
"kubernetes/typed/storage/v1beta1",
|
||||||
|
"listers/admissionregistration/v1alpha1",
|
||||||
|
"listers/admissionregistration/v1beta1",
|
||||||
|
"listers/apps/v1",
|
||||||
|
"listers/apps/v1beta1",
|
||||||
|
"listers/apps/v1beta2",
|
||||||
|
"listers/autoscaling/v1",
|
||||||
|
"listers/autoscaling/v2beta1",
|
||||||
|
"listers/batch/v1",
|
||||||
|
"listers/batch/v1beta1",
|
||||||
|
"listers/batch/v2alpha1",
|
||||||
|
"listers/certificates/v1beta1",
|
||||||
|
"listers/core/v1",
|
||||||
|
"listers/events/v1beta1",
|
||||||
|
"listers/extensions/v1beta1",
|
||||||
|
"listers/networking/v1",
|
||||||
|
"listers/policy/v1beta1",
|
||||||
|
"listers/rbac/v1",
|
||||||
|
"listers/rbac/v1alpha1",
|
||||||
|
"listers/rbac/v1beta1",
|
||||||
|
"listers/scheduling/v1alpha1",
|
||||||
|
"listers/scheduling/v1beta1",
|
||||||
|
"listers/settings/v1alpha1",
|
||||||
|
"listers/storage/v1",
|
||||||
|
"listers/storage/v1alpha1",
|
||||||
|
"listers/storage/v1beta1",
|
||||||
|
"pkg/apis/clientauthentication",
|
||||||
|
"pkg/apis/clientauthentication/v1alpha1",
|
||||||
|
"pkg/apis/clientauthentication/v1beta1",
|
||||||
|
"pkg/version",
|
||||||
|
"plugin/pkg/client/auth/exec",
|
||||||
|
"rest",
|
||||||
|
"rest/watch",
|
||||||
|
"restmapper",
|
||||||
|
"testing",
|
||||||
|
"tools/auth",
|
||||||
|
"tools/cache",
|
||||||
|
"tools/clientcmd",
|
||||||
|
"tools/clientcmd/api",
|
||||||
|
"tools/clientcmd/api/latest",
|
||||||
|
"tools/clientcmd/api/v1",
|
||||||
|
"tools/metrics",
|
||||||
|
"tools/pager",
|
||||||
|
"tools/reference",
|
||||||
|
"transport",
|
||||||
|
"util/buffer",
|
||||||
|
"util/cert",
|
||||||
|
"util/connrotation",
|
||||||
|
"util/flowcontrol",
|
||||||
|
"util/homedir",
|
||||||
|
"util/integer",
|
||||||
|
"util/retry"
|
||||||
|
]
|
||||||
|
revision = "4cacfee698b01630072bc41e3384280562a97d95"
|
||||||
|
version = "kubernetes-1.11.0-rc.1"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "k8s.io/kube-openapi"
|
||||||
|
packages = [
|
||||||
|
"pkg/builder",
|
||||||
|
"pkg/common",
|
||||||
|
"pkg/handler",
|
||||||
|
"pkg/util",
|
||||||
|
"pkg/util/proto"
|
||||||
|
]
|
||||||
|
revision = "91cfa479c814065e420cee7ed227db0f63a5854e"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "k8s.io/metrics"
|
||||||
|
packages = [
|
||||||
|
"pkg/apis/custom_metrics",
|
||||||
|
"pkg/apis/custom_metrics/install",
|
||||||
|
"pkg/apis/custom_metrics/v1beta1",
|
||||||
|
"pkg/apis/external_metrics",
|
||||||
|
"pkg/apis/external_metrics/install",
|
||||||
|
"pkg/apis/external_metrics/v1beta1"
|
||||||
|
]
|
||||||
|
revision = "89f8a18a5efb0c0162a32c75db752bc53ed7f8ee"
|
||||||
|
version = "kubernetes-1.11.0-rc.1"
|
||||||
|
|
||||||
|
[solve-meta]
|
||||||
|
analyzer-name = "dep"
|
||||||
|
analyzer-version = 1
|
||||||
|
inputs-digest = "922da691d7be0fa3bde2ab628c629fea6718792cb234a2e5c661a193f0545d6f"
|
||||||
|
solver-name = "gps-cdcl"
|
||||||
|
solver-version = 1
|
||||||
82
Gopkg.toml
Normal file
82
Gopkg.toml
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
# Gopkg.toml example
|
||||||
|
#
|
||||||
|
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||||
|
# for detailed Gopkg.toml documentation.
|
||||||
|
#
|
||||||
|
# required = ["github.com/user/thing/cmd/thing"]
|
||||||
|
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||||
|
#
|
||||||
|
# [[constraint]]
|
||||||
|
# name = "github.com/user/project"
|
||||||
|
# version = "1.0.0"
|
||||||
|
#
|
||||||
|
# [[constraint]]
|
||||||
|
# name = "github.com/user/project2"
|
||||||
|
# branch = "dev"
|
||||||
|
# source = "github.com/myfork/project2"
|
||||||
|
#
|
||||||
|
# [[override]]
|
||||||
|
# name = "github.com/x/y"
|
||||||
|
# version = "2.4.0"
|
||||||
|
#
|
||||||
|
# [prune]
|
||||||
|
# non-go = false
|
||||||
|
# go-tests = true
|
||||||
|
# unused-packages = true
|
||||||
|
|
||||||
|
|
||||||
|
# Utility library deps
|
||||||
|
[[constraint]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/golang/glog"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/prometheus/client_golang"
|
||||||
|
version = "0.8.0"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/prometheus/common"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/spf13/cobra"
|
||||||
|
version = "0.0.3"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "gopkg.in/yaml.v2"
|
||||||
|
version = "2.2.1"
|
||||||
|
|
||||||
|
# Kubernetes incubator deps
|
||||||
|
[[constraint]]
|
||||||
|
version = "kubernetes-1.11.0-rc.1"
|
||||||
|
name = "github.com/kubernetes-incubator/custom-metrics-apiserver"
|
||||||
|
|
||||||
|
# Core Kubernetes deps
|
||||||
|
[[constraint]]
|
||||||
|
name = "k8s.io/api"
|
||||||
|
version = "kubernetes-1.11.0-rc.1"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "k8s.io/apimachinery"
|
||||||
|
version = "kubernetes-1.11.0-rc.1"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "k8s.io/apiserver"
|
||||||
|
version = "kubernetes-1.11.0-rc.1"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "k8s.io/client-go"
|
||||||
|
version = "kubernetes-1.11.0-rc.1"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "k8s.io/metrics"
|
||||||
|
version = "kubernetes-1.11.0-rc.1"
|
||||||
|
|
||||||
|
# Test deps
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/stretchr/testify"
|
||||||
|
version = "1.2.2"
|
||||||
|
|
||||||
|
[prune]
|
||||||
|
go-tests = true
|
||||||
|
unused-packages = true
|
||||||
33
Makefile
33
Makefile
|
|
@ -8,7 +8,7 @@ OUT_DIR?=./_output
|
||||||
VENDOR_DOCKERIZED=0
|
VENDOR_DOCKERIZED=0
|
||||||
|
|
||||||
VERSION?=latest
|
VERSION?=latest
|
||||||
GOIMAGE=golang:1.8
|
GOIMAGE=golang:1.10
|
||||||
|
|
||||||
ifeq ($(ARCH),amd64)
|
ifeq ($(ARCH),amd64)
|
||||||
BASEIMAGE?=busybox
|
BASEIMAGE?=busybox
|
||||||
|
|
@ -24,25 +24,34 @@ ifeq ($(ARCH),ppc64le)
|
||||||
endif
|
endif
|
||||||
ifeq ($(ARCH),s390x)
|
ifeq ($(ARCH),s390x)
|
||||||
BASEIMAGE?=s390x/busybox
|
BASEIMAGE?=s390x/busybox
|
||||||
GOIMAGE=s390x/golang:1.8
|
GOIMAGE=s390x/golang:1.10
|
||||||
endif
|
endif
|
||||||
|
|
||||||
.PHONY: all build docker-build push-% push test verify-gofmt gofmt verify
|
.PHONY: all docker-build push-% push test verify-gofmt gofmt verify build-local-image
|
||||||
|
|
||||||
all: build
|
all: $(OUT_DIR)/$(ARCH)/adapter
|
||||||
build: vendor
|
|
||||||
CGO_ENABLED=0 GOARCH=$(ARCH) go build -a -tags netgo -o $(OUT_DIR)/$(ARCH)/adapter github.com/directxman12/k8s-prometheus-adapter/cmd/adapter
|
src_deps=$(shell find pkg cmd -type f -name "*.go")
|
||||||
|
$(OUT_DIR)/%/adapter: vendor $(src_deps)
|
||||||
|
CGO_ENABLED=0 GOARCH=$* go build -tags netgo -o $(OUT_DIR)/$(ARCH)/adapter github.com/directxman12/k8s-prometheus-adapter/cmd/adapter
|
||||||
|
|
||||||
docker-build: vendor
|
docker-build: vendor
|
||||||
cp deploy/Dockerfile $(TEMP_DIR)
|
cp deploy/Dockerfile $(TEMP_DIR)
|
||||||
cd $(TEMP_DIR) && sed -i "s|BASEIMAGE|$(BASEIMAGE)|g" Dockerfile
|
cd $(TEMP_DIR) && sed -i "s|BASEIMAGE|$(BASEIMAGE)|g" Dockerfile
|
||||||
|
|
||||||
docker run -it -v $(TEMP_DIR):/build -v $(shell pwd):/go/src/github.com/directxman12/k8s-prometheus-adapter -e GOARCH=$(ARCH) $(GOIMAGE) /bin/bash -c "\
|
docker run -it -v $(TEMP_DIR):/build -v $(shell pwd):/go/src/github.com/directxman12/k8s-prometheus-adapter -e GOARCH=$(ARCH) $(GOIMAGE) /bin/bash -c "\
|
||||||
CGO_ENABLED=0 go build -a -tags netgo -o /build/adapter github.com/directxman12/k8s-prometheus-adapter/cmd/adapter"
|
CGO_ENABLED=0 go build -tags netgo -o /build/adapter github.com/directxman12/k8s-prometheus-adapter/cmd/adapter"
|
||||||
|
|
||||||
docker build -t $(REGISTRY)/$(IMAGE)-$(ARCH):$(VERSION) $(TEMP_DIR)
|
docker build -t $(REGISTRY)/$(IMAGE)-$(ARCH):$(VERSION) $(TEMP_DIR)
|
||||||
rm -rf $(TEMP_DIR)
|
rm -rf $(TEMP_DIR)
|
||||||
|
|
||||||
|
build-local-image: $(OUT_DIR)/$(ARCH)/adapter
|
||||||
|
cp deploy/Dockerfile $(TEMP_DIR)
|
||||||
|
cp $(OUT_DIR)/$(ARCH)/adapter $(TEMP_DIR)
|
||||||
|
cd $(TEMP_DIR) && sed -i "s|BASEIMAGE|scratch|g" Dockerfile
|
||||||
|
docker build -t $(REGISTRY)/$(IMAGE)-$(ARCH):$(VERSION) $(TEMP_DIR)
|
||||||
|
rm -rf $(TEMP_DIR)
|
||||||
|
|
||||||
push-%:
|
push-%:
|
||||||
$(MAKE) ARCH=$* docker-build
|
$(MAKE) ARCH=$* docker-build
|
||||||
docker push $(REGISTRY)/$(IMAGE)-$*:$(VERSION)
|
docker push $(REGISTRY)/$(IMAGE)-$*:$(VERSION)
|
||||||
|
|
@ -54,13 +63,13 @@ push: ./manifest-tool $(addprefix push-,$(ALL_ARCH))
|
||||||
curl -sSL https://github.com/estesp/manifest-tool/releases/download/v0.5.0/manifest-tool-linux-amd64 > manifest-tool
|
curl -sSL https://github.com/estesp/manifest-tool/releases/download/v0.5.0/manifest-tool-linux-amd64 > manifest-tool
|
||||||
chmod +x manifest-tool
|
chmod +x manifest-tool
|
||||||
|
|
||||||
vendor: glide.lock
|
vendor: Gopkg.lock
|
||||||
ifeq ($(VENDOR_DOCKERIZED),1)
|
ifeq ($(VENDOR_DOCKERIZED),1)
|
||||||
docker run -it -v $(shell pwd):/go/src/github.com/directxman12/k8s-prometheus-adapter -w /go/src/github.com/directxman12/k8s-prometheus-adapter golang:1.8 /bin/bash -c "\
|
docker run -it -v $(shell pwd):/go/src/github.com/directxman12/k8s-prometheus-adapter -w /go/src/github.com/directxman12/k8s-prometheus-adapter golang:1.10 /bin/bash -c "\
|
||||||
curl https://glide.sh/get | sh \
|
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh \
|
||||||
&& glide install -v"
|
&& dep ensure -vendor-only"
|
||||||
else
|
else
|
||||||
glide install -v
|
dep ensure -vendor-only
|
||||||
endif
|
endif
|
||||||
|
|
||||||
test: vendor
|
test: vendor
|
||||||
|
|
|
||||||
30
README.md
30
README.md
|
|
@ -29,13 +29,14 @@ adapter talks to Prometheus and the main Kubernetes cluster:
|
||||||
- `--metrics-relist-interval=<duration>`: This is the interval at which to
|
- `--metrics-relist-interval=<duration>`: This is the interval at which to
|
||||||
update the cache of available metrics from Prometheus.
|
update the cache of available metrics from Prometheus.
|
||||||
|
|
||||||
- `--rate-interval=<duration>`: This is the duration used when requesting
|
|
||||||
rate metrics from Prometheus. It *must* be larger than your Prometheus
|
|
||||||
collection interval.
|
|
||||||
|
|
||||||
- `--prometheus-url=<url>`: This is the URL used to connect to Prometheus.
|
- `--prometheus-url=<url>`: This is the URL used to connect to Prometheus.
|
||||||
It will eventually contain query parameters to configure the connection.
|
It will eventually contain query parameters to configure the connection.
|
||||||
|
|
||||||
|
- `--config=<yaml-file>` (`-c`): This configures how the adapter discovers available
|
||||||
|
Prometheus metrics and the associated Kubernetes resources, and how it presents those
|
||||||
|
metrics in the custom metrics API. More information about this file can be found in
|
||||||
|
[docs/config.md](docs/config.md).
|
||||||
|
|
||||||
Presentation
|
Presentation
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
|
@ -43,18 +44,14 @@ The adapter gathers the names of available metrics from Prometheus
|
||||||
a regular interval (see [Configuration](#configuration) above), and then
|
a regular interval (see [Configuration](#configuration) above), and then
|
||||||
only exposes metrics that follow specific forms.
|
only exposes metrics that follow specific forms.
|
||||||
|
|
||||||
In general:
|
The rules governing this discovery are specified in a [configuration file](docs/config.md).
|
||||||
|
If you were relying on the implicit rules from the previous version of the adapter,
|
||||||
|
you can use the included `config-gen` tool to generate a configuration that matches
|
||||||
|
the old implicit ruleset:
|
||||||
|
|
||||||
- Metrics must have the `namespace` label to be considered.
|
```shell
|
||||||
|
$ go run cmd/config-gen main.go [--rate-interval=<duration>] [--label-prefix=<prefix>]
|
||||||
- For each label on a metric, if that label name corresponds to
|
```
|
||||||
a Kubernetes resource (like `pod` or `service`), the metric will be
|
|
||||||
associated with that resource.
|
|
||||||
|
|
||||||
- Metrics ending in `_total` are assumed to be cumulative, and will be
|
|
||||||
exposed without the suffix as a rate metric.
|
|
||||||
|
|
||||||
Detailed information can be found under [docs/format.md](docs/format.md).
|
|
||||||
|
|
||||||
Example
|
Example
|
||||||
-------
|
-------
|
||||||
|
|
@ -65,7 +62,8 @@ Additionally, [@luxas](https://github.com/luxas) has an excellent example
|
||||||
deployment of Prometheus, this adapter, and a demo pod which serves
|
deployment of Prometheus, this adapter, and a demo pod which serves
|
||||||
a metric `http_requests_total`, which becomes the custom metrics API
|
a metric `http_requests_total`, which becomes the custom metrics API
|
||||||
metric `pods/http_requests`. It also autoscales on that metric using the
|
metric `pods/http_requests`. It also autoscales on that metric using the
|
||||||
`autoscaling/v2beta1` HorizontalPodAutoscaler.
|
`autoscaling/v2beta1` HorizontalPodAutoscaler. Note that @luxas's tutorial
|
||||||
|
uses a slightly older version of the adapter.
|
||||||
|
|
||||||
It can be found at https://github.com/luxas/kubeadm-workshop. Pay special
|
It can be found at https://github.com/luxas/kubeadm-workshop. Pay special
|
||||||
attention to:
|
attention to:
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
|
||||||
"k8s.io/client-go/discovery"
|
"k8s.io/client-go/discovery"
|
||||||
"k8s.io/client-go/dynamic"
|
"k8s.io/client-go/dynamic"
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
|
|
@ -32,6 +31,7 @@ import (
|
||||||
|
|
||||||
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"
|
||||||
|
adaptercfg "github.com/directxman12/k8s-prometheus-adapter/pkg/config"
|
||||||
cmprov "github.com/directxman12/k8s-prometheus-adapter/pkg/custom-provider"
|
cmprov "github.com/directxman12/k8s-prometheus-adapter/pkg/custom-provider"
|
||||||
"github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/cmd/server"
|
"github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/cmd/server"
|
||||||
"github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/dynamicmapper"
|
"github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/dynamicmapper"
|
||||||
|
|
@ -43,9 +43,7 @@ func NewCommandStartPrometheusAdapterServer(out, errOut io.Writer, stopCh <-chan
|
||||||
o := PrometheusAdapterServerOptions{
|
o := PrometheusAdapterServerOptions{
|
||||||
CustomMetricsAdapterServerOptions: baseOpts,
|
CustomMetricsAdapterServerOptions: baseOpts,
|
||||||
MetricsRelistInterval: 10 * time.Minute,
|
MetricsRelistInterval: 10 * time.Minute,
|
||||||
RateInterval: 5 * time.Minute,
|
|
||||||
PrometheusURL: "https://localhost",
|
PrometheusURL: "https://localhost",
|
||||||
DiscoveryInterval: 10 * time.Minute,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
|
|
@ -76,19 +74,20 @@ func NewCommandStartPrometheusAdapterServer(out, errOut io.Writer, stopCh <-chan
|
||||||
"any described objets")
|
"any described objets")
|
||||||
flags.DurationVar(&o.MetricsRelistInterval, "metrics-relist-interval", o.MetricsRelistInterval, ""+
|
flags.DurationVar(&o.MetricsRelistInterval, "metrics-relist-interval", o.MetricsRelistInterval, ""+
|
||||||
"interval at which to re-list the set of all available metrics from Prometheus")
|
"interval at which to re-list the set of all available metrics from Prometheus")
|
||||||
flags.DurationVar(&o.RateInterval, "rate-interval", o.RateInterval, ""+
|
|
||||||
"period of time used to calculate rate metrics from cumulative metrics")
|
|
||||||
flags.DurationVar(&o.DiscoveryInterval, "discovery-interval", o.DiscoveryInterval, ""+
|
flags.DurationVar(&o.DiscoveryInterval, "discovery-interval", o.DiscoveryInterval, ""+
|
||||||
"interval at which to refresh API discovery information")
|
"interval at which to refresh API discovery information")
|
||||||
flags.StringVar(&o.PrometheusURL, "prometheus-url", o.PrometheusURL,
|
flags.StringVar(&o.PrometheusURL, "prometheus-url", o.PrometheusURL,
|
||||||
"URL for connecting to Prometheus. Query parameters are used to configure the connection")
|
"URL for connecting to Prometheus.")
|
||||||
flags.BoolVar(&o.PrometheusAuthInCluster, "prometheus-auth-incluster", o.PrometheusAuthInCluster,
|
flags.BoolVar(&o.PrometheusAuthInCluster, "prometheus-auth-incluster", o.PrometheusAuthInCluster,
|
||||||
"use auth details from the in-cluster kubeconfig when connecting to prometheus.")
|
"use auth details from the in-cluster kubeconfig when connecting to prometheus.")
|
||||||
flags.StringVar(&o.PrometheusAuthConf, "prometheus-auth-config", o.PrometheusAuthConf,
|
flags.StringVar(&o.PrometheusAuthConf, "prometheus-auth-config", o.PrometheusAuthConf,
|
||||||
"kubeconfig file used to configure auth when connecting to Prometheus.")
|
"kubeconfig file used to configure auth when connecting to Prometheus.")
|
||||||
flags.StringVar(&o.LabelPrefix, "label-prefix", o.LabelPrefix,
|
flags.StringVar(&o.AdapterConfigFile, "config", o.AdapterConfigFile,
|
||||||
"Prefix to expect on labels referring to pod resources. For example, if the prefix is "+
|
"Configuration file containing details of how to transform between Prometheus metrics "+
|
||||||
"'kube_', any series with the 'kube_pod' label would be considered a pod metric")
|
"and custom metrics API resources")
|
||||||
|
|
||||||
|
cmd.MarkFlagRequired("config")
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -128,6 +127,15 @@ func makeHTTPClient(inClusterAuth bool, kubeConfigPath string) (*http.Client, er
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o PrometheusAdapterServerOptions) RunCustomMetricsAdapterServer(stopCh <-chan struct{}) error {
|
func (o PrometheusAdapterServerOptions) RunCustomMetricsAdapterServer(stopCh <-chan struct{}) error {
|
||||||
|
if o.AdapterConfigFile == "" {
|
||||||
|
return fmt.Errorf("no discovery configuration file specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
metricsConfig, err := adaptercfg.FromFile(o.AdapterConfigFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to load metrics discovery configuration: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
config, err := o.Config()
|
config, err := o.Config()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -153,12 +161,12 @@ func (o PrometheusAdapterServerOptions) RunCustomMetricsAdapterServer(stopCh <-c
|
||||||
return fmt.Errorf("unable to construct discovery client for dynamic client: %v", err)
|
return fmt.Errorf("unable to construct discovery client for dynamic client: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
dynamicMapper, err := dynamicmapper.NewRESTMapper(discoveryClient, apimeta.InterfacesForUnstructured, o.DiscoveryInterval)
|
dynamicMapper, err := dynamicmapper.NewRESTMapper(discoveryClient, o.DiscoveryInterval)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to construct dynamic discovery mapper: %v", err)
|
return fmt.Errorf("unable to construct dynamic discovery mapper: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
clientPool := dynamic.NewClientPool(clientConfig, dynamicMapper, dynamic.LegacyAPIPathResolverFunc)
|
dynamicClient, err := dynamic.NewForConfig(clientConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to construct lister client to initialize provider: %v", err)
|
return fmt.Errorf("unable to construct lister client to initialize provider: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -176,9 +184,15 @@ func (o PrometheusAdapterServerOptions) RunCustomMetricsAdapterServer(stopCh <-c
|
||||||
instrumentedGenericPromClient := mprom.InstrumentGenericAPIClient(genericPromClient, baseURL.String())
|
instrumentedGenericPromClient := mprom.InstrumentGenericAPIClient(genericPromClient, baseURL.String())
|
||||||
promClient := prom.NewClientForAPI(instrumentedGenericPromClient)
|
promClient := prom.NewClientForAPI(instrumentedGenericPromClient)
|
||||||
|
|
||||||
cmProvider := cmprov.NewPrometheusProvider(dynamicMapper, clientPool, promClient, o.LabelPrefix, o.MetricsRelistInterval, o.RateInterval, stopCh)
|
namers, err := cmprov.NamersFromConfig(metricsConfig, dynamicMapper)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to construct naming scheme from metrics rules: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
server, err := config.Complete().New("prometheus-custom-metrics-adapter", cmProvider)
|
cmProvider, runner := cmprov.NewPrometheusProvider(dynamicMapper, dynamicClient, promClient, namers, o.MetricsRelistInterval)
|
||||||
|
runner.RunUntil(stopCh)
|
||||||
|
|
||||||
|
server, err := config.Complete().New("prometheus-custom-metrics-adapter", cmProvider, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -192,8 +206,6 @@ type PrometheusAdapterServerOptions struct {
|
||||||
RemoteKubeConfigFile string
|
RemoteKubeConfigFile string
|
||||||
// MetricsRelistInterval is the interval at which to relist the set of available metrics
|
// MetricsRelistInterval is the interval at which to relist the set of available metrics
|
||||||
MetricsRelistInterval time.Duration
|
MetricsRelistInterval time.Duration
|
||||||
// RateInterval is the period of time used to calculate rate metrics
|
|
||||||
RateInterval time.Duration
|
|
||||||
// DiscoveryInterval is the interval at which discovery information is refreshed
|
// DiscoveryInterval is the interval at which discovery information is refreshed
|
||||||
DiscoveryInterval time.Duration
|
DiscoveryInterval time.Duration
|
||||||
// PrometheusURL is the URL describing how to connect to Prometheus. Query parameters configure connection options.
|
// PrometheusURL is the URL describing how to connect to Prometheus. Query parameters configure connection options.
|
||||||
|
|
@ -202,7 +214,6 @@ type PrometheusAdapterServerOptions struct {
|
||||||
PrometheusAuthInCluster bool
|
PrometheusAuthInCluster bool
|
||||||
// PrometheusAuthConf is the kubeconfig file that contains auth details used to connect to Prometheus
|
// PrometheusAuthConf is the kubeconfig file that contains auth details used to connect to Prometheus
|
||||||
PrometheusAuthConf string
|
PrometheusAuthConf string
|
||||||
// LabelPrefix is the prefix to expect on labels for Kubernetes resources
|
// AdapterConfigFile points to the file containing the metrics discovery configuration.
|
||||||
// (e.g. if the prefix is "kube_", we'd expect a "kube_pod" label for pod metrics).
|
AdapterConfigFile string
|
||||||
LabelPrefix string
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
44
cmd/config-gen/main.go
Normal file
44
cmd/config-gen/main.go
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
yaml "gopkg.in/yaml.v2"
|
||||||
|
|
||||||
|
"github.com/directxman12/k8s-prometheus-adapter/cmd/config-gen/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var labelPrefix string
|
||||||
|
var rateInterval time.Duration
|
||||||
|
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Short: "Generate a config matching the legacy discovery rules",
|
||||||
|
Long: `Generate a config that produces the same functionality
|
||||||
|
as the legacy discovery rules. This includes discovering metrics and associating
|
||||||
|
resources according to the Kubernetes instrumention conventions and the cAdvisor
|
||||||
|
conventions, and auto-converting cumulative metrics into rate metrics.`,
|
||||||
|
RunE: func(c *cobra.Command, args []string) error {
|
||||||
|
cfg := utils.DefaultConfig(rateInterval, labelPrefix)
|
||||||
|
enc := yaml.NewEncoder(os.Stdout)
|
||||||
|
if err := enc.Encode(cfg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return enc.Close()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Flags().StringVar(&labelPrefix, "label-prefix", "",
|
||||||
|
"Prefix to expect on labels referring to pod resources. For example, if the prefix is "+
|
||||||
|
"'kube_', any series with the 'kube_pod' label would be considered a pod metric")
|
||||||
|
cmd.Flags().DurationVar(&rateInterval, "rate-interval", 5*time.Minute,
|
||||||
|
"Period of time used to calculate rate metrics from cumulative metrics")
|
||||||
|
|
||||||
|
if err := cmd.Execute(); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Unable to generate config: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
93
cmd/config-gen/utils/default.go
Normal file
93
cmd/config-gen/utils/default.go
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
prom "github.com/directxman12/k8s-prometheus-adapter/pkg/client"
|
||||||
|
. "github.com/directxman12/k8s-prometheus-adapter/pkg/config"
|
||||||
|
pmodel "github.com/prometheus/common/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultConfig returns a configuration equivalent to the former
|
||||||
|
// pre-advanced-config settings. This means that "normal" series labels
|
||||||
|
// will be of the form `<prefix><<.Resource>>`, cadvisor series will be
|
||||||
|
// of the form `container_`, and have the label `pod_name`. Any series ending
|
||||||
|
// in total will be treated as a rate metric.
|
||||||
|
func DefaultConfig(rateInterval time.Duration, labelPrefix string) *MetricsDiscoveryConfig {
|
||||||
|
return &MetricsDiscoveryConfig{
|
||||||
|
Rules: []DiscoveryRule{
|
||||||
|
// container seconds rate metrics
|
||||||
|
{
|
||||||
|
SeriesQuery: string(prom.MatchSeries("", prom.NameMatches("^container_.*"), prom.LabelNeq("container_name", "POD"), prom.LabelNeq("namespace", ""), prom.LabelNeq("pod_name", ""))),
|
||||||
|
Resources: ResourceMapping{
|
||||||
|
Overrides: map[string]GroupResource{
|
||||||
|
"namespace": {Resource: "namespace"},
|
||||||
|
"pod_name": {Resource: "pod"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Name: NameMapping{Matches: "^container_(.*)_seconds_total$"},
|
||||||
|
MetricsQuery: fmt.Sprintf(`sum(rate(<<.Series>>{<<.LabelMatchers>>,container_name!="POD"}[%s])) by (<<.GroupBy>>)`, pmodel.Duration(rateInterval).String()),
|
||||||
|
},
|
||||||
|
|
||||||
|
// container rate metrics
|
||||||
|
{
|
||||||
|
SeriesQuery: string(prom.MatchSeries("", prom.NameMatches("^container_.*"), prom.LabelNeq("container_name", "POD"), prom.LabelNeq("namespace", ""), prom.LabelNeq("pod_name", ""))),
|
||||||
|
SeriesFilters: []RegexFilter{{IsNot: "^container_.*_seconds_total$"}},
|
||||||
|
Resources: ResourceMapping{
|
||||||
|
Overrides: map[string]GroupResource{
|
||||||
|
"namespace": {Resource: "namespace"},
|
||||||
|
"pod_name": {Resource: "pod"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Name: NameMapping{Matches: "^container_(.*)_total$"},
|
||||||
|
MetricsQuery: fmt.Sprintf(`sum(rate(<<.Series>>{<<.LabelMatchers>>,container_name!="POD"}[%s])) by (<<.GroupBy>>)`, pmodel.Duration(rateInterval).String()),
|
||||||
|
},
|
||||||
|
|
||||||
|
// container non-cumulative metrics
|
||||||
|
{
|
||||||
|
SeriesQuery: string(prom.MatchSeries("", prom.NameMatches("^container_.*"), prom.LabelNeq("container_name", "POD"), prom.LabelNeq("namespace", ""), prom.LabelNeq("pod_name", ""))),
|
||||||
|
SeriesFilters: []RegexFilter{{IsNot: "^container_.*_total$"}},
|
||||||
|
Resources: ResourceMapping{
|
||||||
|
Overrides: map[string]GroupResource{
|
||||||
|
"namespace": {Resource: "namespace"},
|
||||||
|
"pod_name": {Resource: "pod"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Name: NameMapping{Matches: "^container_(.*)$"},
|
||||||
|
MetricsQuery: `sum(<<.Series>>{<<.LabelMatchers>>,container_name!="POD"}) by (<<.GroupBy>>)`,
|
||||||
|
},
|
||||||
|
|
||||||
|
// normal non-cumulative metrics
|
||||||
|
{
|
||||||
|
SeriesQuery: string(prom.MatchSeries("", prom.LabelNeq(fmt.Sprintf("%snamespace", labelPrefix), ""), prom.NameNotMatches("^container_.*"))),
|
||||||
|
SeriesFilters: []RegexFilter{{IsNot: ".*_total$"}},
|
||||||
|
Resources: ResourceMapping{
|
||||||
|
Template: fmt.Sprintf("%s<<.Resource>>", labelPrefix),
|
||||||
|
},
|
||||||
|
MetricsQuery: "sum(<<.Series>>{<<.LabelMatchers>>}) by (<<.GroupBy>>)",
|
||||||
|
},
|
||||||
|
|
||||||
|
// normal rate metrics
|
||||||
|
{
|
||||||
|
SeriesQuery: string(prom.MatchSeries("", prom.LabelNeq(fmt.Sprintf("%snamespace", labelPrefix), ""), prom.NameNotMatches("^container_.*"))),
|
||||||
|
SeriesFilters: []RegexFilter{{IsNot: ".*_seconds_total"}},
|
||||||
|
Name: NameMapping{Matches: "^(.*)_total$"},
|
||||||
|
Resources: ResourceMapping{
|
||||||
|
Template: fmt.Sprintf("%s<<.Resource>>", labelPrefix),
|
||||||
|
},
|
||||||
|
MetricsQuery: fmt.Sprintf("sum(rate(<<.Series>>{<<.LabelMatchers>>}[%s])) by (<<.GroupBy>>)", pmodel.Duration(rateInterval).String()),
|
||||||
|
},
|
||||||
|
|
||||||
|
// seconds rate metrics
|
||||||
|
{
|
||||||
|
SeriesQuery: string(prom.MatchSeries("", prom.LabelNeq(fmt.Sprintf("%snamespace", labelPrefix), ""), prom.NameNotMatches("^container_.*"))),
|
||||||
|
Name: NameMapping{Matches: "^(.*)_seconds_total$"},
|
||||||
|
Resources: ResourceMapping{
|
||||||
|
Template: fmt.Sprintf("%s<<.Resource>>", labelPrefix),
|
||||||
|
},
|
||||||
|
MetricsQuery: fmt.Sprintf("sum(rate(<<.Series>>{<<.LabelMatchers>>}[%s])) by (<<.GroupBy>>)", pmodel.Duration(rateInterval).String()),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -28,15 +28,21 @@ spec:
|
||||||
- --logtostderr=true
|
- --logtostderr=true
|
||||||
- --prometheus-url=http://prometheus.prom.svc:9090/
|
- --prometheus-url=http://prometheus.prom.svc:9090/
|
||||||
- --metrics-relist-interval=30s
|
- --metrics-relist-interval=30s
|
||||||
- --rate-interval=5m
|
|
||||||
- --v=10
|
- --v=10
|
||||||
|
- --config=/default-config.yaml
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 6443
|
- containerPort: 6443
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- mountPath: /var/run/serving-cert
|
- mountPath: /var/run/serving-cert
|
||||||
name: volume-serving-cert
|
name: volume-serving-cert
|
||||||
readOnly: true
|
readOnly: true
|
||||||
|
- mountPath: /etc/adapter/
|
||||||
|
name: config
|
||||||
|
readOnly: true
|
||||||
volumes:
|
volumes:
|
||||||
- name: volume-serving-cert
|
- name: volume-serving-cert
|
||||||
secret:
|
secret:
|
||||||
secretName: cm-adapter-serving-certs
|
secretName: cm-adapter-serving-certs
|
||||||
|
- name: config
|
||||||
|
configMap:
|
||||||
|
name: adapter-config
|
||||||
|
|
|
||||||
74
deploy/manifests/custom-metrics-config-map.yaml
Normal file
74
deploy/manifests/custom-metrics-config-map.yaml
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: adapter-config
|
||||||
|
namespace: custom-metrics
|
||||||
|
data:
|
||||||
|
config.yaml: |
|
||||||
|
rules:
|
||||||
|
- seriesQuery: '{__name__=~"^container_.*",container_name!="POD",namespace!="",pod_name!=""}'
|
||||||
|
seriesFilters: []
|
||||||
|
resources:
|
||||||
|
overrides:
|
||||||
|
namespace:
|
||||||
|
resource: namespace
|
||||||
|
pod_name:
|
||||||
|
resource: pod
|
||||||
|
name:
|
||||||
|
matches: ^container_(.*)_seconds_total$
|
||||||
|
as: ""
|
||||||
|
metricsQuery: sum(rate(<<.Series>>{<<.LabelMatchers>>,container_name!="POD"}[5m]))
|
||||||
|
by (<<.GroupBy>>)
|
||||||
|
- seriesQuery: '{__name__=~"^container_.*",container_name!="POD",namespace!="",pod_name!=""}'
|
||||||
|
seriesFilters:
|
||||||
|
- isNot: ^container_.*_seconds_total$
|
||||||
|
resources:
|
||||||
|
overrides:
|
||||||
|
namespace:
|
||||||
|
resource: namespace
|
||||||
|
pod_name:
|
||||||
|
resource: pod
|
||||||
|
name:
|
||||||
|
matches: ^container_(.*)_total$
|
||||||
|
as: ""
|
||||||
|
metricsQuery: sum(rate(<<.Series>>{<<.LabelMatchers>>,container_name!="POD"}[5m]))
|
||||||
|
by (<<.GroupBy>>)
|
||||||
|
- seriesQuery: '{__name__=~"^container_.*",container_name!="POD",namespace!="",pod_name!=""}'
|
||||||
|
seriesFilters:
|
||||||
|
- isNot: ^container_.*_total$
|
||||||
|
resources:
|
||||||
|
overrides:
|
||||||
|
namespace:
|
||||||
|
resource: namespace
|
||||||
|
pod_name:
|
||||||
|
resource: pod
|
||||||
|
name:
|
||||||
|
matches: ^container_(.*)$
|
||||||
|
as: ""
|
||||||
|
metricsQuery: sum(<<.Series>>{<<.LabelMatchers>>,container_name!="POD"}) by (<<.GroupBy>>)
|
||||||
|
- seriesQuery: '{namespace!="",__name__!~"^container_.*"}'
|
||||||
|
seriesFilters:
|
||||||
|
- isNot: .*_total$
|
||||||
|
resources:
|
||||||
|
template: <<.Resource>>
|
||||||
|
name:
|
||||||
|
matches: ""
|
||||||
|
as: ""
|
||||||
|
metricsQuery: sum(<<.Series>>{<<.LabelMatchers>>}) by (<<.GroupBy>>)
|
||||||
|
- seriesQuery: '{namespace!="",__name__!~"^container_.*"}'
|
||||||
|
seriesFilters:
|
||||||
|
- isNot: .*_seconds_total
|
||||||
|
resources:
|
||||||
|
template: <<.Resource>>
|
||||||
|
name:
|
||||||
|
matches: ^(.*)_total$
|
||||||
|
as: ""
|
||||||
|
metricsQuery: sum(rate(<<.Series>>{<<.LabelMatchers>>}[5m])) by (<<.GroupBy>>)
|
||||||
|
- seriesQuery: '{namespace!="",__name__!~"^container_.*"}'
|
||||||
|
seriesFilters: []
|
||||||
|
resources:
|
||||||
|
template: <<.Resource>>
|
||||||
|
name:
|
||||||
|
matches: ^(.*)_seconds_total$
|
||||||
|
as: ""
|
||||||
|
metricsQuery: sum(rate(<<.Series>>{<<.LabelMatchers>>}[5m])) by (<<.GroupBy>>)
|
||||||
208
docs/config.md
Normal file
208
docs/config.md
Normal file
|
|
@ -0,0 +1,208 @@
|
||||||
|
Metrics Discovery and Presentation Configuration
|
||||||
|
================================================
|
||||||
|
|
||||||
|
The adapter determines which metrics to expose, and how to expose them,
|
||||||
|
through a set of "discovery" rules. Each rule is executed independently
|
||||||
|
(so make sure that your rules are mutually exclusive), and specifies each
|
||||||
|
of the steps the adapter needs to take to expose a metric in the API.
|
||||||
|
|
||||||
|
Each rule can be broken down into roughly four parts:
|
||||||
|
|
||||||
|
- *Discovery*, which specifies how the adapter should find all Prometheus
|
||||||
|
metrics for this rule.
|
||||||
|
|
||||||
|
- *Association*, which specifies how the adapter should determine which
|
||||||
|
Kubernetes resources a particular metric is associated with.
|
||||||
|
|
||||||
|
- *Naming*, which specifies how the adapter should expose the metric in
|
||||||
|
the custom metrics API.
|
||||||
|
|
||||||
|
- *Querying*, which specifies how a request for a particular metric on one
|
||||||
|
or more Kubernetes objects should be turned into a query to Prometheus.
|
||||||
|
|
||||||
|
A more comprehensive configuration file can be found in
|
||||||
|
[sample-config.yaml](sample-config.yaml), but a basic config with one rule
|
||||||
|
might look like:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
rules:
|
||||||
|
# this rule matches cumulative cAdvisor metrics measured in seconds
|
||||||
|
- seriesQuery: '{__name__=~"^container_.*",container_name!="POD",namespace!="",pod_name!=""}'
|
||||||
|
resources:
|
||||||
|
# skip specifying generic resource<->label mappings, and just
|
||||||
|
# attach only pod and namespace resources by mapping label names to group-resources
|
||||||
|
overrides:
|
||||||
|
namespace: {resource: "namespace"},
|
||||||
|
pod_name: {resource: "pod"},
|
||||||
|
# specify that the `container_` and `_seconds_total` suffixes should be removed.
|
||||||
|
# this also introduces an implicit filter on metric family names
|
||||||
|
name:
|
||||||
|
# we use the value of the capture group implicitly as the API name
|
||||||
|
# we could also explicitly write `as: "$1"`
|
||||||
|
matches: "^container_(.*)_seconds_total$"
|
||||||
|
# specify how to construct a query to fetch samples for a given series
|
||||||
|
# This is a Go template where the `.Series` and `.LabelMatchers` string values
|
||||||
|
# are available, and the delimiters are `<<` and `>>` to avoid conflicts with
|
||||||
|
# the prometheus query language
|
||||||
|
metricsQuery: "sum(rate(<<.Series>>{<<.LabelMatchers>>,container_name!="POD"}[2m])) by (<<.GroupBy>>)"
|
||||||
|
```
|
||||||
|
|
||||||
|
Discovery
|
||||||
|
---------
|
||||||
|
|
||||||
|
Discovery governs the process of finding the metrics that you want to
|
||||||
|
expose in the custom metrics API. There are two fields that factor into
|
||||||
|
discovery: `seriesQuery` and `seriesFilters`.
|
||||||
|
|
||||||
|
`seriesQuery` specifies Prometheus series query (as passed to the
|
||||||
|
`/api/v1/series` endpoint in Prometheus) to use to find some set of
|
||||||
|
Prometheus series. The adapter will strip the label values from this
|
||||||
|
series, and then use the resulting metric-name-label-names combinations
|
||||||
|
later on.
|
||||||
|
|
||||||
|
In many cases, `seriesQuery` will be sufficient to narrow down the list of
|
||||||
|
Prometheus series. However, sometimes (especially if two rules might
|
||||||
|
otherwise overlap), it's useful to do additional filtering on metric
|
||||||
|
names. In this case, `seriesFilters` can be used. After the list of
|
||||||
|
series is returned from `seriesQuery`, each series has its metric name
|
||||||
|
filtered through any specified filters.
|
||||||
|
|
||||||
|
Filters may be either:
|
||||||
|
|
||||||
|
- `is: <regex>`, which matches any series whose name matches the specified
|
||||||
|
regex.
|
||||||
|
|
||||||
|
- `isNot: <regex>`, which matches any series whose name does not match the
|
||||||
|
specified regex.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# match all cAdvisor metrics that aren't measured in seconds
|
||||||
|
seriesQuery: '{__name__=~"^container_.*_total",container_name!="POD",namespace!="",pod_name!=""}'
|
||||||
|
seriesFilters:
|
||||||
|
isNot: "^container_.*_seconds_total"
|
||||||
|
```
|
||||||
|
|
||||||
|
Association
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Association governs the process of figuring out which Kubernetes resources
|
||||||
|
a particular metric could be attached to. The `resources` field controls
|
||||||
|
this process.
|
||||||
|
|
||||||
|
There are two ways to associate resources with a particular metric. In
|
||||||
|
both cases, the value of the label becomes the name of the particular
|
||||||
|
object.
|
||||||
|
|
||||||
|
One way is to specify that any label name that matches some particular
|
||||||
|
pattern refers to some group-resource based on the label name. This can
|
||||||
|
be done using the `template` field. The pattern is specified as a Go
|
||||||
|
template, with the `Group` and `Resource` fields representing group and
|
||||||
|
resource. You don't necessarily have to use the `Group` field (in which
|
||||||
|
case the group is guessed by the system). For instance:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# any label `kube_<group>_<resource>` becomes <group>.<resource> in Kubernetes
|
||||||
|
resources:
|
||||||
|
template: "kube_<<.Group>>_<<.Resource>>"
|
||||||
|
```
|
||||||
|
|
||||||
|
The other way is to specify that some particular label represents some
|
||||||
|
particular Kubernetes resource. This can be done using the `overrides`
|
||||||
|
field. Each override maps a Prometheus label to a Kubernetes
|
||||||
|
group-resource. For instance:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# the microservice label corresponds to the apps.deployment resource
|
||||||
|
resource:
|
||||||
|
overrides:
|
||||||
|
microservice: {group: "apps", resource: "deployment"}
|
||||||
|
```
|
||||||
|
|
||||||
|
These two can be combined, so you can specify both a template and some
|
||||||
|
individual overrides.
|
||||||
|
|
||||||
|
Naming
|
||||||
|
------
|
||||||
|
|
||||||
|
Naming governs the process of converting a Prometheus metric name into
|
||||||
|
a metric in the custom metrics API, and vice version. It's controlled by
|
||||||
|
the `name` field.
|
||||||
|
|
||||||
|
Naming is controlled by specifying a pattern to extract an API name from
|
||||||
|
a Prometheus name, and potentially a transformation on that extracted
|
||||||
|
value.
|
||||||
|
|
||||||
|
The pattern is specified in the `matches` field, and is just a regular
|
||||||
|
expression. If not specified, it defaults to `.*`.
|
||||||
|
|
||||||
|
The transformation is specified by the `as` field. You can use any
|
||||||
|
capture groups defined in the `matches` field. If the `matches` field
|
||||||
|
doesn't contain capture groups, the `as` field defaults to `$0`. If it
|
||||||
|
contains a single capture group, the `as` field defautls to `$1`.
|
||||||
|
Otherwise, it's an error not to specify the as field.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# match turn any name <name>_total to <name>_per_second
|
||||||
|
# e.g. http_requests_total becomes http_requests_per_second
|
||||||
|
name:
|
||||||
|
matches: "^(.*)_total$"
|
||||||
|
as: "<<1}_per_second"
|
||||||
|
```
|
||||||
|
|
||||||
|
Querying
|
||||||
|
--------
|
||||||
|
|
||||||
|
Querying governs the process of actually fetching values for a particular
|
||||||
|
metric. It's controlled by the `metricsQuery` field.
|
||||||
|
|
||||||
|
The `metricsQuery` field is a Go template that gets turned into
|
||||||
|
a Prometheus query, using input from a particular call to the custom
|
||||||
|
metrics API. A given call to the custom metrics API is distilled down to
|
||||||
|
a metric name, a group-resource, and one or more objects of that
|
||||||
|
group-resource. These get turned into the following fields in the
|
||||||
|
template:
|
||||||
|
|
||||||
|
- `Series`: the metric name
|
||||||
|
- `LabelMatchers`: a comma-separated list of label matchers matching the
|
||||||
|
given objects. Currently, this is the label for the particular
|
||||||
|
group-resource, plus the label for namespace, if the group-resource is
|
||||||
|
namespaced.
|
||||||
|
- `GroupBy`: a comma-separated list of labels to group by. Currently,
|
||||||
|
this contains the group-resoure label used in `LabelMarchers`.
|
||||||
|
|
||||||
|
For instance, suppose we had a series `http_requests_total` (exposed as
|
||||||
|
`http_requests_per_second` in the API) with labels `service`, `pod`,
|
||||||
|
`ingress`, `namespace`, and `verb`. The first four correspond to
|
||||||
|
Kubernetes resources. Then, if someone requested the metric
|
||||||
|
`pods/http_request_per_second` for the pods `pod1` and `pod2` in the
|
||||||
|
`somens` namespace, we'd have:
|
||||||
|
|
||||||
|
- `Series: "http_requests_total"
|
||||||
|
- `LabelMatchers: "pod=~\"pod1|pod2",namespace="somens"`
|
||||||
|
- `GroupBy`: `pod`
|
||||||
|
|
||||||
|
Additionally, there are two advanced fields that are "raw" forms of other
|
||||||
|
fields:
|
||||||
|
|
||||||
|
- `LabelValuesByName`: a map mapping the labels and values from the
|
||||||
|
`LabelMatchers` field. The values are pre-joined by `|`
|
||||||
|
(for used with the `=~` matcher in Prometheus).
|
||||||
|
- `GroupBySlice`: the slice form of `GroupBy`.
|
||||||
|
|
||||||
|
In general, you'll probably want to use the `Series`, `LabelMatchers`, and
|
||||||
|
`GroupBy` fields. The other two are for advanced usage.
|
||||||
|
|
||||||
|
The query is expected to return one value for each object requested. The
|
||||||
|
adapter will use the labels on the returned series to associate a given
|
||||||
|
series back to its corresponding object.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# convert cumulative cAdvisor metrics into rates calculated over 2 minutes
|
||||||
|
metricsQuery: "sum(rate(<<.Series>>{<<.LabelMatchers>>,container_name!="POD"}[2m])) by (<<.GroupBy>>)"
|
||||||
|
```
|
||||||
69
docs/sample-config.yaml
Normal file
69
docs/sample-config.yaml
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
rules:
|
||||||
|
# Each rule represents a some naming and discovery logic.
|
||||||
|
# Each rule is executed independently of the others, so
|
||||||
|
# take care to avoid overlap. As an optimization, rules
|
||||||
|
# with the same `seriesQuery` but different
|
||||||
|
# `name` or `seriesFilters` will use only one query to
|
||||||
|
# Prometheus for discovery.
|
||||||
|
|
||||||
|
# some of these rules are taken from the "default" configuration, which
|
||||||
|
# can be found in pkg/config/default.go
|
||||||
|
|
||||||
|
# this rule matches cumulative cAdvisor metrics measured in seconds
|
||||||
|
- seriesQuery: '{__name__=~"^container_.*",container_name!="POD",namespace!="",pod_name!=""}'
|
||||||
|
resources:
|
||||||
|
# skip specifying generic resource<->label mappings, and just
|
||||||
|
# attach only pod and namespace resources by mapping label names to group-resources
|
||||||
|
overrides:
|
||||||
|
namespace: {resource: "namespace"},
|
||||||
|
pod_name: {resource: "pod"},
|
||||||
|
# specify that the `container_` and `_seconds_total` suffixes should be removed.
|
||||||
|
# this also introduces an implicit filter on metric family names
|
||||||
|
name:
|
||||||
|
# we use the value of the capture group implicitly as the API name
|
||||||
|
# we could also explicitly write `as: "$1"`
|
||||||
|
matches: "^container_(.*)_seconds_total$"
|
||||||
|
# specify how to construct a query to fetch samples for a given series
|
||||||
|
# This is a Go template where the `.Series` and `.LabelMatchers` string values
|
||||||
|
# are available, and the delimiters are `<<` and `>>` to avoid conflicts with
|
||||||
|
# the prometheus query language
|
||||||
|
metricsQuery: "sum(rate(<<.Series>>{<<.LabelMatchers>>,container_name!="POD"}[2m])) by (<<.GroupBy>>)"
|
||||||
|
|
||||||
|
# this rule matches cumulative cAdvisor metrics not measured in seconds
|
||||||
|
- seriesQuery: '{__name__=~"^container_.*_total",container_name!="POD",namespace!="",pod_name!=""}'
|
||||||
|
resources:
|
||||||
|
overrides:
|
||||||
|
namespace: {resource: "namespace"},
|
||||||
|
pod_name: {resource: "pod"},
|
||||||
|
seriesFilters:
|
||||||
|
# since this is a superset of the query above, we introduce an additional filter here
|
||||||
|
- isNot: "^container_.*_seconds_total$"
|
||||||
|
name: {matches: "^container_(.*)_total$"}
|
||||||
|
metricsQuery: "sum(rate(<<.Series>>{<<.LabelMatchers>>,container_name!="POD"}[2m])) by (<<.GroupBy>>)"
|
||||||
|
|
||||||
|
# this rule matches cumulative non-cAdvisor metrics
|
||||||
|
- seriesQuery: '{namespace!="",__name__!="^container_.*"}'
|
||||||
|
name: {matches: "^(.*)_total$"}
|
||||||
|
resources:
|
||||||
|
# specify an a generic mapping between resources and labels. This
|
||||||
|
# is a template, like the `metricsQuery` template, except with the `.Group`
|
||||||
|
# and `.Resource` strings available. It will also be used to match labels,
|
||||||
|
# so avoid using template functions which truncate the group or resource.
|
||||||
|
# Group will be converted to a form acceptible for use as a label automatically.
|
||||||
|
template: "<<.Resource>>"
|
||||||
|
# if we wanted to, we could also specify overrides here
|
||||||
|
metricsQuery: "sum(rate(<<.Series>>{<<.LabelMatchers>>,container_name!="POD"}[2m])) by (<<.GroupBy>>)"
|
||||||
|
|
||||||
|
# this rule matches only a single metric, explicitly naming it something else
|
||||||
|
# It's series query *must* return only a single metric family
|
||||||
|
- seriesQuery: 'cheddar{sharp="true"}'
|
||||||
|
# this metric will appear as "cheesy_goodness" in the custom metrics API
|
||||||
|
name: {as: "cheesy_goodness"}
|
||||||
|
resources:
|
||||||
|
overrides:
|
||||||
|
# this should still resolve in our cluster
|
||||||
|
brand: {group: "cheese.io", resource: "brand"}
|
||||||
|
metricQuery: 'count(cheddar{sharp="true"})'
|
||||||
|
|
||||||
|
# TODO: should we be able to map to a constant instance of a resource
|
||||||
|
# (e.g. `resources: {constant: [{resource: "namespace", name: "kube-system"}}]`)?
|
||||||
571
glide.lock
generated
571
glide.lock
generated
|
|
@ -1,571 +0,0 @@
|
||||||
hash: 1d2ad16816cec54a2561278dfd05ae9725247ea9b72db5c7d330f0074d069fad
|
|
||||||
updated: 2017-09-28T15:22:28.593829441-04:00
|
|
||||||
imports:
|
|
||||||
- name: bitbucket.org/ww/goautoneg
|
|
||||||
version: 75cd24fc2f2c2a2088577d12123ddee5f54e0675
|
|
||||||
- name: github.com/beorn7/perks
|
|
||||||
version: 3ac7bf7a47d159a033b107610db8a1b6575507a4
|
|
||||||
subpackages:
|
|
||||||
- quantile
|
|
||||||
- name: github.com/coreos/etcd
|
|
||||||
version: 0520cb9304cb2385f7e72b8bc02d6e4d3257158a
|
|
||||||
subpackages:
|
|
||||||
- alarm
|
|
||||||
- auth
|
|
||||||
- auth/authpb
|
|
||||||
- client
|
|
||||||
- clientv3
|
|
||||||
- compactor
|
|
||||||
- discovery
|
|
||||||
- error
|
|
||||||
- etcdserver
|
|
||||||
- etcdserver/api
|
|
||||||
- etcdserver/api/v2http
|
|
||||||
- etcdserver/api/v2http/httptypes
|
|
||||||
- etcdserver/api/v3rpc
|
|
||||||
- etcdserver/api/v3rpc/rpctypes
|
|
||||||
- etcdserver/auth
|
|
||||||
- etcdserver/etcdserverpb
|
|
||||||
- etcdserver/membership
|
|
||||||
- etcdserver/stats
|
|
||||||
- integration
|
|
||||||
- lease
|
|
||||||
- lease/leasehttp
|
|
||||||
- lease/leasepb
|
|
||||||
- mvcc
|
|
||||||
- mvcc/backend
|
|
||||||
- mvcc/mvccpb
|
|
||||||
- pkg/adt
|
|
||||||
- pkg/contention
|
|
||||||
- pkg/cpuutil
|
|
||||||
- pkg/crc
|
|
||||||
- pkg/fileutil
|
|
||||||
- pkg/httputil
|
|
||||||
- pkg/idutil
|
|
||||||
- pkg/ioutil
|
|
||||||
- pkg/logutil
|
|
||||||
- pkg/monotime
|
|
||||||
- pkg/netutil
|
|
||||||
- pkg/pathutil
|
|
||||||
- pkg/pbutil
|
|
||||||
- pkg/runtime
|
|
||||||
- pkg/schedule
|
|
||||||
- pkg/testutil
|
|
||||||
- pkg/tlsutil
|
|
||||||
- pkg/transport
|
|
||||||
- pkg/types
|
|
||||||
- pkg/wait
|
|
||||||
- proxy/grpcproxy
|
|
||||||
- proxy/grpcproxy/cache
|
|
||||||
- raft
|
|
||||||
- raft/raftpb
|
|
||||||
- rafthttp
|
|
||||||
- snap
|
|
||||||
- snap/snappb
|
|
||||||
- store
|
|
||||||
- version
|
|
||||||
- wal
|
|
||||||
- wal/walpb
|
|
||||||
- name: github.com/coreos/go-systemd
|
|
||||||
version: 48702e0da86bd25e76cfef347e2adeb434a0d0a6
|
|
||||||
subpackages:
|
|
||||||
- daemon
|
|
||||||
- journal
|
|
||||||
- name: github.com/coreos/pkg
|
|
||||||
version: fa29b1d70f0beaddd4c7021607cc3c3be8ce94b8
|
|
||||||
subpackages:
|
|
||||||
- capnslog
|
|
||||||
- health
|
|
||||||
- httputil
|
|
||||||
- timeutil
|
|
||||||
- name: github.com/davecgh/go-spew
|
|
||||||
version: 782f4967f2dc4564575ca782fe2d04090b5faca8
|
|
||||||
subpackages:
|
|
||||||
- spew
|
|
||||||
- name: github.com/elazarl/go-bindata-assetfs
|
|
||||||
version: 3dcc96556217539f50599357fb481ac0dc7439b9
|
|
||||||
- name: github.com/emicklei/go-restful
|
|
||||||
version: ff4f55a206334ef123e4f79bbf348980da81ca46
|
|
||||||
subpackages:
|
|
||||||
- log
|
|
||||||
- name: github.com/emicklei/go-restful-swagger12
|
|
||||||
version: dcef7f55730566d41eae5db10e7d6981829720f6
|
|
||||||
- name: github.com/evanphx/json-patch
|
|
||||||
version: 944e07253867aacae43c04b2e6a239005443f33a
|
|
||||||
- name: github.com/ghodss/yaml
|
|
||||||
version: 73d445a93680fa1a78ae23a5839bad48f32ba1ee
|
|
||||||
- name: github.com/go-openapi/jsonpointer
|
|
||||||
version: 46af16f9f7b149af66e5d1bd010e3574dc06de98
|
|
||||||
- name: github.com/go-openapi/jsonreference
|
|
||||||
version: 13c6e3589ad90f49bd3e3bbe2c2cb3d7a4142272
|
|
||||||
- name: github.com/go-openapi/spec
|
|
||||||
version: 6aced65f8501fe1217321abf0749d354824ba2ff
|
|
||||||
- name: github.com/go-openapi/swag
|
|
||||||
version: 1d0bd113de87027671077d3c71eb3ac5d7dbba72
|
|
||||||
- name: github.com/gogo/protobuf
|
|
||||||
version: c0656edd0d9eab7c66d1eb0c568f9039345796f7
|
|
||||||
subpackages:
|
|
||||||
- proto
|
|
||||||
- sortkeys
|
|
||||||
- name: github.com/golang/glog
|
|
||||||
version: 44145f04b68cf362d9c4df2182967c2275eaefed
|
|
||||||
- name: github.com/golang/protobuf
|
|
||||||
version: 4bd1920723d7b7c925de087aa32e2187708897f7
|
|
||||||
subpackages:
|
|
||||||
- jsonpb
|
|
||||||
- proto
|
|
||||||
- ptypes
|
|
||||||
- ptypes/any
|
|
||||||
- ptypes/duration
|
|
||||||
- ptypes/timestamp
|
|
||||||
- name: github.com/google/btree
|
|
||||||
version: 7d79101e329e5a3adf994758c578dab82b90c017
|
|
||||||
- name: github.com/google/gofuzz
|
|
||||||
version: 44d81051d367757e1c7c6a5a86423ece9afcf63c
|
|
||||||
- name: github.com/googleapis/gnostic
|
|
||||||
version: 0c5108395e2debce0d731cf0287ddf7242066aba
|
|
||||||
subpackages:
|
|
||||||
- OpenAPIv2
|
|
||||||
- compiler
|
|
||||||
- extensions
|
|
||||||
- name: github.com/gregjones/httpcache
|
|
||||||
version: 787624de3eb7bd915c329cba748687a3b22666a6
|
|
||||||
subpackages:
|
|
||||||
- diskcache
|
|
||||||
- name: github.com/grpc-ecosystem/go-grpc-prometheus
|
|
||||||
version: 2500245aa6110c562d17020fb31a2c133d737799
|
|
||||||
- name: github.com/grpc-ecosystem/grpc-gateway
|
|
||||||
version: 84398b94e188ee336f307779b57b3aa91af7063c
|
|
||||||
subpackages:
|
|
||||||
- runtime
|
|
||||||
- runtime/internal
|
|
||||||
- utilities
|
|
||||||
- name: github.com/hashicorp/golang-lru
|
|
||||||
version: a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4
|
|
||||||
subpackages:
|
|
||||||
- simplelru
|
|
||||||
- name: github.com/howeyc/gopass
|
|
||||||
version: bf9dde6d0d2c004a008c27aaee91170c786f6db8
|
|
||||||
- name: github.com/imdario/mergo
|
|
||||||
version: 6633656539c1639d9d78127b7d47c622b5d7b6dc
|
|
||||||
- name: github.com/inconshreveable/mousetrap
|
|
||||||
version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
|
|
||||||
- name: github.com/json-iterator/go
|
|
||||||
version: 36b14963da70d11297d313183d7e6388c8510e1e
|
|
||||||
- name: github.com/juju/ratelimit
|
|
||||||
version: 5b9ff866471762aa2ab2dced63c9fb6f53921342
|
|
||||||
- name: github.com/kubernetes-incubator/custom-metrics-apiserver
|
|
||||||
version: fae01650d93f5de6151a024e36323344e14427aa
|
|
||||||
subpackages:
|
|
||||||
- pkg/apiserver
|
|
||||||
- pkg/apiserver/installer
|
|
||||||
- pkg/cmd/server
|
|
||||||
- pkg/dynamicmapper
|
|
||||||
- pkg/provider
|
|
||||||
- pkg/registry/custom_metrics
|
|
||||||
- name: github.com/mailru/easyjson
|
|
||||||
version: d5b7844b561a7bc640052f1b935f7b800330d7e0
|
|
||||||
subpackages:
|
|
||||||
- buffer
|
|
||||||
- jlexer
|
|
||||||
- jwriter
|
|
||||||
- name: github.com/matttproud/golang_protobuf_extensions
|
|
||||||
version: fc2b8d3a73c4867e51861bbdd5ae3c1f0869dd6a
|
|
||||||
subpackages:
|
|
||||||
- pbutil
|
|
||||||
- name: github.com/mxk/go-flowrate
|
|
||||||
version: cca7078d478f8520f85629ad7c68962d31ed7682
|
|
||||||
subpackages:
|
|
||||||
- flowrate
|
|
||||||
- name: github.com/NYTimes/gziphandler
|
|
||||||
version: 56545f4a5d46df9a6648819d1664c3a03a13ffdb
|
|
||||||
- name: github.com/pborman/uuid
|
|
||||||
version: ca53cad383cad2479bbba7f7a1a05797ec1386e4
|
|
||||||
- name: github.com/peterbourgon/diskv
|
|
||||||
version: 5f041e8faa004a95c88a202771f4cc3e991971e6
|
|
||||||
- name: github.com/pkg/errors
|
|
||||||
version: a22138067af1c4942683050411a841ade67fe1eb
|
|
||||||
- name: github.com/prometheus/client_golang
|
|
||||||
version: e7e903064f5e9eb5da98208bae10b475d4db0f8c
|
|
||||||
subpackages:
|
|
||||||
- prometheus
|
|
||||||
- name: github.com/prometheus/client_model
|
|
||||||
version: fa8ad6fec33561be4280a8f0514318c79d7f6cb6
|
|
||||||
subpackages:
|
|
||||||
- go
|
|
||||||
- name: github.com/prometheus/common
|
|
||||||
version: 13ba4ddd0caa9c28ca7b7bffe1dfa9ed8d5ef207
|
|
||||||
subpackages:
|
|
||||||
- expfmt
|
|
||||||
- internal/bitbucket.org/ww/goautoneg
|
|
||||||
- model
|
|
||||||
- name: github.com/prometheus/procfs
|
|
||||||
version: 65c1f6f8f0fc1e2185eb9863a3bc751496404259
|
|
||||||
subpackages:
|
|
||||||
- xfs
|
|
||||||
- name: github.com/PuerkitoBio/purell
|
|
||||||
version: 8a290539e2e8629dbc4e6bad948158f790ec31f4
|
|
||||||
- name: github.com/PuerkitoBio/urlesc
|
|
||||||
version: 5bd2802263f21d8788851d5305584c82a5c75d7e
|
|
||||||
- name: github.com/spf13/cobra
|
|
||||||
version: f62e98d28ab7ad31d707ba837a966378465c7b57
|
|
||||||
- name: github.com/spf13/pflag
|
|
||||||
version: 9ff6c6923cfffbcd502984b8e0c80539a94968b7
|
|
||||||
- name: github.com/stretchr/testify
|
|
||||||
version: 69483b4bd14f5845b5a1e55bca19e954e827f1d0
|
|
||||||
subpackages:
|
|
||||||
- assert
|
|
||||||
- require
|
|
||||||
- name: github.com/ugorji/go
|
|
||||||
version: ded73eae5db7e7a0ef6f55aace87a2873c5d2b74
|
|
||||||
subpackages:
|
|
||||||
- codec
|
|
||||||
- name: golang.org/x/crypto
|
|
||||||
version: 81e90905daefcd6fd217b62423c0908922eadb30
|
|
||||||
subpackages:
|
|
||||||
- bcrypt
|
|
||||||
- blowfish
|
|
||||||
- nacl/secretbox
|
|
||||||
- poly1305
|
|
||||||
- salsa20/salsa
|
|
||||||
- ssh/terminal
|
|
||||||
- name: golang.org/x/net
|
|
||||||
version: 1c05540f6879653db88113bc4a2b70aec4bd491f
|
|
||||||
subpackages:
|
|
||||||
- context
|
|
||||||
- html
|
|
||||||
- html/atom
|
|
||||||
- http2
|
|
||||||
- http2/hpack
|
|
||||||
- idna
|
|
||||||
- internal/timeseries
|
|
||||||
- lex/httplex
|
|
||||||
- trace
|
|
||||||
- websocket
|
|
||||||
- name: golang.org/x/sys
|
|
||||||
version: 7ddbeae9ae08c6a06a59597f0c9edbc5ff2444ce
|
|
||||||
subpackages:
|
|
||||||
- unix
|
|
||||||
- windows
|
|
||||||
- name: golang.org/x/text
|
|
||||||
version: b19bf474d317b857955b12035d2c5acb57ce8b01
|
|
||||||
subpackages:
|
|
||||||
- cases
|
|
||||||
- internal
|
|
||||||
- internal/tag
|
|
||||||
- language
|
|
||||||
- runes
|
|
||||||
- secure/bidirule
|
|
||||||
- secure/precis
|
|
||||||
- transform
|
|
||||||
- unicode/bidi
|
|
||||||
- unicode/norm
|
|
||||||
- width
|
|
||||||
- name: google.golang.org/genproto
|
|
||||||
version: 09f6ed296fc66555a25fe4ce95173148778dfa85
|
|
||||||
subpackages:
|
|
||||||
- googleapis/rpc/status
|
|
||||||
- name: google.golang.org/grpc
|
|
||||||
version: d2e1b51f33ff8c5e4a15560ff049d200e83726c5
|
|
||||||
subpackages:
|
|
||||||
- codes
|
|
||||||
- credentials
|
|
||||||
- grpclb/grpc_lb_v1
|
|
||||||
- grpclog
|
|
||||||
- internal
|
|
||||||
- keepalive
|
|
||||||
- metadata
|
|
||||||
- naming
|
|
||||||
- peer
|
|
||||||
- stats
|
|
||||||
- status
|
|
||||||
- tap
|
|
||||||
- transport
|
|
||||||
- name: gopkg.in/inf.v0
|
|
||||||
version: 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4
|
|
||||||
- name: gopkg.in/natefinch/lumberjack.v2
|
|
||||||
version: 20b71e5b60d756d3d2f80def009790325acc2b23
|
|
||||||
- name: gopkg.in/yaml.v2
|
|
||||||
version: 53feefa2559fb8dfa8d81baad31be332c97d6c77
|
|
||||||
- name: k8s.io/api
|
|
||||||
version: cadaf100c0a3dd6b254f320d6d651df079ec8e0a
|
|
||||||
subpackages:
|
|
||||||
- admissionregistration/v1alpha1
|
|
||||||
- apps/v1beta1
|
|
||||||
- apps/v1beta2
|
|
||||||
- authentication/v1
|
|
||||||
- authentication/v1beta1
|
|
||||||
- authorization/v1
|
|
||||||
- authorization/v1beta1
|
|
||||||
- autoscaling/v1
|
|
||||||
- autoscaling/v2beta1
|
|
||||||
- batch/v1
|
|
||||||
- batch/v1beta1
|
|
||||||
- batch/v2alpha1
|
|
||||||
- certificates/v1beta1
|
|
||||||
- core/v1
|
|
||||||
- extensions/v1beta1
|
|
||||||
- networking/v1
|
|
||||||
- policy/v1beta1
|
|
||||||
- rbac/v1
|
|
||||||
- rbac/v1alpha1
|
|
||||||
- rbac/v1beta1
|
|
||||||
- scheduling/v1alpha1
|
|
||||||
- settings/v1alpha1
|
|
||||||
- storage/v1
|
|
||||||
- storage/v1beta1
|
|
||||||
- name: k8s.io/apimachinery
|
|
||||||
version: 3b05bbfa0a45413bfa184edbf9af617e277962fb
|
|
||||||
subpackages:
|
|
||||||
- pkg/api/equality
|
|
||||||
- pkg/api/errors
|
|
||||||
- pkg/api/meta
|
|
||||||
- pkg/api/resource
|
|
||||||
- pkg/api/validation
|
|
||||||
- pkg/api/validation/path
|
|
||||||
- pkg/apimachinery
|
|
||||||
- pkg/apimachinery/announced
|
|
||||||
- pkg/apimachinery/registered
|
|
||||||
- pkg/apis/meta/internalversion
|
|
||||||
- pkg/apis/meta/v1
|
|
||||||
- pkg/apis/meta/v1/unstructured
|
|
||||||
- pkg/apis/meta/v1/validation
|
|
||||||
- pkg/apis/meta/v1alpha1
|
|
||||||
- pkg/conversion
|
|
||||||
- pkg/conversion/queryparams
|
|
||||||
- pkg/conversion/unstructured
|
|
||||||
- pkg/fields
|
|
||||||
- pkg/labels
|
|
||||||
- pkg/runtime
|
|
||||||
- pkg/runtime/schema
|
|
||||||
- pkg/runtime/serializer
|
|
||||||
- pkg/runtime/serializer/json
|
|
||||||
- pkg/runtime/serializer/protobuf
|
|
||||||
- pkg/runtime/serializer/recognizer
|
|
||||||
- pkg/runtime/serializer/streaming
|
|
||||||
- pkg/runtime/serializer/versioning
|
|
||||||
- pkg/selection
|
|
||||||
- pkg/types
|
|
||||||
- pkg/util/cache
|
|
||||||
- pkg/util/clock
|
|
||||||
- pkg/util/diff
|
|
||||||
- pkg/util/errors
|
|
||||||
- pkg/util/framer
|
|
||||||
- pkg/util/httpstream
|
|
||||||
- pkg/util/intstr
|
|
||||||
- pkg/util/json
|
|
||||||
- pkg/util/mergepatch
|
|
||||||
- pkg/util/net
|
|
||||||
- pkg/util/proxy
|
|
||||||
- pkg/util/rand
|
|
||||||
- pkg/util/runtime
|
|
||||||
- pkg/util/sets
|
|
||||||
- pkg/util/strategicpatch
|
|
||||||
- pkg/util/uuid
|
|
||||||
- pkg/util/validation
|
|
||||||
- pkg/util/validation/field
|
|
||||||
- pkg/util/wait
|
|
||||||
- pkg/util/yaml
|
|
||||||
- pkg/version
|
|
||||||
- pkg/watch
|
|
||||||
- third_party/forked/golang/json
|
|
||||||
- third_party/forked/golang/netutil
|
|
||||||
- third_party/forked/golang/reflect
|
|
||||||
- name: k8s.io/apiserver
|
|
||||||
version: c1e53d745d0fe45bf7d5d44697e6eface25fceca
|
|
||||||
subpackages:
|
|
||||||
- pkg/admission
|
|
||||||
- pkg/admission/initializer
|
|
||||||
- pkg/admission/plugin/namespace/lifecycle
|
|
||||||
- pkg/apis/apiserver
|
|
||||||
- pkg/apis/apiserver/install
|
|
||||||
- pkg/apis/apiserver/v1alpha1
|
|
||||||
- pkg/apis/audit
|
|
||||||
- pkg/apis/audit/install
|
|
||||||
- pkg/apis/audit/v1alpha1
|
|
||||||
- pkg/apis/audit/v1beta1
|
|
||||||
- pkg/apis/audit/validation
|
|
||||||
- pkg/audit
|
|
||||||
- pkg/audit/policy
|
|
||||||
- pkg/authentication/authenticator
|
|
||||||
- pkg/authentication/authenticatorfactory
|
|
||||||
- pkg/authentication/group
|
|
||||||
- pkg/authentication/request/anonymous
|
|
||||||
- pkg/authentication/request/bearertoken
|
|
||||||
- pkg/authentication/request/headerrequest
|
|
||||||
- pkg/authentication/request/union
|
|
||||||
- pkg/authentication/request/websocket
|
|
||||||
- pkg/authentication/request/x509
|
|
||||||
- pkg/authentication/serviceaccount
|
|
||||||
- pkg/authentication/token/tokenfile
|
|
||||||
- pkg/authentication/user
|
|
||||||
- pkg/authorization/authorizer
|
|
||||||
- pkg/authorization/authorizerfactory
|
|
||||||
- pkg/authorization/union
|
|
||||||
- pkg/endpoints
|
|
||||||
- pkg/endpoints/discovery
|
|
||||||
- pkg/endpoints/filters
|
|
||||||
- pkg/endpoints/handlers
|
|
||||||
- pkg/endpoints/handlers/negotiation
|
|
||||||
- pkg/endpoints/handlers/responsewriters
|
|
||||||
- pkg/endpoints/metrics
|
|
||||||
- pkg/endpoints/openapi
|
|
||||||
- pkg/endpoints/request
|
|
||||||
- pkg/features
|
|
||||||
- pkg/registry/generic
|
|
||||||
- pkg/registry/generic/registry
|
|
||||||
- pkg/registry/rest
|
|
||||||
- pkg/server
|
|
||||||
- pkg/server/filters
|
|
||||||
- pkg/server/healthz
|
|
||||||
- pkg/server/httplog
|
|
||||||
- pkg/server/mux
|
|
||||||
- pkg/server/options
|
|
||||||
- pkg/server/routes
|
|
||||||
- pkg/server/routes/data/swagger
|
|
||||||
- pkg/server/storage
|
|
||||||
- pkg/storage
|
|
||||||
- pkg/storage/errors
|
|
||||||
- pkg/storage/etcd
|
|
||||||
- pkg/storage/etcd/metrics
|
|
||||||
- pkg/storage/etcd/util
|
|
||||||
- pkg/storage/etcd3
|
|
||||||
- pkg/storage/etcd3/preflight
|
|
||||||
- pkg/storage/names
|
|
||||||
- pkg/storage/storagebackend
|
|
||||||
- pkg/storage/storagebackend/factory
|
|
||||||
- pkg/storage/value
|
|
||||||
- pkg/util/feature
|
|
||||||
- pkg/util/flag
|
|
||||||
- pkg/util/flushwriter
|
|
||||||
- pkg/util/logs
|
|
||||||
- pkg/util/trace
|
|
||||||
- pkg/util/webhook
|
|
||||||
- pkg/util/wsstream
|
|
||||||
- plugin/pkg/audit/log
|
|
||||||
- plugin/pkg/audit/webhook
|
|
||||||
- plugin/pkg/authenticator/token/webhook
|
|
||||||
- plugin/pkg/authorizer/webhook
|
|
||||||
- name: k8s.io/client-go
|
|
||||||
version: 82aa063804cf055e16e8911250f888bc216e8b61
|
|
||||||
subpackages:
|
|
||||||
- discovery
|
|
||||||
- dynamic
|
|
||||||
- dynamic/fake
|
|
||||||
- informers
|
|
||||||
- informers/admissionregistration
|
|
||||||
- informers/admissionregistration/v1alpha1
|
|
||||||
- informers/apps
|
|
||||||
- informers/apps/v1beta1
|
|
||||||
- informers/apps/v1beta2
|
|
||||||
- informers/autoscaling
|
|
||||||
- informers/autoscaling/v1
|
|
||||||
- informers/autoscaling/v2beta1
|
|
||||||
- informers/batch
|
|
||||||
- informers/batch/v1
|
|
||||||
- informers/batch/v1beta1
|
|
||||||
- informers/batch/v2alpha1
|
|
||||||
- informers/certificates
|
|
||||||
- informers/certificates/v1beta1
|
|
||||||
- informers/core
|
|
||||||
- informers/core/v1
|
|
||||||
- informers/extensions
|
|
||||||
- informers/extensions/v1beta1
|
|
||||||
- informers/internalinterfaces
|
|
||||||
- informers/networking
|
|
||||||
- informers/networking/v1
|
|
||||||
- informers/policy
|
|
||||||
- informers/policy/v1beta1
|
|
||||||
- informers/rbac
|
|
||||||
- informers/rbac/v1
|
|
||||||
- informers/rbac/v1alpha1
|
|
||||||
- informers/rbac/v1beta1
|
|
||||||
- informers/scheduling
|
|
||||||
- informers/scheduling/v1alpha1
|
|
||||||
- informers/settings
|
|
||||||
- informers/settings/v1alpha1
|
|
||||||
- informers/storage
|
|
||||||
- informers/storage/v1
|
|
||||||
- informers/storage/v1beta1
|
|
||||||
- kubernetes
|
|
||||||
- kubernetes/scheme
|
|
||||||
- kubernetes/typed/admissionregistration/v1alpha1
|
|
||||||
- kubernetes/typed/apps/v1beta1
|
|
||||||
- kubernetes/typed/apps/v1beta2
|
|
||||||
- kubernetes/typed/authentication/v1
|
|
||||||
- kubernetes/typed/authentication/v1beta1
|
|
||||||
- kubernetes/typed/authorization/v1
|
|
||||||
- kubernetes/typed/authorization/v1beta1
|
|
||||||
- kubernetes/typed/autoscaling/v1
|
|
||||||
- kubernetes/typed/autoscaling/v2beta1
|
|
||||||
- kubernetes/typed/batch/v1
|
|
||||||
- kubernetes/typed/batch/v1beta1
|
|
||||||
- kubernetes/typed/batch/v2alpha1
|
|
||||||
- kubernetes/typed/certificates/v1beta1
|
|
||||||
- kubernetes/typed/core/v1
|
|
||||||
- kubernetes/typed/extensions/v1beta1
|
|
||||||
- kubernetes/typed/networking/v1
|
|
||||||
- kubernetes/typed/policy/v1beta1
|
|
||||||
- kubernetes/typed/rbac/v1
|
|
||||||
- kubernetes/typed/rbac/v1alpha1
|
|
||||||
- kubernetes/typed/rbac/v1beta1
|
|
||||||
- kubernetes/typed/scheduling/v1alpha1
|
|
||||||
- kubernetes/typed/settings/v1alpha1
|
|
||||||
- kubernetes/typed/storage/v1
|
|
||||||
- kubernetes/typed/storage/v1beta1
|
|
||||||
- listers/admissionregistration/v1alpha1
|
|
||||||
- listers/apps/v1beta1
|
|
||||||
- listers/apps/v1beta2
|
|
||||||
- listers/autoscaling/v1
|
|
||||||
- listers/autoscaling/v2beta1
|
|
||||||
- listers/batch/v1
|
|
||||||
- listers/batch/v1beta1
|
|
||||||
- listers/batch/v2alpha1
|
|
||||||
- listers/certificates/v1beta1
|
|
||||||
- listers/core/v1
|
|
||||||
- listers/extensions/v1beta1
|
|
||||||
- listers/networking/v1
|
|
||||||
- listers/policy/v1beta1
|
|
||||||
- listers/rbac/v1
|
|
||||||
- listers/rbac/v1alpha1
|
|
||||||
- listers/rbac/v1beta1
|
|
||||||
- listers/scheduling/v1alpha1
|
|
||||||
- listers/settings/v1alpha1
|
|
||||||
- listers/storage/v1
|
|
||||||
- listers/storage/v1beta1
|
|
||||||
- pkg/version
|
|
||||||
- rest
|
|
||||||
- rest/watch
|
|
||||||
- testing
|
|
||||||
- tools/auth
|
|
||||||
- tools/cache
|
|
||||||
- tools/clientcmd
|
|
||||||
- tools/clientcmd/api
|
|
||||||
- tools/clientcmd/api/latest
|
|
||||||
- tools/clientcmd/api/v1
|
|
||||||
- tools/metrics
|
|
||||||
- tools/pager
|
|
||||||
- tools/reference
|
|
||||||
- transport
|
|
||||||
- util/cert
|
|
||||||
- util/flowcontrol
|
|
||||||
- util/homedir
|
|
||||||
- util/integer
|
|
||||||
- name: k8s.io/kube-openapi
|
|
||||||
version: 868f2f29720b192240e18284659231b440f9cda5
|
|
||||||
subpackages:
|
|
||||||
- pkg/builder
|
|
||||||
- pkg/common
|
|
||||||
- pkg/handler
|
|
||||||
- pkg/util
|
|
||||||
- name: k8s.io/metrics
|
|
||||||
version: 4c7ac522b9daf7beeb53f6766722ba78b7e5712d
|
|
||||||
subpackages:
|
|
||||||
- pkg/apis/custom_metrics
|
|
||||||
- pkg/apis/custom_metrics/install
|
|
||||||
- pkg/apis/custom_metrics/v1beta1
|
|
||||||
testImports:
|
|
||||||
- name: github.com/pmezard/go-difflib
|
|
||||||
version: d8ed2627bdf02c080bf22230dbb337003b7aba2d
|
|
||||||
subpackages:
|
|
||||||
- difflib
|
|
||||||
23
glide.yaml
23
glide.yaml
|
|
@ -1,23 +0,0 @@
|
||||||
package: github.com/directxman12/k8s-prometheus-adapter
|
|
||||||
import:
|
|
||||||
- package: github.com/spf13/cobra
|
|
||||||
- package: k8s.io/apimachinery
|
|
||||||
subpackages:
|
|
||||||
- pkg/util/wait
|
|
||||||
- package: k8s.io/apiserver
|
|
||||||
subpackages:
|
|
||||||
- pkg/util/logs
|
|
||||||
- package: k8s.io/client-go
|
|
||||||
subpackages:
|
|
||||||
- kubernetes/typed/core/v1
|
|
||||||
- rest
|
|
||||||
- tools/clientcmd
|
|
||||||
- package: github.com/kubernetes-incubator/custom-metrics-apiserver
|
|
||||||
subpackages:
|
|
||||||
- pkg/cmd/server
|
|
||||||
- pkg/provider
|
|
||||||
- package: github.com/stretchr/testify
|
|
||||||
version: ^1.1.4
|
|
||||||
subpackages:
|
|
||||||
- assert
|
|
||||||
- require
|
|
||||||
|
|
@ -19,6 +19,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/prometheus/common/model"
|
"github.com/prometheus/common/model"
|
||||||
|
|
@ -121,3 +122,11 @@ func (s *Series) UnmarshalJSON(data []byte) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Series) String() string {
|
||||||
|
lblStrings := make([]string, 0, len(s.Labels))
|
||||||
|
for k, v := range s.Labels {
|
||||||
|
lblStrings = append(lblStrings, fmt.Sprintf("%s=%q", k, v))
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s{%s}", s.Name, strings.Join(lblStrings, ","))
|
||||||
|
}
|
||||||
|
|
|
||||||
75
pkg/config/config.go
Normal file
75
pkg/config/config.go
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
type MetricsDiscoveryConfig struct {
|
||||||
|
// Rules specifies how to discover and map Prometheus metrics to
|
||||||
|
// custom metrics API resources. The rules are applied independently,
|
||||||
|
// and thus must be mutually exclusive. Rules will the same SeriesQuery
|
||||||
|
// will make only a single API call.
|
||||||
|
Rules []DiscoveryRule `yaml:"rules"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiscoveryRule describes on set of rules for transforming Prometheus metrics to/from
|
||||||
|
// custom metrics API resources.
|
||||||
|
type DiscoveryRule struct {
|
||||||
|
// SeriesQuery specifies which metrics this rule should consider via a Prometheus query
|
||||||
|
// series selector query.
|
||||||
|
SeriesQuery string `yaml:"seriesQuery"`
|
||||||
|
// SeriesFilters specifies additional regular expressions to be applied on
|
||||||
|
// the series names returned from the query. This is useful for constraints
|
||||||
|
// that can't be represented in the SeriesQuery (e.g. series matching `container_.+`
|
||||||
|
// not matching `container_.+_total`. A filter will be automatically appended to
|
||||||
|
// match the form specified in Name.
|
||||||
|
SeriesFilters []RegexFilter `yaml:"seriesFilters"`
|
||||||
|
// Resources specifies how associated Kubernetes resources should be discovered for
|
||||||
|
// the given metrics.
|
||||||
|
Resources ResourceMapping `yaml:"resources"`
|
||||||
|
// Name specifies how the metric name should be transformed between custom metric
|
||||||
|
// API resources, and Prometheus metric names.
|
||||||
|
Name NameMapping `yaml:"name"`
|
||||||
|
// MetricsQuery specifies modifications to the metrics query, such as converting
|
||||||
|
// cumulative metrics to rate metrics. It is a template where `.LabelMatchers` is
|
||||||
|
// a the comma-separated base label matchers and `.Series` is the series name, and
|
||||||
|
// `.GroupBy` is the comma-separated expected group-by label names. The delimeters
|
||||||
|
// are `<<` and `>>`.
|
||||||
|
MetricsQuery string `yaml:"metricsQuery,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegexFilter is a filter that matches positively or negatively against a regex.
|
||||||
|
// Only one field may be set at a time.
|
||||||
|
type RegexFilter struct {
|
||||||
|
Is string `yaml:"is,omitempty"`
|
||||||
|
IsNot string `yaml:"isNot,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResourceMapping specifies how to map Kubernetes resources to Prometheus labels
|
||||||
|
type ResourceMapping struct {
|
||||||
|
// Template specifies a golang string template for converting a Kubernetes
|
||||||
|
// group-resource to a Prometheus label. The template object contains
|
||||||
|
// the `.Group` and `.Resource` fields. The `.Group` field will have
|
||||||
|
// dots replaced with underscores, and the `.Resource` field will be
|
||||||
|
// singularized. The delimiters are `<<` and `>>`.
|
||||||
|
Template string `yaml:"template,omitempty"`
|
||||||
|
// Overrides specifies exceptions to the above template, mapping label names
|
||||||
|
// to group-resources
|
||||||
|
Overrides map[string]GroupResource `yaml:"overrides,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupResource represents a Kubernetes group-resource.
|
||||||
|
type GroupResource struct {
|
||||||
|
Group string `yaml:"group,omitempty"`
|
||||||
|
Resource string `yaml:"resource"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NameMapping specifies how to convert Prometheus metrics
|
||||||
|
// to/from custom metrics API resources.
|
||||||
|
type NameMapping struct {
|
||||||
|
// Matches is a regular expression that is used to match
|
||||||
|
// Prometheus series names. It may be left blank, in which
|
||||||
|
// case it is equivalent to `.*`.
|
||||||
|
Matches string `yaml:"matches"`
|
||||||
|
// As is the name used in the API. Captures from Matches
|
||||||
|
// are available for use here. If not specified, it defaults
|
||||||
|
// to $0 if no capture groups are present in Matches, or $1
|
||||||
|
// if only one is present, and will error if multiple are.
|
||||||
|
As string `yaml:"as"`
|
||||||
|
}
|
||||||
32
pkg/config/loader.go
Normal file
32
pkg/config/loader.go
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
yaml "gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FromFile loads the configuration from a particular file.
|
||||||
|
func FromFile(filename string) (*MetricsDiscoveryConfig, error) {
|
||||||
|
file, err := os.Open(filename)
|
||||||
|
defer file.Close()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to load metrics discovery config file: %v", err)
|
||||||
|
}
|
||||||
|
contents, err := ioutil.ReadAll(file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to load metrics discovery config file: %v", err)
|
||||||
|
}
|
||||||
|
return FromYAML(contents)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromYAML loads the configuration from a blob of YAML.
|
||||||
|
func FromYAML(contents []byte) (*MetricsDiscoveryConfig, error) {
|
||||||
|
var cfg MetricsDiscoveryConfig
|
||||||
|
if err := yaml.Unmarshal(contents, &cfg); err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to parse metrics discovery config: %v", err)
|
||||||
|
}
|
||||||
|
return &cfg, nil
|
||||||
|
}
|
||||||
|
|
@ -1,367 +1,474 @@
|
||||||
/*
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package provider
|
package provider
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/golang/glog"
|
||||||
"github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/provider"
|
"github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/provider"
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
|
||||||
prom "github.com/directxman12/k8s-prometheus-adapter/pkg/client"
|
prom "github.com/directxman12/k8s-prometheus-adapter/pkg/client"
|
||||||
"github.com/golang/glog"
|
"github.com/directxman12/k8s-prometheus-adapter/pkg/config"
|
||||||
pmodel "github.com/prometheus/common/model"
|
pmodel "github.com/prometheus/common/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NB: container metrics sourced from cAdvisor don't consistently follow naming conventions,
|
var nsGroupResource = schema.GroupResource{Resource: "namespaces"}
|
||||||
// so we need to whitelist them and handle them on a case-by-case basis. Metrics ending in `_total`
|
var groupNameSanitizer = strings.NewReplacer(".", "_", "-", "_")
|
||||||
// *should* be counters, but may actually be guages in this case.
|
|
||||||
|
|
||||||
// SeriesType represents the kind of series backing a metric.
|
// MetricNamer knows how to convert Prometheus series names and label names to
|
||||||
type SeriesType int
|
// metrics API resources, and vice-versa. MetricNamers should be safe to access
|
||||||
|
// concurrently. Returned group-resources are "normalized" as per the
|
||||||
const (
|
// MetricInfo#Normalized method. Group-resources passed as arguments must
|
||||||
CounterSeries SeriesType = iota
|
// themselves be normalized.
|
||||||
SecondsCounterSeries
|
type MetricNamer interface {
|
||||||
GaugeSeries
|
// Selector produces the appropriate Prometheus series selector to match all
|
||||||
)
|
// series handlable by this namer.
|
||||||
|
Selector() prom.Selector
|
||||||
// SeriesRegistry provides conversions between Prometheus series and MetricInfo
|
// FilterSeries checks to see which of the given series match any additional
|
||||||
type SeriesRegistry interface {
|
// constrains beyond the series query. It's assumed that the series given
|
||||||
// Selectors produces the appropriate Prometheus selectors to match all series handlable
|
// already matche the series query.
|
||||||
// by this registry, as an optimization for SetSeries.
|
FilterSeries(series []prom.Series) []prom.Series
|
||||||
Selectors() []prom.Selector
|
// ResourcesForSeries returns the group-resources associated with the given series,
|
||||||
// SetSeries replaces the known series in this registry
|
// as well as whether or not the given series has the "namespace" resource).
|
||||||
SetSeries(series []prom.Series) error
|
ResourcesForSeries(series prom.Series) (res []schema.GroupResource, namespaced bool)
|
||||||
// ListAllMetrics lists all metrics known to this registry
|
// LabelForResource returns the appropriate label for the given resource.
|
||||||
ListAllMetrics() []provider.MetricInfo
|
LabelForResource(resource schema.GroupResource) (pmodel.LabelName, error)
|
||||||
// SeriesForMetric looks up the minimum required series information to make a query for the given metric
|
// MetricNameForSeries returns the name (as presented in the API) for a given series.
|
||||||
// against the given resource (namespace may be empty for non-namespaced resources)
|
MetricNameForSeries(series prom.Series) (string, error)
|
||||||
QueryForMetric(info provider.MetricInfo, namespace string, resourceNames ...string) (kind SeriesType, query prom.Selector, groupBy string, found bool)
|
// QueryForSeries returns the query for a given series (not API metric name), with
|
||||||
// MatchValuesToNames matches result values to resource names for the given metric and value set
|
// the given namespace name (if relevant), resource, and resource names.
|
||||||
MatchValuesToNames(metricInfo provider.MetricInfo, values pmodel.Vector) (matchedValues map[string]pmodel.SampleValue, found bool)
|
QueryForSeries(series string, resource schema.GroupResource, namespace string, names ...string) (prom.Selector, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type seriesInfo struct {
|
// labelGroupResExtractor extracts schema.GroupResources from series labels.
|
||||||
// baseSeries represents the minimum information to access a particular series
|
type labelGroupResExtractor struct {
|
||||||
baseSeries prom.Series
|
regex *regexp.Regexp
|
||||||
// kind is the type of this series
|
|
||||||
kind SeriesType
|
|
||||||
// isContainer indicates if the series is a cAdvisor container_ metric, and thus needs special handling
|
|
||||||
isContainer bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// overridableSeriesRegistry is a basic SeriesRegistry
|
|
||||||
type basicSeriesRegistry struct {
|
|
||||||
mu sync.RWMutex
|
|
||||||
|
|
||||||
// info maps metric info to information about the corresponding series
|
|
||||||
info map[provider.MetricInfo]seriesInfo
|
|
||||||
// metrics is the list of all known metrics
|
|
||||||
metrics []provider.MetricInfo
|
|
||||||
|
|
||||||
// namer is the metricNamer responsible for converting series to metric names and information
|
|
||||||
namer metricNamer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *basicSeriesRegistry) Selectors() []prom.Selector {
|
|
||||||
// container-specific metrics from cAdvsior have their own form, and need special handling
|
|
||||||
// TODO: figure out how to determine which metrics on non-namespaced objects are kubernetes-related
|
|
||||||
containerSel := prom.MatchSeries("", prom.NameMatches("^container_.*"), prom.LabelNeq("container_name", "POD"), prom.LabelNeq("namespace", ""), prom.LabelNeq("pod_name", ""))
|
|
||||||
namespacedSel := prom.MatchSeries("", prom.LabelNeq(r.namer.labelPrefix+"namespace", ""), prom.NameNotMatches("^container_.*"))
|
|
||||||
|
|
||||||
return []prom.Selector{containerSel, namespacedSel}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *basicSeriesRegistry) SetSeries(newSeries []prom.Series) error {
|
|
||||||
newInfo := make(map[provider.MetricInfo]seriesInfo)
|
|
||||||
for _, series := range newSeries {
|
|
||||||
if strings.HasPrefix(series.Name, "container_") {
|
|
||||||
r.namer.processContainerSeries(series, newInfo)
|
|
||||||
} else if namespaceLabel, hasNamespaceLabel := series.Labels[pmodel.LabelName(r.namer.labelPrefix+"namespace")]; hasNamespaceLabel && namespaceLabel != "" {
|
|
||||||
// we also handle namespaced metrics here as part of the resource-association logic
|
|
||||||
if err := r.namer.processNamespacedSeries(series, newInfo); err != nil {
|
|
||||||
glog.Errorf("Unable to process namespaced series %q: %v", series.Name, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err := r.namer.processRootScopedSeries(series, newInfo); err != nil {
|
|
||||||
glog.Errorf("Unable to process root-scoped series %q: %v", series.Name, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
newMetrics := make([]provider.MetricInfo, 0, len(newInfo))
|
|
||||||
for info := range newInfo {
|
|
||||||
newMetrics = append(newMetrics, info)
|
|
||||||
}
|
|
||||||
|
|
||||||
r.mu.Lock()
|
|
||||||
defer r.mu.Unlock()
|
|
||||||
|
|
||||||
r.info = newInfo
|
|
||||||
r.metrics = newMetrics
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *basicSeriesRegistry) ListAllMetrics() []provider.MetricInfo {
|
|
||||||
r.mu.RLock()
|
|
||||||
defer r.mu.RUnlock()
|
|
||||||
|
|
||||||
return r.metrics
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *basicSeriesRegistry) QueryForMetric(metricInfo provider.MetricInfo, namespace string, resourceNames ...string) (kind SeriesType, query prom.Selector, groupBy string, found bool) {
|
|
||||||
r.mu.RLock()
|
|
||||||
defer r.mu.RUnlock()
|
|
||||||
|
|
||||||
if len(resourceNames) == 0 {
|
|
||||||
glog.Errorf("no resource names requested while producing a query for metric %s", metricInfo.String())
|
|
||||||
return 0, "", "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
metricInfo, singularResource, err := metricInfo.Normalized(r.namer.mapper)
|
|
||||||
if err != nil {
|
|
||||||
glog.Errorf("unable to normalize group resource while producing a query: %v", err)
|
|
||||||
return 0, "", "", false
|
|
||||||
}
|
|
||||||
resourceLbl := r.namer.labelPrefix + singularResource
|
|
||||||
|
|
||||||
// TODO: support container metrics
|
|
||||||
if info, found := r.info[metricInfo]; found {
|
|
||||||
targetValue := resourceNames[0]
|
|
||||||
matcher := prom.LabelEq
|
|
||||||
if len(resourceNames) > 1 {
|
|
||||||
targetValue = strings.Join(resourceNames, "|")
|
|
||||||
matcher = prom.LabelMatches
|
|
||||||
}
|
|
||||||
|
|
||||||
var expressions []string
|
|
||||||
if info.isContainer {
|
|
||||||
expressions = []string{matcher("pod_name", targetValue), prom.LabelNeq("container_name", "POD")}
|
|
||||||
groupBy = "pod_name"
|
|
||||||
} else {
|
|
||||||
// TODO: copy base series labels?
|
|
||||||
expressions = []string{matcher(resourceLbl, targetValue)}
|
|
||||||
groupBy = resourceLbl
|
|
||||||
}
|
|
||||||
|
|
||||||
if metricInfo.Namespaced {
|
|
||||||
prefix := r.namer.labelPrefix
|
|
||||||
if info.isContainer {
|
|
||||||
prefix = ""
|
|
||||||
}
|
|
||||||
expressions = append(expressions, prom.LabelEq(prefix+"namespace", namespace))
|
|
||||||
}
|
|
||||||
|
|
||||||
return info.kind, prom.MatchSeries(info.baseSeries.Name, expressions...), groupBy, true
|
|
||||||
}
|
|
||||||
|
|
||||||
glog.V(10).Infof("metric %v not registered", metricInfo)
|
|
||||||
return 0, "", "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *basicSeriesRegistry) MatchValuesToNames(metricInfo provider.MetricInfo, values pmodel.Vector) (matchedValues map[string]pmodel.SampleValue, found bool) {
|
|
||||||
r.mu.RLock()
|
|
||||||
defer r.mu.RUnlock()
|
|
||||||
|
|
||||||
metricInfo, singularResource, err := metricInfo.Normalized(r.namer.mapper)
|
|
||||||
if err != nil {
|
|
||||||
glog.Errorf("unable to normalize group resource while matching values to names: %v", err)
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
resourceLbl := r.namer.labelPrefix + singularResource
|
|
||||||
|
|
||||||
if info, found := r.info[metricInfo]; found {
|
|
||||||
res := make(map[string]pmodel.SampleValue, len(values))
|
|
||||||
for _, val := range values {
|
|
||||||
if val == nil {
|
|
||||||
// skip empty values
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
labelName := pmodel.LabelName(resourceLbl)
|
|
||||||
if info.isContainer {
|
|
||||||
labelName = pmodel.LabelName("pod_name")
|
|
||||||
}
|
|
||||||
res[string(val.Metric[labelName])] = val.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
return res, true
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// metricNamer knows how to construct MetricInfo out of raw prometheus series descriptions.
|
|
||||||
type metricNamer struct {
|
|
||||||
// overrides contains the list of container metrics whose naming we want to override.
|
|
||||||
// This is used to properly convert certain cAdvisor container metrics.
|
|
||||||
overrides map[string]seriesSpec
|
|
||||||
|
|
||||||
|
resourceInd int
|
||||||
|
groupInd *int
|
||||||
mapper apimeta.RESTMapper
|
mapper apimeta.RESTMapper
|
||||||
|
|
||||||
labelPrefix string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// seriesSpec specifies how to produce metric info for a particular prometheus series source
|
// newLabelGroupResExtractor creates a new labelGroupResExtractor for labels whose form
|
||||||
type seriesSpec struct {
|
// matches the given template. It does so by creating a regular expression from the template,
|
||||||
// metricName is the desired output API metric name
|
// so anything in the template which limits resource or group name length will cause issues.
|
||||||
metricName string
|
func newLabelGroupResExtractor(labelTemplate *template.Template) (*labelGroupResExtractor, error) {
|
||||||
// kind indicates whether or not this metric is cumulative,
|
labelRegexBuff := new(bytes.Buffer)
|
||||||
// and thus has to be calculated as a rate when returning it
|
if err := labelTemplate.Execute(labelRegexBuff, schema.GroupResource{"(?P<group>.+?)", "(?P<resource>.+?)"}); err != nil {
|
||||||
kind SeriesType
|
return nil, fmt.Errorf("unable to convert label template to matcher: %v", err)
|
||||||
|
}
|
||||||
|
if labelRegexBuff.Len() == 0 {
|
||||||
|
return nil, fmt.Errorf("unable to convert label template to matcher: empty template")
|
||||||
|
}
|
||||||
|
labelRegexRaw := "^" + labelRegexBuff.String() + "$"
|
||||||
|
labelRegex, err := regexp.Compile(labelRegexRaw)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to convert label template to matcher: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var groupInd *int
|
||||||
|
var resInd *int
|
||||||
|
|
||||||
|
for i, name := range labelRegex.SubexpNames() {
|
||||||
|
switch name {
|
||||||
|
case "group":
|
||||||
|
ind := i // copy to avoid iteration variable reference
|
||||||
|
groupInd = &ind
|
||||||
|
case "resource":
|
||||||
|
ind := i // copy to avoid iteration variable reference
|
||||||
|
resInd = &ind
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if resInd == nil {
|
||||||
|
return nil, fmt.Errorf("must include at least `{{.Resource}}` in the label template")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &labelGroupResExtractor{
|
||||||
|
regex: labelRegex,
|
||||||
|
resourceInd: *resInd,
|
||||||
|
groupInd: groupInd,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// processContainerSeries performs special work to extract metric definitions
|
// GroupResourceForLabel extracts a schema.GroupResource from the given label, if possible.
|
||||||
// from cAdvisor-sourced container metrics, which don't particularly follow any useful conventions consistently.
|
// The second argument indicates whether or not a potential group-resource was found in this label.
|
||||||
func (n *metricNamer) processContainerSeries(series prom.Series, infos map[provider.MetricInfo]seriesInfo) {
|
func (e *labelGroupResExtractor) GroupResourceForLabel(lbl pmodel.LabelName) (schema.GroupResource, bool) {
|
||||||
|
matchGroups := e.regex.FindStringSubmatch(string(lbl))
|
||||||
|
if matchGroups != nil {
|
||||||
|
group := ""
|
||||||
|
if e.groupInd != nil {
|
||||||
|
group = matchGroups[*e.groupInd]
|
||||||
|
}
|
||||||
|
|
||||||
originalName := series.Name
|
return schema.GroupResource{
|
||||||
|
Group: group,
|
||||||
|
Resource: matchGroups[e.resourceInd],
|
||||||
|
}, true
|
||||||
|
}
|
||||||
|
|
||||||
var name string
|
return schema.GroupResource{}, false
|
||||||
metricKind := GaugeSeries
|
}
|
||||||
if override, hasOverride := n.overrides[series.Name]; hasOverride {
|
|
||||||
name = override.metricName
|
func (r *metricNamer) Selector() prom.Selector {
|
||||||
metricKind = override.kind
|
return r.seriesQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
// reMatcher either positively or negatively matches a regex
|
||||||
|
type reMatcher struct {
|
||||||
|
regex *regexp.Regexp
|
||||||
|
positive bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newReMatcher(cfg config.RegexFilter) (*reMatcher, error) {
|
||||||
|
if cfg.Is != "" && cfg.IsNot != "" {
|
||||||
|
return nil, fmt.Errorf("cannot have both an `is` (%q) and `isNot` (%q) expression in a single filter", cfg.Is, cfg.IsNot)
|
||||||
|
}
|
||||||
|
if cfg.Is == "" && cfg.IsNot == "" {
|
||||||
|
return nil, fmt.Errorf("must have either an `is` or `isNot` expression in a filter")
|
||||||
|
}
|
||||||
|
|
||||||
|
var positive bool
|
||||||
|
var regexRaw string
|
||||||
|
if cfg.Is != "" {
|
||||||
|
positive = true
|
||||||
|
regexRaw = cfg.Is
|
||||||
} else {
|
} else {
|
||||||
// chop of the "container_" prefix
|
positive = false
|
||||||
series.Name = series.Name[10:]
|
regexRaw = cfg.IsNot
|
||||||
name, metricKind = n.metricNameFromSeries(series)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
info := provider.MetricInfo{
|
regex, err := regexp.Compile(regexRaw)
|
||||||
GroupResource: schema.GroupResource{Resource: "pods"},
|
|
||||||
Namespaced: true,
|
|
||||||
Metric: name,
|
|
||||||
}
|
|
||||||
|
|
||||||
infos[info] = seriesInfo{
|
|
||||||
kind: metricKind,
|
|
||||||
baseSeries: prom.Series{Name: originalName},
|
|
||||||
isContainer: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// processNamespacedSeries adds the metric info for the given generic namespaced series to
|
|
||||||
// the map of metric info.
|
|
||||||
func (n *metricNamer) processNamespacedSeries(series prom.Series, infos map[provider.MetricInfo]seriesInfo) error {
|
|
||||||
// NB: all errors must occur *before* we save the series info
|
|
||||||
name, metricKind := n.metricNameFromSeries(series)
|
|
||||||
resources, err := n.groupResourcesFromSeries(series)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to process prometheus series %s: %v", series.Name, err)
|
return nil, fmt.Errorf("unable to compile series filter %q: %v", regexRaw, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// we add one metric for each resource that this could describe
|
return &reMatcher{
|
||||||
for _, resource := range resources {
|
regex: regex,
|
||||||
info := provider.MetricInfo{
|
positive: positive,
|
||||||
GroupResource: resource,
|
}, nil
|
||||||
Namespaced: true,
|
|
||||||
Metric: name,
|
|
||||||
}
|
|
||||||
|
|
||||||
// metrics describing namespaces aren't considered to be namespaced
|
|
||||||
if resource == (schema.GroupResource{Resource: "namespaces"}) {
|
|
||||||
info.Namespaced = false
|
|
||||||
}
|
|
||||||
|
|
||||||
infos[info] = seriesInfo{
|
|
||||||
kind: metricKind,
|
|
||||||
baseSeries: prom.Series{Name: series.Name},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// processesRootScopedSeries adds the metric info for the given generic namespaced series to
|
func (m *reMatcher) Matches(val string) bool {
|
||||||
// the map of metric info.
|
return m.regex.MatchString(val) == m.positive
|
||||||
func (n *metricNamer) processRootScopedSeries(series prom.Series, infos map[provider.MetricInfo]seriesInfo) error {
|
}
|
||||||
// NB: all errors must occur *before* we save the series info
|
|
||||||
name, metricKind := n.metricNameFromSeries(series)
|
type metricNamer struct {
|
||||||
resources, err := n.groupResourcesFromSeries(series)
|
seriesQuery prom.Selector
|
||||||
|
labelTemplate *template.Template
|
||||||
|
labelResExtractor *labelGroupResExtractor
|
||||||
|
metricsQueryTemplate *template.Template
|
||||||
|
nameMatches *regexp.Regexp
|
||||||
|
nameAs string
|
||||||
|
seriesMatchers []*reMatcher
|
||||||
|
|
||||||
|
labelResourceMu sync.RWMutex
|
||||||
|
labelToResource map[pmodel.LabelName]schema.GroupResource
|
||||||
|
resourceToLabel map[schema.GroupResource]pmodel.LabelName
|
||||||
|
mapper apimeta.RESTMapper
|
||||||
|
}
|
||||||
|
|
||||||
|
// queryTemplateArgs are the arguments for the metrics query template.
|
||||||
|
type queryTemplateArgs struct {
|
||||||
|
Series string
|
||||||
|
LabelMatchers string
|
||||||
|
LabelValuesByName map[string][]string
|
||||||
|
GroupBy string
|
||||||
|
GroupBySlice []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *metricNamer) FilterSeries(initialSeries []prom.Series) []prom.Series {
|
||||||
|
if len(n.seriesMatchers) == 0 {
|
||||||
|
return initialSeries
|
||||||
|
}
|
||||||
|
|
||||||
|
finalSeries := make([]prom.Series, 0, len(initialSeries))
|
||||||
|
SeriesLoop:
|
||||||
|
for _, series := range initialSeries {
|
||||||
|
for _, matcher := range n.seriesMatchers {
|
||||||
|
if !matcher.Matches(series.Name) {
|
||||||
|
continue SeriesLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finalSeries = append(finalSeries, series)
|
||||||
|
}
|
||||||
|
|
||||||
|
return finalSeries
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *metricNamer) QueryForSeries(series string, resource schema.GroupResource, namespace string, names ...string) (prom.Selector, error) {
|
||||||
|
var exprs []string
|
||||||
|
valuesByName := map[string][]string{}
|
||||||
|
|
||||||
|
if namespace != "" {
|
||||||
|
namespaceLbl, err := n.LabelForResource(nsGroupResource)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to process prometheus series %s: %v", series.Name, err)
|
return "", err
|
||||||
|
}
|
||||||
|
exprs = append(exprs, prom.LabelEq(string(namespaceLbl), namespace))
|
||||||
|
valuesByName[string(namespaceLbl)] = []string{namespace}
|
||||||
}
|
}
|
||||||
|
|
||||||
// we add one metric for each resource that this could describe
|
resourceLbl, err := n.LabelForResource(resource)
|
||||||
for _, resource := range resources {
|
if err != nil {
|
||||||
info := provider.MetricInfo{
|
return "", err
|
||||||
GroupResource: resource,
|
}
|
||||||
Namespaced: false,
|
matcher := prom.LabelEq
|
||||||
Metric: name,
|
targetValue := names[0]
|
||||||
|
if len(names) > 1 {
|
||||||
|
matcher = prom.LabelMatches
|
||||||
|
targetValue = strings.Join(names, "|")
|
||||||
|
}
|
||||||
|
exprs = append(exprs, matcher(string(resourceLbl), targetValue))
|
||||||
|
valuesByName[string(resourceLbl)] = names
|
||||||
|
|
||||||
|
args := queryTemplateArgs{
|
||||||
|
Series: series,
|
||||||
|
LabelMatchers: strings.Join(exprs, ","),
|
||||||
|
LabelValuesByName: valuesByName,
|
||||||
|
GroupBy: string(resourceLbl),
|
||||||
|
GroupBySlice: []string{string(resourceLbl)},
|
||||||
|
}
|
||||||
|
queryBuff := new(bytes.Buffer)
|
||||||
|
if err := n.metricsQueryTemplate.Execute(queryBuff, args); err != nil {
|
||||||
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
infos[info] = seriesInfo{
|
if queryBuff.Len() == 0 {
|
||||||
kind: metricKind,
|
return "", fmt.Errorf("empty query produced by metrics query template")
|
||||||
baseSeries: prom.Series{Name: series.Name},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return prom.Selector(queryBuff.String()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// groupResourceFromSeries collects the possible group-resources that this series could describe by
|
func (n *metricNamer) ResourcesForSeries(series prom.Series) ([]schema.GroupResource, bool) {
|
||||||
// going through each label, checking to see if it corresponds to a known resource. For instance,
|
// use an updates map to avoid having to drop the read lock to update the cache
|
||||||
// a series `ingress_http_hits_total{pod="foo",service="bar",ingress="baz",namespace="ns"}`
|
// until the end. Since we'll probably have few updates after the first run,
|
||||||
// would return three GroupResources: "pods", "services", and "ingresses".
|
// this should mean that we rarely have to hold the write lock.
|
||||||
// Returned MetricInfo is equilavent to the "normalized" info produced by metricInfo.Normalized.
|
var resources []schema.GroupResource
|
||||||
func (n *metricNamer) groupResourcesFromSeries(series prom.Series) ([]schema.GroupResource, error) {
|
updates := make(map[pmodel.LabelName]schema.GroupResource)
|
||||||
var res []schema.GroupResource
|
namespaced := false
|
||||||
for label := range series.Labels {
|
|
||||||
if !strings.HasPrefix(string(label), n.labelPrefix) {
|
// use an anon func to get the right defer behavior
|
||||||
|
func() {
|
||||||
|
n.labelResourceMu.RLock()
|
||||||
|
defer n.labelResourceMu.RUnlock()
|
||||||
|
|
||||||
|
for lbl := range series.Labels {
|
||||||
|
var groupRes schema.GroupResource
|
||||||
|
var ok bool
|
||||||
|
|
||||||
|
// check if we have an override
|
||||||
|
if groupRes, ok = n.labelToResource[lbl]; ok {
|
||||||
|
resources = append(resources, groupRes)
|
||||||
|
} else if groupRes, ok = updates[lbl]; ok {
|
||||||
|
resources = append(resources, groupRes)
|
||||||
|
} else if n.labelResExtractor != nil {
|
||||||
|
// if not, check if it matches the form we expect, and if so,
|
||||||
|
// convert to a group-resource.
|
||||||
|
if groupRes, ok = n.labelResExtractor.GroupResourceForLabel(lbl); ok {
|
||||||
|
info, _, err := provider.CustomMetricInfo{GroupResource: groupRes}.Normalized(n.mapper)
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("unable to normalize group-resource %s from label %q, skipping: %v", groupRes.String(), lbl, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
label = label[len(n.labelPrefix):]
|
|
||||||
// TODO: figure out a way to let people specify a fully-qualified name in label-form
|
groupRes = info.GroupResource
|
||||||
gvr, err := n.mapper.ResourceFor(schema.GroupVersionResource{Resource: string(label)})
|
resources = append(resources, groupRes)
|
||||||
|
updates[lbl] = groupRes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if groupRes == nsGroupResource {
|
||||||
|
namespaced = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// update the cache for next time. This should only be called by discovery,
|
||||||
|
// so we don't really have to worry about the grap between read and write locks
|
||||||
|
// (plus, we don't care if someone else updates the cache first, since the results
|
||||||
|
// are necessarily the same, so at most we've done extra work).
|
||||||
|
if len(updates) > 0 {
|
||||||
|
n.labelResourceMu.Lock()
|
||||||
|
defer n.labelResourceMu.Unlock()
|
||||||
|
|
||||||
|
for lbl, groupRes := range updates {
|
||||||
|
n.labelToResource[lbl] = groupRes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resources, namespaced
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *metricNamer) LabelForResource(resource schema.GroupResource) (pmodel.LabelName, error) {
|
||||||
|
n.labelResourceMu.RLock()
|
||||||
|
// check if we have a cached copy or override
|
||||||
|
lbl, ok := n.resourceToLabel[resource]
|
||||||
|
n.labelResourceMu.RUnlock() // release before we call makeLabelForResource
|
||||||
|
if ok {
|
||||||
|
return lbl, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NB: we don't actually care about the gap between releasing read lock
|
||||||
|
// and acquiring the write lock -- if we do duplicate work sometimes, so be
|
||||||
|
// it, as long as we're correct.
|
||||||
|
|
||||||
|
// otherwise, use the template and save the result
|
||||||
|
lbl, err := n.makeLabelForResource(resource)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if apimeta.IsNoMatchError(err) {
|
return "", fmt.Errorf("unable to convert resource %s into label: %v", resource.String(), err)
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
return nil, err
|
return lbl, nil
|
||||||
}
|
|
||||||
res = append(res, gvr.GroupResource())
|
|
||||||
}
|
|
||||||
|
|
||||||
return res, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// metricNameFromSeries extracts a metric name from a series name, and indicates
|
// makeLabelForResource constructs a label name for the given resource, and saves the result.
|
||||||
// whether or not that series was a counter. It also has special logic to deal with time-based
|
// It must *not* be called under an existing lock.
|
||||||
// counters, which general get converted to milli-unit rate metrics.
|
func (n *metricNamer) makeLabelForResource(resource schema.GroupResource) (pmodel.LabelName, error) {
|
||||||
func (n *metricNamer) metricNameFromSeries(series prom.Series) (name string, kind SeriesType) {
|
if n.labelTemplate == nil {
|
||||||
kind = GaugeSeries
|
return "", fmt.Errorf("no generic resource label form specified for this metric")
|
||||||
name = series.Name
|
|
||||||
if strings.HasSuffix(name, "_total") {
|
|
||||||
kind = CounterSeries
|
|
||||||
name = name[:len(name)-6]
|
|
||||||
|
|
||||||
if strings.HasSuffix(name, "_seconds") {
|
|
||||||
kind = SecondsCounterSeries
|
|
||||||
name = name[:len(name)-8]
|
|
||||||
}
|
}
|
||||||
|
buff := new(bytes.Buffer)
|
||||||
|
|
||||||
|
singularRes, err := n.mapper.ResourceSingularizer(resource.Resource)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("unable to singularize resource %s: %v", resource.String(), err)
|
||||||
|
}
|
||||||
|
convResource := schema.GroupResource{
|
||||||
|
Group: groupNameSanitizer.Replace(resource.Group),
|
||||||
|
Resource: singularRes,
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
if err := n.labelTemplate.Execute(buff, convResource); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if buff.Len() == 0 {
|
||||||
|
return "", fmt.Errorf("empty label produced by label template")
|
||||||
|
}
|
||||||
|
lbl := pmodel.LabelName(buff.String())
|
||||||
|
|
||||||
|
n.labelResourceMu.Lock()
|
||||||
|
defer n.labelResourceMu.Unlock()
|
||||||
|
|
||||||
|
n.resourceToLabel[resource] = lbl
|
||||||
|
n.labelToResource[lbl] = resource
|
||||||
|
return lbl, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *metricNamer) MetricNameForSeries(series prom.Series) (string, error) {
|
||||||
|
matches := n.nameMatches.FindStringSubmatchIndex(series.Name)
|
||||||
|
if matches == nil {
|
||||||
|
return "", fmt.Errorf("series name %q did not match expected pattern %q", series.Name, n.nameMatches.String())
|
||||||
|
}
|
||||||
|
outNameBytes := n.nameMatches.ExpandString(nil, n.nameAs, series.Name, matches)
|
||||||
|
return string(outNameBytes), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NamersFromConfig produces a MetricNamer for each rule in the given config.
|
||||||
|
func NamersFromConfig(cfg *config.MetricsDiscoveryConfig, mapper apimeta.RESTMapper) ([]MetricNamer, error) {
|
||||||
|
namers := make([]MetricNamer, len(cfg.Rules))
|
||||||
|
|
||||||
|
for i, rule := range cfg.Rules {
|
||||||
|
var labelTemplate *template.Template
|
||||||
|
var labelResExtractor *labelGroupResExtractor
|
||||||
|
var err error
|
||||||
|
if rule.Resources.Template != "" {
|
||||||
|
labelTemplate, err = template.New("resource-label").Delims("<<", ">>").Parse(rule.Resources.Template)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to parse label template %q associated with series query %q: %v", rule.Resources.Template, rule.SeriesQuery, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
labelResExtractor, err = newLabelGroupResExtractor(labelTemplate)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to generate label format from template %q associated with series query %q: %v", rule.Resources.Template, rule.SeriesQuery, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
metricsQueryTemplate, err := template.New("metrics-query").Delims("<<", ">>").Parse(rule.MetricsQuery)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to parse metrics query template %q associated with series query %q: %v", rule.MetricsQuery, rule.SeriesQuery, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
seriesMatchers := make([]*reMatcher, len(rule.SeriesFilters))
|
||||||
|
for i, filterRaw := range rule.SeriesFilters {
|
||||||
|
matcher, err := newReMatcher(filterRaw)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to generate series name filter associated with series query %q: %v", rule.SeriesQuery, err)
|
||||||
|
}
|
||||||
|
seriesMatchers[i] = matcher
|
||||||
|
}
|
||||||
|
if rule.Name.Matches != "" {
|
||||||
|
matcher, err := newReMatcher(config.RegexFilter{Is: rule.Name.Matches})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to generate series name filter from name rules associated with series query %q: %v", rule.SeriesQuery, err)
|
||||||
|
}
|
||||||
|
seriesMatchers = append(seriesMatchers, matcher)
|
||||||
|
}
|
||||||
|
|
||||||
|
var nameMatches *regexp.Regexp
|
||||||
|
if rule.Name.Matches != "" {
|
||||||
|
nameMatches, err = regexp.Compile(rule.Name.Matches)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to compile series name match expression %q associated with series query %q: %v", rule.Name.Matches, rule.SeriesQuery, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// this will always succeed
|
||||||
|
nameMatches = regexp.MustCompile(".*")
|
||||||
|
}
|
||||||
|
nameAs := rule.Name.As
|
||||||
|
if nameAs == "" {
|
||||||
|
// check if we have an obvious default
|
||||||
|
subexpNames := nameMatches.SubexpNames()
|
||||||
|
if len(subexpNames) == 1 {
|
||||||
|
// no capture groups, use the whole thing
|
||||||
|
nameAs = "$0"
|
||||||
|
} else if len(subexpNames) == 2 {
|
||||||
|
// one capture group, use that
|
||||||
|
nameAs = "$1"
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("must specify an 'as' value for name matcher %q associated with series query %q", rule.Name.Matches, rule.SeriesQuery)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namer := &metricNamer{
|
||||||
|
seriesQuery: prom.Selector(rule.SeriesQuery),
|
||||||
|
labelTemplate: labelTemplate,
|
||||||
|
labelResExtractor: labelResExtractor,
|
||||||
|
metricsQueryTemplate: metricsQueryTemplate,
|
||||||
|
mapper: mapper,
|
||||||
|
nameMatches: nameMatches,
|
||||||
|
nameAs: nameAs,
|
||||||
|
seriesMatchers: seriesMatchers,
|
||||||
|
|
||||||
|
labelToResource: make(map[pmodel.LabelName]schema.GroupResource),
|
||||||
|
resourceToLabel: make(map[schema.GroupResource]pmodel.LabelName),
|
||||||
|
}
|
||||||
|
|
||||||
|
// invert the structure for consistency with the template
|
||||||
|
for lbl, groupRes := range rule.Resources.Overrides {
|
||||||
|
infoRaw := provider.CustomMetricInfo{
|
||||||
|
GroupResource: schema.GroupResource{
|
||||||
|
Group: groupRes.Group,
|
||||||
|
Resource: groupRes.Resource,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
info, _, err := infoRaw.Normalized(mapper)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to normalize group-resource %v: %v", groupRes, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
namer.labelToResource[pmodel.LabelName(lbl)] = info.GroupResource
|
||||||
|
namer.resourceToLabel[info.GroupResource] = pmodel.LabelName(lbl)
|
||||||
|
}
|
||||||
|
|
||||||
|
namers[i] = namer
|
||||||
|
}
|
||||||
|
|
||||||
|
return namers, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,9 @@ package provider
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/golang/glog"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/golang/glog"
|
||||||
"github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/provider"
|
"github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/provider"
|
||||||
pmodel "github.com/prometheus/common/model"
|
pmodel "github.com/prometheus/common/model"
|
||||||
apierr "k8s.io/apimachinery/pkg/api/errors"
|
apierr "k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
|
@ -40,42 +40,40 @@ import (
|
||||||
prom "github.com/directxman12/k8s-prometheus-adapter/pkg/client"
|
prom "github.com/directxman12/k8s-prometheus-adapter/pkg/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Runnable represents something that can be run until told to stop.
|
||||||
|
type Runnable interface {
|
||||||
|
// Run runs the runnable forever.
|
||||||
|
Run()
|
||||||
|
// RunUntil runs the runnable until the given channel is closed.
|
||||||
|
RunUntil(stopChan <-chan struct{})
|
||||||
|
}
|
||||||
|
|
||||||
type prometheusProvider struct {
|
type prometheusProvider struct {
|
||||||
mapper apimeta.RESTMapper
|
mapper apimeta.RESTMapper
|
||||||
kubeClient dynamic.ClientPool
|
kubeClient dynamic.Interface
|
||||||
promClient prom.Client
|
promClient prom.Client
|
||||||
|
|
||||||
SeriesRegistry
|
SeriesRegistry
|
||||||
|
|
||||||
rateInterval time.Duration
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPrometheusProvider(mapper apimeta.RESTMapper, kubeClient dynamic.ClientPool, promClient prom.Client, labelPrefix string, updateInterval time.Duration, rateInterval time.Duration, stopChan <-chan struct{}) provider.CustomMetricsProvider {
|
func NewPrometheusProvider(mapper apimeta.RESTMapper, kubeClient dynamic.Interface, promClient prom.Client, namers []MetricNamer, updateInterval time.Duration) (provider.CustomMetricsProvider, Runnable) {
|
||||||
lister := &cachingMetricsLister{
|
lister := &cachingMetricsLister{
|
||||||
updateInterval: updateInterval,
|
updateInterval: updateInterval,
|
||||||
promClient: promClient,
|
promClient: promClient,
|
||||||
|
namers: namers,
|
||||||
|
|
||||||
SeriesRegistry: &basicSeriesRegistry{
|
SeriesRegistry: &basicSeriesRegistry{
|
||||||
namer: metricNamer{
|
|
||||||
// TODO: populate the overrides list
|
|
||||||
overrides: nil,
|
|
||||||
mapper: mapper,
|
mapper: mapper,
|
||||||
labelPrefix: labelPrefix,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
lister.RunUntil(stopChan)
|
|
||||||
|
|
||||||
return &prometheusProvider{
|
return &prometheusProvider{
|
||||||
mapper: mapper,
|
mapper: mapper,
|
||||||
kubeClient: kubeClient,
|
kubeClient: kubeClient,
|
||||||
promClient: promClient,
|
promClient: promClient,
|
||||||
|
|
||||||
SeriesRegistry: lister,
|
SeriesRegistry: lister,
|
||||||
|
}, lister
|
||||||
rateInterval: rateInterval,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *prometheusProvider) metricFor(value pmodel.SampleValue, groupResource schema.GroupResource, namespace string, name string, metricName string) (*custom_metrics.MetricValue, error) {
|
func (p *prometheusProvider) metricFor(value pmodel.SampleValue, groupResource schema.GroupResource, namespace string, name string, metricName string) (*custom_metrics.MetricValue, error) {
|
||||||
|
|
@ -97,7 +95,7 @@ func (p *prometheusProvider) metricFor(value pmodel.SampleValue, groupResource s
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *prometheusProvider) metricsFor(valueSet pmodel.Vector, info provider.MetricInfo, list runtime.Object) (*custom_metrics.MetricValueList, error) {
|
func (p *prometheusProvider) metricsFor(valueSet pmodel.Vector, info provider.CustomMetricInfo, list runtime.Object) (*custom_metrics.MetricValueList, error) {
|
||||||
if !apimeta.IsListType(list) {
|
if !apimeta.IsListType(list) {
|
||||||
return nil, apierr.NewInternalError(fmt.Errorf("result of label selector list operation was not a list"))
|
return nil, apierr.NewInternalError(fmt.Errorf("result of label selector list operation was not a list"))
|
||||||
}
|
}
|
||||||
|
|
@ -131,30 +129,14 @@ func (p *prometheusProvider) metricsFor(valueSet pmodel.Vector, info provider.Me
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *prometheusProvider) buildQuery(info provider.MetricInfo, namespace string, names ...string) (pmodel.Vector, error) {
|
func (p *prometheusProvider) buildQuery(info provider.CustomMetricInfo, namespace string, names ...string) (pmodel.Vector, error) {
|
||||||
kind, baseQuery, groupBy, found := p.QueryForMetric(info, namespace, names...)
|
query, found := p.QueryForMetric(info, namespace, names...)
|
||||||
if !found {
|
if !found {
|
||||||
return nil, provider.NewMetricNotFoundError(info.GroupResource, info.Metric)
|
return nil, provider.NewMetricNotFoundError(info.GroupResource, info.Metric)
|
||||||
}
|
}
|
||||||
|
|
||||||
fullQuery := baseQuery
|
|
||||||
switch kind {
|
|
||||||
case CounterSeries:
|
|
||||||
fullQuery = prom.Selector(fmt.Sprintf("rate(%s[%s])", baseQuery, pmodel.Duration(p.rateInterval).String()))
|
|
||||||
case SecondsCounterSeries:
|
|
||||||
// TODO: futher modify for seconds?
|
|
||||||
fullQuery = prom.Selector(prom.Selector(fmt.Sprintf("rate(%s[%s])", baseQuery, pmodel.Duration(p.rateInterval).String())))
|
|
||||||
}
|
|
||||||
|
|
||||||
// NB: too small of a rate interval will return no results...
|
|
||||||
|
|
||||||
// sum over all other dimensions of this query (e.g. if we select on route, sum across all pods,
|
|
||||||
// but if we select on pods, sum across all routes), and split by the dimension of our resource
|
|
||||||
// TODO: return/populate the by list in SeriesForMetric
|
|
||||||
fullQuery = prom.Selector(fmt.Sprintf("sum(%s) by (%s)", fullQuery, groupBy))
|
|
||||||
|
|
||||||
// TODO: use an actual context
|
// TODO: use an actual context
|
||||||
queryResults, err := p.promClient.Query(context.Background(), pmodel.Now(), fullQuery)
|
queryResults, err := p.promClient.Query(context.TODO(), pmodel.Now(), query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Errorf("unable to fetch metrics from prometheus: %v", err)
|
glog.Errorf("unable to fetch metrics from prometheus: %v", err)
|
||||||
// don't leak implementation details to the user
|
// don't leak implementation details to the user
|
||||||
|
|
@ -169,7 +151,7 @@ func (p *prometheusProvider) buildQuery(info provider.MetricInfo, namespace stri
|
||||||
return *queryResults.Vector, nil
|
return *queryResults.Vector, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *prometheusProvider) getSingle(info provider.MetricInfo, namespace, name string) (*custom_metrics.MetricValue, error) {
|
func (p *prometheusProvider) getSingle(info provider.CustomMetricInfo, namespace, name string) (*custom_metrics.MetricValue, error) {
|
||||||
queryResults, err := p.buildQuery(info, namespace, name)
|
queryResults, err := p.buildQuery(info, namespace, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -197,24 +179,25 @@ func (p *prometheusProvider) getSingle(info provider.MetricInfo, namespace, name
|
||||||
return p.metricFor(resultValue, info.GroupResource, "", name, info.Metric)
|
return p.metricFor(resultValue, info.GroupResource, "", name, info.Metric)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *prometheusProvider) getMultiple(info provider.MetricInfo, namespace string, selector labels.Selector) (*custom_metrics.MetricValueList, error) {
|
func (p *prometheusProvider) getMultiple(info provider.CustomMetricInfo, namespace string, selector labels.Selector) (*custom_metrics.MetricValueList, error) {
|
||||||
// construct a client to list the names of objects matching the label selector
|
fullResources, err := p.mapper.ResourcesFor(info.GroupResource.WithVersion(""))
|
||||||
client, err := p.kubeClient.ClientForGroupVersionResource(info.GroupResource.WithVersion(""))
|
if err == nil && len(fullResources) == 0 {
|
||||||
|
err = fmt.Errorf("no fully versioned resources known for group-resource %v", info.GroupResource)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Errorf("unable to construct dynamic client to list matching resource names: %v", err)
|
glog.Errorf("unable to find preferred version to list matching resource names: %v", err)
|
||||||
// don't leak implementation details to the user
|
// don't leak implementation details to the user
|
||||||
return nil, apierr.NewInternalError(fmt.Errorf("unable to list matching resources"))
|
return nil, apierr.NewInternalError(fmt.Errorf("unable to list matching resources"))
|
||||||
}
|
}
|
||||||
|
var client dynamic.ResourceInterface
|
||||||
// we can construct a this APIResource ourself, since the dynamic client only uses Name and Namespaced
|
if namespace != "" {
|
||||||
apiRes := &metav1.APIResource{
|
client = p.kubeClient.Resource(fullResources[0]).Namespace(namespace)
|
||||||
Name: info.GroupResource.Resource,
|
} else {
|
||||||
Namespaced: info.Namespaced,
|
client = p.kubeClient.Resource(fullResources[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
// actually list the objects matching the label selector
|
// actually list the objects matching the label selector
|
||||||
matchingObjectsRaw, err := client.Resource(apiRes, namespace).
|
matchingObjectsRaw, err := client.List(metav1.ListOptions{LabelSelector: selector.String()})
|
||||||
List(metav1.ListOptions{LabelSelector: selector.String()})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Errorf("unable to list matching resource names: %v", err)
|
glog.Errorf("unable to list matching resource names: %v", err)
|
||||||
// don't leak implementation details to the user
|
// don't leak implementation details to the user
|
||||||
|
|
@ -243,7 +226,7 @@ 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.CustomMetricInfo{
|
||||||
GroupResource: groupResource,
|
GroupResource: groupResource,
|
||||||
Metric: metricName,
|
Metric: metricName,
|
||||||
Namespaced: false,
|
Namespaced: false,
|
||||||
|
|
@ -253,7 +236,7 @@ func (p *prometheusProvider) GetRootScopedMetricByName(groupResource schema.Grou
|
||||||
}
|
}
|
||||||
|
|
||||||
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.CustomMetricInfo{
|
||||||
GroupResource: groupResource,
|
GroupResource: groupResource,
|
||||||
Metric: metricName,
|
Metric: metricName,
|
||||||
Namespaced: false,
|
Namespaced: false,
|
||||||
|
|
@ -262,7 +245,7 @@ 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.CustomMetricInfo{
|
||||||
GroupResource: groupResource,
|
GroupResource: groupResource,
|
||||||
Metric: metricName,
|
Metric: metricName,
|
||||||
Namespaced: true,
|
Namespaced: true,
|
||||||
|
|
@ -272,7 +255,7 @@ 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.CustomMetricInfo{
|
||||||
GroupResource: groupResource,
|
GroupResource: groupResource,
|
||||||
Metric: metricName,
|
Metric: metricName,
|
||||||
Namespaced: true,
|
Namespaced: true,
|
||||||
|
|
@ -285,6 +268,7 @@ type cachingMetricsLister struct {
|
||||||
|
|
||||||
promClient prom.Client
|
promClient prom.Client
|
||||||
updateInterval time.Duration
|
updateInterval time.Duration
|
||||||
|
namers []MetricNamer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *cachingMetricsLister) Run() {
|
func (l *cachingMetricsLister) Run() {
|
||||||
|
|
@ -299,20 +283,65 @@ func (l *cachingMetricsLister) RunUntil(stopChan <-chan struct{}) {
|
||||||
}, l.updateInterval, stopChan)
|
}, l.updateInterval, stopChan)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type selectorSeries struct {
|
||||||
|
selector prom.Selector
|
||||||
|
series []prom.Series
|
||||||
|
}
|
||||||
|
|
||||||
func (l *cachingMetricsLister) updateMetrics() error {
|
func (l *cachingMetricsLister) updateMetrics() error {
|
||||||
startTime := pmodel.Now().Add(-1 * l.updateInterval)
|
startTime := pmodel.Now().Add(-1 * l.updateInterval)
|
||||||
|
|
||||||
sels := l.Selectors()
|
// don't do duplicate queries when it's just the matchers that change
|
||||||
|
seriesCacheByQuery := make(map[prom.Selector][]prom.Series)
|
||||||
|
|
||||||
// TODO: use an actual context here
|
// these can take a while on large clusters, so launch in parallel
|
||||||
series, err := l.promClient.Series(context.Background(), pmodel.Interval{startTime, 0}, sels...)
|
// and don't duplicate
|
||||||
|
selectors := make(map[prom.Selector]struct{})
|
||||||
|
selectorSeriesChan := make(chan selectorSeries, len(l.namers))
|
||||||
|
errs := make(chan error, len(l.namers))
|
||||||
|
for _, namer := range l.namers {
|
||||||
|
sel := namer.Selector()
|
||||||
|
if _, ok := selectors[sel]; ok {
|
||||||
|
errs <- nil
|
||||||
|
selectorSeriesChan <- selectorSeries{}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
selectors[sel] = struct{}{}
|
||||||
|
go func() {
|
||||||
|
series, err := l.promClient.Series(context.TODO(), pmodel.Interval{startTime, 0}, sel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to update list of all available metrics: %v", err)
|
errs <- fmt.Errorf("unable to fetch metrics for query %q: %v", sel, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
errs <- nil
|
||||||
|
selectorSeriesChan <- selectorSeries{
|
||||||
|
selector: sel,
|
||||||
|
series: series,
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
glog.V(10).Infof("Set available metric list from Prometheus to: %v", series)
|
// iterate through, blocking until we've got all results
|
||||||
|
for range l.namers {
|
||||||
|
if err := <-errs; err != nil {
|
||||||
|
return fmt.Errorf("unable to update list of all metrics: %v", err)
|
||||||
|
}
|
||||||
|
if ss := <-selectorSeriesChan; ss.series != nil {
|
||||||
|
seriesCacheByQuery[ss.selector] = ss.series
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(errs)
|
||||||
|
|
||||||
l.SetSeries(series)
|
newSeries := make([][]prom.Series, len(l.namers))
|
||||||
|
for i, namer := range l.namers {
|
||||||
|
series, cached := seriesCacheByQuery[namer.Selector()]
|
||||||
|
if !cached {
|
||||||
|
return fmt.Errorf("unable to update list of all metrics: no metrics retrieved for query %q", namer.Selector())
|
||||||
|
}
|
||||||
|
newSeries[i] = namer.FilterSeries(series)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
glog.V(10).Infof("Set available metric list from Prometheus to: %v", newSeries)
|
||||||
|
|
||||||
|
return l.SetSeries(newSeries, l.namers)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
fakedyn "k8s.io/client-go/dynamic/fake"
|
fakedyn "k8s.io/client-go/dynamic/fake"
|
||||||
|
|
||||||
|
config "github.com/directxman12/k8s-prometheus-adapter/cmd/config-gen/utils"
|
||||||
prom "github.com/directxman12/k8s-prometheus-adapter/pkg/client"
|
prom "github.com/directxman12/k8s-prometheus-adapter/pkg/client"
|
||||||
pmodel "github.com/prometheus/common/model"
|
pmodel "github.com/prometheus/common/model"
|
||||||
)
|
)
|
||||||
|
|
@ -86,20 +87,20 @@ func (c *fakePromClient) QueryRange(_ context.Context, r prom.Range, query prom.
|
||||||
return prom.QueryResult{}, nil
|
return prom.QueryResult{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupPrometheusProvider(t *testing.T, stopCh <-chan struct{}) (provider.CustomMetricsProvider, *fakePromClient) {
|
func setupPrometheusProvider(t *testing.T) (provider.CustomMetricsProvider, *fakePromClient) {
|
||||||
fakeProm := &fakePromClient{}
|
fakeProm := &fakePromClient{}
|
||||||
fakeKubeClient := &fakedyn.FakeClientPool{}
|
fakeKubeClient := &fakedyn.FakeDynamicClient{}
|
||||||
|
|
||||||
prov := NewPrometheusProvider(restMapper(), fakeKubeClient, fakeProm, "", fakeProviderUpdateInterval, 1*time.Minute, stopCh)
|
cfg := config.DefaultConfig(1*time.Minute, "")
|
||||||
|
namers, err := NamersFromConfig(cfg, restMapper())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
prov, _ := NewPrometheusProvider(restMapper(), fakeKubeClient, fakeProm, namers, fakeProviderUpdateInterval)
|
||||||
|
|
||||||
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: {
|
containerSel: {
|
||||||
{
|
|
||||||
Name: "container_actually_gauge_seconds_total",
|
|
||||||
Labels: pmodel.LabelSet{"pod_name": "somepod", "namespace": "somens", "container_name": "somecont"},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Name: "container_some_usage",
|
Name: "container_some_usage",
|
||||||
Labels: pmodel.LabelSet{"pod_name": "somepod", "namespace": "somens", "container_name": "somecont"},
|
Labels: pmodel.LabelSet{"pod_name": "somepod", "namespace": "somens", "container_name": "somecont"},
|
||||||
|
|
@ -130,28 +131,24 @@ func setupPrometheusProvider(t *testing.T, stopCh <-chan struct{}) (provider.Cus
|
||||||
|
|
||||||
func TestListAllMetrics(t *testing.T) {
|
func TestListAllMetrics(t *testing.T) {
|
||||||
// setup
|
// setup
|
||||||
stopCh := make(chan struct{})
|
prov, fakeProm := setupPrometheusProvider(t)
|
||||||
defer close(stopCh)
|
|
||||||
prov, fakeProm := setupPrometheusProvider(t, stopCh)
|
|
||||||
|
|
||||||
// assume we have no updates
|
// assume we have no updates
|
||||||
require.Len(t, prov.ListAllMetrics(), 0, "assume: should have no metrics updates at the start")
|
require.Len(t, prov.ListAllMetrics(), 0, "assume: should have no metrics updates at the start")
|
||||||
|
|
||||||
// set the acceptible interval (now until the next update, with a bit of wiggle room)
|
// set the acceptible interval (now until the next update, with a bit of wiggle room)
|
||||||
startTime := pmodel.Now()
|
startTime := pmodel.Now().Add(-1*fakeProviderUpdateInterval - fakeProviderUpdateInterval/10)
|
||||||
endTime := startTime.Add(fakeProviderUpdateInterval + fakeProviderUpdateInterval/10)
|
fakeProm.acceptibleInterval = pmodel.Interval{Start: startTime, End: 0}
|
||||||
fakeProm.acceptibleInterval = pmodel.Interval{Start: startTime, End: endTime}
|
|
||||||
|
|
||||||
// wait one update interval (with a bit of wiggle room)
|
// update the metrics (without actually calling RunUntil, so we can avoid timing issues)
|
||||||
time.Sleep(fakeProviderUpdateInterval + fakeProviderUpdateInterval/10)
|
lister := prov.(*prometheusProvider).SeriesRegistry.(*cachingMetricsLister)
|
||||||
|
require.NoError(t, lister.updateMetrics())
|
||||||
|
|
||||||
// list/sort the metrics
|
// list/sort the metrics
|
||||||
actualMetrics := prov.ListAllMetrics()
|
actualMetrics := prov.ListAllMetrics()
|
||||||
sort.Sort(metricInfoSorter(actualMetrics))
|
sort.Sort(metricInfoSorter(actualMetrics))
|
||||||
|
|
||||||
expectedMetrics := []provider.MetricInfo{
|
expectedMetrics := []provider.CustomMetricInfo{
|
||||||
{schema.GroupResource{Resource: "pods"}, true, "actually_gauge"},
|
|
||||||
{schema.GroupResource{Resource: "pods"}, true, "some_usage"},
|
|
||||||
{schema.GroupResource{Resource: "services"}, true, "ingress_hits"},
|
{schema.GroupResource{Resource: "services"}, true, "ingress_hits"},
|
||||||
{schema.GroupResource{Group: "extensions", Resource: "ingresses"}, true, "ingress_hits"},
|
{schema.GroupResource{Group: "extensions", Resource: "ingresses"}, true, "ingress_hits"},
|
||||||
{schema.GroupResource{Resource: "pods"}, true, "ingress_hits"},
|
{schema.GroupResource{Resource: "pods"}, true, "ingress_hits"},
|
||||||
|
|
@ -160,6 +157,8 @@ func TestListAllMetrics(t *testing.T) {
|
||||||
{schema.GroupResource{Resource: "namespaces"}, false, "service_proxy_packets"},
|
{schema.GroupResource{Resource: "namespaces"}, false, "service_proxy_packets"},
|
||||||
{schema.GroupResource{Group: "extensions", Resource: "deployments"}, true, "work_queue_wait"},
|
{schema.GroupResource{Group: "extensions", Resource: "deployments"}, true, "work_queue_wait"},
|
||||||
{schema.GroupResource{Resource: "namespaces"}, false, "work_queue_wait"},
|
{schema.GroupResource{Resource: "namespaces"}, false, "work_queue_wait"},
|
||||||
|
{schema.GroupResource{Resource: "namespaces"}, false, "some_usage"},
|
||||||
|
{schema.GroupResource{Resource: "pods"}, true, "some_usage"},
|
||||||
}
|
}
|
||||||
sort.Sort(metricInfoSorter(expectedMetrics))
|
sort.Sort(metricInfoSorter(expectedMetrics))
|
||||||
|
|
||||||
|
|
|
||||||
198
pkg/custom-provider/series_registry.go
Normal file
198
pkg/custom-provider/series_registry.go
Normal file
|
|
@ -0,0 +1,198 @@
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package provider
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/provider"
|
||||||
|
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||||
|
|
||||||
|
prom "github.com/directxman12/k8s-prometheus-adapter/pkg/client"
|
||||||
|
"github.com/golang/glog"
|
||||||
|
pmodel "github.com/prometheus/common/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NB: container metrics sourced from cAdvisor don't consistently follow naming conventions,
|
||||||
|
// so we need to whitelist them and handle them on a case-by-case basis. Metrics ending in `_total`
|
||||||
|
// *should* be counters, but may actually be guages in this case.
|
||||||
|
|
||||||
|
// SeriesType represents the kind of series backing a metric.
|
||||||
|
type SeriesType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
CounterSeries SeriesType = iota
|
||||||
|
SecondsCounterSeries
|
||||||
|
GaugeSeries
|
||||||
|
)
|
||||||
|
|
||||||
|
// SeriesRegistry provides conversions between Prometheus series and MetricInfo
|
||||||
|
type SeriesRegistry interface {
|
||||||
|
// SetSeries replaces the known series in this registry.
|
||||||
|
// Each slice in series should correspond to a MetricNamer in namers.
|
||||||
|
SetSeries(series [][]prom.Series, namers []MetricNamer) error
|
||||||
|
// ListAllMetrics lists all metrics known to this registry
|
||||||
|
ListAllMetrics() []provider.CustomMetricInfo
|
||||||
|
// SeriesForMetric looks up the minimum required series information to make a query for the given metric
|
||||||
|
// against the given resource (namespace may be empty for non-namespaced resources)
|
||||||
|
QueryForMetric(info provider.CustomMetricInfo, namespace string, resourceNames ...string) (query prom.Selector, found bool)
|
||||||
|
// MatchValuesToNames matches result values to resource names for the given metric and value set
|
||||||
|
MatchValuesToNames(metricInfo provider.CustomMetricInfo, values pmodel.Vector) (matchedValues map[string]pmodel.SampleValue, found bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
type seriesInfo struct {
|
||||||
|
// seriesName is the name of the corresponding Prometheus series
|
||||||
|
seriesName string
|
||||||
|
|
||||||
|
// namer is the MetricNamer used to name this series
|
||||||
|
namer MetricNamer
|
||||||
|
}
|
||||||
|
|
||||||
|
// overridableSeriesRegistry is a basic SeriesRegistry
|
||||||
|
type basicSeriesRegistry struct {
|
||||||
|
mu sync.RWMutex
|
||||||
|
|
||||||
|
// info maps metric info to information about the corresponding series
|
||||||
|
info map[provider.CustomMetricInfo]seriesInfo
|
||||||
|
// metrics is the list of all known metrics
|
||||||
|
metrics []provider.CustomMetricInfo
|
||||||
|
|
||||||
|
mapper apimeta.RESTMapper
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *basicSeriesRegistry) SetSeries(newSeriesSlices [][]prom.Series, namers []MetricNamer) error {
|
||||||
|
if len(newSeriesSlices) != len(namers) {
|
||||||
|
return fmt.Errorf("need one set of series per namer")
|
||||||
|
}
|
||||||
|
|
||||||
|
newInfo := make(map[provider.CustomMetricInfo]seriesInfo)
|
||||||
|
for i, newSeries := range newSeriesSlices {
|
||||||
|
namer := namers[i]
|
||||||
|
for _, series := range newSeries {
|
||||||
|
// TODO: warn if it doesn't match any resources
|
||||||
|
resources, namespaced := namer.ResourcesForSeries(series)
|
||||||
|
name, err := namer.MetricNameForSeries(series)
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("unable to name series %q, skipping: %v", series.String(), err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, resource := range resources {
|
||||||
|
info := provider.CustomMetricInfo{
|
||||||
|
GroupResource: resource,
|
||||||
|
Namespaced: namespaced,
|
||||||
|
Metric: name,
|
||||||
|
}
|
||||||
|
|
||||||
|
// namespace metrics aren't counted as namespaced
|
||||||
|
if resource == nsGroupResource {
|
||||||
|
info.Namespaced = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// we don't need to re-normalize, because the metric namer should have already normalized for us
|
||||||
|
newInfo[info] = seriesInfo{
|
||||||
|
seriesName: series.Name,
|
||||||
|
namer: namer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// regenerate metrics
|
||||||
|
newMetrics := make([]provider.CustomMetricInfo, 0, len(newInfo))
|
||||||
|
for info := range newInfo {
|
||||||
|
newMetrics = append(newMetrics, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.mu.Lock()
|
||||||
|
defer r.mu.Unlock()
|
||||||
|
|
||||||
|
r.info = newInfo
|
||||||
|
r.metrics = newMetrics
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *basicSeriesRegistry) ListAllMetrics() []provider.CustomMetricInfo {
|
||||||
|
r.mu.RLock()
|
||||||
|
defer r.mu.RUnlock()
|
||||||
|
|
||||||
|
return r.metrics
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *basicSeriesRegistry) QueryForMetric(metricInfo provider.CustomMetricInfo, namespace string, resourceNames ...string) (prom.Selector, bool) {
|
||||||
|
r.mu.RLock()
|
||||||
|
defer r.mu.RUnlock()
|
||||||
|
|
||||||
|
if len(resourceNames) == 0 {
|
||||||
|
glog.Errorf("no resource names requested while producing a query for metric %s", metricInfo.String())
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
metricInfo, _, err := metricInfo.Normalized(r.mapper)
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("unable to normalize group resource while producing a query: %v", err)
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
info, infoFound := r.info[metricInfo]
|
||||||
|
if !infoFound {
|
||||||
|
glog.V(10).Infof("metric %v not registered", metricInfo)
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
query, err := info.namer.QueryForSeries(info.seriesName, metricInfo.GroupResource, namespace, resourceNames...)
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("unable to construct query for metric %s: %v", metricInfo.String(), err)
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
return query, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *basicSeriesRegistry) MatchValuesToNames(metricInfo provider.CustomMetricInfo, values pmodel.Vector) (matchedValues map[string]pmodel.SampleValue, found bool) {
|
||||||
|
r.mu.RLock()
|
||||||
|
defer r.mu.RUnlock()
|
||||||
|
|
||||||
|
metricInfo, _, err := metricInfo.Normalized(r.mapper)
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("unable to normalize group resource while matching values to names: %v", err)
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
info, infoFound := r.info[metricInfo]
|
||||||
|
if !infoFound {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
resourceLbl, err := info.namer.LabelForResource(metricInfo.GroupResource)
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("unable to construct resource label for metric %s: %v", metricInfo.String(), err)
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
res := make(map[string]pmodel.SampleValue, len(values))
|
||||||
|
for _, val := range values {
|
||||||
|
if val == nil {
|
||||||
|
// skip empty values
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
res[string(val.Metric[resourceLbl])] = val.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, true
|
||||||
|
}
|
||||||
|
|
@ -19,6 +19,7 @@ package provider
|
||||||
import (
|
import (
|
||||||
"sort"
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/provider"
|
"github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/provider"
|
||||||
pmodel "github.com/prometheus/common/model"
|
pmodel "github.com/prometheus/common/model"
|
||||||
|
|
@ -29,13 +30,14 @@ import (
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
|
||||||
|
config "github.com/directxman12/k8s-prometheus-adapter/cmd/config-gen/utils"
|
||||||
prom "github.com/directxman12/k8s-prometheus-adapter/pkg/client"
|
prom "github.com/directxman12/k8s-prometheus-adapter/pkg/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
// restMapper creates a RESTMapper with just the types we need for
|
// restMapper creates a RESTMapper with just the types we need for
|
||||||
// these tests.
|
// these tests.
|
||||||
func restMapper() apimeta.RESTMapper {
|
func restMapper() apimeta.RESTMapper {
|
||||||
mapper := apimeta.NewDefaultRESTMapper([]schema.GroupVersion{coreapi.SchemeGroupVersion}, apimeta.InterfacesForUnstructured)
|
mapper := apimeta.NewDefaultRESTMapper([]schema.GroupVersion{coreapi.SchemeGroupVersion})
|
||||||
|
|
||||||
mapper.Add(coreapi.SchemeGroupVersion.WithKind("Pod"), apimeta.RESTScopeNamespace)
|
mapper.Add(coreapi.SchemeGroupVersion.WithKind("Pod"), apimeta.RESTScopeNamespace)
|
||||||
mapper.Add(coreapi.SchemeGroupVersion.WithKind("Service"), apimeta.RESTScopeNamespace)
|
mapper.Add(coreapi.SchemeGroupVersion.WithKind("Service"), apimeta.RESTScopeNamespace)
|
||||||
|
|
@ -49,122 +51,46 @@ func restMapper() apimeta.RESTMapper {
|
||||||
return mapper
|
return mapper
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupMetricNamer(t *testing.T) *metricNamer {
|
func setupMetricNamer(t testing.TB) []MetricNamer {
|
||||||
return &metricNamer{
|
cfg := config.DefaultConfig(1*time.Minute, "kube_")
|
||||||
overrides: map[string]seriesSpec{
|
namers, err := NamersFromConfig(cfg, restMapper())
|
||||||
"container_actually_gauge_seconds_total": {
|
require.NoError(t, err)
|
||||||
metricName: "actually_gauge",
|
return namers
|
||||||
kind: GaugeSeries,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
labelPrefix: "kube_",
|
|
||||||
mapper: restMapper(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMetricNamerContainerSeries(t *testing.T) {
|
var seriesRegistryTestSeries = [][]prom.Series{
|
||||||
testCases := []struct {
|
|
||||||
input prom.Series
|
|
||||||
outputMetricName string
|
|
||||||
outputInfo seriesInfo
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
input: prom.Series{
|
|
||||||
Name: "container_actually_gauge_seconds_total",
|
|
||||||
Labels: pmodel.LabelSet{"pod_name": "somepod", "namespace": "somens", "container_name": "somecont"},
|
|
||||||
},
|
|
||||||
outputMetricName: "actually_gauge",
|
|
||||||
outputInfo: seriesInfo{
|
|
||||||
baseSeries: prom.Series{Name: "container_actually_gauge_seconds_total"},
|
|
||||||
kind: GaugeSeries,
|
|
||||||
isContainer: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: prom.Series{
|
|
||||||
Name: "container_some_usage",
|
|
||||||
Labels: pmodel.LabelSet{"pod_name": "somepod", "namespace": "somens", "container_name": "somecont"},
|
|
||||||
},
|
|
||||||
outputMetricName: "some_usage",
|
|
||||||
outputInfo: seriesInfo{
|
|
||||||
baseSeries: prom.Series{Name: "container_some_usage"},
|
|
||||||
kind: GaugeSeries,
|
|
||||||
isContainer: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: prom.Series{
|
|
||||||
Name: "container_some_count_total",
|
|
||||||
Labels: pmodel.LabelSet{"pod_name": "somepod", "namespace": "somens", "container_name": "somecont"},
|
|
||||||
},
|
|
||||||
outputMetricName: "some_count",
|
|
||||||
outputInfo: seriesInfo{
|
|
||||||
baseSeries: prom.Series{Name: "container_some_count_total"},
|
|
||||||
kind: CounterSeries,
|
|
||||||
isContainer: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: prom.Series{
|
|
||||||
Name: "container_some_time_seconds_total",
|
|
||||||
Labels: pmodel.LabelSet{"pod_name": "somepod", "namespace": "somens", "container_name": "somecont"},
|
|
||||||
},
|
|
||||||
outputMetricName: "some_time",
|
|
||||||
outputInfo: seriesInfo{
|
|
||||||
baseSeries: prom.Series{Name: "container_some_time_seconds_total"},
|
|
||||||
kind: SecondsCounterSeries,
|
|
||||||
isContainer: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
namer := setupMetricNamer(t)
|
|
||||||
resMap := map[provider.MetricInfo]seriesInfo{}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
namer.processContainerSeries(test.input, resMap)
|
|
||||||
metric := provider.MetricInfo{
|
|
||||||
Metric: test.outputMetricName,
|
|
||||||
GroupResource: schema.GroupResource{Resource: "pods"},
|
|
||||||
Namespaced: true,
|
|
||||||
}
|
|
||||||
if assert.Contains(resMap, metric) {
|
|
||||||
assert.Equal(test.outputInfo, resMap[metric])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSeriesRegistry(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
require := require.New(t)
|
|
||||||
|
|
||||||
namer := setupMetricNamer(t)
|
|
||||||
registry := &basicSeriesRegistry{
|
|
||||||
namer: *namer,
|
|
||||||
}
|
|
||||||
|
|
||||||
inputSeries := []prom.Series{
|
|
||||||
// container series
|
// container series
|
||||||
{
|
{
|
||||||
Name: "container_actually_gauge_seconds_total",
|
|
||||||
Labels: pmodel.LabelSet{"pod_name": "somepod", "namespace": "somens", "container_name": "somecont"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "container_some_usage",
|
|
||||||
Labels: pmodel.LabelSet{"pod_name": "somepod", "namespace": "somens", "container_name": "somecont"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "container_some_count_total",
|
|
||||||
Labels: pmodel.LabelSet{"pod_name": "somepod", "namespace": "somens", "container_name": "somecont"},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Name: "container_some_time_seconds_total",
|
Name: "container_some_time_seconds_total",
|
||||||
Labels: pmodel.LabelSet{"pod_name": "somepod", "namespace": "somens", "container_name": "somecont"},
|
Labels: pmodel.LabelSet{"pod_name": "somepod", "namespace": "somens", "container_name": "somecont"},
|
||||||
},
|
},
|
||||||
// namespaced series
|
},
|
||||||
// a series that should turn into multiple metrics
|
{
|
||||||
|
{
|
||||||
|
Name: "container_some_count_total",
|
||||||
|
Labels: pmodel.LabelSet{"pod_name": "somepod", "namespace": "somens", "container_name": "somecont"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{
|
||||||
|
Name: "container_some_usage",
|
||||||
|
Labels: pmodel.LabelSet{"pod_name": "somepod", "namespace": "somens", "container_name": "somecont"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// guage metrics
|
||||||
|
{
|
||||||
|
Name: "node_gigawatts",
|
||||||
|
Labels: pmodel.LabelSet{"kube_node": "somenode"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "service_proxy_packets",
|
||||||
|
Labels: pmodel.LabelSet{"kube_service": "somesvc", "kube_namespace": "somens"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// cumulative --> rate metrics
|
||||||
{
|
{
|
||||||
Name: "ingress_hits_total",
|
Name: "ingress_hits_total",
|
||||||
Labels: pmodel.LabelSet{"kube_ingress": "someingress", "kube_service": "somesvc", "kube_pod": "backend1", "kube_namespace": "somens"},
|
Labels: pmodel.LabelSet{"kube_ingress": "someingress", "kube_service": "somesvc", "kube_pod": "backend1", "kube_namespace": "somens"},
|
||||||
|
|
@ -174,191 +100,151 @@ func TestSeriesRegistry(t *testing.T) {
|
||||||
Labels: pmodel.LabelSet{"kube_ingress": "someingress", "kube_service": "somesvc", "kube_pod": "backend2", "kube_namespace": "somens"},
|
Labels: pmodel.LabelSet{"kube_ingress": "someingress", "kube_service": "somesvc", "kube_pod": "backend2", "kube_namespace": "somens"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "service_proxy_packets",
|
Name: "volume_claims_total",
|
||||||
Labels: pmodel.LabelSet{"kube_service": "somesvc", "kube_namespace": "somens"},
|
Labels: pmodel.LabelSet{"kube_persistentvolume": "somepv"},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// cumulative seconds --> rate metrics
|
||||||
{
|
{
|
||||||
Name: "work_queue_wait_seconds_total",
|
Name: "work_queue_wait_seconds_total",
|
||||||
Labels: pmodel.LabelSet{"kube_deployment": "somedep", "kube_namespace": "somens"},
|
Labels: pmodel.LabelSet{"kube_deployment": "somedep", "kube_namespace": "somens"},
|
||||||
},
|
},
|
||||||
// non-namespaced series
|
|
||||||
{
|
|
||||||
Name: "node_gigawatts",
|
|
||||||
Labels: pmodel.LabelSet{"kube_node": "somenode"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "volume_claims_total",
|
|
||||||
Labels: pmodel.LabelSet{"kube_persistentvolume": "somepv"},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Name: "node_fan_seconds_total",
|
Name: "node_fan_seconds_total",
|
||||||
Labels: pmodel.LabelSet{"kube_node": "somenode"},
|
Labels: pmodel.LabelSet{"kube_node": "somenode"},
|
||||||
},
|
},
|
||||||
// unrelated series
|
|
||||||
{
|
|
||||||
Name: "admin_coffee_liters_total",
|
|
||||||
Labels: pmodel.LabelSet{"admin": "some-admin"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "admin_unread_emails",
|
|
||||||
Labels: pmodel.LabelSet{"admin": "some-admin"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "admin_reddit_seconds_total",
|
|
||||||
Labels: pmodel.LabelSet{"kube_admin": "some-admin"},
|
|
||||||
},
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSeriesRegistry(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
|
namers := setupMetricNamer(t)
|
||||||
|
registry := &basicSeriesRegistry{
|
||||||
|
mapper: restMapper(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// set up the registry
|
// set up the registry
|
||||||
require.NoError(registry.SetSeries(inputSeries))
|
require.NoError(registry.SetSeries(seriesRegistryTestSeries, namers))
|
||||||
|
|
||||||
// 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.CustomMetricInfo
|
||||||
namespace string
|
namespace string
|
||||||
resourceNames []string
|
resourceNames []string
|
||||||
|
|
||||||
expectedKind SeriesType
|
|
||||||
expectedQuery string
|
expectedQuery string
|
||||||
expectedGroupBy string
|
|
||||||
}{
|
}{
|
||||||
// container metrics
|
// container metrics
|
||||||
{
|
|
||||||
title: "container metrics overrides / single resource name",
|
|
||||||
info: provider.MetricInfo{schema.GroupResource{Resource: "pods"}, true, "actually_gauge"},
|
|
||||||
namespace: "somens",
|
|
||||||
resourceNames: []string{"somepod"},
|
|
||||||
|
|
||||||
expectedKind: GaugeSeries,
|
|
||||||
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.CustomMetricInfo{schema.GroupResource{Resource: "pods"}, true, "some_usage"},
|
||||||
namespace: "somens",
|
namespace: "somens",
|
||||||
resourceNames: []string{"somepod1", "somepod2"},
|
resourceNames: []string{"somepod1", "somepod2"},
|
||||||
|
|
||||||
expectedKind: GaugeSeries,
|
expectedQuery: "sum(container_some_usage{namespace=\"somens\",pod_name=~\"somepod1|somepod2\",container_name!=\"POD\"}) by (pod_name)",
|
||||||
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.CustomMetricInfo{schema.GroupResource{Resource: "pods"}, true, "some_count"},
|
||||||
namespace: "somens",
|
namespace: "somens",
|
||||||
resourceNames: []string{"somepod1", "somepod2"},
|
resourceNames: []string{"somepod1", "somepod2"},
|
||||||
|
|
||||||
expectedKind: CounterSeries,
|
expectedQuery: "sum(rate(container_some_count_total{namespace=\"somens\",pod_name=~\"somepod1|somepod2\",container_name!=\"POD\"}[1m])) by (pod_name)",
|
||||||
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.CustomMetricInfo{schema.GroupResource{Resource: "pods"}, true, "some_time"},
|
||||||
namespace: "somens",
|
namespace: "somens",
|
||||||
resourceNames: []string{"somepod1", "somepod2"},
|
resourceNames: []string{"somepod1", "somepod2"},
|
||||||
|
|
||||||
expectedKind: SecondsCounterSeries,
|
expectedQuery: "sum(rate(container_some_time_seconds_total{namespace=\"somens\",pod_name=~\"somepod1|somepod2\",container_name!=\"POD\"}[1m])) by (pod_name)",
|
||||||
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.CustomMetricInfo{schema.GroupResource{Resource: "service"}, true, "ingress_hits"},
|
||||||
namespace: "somens",
|
namespace: "somens",
|
||||||
resourceNames: []string{"somesvc"},
|
resourceNames: []string{"somesvc"},
|
||||||
|
|
||||||
expectedKind: CounterSeries,
|
expectedQuery: "sum(rate(ingress_hits_total{kube_namespace=\"somens\",kube_service=\"somesvc\"}[1m])) by (kube_service)",
|
||||||
expectedQuery: "ingress_hits_total{kube_service=\"somesvc\",kube_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.CustomMetricInfo{schema.GroupResource{Group: "extensions", Resource: "ingress"}, true, "ingress_hits"},
|
||||||
namespace: "somens",
|
namespace: "somens",
|
||||||
resourceNames: []string{"someingress"},
|
resourceNames: []string{"someingress"},
|
||||||
|
|
||||||
expectedKind: CounterSeries,
|
expectedQuery: "sum(rate(ingress_hits_total{kube_namespace=\"somens\",kube_ingress=\"someingress\"}[1m])) by (kube_ingress)",
|
||||||
expectedQuery: "ingress_hits_total{kube_ingress=\"someingress\",kube_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.CustomMetricInfo{schema.GroupResource{Resource: "pod"}, true, "ingress_hits"},
|
||||||
namespace: "somens",
|
namespace: "somens",
|
||||||
resourceNames: []string{"somepod"},
|
resourceNames: []string{"somepod"},
|
||||||
|
|
||||||
expectedKind: CounterSeries,
|
expectedQuery: "sum(rate(ingress_hits_total{kube_namespace=\"somens\",kube_pod=\"somepod\"}[1m])) by (kube_pod)",
|
||||||
expectedQuery: "ingress_hits_total{kube_pod=\"somepod\",kube_namespace=\"somens\"}",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "namespaced metrics gauge",
|
title: "namespaced metrics gauge",
|
||||||
info: provider.MetricInfo{schema.GroupResource{Resource: "service"}, true, "service_proxy_packets"},
|
info: provider.CustomMetricInfo{schema.GroupResource{Resource: "service"}, true, "service_proxy_packets"},
|
||||||
namespace: "somens",
|
namespace: "somens",
|
||||||
resourceNames: []string{"somesvc"},
|
resourceNames: []string{"somesvc"},
|
||||||
|
|
||||||
expectedKind: GaugeSeries,
|
expectedQuery: "sum(service_proxy_packets{kube_namespace=\"somens\",kube_service=\"somesvc\"}) by (kube_service)",
|
||||||
expectedQuery: "service_proxy_packets{kube_service=\"somesvc\",kube_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.CustomMetricInfo{schema.GroupResource{Group: "extensions", Resource: "deployment"}, true, "work_queue_wait"},
|
||||||
namespace: "somens",
|
namespace: "somens",
|
||||||
resourceNames: []string{"somedep"},
|
resourceNames: []string{"somedep"},
|
||||||
|
|
||||||
expectedKind: SecondsCounterSeries,
|
expectedQuery: "sum(rate(work_queue_wait_seconds_total{kube_namespace=\"somens\",kube_deployment=\"somedep\"}[1m])) by (kube_deployment)",
|
||||||
expectedQuery: "work_queue_wait_seconds_total{kube_deployment=\"somedep\",kube_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.CustomMetricInfo{schema.GroupResource{Resource: "node"}, false, "node_gigawatts"},
|
||||||
resourceNames: []string{"somenode"},
|
resourceNames: []string{"somenode"},
|
||||||
|
|
||||||
expectedKind: GaugeSeries,
|
expectedQuery: "sum(node_gigawatts{kube_node=\"somenode\"}) by (kube_node)",
|
||||||
expectedQuery: "node_gigawatts{kube_node=\"somenode\"}",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "root scoped metrics counter",
|
title: "root scoped metrics counter",
|
||||||
info: provider.MetricInfo{schema.GroupResource{Resource: "persistentvolume"}, false, "volume_claims"},
|
info: provider.CustomMetricInfo{schema.GroupResource{Resource: "persistentvolume"}, false, "volume_claims"},
|
||||||
resourceNames: []string{"somepv"},
|
resourceNames: []string{"somepv"},
|
||||||
|
|
||||||
expectedKind: CounterSeries,
|
expectedQuery: "sum(rate(volume_claims_total{kube_persistentvolume=\"somepv\"}[1m])) by (kube_persistentvolume)",
|
||||||
expectedQuery: "volume_claims_total{kube_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.CustomMetricInfo{schema.GroupResource{Resource: "node"}, false, "node_fan"},
|
||||||
resourceNames: []string{"somenode"},
|
resourceNames: []string{"somenode"},
|
||||||
|
|
||||||
expectedKind: SecondsCounterSeries,
|
expectedQuery: "sum(rate(node_fan_seconds_total{kube_node=\"somenode\"}[1m])) by (kube_node)",
|
||||||
expectedQuery: "node_fan_seconds_total{kube_node=\"somenode\"}",
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
outputKind, outputQuery, groupBy, found := registry.QueryForMetric(testCase.info, testCase.namespace, testCase.resourceNames...)
|
outputQuery, 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(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 = registry.namer.labelPrefix + testCase.info.GroupResource.Resource
|
|
||||||
}
|
|
||||||
assert.Equal(expectedGroupBy, groupBy, "%s: metric %v should have produced the correct groupBy clause", testCase.title, testCase.info)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
allMetrics := registry.ListAllMetrics()
|
allMetrics := registry.ListAllMetrics()
|
||||||
expectedMetrics := []provider.MetricInfo{
|
expectedMetrics := []provider.CustomMetricInfo{
|
||||||
{schema.GroupResource{Resource: "pods"}, true, "actually_gauge"},
|
|
||||||
{schema.GroupResource{Resource: "pods"}, true, "some_usage"},
|
|
||||||
{schema.GroupResource{Resource: "pods"}, true, "some_count"},
|
{schema.GroupResource{Resource: "pods"}, true, "some_count"},
|
||||||
|
{schema.GroupResource{Resource: "namespaces"}, false, "some_count"},
|
||||||
{schema.GroupResource{Resource: "pods"}, true, "some_time"},
|
{schema.GroupResource{Resource: "pods"}, true, "some_time"},
|
||||||
|
{schema.GroupResource{Resource: "namespaces"}, false, "some_time"},
|
||||||
|
{schema.GroupResource{Resource: "pods"}, true, "some_usage"},
|
||||||
|
{schema.GroupResource{Resource: "namespaces"}, false, "some_usage"},
|
||||||
{schema.GroupResource{Resource: "services"}, true, "ingress_hits"},
|
{schema.GroupResource{Resource: "services"}, true, "ingress_hits"},
|
||||||
{schema.GroupResource{Group: "extensions", Resource: "ingresses"}, true, "ingress_hits"},
|
{schema.GroupResource{Group: "extensions", Resource: "ingresses"}, true, "ingress_hits"},
|
||||||
{schema.GroupResource{Resource: "pods"}, true, "ingress_hits"},
|
{schema.GroupResource{Resource: "pods"}, true, "ingress_hits"},
|
||||||
|
|
@ -379,8 +265,32 @@ func TestSeriesRegistry(t *testing.T) {
|
||||||
assert.Equal(expectedMetrics, allMetrics, "should have listed all expected metrics")
|
assert.Equal(expectedMetrics, allMetrics, "should have listed all expected metrics")
|
||||||
}
|
}
|
||||||
|
|
||||||
// metricInfoSorter is a sort.Interface for sorting provider.MetricInfos
|
func BenchmarkSetSeries(b *testing.B) {
|
||||||
type metricInfoSorter []provider.MetricInfo
|
namers := setupMetricNamer(b)
|
||||||
|
registry := &basicSeriesRegistry{
|
||||||
|
mapper: restMapper(),
|
||||||
|
}
|
||||||
|
|
||||||
|
numDuplicates := 10000
|
||||||
|
newSeriesSlices := make([][]prom.Series, len(seriesRegistryTestSeries))
|
||||||
|
for i, seriesSlice := range seriesRegistryTestSeries {
|
||||||
|
newSlice := make([]prom.Series, len(seriesSlice)*numDuplicates)
|
||||||
|
for j, series := range seriesSlice {
|
||||||
|
for k := 0; k < numDuplicates; k++ {
|
||||||
|
newSlice[j*numDuplicates+k] = series
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newSeriesSlices[i] = newSlice
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
registry.SetSeries(newSeriesSlices, namers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// metricInfoSorter is a sort.Interface for sorting provider.CustomMetricInfos
|
||||||
|
type metricInfoSorter []provider.CustomMetricInfo
|
||||||
|
|
||||||
func (s metricInfoSorter) Len() int {
|
func (s metricInfoSorter) Len() int {
|
||||||
return len(s)
|
return len(s)
|
||||||
Loading…
Add table
Add a link
Reference in a new issue