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",
"registry.go",
"util.go",
"version_parser.go",
"wrappers.go",
],
importpath = "k8s.io/kubernetes/pkg/util/metrics",
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",
"//vendor/github.com/blang/semver:go_default_library",
"//vendor/github.com/prometheus/client_golang/prometheus:go_default_library",
@ -32,9 +35,11 @@ go_test(
"counter_test.go",
"registry_test.go",
"util_test.go",
"version_parser_test.go",
],
embed = [":go_default_library"],
deps = [
"//staging/src/k8s.io/apimachinery/pkg/version: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/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.
// However, the object returned will not measure anything unless the collector is first
// registered, since the metric is lazily instantiated.
func NewCounter(opts CounterOpts) *kubeCounter {
func NewCounter(opts *CounterOpts) *kubeCounter {
// todo: handle defaulting better
if opts.StabilityLevel == "" {
opts.StabilityLevel = ALPHA
}
kc := &kubeCounter{
CounterOpts: &opts,
CounterOpts: opts,
lazyMetric: lazyMetric{},
}
kc.setPrometheusCounter(noop)
@ -85,10 +85,10 @@ type kubeCounterVec struct {
// NewCounterVec returns an object which satisfies the KubeCollector and KubeCounterVec 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) *kubeCounterVec {
func NewCounterVec(opts *CounterOpts, labels []string) *kubeCounterVec {
cv := &kubeCounterVec{
CounterVec: noopCounterVec,
CounterOpts: &opts,
CounterOpts: opts,
originalLabels: labels,
lazyMetric: lazyMetric{},
}

View File

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

View File

@ -29,7 +29,7 @@ This extends the prometheus.Collector interface to allow customization of the me
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
LazyMetric
@ -46,7 +46,7 @@ type KubeCollector interface {
LazyMetric defines our registration functionality. LazyMetric objects are expected
to lazily instantiate metrics (i.e defer metric instantiation until when
the Create() function is explicitly called).
*/
*/
type LazyMetric interface {
Create(*semver.Version) bool
IsCreated() bool
@ -59,7 +59,7 @@ lazyMetric implements LazyMetric. A lazy metric is lazy because it waits until m
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
@ -136,7 +136,7 @@ This code is directly lifted from the prometheus codebase. It's a convenience st
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
}

View File

@ -67,7 +67,7 @@ func (o *CounterOpts) annotateStabilityLevel() {
// 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 {
func (o *CounterOpts) toPromCounterOpts() prometheus.CounterOpts {
return prometheus.CounterOpts{
Namespace: o.Namespace,
Subsystem: o.Subsystem,

View File

@ -20,12 +20,11 @@ import (
"github.com/blang/semver"
"github.com/prometheus/client_golang/prometheus"
dto "github.com/prometheus/client_model/go"
"k8s.io/klog"
"k8s.io/kubernetes/pkg/version"
)
var (
// todo: load the version dynamically at application boot.
DefaultGlobalRegistry = NewKubeRegistry(semver.MustParse("1.15.0"))
)
var DefaultGlobalRegistry = NewKubeRegistry()
type KubeRegistry struct {
PromRegistry
@ -69,9 +68,23 @@ func (kr *KubeRegistry) Gather() ([]*dto.MetricFamily, error) {
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.
func NewKubeRegistry(version semver.Version) *KubeRegistry {
func newKubeRegistry(version semver.Version) *KubeRegistry {
return &KubeRegistry{
PromRegistry: prometheus.NewRegistry(),
version: version,

View File

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