Merge pull request #77037 from logicalhan/counter-wrapper

Add wrappers for prometheus.Counter/CounterVec and prometheus.Registry
This commit is contained in:
Kubernetes Prow Robot 2019-05-08 14:32:32 -07:00 committed by GitHub
commit 19431e0168
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 1540 additions and 0 deletions

View File

@ -14,6 +14,7 @@ github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf h1:eg0MeVzs
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/beorn7/perks v0.0.0-20160229213445-3ac7bf7a47d1 h1:OnJHjoVbY69GG4gclp0ngXfywigLhR6rrgUxmxQRWO4=
github.com/beorn7/perks v0.0.0-20160229213445-3ac7bf7a47d1/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/coreos/bbolt v1.3.1-coreos.6 h1:uTXKg9gY70s9jMAKdfljFQcuh4e/BXOM+V+d00KFj3A=
github.com/coreos/bbolt v1.3.1-coreos.6/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ=

View File

@ -12,6 +12,7 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/beorn7/perks v0.0.0-20160229213445-3ac7bf7a47d1 h1:OnJHjoVbY69GG4gclp0ngXfywigLhR6rrgUxmxQRWO4=
github.com/beorn7/perks v0.0.0-20160229213445-3ac7bf7a47d1/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/coreos/bbolt v1.3.1-coreos.6 h1:uTXKg9gY70s9jMAKdfljFQcuh4e/BXOM+V+d00KFj3A=
github.com/coreos/bbolt v1.3.1-coreos.6/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ=

View File

@ -14,6 +14,7 @@ filegroup(
"//staging/src/k8s.io/component-base/config:all-srcs",
"//staging/src/k8s.io/component-base/featuregate:all-srcs",
"//staging/src/k8s.io/component-base/logs:all-srcs",
"//staging/src/k8s.io/component-base/metrics:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],

View File

@ -5,6 +5,10 @@ module k8s.io/component-base
go 1.12
require (
github.com/blang/semver v3.5.0+incompatible
github.com/prometheus/client_golang v0.9.2
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275
github.com/spf13/pflag v1.0.1
github.com/stretchr/testify v1.2.2
k8s.io/apimachinery v0.0.0
@ -13,6 +17,7 @@ require (
)
replace (
github.com/beorn7/perks => github.com/beorn7/perks v0.0.0-20160229213445-3ac7bf7a47d1
golang.org/x/sync => golang.org/x/sync v0.0.0-20181108010431-42b317875d0f
golang.org/x/sys => golang.org/x/sys v0.0.0-20190209173611-3b5209105503
golang.org/x/tools => golang.org/x/tools v0.0.0-20190313210603-aa82965741a9

View File

@ -1,3 +1,7 @@
github.com/beorn7/perks v0.0.0-20160229213445-3ac7bf7a47d1 h1:OnJHjoVbY69GG4gclp0ngXfywigLhR6rrgUxmxQRWO4=
github.com/beorn7/perks v0.0.0-20160229213445-3ac7bf7a47d1/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/blang/semver v3.5.0+incompatible h1:CGxCgetQ64DKk7rdZ++Vfnb1+ogGNnB17OJKJXD2Cfs=
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
@ -7,6 +11,7 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415 h1:WSBJMqJbLxsn+bTCPyPYZfqHdJmc8MK4wrBjMft6BAM=
github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck=
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
@ -16,6 +21,8 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be h1:AHimNtVIpiBjPUhEF5KNCkrUyqTSA5zWUl8sQ2bfGBE=
github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
@ -25,12 +32,21 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.2 h1:awm861/B8OKDd2I/6o1dy3ra4BamzKhYOiGItCeZ740=
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275 h1:PnBWHBf+6L0jOqq0gIVUe6Yk0/QMZ640k6NvkxcBf+8=
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a h1:9a8MnZMP0X2nLJdBg+pBmGgkJlSaKC2KaQmTCk1XDtE=
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
golang.org/x/net v0.0.0-20190206173232-65e2d4e15006 h1:bfLnR+k0tq5Lqt6dflRLcZiz6UaXCMt3vhYJ1l4FQ80=
golang.org/x/net v0.0.0-20190206173232-65e2d4e15006/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db h1:6/JqlYfC1CCaLnGceQTI+sDGhC9UBSPAsBqI0Gun6kU=

View File

@ -0,0 +1,61 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"counter.go",
"metric.go",
"opts.go",
"registry.go",
"version_parser.go",
"wrappers.go",
],
importmap = "k8s.io/kubernetes/vendor/k8s.io/component-base/metrics",
importpath = "k8s.io/component-base/metrics",
deps = [
"//staging/src/k8s.io/apimachinery/pkg/version:go_default_library",
"//vendor/github.com/blang/semver:go_default_library",
"//vendor/github.com/prometheus/client_golang/prometheus:go_default_library",
"//vendor/github.com/prometheus/client_model/go:go_default_library",
"//vendor/k8s.io/klog:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = [
"counter_test.go",
"registry_test.go",
"version_parser_test.go",
],
embed = [":go_default_library"],
deps = [
"//staging/src/k8s.io/apimachinery/pkg/version:go_default_library",
"//vendor/github.com/blang/semver:go_default_library",
"//vendor/github.com/prometheus/client_golang/prometheus:go_default_library",
"//vendor/github.com/prometheus/common/expfmt:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//staging/src/k8s.io/component-base/metrics/legacyregistry:all-srcs",
],
tags = ["automanaged"],
)

View File

@ -0,0 +1,145 @@
/*
Copyright 2019 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 metrics
import (
"github.com/blang/semver"
"github.com/prometheus/client_golang/prometheus"
)
// Counter is our internal representation for our wrapping struct around prometheus
// counters. Counter implements both KubeCollector and CounterMetric.
type Counter struct {
CounterMetric
*CounterOpts
lazyMetric
selfCollector
}
// NewCounter returns an object which satisfies the KubeCollector and CounterMetric interfaces.
// However, the object returned will not measure anything unless the collector is first
// registered, since the metric is lazily instantiated.
func NewCounter(opts *CounterOpts) *Counter {
// todo: handle defaulting better
if opts.StabilityLevel == "" {
opts.StabilityLevel = ALPHA
}
kc := &Counter{
CounterOpts: opts,
lazyMetric: lazyMetric{},
}
kc.setPrometheusCounter(noop)
kc.lazyInit(kc)
return kc
}
// setPrometheusCounter sets the underlying CounterMetric object, i.e. the thing that does the measurement.
func (c *Counter) setPrometheusCounter(counter prometheus.Counter) {
c.CounterMetric = counter
c.initSelfCollection(counter)
}
// DeprecatedVersion returns a pointer to the Version or nil
func (c *Counter) DeprecatedVersion() *semver.Version {
return c.CounterOpts.DeprecatedVersion
}
// initializeMetric invocation creates the actual underlying Counter. Until this method is called
// the underlying counter is a no-op.
func (c *Counter) initializeMetric() {
c.CounterOpts.annotateStabilityLevel()
// this actually creates the underlying prometheus counter.
c.setPrometheusCounter(prometheus.NewCounter(c.CounterOpts.toPromCounterOpts()))
}
// initializeDeprecatedMetric invocation creates the actual (but deprecated) Counter. Until this method
// is called the underlying counter is a no-op.
func (c *Counter) initializeDeprecatedMetric() {
c.CounterOpts.markDeprecated()
c.initializeMetric()
}
// CounterVec is the internal representation of our wrapping struct around prometheus
// counterVecs. CounterVec implements both KubeCollector and CounterVecMetric.
type CounterVec struct {
*prometheus.CounterVec
*CounterOpts
lazyMetric
originalLabels []string
}
// NewCounterVec returns an object which satisfies the KubeCollector and CounterVecMetric interfaces.
// However, the object returned will not measure anything unless the collector is first
// registered, since the metric is lazily instantiated.
func NewCounterVec(opts *CounterOpts, labels []string) *CounterVec {
cv := &CounterVec{
CounterVec: noopCounterVec,
CounterOpts: opts,
originalLabels: labels,
lazyMetric: lazyMetric{},
}
cv.lazyInit(cv)
return cv
}
// DeprecatedVersion returns a pointer to the Version or nil
func (v *CounterVec) DeprecatedVersion() *semver.Version {
return v.CounterOpts.DeprecatedVersion
}
// initializeMetric invocation creates the actual underlying CounterVec. Until this method is called
// the underlying counterVec is a no-op.
func (v *CounterVec) initializeMetric() {
v.CounterVec = prometheus.NewCounterVec(v.CounterOpts.toPromCounterOpts(), v.originalLabels)
}
// initializeDeprecatedMetric invocation creates the actual (but deprecated) CounterVec. Until this method is called
// the underlying counterVec is a no-op.
func (v *CounterVec) initializeDeprecatedMetric() {
v.CounterOpts.markDeprecated()
v.initializeMetric()
}
// Default Prometheus behavior actually results in the creation of a new metric
// if a metric with the unique label values is not found in the underlying stored metricMap.
// This means that if this function is called but the underlying metric is not registered
// (which means it will never be exposed externally nor consumed), the metric will exist in memory
// for perpetuity (i.e. throughout application lifecycle).
//
// For reference: https://github.com/prometheus/client_golang/blob/v0.9.2/prometheus/counter.go#L179-L197
// WithLabelValues returns the Counter for the given slice of label
// values (same order as the VariableLabels in Desc). If that combination of
// label values is accessed for the first time, a new Counter is created IFF the counterVec
// has been registered to a metrics registry.
func (v *CounterVec) WithLabelValues(lvs ...string) CounterMetric {
if !v.IsCreated() {
return noop // return no-op counter
}
return v.CounterVec.WithLabelValues(lvs...)
}
// With returns the Counter for the given Labels map (the label names
// must match those of the VariableLabels in Desc). If that label map is
// accessed for the first time, a new Counter is created IFF the counterVec has
// been registered to a metrics registry.
func (v *CounterVec) With(labels prometheus.Labels) CounterMetric {
if !v.IsCreated() {
return noop // return no-op counter
}
return v.CounterVec.With(labels)
}

View File

@ -0,0 +1,219 @@
/*
Copyright 2019 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 metrics
import (
"bytes"
"github.com/blang/semver"
"github.com/prometheus/common/expfmt"
apimachineryversion "k8s.io/apimachinery/pkg/version"
"testing"
)
func TestCounter(t *testing.T) {
v114 := semver.MustParse("1.14.0")
v115 := semver.MustParse("1.15.0")
var tests = []struct {
desc string
*CounterOpts
expectedMetricCount int
expectedHelp string
}{
{
desc: "Test non deprecated",
CounterOpts: &CounterOpts{
Namespace: "namespace",
Name: "metric_test_name",
Subsystem: "subsystem",
StabilityLevel: ALPHA,
Help: "counter help",
},
expectedMetricCount: 1,
expectedHelp: "[ALPHA] counter help",
},
{
desc: "Test deprecated",
CounterOpts: &CounterOpts{
Namespace: "namespace",
Name: "metric_test_name",
Subsystem: "subsystem",
Help: "counter help",
StabilityLevel: ALPHA,
DeprecatedVersion: &v115,
},
expectedMetricCount: 1,
expectedHelp: "[ALPHA] (Deprecated since 1.15.0) counter help",
},
{
desc: "Test hidden",
CounterOpts: &CounterOpts{
Namespace: "namespace",
Name: "metric_test_name",
Subsystem: "subsystem",
Help: "counter help",
StabilityLevel: ALPHA,
DeprecatedVersion: &v114,
},
expectedMetricCount: 0,
},
}
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
registry := NewKubeRegistry(apimachineryversion.Info{
Major: "1",
Minor: "15",
GitVersion: "v1.15.0-alpha-1.12345",
})
c := NewCounter(test.CounterOpts)
registry.MustRegister(c)
ms, err := registry.Gather()
var buf bytes.Buffer
enc := expfmt.NewEncoder(&buf, "text/plain; version=0.0.4; charset=utf-8")
if len(ms) != test.expectedMetricCount {
t.Errorf("Got %v metrics, Want: %v metrics", len(ms), test.expectedMetricCount)
}
if err != nil {
t.Fatalf("Gather failed %v", err)
}
for _, metric := range ms {
err := enc.Encode(metric)
if err != nil {
t.Fatalf("Unexpected err %v in encoding the metric", err)
}
if metric.GetHelp() != test.expectedHelp {
t.Errorf("Got %s as help message, want %s", metric.GetHelp(), test.expectedHelp)
}
}
// increment the counter N number of times and verify that the metric retains the count correctly
numberOfTimesToIncrement := 3
for i := 0; i < numberOfTimesToIncrement; i++ {
c.Inc()
}
ms, err = registry.Gather()
if err != nil {
t.Fatalf("Gather failed %v", err)
}
for _, mf := range ms {
for _, m := range mf.GetMetric() {
if int(m.GetCounter().GetValue()) != numberOfTimesToIncrement {
t.Errorf("Got %v, wanted %v as the count", m.GetCounter().GetValue(), numberOfTimesToIncrement)
}
}
}
})
}
}
func TestCounterVec(t *testing.T) {
v115 := semver.MustParse("1.15.0")
v114 := semver.MustParse("1.14.0")
var tests = []struct {
desc string
*CounterOpts
labels []string
registryVersion *semver.Version
expectedMetricFamilyCount int
expectedHelp string
}{
{
desc: "Test non deprecated",
CounterOpts: &CounterOpts{
Namespace: "namespace",
Name: "metric_test_name",
Subsystem: "subsystem",
Help: "counter help",
},
labels: []string{"label_a", "label_b"},
expectedMetricFamilyCount: 1,
expectedHelp: "counter help",
},
{
desc: "Test deprecated",
CounterOpts: &CounterOpts{
Namespace: "namespace",
Name: "metric_test_name",
Subsystem: "subsystem",
Help: "counter help",
DeprecatedVersion: &v115,
},
labels: []string{"label_a", "label_b"},
expectedMetricFamilyCount: 1,
expectedHelp: "(Deprecated since 1.15.0) counter help",
},
{
desc: "Test hidden",
CounterOpts: &CounterOpts{
Namespace: "namespace",
Name: "metric_test_name",
Subsystem: "subsystem",
Help: "counter help",
DeprecatedVersion: &v114,
},
labels: []string{"label_a", "label_b"},
expectedMetricFamilyCount: 0,
expectedHelp: "counter help",
},
}
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
registry := NewKubeRegistry(apimachineryversion.Info{
Major: "1",
Minor: "15",
GitVersion: "v1.15.0-alpha-1.12345",
})
c := NewCounterVec(test.CounterOpts, test.labels)
registry.MustRegister(c)
c.WithLabelValues("1", "2").Inc()
mfs, err := registry.Gather()
if len(mfs) != test.expectedMetricFamilyCount {
t.Errorf("Got %v metric families, Want: %v metric families", len(mfs), test.expectedMetricFamilyCount)
}
if err != nil {
t.Fatalf("Gather failed %v", err)
}
// this no-opts here when there are no metric families (i.e. when the metric is hidden)
for _, mf := range mfs {
if len(mf.GetMetric()) != 1 {
t.Errorf("Got %v metrics, wanted 1 as the count", len(mf.GetMetric()))
}
if mf.GetHelp() != test.expectedHelp {
t.Errorf("Got %s as help message, want %s", mf.GetHelp(), test.expectedHelp)
}
}
// let's increment the counter and verify that the metric still works
c.WithLabelValues("1", "3").Inc()
c.WithLabelValues("2", "3").Inc()
mfs, err = registry.Gather()
if err != nil {
t.Fatalf("Gather failed %v", err)
}
// this no-opts here when there are no metric families (i.e. when the metric is hidden)
for _, mf := range mfs {
if len(mf.GetMetric()) != 3 {
t.Errorf("Got %v metrics, wanted 3 as the count", len(mf.GetMetric()))
}
}
})
}
}

View File

@ -0,0 +1,40 @@
package(default_visibility = ["//visibility:public"])
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = ["registry.go"],
importmap = "k8s.io/kubernetes/vendor/k8s.io/component-base/metrics/legacyregistry",
importpath = "k8s.io/component-base/metrics/legacyregistry",
deps = [
"//staging/src/k8s.io/apimachinery/pkg/version:go_default_library",
"//staging/src/k8s.io/component-base/metrics:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)
go_test(
name = "go_default_test",
srcs = ["registry_test.go"],
embed = [":go_default_library"],
deps = [
"//staging/src/k8s.io/apimachinery/pkg/version:go_default_library",
"//staging/src/k8s.io/component-base/metrics:go_default_library",
"//vendor/github.com/blang/semver:go_default_library",
"//vendor/github.com/prometheus/client_golang/prometheus:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library",
],
)

View File

@ -0,0 +1,95 @@
/*
Copyright 2019 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 legacyregistry
import (
"fmt"
apimachineryversion "k8s.io/apimachinery/pkg/version"
"k8s.io/component-base/metrics"
"sync"
)
var globalRegistryFactory = metricsRegistryFactory{
registerQueue: make([]metrics.KubeCollector, 0),
mustRegisterQueue: make([]metrics.KubeCollector, 0),
}
type metricsRegistryFactory struct {
globalRegistry metrics.KubeRegistry
kubeVersion *apimachineryversion.Info
registrationLock sync.Mutex
registerQueue []metrics.KubeCollector
mustRegisterQueue []metrics.KubeCollector
}
// SetRegistryFactoryVersion sets the kubernetes version information for all
// subsequent metrics registry initializations. Only the first call has an effect.
// If a version is not set, then metrics registry creation will no-opt
func SetRegistryFactoryVersion(ver apimachineryversion.Info) []error {
globalRegistryFactory.registrationLock.Lock()
defer globalRegistryFactory.registrationLock.Unlock()
if globalRegistryFactory.kubeVersion != nil {
if globalRegistryFactory.kubeVersion.String() != ver.String() {
panic(fmt.Sprintf("Cannot load a global registry more than once, had %s tried to load %s",
globalRegistryFactory.kubeVersion.String(),
ver.String()))
}
return nil
}
registrationErrs := make([]error, 0)
globalRegistryFactory.globalRegistry = metrics.NewKubeRegistry(ver)
globalRegistryFactory.kubeVersion = &ver
for _, c := range globalRegistryFactory.registerQueue {
err := globalRegistryFactory.globalRegistry.Register(c)
if err != nil {
registrationErrs = append(registrationErrs, err)
}
}
for _, c := range globalRegistryFactory.mustRegisterQueue {
globalRegistryFactory.globalRegistry.MustRegister(c)
}
return registrationErrs
}
// Register registers a collectable metric, but it uses a global registry. Registration is deferred
// until the global registry has a version to use.
func Register(c metrics.KubeCollector) error {
globalRegistryFactory.registrationLock.Lock()
defer globalRegistryFactory.registrationLock.Unlock()
if globalRegistryFactory.kubeVersion != nil {
return globalRegistryFactory.globalRegistry.Register(c)
}
globalRegistryFactory.registerQueue = append(globalRegistryFactory.registerQueue, c)
return nil
}
// MustRegister works like Register but registers any number of
// Collectors and panics upon the first registration that causes an
// error. Registration is deferred until the global registry has a version to use.
func MustRegister(cs ...metrics.KubeCollector) {
globalRegistryFactory.registrationLock.Lock()
defer globalRegistryFactory.registrationLock.Unlock()
if globalRegistryFactory.kubeVersion != nil {
globalRegistryFactory.globalRegistry.MustRegister(cs...)
return
}
for _, c := range cs {
globalRegistryFactory.mustRegisterQueue = append(globalRegistryFactory.mustRegisterQueue, c)
}
}

View File

@ -0,0 +1,195 @@
/*
Copyright 2019 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 legacyregistry
import (
"github.com/blang/semver"
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/assert"
"k8s.io/component-base/metrics"
"testing"
apimachineryversion "k8s.io/apimachinery/pkg/version"
)
func init() {
SetRegistryFactoryVersion(apimachineryversion.Info{
Major: "1",
Minor: "15",
GitVersion: "v1.15.0-alpha-1.12345",
})
}
var (
v115 = semver.MustParse("1.15.0")
v114 = semver.MustParse("1.14.0")
alphaCounter = metrics.NewCounter(
&metrics.CounterOpts{
Namespace: "some_namespace",
Name: "test_counter_name",
Subsystem: "subsystem",
StabilityLevel: metrics.ALPHA,
Help: "counter help",
},
)
alphaDeprecatedCounter = metrics.NewCounter(
&metrics.CounterOpts{
Namespace: "some_namespace",
Name: "test_alpha_dep_counter",
Subsystem: "subsystem",
StabilityLevel: metrics.ALPHA,
Help: "counter help",
DeprecatedVersion: &v115,
},
)
alphaHiddenCounter = metrics.NewCounter(
&metrics.CounterOpts{
Namespace: "some_namespace",
Name: "test_alpha_hidden_counter",
Subsystem: "subsystem",
StabilityLevel: metrics.ALPHA,
Help: "counter help",
DeprecatedVersion: &v114,
},
)
)
func TestRegister(t *testing.T) {
var tests = []struct {
desc string
metrics []*metrics.Counter
registryVersion *semver.Version
expectedErrors []error
expectedIsCreatedValues []bool
expectedIsDeprecated []bool
expectedIsHidden []bool
}{
{
desc: "test registering same metric multiple times",
metrics: []*metrics.Counter{alphaCounter, alphaCounter},
expectedErrors: []error{nil, prometheus.AlreadyRegisteredError{}},
expectedIsCreatedValues: []bool{true, true},
expectedIsDeprecated: []bool{false, false},
expectedIsHidden: []bool{false, false},
},
}
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
//t.Errorf("len %v - %v\n", len(test.metrics), len(test.expectedErrors))
for i, m := range test.metrics {
//t.Errorf("m %v\n", m)
err := Register(m)
if err != test.expectedErrors[i] && err.Error() != test.expectedErrors[i].Error() {
t.Errorf("Got unexpected error %v, wanted %v", err, test.expectedErrors[i])
}
if m.IsCreated() != test.expectedIsCreatedValues[i] {
t.Errorf("Got isCreated == %v, wanted isCreated to be %v", m.IsCreated(), test.expectedIsCreatedValues[i])
}
if m.IsDeprecated() != test.expectedIsDeprecated[i] {
t.Errorf("Got IsDeprecated == %v, wanted IsDeprecated to be %v", m.IsDeprecated(), test.expectedIsDeprecated[i])
}
if m.IsHidden() != test.expectedIsHidden[i] {
t.Errorf("Got IsHidden == %v, wanted IsHidden to be %v", m.IsHidden(), test.expectedIsDeprecated[i])
}
}
})
}
}
func TestMustRegister(t *testing.T) {
var tests = []struct {
desc string
metrics []*metrics.Counter
registryVersion *semver.Version
expectedPanics []bool
}{
{
desc: "test must registering same deprecated metric",
metrics: []*metrics.Counter{alphaDeprecatedCounter, alphaDeprecatedCounter},
expectedPanics: []bool{false, true},
},
{
desc: "test alpha hidden metric",
metrics: []*metrics.Counter{alphaHiddenCounter},
expectedPanics: []bool{false},
},
}
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
for i, m := range test.metrics {
if test.expectedPanics[i] {
assert.Panics(t,
func() { MustRegister(m) },
"Did not panic even though we expected it.")
} else {
MustRegister(m)
}
}
})
}
}
func TestDeferredRegister(t *testing.T) {
// reset the global registry for this test.
globalRegistryFactory = metricsRegistryFactory{
registerQueue: make([]metrics.KubeCollector, 0),
mustRegisterQueue: make([]metrics.KubeCollector, 0),
}
var err error
err = Register(alphaDeprecatedCounter)
if err != nil {
t.Errorf("Got err == %v, expected no error", err)
}
err = Register(alphaDeprecatedCounter)
if err != nil {
t.Errorf("Got err == %v, expected no error", err)
}
// set the global registry version
errs := SetRegistryFactoryVersion(apimachineryversion.Info{
Major: "1",
Minor: "15",
GitVersion: "v1.15.0-alpha-1.12345",
})
if len(errs) != 1 {
t.Errorf("Got %d errs, expected 1", len(errs))
for _, err := range errs {
t.Logf("\t Got %v", err)
}
}
}
func TestDeferredMustRegister(t *testing.T) {
// reset the global registry for this test.
globalRegistryFactory = metricsRegistryFactory{
registerQueue: make([]metrics.KubeCollector, 0),
mustRegisterQueue: make([]metrics.KubeCollector, 0),
}
MustRegister(alphaDeprecatedCounter)
MustRegister(alphaDeprecatedCounter)
assert.Panics(t,
func() {
SetRegistryFactoryVersion(apimachineryversion.Info{
Major: "1",
Minor: "15",
GitVersion: "v1.15.0-alpha-1.12345",
})
},
"Did not panic even though we expected it.")
}

View File

@ -0,0 +1,197 @@
/*
Copyright 2019 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 metrics
import (
"github.com/blang/semver"
"github.com/prometheus/client_golang/prometheus"
dto "github.com/prometheus/client_model/go"
"k8s.io/klog"
"sync"
)
/*
KubeCollector extends the prometheus.Collector interface to allow customization of the metric
registration process. Defer metric initialization until Create() is called, which then
delegates to the underlying metric's initializeMetric or initializeDeprecatedMetric
method call depending on whether the metric is deprecated or not.
*/
type KubeCollector interface {
Collector
lazyKubeMetric
DeprecatedVersion() *semver.Version
// Each collector metric should provide an initialization function
// for both deprecated and non-deprecated variants of a metric. This
// is necessary since metric instantiation will be deferred
// until the metric is actually registered somewhere.
initializeMetric()
initializeDeprecatedMetric()
}
/*
lazyKubeMetric defines our metric registration interface. lazyKubeMetric objects are expected
to lazily instantiate metrics (i.e defer metric instantiation until when
the Create() function is explicitly called).
*/
type lazyKubeMetric interface {
Create(*semver.Version) bool
IsCreated() bool
IsHidden() bool
IsDeprecated() bool
}
/*
lazyMetric implements lazyKubeMetric. A lazy metric is lazy because it waits until metric
registration time before instantiation. Add it as an anonymous field to a struct that
implements KubeCollector to get deferred registration behavior. You must call lazyInit
with the KubeCollector itself as an argument.
*/
type lazyMetric struct {
isDeprecated bool
isHidden bool
isCreated bool
markDeprecationOnce sync.Once
createOnce sync.Once
self KubeCollector
}
func (r *lazyMetric) IsCreated() bool {
return r.isCreated
}
// lazyInit provides the lazyMetric with a reference to the KubeCollector it is supposed
// to allow lazy initialization for. It should be invoked in the factory function which creates new
// KubeCollector type objects.
func (r *lazyMetric) lazyInit(self KubeCollector) {
r.self = self
}
// determineDeprecationStatus figures out whether the lazy metric should be deprecated or not.
// This method takes a Version argument which should be the version of the binary in which
// this code is currently being executed.
func (r *lazyMetric) determineDeprecationStatus(version semver.Version) {
selfVersion := r.self.DeprecatedVersion()
if selfVersion == nil {
return
}
r.markDeprecationOnce.Do(func() {
if selfVersion.LTE(version) {
r.isDeprecated = true
}
if selfVersion.LT(version) {
klog.Warningf("This metric has been deprecated for more than one release, hiding.")
r.isHidden = true
}
})
}
func (r *lazyMetric) IsHidden() bool {
return r.isHidden
}
func (r *lazyMetric) IsDeprecated() bool {
return r.isDeprecated
}
// Create forces the initialization of metric which has been deferred until
// the point at which this method is invoked. This method will determine whether
// the metric is deprecated or hidden, no-opting if the metric should be considered
// hidden. Furthermore, this function no-opts and returns true if metric is already
// created.
func (r *lazyMetric) Create(version *semver.Version) bool {
if version != nil {
r.determineDeprecationStatus(*version)
}
// let's not create if this metric is slated to be hidden
if r.IsHidden() {
return false
}
r.createOnce.Do(func() {
r.isCreated = true
if r.IsDeprecated() {
r.self.initializeDeprecatedMetric()
} else {
r.self.initializeMetric()
}
})
return r.IsCreated()
}
/*
This code is directly lifted from the prometheus codebase. It's a convenience struct which
allows you satisfy the Collector interface automatically if you already satisfy the Metric interface.
For reference: https://github.com/prometheus/client_golang/blob/v0.9.2/prometheus/collector.go#L98-L120
*/
type selfCollector struct {
metric prometheus.Metric
}
func (c *selfCollector) initSelfCollection(m prometheus.Metric) {
c.metric = m
}
func (c *selfCollector) Describe(ch chan<- *prometheus.Desc) {
ch <- c.metric.Desc()
}
func (c *selfCollector) Collect(ch chan<- prometheus.Metric) {
ch <- c.metric
}
// no-op vecs for convenience
var noopCounterVec = &prometheus.CounterVec{}
var noopHistogramVec = &prometheus.HistogramVec{}
var noopSummaryVec = &prometheus.SummaryVec{}
var noopGaugeVec = &prometheus.GaugeVec{}
var noopObserverVec = &noopObserverVector{}
// just use a convenience struct for all the no-ops
var noop = &noopMetric{}
type noopMetric struct{}
func (noopMetric) Inc() {}
func (noopMetric) Add(float64) {}
func (noopMetric) Dec() {}
func (noopMetric) Set(float64) {}
func (noopMetric) Sub(float64) {}
func (noopMetric) Observe(float64) {}
func (noopMetric) SetToCurrentTime() {}
func (noopMetric) Desc() *prometheus.Desc { return nil }
func (noopMetric) Write(*dto.Metric) error { return nil }
func (noopMetric) Describe(chan<- *prometheus.Desc) {}
func (noopMetric) Collect(chan<- prometheus.Metric) {}
type noopObserverVector struct{}
func (noopObserverVector) GetMetricWith(prometheus.Labels) (prometheus.Observer, error) {
return noop, nil
}
func (noopObserverVector) GetMetricWithLabelValues(...string) (prometheus.Observer, error) {
return noop, nil
}
func (noopObserverVector) With(prometheus.Labels) prometheus.Observer { return noop }
func (noopObserverVector) WithLabelValues(...string) prometheus.Observer { return noop }
func (noopObserverVector) CurryWith(prometheus.Labels) (prometheus.ObserverVec, error) {
return noopObserverVec, nil
}
func (noopObserverVector) MustCurryWith(prometheus.Labels) prometheus.ObserverVec {
return noopObserverVec
}
func (noopObserverVector) Describe(chan<- *prometheus.Desc) {}
func (noopObserverVector) Collect(chan<- prometheus.Metric) {}

View File

@ -0,0 +1,84 @@
/*
Copyright 2019 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 metrics
import (
"fmt"
"github.com/blang/semver"
"github.com/prometheus/client_golang/prometheus"
"sync"
)
// KubeOpts is superset struct for prometheus.Opts. The prometheus Opts structure
// is purposefully not embedded here because that would change struct initialization
// in the manner which people are currently accustomed.
//
// Name must be set to a non-empty string. DeprecatedVersion is defined only
// if the metric for which this options applies is, in fact, deprecated.
type KubeOpts struct {
Namespace string
Subsystem string
Name string
Help string
ConstLabels prometheus.Labels
DeprecatedVersion *semver.Version
deprecateOnce sync.Once
annotateOnce sync.Once
StabilityLevel StabilityLevel
}
// StabilityLevel represents the API guarantees for a given defined metric.
type StabilityLevel string
const (
// ALPHA metrics have no stability guarantees, as such, labels may
// be arbitrarily added/removed and the metric may be deleted at any time.
ALPHA StabilityLevel = "ALPHA"
// STABLE metrics are guaranteed not be mutated and removal is governed by
// the deprecation policy outlined in by the control plane metrics stability KEP.
STABLE StabilityLevel = "STABLE"
)
// CounterOpts is an alias for Opts. See there for doc comments.
type CounterOpts KubeOpts
// Modify help description on the metric description.
func (o *CounterOpts) markDeprecated() {
o.deprecateOnce.Do(func() {
o.Help = fmt.Sprintf("(Deprecated since %v) %v", o.DeprecatedVersion, o.Help)
})
}
// annotateStabilityLevel annotates help description on the metric description with the stability level
// of the metric
func (o *CounterOpts) annotateStabilityLevel() {
o.annotateOnce.Do(func() {
o.Help = fmt.Sprintf("[%v] %v", o.StabilityLevel, o.Help)
})
}
// convenience function to allow easy transformation to the prometheus
// counterpart. This will do more once we have a proper label abstraction
func (o *CounterOpts) toPromCounterOpts() prometheus.CounterOpts {
return prometheus.CounterOpts{
Namespace: o.Namespace,
Subsystem: o.Subsystem,
Name: o.Name,
Help: o.Help,
ConstLabels: o.ConstLabels,
}
}

View File

@ -0,0 +1,96 @@
/*
Copyright 2019 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 metrics
import (
"github.com/blang/semver"
"github.com/prometheus/client_golang/prometheus"
dto "github.com/prometheus/client_model/go"
apimachineryversion "k8s.io/apimachinery/pkg/version"
)
// KubeRegistry is an interface which implements a subset of prometheus.Registerer and
// prometheus.Gatherer interfaces
type KubeRegistry interface {
Register(KubeCollector) error
MustRegister(...KubeCollector)
Unregister(KubeCollector) bool
Gather() ([]*dto.MetricFamily, error)
}
// kubeRegistry is a wrapper around a prometheus registry-type object. Upon initialization
// the kubernetes binary version information is loaded into the registry object, so that
// automatic behavior can be configured for metric versioning.
type kubeRegistry struct {
PromRegistry
version semver.Version
}
// Register registers a new Collector to be included in metrics
// collection. It returns an error if the descriptors provided by the
// Collector are invalid or if they — in combination with descriptors of
// already registered Collectors — do not fulfill the consistency and
// uniqueness criteria described in the documentation of metric.Desc.
func (kr *kubeRegistry) Register(c KubeCollector) error {
if c.Create(&kr.version) {
return kr.PromRegistry.Register(c)
}
return nil
}
// MustRegister works like Register but registers any number of
// Collectors and panics upon the first registration that causes an
// error.
func (kr *kubeRegistry) MustRegister(cs ...KubeCollector) {
metrics := make([]prometheus.Collector, 0, len(cs))
for _, c := range cs {
if c.Create(&kr.version) {
metrics = append(metrics, c)
}
}
kr.PromRegistry.MustRegister(metrics...)
}
// Unregister unregisters the Collector that equals the Collector passed
// in as an argument. (Two Collectors are considered equal if their
// Describe method yields the same set of descriptors.) The function
// returns whether a Collector was unregistered. Note that an unchecked
// Collector cannot be unregistered (as its Describe method does not
// yield any descriptor).
func (kr *kubeRegistry) Unregister(collector KubeCollector) bool {
return kr.PromRegistry.Unregister(collector)
}
// Gather calls the Collect method of the registered Collectors and then
// gathers the collected metrics into a lexicographically sorted slice
// of uniquely named MetricFamily protobufs. Gather ensures that the
// returned slice is valid and self-consistent so that it can be used
// for valid exposition. As an exception to the strict consistency
// requirements described for metric.Desc, Gather will tolerate
// different sets of label names for metrics of the same metric family.
func (kr *kubeRegistry) Gather() ([]*dto.MetricFamily, error) {
return kr.PromRegistry.Gather()
}
// NewKubeRegistry creates a new vanilla Registry without any Collectors
// pre-registered.
func NewKubeRegistry(v apimachineryversion.Info) KubeRegistry {
return &kubeRegistry{
PromRegistry: prometheus.NewRegistry(),
version: parseVersion(v),
}
}

View File

@ -0,0 +1,198 @@
/*
Copyright 2019 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 metrics
import (
"github.com/blang/semver"
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/assert"
apimachineryversion "k8s.io/apimachinery/pkg/version"
"testing"
)
var (
v115 = semver.MustParse("1.15.0")
v114 = semver.MustParse("1.14.0")
alphaCounter = NewCounter(
&CounterOpts{
Namespace: "some_namespace",
Name: "test_counter_name",
Subsystem: "subsystem",
StabilityLevel: ALPHA,
Help: "counter help",
},
)
alphaDeprecatedCounter = NewCounter(
&CounterOpts{
Namespace: "some_namespace",
Name: "test_alpha_dep_counter",
Subsystem: "subsystem",
StabilityLevel: ALPHA,
Help: "counter help",
DeprecatedVersion: &v115,
},
)
alphaHiddenCounter = NewCounter(
&CounterOpts{
Namespace: "some_namespace",
Name: "test_alpha_hidden_counter",
Subsystem: "subsystem",
StabilityLevel: ALPHA,
Help: "counter help",
DeprecatedVersion: &v114,
},
)
)
func TestRegister(t *testing.T) {
var tests = []struct {
desc string
metrics []*Counter
registryVersion *semver.Version
expectedErrors []error
expectedIsCreatedValues []bool
expectedIsDeprecated []bool
expectedIsHidden []bool
}{
{
desc: "test alpha metric",
metrics: []*Counter{alphaCounter},
registryVersion: &v115,
expectedErrors: []error{nil},
expectedIsCreatedValues: []bool{true},
expectedIsDeprecated: []bool{false},
expectedIsHidden: []bool{false},
},
{
desc: "test registering same metric multiple times",
metrics: []*Counter{alphaCounter, alphaCounter},
registryVersion: &v115,
expectedErrors: []error{nil, prometheus.AlreadyRegisteredError{}},
expectedIsCreatedValues: []bool{true, true},
expectedIsDeprecated: []bool{false, false},
expectedIsHidden: []bool{false, false},
},
{
desc: "test alpha deprecated metric",
metrics: []*Counter{alphaDeprecatedCounter},
registryVersion: &v115,
expectedErrors: []error{nil},
expectedIsCreatedValues: []bool{true},
expectedIsDeprecated: []bool{true},
expectedIsHidden: []bool{false},
},
{
desc: "test alpha hidden metric",
metrics: []*Counter{alphaHiddenCounter},
registryVersion: &v115,
expectedErrors: []error{nil},
expectedIsCreatedValues: []bool{false},
expectedIsDeprecated: []bool{true},
expectedIsHidden: []bool{true},
},
}
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
registry := NewKubeRegistry(apimachineryversion.Info{
Major: "1",
Minor: "15",
GitVersion: "v1.15.0-alpha-1.12345",
})
for i, m := range test.metrics {
err := registry.Register(m)
if err != test.expectedErrors[i] && err.Error() != test.expectedErrors[i].Error() {
t.Errorf("Got unexpected error %v, wanted %v", err, test.expectedErrors[i])
}
if m.IsCreated() != test.expectedIsCreatedValues[i] {
t.Errorf("Got isCreated == %v, wanted isCreated to be %v", m.IsCreated(), test.expectedIsCreatedValues[i])
}
if m.IsDeprecated() != test.expectedIsDeprecated[i] {
t.Errorf("Got IsDeprecated == %v, wanted IsDeprecated to be %v", m.IsDeprecated(), test.expectedIsDeprecated[i])
}
if m.IsHidden() != test.expectedIsHidden[i] {
t.Errorf("Got IsHidden == %v, wanted IsHidden to be %v", m.IsHidden(), test.expectedIsDeprecated[i])
}
}
})
}
}
func TestMustRegister(t *testing.T) {
var tests = []struct {
desc string
metrics []*Counter
registryVersion *semver.Version
expectedPanics []bool
}{
{
desc: "test alpha metric",
metrics: []*Counter{alphaCounter},
registryVersion: &v115,
expectedPanics: []bool{false},
},
{
desc: "test registering same metric multiple times",
metrics: []*Counter{alphaCounter, alphaCounter},
registryVersion: &v115,
expectedPanics: []bool{false, true},
},
{
desc: "test alpha deprecated metric",
metrics: []*Counter{alphaDeprecatedCounter},
registryVersion: &v115,
expectedPanics: []bool{false},
},
{
desc: "test must registering same deprecated metric",
metrics: []*Counter{alphaDeprecatedCounter, alphaDeprecatedCounter},
registryVersion: &v115,
expectedPanics: []bool{false, true},
},
{
desc: "test alpha hidden metric",
metrics: []*Counter{alphaHiddenCounter},
registryVersion: &v115,
expectedPanics: []bool{false},
},
{
desc: "test must registering same hidden metric",
metrics: []*Counter{alphaHiddenCounter, alphaHiddenCounter},
registryVersion: &v115,
expectedPanics: []bool{false, false}, // hidden metrics no-opt
},
}
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
registry := NewKubeRegistry(apimachineryversion.Info{
Major: "1",
Minor: "15",
GitVersion: "v1.15.0-alpha-1.12345",
})
for i, m := range test.metrics {
if test.expectedPanics[i] {
assert.Panics(t,
func() { registry.MustRegister(m) },
"Did not panic even though we expected it.")
} else {
registry.MustRegister(m)
}
}
})
}
}

View File

@ -0,0 +1,41 @@
/*
Copyright 2019 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 metrics
import (
"fmt"
"github.com/blang/semver"
apimachineryversion "k8s.io/apimachinery/pkg/version"
"regexp"
)
const (
versionRegexpString = `^v(\d+\.\d+\.\d+)`
)
var (
versionRe = regexp.MustCompile(versionRegexpString)
)
func parseVersion(ver apimachineryversion.Info) semver.Version {
matches := versionRe.FindAllStringSubmatch(ver.String(), -1)
if len(matches) != 1 {
panic(fmt.Sprintf("version string \"%v\" doesn't match expected regular expression: \"%v\"", ver.String(), versionRe.String()))
}
return semver.MustParse(matches[0][1])
}

View File

@ -0,0 +1,53 @@
/*
Copyright 2019 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 metrics
import (
apimachineryversion "k8s.io/apimachinery/pkg/version"
"testing"
)
func TestVersionParsing(t *testing.T) {
var tests = []struct {
desc string
versionString string
expectedVersion string
}{
{
"v1.15.0-alpha-1.12345",
"v1.15.0-alpha-1.12345",
"1.15.0",
},
{
"Parse out defaulted string",
"v0.0.0-master",
"0.0.0",
},
}
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
version := apimachineryversion.Info{
GitVersion: test.versionString,
}
parsedV := parseVersion(version)
if test.expectedVersion != parsedV.String() {
t.Errorf("Got %v, wanted %v", parsedV.String(), test.expectedVersion)
}
})
}
}

View File

@ -0,0 +1,66 @@
/*
Copyright 2019 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 metrics
import (
"github.com/prometheus/client_golang/prometheus"
dto "github.com/prometheus/client_model/go"
)
// This file contains a series of interfaces which we explicitly define for
// integrating with prometheus. We redefine the interfaces explicitly here
// so that we can prevent breakage if methods are ever added to prometheus
// variants of them.
// Collector defines a subset of prometheus.Collector interface methods
type Collector interface {
Describe(chan<- *prometheus.Desc)
Collect(chan<- prometheus.Metric)
}
// Metric defines a subset of prometheus.Metric interface methods
type Metric interface {
Desc() *prometheus.Desc
Write(*dto.Metric) error
}
// CounterMetric is a Metric that represents a single numerical value that only ever
// goes up. That implies that it cannot be used to count items whose number can
// also go down, e.g. the number of currently running goroutines. Those
// "counters" are represented by Gauges.
// CounterMetric is an interface which defines a subset of the interface provided by prometheus.Counter
type CounterMetric interface {
Inc()
Add(float64)
}
// CounterVecMetric is an interface which prometheus.CounterVec satisfies.
type CounterVecMetric interface {
WithLabelValues(...string) CounterMetric
With(prometheus.Labels) CounterMetric
}
// PromRegistry is an interface which implements a subset of prometheus.Registerer and
// prometheus.Gatherer interfaces
type PromRegistry interface {
Register(prometheus.Collector) error
MustRegister(...prometheus.Collector)
Unregister(prometheus.Collector) bool
Gather() ([]*dto.MetricFamily, error)
}

View File

@ -12,6 +12,7 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/beorn7/perks v0.0.0-20160229213445-3ac7bf7a47d1 h1:OnJHjoVbY69GG4gclp0ngXfywigLhR6rrgUxmxQRWO4=
github.com/beorn7/perks v0.0.0-20160229213445-3ac7bf7a47d1/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/coreos/bbolt v1.3.1-coreos.6 h1:uTXKg9gY70s9jMAKdfljFQcuh4e/BXOM+V+d00KFj3A=
github.com/coreos/bbolt v1.3.1-coreos.6/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ=

View File

@ -10,6 +10,7 @@ require (
)
replace (
github.com/beorn7/perks => github.com/beorn7/perks v0.0.0-20160229213445-3ac7bf7a47d1
golang.org/x/sync => golang.org/x/sync v0.0.0-20181108010431-42b317875d0f
golang.org/x/sys => golang.org/x/sys v0.0.0-20190209173611-3b5209105503
golang.org/x/tools => golang.org/x/tools v0.0.0-20190313210603-aa82965741a9

View File

@ -1,3 +1,5 @@
github.com/beorn7/perks v0.0.0-20160229213445-3ac7bf7a47d1/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
@ -16,6 +18,7 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be h1:AHimNtVIpiBjPUhEF5KNCkrUyqTSA5zWUl8sQ2bfGBE=
github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
@ -25,6 +28,10 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=

View File

@ -10,6 +10,7 @@ require (
)
replace (
github.com/beorn7/perks => github.com/beorn7/perks v0.0.0-20160229213445-3ac7bf7a47d1
golang.org/x/sync => golang.org/x/sync v0.0.0-20181108010431-42b317875d0f
golang.org/x/sys => golang.org/x/sys v0.0.0-20190209173611-3b5209105503
golang.org/x/tools => golang.org/x/tools v0.0.0-20190313210603-aa82965741a9

View File

@ -1,3 +1,5 @@
github.com/beorn7/perks v0.0.0-20160229213445-3ac7bf7a47d1/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
@ -16,6 +18,7 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be h1:AHimNtVIpiBjPUhEF5KNCkrUyqTSA5zWUl8sQ2bfGBE=
github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
@ -25,6 +28,10 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=

View File

@ -10,6 +10,7 @@ require (
)
replace (
github.com/beorn7/perks => github.com/beorn7/perks v0.0.0-20160229213445-3ac7bf7a47d1
golang.org/x/sync => golang.org/x/sync v0.0.0-20181108010431-42b317875d0f
golang.org/x/sys => golang.org/x/sys v0.0.0-20190209173611-3b5209105503
golang.org/x/tools => golang.org/x/tools v0.0.0-20190313210603-aa82965741a9

View File

@ -1,3 +1,5 @@
github.com/beorn7/perks v0.0.0-20160229213445-3ac7bf7a47d1/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
@ -16,6 +18,7 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be h1:AHimNtVIpiBjPUhEF5KNCkrUyqTSA5zWUl8sQ2bfGBE=
github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
@ -25,6 +28,10 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=

View File

@ -12,6 +12,7 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/beorn7/perks v0.0.0-20160229213445-3ac7bf7a47d1 h1:OnJHjoVbY69GG4gclp0ngXfywigLhR6rrgUxmxQRWO4=
github.com/beorn7/perks v0.0.0-20160229213445-3ac7bf7a47d1/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/coreos/bbolt v1.3.1-coreos.6 h1:uTXKg9gY70s9jMAKdfljFQcuh4e/BXOM+V+d00KFj3A=
github.com/coreos/bbolt v1.3.1-coreos.6/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ=