From cf181cdd9a442a15d90c9c82687a8c604f561091 Mon Sep 17 00:00:00 2001 From: Han Kang Date: Tue, 23 Apr 2019 15:59:29 -0700 Subject: [PATCH 01/10] Add initial wrappers for prometheus.Counter and prometheus.CounterVec. Also add wrapper around prometheus.Registry to customize control-flow --- pkg/util/metrics/BUILD | 22 ++- pkg/util/metrics/counter.go | 135 +++++++++++++++++++ pkg/util/metrics/counter_test.go | 217 ++++++++++++++++++++++++++++++ pkg/util/metrics/metric.go | 196 +++++++++++++++++++++++++++ pkg/util/metrics/opts.go | 78 +++++++++++ pkg/util/metrics/registry.go | 79 +++++++++++ pkg/util/metrics/registry_test.go | 198 +++++++++++++++++++++++++++ pkg/util/metrics/wrappers.go | 67 +++++++++ 8 files changed, 990 insertions(+), 2 deletions(-) create mode 100644 pkg/util/metrics/counter.go create mode 100644 pkg/util/metrics/counter_test.go create mode 100644 pkg/util/metrics/metric.go create mode 100644 pkg/util/metrics/opts.go create mode 100644 pkg/util/metrics/registry.go create mode 100644 pkg/util/metrics/registry_test.go create mode 100644 pkg/util/metrics/wrappers.go diff --git a/pkg/util/metrics/BUILD b/pkg/util/metrics/BUILD index a15d30ec247..2a93ba857d5 100644 --- a/pkg/util/metrics/BUILD +++ b/pkg/util/metrics/BUILD @@ -8,20 +8,38 @@ load( go_library( name = "go_default_library", - srcs = ["util.go"], + srcs = [ + "counter.go", + "metric.go", + "opts.go", + "registry.go", + "util.go", + "wrappers.go", + ], importpath = "k8s.io/kubernetes/pkg/util/metrics", deps = [ "//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", + "//vendor/github.com/prometheus/client_model/go:go_default_library", + "//vendor/k8s.io/klog:go_default_library", ], ) go_test( name = "go_default_test", - srcs = ["util_test.go"], + srcs = [ + "counter_test.go", + "registry_test.go", + "util_test.go", + ], embed = [":go_default_library"], deps = [ "//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", + "//vendor/github.com/prometheus/common/expfmt:go_default_library", + "//vendor/github.com/stretchr/testify/assert:go_default_library", ], ) diff --git a/pkg/util/metrics/counter.go b/pkg/util/metrics/counter.go new file mode 100644 index 00000000000..588fd061bf1 --- /dev/null +++ b/pkg/util/metrics/counter.go @@ -0,0 +1,135 @@ +/* +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" +) + +// kubeCounter is our internal representation for our wrapping struct around prometheus +// counters. kubeCounter implements both KubeCollector and KubeCounter. +type kubeCounter struct { + KubeCounter + *CounterOpts + lazyMetric + selfCollector +} + +// 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 { + // todo: handle defaulting better + if opts.StabilityLevel == "" { + opts.StabilityLevel = ALPHA + } + kc := &kubeCounter{ + CounterOpts: &opts, + lazyMetric: lazyMetric{}, + } + kc.setPrometheusCounter(noop) + kc.lazyInit(kc) + return kc +} + +// setPrometheusCounter sets the underlying KubeCounter object, i.e. the thing that does the measurement. +func (c *kubeCounter) setPrometheusCounter(counter prometheus.Counter) { + c.KubeCounter = counter + c.initSelfCollection(counter) +} + +// GetDeprecatedVersion returns a pointer to the Version or nil +func (c *kubeCounter) GetDeprecatedVersion() *semver.Version { + return c.CounterOpts.DeprecatedVersion +} + +// initializeMetric invocation creates the actual underlying Counter. Until this method is called +// our underlying counter is a no-op. +func (c *kubeCounter) 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 our underlying counter is a no-op. +func (c *kubeCounter) initializeDeprecatedMetric() { + c.CounterOpts.markDeprecated() + c.initializeMetric() +} + +// kubeCounterVec is our internal representation of our wrapping struct around prometheus +// counterVecs. kubeCounterVec implements both KubeCollector and KubeCounterVec. +type kubeCounterVec struct { + *prometheus.CounterVec + *CounterOpts + lazyMetric + originalLabels []string +} + +// 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 { + cv := &kubeCounterVec{ + CounterVec: noopCounterVec, + CounterOpts: &opts, + originalLabels: labels, + lazyMetric: lazyMetric{}, + } + cv.lazyInit(cv) + return cv +} + +// GetDeprecatedVersion returns a pointer to the Version or nil +func (v *kubeCounterVec) GetDeprecatedVersion() *semver.Version { + return v.CounterOpts.DeprecatedVersion +} + +// initializeMetric invocation creates the actual underlying CounterVec. Until this method is called +// our underlying counterVec is a no-op. +func (v *kubeCounterVec) initializeMetric() { + v.CounterVec = prometheus.NewCounterVec(v.CounterOpts.toPromCounterOpts(), v.originalLabels) +} + +// initializeMetric invocation creates the actual (but deprecated) CounterVec. Until this method is called +// our underlying counterVec is a no-op. +func (v *kubeCounterVec) 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 +// is undesirable for us, since we want a way to turn OFF metrics which end up turning into memory +// leaks. +// +// For reference: https://github.com/prometheus/client_golang/blob/master/prometheus/counter.go#L148-L177 +func (v *kubeCounterVec) WithLabelValues(lvs ...string) KubeCounter { + if !v.IsCreated() { + return noop // return no-op counter + } + return v.CounterVec.WithLabelValues(lvs...) +} + +func (v *kubeCounterVec) With(labels prometheus.Labels) KubeCounter { + if !v.IsCreated() { + return noop // return no-op counter + } + return v.CounterVec.With(labels) +} diff --git a/pkg/util/metrics/counter_test.go b/pkg/util/metrics/counter_test.go new file mode 100644 index 00000000000..82609e23db8 --- /dev/null +++ b/pkg/util/metrics/counter_test.go @@ -0,0 +1,217 @@ +/* +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" + "testing" +) + +func TestCounter(t *testing.T) { + v115 := semver.MustParse("1.15.0") + v114 := semver.MustParse("1.14.0") + var tests = []struct { + desc string + CounterOpts + registryVersion *semver.Version + expectedMetricCount int + expectedHelp string + }{ + { + desc: "Test non deprecated", + CounterOpts: CounterOpts{ + Namespace: "namespace", + Name: "metric_test_name", + Subsystem: "subsystem", + StabilityLevel: ALPHA, + Help: "counter help", + }, + registryVersion: &v115, + 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, + }, + registryVersion: &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, + }, + registryVersion: &v115, + expectedMetricCount: 0, + }, + } + + for _, test := range tests { + t.Run(test.desc, func(t *testing.T) { + registry := NewKubeRegistry(*test.registryVersion) + 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) + } + } + + // let's 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"}, + registryVersion: &v115, + 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"}, + registryVersion: &v115, + 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"}, + registryVersion: &v115, + expectedMetricFamilyCount: 0, + expectedHelp: "counter help", + }, + } + + for _, test := range tests { + t.Run(test.desc, func(t *testing.T) { + registry := NewKubeRegistry(*test.registryVersion) + 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) + } + // we no-opt here when we don't have any 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) + } + + // we no-opt here when we don't have any 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())) + } + } + }) + } +} diff --git a/pkg/util/metrics/metric.go b/pkg/util/metrics/metric.go new file mode 100644 index 00000000000..cc21e1a33af --- /dev/null +++ b/pkg/util/metrics/metric.go @@ -0,0 +1,196 @@ +/* +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" +) + +/** + * This extends the prometheus.Collector interface so that we can customize the metric + * registration process. Specifically, we defer metric initialization until ActuallyCreate + * 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 + GetDeprecatedVersion() *semver.Version + // Each collector metric should provide an initialization function + // for both deprecated and non-deprecated variants of a metric. This + // is necessary since we are now deferring metric instantiation + // until the metric is actually registered somewhere. + initializeMetric() + initializeDeprecatedMetric() +} + +// LazyMetric defines our registration functionality. We expect LazyMetric +// objects to lazily instantiate metrics (i.e defer metric instantiation until when +// ActuallyCreate is explicitly called). +type LazyMetric interface { + ActuallyCreate(*semver.Version) bool + IsCreated() bool + IsHidden() bool + IsDeprecated() bool +} + +/* + * lazyMetric implements LazyMetric. 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 our lazy metric should be deprecated or not. It 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.GetDeprecatedVersion() + 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 +} + +// Defer initialization of metric until we know if we actually need to +// register the thing. This wrapper just allows us to consolidate the +// syncOnce logic in a single spot and toggle the flag, since this +// behavior will be consistent across metrics. +// +// This no-opts and returns true if metric is already created. +func (r *lazyMetric) ActuallyCreate(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/65d3a96fbaa7c8c9535d7133d6d98cd50eed4db8/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) {} diff --git a/pkg/util/metrics/opts.go b/pkg/util/metrics/opts.go new file mode 100644 index 00000000000..985c323c6d4 --- /dev/null +++ b/pkg/util/metrics/opts.go @@ -0,0 +1,78 @@ +/* +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. We choose not to embed +// the prometheus Opts structure here because that would change struct initialization +// in the manner to 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 +} + +type StabilityLevel string + +const ( + ALPHA StabilityLevel = "ALPHA" + STABLE StabilityLevel = "STABLE" +) + +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, + } +} diff --git a/pkg/util/metrics/registry.go b/pkg/util/metrics/registry.go new file mode 100644 index 00000000000..97a7ee3e8fb --- /dev/null +++ b/pkg/util/metrics/registry.go @@ -0,0 +1,79 @@ +/* +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" +) + +var ( + // todo: load the version dynamically at application boot. + DefaultGlobalRegistry = NewKubeRegistry(semver.MustParse("1.15.0")) +) + +type KubeRegistry struct { + PromRegistry + version semver.Version +} + +// Register registers a collectable metric, but it uses a global registry. +func Register(c KubeCollector) error { + return DefaultGlobalRegistry.Register(c) +} + +// MustRegister works like Register but registers any number of +// Collectors and panics upon the first registration that causes an +// error. +func MustRegister(cs ...KubeCollector) { + DefaultGlobalRegistry.MustRegister(cs...) +} + +func (kr *KubeRegistry) Register(c KubeCollector) error { + if c.ActuallyCreate(&kr.version) { + return kr.PromRegistry.Register(c) + } + return nil +} + +func (kr *KubeRegistry) MustRegister(cs ...KubeCollector) { + metrics := make([]prometheus.Collector, 0, len(cs)) + for _, c := range cs { + if c.ActuallyCreate(&kr.version) { + metrics = append(metrics, c) + } + } + kr.PromRegistry.MustRegister(metrics...) +} + +func (kr *KubeRegistry) Unregister(collector KubeCollector) bool { + return kr.PromRegistry.Unregister(collector) +} + +func (kr *KubeRegistry) Gather() ([]*dto.MetricFamily, error) { + return kr.PromRegistry.Gather() +} + +// NewRegistry creates a new vanilla Registry without any Collectors +// pre-registered. +func NewKubeRegistry(version semver.Version) *KubeRegistry { + return &KubeRegistry{ + PromRegistry: prometheus.NewRegistry(), + version: version, + } +} diff --git a/pkg/util/metrics/registry_test.go b/pkg/util/metrics/registry_test.go new file mode 100644 index 00000000000..f9cabe87cd8 --- /dev/null +++ b/pkg/util/metrics/registry_test.go @@ -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" + "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, + }, + ) + stableCounter = NewCounter( + CounterOpts{ + Namespace: "some_namespace", + Name: "test_some_other_counter", + Subsystem: "subsystem", + StabilityLevel: STABLE, + Help: "counter help", + }, + ) +) + +func TestRegister(t *testing.T) { + var tests = []struct { + desc string + metrics []*kubeCounter + registryVersion *semver.Version + expectedErrors []error + expectedIsCreatedValues []bool + expectedIsDeprecated []bool + expectedIsHidden []bool + }{ + { + desc: "test alpha metric", + metrics: []*kubeCounter{alphaCounter}, + registryVersion: &v115, + expectedErrors: []error{nil}, + expectedIsCreatedValues: []bool{true}, + expectedIsDeprecated: []bool{false}, + expectedIsHidden: []bool{false}, + }, + { + desc: "test registering same metric multiple times", + metrics: []*kubeCounter{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: []*kubeCounter{alphaDeprecatedCounter}, + registryVersion: &v115, + expectedErrors: []error{nil, prometheus.AlreadyRegisteredError{}}, + expectedIsCreatedValues: []bool{true}, + expectedIsDeprecated: []bool{true}, + expectedIsHidden: []bool{false}, + }, + { + desc: "test alpha hidden metric", + metrics: []*kubeCounter{alphaHiddenCounter}, + registryVersion: &v115, + expectedErrors: []error{nil, prometheus.AlreadyRegisteredError{}}, + expectedIsCreatedValues: []bool{false}, + expectedIsDeprecated: []bool{true}, + expectedIsHidden: []bool{true}, + }, + } + + for _, test := range tests { + t.Run(test.desc, func(t *testing.T) { + 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() { + 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 []*kubeCounter + registryVersion *semver.Version + expectedPanics []bool + }{ + { + desc: "test alpha metric", + metrics: []*kubeCounter{alphaCounter}, + registryVersion: &v115, + expectedPanics: []bool{false}, + }, + { + desc: "test registering same metric multiple times", + metrics: []*kubeCounter{alphaCounter, alphaCounter}, + registryVersion: &v115, + expectedPanics: []bool{false, true}, + }, + { + desc: "test alpha deprecated metric", + metrics: []*kubeCounter{alphaDeprecatedCounter}, + registryVersion: &v115, + expectedPanics: []bool{false}, + }, + { + desc: "test must registering same deprecated metric", + metrics: []*kubeCounter{alphaDeprecatedCounter, alphaDeprecatedCounter}, + registryVersion: &v115, + expectedPanics: []bool{false, true}, + }, + { + desc: "test alpha hidden metric", + metrics: []*kubeCounter{alphaHiddenCounter}, + registryVersion: &v115, + expectedPanics: []bool{false}, + }, + { + desc: "test must registering same hidden metric", + metrics: []*kubeCounter{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(*test.registryVersion) + 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) + } + } + }) + } +} diff --git a/pkg/util/metrics/wrappers.go b/pkg/util/metrics/wrappers.go new file mode 100644 index 00000000000..29d44ceb2c0 --- /dev/null +++ b/pkg/util/metrics/wrappers.go @@ -0,0 +1,67 @@ +/* +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 +} + +// Counter 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. +// +// This interface defines a subset of the interface provided by prometheus.Counter +type KubeCounter interface { + Inc() + Add(float64) +} + +type KubeCounterVec interface { + WithLabelValues(...string) KubeCounter + With(prometheus.Labels) KubeCounter +} + +type PromRegistry interface { + Register(prometheus.Collector) error + MustRegister(...prometheus.Collector) + Unregister(prometheus.Collector) bool + Gather() ([]*dto.MetricFamily, error) +} From cebad0da66ea8251667a6990320da01e17cee1a7 Mon Sep 17 00:00:00 2001 From: Han Kang Date: Thu, 25 Apr 2019 14:49:03 -0700 Subject: [PATCH 02/10] make method names more succinct, improve documentation for posterity --- pkg/util/metrics/counter.go | 32 +++++++++------- pkg/util/metrics/counter_test.go | 6 +-- pkg/util/metrics/metric.go | 63 ++++++++++++++++---------------- pkg/util/metrics/opts.go | 6 +-- pkg/util/metrics/registry.go | 4 +- 5 files changed, 58 insertions(+), 53 deletions(-) diff --git a/pkg/util/metrics/counter.go b/pkg/util/metrics/counter.go index 588fd061bf1..29d87742a3f 100644 --- a/pkg/util/metrics/counter.go +++ b/pkg/util/metrics/counter.go @@ -53,13 +53,13 @@ func (c *kubeCounter) setPrometheusCounter(counter prometheus.Counter) { c.initSelfCollection(counter) } -// GetDeprecatedVersion returns a pointer to the Version or nil -func (c *kubeCounter) GetDeprecatedVersion() *semver.Version { +// DeprecatedVersion returns a pointer to the Version or nil +func (c *kubeCounter) DeprecatedVersion() *semver.Version { return c.CounterOpts.DeprecatedVersion } // initializeMetric invocation creates the actual underlying Counter. Until this method is called -// our underlying counter is a no-op. +// the underlying counter is a no-op. func (c *kubeCounter) initializeMetric() { c.CounterOpts.annotateStabilityLevel() // this actually creates the underlying prometheus counter. @@ -67,13 +67,13 @@ func (c *kubeCounter) initializeMetric() { } // initializeDeprecatedMetric invocation creates the actual (but deprecated) Counter. Until this method -// is called our underlying counter is a no-op. +// is called the underlying counter is a no-op. func (c *kubeCounter) initializeDeprecatedMetric() { c.CounterOpts.markDeprecated() c.initializeMetric() } -// kubeCounterVec is our internal representation of our wrapping struct around prometheus +// kubeCounterVec is the internal representation of our wrapping struct around prometheus // counterVecs. kubeCounterVec implements both KubeCollector and KubeCounterVec. type kubeCounterVec struct { *prometheus.CounterVec @@ -96,30 +96,34 @@ func NewCounterVec(opts CounterOpts, labels []string) *kubeCounterVec { return cv } -// GetDeprecatedVersion returns a pointer to the Version or nil -func (v *kubeCounterVec) GetDeprecatedVersion() *semver.Version { +// DeprecatedVersion returns a pointer to the Version or nil +func (v *kubeCounterVec) DeprecatedVersion() *semver.Version { return v.CounterOpts.DeprecatedVersion } // initializeMetric invocation creates the actual underlying CounterVec. Until this method is called -// our underlying counterVec is a no-op. +// the underlying counterVec is a no-op. func (v *kubeCounterVec) initializeMetric() { v.CounterVec = prometheus.NewCounterVec(v.CounterOpts.toPromCounterOpts(), v.originalLabels) } -// initializeMetric invocation creates the actual (but deprecated) CounterVec. Until this method is called -// our underlying counterVec is a no-op. +// initializeDeprecatedMetric invocation creates the actual (but deprecated) CounterVec. Until this method is called +// the underlying counterVec is a no-op. func (v *kubeCounterVec) 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 -// is undesirable for us, since we want a way to turn OFF metrics which end up turning into memory -// leaks. +// 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/master/prometheus/counter.go#L148-L177 +// For reference: https://github.com/prometheus/client_golang/blob/v0.9.2/prometheus/counter.go#L179-L197 +// +// This method returns a no-op metric if the metric is not actually created/registered, avoiding that +// memory leak. func (v *kubeCounterVec) WithLabelValues(lvs ...string) KubeCounter { if !v.IsCreated() { return noop // return no-op counter diff --git a/pkg/util/metrics/counter_test.go b/pkg/util/metrics/counter_test.go index 82609e23db8..62b4944c27b 100644 --- a/pkg/util/metrics/counter_test.go +++ b/pkg/util/metrics/counter_test.go @@ -101,7 +101,7 @@ func TestCounter(t *testing.T) { } } - // let's increment the counter N number of times and verify that the metric retains the count correctly + // 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() @@ -188,7 +188,7 @@ func TestCounterVec(t *testing.T) { if err != nil { t.Fatalf("Gather failed %v", err) } - // we no-opt here when we don't have any metric families (i.e. when the metric is hidden) + // 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())) @@ -206,7 +206,7 @@ func TestCounterVec(t *testing.T) { t.Fatalf("Gather failed %v", err) } - // we no-opt here when we don't have any metric families (i.e. when the metric is hidden) + // 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())) diff --git a/pkg/util/metrics/metric.go b/pkg/util/metrics/metric.go index cc21e1a33af..d63bf9e408a 100644 --- a/pkg/util/metrics/metric.go +++ b/pkg/util/metrics/metric.go @@ -24,39 +24,41 @@ import ( "sync" ) -/** - * This extends the prometheus.Collector interface so that we can customize the metric - * registration process. Specifically, we defer metric initialization until ActuallyCreate - * is called, which then delegates to the underlying metric's initializeMetric or - * initializeDeprecatedMetric method call depending on whether the metric is deprecated or not. +/* +This 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 LazyMetric - GetDeprecatedVersion() *semver.Version + 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 we are now deferring metric instantiation + // is necessary since metric instantiation will be deferred // until the metric is actually registered somewhere. initializeMetric() initializeDeprecatedMetric() } -// LazyMetric defines our registration functionality. We expect LazyMetric -// objects to lazily instantiate metrics (i.e defer metric instantiation until when -// ActuallyCreate is explicitly called). +/* +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 { - ActuallyCreate(*semver.Version) bool + Create(*semver.Version) bool IsCreated() bool IsHidden() bool IsDeprecated() bool } /* - * lazyMetric implements LazyMetric. 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. +lazyMetric implements LazyMetric. 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 @@ -78,11 +80,11 @@ func (r *lazyMetric) lazyInit(self KubeCollector) { r.self = self } -// determineDeprecationStatus figures out whether our lazy metric should be deprecated or not. It takes -// a Version argument which should be the version of the binary in which this code is currently being -// executed. +// 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.GetDeprecatedVersion() + selfVersion := r.self.DeprecatedVersion() if selfVersion == nil { return } @@ -105,13 +107,12 @@ func (r *lazyMetric) IsDeprecated() bool { return r.isDeprecated } -// Defer initialization of metric until we know if we actually need to -// register the thing. This wrapper just allows us to consolidate the -// syncOnce logic in a single spot and toggle the flag, since this -// behavior will be consistent across metrics. -// -// This no-opts and returns true if metric is already created. -func (r *lazyMetric) ActuallyCreate(version *semver.Version) bool { +// 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) } @@ -130,11 +131,11 @@ func (r *lazyMetric) ActuallyCreate(version *semver.Version) bool { 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/65d3a96fbaa7c8c9535d7133d6d98cd50eed4db8/prometheus/collector.go#L98-L120 +/* +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 diff --git a/pkg/util/metrics/opts.go b/pkg/util/metrics/opts.go index 985c323c6d4..ad1d9980634 100644 --- a/pkg/util/metrics/opts.go +++ b/pkg/util/metrics/opts.go @@ -23,9 +23,9 @@ import ( "sync" ) -// KubeOpts is superset struct for prometheus.Opts. We choose not to embed -// the prometheus Opts structure here because that would change struct initialization -// in the manner to which people are currently accustomed. +// 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. diff --git a/pkg/util/metrics/registry.go b/pkg/util/metrics/registry.go index 97a7ee3e8fb..57afeb149c7 100644 --- a/pkg/util/metrics/registry.go +++ b/pkg/util/metrics/registry.go @@ -45,7 +45,7 @@ func MustRegister(cs ...KubeCollector) { } func (kr *KubeRegistry) Register(c KubeCollector) error { - if c.ActuallyCreate(&kr.version) { + if c.Create(&kr.version) { return kr.PromRegistry.Register(c) } return nil @@ -54,7 +54,7 @@ func (kr *KubeRegistry) Register(c KubeCollector) error { func (kr *KubeRegistry) MustRegister(cs ...KubeCollector) { metrics := make([]prometheus.Collector, 0, len(cs)) for _, c := range cs { - if c.ActuallyCreate(&kr.version) { + if c.Create(&kr.version) { metrics = append(metrics, c) } } From abe64acc8d1f88988a370468f1276a22905ac48b Mon Sep 17 00:00:00 2001 From: Han Kang Date: Fri, 26 Apr 2019 16:43:15 -0700 Subject: [PATCH 03/10] add version parsing to metrics framework, use build version information for registry version --- pkg/util/metrics/BUILD | 5 +++ pkg/util/metrics/counter.go | 8 ++-- pkg/util/metrics/counter_test.go | 20 ++++----- pkg/util/metrics/metric.go | 8 ++-- pkg/util/metrics/opts.go | 2 +- pkg/util/metrics/registry.go | 25 ++++++++--- pkg/util/metrics/registry_test.go | 12 +++--- pkg/util/metrics/version_parser.go | 40 ++++++++++++++++++ pkg/util/metrics/version_parser_test.go | 56 +++++++++++++++++++++++++ 9 files changed, 145 insertions(+), 31 deletions(-) create mode 100644 pkg/util/metrics/version_parser.go create mode 100644 pkg/util/metrics/version_parser_test.go diff --git a/pkg/util/metrics/BUILD b/pkg/util/metrics/BUILD index 2a93ba857d5..9545c23ea10 100644 --- a/pkg/util/metrics/BUILD +++ b/pkg/util/metrics/BUILD @@ -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", diff --git a/pkg/util/metrics/counter.go b/pkg/util/metrics/counter.go index 29d87742a3f..69367ac5822 100644 --- a/pkg/util/metrics/counter.go +++ b/pkg/util/metrics/counter.go @@ -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{}, } diff --git a/pkg/util/metrics/counter_test.go b/pkg/util/metrics/counter_test.go index 62b4944c27b..742230b51e4 100644 --- a/pkg/util/metrics/counter_test.go +++ b/pkg/util/metrics/counter_test.go @@ -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() diff --git a/pkg/util/metrics/metric.go b/pkg/util/metrics/metric.go index d63bf9e408a..7231c2af2ce 100644 --- a/pkg/util/metrics/metric.go +++ b/pkg/util/metrics/metric.go @@ -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 } diff --git a/pkg/util/metrics/opts.go b/pkg/util/metrics/opts.go index ad1d9980634..f13e7edffd1 100644 --- a/pkg/util/metrics/opts.go +++ b/pkg/util/metrics/opts.go @@ -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, diff --git a/pkg/util/metrics/registry.go b/pkg/util/metrics/registry.go index 57afeb149c7..c26b137d885 100644 --- a/pkg/util/metrics/registry.go +++ b/pkg/util/metrics/registry.go @@ -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, diff --git a/pkg/util/metrics/registry_test.go b/pkg/util/metrics/registry_test.go index f9cabe87cd8..aeea754bb88 100644 --- a/pkg/util/metrics/registry_test.go +++ b/pkg/util/metrics/registry_test.go @@ -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, diff --git a/pkg/util/metrics/version_parser.go b/pkg/util/metrics/version_parser.go new file mode 100644 index 00000000000..df79582d372 --- /dev/null +++ b/pkg/util/metrics/version_parser.go @@ -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 +} diff --git a/pkg/util/metrics/version_parser_test.go b/pkg/util/metrics/version_parser_test.go new file mode 100644 index 00000000000..648f78948ac --- /dev/null +++ b/pkg/util/metrics/version_parser_test.go @@ -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) + } + }) + } +} From 7f9970645c355322f363f805bcc21f0c87559a52 Mon Sep 17 00:00:00 2001 From: Han Kang Date: Wed, 1 May 2019 15:07:50 -0700 Subject: [PATCH 04/10] swap out internal reference to use unexported registry initializer --- pkg/util/metrics/registry.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/pkg/util/metrics/registry.go b/pkg/util/metrics/registry.go index c26b137d885..2c15f95ca74 100644 --- a/pkg/util/metrics/registry.go +++ b/pkg/util/metrics/registry.go @@ -74,12 +74,9 @@ func NewKubeRegistry() *KubeRegistry { 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), + klog.Fatalf("No valid version loaded for metrics registry") } + return newKubeRegistry(semver.MustParse(*v)) } // newKubeRegistry creates a new vanilla Registry without any Collectors From e6fbb593bb1646369db27e802ae70ee65a2d41be Mon Sep 17 00:00:00 2001 From: Han Kang Date: Wed, 1 May 2019 15:49:28 -0700 Subject: [PATCH 05/10] move framework files to subdirectory for isolation --- pkg/util/metrics/BUILD | 32 ++-------- pkg/util/metrics/framework/BUILD | 58 +++++++++++++++++++ pkg/util/metrics/{ => framework}/counter.go | 2 +- .../metrics/{ => framework}/counter_test.go | 2 +- pkg/util/metrics/{ => framework}/metric.go | 2 +- pkg/util/metrics/{ => framework}/opts.go | 2 +- pkg/util/metrics/{ => framework}/registry.go | 2 +- .../metrics/{ => framework}/registry_test.go | 2 +- .../metrics/{ => framework}/version_parser.go | 2 +- .../{ => framework}/version_parser_test.go | 2 +- pkg/util/metrics/{ => framework}/wrappers.go | 2 +- 11 files changed, 73 insertions(+), 35 deletions(-) create mode 100644 pkg/util/metrics/framework/BUILD rename pkg/util/metrics/{ => framework}/counter.go (99%) rename pkg/util/metrics/{ => framework}/counter_test.go (99%) rename pkg/util/metrics/{ => framework}/metric.go (99%) rename pkg/util/metrics/{ => framework}/opts.go (99%) rename pkg/util/metrics/{ => framework}/registry.go (99%) rename pkg/util/metrics/{ => framework}/registry_test.go (99%) rename pkg/util/metrics/{ => framework}/version_parser.go (98%) rename pkg/util/metrics/{ => framework}/version_parser_test.go (98%) rename pkg/util/metrics/{ => framework}/wrappers.go (99%) diff --git a/pkg/util/metrics/BUILD b/pkg/util/metrics/BUILD index 9545c23ea10..f6ebcabce7b 100644 --- a/pkg/util/metrics/BUILD +++ b/pkg/util/metrics/BUILD @@ -8,43 +8,20 @@ load( go_library( name = "go_default_library", - srcs = [ - "counter.go", - "metric.go", - "opts.go", - "registry.go", - "util.go", - "version_parser.go", - "wrappers.go", - ], + srcs = ["util.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", - "//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", - "util_test.go", - "version_parser_test.go", - ], + srcs = ["util_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", - "//vendor/github.com/prometheus/common/expfmt:go_default_library", - "//vendor/github.com/stretchr/testify/assert:go_default_library", ], ) @@ -57,6 +34,9 @@ filegroup( filegroup( name = "all-srcs", - srcs = [":package-srcs"], + srcs = [ + ":package-srcs", + "//pkg/util/metrics/framework:all-srcs", + ], tags = ["automanaged"], ) diff --git a/pkg/util/metrics/framework/BUILD b/pkg/util/metrics/framework/BUILD new file mode 100644 index 00000000000..1314bbde4a1 --- /dev/null +++ b/pkg/util/metrics/framework/BUILD @@ -0,0 +1,58 @@ +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", + ], + importpath = "k8s.io/kubernetes/pkg/util/metrics/framework", + deps = [ + "//pkg/version:go_default_library", + "//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"], + tags = ["automanaged"], +) diff --git a/pkg/util/metrics/counter.go b/pkg/util/metrics/framework/counter.go similarity index 99% rename from pkg/util/metrics/counter.go rename to pkg/util/metrics/framework/counter.go index 69367ac5822..d02b7fd8369 100644 --- a/pkg/util/metrics/counter.go +++ b/pkg/util/metrics/framework/counter.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package metrics +package framework import ( "github.com/blang/semver" diff --git a/pkg/util/metrics/counter_test.go b/pkg/util/metrics/framework/counter_test.go similarity index 99% rename from pkg/util/metrics/counter_test.go rename to pkg/util/metrics/framework/counter_test.go index 742230b51e4..26db8c1c5ac 100644 --- a/pkg/util/metrics/counter_test.go +++ b/pkg/util/metrics/framework/counter_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package metrics +package framework import ( "bytes" diff --git a/pkg/util/metrics/metric.go b/pkg/util/metrics/framework/metric.go similarity index 99% rename from pkg/util/metrics/metric.go rename to pkg/util/metrics/framework/metric.go index 7231c2af2ce..6f41041e09e 100644 --- a/pkg/util/metrics/metric.go +++ b/pkg/util/metrics/framework/metric.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package metrics +package framework import ( "github.com/blang/semver" diff --git a/pkg/util/metrics/opts.go b/pkg/util/metrics/framework/opts.go similarity index 99% rename from pkg/util/metrics/opts.go rename to pkg/util/metrics/framework/opts.go index f13e7edffd1..44597601697 100644 --- a/pkg/util/metrics/opts.go +++ b/pkg/util/metrics/framework/opts.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package metrics +package framework import ( "fmt" diff --git a/pkg/util/metrics/registry.go b/pkg/util/metrics/framework/registry.go similarity index 99% rename from pkg/util/metrics/registry.go rename to pkg/util/metrics/framework/registry.go index 2c15f95ca74..9b42b935e0d 100644 --- a/pkg/util/metrics/registry.go +++ b/pkg/util/metrics/framework/registry.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package metrics +package framework import ( "github.com/blang/semver" diff --git a/pkg/util/metrics/registry_test.go b/pkg/util/metrics/framework/registry_test.go similarity index 99% rename from pkg/util/metrics/registry_test.go rename to pkg/util/metrics/framework/registry_test.go index aeea754bb88..7d72e78aadf 100644 --- a/pkg/util/metrics/registry_test.go +++ b/pkg/util/metrics/framework/registry_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package metrics +package framework import ( "github.com/blang/semver" diff --git a/pkg/util/metrics/version_parser.go b/pkg/util/metrics/framework/version_parser.go similarity index 98% rename from pkg/util/metrics/version_parser.go rename to pkg/util/metrics/framework/version_parser.go index df79582d372..2d94f33c0d3 100644 --- a/pkg/util/metrics/version_parser.go +++ b/pkg/util/metrics/framework/version_parser.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package metrics +package framework import ( "fmt" diff --git a/pkg/util/metrics/version_parser_test.go b/pkg/util/metrics/framework/version_parser_test.go similarity index 98% rename from pkg/util/metrics/version_parser_test.go rename to pkg/util/metrics/framework/version_parser_test.go index 648f78948ac..ac02ae5bb77 100644 --- a/pkg/util/metrics/version_parser_test.go +++ b/pkg/util/metrics/framework/version_parser_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package metrics +package framework import ( apimachineryversion "k8s.io/apimachinery/pkg/version" diff --git a/pkg/util/metrics/wrappers.go b/pkg/util/metrics/framework/wrappers.go similarity index 99% rename from pkg/util/metrics/wrappers.go rename to pkg/util/metrics/framework/wrappers.go index 29d44ceb2c0..c085bed8e1a 100644 --- a/pkg/util/metrics/wrappers.go +++ b/pkg/util/metrics/framework/wrappers.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package metrics +package framework import ( "github.com/prometheus/client_golang/prometheus" From 634ab0be53582ad3e230f8eb66cd2eba537a365a Mon Sep 17 00:00:00 2001 From: Han Kang Date: Wed, 1 May 2019 16:28:52 -0700 Subject: [PATCH 06/10] add additional documentation around exposed functionality --- pkg/util/metrics/framework/counter.go | 60 +++++++++++---------- pkg/util/metrics/framework/metric.go | 2 +- pkg/util/metrics/framework/opts.go | 8 ++- pkg/util/metrics/framework/registry.go | 28 ++++++++++ pkg/util/metrics/framework/registry_test.go | 24 ++++----- pkg/util/metrics/framework/wrappers.go | 25 +++++---- 6 files changed, 93 insertions(+), 54 deletions(-) diff --git a/pkg/util/metrics/framework/counter.go b/pkg/util/metrics/framework/counter.go index d02b7fd8369..a2e3ab5455c 100644 --- a/pkg/util/metrics/framework/counter.go +++ b/pkg/util/metrics/framework/counter.go @@ -21,24 +21,24 @@ import ( "github.com/prometheus/client_golang/prometheus" ) -// kubeCounter is our internal representation for our wrapping struct around prometheus -// counters. kubeCounter implements both KubeCollector and KubeCounter. -type kubeCounter struct { - KubeCounter +// 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 KubeCounter interfaces. +// 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) *kubeCounter { +func NewCounter(opts *CounterOpts) *Counter { // todo: handle defaulting better if opts.StabilityLevel == "" { opts.StabilityLevel = ALPHA } - kc := &kubeCounter{ + kc := &Counter{ CounterOpts: opts, lazyMetric: lazyMetric{}, } @@ -47,20 +47,20 @@ func NewCounter(opts *CounterOpts) *kubeCounter { return kc } -// setPrometheusCounter sets the underlying KubeCounter object, i.e. the thing that does the measurement. -func (c *kubeCounter) setPrometheusCounter(counter prometheus.Counter) { - c.KubeCounter = counter +// 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 *kubeCounter) DeprecatedVersion() *semver.Version { +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 *kubeCounter) initializeMetric() { +func (c *Counter) initializeMetric() { c.CounterOpts.annotateStabilityLevel() // this actually creates the underlying prometheus counter. c.setPrometheusCounter(prometheus.NewCounter(c.CounterOpts.toPromCounterOpts())) @@ -68,25 +68,25 @@ func (c *kubeCounter) initializeMetric() { // initializeDeprecatedMetric invocation creates the actual (but deprecated) Counter. Until this method // is called the underlying counter is a no-op. -func (c *kubeCounter) initializeDeprecatedMetric() { +func (c *Counter) initializeDeprecatedMetric() { c.CounterOpts.markDeprecated() c.initializeMetric() } -// kubeCounterVec is the internal representation of our wrapping struct around prometheus -// counterVecs. kubeCounterVec implements both KubeCollector and KubeCounterVec. -type kubeCounterVec struct { +// 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 KubeCounterVec interfaces. +// 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) *kubeCounterVec { - cv := &kubeCounterVec{ +func NewCounterVec(opts *CounterOpts, labels []string) *CounterVec { + cv := &CounterVec{ CounterVec: noopCounterVec, CounterOpts: opts, originalLabels: labels, @@ -97,19 +97,19 @@ func NewCounterVec(opts *CounterOpts, labels []string) *kubeCounterVec { } // DeprecatedVersion returns a pointer to the Version or nil -func (v *kubeCounterVec) DeprecatedVersion() *semver.Version { +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 *kubeCounterVec) initializeMetric() { +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 *kubeCounterVec) initializeDeprecatedMetric() { +func (v *CounterVec) initializeDeprecatedMetric() { v.CounterOpts.markDeprecated() v.initializeMetric() } @@ -121,17 +121,23 @@ func (v *kubeCounterVec) initializeDeprecatedMetric() { // for perpetuity (i.e. throughout application lifecycle). // // For reference: https://github.com/prometheus/client_golang/blob/v0.9.2/prometheus/counter.go#L179-L197 -// -// This method returns a no-op metric if the metric is not actually created/registered, avoiding that -// memory leak. -func (v *kubeCounterVec) WithLabelValues(lvs ...string) KubeCounter { + +// 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...) } -func (v *kubeCounterVec) With(labels prometheus.Labels) KubeCounter { +// 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 } diff --git a/pkg/util/metrics/framework/metric.go b/pkg/util/metrics/framework/metric.go index 6f41041e09e..e9733dbf1ed 100644 --- a/pkg/util/metrics/framework/metric.go +++ b/pkg/util/metrics/framework/metric.go @@ -25,7 +25,7 @@ import ( ) /* -This extends the prometheus.Collector interface to allow customization of the metric +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. diff --git a/pkg/util/metrics/framework/opts.go b/pkg/util/metrics/framework/opts.go index 44597601697..315e8055eee 100644 --- a/pkg/util/metrics/framework/opts.go +++ b/pkg/util/metrics/framework/opts.go @@ -41,13 +41,19 @@ type KubeOpts struct { StabilityLevel StabilityLevel } +// StabilityLevel represents the API guarantees for a given defined metric. type StabilityLevel string const ( - ALPHA StabilityLevel = "ALPHA" + // 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. diff --git a/pkg/util/metrics/framework/registry.go b/pkg/util/metrics/framework/registry.go index 9b42b935e0d..2f31aa4f167 100644 --- a/pkg/util/metrics/framework/registry.go +++ b/pkg/util/metrics/framework/registry.go @@ -24,8 +24,13 @@ import ( "k8s.io/kubernetes/pkg/version" ) +// DefaultGlobalRegistry is a stub for the global registry which prometheus client +// currently uses. var DefaultGlobalRegistry = NewKubeRegistry() +// 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 @@ -43,6 +48,11 @@ func MustRegister(cs ...KubeCollector) { DefaultGlobalRegistry.MustRegister(cs...) } +// 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) @@ -50,6 +60,9 @@ func (kr *KubeRegistry) Register(c KubeCollector) error { 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 { @@ -60,14 +73,29 @@ func (kr *KubeRegistry) MustRegister(cs ...KubeCollector) { 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 kubernetes metric registry, loading in the kubernetes +// version information available to the binary. func NewKubeRegistry() *KubeRegistry { v, err := parseVersion(version.Get()) if err != nil { diff --git a/pkg/util/metrics/framework/registry_test.go b/pkg/util/metrics/framework/registry_test.go index 7d72e78aadf..7b4d09a5395 100644 --- a/pkg/util/metrics/framework/registry_test.go +++ b/pkg/util/metrics/framework/registry_test.go @@ -69,7 +69,7 @@ var ( func TestRegister(t *testing.T) { var tests = []struct { desc string - metrics []*kubeCounter + metrics []*Counter registryVersion *semver.Version expectedErrors []error expectedIsCreatedValues []bool @@ -78,7 +78,7 @@ func TestRegister(t *testing.T) { }{ { desc: "test alpha metric", - metrics: []*kubeCounter{alphaCounter}, + metrics: []*Counter{alphaCounter}, registryVersion: &v115, expectedErrors: []error{nil}, expectedIsCreatedValues: []bool{true}, @@ -87,7 +87,7 @@ func TestRegister(t *testing.T) { }, { desc: "test registering same metric multiple times", - metrics: []*kubeCounter{alphaCounter, alphaCounter}, + metrics: []*Counter{alphaCounter, alphaCounter}, registryVersion: &v115, expectedErrors: []error{nil, prometheus.AlreadyRegisteredError{}}, expectedIsCreatedValues: []bool{true, true}, @@ -96,7 +96,7 @@ func TestRegister(t *testing.T) { }, { desc: "test alpha deprecated metric", - metrics: []*kubeCounter{alphaDeprecatedCounter}, + metrics: []*Counter{alphaDeprecatedCounter}, registryVersion: &v115, expectedErrors: []error{nil, prometheus.AlreadyRegisteredError{}}, expectedIsCreatedValues: []bool{true}, @@ -105,7 +105,7 @@ func TestRegister(t *testing.T) { }, { desc: "test alpha hidden metric", - metrics: []*kubeCounter{alphaHiddenCounter}, + metrics: []*Counter{alphaHiddenCounter}, registryVersion: &v115, expectedErrors: []error{nil, prometheus.AlreadyRegisteredError{}}, expectedIsCreatedValues: []bool{false}, @@ -139,43 +139,43 @@ func TestRegister(t *testing.T) { func TestMustRegister(t *testing.T) { var tests = []struct { desc string - metrics []*kubeCounter + metrics []*Counter registryVersion *semver.Version expectedPanics []bool }{ { desc: "test alpha metric", - metrics: []*kubeCounter{alphaCounter}, + metrics: []*Counter{alphaCounter}, registryVersion: &v115, expectedPanics: []bool{false}, }, { desc: "test registering same metric multiple times", - metrics: []*kubeCounter{alphaCounter, alphaCounter}, + metrics: []*Counter{alphaCounter, alphaCounter}, registryVersion: &v115, expectedPanics: []bool{false, true}, }, { desc: "test alpha deprecated metric", - metrics: []*kubeCounter{alphaDeprecatedCounter}, + metrics: []*Counter{alphaDeprecatedCounter}, registryVersion: &v115, expectedPanics: []bool{false}, }, { desc: "test must registering same deprecated metric", - metrics: []*kubeCounter{alphaDeprecatedCounter, alphaDeprecatedCounter}, + metrics: []*Counter{alphaDeprecatedCounter, alphaDeprecatedCounter}, registryVersion: &v115, expectedPanics: []bool{false, true}, }, { desc: "test alpha hidden metric", - metrics: []*kubeCounter{alphaHiddenCounter}, + metrics: []*Counter{alphaHiddenCounter}, registryVersion: &v115, expectedPanics: []bool{false}, }, { desc: "test must registering same hidden metric", - metrics: []*kubeCounter{alphaHiddenCounter, alphaHiddenCounter}, + metrics: []*Counter{alphaHiddenCounter, alphaHiddenCounter}, registryVersion: &v115, expectedPanics: []bool{false, false}, // hidden metrics no-opt }, diff --git a/pkg/util/metrics/framework/wrappers.go b/pkg/util/metrics/framework/wrappers.go index c085bed8e1a..fa790b090c5 100644 --- a/pkg/util/metrics/framework/wrappers.go +++ b/pkg/util/metrics/framework/wrappers.go @@ -27,38 +27,37 @@ import ( // 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 - */ +// 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 - */ +// Metric defines a subset of prometheus.Metric interface methods type Metric interface { Desc() *prometheus.Desc Write(*dto.Metric) error } -// Counter is a Metric that represents a single numerical value that only ever +// 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. -// -// This interface defines a subset of the interface provided by prometheus.Counter -type KubeCounter interface { + +// CounterMetric is an interface which defines a subset of the interface provided by prometheus.Counter +type CounterMetric interface { Inc() Add(float64) } -type KubeCounterVec interface { - WithLabelValues(...string) KubeCounter - With(prometheus.Labels) KubeCounter +// 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) From 04db3dc9f7478f5517fdbe3154be6c2a758ee9fc Mon Sep 17 00:00:00 2001 From: Han Kang Date: Fri, 3 May 2019 10:57:49 -0700 Subject: [PATCH 07/10] move files to component-base --- pkg/util/metrics/BUILD | 5 +- staging/src/k8s.io/component-base/BUILD | 1 + .../src/k8s.io/component-base/metrics}/BUILD | 4 +- .../k8s.io/component-base/metrics}/counter.go | 2 +- .../component-base/metrics}/counter_test.go | 24 ++--- .../k8s.io/component-base/metrics}/metric.go | 2 +- .../k8s.io/component-base/metrics}/opts.go | 2 +- .../component-base/metrics}/registry.go | 89 +++++++++++++------ .../component-base/metrics}/registry_test.go | 15 +++- .../component-base/metrics}/version_parser.go | 9 +- .../metrics}/version_parser_test.go | 11 +-- .../component-base/metrics}/wrappers.go | 2 +- 12 files changed, 105 insertions(+), 61 deletions(-) rename {pkg/util/metrics/framework => staging/src/k8s.io/component-base/metrics}/BUILD (92%) rename {pkg/util/metrics/framework => staging/src/k8s.io/component-base/metrics}/counter.go (99%) rename {pkg/util/metrics/framework => staging/src/k8s.io/component-base/metrics}/counter_test.go (94%) rename {pkg/util/metrics/framework => staging/src/k8s.io/component-base/metrics}/metric.go (99%) rename {pkg/util/metrics/framework => staging/src/k8s.io/component-base/metrics}/opts.go (99%) rename {pkg/util/metrics/framework => staging/src/k8s.io/component-base/metrics}/registry.go (57%) rename {pkg/util/metrics/framework => staging/src/k8s.io/component-base/metrics}/registry_test.go (94%) rename {pkg/util/metrics/framework => staging/src/k8s.io/component-base/metrics}/version_parser.go (75%) rename {pkg/util/metrics/framework => staging/src/k8s.io/component-base/metrics}/version_parser_test.go (82%) rename {pkg/util/metrics/framework => staging/src/k8s.io/component-base/metrics}/wrappers.go (99%) diff --git a/pkg/util/metrics/BUILD b/pkg/util/metrics/BUILD index f6ebcabce7b..a15d30ec247 100644 --- a/pkg/util/metrics/BUILD +++ b/pkg/util/metrics/BUILD @@ -34,9 +34,6 @@ filegroup( filegroup( name = "all-srcs", - srcs = [ - ":package-srcs", - "//pkg/util/metrics/framework:all-srcs", - ], + srcs = [":package-srcs"], tags = ["automanaged"], ) diff --git a/staging/src/k8s.io/component-base/BUILD b/staging/src/k8s.io/component-base/BUILD index 69be7ee4959..68ac5d60057 100644 --- a/staging/src/k8s.io/component-base/BUILD +++ b/staging/src/k8s.io/component-base/BUILD @@ -13,6 +13,7 @@ filegroup( "//staging/src/k8s.io/component-base/cli/globalflag:all-srcs", "//staging/src/k8s.io/component-base/config: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"], diff --git a/pkg/util/metrics/framework/BUILD b/staging/src/k8s.io/component-base/metrics/BUILD similarity index 92% rename from pkg/util/metrics/framework/BUILD rename to staging/src/k8s.io/component-base/metrics/BUILD index 1314bbde4a1..30e6181eb99 100644 --- a/pkg/util/metrics/framework/BUILD +++ b/staging/src/k8s.io/component-base/metrics/BUILD @@ -16,9 +16,9 @@ go_library( "version_parser.go", "wrappers.go", ], - importpath = "k8s.io/kubernetes/pkg/util/metrics/framework", + importmap = "k8s.io/kubernetes/vendor/k8s.io/component-base/metrics", + importpath = "k8s.io/component-base/metrics", deps = [ - "//pkg/version:go_default_library", "//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", diff --git a/pkg/util/metrics/framework/counter.go b/staging/src/k8s.io/component-base/metrics/counter.go similarity index 99% rename from pkg/util/metrics/framework/counter.go rename to staging/src/k8s.io/component-base/metrics/counter.go index a2e3ab5455c..0d4e7fb3f24 100644 --- a/pkg/util/metrics/framework/counter.go +++ b/staging/src/k8s.io/component-base/metrics/counter.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package framework +package metrics import ( "github.com/blang/semver" diff --git a/pkg/util/metrics/framework/counter_test.go b/staging/src/k8s.io/component-base/metrics/counter_test.go similarity index 94% rename from pkg/util/metrics/framework/counter_test.go rename to staging/src/k8s.io/component-base/metrics/counter_test.go index 26db8c1c5ac..79fd30cdbd8 100644 --- a/pkg/util/metrics/framework/counter_test.go +++ b/staging/src/k8s.io/component-base/metrics/counter_test.go @@ -14,22 +14,22 @@ See the License for the specific language governing permissions and limitations under the License. */ -package framework +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) { - v115 := semver.MustParse("1.15.0") v114 := semver.MustParse("1.14.0") + v115 := semver.MustParse("1.15.0") var tests = []struct { desc string *CounterOpts - registryVersion *semver.Version expectedMetricCount int expectedHelp string }{ @@ -42,7 +42,6 @@ func TestCounter(t *testing.T) { StabilityLevel: ALPHA, Help: "counter help", }, - registryVersion: &v115, expectedMetricCount: 1, expectedHelp: "[ALPHA] counter help", }, @@ -56,7 +55,6 @@ func TestCounter(t *testing.T) { StabilityLevel: ALPHA, DeprecatedVersion: &v115, }, - registryVersion: &v115, expectedMetricCount: 1, expectedHelp: "[ALPHA] (Deprecated since 1.15.0) counter help", }, @@ -70,14 +68,17 @@ func TestCounter(t *testing.T) { StabilityLevel: ALPHA, DeprecatedVersion: &v114, }, - registryVersion: &v115, expectedMetricCount: 0, }, } for _, test := range tests { t.Run(test.desc, func(t *testing.T) { - registry := newKubeRegistry(*test.registryVersion) + registry := newKubeRegistry(&apimachineryversion.Info{ + Major: "1", + Minor: "15", + GitVersion: "v1.15.0-alpha-1.12345", + }) c := NewCounter(test.CounterOpts) registry.MustRegister(c) @@ -141,7 +142,6 @@ func TestCounterVec(t *testing.T) { Help: "counter help", }, labels: []string{"label_a", "label_b"}, - registryVersion: &v115, expectedMetricFamilyCount: 1, expectedHelp: "counter help", }, @@ -155,7 +155,6 @@ func TestCounterVec(t *testing.T) { DeprecatedVersion: &v115, }, labels: []string{"label_a", "label_b"}, - registryVersion: &v115, expectedMetricFamilyCount: 1, expectedHelp: "(Deprecated since 1.15.0) counter help", }, @@ -169,7 +168,6 @@ func TestCounterVec(t *testing.T) { DeprecatedVersion: &v114, }, labels: []string{"label_a", "label_b"}, - registryVersion: &v115, expectedMetricFamilyCount: 0, expectedHelp: "counter help", }, @@ -177,7 +175,11 @@ func TestCounterVec(t *testing.T) { for _, test := range tests { t.Run(test.desc, func(t *testing.T) { - registry := newKubeRegistry(*test.registryVersion) + 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() diff --git a/pkg/util/metrics/framework/metric.go b/staging/src/k8s.io/component-base/metrics/metric.go similarity index 99% rename from pkg/util/metrics/framework/metric.go rename to staging/src/k8s.io/component-base/metrics/metric.go index e9733dbf1ed..4d5bbf85289 100644 --- a/pkg/util/metrics/framework/metric.go +++ b/staging/src/k8s.io/component-base/metrics/metric.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package framework +package metrics import ( "github.com/blang/semver" diff --git a/pkg/util/metrics/framework/opts.go b/staging/src/k8s.io/component-base/metrics/opts.go similarity index 99% rename from pkg/util/metrics/framework/opts.go rename to staging/src/k8s.io/component-base/metrics/opts.go index 315e8055eee..1409f241020 100644 --- a/pkg/util/metrics/framework/opts.go +++ b/staging/src/k8s.io/component-base/metrics/opts.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package framework +package metrics import ( "fmt" diff --git a/pkg/util/metrics/framework/registry.go b/staging/src/k8s.io/component-base/metrics/registry.go similarity index 57% rename from pkg/util/metrics/framework/registry.go rename to staging/src/k8s.io/component-base/metrics/registry.go index 2f31aa4f167..0b67308845d 100644 --- a/pkg/util/metrics/framework/registry.go +++ b/staging/src/k8s.io/component-base/metrics/registry.go @@ -14,38 +14,72 @@ See the License for the specific language governing permissions and limitations under the License. */ -package framework +package metrics 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" + apimachineryversion "k8s.io/apimachinery/pkg/version" + "sync" ) -// DefaultGlobalRegistry is a stub for the global registry which prometheus client -// currently uses. -var DefaultGlobalRegistry = NewKubeRegistry() +var globalRegistryFactory = metricsRegistryFactory{ + globalRegistry: &noopKubeRegistry{}, +} -// KubeRegistry is a wrapper around a prometheus registry-type object. Upon initialization +// NewKubeRegistry creates a new kubernetes metric registry, loading in the kubernetes +// version information available to the binary. +func (r metricsRegistryFactory) newKubeRegistry() KubeRegistry { + if r.kubeVersion == nil { + return noopKubeRegistry{} + } + return newKubeRegistry(r.kubeVersion) +} + +type metricsRegistryFactory struct { + globalRegistry KubeRegistry + kubeVersion *apimachineryversion.Info + setVersionOnce sync.Once +} + +// 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 { +type kubeRegistry struct { PromRegistry version semver.Version } +// 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) { + globalRegistryFactory.setVersionOnce.Do(func() { + globalRegistryFactory.globalRegistry = newKubeRegistry(ver) + globalRegistryFactory.kubeVersion = ver + }) +} + // Register registers a collectable metric, but it uses a global registry. func Register(c KubeCollector) error { - return DefaultGlobalRegistry.Register(c) + return globalRegistryFactory.globalRegistry.Register(c) } // MustRegister works like Register but registers any number of // Collectors and panics upon the first registration that causes an // error. func MustRegister(cs ...KubeCollector) { - DefaultGlobalRegistry.MustRegister(cs...) + globalRegistryFactory.globalRegistry.MustRegister(cs...) } // Register registers a new Collector to be included in metrics @@ -53,7 +87,7 @@ func MustRegister(cs ...KubeCollector) { // 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 { +func (kr *kubeRegistry) Register(c KubeCollector) error { if c.Create(&kr.version) { return kr.PromRegistry.Register(c) } @@ -63,7 +97,7 @@ func (kr *KubeRegistry) Register(c KubeCollector) error { // 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) { +func (kr *kubeRegistry) MustRegister(cs ...KubeCollector) { metrics := make([]prometheus.Collector, 0, len(cs)) for _, c := range cs { if c.Create(&kr.version) { @@ -79,7 +113,7 @@ func (kr *KubeRegistry) MustRegister(cs ...KubeCollector) { // 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 { +func (kr *kubeRegistry) Unregister(collector KubeCollector) bool { return kr.PromRegistry.Unregister(collector) } @@ -90,28 +124,31 @@ func (kr *KubeRegistry) Unregister(collector KubeCollector) bool { // 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) { +func (kr *kubeRegistry) Gather() ([]*dto.MetricFamily, error) { return kr.PromRegistry.Gather() } // NewKubeRegistry creates a new kubernetes metric registry, loading in the kubernetes // version information available to the binary. -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 loaded for metrics registry") - } - return newKubeRegistry(semver.MustParse(*v)) +func NewKubeRegistry() KubeRegistry { + return globalRegistryFactory.newKubeRegistry() } // newKubeRegistry creates a new vanilla Registry without any Collectors // pre-registered. -func newKubeRegistry(version semver.Version) *KubeRegistry { - return &KubeRegistry{ +func newKubeRegistry(v *apimachineryversion.Info) KubeRegistry { + return &kubeRegistry{ PromRegistry: prometheus.NewRegistry(), - version: version, + version: parseVersion(*v), } } + +// noop registry +var noopRegistry = &noopKubeRegistry{} + +type noopKubeRegistry struct{} + +func (noopKubeRegistry) Register(KubeCollector) error { return nil } +func (noopKubeRegistry) MustRegister(...KubeCollector) {} +func (noopKubeRegistry) Unregister(KubeCollector) bool { return false } +func (noopKubeRegistry) Gather() ([]*dto.MetricFamily, error) { return nil, nil } diff --git a/pkg/util/metrics/framework/registry_test.go b/staging/src/k8s.io/component-base/metrics/registry_test.go similarity index 94% rename from pkg/util/metrics/framework/registry_test.go rename to staging/src/k8s.io/component-base/metrics/registry_test.go index 7b4d09a5395..020cca747f2 100644 --- a/pkg/util/metrics/framework/registry_test.go +++ b/staging/src/k8s.io/component-base/metrics/registry_test.go @@ -14,12 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -package framework +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" ) @@ -116,7 +117,11 @@ func TestRegister(t *testing.T) { for _, test := range tests { t.Run(test.desc, func(t *testing.T) { - registry := newKubeRegistry(*test.registryVersion) + 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() { @@ -183,7 +188,11 @@ func TestMustRegister(t *testing.T) { for _, test := range tests { t.Run(test.desc, func(t *testing.T) { - registry := newKubeRegistry(*test.registryVersion) + 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, diff --git a/pkg/util/metrics/framework/version_parser.go b/staging/src/k8s.io/component-base/metrics/version_parser.go similarity index 75% rename from pkg/util/metrics/framework/version_parser.go rename to staging/src/k8s.io/component-base/metrics/version_parser.go index 2d94f33c0d3..7ef66d08a22 100644 --- a/pkg/util/metrics/framework/version_parser.go +++ b/staging/src/k8s.io/component-base/metrics/version_parser.go @@ -14,10 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -package framework +package metrics import ( "fmt" + "github.com/blang/semver" apimachineryversion "k8s.io/apimachinery/pkg/version" "regexp" ) @@ -30,11 +31,11 @@ var ( versionRe = regexp.MustCompile(versionRegexpString) ) -func parseVersion(ver apimachineryversion.Info) (*string, error) { +func parseVersion(ver apimachineryversion.Info) semver.Version { 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()) + panic(fmt.Sprintf("version string \"%v\" doesn't match expected regular expression: \"%v\"", ver.String(), versionRe.String())) } - return &matches[0][1], nil + return semver.MustParse(matches[0][1]) } diff --git a/pkg/util/metrics/framework/version_parser_test.go b/staging/src/k8s.io/component-base/metrics/version_parser_test.go similarity index 82% rename from pkg/util/metrics/framework/version_parser_test.go rename to staging/src/k8s.io/component-base/metrics/version_parser_test.go index ac02ae5bb77..735053c1126 100644 --- a/pkg/util/metrics/framework/version_parser_test.go +++ b/staging/src/k8s.io/component-base/metrics/version_parser_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package framework +package metrics import ( apimachineryversion "k8s.io/apimachinery/pkg/version" @@ -44,12 +44,9 @@ func TestVersionParsing(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) + parsedV := parseVersion(version) + if test.expectedVersion != parsedV.String() { + t.Errorf("Got %v, wanted %v", parsedV.String(), test.expectedVersion) } }) } diff --git a/pkg/util/metrics/framework/wrappers.go b/staging/src/k8s.io/component-base/metrics/wrappers.go similarity index 99% rename from pkg/util/metrics/framework/wrappers.go rename to staging/src/k8s.io/component-base/metrics/wrappers.go index fa790b090c5..5bb72cacfbc 100644 --- a/pkg/util/metrics/framework/wrappers.go +++ b/staging/src/k8s.io/component-base/metrics/wrappers.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package framework +package metrics import ( "github.com/prometheus/client_golang/prometheus" From 7b619f5763261a58e4aa161855257dd67a41428a Mon Sep 17 00:00:00 2001 From: Han Kang Date: Mon, 6 May 2019 12:29:57 -0700 Subject: [PATCH 08/10] move global registry code into subdirectory 'legacyregistry' --- .../src/k8s.io/component-base/metrics/BUILD | 5 +- .../component-base/metrics/counter_test.go | 4 +- .../metrics/legacyregistry/BUILD | 41 ++++ .../metrics/legacyregistry/registry.go | 89 ++++++++ .../metrics/legacyregistry/registry_test.go | 195 ++++++++++++++++++ .../k8s.io/component-base/metrics/metric.go | 8 +- .../k8s.io/component-base/metrics/registry.go | 62 +----- .../component-base/metrics/registry_test.go | 17 +- 8 files changed, 341 insertions(+), 80 deletions(-) create mode 100644 staging/src/k8s.io/component-base/metrics/legacyregistry/BUILD create mode 100644 staging/src/k8s.io/component-base/metrics/legacyregistry/registry.go create mode 100644 staging/src/k8s.io/component-base/metrics/legacyregistry/registry_test.go diff --git a/staging/src/k8s.io/component-base/metrics/BUILD b/staging/src/k8s.io/component-base/metrics/BUILD index 30e6181eb99..b13db6f3536 100644 --- a/staging/src/k8s.io/component-base/metrics/BUILD +++ b/staging/src/k8s.io/component-base/metrics/BUILD @@ -53,6 +53,9 @@ filegroup( filegroup( name = "all-srcs", - srcs = [":package-srcs"], + srcs = [ + ":package-srcs", + "//staging/src/k8s.io/component-base/metrics/legacyregistry:all-srcs", + ], tags = ["automanaged"], ) diff --git a/staging/src/k8s.io/component-base/metrics/counter_test.go b/staging/src/k8s.io/component-base/metrics/counter_test.go index 79fd30cdbd8..72ddf762a75 100644 --- a/staging/src/k8s.io/component-base/metrics/counter_test.go +++ b/staging/src/k8s.io/component-base/metrics/counter_test.go @@ -74,7 +74,7 @@ func TestCounter(t *testing.T) { for _, test := range tests { t.Run(test.desc, func(t *testing.T) { - registry := newKubeRegistry(&apimachineryversion.Info{ + registry := NewKubeRegistry(&apimachineryversion.Info{ Major: "1", Minor: "15", GitVersion: "v1.15.0-alpha-1.12345", @@ -175,7 +175,7 @@ func TestCounterVec(t *testing.T) { for _, test := range tests { t.Run(test.desc, func(t *testing.T) { - registry := newKubeRegistry(&apimachineryversion.Info{ + registry := NewKubeRegistry(&apimachineryversion.Info{ Major: "1", Minor: "15", GitVersion: "v1.15.0-alpha-1.12345", diff --git a/staging/src/k8s.io/component-base/metrics/legacyregistry/BUILD b/staging/src/k8s.io/component-base/metrics/legacyregistry/BUILD new file mode 100644 index 00000000000..14937d9da42 --- /dev/null +++ b/staging/src/k8s.io/component-base/metrics/legacyregistry/BUILD @@ -0,0 +1,41 @@ +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", + "//vendor/github.com/prometheus/client_model/go: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", + ], +) diff --git a/staging/src/k8s.io/component-base/metrics/legacyregistry/registry.go b/staging/src/k8s.io/component-base/metrics/legacyregistry/registry.go new file mode 100644 index 00000000000..8a941e7dc3c --- /dev/null +++ b/staging/src/k8s.io/component-base/metrics/legacyregistry/registry.go @@ -0,0 +1,89 @@ +/* +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 ( + 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 { + 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) + } +} diff --git a/staging/src/k8s.io/component-base/metrics/legacyregistry/registry_test.go b/staging/src/k8s.io/component-base/metrics/legacyregistry/registry_test.go new file mode 100644 index 00000000000..a448bf3332d --- /dev/null +++ b/staging/src/k8s.io/component-base/metrics/legacyregistry/registry_test.go @@ -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.") +} diff --git a/staging/src/k8s.io/component-base/metrics/metric.go b/staging/src/k8s.io/component-base/metrics/metric.go index 4d5bbf85289..81f2456e4bc 100644 --- a/staging/src/k8s.io/component-base/metrics/metric.go +++ b/staging/src/k8s.io/component-base/metrics/metric.go @@ -32,7 +32,7 @@ method call depending on whether the metric is deprecated or not. */ type KubeCollector interface { Collector - LazyMetric + lazyKubeMetric DeprecatedVersion() *semver.Version // Each collector metric should provide an initialization function // for both deprecated and non-deprecated variants of a metric. This @@ -43,11 +43,11 @@ type KubeCollector interface { } /* -LazyMetric defines our registration functionality. LazyMetric objects are expected +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 LazyMetric interface { +type lazyKubeMetric interface { Create(*semver.Version) bool IsCreated() bool IsHidden() bool @@ -55,7 +55,7 @@ type LazyMetric interface { } /* -lazyMetric implements LazyMetric. A lazy metric is lazy because it waits until metric +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. diff --git a/staging/src/k8s.io/component-base/metrics/registry.go b/staging/src/k8s.io/component-base/metrics/registry.go index 0b67308845d..a91dd25a982 100644 --- a/staging/src/k8s.io/component-base/metrics/registry.go +++ b/staging/src/k8s.io/component-base/metrics/registry.go @@ -21,28 +21,8 @@ import ( "github.com/prometheus/client_golang/prometheus" dto "github.com/prometheus/client_model/go" apimachineryversion "k8s.io/apimachinery/pkg/version" - "sync" ) -var globalRegistryFactory = metricsRegistryFactory{ - globalRegistry: &noopKubeRegistry{}, -} - -// NewKubeRegistry creates a new kubernetes metric registry, loading in the kubernetes -// version information available to the binary. -func (r metricsRegistryFactory) newKubeRegistry() KubeRegistry { - if r.kubeVersion == nil { - return noopKubeRegistry{} - } - return newKubeRegistry(r.kubeVersion) -} - -type metricsRegistryFactory struct { - globalRegistry KubeRegistry - kubeVersion *apimachineryversion.Info - setVersionOnce sync.Once -} - // KubeRegistry is an interface which implements a subset of prometheus.Registerer and // prometheus.Gatherer interfaces type KubeRegistry interface { @@ -60,28 +40,6 @@ type kubeRegistry struct { version semver.Version } -// 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) { - globalRegistryFactory.setVersionOnce.Do(func() { - globalRegistryFactory.globalRegistry = newKubeRegistry(ver) - globalRegistryFactory.kubeVersion = ver - }) -} - -// Register registers a collectable metric, but it uses a global registry. -func Register(c KubeCollector) error { - return globalRegistryFactory.globalRegistry.Register(c) -} - -// MustRegister works like Register but registers any number of -// Collectors and panics upon the first registration that causes an -// error. -func MustRegister(cs ...KubeCollector) { - globalRegistryFactory.globalRegistry.MustRegister(cs...) -} - // 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 @@ -128,27 +86,11 @@ func (kr *kubeRegistry) Gather() ([]*dto.MetricFamily, error) { return kr.PromRegistry.Gather() } -// NewKubeRegistry creates a new kubernetes metric registry, loading in the kubernetes -// version information available to the binary. -func NewKubeRegistry() KubeRegistry { - return globalRegistryFactory.newKubeRegistry() -} - -// newKubeRegistry creates a new vanilla Registry without any Collectors +// NewKubeRegistry creates a new vanilla Registry without any Collectors // pre-registered. -func newKubeRegistry(v *apimachineryversion.Info) KubeRegistry { +func NewKubeRegistry(v *apimachineryversion.Info) KubeRegistry { return &kubeRegistry{ PromRegistry: prometheus.NewRegistry(), version: parseVersion(*v), } } - -// noop registry -var noopRegistry = &noopKubeRegistry{} - -type noopKubeRegistry struct{} - -func (noopKubeRegistry) Register(KubeCollector) error { return nil } -func (noopKubeRegistry) MustRegister(...KubeCollector) {} -func (noopKubeRegistry) Unregister(KubeCollector) bool { return false } -func (noopKubeRegistry) Gather() ([]*dto.MetricFamily, error) { return nil, nil } diff --git a/staging/src/k8s.io/component-base/metrics/registry_test.go b/staging/src/k8s.io/component-base/metrics/registry_test.go index 020cca747f2..4c8e071e25d 100644 --- a/staging/src/k8s.io/component-base/metrics/registry_test.go +++ b/staging/src/k8s.io/component-base/metrics/registry_test.go @@ -56,15 +56,6 @@ var ( DeprecatedVersion: &v114, }, ) - stableCounter = NewCounter( - &CounterOpts{ - Namespace: "some_namespace", - Name: "test_some_other_counter", - Subsystem: "subsystem", - StabilityLevel: STABLE, - Help: "counter help", - }, - ) ) func TestRegister(t *testing.T) { @@ -99,7 +90,7 @@ func TestRegister(t *testing.T) { desc: "test alpha deprecated metric", metrics: []*Counter{alphaDeprecatedCounter}, registryVersion: &v115, - expectedErrors: []error{nil, prometheus.AlreadyRegisteredError{}}, + expectedErrors: []error{nil}, expectedIsCreatedValues: []bool{true}, expectedIsDeprecated: []bool{true}, expectedIsHidden: []bool{false}, @@ -108,7 +99,7 @@ func TestRegister(t *testing.T) { desc: "test alpha hidden metric", metrics: []*Counter{alphaHiddenCounter}, registryVersion: &v115, - expectedErrors: []error{nil, prometheus.AlreadyRegisteredError{}}, + expectedErrors: []error{nil}, expectedIsCreatedValues: []bool{false}, expectedIsDeprecated: []bool{true}, expectedIsHidden: []bool{true}, @@ -117,7 +108,7 @@ func TestRegister(t *testing.T) { for _, test := range tests { t.Run(test.desc, func(t *testing.T) { - registry := newKubeRegistry(&apimachineryversion.Info{ + registry := NewKubeRegistry(&apimachineryversion.Info{ Major: "1", Minor: "15", GitVersion: "v1.15.0-alpha-1.12345", @@ -188,7 +179,7 @@ func TestMustRegister(t *testing.T) { for _, test := range tests { t.Run(test.desc, func(t *testing.T) { - registry := newKubeRegistry(&apimachineryversion.Info{ + registry := NewKubeRegistry(&apimachineryversion.Info{ Major: "1", Minor: "15", GitVersion: "v1.15.0-alpha-1.12345", From 91d3a79aaf787361210bd5eb4142a47e3e5dfb84 Mon Sep 17 00:00:00 2001 From: Han Kang Date: Tue, 7 May 2019 10:38:53 -0700 Subject: [PATCH 09/10] update dependencies (bring in prometheus and semver) --- .../src/k8s.io/apiextensions-apiserver/go.sum | 1 + staging/src/k8s.io/apiserver/go.sum | 1 + staging/src/k8s.io/component-base/go.mod | 6 ++++++ staging/src/k8s.io/component-base/go.sum | 16 ++++++++++++++++ .../component-base/metrics/legacyregistry/BUILD | 1 - staging/src/k8s.io/kube-aggregator/go.sum | 1 + .../src/k8s.io/kube-controller-manager/go.mod | 1 + .../src/k8s.io/kube-controller-manager/go.sum | 7 +++++++ staging/src/k8s.io/kube-proxy/go.mod | 1 + staging/src/k8s.io/kube-proxy/go.sum | 7 +++++++ staging/src/k8s.io/kube-scheduler/go.mod | 1 + staging/src/k8s.io/kube-scheduler/go.sum | 7 +++++++ staging/src/k8s.io/sample-apiserver/go.sum | 1 + 13 files changed, 50 insertions(+), 1 deletion(-) diff --git a/staging/src/k8s.io/apiextensions-apiserver/go.sum b/staging/src/k8s.io/apiextensions-apiserver/go.sum index 2d5c83d7f8b..6986c805256 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/go.sum +++ b/staging/src/k8s.io/apiextensions-apiserver/go.sum @@ -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= diff --git a/staging/src/k8s.io/apiserver/go.sum b/staging/src/k8s.io/apiserver/go.sum index e9c546dbdf8..11df5623494 100644 --- a/staging/src/k8s.io/apiserver/go.sum +++ b/staging/src/k8s.io/apiserver/go.sum @@ -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= diff --git a/staging/src/k8s.io/component-base/go.mod b/staging/src/k8s.io/component-base/go.mod index 0d6ba6994b8..9cb7c9ef522 100644 --- a/staging/src/k8s.io/component-base/go.mod +++ b/staging/src/k8s.io/component-base/go.mod @@ -5,13 +5,19 @@ 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 k8s.io/klog v0.3.0 k8s.io/utils v0.0.0-20190221042446-c2654d5206da ) 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 diff --git a/staging/src/k8s.io/component-base/go.sum b/staging/src/k8s.io/component-base/go.sum index 698c02811f1..da5396b53f5 100644 --- a/staging/src/k8s.io/component-base/go.sum +++ b/staging/src/k8s.io/component-base/go.sum @@ -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= diff --git a/staging/src/k8s.io/component-base/metrics/legacyregistry/BUILD b/staging/src/k8s.io/component-base/metrics/legacyregistry/BUILD index 14937d9da42..2afa5d91353 100644 --- a/staging/src/k8s.io/component-base/metrics/legacyregistry/BUILD +++ b/staging/src/k8s.io/component-base/metrics/legacyregistry/BUILD @@ -10,7 +10,6 @@ go_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/prometheus/client_model/go:go_default_library", ], ) diff --git a/staging/src/k8s.io/kube-aggregator/go.sum b/staging/src/k8s.io/kube-aggregator/go.sum index 70fd7d1a1ac..3268152d6bc 100644 --- a/staging/src/k8s.io/kube-aggregator/go.sum +++ b/staging/src/k8s.io/kube-aggregator/go.sum @@ -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= diff --git a/staging/src/k8s.io/kube-controller-manager/go.mod b/staging/src/k8s.io/kube-controller-manager/go.mod index 82ffa43b55d..a1e513a119d 100644 --- a/staging/src/k8s.io/kube-controller-manager/go.mod +++ b/staging/src/k8s.io/kube-controller-manager/go.mod @@ -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 diff --git a/staging/src/k8s.io/kube-controller-manager/go.sum b/staging/src/k8s.io/kube-controller-manager/go.sum index 698c02811f1..86939f2a039 100644 --- a/staging/src/k8s.io/kube-controller-manager/go.sum +++ b/staging/src/k8s.io/kube-controller-manager/go.sum @@ -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= diff --git a/staging/src/k8s.io/kube-proxy/go.mod b/staging/src/k8s.io/kube-proxy/go.mod index 339b8eb0efb..6bd8199e210 100644 --- a/staging/src/k8s.io/kube-proxy/go.mod +++ b/staging/src/k8s.io/kube-proxy/go.mod @@ -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 diff --git a/staging/src/k8s.io/kube-proxy/go.sum b/staging/src/k8s.io/kube-proxy/go.sum index 698c02811f1..86939f2a039 100644 --- a/staging/src/k8s.io/kube-proxy/go.sum +++ b/staging/src/k8s.io/kube-proxy/go.sum @@ -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= diff --git a/staging/src/k8s.io/kube-scheduler/go.mod b/staging/src/k8s.io/kube-scheduler/go.mod index 3f59fcfc37e..0c1d1700915 100644 --- a/staging/src/k8s.io/kube-scheduler/go.mod +++ b/staging/src/k8s.io/kube-scheduler/go.mod @@ -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 diff --git a/staging/src/k8s.io/kube-scheduler/go.sum b/staging/src/k8s.io/kube-scheduler/go.sum index 698c02811f1..86939f2a039 100644 --- a/staging/src/k8s.io/kube-scheduler/go.sum +++ b/staging/src/k8s.io/kube-scheduler/go.sum @@ -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= diff --git a/staging/src/k8s.io/sample-apiserver/go.sum b/staging/src/k8s.io/sample-apiserver/go.sum index ee6bd576912..f044dad8976 100644 --- a/staging/src/k8s.io/sample-apiserver/go.sum +++ b/staging/src/k8s.io/sample-apiserver/go.sum @@ -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= From 6d839235c2e6bf47c7f0f7c354572e59e025fb96 Mon Sep 17 00:00:00 2001 From: Han Kang Date: Wed, 8 May 2019 10:54:20 -0700 Subject: [PATCH 10/10] handle global registry version loading more than once (with different versions) --- .../src/k8s.io/component-base/metrics/counter_test.go | 4 ++-- .../component-base/metrics/legacyregistry/registry.go | 10 ++++++++-- .../metrics/legacyregistry/registry_test.go | 6 +++--- staging/src/k8s.io/component-base/metrics/registry.go | 4 ++-- .../src/k8s.io/component-base/metrics/registry_test.go | 4 ++-- 5 files changed, 17 insertions(+), 11 deletions(-) diff --git a/staging/src/k8s.io/component-base/metrics/counter_test.go b/staging/src/k8s.io/component-base/metrics/counter_test.go index 72ddf762a75..15eaf3db6b1 100644 --- a/staging/src/k8s.io/component-base/metrics/counter_test.go +++ b/staging/src/k8s.io/component-base/metrics/counter_test.go @@ -74,7 +74,7 @@ func TestCounter(t *testing.T) { for _, test := range tests { t.Run(test.desc, func(t *testing.T) { - registry := NewKubeRegistry(&apimachineryversion.Info{ + registry := NewKubeRegistry(apimachineryversion.Info{ Major: "1", Minor: "15", GitVersion: "v1.15.0-alpha-1.12345", @@ -175,7 +175,7 @@ func TestCounterVec(t *testing.T) { for _, test := range tests { t.Run(test.desc, func(t *testing.T) { - registry := NewKubeRegistry(&apimachineryversion.Info{ + registry := NewKubeRegistry(apimachineryversion.Info{ Major: "1", Minor: "15", GitVersion: "v1.15.0-alpha-1.12345", diff --git a/staging/src/k8s.io/component-base/metrics/legacyregistry/registry.go b/staging/src/k8s.io/component-base/metrics/legacyregistry/registry.go index 8a941e7dc3c..2653cf54f32 100644 --- a/staging/src/k8s.io/component-base/metrics/legacyregistry/registry.go +++ b/staging/src/k8s.io/component-base/metrics/legacyregistry/registry.go @@ -17,6 +17,7 @@ limitations under the License. package legacyregistry import ( + "fmt" apimachineryversion "k8s.io/apimachinery/pkg/version" "k8s.io/component-base/metrics" "sync" @@ -38,15 +39,20 @@ type metricsRegistryFactory struct { // 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 { +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 + globalRegistryFactory.kubeVersion = &ver for _, c := range globalRegistryFactory.registerQueue { err := globalRegistryFactory.globalRegistry.Register(c) if err != nil { diff --git a/staging/src/k8s.io/component-base/metrics/legacyregistry/registry_test.go b/staging/src/k8s.io/component-base/metrics/legacyregistry/registry_test.go index a448bf3332d..fdb4b13c50a 100644 --- a/staging/src/k8s.io/component-base/metrics/legacyregistry/registry_test.go +++ b/staging/src/k8s.io/component-base/metrics/legacyregistry/registry_test.go @@ -27,7 +27,7 @@ import ( ) func init() { - SetRegistryFactoryVersion(&apimachineryversion.Info{ + SetRegistryFactoryVersion(apimachineryversion.Info{ Major: "1", Minor: "15", GitVersion: "v1.15.0-alpha-1.12345", @@ -161,7 +161,7 @@ func TestDeferredRegister(t *testing.T) { t.Errorf("Got err == %v, expected no error", err) } // set the global registry version - errs := SetRegistryFactoryVersion(&apimachineryversion.Info{ + errs := SetRegistryFactoryVersion(apimachineryversion.Info{ Major: "1", Minor: "15", GitVersion: "v1.15.0-alpha-1.12345", @@ -185,7 +185,7 @@ func TestDeferredMustRegister(t *testing.T) { MustRegister(alphaDeprecatedCounter) assert.Panics(t, func() { - SetRegistryFactoryVersion(&apimachineryversion.Info{ + SetRegistryFactoryVersion(apimachineryversion.Info{ Major: "1", Minor: "15", GitVersion: "v1.15.0-alpha-1.12345", diff --git a/staging/src/k8s.io/component-base/metrics/registry.go b/staging/src/k8s.io/component-base/metrics/registry.go index a91dd25a982..1471cd7a8da 100644 --- a/staging/src/k8s.io/component-base/metrics/registry.go +++ b/staging/src/k8s.io/component-base/metrics/registry.go @@ -88,9 +88,9 @@ func (kr *kubeRegistry) Gather() ([]*dto.MetricFamily, error) { // NewKubeRegistry creates a new vanilla Registry without any Collectors // pre-registered. -func NewKubeRegistry(v *apimachineryversion.Info) KubeRegistry { +func NewKubeRegistry(v apimachineryversion.Info) KubeRegistry { return &kubeRegistry{ PromRegistry: prometheus.NewRegistry(), - version: parseVersion(*v), + version: parseVersion(v), } } diff --git a/staging/src/k8s.io/component-base/metrics/registry_test.go b/staging/src/k8s.io/component-base/metrics/registry_test.go index 4c8e071e25d..30ad55b528f 100644 --- a/staging/src/k8s.io/component-base/metrics/registry_test.go +++ b/staging/src/k8s.io/component-base/metrics/registry_test.go @@ -108,7 +108,7 @@ func TestRegister(t *testing.T) { for _, test := range tests { t.Run(test.desc, func(t *testing.T) { - registry := NewKubeRegistry(&apimachineryversion.Info{ + registry := NewKubeRegistry(apimachineryversion.Info{ Major: "1", Minor: "15", GitVersion: "v1.15.0-alpha-1.12345", @@ -179,7 +179,7 @@ func TestMustRegister(t *testing.T) { for _, test := range tests { t.Run(test.desc, func(t *testing.T) { - registry := NewKubeRegistry(&apimachineryversion.Info{ + registry := NewKubeRegistry(apimachineryversion.Info{ Major: "1", Minor: "15", GitVersion: "v1.15.0-alpha-1.12345",