2020-01-14 10:38:55 +00:00
|
|
|
/*
|
|
|
|
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 (
|
|
|
|
"sync"
|
|
|
|
|
2022-05-05 02:47:06 +00:00
|
|
|
"github.com/blang/semver/v4"
|
2020-01-14 10:38:55 +00:00
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
|
|
dto "github.com/prometheus/client_model/go"
|
2022-08-24 02:24:25 +00:00
|
|
|
promext "k8s.io/component-base/metrics/prometheusextension"
|
2020-01-14 10:38:55 +00:00
|
|
|
|
2020-12-17 12:28:29 +00:00
|
|
|
"k8s.io/klog/v2"
|
2020-01-14 10:38:55 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
/*
|
|
|
|
kubeCollector extends the prometheus.Collector interface to allow customization of the metric
|
|
|
|
registration process. Defer metric initialization until Create() is called, which then
|
|
|
|
delegates to the underlying metric's initializeMetric or initializeDeprecatedMetric
|
|
|
|
method call depending on whether the metric is deprecated or not.
|
|
|
|
*/
|
|
|
|
type kubeCollector interface {
|
|
|
|
Collector
|
|
|
|
lazyKubeMetric
|
|
|
|
DeprecatedVersion() *semver.Version
|
|
|
|
// Each collector metric should provide an initialization function
|
|
|
|
// for both deprecated and non-deprecated variants of a metric. This
|
|
|
|
// is necessary since metric instantiation will be deferred
|
|
|
|
// until the metric is actually registered somewhere.
|
|
|
|
initializeMetric()
|
|
|
|
initializeDeprecatedMetric()
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
lazyKubeMetric defines our metric registration interface. lazyKubeMetric objects are expected
|
|
|
|
to lazily instantiate metrics (i.e defer metric instantiation until when
|
|
|
|
the Create() function is explicitly called).
|
|
|
|
*/
|
|
|
|
type lazyKubeMetric interface {
|
|
|
|
Create(*semver.Version) bool
|
|
|
|
IsCreated() bool
|
|
|
|
IsHidden() bool
|
|
|
|
IsDeprecated() bool
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
lazyMetric implements lazyKubeMetric. A lazy metric is lazy because it waits until metric
|
|
|
|
registration time before instantiation. Add it as an anonymous field to a struct that
|
|
|
|
implements kubeCollector to get deferred registration behavior. You must call lazyInit
|
|
|
|
with the kubeCollector itself as an argument.
|
|
|
|
*/
|
|
|
|
type lazyMetric struct {
|
2020-04-14 07:04:33 +00:00
|
|
|
fqName string
|
2020-01-14 10:38:55 +00:00
|
|
|
isDeprecated bool
|
|
|
|
isHidden bool
|
|
|
|
isCreated bool
|
|
|
|
createLock sync.RWMutex
|
|
|
|
markDeprecationOnce sync.Once
|
|
|
|
createOnce sync.Once
|
|
|
|
self kubeCollector
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *lazyMetric) IsCreated() bool {
|
|
|
|
r.createLock.RLock()
|
|
|
|
defer r.createLock.RUnlock()
|
|
|
|
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.
|
2020-04-14 07:04:33 +00:00
|
|
|
func (r *lazyMetric) lazyInit(self kubeCollector, fqName string) {
|
|
|
|
r.fqName = fqName
|
2020-01-14 10:38:55 +00:00
|
|
|
r.self = self
|
|
|
|
}
|
|
|
|
|
2021-06-25 04:59:51 +00:00
|
|
|
// preprocessMetric figures out whether the lazy metric should be hidden or not.
|
2020-01-14 10:38:55 +00:00
|
|
|
// This method takes a Version argument which should be the version of the binary in which
|
2021-06-25 04:59:51 +00:00
|
|
|
// this code is currently being executed. A metric can be hidden under two conditions:
|
2022-08-24 02:24:25 +00:00
|
|
|
// 1. if the metric is deprecated and is outside the grace period (i.e. has been
|
|
|
|
// deprecated for more than one release
|
|
|
|
// 2. if the metric is manually disabled via a CLI flag.
|
2021-06-25 04:59:51 +00:00
|
|
|
//
|
|
|
|
// Disclaimer: disabling a metric via a CLI flag has higher precedence than
|
2022-08-24 02:24:25 +00:00
|
|
|
//
|
|
|
|
// deprecation and will override show-hidden-metrics for the explicitly
|
|
|
|
// disabled metric.
|
2021-06-25 04:59:51 +00:00
|
|
|
func (r *lazyMetric) preprocessMetric(version semver.Version) {
|
|
|
|
disabledMetricsLock.RLock()
|
|
|
|
defer disabledMetricsLock.RUnlock()
|
|
|
|
// disabling metrics is higher in precedence than showing hidden metrics
|
|
|
|
if _, ok := disabledMetrics[r.fqName]; ok {
|
|
|
|
r.isHidden = true
|
|
|
|
return
|
|
|
|
}
|
2020-01-14 10:38:55 +00:00
|
|
|
selfVersion := r.self.DeprecatedVersion()
|
|
|
|
if selfVersion == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
r.markDeprecationOnce.Do(func() {
|
|
|
|
if selfVersion.LTE(version) {
|
|
|
|
r.isDeprecated = true
|
|
|
|
}
|
2021-06-25 04:59:51 +00:00
|
|
|
|
2020-01-14 10:38:55 +00:00
|
|
|
if ShouldShowHidden() {
|
2020-04-14 07:04:33 +00:00
|
|
|
klog.Warningf("Hidden metrics (%s) have been manually overridden, showing this very deprecated metric.", r.fqName)
|
2020-01-14 10:38:55 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
if shouldHide(&version, selfVersion) {
|
|
|
|
// TODO(RainbowMango): Remove this log temporarily. https://github.com/kubernetes/kubernetes/issues/85369
|
|
|
|
// klog.Warningf("This metric has been deprecated for more than one release, hiding.")
|
|
|
|
r.isHidden = true
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *lazyMetric) IsHidden() bool {
|
|
|
|
return r.isHidden
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *lazyMetric) IsDeprecated() bool {
|
|
|
|
return r.isDeprecated
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create forces the initialization of metric which has been deferred until
|
|
|
|
// the point at which this method is invoked. This method will determine whether
|
|
|
|
// the metric is deprecated or hidden, no-opting if the metric should be considered
|
|
|
|
// hidden. Furthermore, this function no-opts and returns true if metric is already
|
|
|
|
// created.
|
|
|
|
func (r *lazyMetric) Create(version *semver.Version) bool {
|
|
|
|
if version != nil {
|
2021-06-25 04:59:51 +00:00
|
|
|
r.preprocessMetric(*version)
|
2020-01-14 10:38:55 +00:00
|
|
|
}
|
|
|
|
// let's not create if this metric is slated to be hidden
|
|
|
|
if r.IsHidden() {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
r.createOnce.Do(func() {
|
|
|
|
r.createLock.Lock()
|
|
|
|
defer r.createLock.Unlock()
|
|
|
|
r.isCreated = true
|
|
|
|
if r.IsDeprecated() {
|
|
|
|
r.self.initializeDeprecatedMetric()
|
|
|
|
} else {
|
|
|
|
r.self.initializeMetric()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
return r.IsCreated()
|
|
|
|
}
|
|
|
|
|
|
|
|
// ClearState will clear all the states marked by Create.
|
|
|
|
// It intends to be used for re-register a hidden metric.
|
|
|
|
func (r *lazyMetric) ClearState() {
|
|
|
|
r.createLock.Lock()
|
|
|
|
defer r.createLock.Unlock()
|
|
|
|
|
|
|
|
r.isDeprecated = false
|
|
|
|
r.isHidden = false
|
|
|
|
r.isCreated = false
|
2022-05-05 02:47:06 +00:00
|
|
|
r.markDeprecationOnce = sync.Once{}
|
|
|
|
r.createOnce = sync.Once{}
|
2020-01-14 10:38:55 +00:00
|
|
|
}
|
|
|
|
|
2020-04-14 07:04:33 +00:00
|
|
|
// FQName returns the fully-qualified metric name of the collector.
|
|
|
|
func (r *lazyMetric) FQName() string {
|
|
|
|
return r.fqName
|
|
|
|
}
|
|
|
|
|
2020-01-14 10:38:55 +00:00
|
|
|
/*
|
|
|
|
This code is directly lifted from the prometheus codebase. It's a convenience struct which
|
|
|
|
allows you satisfy the Collector interface automatically if you already satisfy the Metric interface.
|
|
|
|
|
|
|
|
For reference: https://github.com/prometheus/client_golang/blob/v0.9.2/prometheus/collector.go#L98-L120
|
|
|
|
*/
|
|
|
|
type selfCollector struct {
|
|
|
|
metric prometheus.Metric
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *selfCollector) initSelfCollection(m prometheus.Metric) {
|
|
|
|
c.metric = m
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *selfCollector) Describe(ch chan<- *prometheus.Desc) {
|
|
|
|
ch <- c.metric.Desc()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *selfCollector) Collect(ch chan<- prometheus.Metric) {
|
|
|
|
ch <- c.metric
|
|
|
|
}
|
|
|
|
|
|
|
|
// no-op vecs for convenience
|
|
|
|
var noopCounterVec = &prometheus.CounterVec{}
|
|
|
|
var noopHistogramVec = &prometheus.HistogramVec{}
|
2022-08-24 02:24:25 +00:00
|
|
|
var noopTimingHistogramVec = &promext.TimingHistogramVec{}
|
2020-01-14 10:38:55 +00:00
|
|
|
var noopGaugeVec = &prometheus.GaugeVec{}
|
|
|
|
var noopObserverVec = &noopObserverVector{}
|
|
|
|
|
|
|
|
// just use a convenience struct for all the no-ops
|
|
|
|
var noop = &noopMetric{}
|
|
|
|
|
|
|
|
type noopMetric struct{}
|
|
|
|
|
2022-08-24 02:24:25 +00:00
|
|
|
func (noopMetric) Inc() {}
|
|
|
|
func (noopMetric) Add(float64) {}
|
|
|
|
func (noopMetric) Dec() {}
|
|
|
|
func (noopMetric) Set(float64) {}
|
|
|
|
func (noopMetric) Sub(float64) {}
|
|
|
|
func (noopMetric) Observe(float64) {}
|
|
|
|
func (noopMetric) ObserveWithWeight(float64, uint64) {}
|
|
|
|
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) {}
|
2020-01-14 10:38:55 +00:00
|
|
|
|
|
|
|
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) {}
|