add version parsing to metrics framework, use build version information for registry version

This commit is contained in:
Han Kang 2019-04-26 16:43:15 -07:00
parent cebad0da66
commit abe64acc8d
9 changed files with 145 additions and 31 deletions

View File

@ -14,10 +14,13 @@ go_library(
"opts.go", "opts.go",
"registry.go", "registry.go",
"util.go", "util.go",
"version_parser.go",
"wrappers.go", "wrappers.go",
], ],
importpath = "k8s.io/kubernetes/pkg/util/metrics", importpath = "k8s.io/kubernetes/pkg/util/metrics",
deps = [ deps = [
"//pkg/version:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/version:go_default_library",
"//staging/src/k8s.io/client-go/util/flowcontrol:go_default_library", "//staging/src/k8s.io/client-go/util/flowcontrol:go_default_library",
"//vendor/github.com/blang/semver: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_golang/prometheus:go_default_library",
@ -32,9 +35,11 @@ go_test(
"counter_test.go", "counter_test.go",
"registry_test.go", "registry_test.go",
"util_test.go", "util_test.go",
"version_parser_test.go",
], ],
embed = [":go_default_library"], embed = [":go_default_library"],
deps = [ deps = [
"//staging/src/k8s.io/apimachinery/pkg/version:go_default_library",
"//staging/src/k8s.io/client-go/util/flowcontrol:go_default_library", "//staging/src/k8s.io/client-go/util/flowcontrol:go_default_library",
"//vendor/github.com/blang/semver: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_golang/prometheus:go_default_library",

View File

@ -33,13 +33,13 @@ type kubeCounter struct {
// NewCounter returns an object which satisfies the KubeCollector and KubeCounter interfaces. // NewCounter returns an object which satisfies the KubeCollector and KubeCounter interfaces.
// However, the object returned will not measure anything unless the collector is first // However, the object returned will not measure anything unless the collector is first
// registered, since the metric is lazily instantiated. // registered, since the metric is lazily instantiated.
func NewCounter(opts CounterOpts) *kubeCounter { func NewCounter(opts *CounterOpts) *kubeCounter {
// todo: handle defaulting better // todo: handle defaulting better
if opts.StabilityLevel == "" { if opts.StabilityLevel == "" {
opts.StabilityLevel = ALPHA opts.StabilityLevel = ALPHA
} }
kc := &kubeCounter{ kc := &kubeCounter{
CounterOpts: &opts, CounterOpts: opts,
lazyMetric: lazyMetric{}, lazyMetric: lazyMetric{},
} }
kc.setPrometheusCounter(noop) kc.setPrometheusCounter(noop)
@ -85,10 +85,10 @@ type kubeCounterVec struct {
// NewCounterVec returns an object which satisfies the KubeCollector and KubeCounterVec interfaces. // NewCounterVec returns an object which satisfies the KubeCollector and KubeCounterVec interfaces.
// However, the object returned will not measure anything unless the collector is first // However, the object returned will not measure anything unless the collector is first
// registered, since the metric is lazily instantiated. // registered, since the metric is lazily instantiated.
func NewCounterVec(opts CounterOpts, labels []string) *kubeCounterVec { func NewCounterVec(opts *CounterOpts, labels []string) *kubeCounterVec {
cv := &kubeCounterVec{ cv := &kubeCounterVec{
CounterVec: noopCounterVec, CounterVec: noopCounterVec,
CounterOpts: &opts, CounterOpts: opts,
originalLabels: labels, originalLabels: labels,
lazyMetric: lazyMetric{}, lazyMetric: lazyMetric{},
} }

View File

@ -28,14 +28,14 @@ func TestCounter(t *testing.T) {
v114 := semver.MustParse("1.14.0") v114 := semver.MustParse("1.14.0")
var tests = []struct { var tests = []struct {
desc string desc string
CounterOpts *CounterOpts
registryVersion *semver.Version registryVersion *semver.Version
expectedMetricCount int expectedMetricCount int
expectedHelp string expectedHelp string
}{ }{
{ {
desc: "Test non deprecated", desc: "Test non deprecated",
CounterOpts: CounterOpts{ CounterOpts: &CounterOpts{
Namespace: "namespace", Namespace: "namespace",
Name: "metric_test_name", Name: "metric_test_name",
Subsystem: "subsystem", Subsystem: "subsystem",
@ -48,7 +48,7 @@ func TestCounter(t *testing.T) {
}, },
{ {
desc: "Test deprecated", desc: "Test deprecated",
CounterOpts: CounterOpts{ CounterOpts: &CounterOpts{
Namespace: "namespace", Namespace: "namespace",
Name: "metric_test_name", Name: "metric_test_name",
Subsystem: "subsystem", Subsystem: "subsystem",
@ -62,7 +62,7 @@ func TestCounter(t *testing.T) {
}, },
{ {
desc: "Test hidden", desc: "Test hidden",
CounterOpts: CounterOpts{ CounterOpts: &CounterOpts{
Namespace: "namespace", Namespace: "namespace",
Name: "metric_test_name", Name: "metric_test_name",
Subsystem: "subsystem", Subsystem: "subsystem",
@ -77,7 +77,7 @@ func TestCounter(t *testing.T) {
for _, test := range tests { for _, test := range tests {
t.Run(test.desc, func(t *testing.T) { t.Run(test.desc, func(t *testing.T) {
registry := NewKubeRegistry(*test.registryVersion) registry := newKubeRegistry(*test.registryVersion)
c := NewCounter(test.CounterOpts) c := NewCounter(test.CounterOpts)
registry.MustRegister(c) registry.MustRegister(c)
@ -126,7 +126,7 @@ func TestCounterVec(t *testing.T) {
v114 := semver.MustParse("1.14.0") v114 := semver.MustParse("1.14.0")
var tests = []struct { var tests = []struct {
desc string desc string
CounterOpts *CounterOpts
labels []string labels []string
registryVersion *semver.Version registryVersion *semver.Version
expectedMetricFamilyCount int expectedMetricFamilyCount int
@ -134,7 +134,7 @@ func TestCounterVec(t *testing.T) {
}{ }{
{ {
desc: "Test non deprecated", desc: "Test non deprecated",
CounterOpts: CounterOpts{ CounterOpts: &CounterOpts{
Namespace: "namespace", Namespace: "namespace",
Name: "metric_test_name", Name: "metric_test_name",
Subsystem: "subsystem", Subsystem: "subsystem",
@ -147,7 +147,7 @@ func TestCounterVec(t *testing.T) {
}, },
{ {
desc: "Test deprecated", desc: "Test deprecated",
CounterOpts: CounterOpts{ CounterOpts: &CounterOpts{
Namespace: "namespace", Namespace: "namespace",
Name: "metric_test_name", Name: "metric_test_name",
Subsystem: "subsystem", Subsystem: "subsystem",
@ -161,7 +161,7 @@ func TestCounterVec(t *testing.T) {
}, },
{ {
desc: "Test hidden", desc: "Test hidden",
CounterOpts: CounterOpts{ CounterOpts: &CounterOpts{
Namespace: "namespace", Namespace: "namespace",
Name: "metric_test_name", Name: "metric_test_name",
Subsystem: "subsystem", Subsystem: "subsystem",
@ -177,7 +177,7 @@ func TestCounterVec(t *testing.T) {
for _, test := range tests { for _, test := range tests {
t.Run(test.desc, func(t *testing.T) { t.Run(test.desc, func(t *testing.T) {
registry := NewKubeRegistry(*test.registryVersion) registry := newKubeRegistry(*test.registryVersion)
c := NewCounterVec(test.CounterOpts, test.labels) c := NewCounterVec(test.CounterOpts, test.labels)
registry.MustRegister(c) registry.MustRegister(c)
c.WithLabelValues("1", "2").Inc() c.WithLabelValues("1", "2").Inc()

View File

@ -67,7 +67,7 @@ func (o *CounterOpts) annotateStabilityLevel() {
// convenience function to allow easy transformation to the prometheus // convenience function to allow easy transformation to the prometheus
// counterpart. This will do more once we have a proper label abstraction // counterpart. This will do more once we have a proper label abstraction
func (o CounterOpts) toPromCounterOpts() prometheus.CounterOpts { func (o *CounterOpts) toPromCounterOpts() prometheus.CounterOpts {
return prometheus.CounterOpts{ return prometheus.CounterOpts{
Namespace: o.Namespace, Namespace: o.Namespace,
Subsystem: o.Subsystem, Subsystem: o.Subsystem,

View File

@ -20,12 +20,11 @@ import (
"github.com/blang/semver" "github.com/blang/semver"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
dto "github.com/prometheus/client_model/go" dto "github.com/prometheus/client_model/go"
"k8s.io/klog"
"k8s.io/kubernetes/pkg/version"
) )
var ( var DefaultGlobalRegistry = NewKubeRegistry()
// todo: load the version dynamically at application boot.
DefaultGlobalRegistry = NewKubeRegistry(semver.MustParse("1.15.0"))
)
type KubeRegistry struct { type KubeRegistry struct {
PromRegistry PromRegistry
@ -69,9 +68,23 @@ func (kr *KubeRegistry) Gather() ([]*dto.MetricFamily, error) {
return kr.PromRegistry.Gather() return kr.PromRegistry.Gather()
} }
// NewRegistry creates a new vanilla Registry without any Collectors func NewKubeRegistry() *KubeRegistry {
v, err := parseVersion(version.Get())
if err != nil {
klog.Fatalf("Can't initialize a registry without a valid version %v", err)
}
if v == nil {
klog.Fatalf("No valid version %v", *v)
}
return &KubeRegistry{
PromRegistry: prometheus.NewRegistry(),
version: semver.MustParse(*v),
}
}
// newKubeRegistry creates a new vanilla Registry without any Collectors
// pre-registered. // pre-registered.
func NewKubeRegistry(version semver.Version) *KubeRegistry { func newKubeRegistry(version semver.Version) *KubeRegistry {
return &KubeRegistry{ return &KubeRegistry{
PromRegistry: prometheus.NewRegistry(), PromRegistry: prometheus.NewRegistry(),
version: version, version: version,

View File

@ -27,7 +27,7 @@ var (
v115 = semver.MustParse("1.15.0") v115 = semver.MustParse("1.15.0")
v114 = semver.MustParse("1.14.0") v114 = semver.MustParse("1.14.0")
alphaCounter = NewCounter( alphaCounter = NewCounter(
CounterOpts{ &CounterOpts{
Namespace: "some_namespace", Namespace: "some_namespace",
Name: "test_counter_name", Name: "test_counter_name",
Subsystem: "subsystem", Subsystem: "subsystem",
@ -36,7 +36,7 @@ var (
}, },
) )
alphaDeprecatedCounter = NewCounter( alphaDeprecatedCounter = NewCounter(
CounterOpts{ &CounterOpts{
Namespace: "some_namespace", Namespace: "some_namespace",
Name: "test_alpha_dep_counter", Name: "test_alpha_dep_counter",
Subsystem: "subsystem", Subsystem: "subsystem",
@ -46,7 +46,7 @@ var (
}, },
) )
alphaHiddenCounter = NewCounter( alphaHiddenCounter = NewCounter(
CounterOpts{ &CounterOpts{
Namespace: "some_namespace", Namespace: "some_namespace",
Name: "test_alpha_hidden_counter", Name: "test_alpha_hidden_counter",
Subsystem: "subsystem", Subsystem: "subsystem",
@ -56,7 +56,7 @@ var (
}, },
) )
stableCounter = NewCounter( stableCounter = NewCounter(
CounterOpts{ &CounterOpts{
Namespace: "some_namespace", Namespace: "some_namespace",
Name: "test_some_other_counter", Name: "test_some_other_counter",
Subsystem: "subsystem", Subsystem: "subsystem",
@ -116,7 +116,7 @@ func TestRegister(t *testing.T) {
for _, test := range tests { for _, test := range tests {
t.Run(test.desc, func(t *testing.T) { t.Run(test.desc, func(t *testing.T) {
registry := NewKubeRegistry(*test.registryVersion) registry := newKubeRegistry(*test.registryVersion)
for i, m := range test.metrics { for i, m := range test.metrics {
err := registry.Register(m) err := registry.Register(m)
if err != test.expectedErrors[i] && err.Error() != test.expectedErrors[i].Error() { if err != test.expectedErrors[i] && err.Error() != test.expectedErrors[i].Error() {
@ -183,7 +183,7 @@ func TestMustRegister(t *testing.T) {
for _, test := range tests { for _, test := range tests {
t.Run(test.desc, func(t *testing.T) { t.Run(test.desc, func(t *testing.T) {
registry := NewKubeRegistry(*test.registryVersion) registry := newKubeRegistry(*test.registryVersion)
for i, m := range test.metrics { for i, m := range test.metrics {
if test.expectedPanics[i] { if test.expectedPanics[i] {
assert.Panics(t, assert.Panics(t,

View File

@ -0,0 +1,40 @@
/*
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"
apimachineryversion "k8s.io/apimachinery/pkg/version"
"regexp"
)
const (
versionRegexpString = `^v(\d+\.\d+\.\d+)`
)
var (
versionRe = regexp.MustCompile(versionRegexpString)
)
func parseVersion(ver apimachineryversion.Info) (*string, error) {
matches := versionRe.FindAllStringSubmatch(ver.String(), -1)
if len(matches) != 1 {
return nil, fmt.Errorf("version string \"%v\" doesn't match expected regular expression: \"%v\"", ver.String(), versionRe.String())
}
return &matches[0][1], nil
}

View File

@ -0,0 +1,56 @@
/*
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, err := parseVersion(version)
if err != nil {
t.Fatalf("Should be able to parse %v", version)
}
if test.expectedVersion != *parsedV {
t.Errorf("Got %v, wanted %v", *parsedV, test.expectedVersion)
}
})
}
}