Changes to accommodate client-go changes and kube vendor update

to v1.18.0

Signed-off-by: Humble Chirammal <hchiramm@redhat.com>
This commit is contained in:
Humble Chirammal
2020-04-14 12:34:33 +05:30
committed by mergify[bot]
parent 4c96ad3c85
commit 34fc1d847e
1083 changed files with 50505 additions and 155846 deletions

14
vendor/k8s.io/component-base/config/OWNERS generated vendored Normal file
View File

@ -0,0 +1,14 @@
# See the OWNERS docs at https://go.k8s.io/owners
# Disable inheritance as this is an api owners file
options:
no_parent_owners: true
approvers:
- api-approvers
reviewers:
- api-reviewers
- luxas
- mtaufen
- sttts
labels:
- kind/api-change

19
vendor/k8s.io/component-base/config/doc.go generated vendored Normal file
View File

@ -0,0 +1,19 @@
/*
Copyright 2018 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.
*/
// +k8s:deepcopy-gen=package
package config // import "k8s.io/component-base/config"

80
vendor/k8s.io/component-base/config/types.go generated vendored Normal file
View File

@ -0,0 +1,80 @@
/*
Copyright 2018 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 config
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// ClientConnectionConfiguration contains details for constructing a client.
type ClientConnectionConfiguration struct {
// kubeconfig is the path to a KubeConfig file.
Kubeconfig string
// acceptContentTypes defines the Accept header sent by clients when connecting to a server, overriding the
// default value of 'application/json'. This field will control all connections to the server used by a particular
// client.
AcceptContentTypes string
// contentType is the content type used when sending data to the server from this client.
ContentType string
// qps controls the number of queries per second allowed for this connection.
QPS float32
// burst allows extra queries to accumulate when a client is exceeding its rate.
Burst int32
}
// LeaderElectionConfiguration defines the configuration of leader election
// clients for components that can run with leader election enabled.
type LeaderElectionConfiguration struct {
// leaderElect enables a leader election client to gain leadership
// before executing the main loop. Enable this when running replicated
// components for high availability.
LeaderElect bool
// leaseDuration is the duration that non-leader candidates will wait
// after observing a leadership renewal until attempting to acquire
// leadership of a led but unrenewed leader slot. This is effectively the
// maximum duration that a leader can be stopped before it is replaced
// by another candidate. This is only applicable if leader election is
// enabled.
LeaseDuration metav1.Duration
// renewDeadline is the interval between attempts by the acting master to
// renew a leadership slot before it stops leading. This must be less
// than or equal to the lease duration. This is only applicable if leader
// election is enabled.
RenewDeadline metav1.Duration
// retryPeriod is the duration the clients should wait between attempting
// acquisition and renewal of a leadership. This is only applicable if
// leader election is enabled.
RetryPeriod metav1.Duration
// resourceLock indicates the resource object type that will be used to lock
// during leader election cycles.
ResourceLock string
// resourceName indicates the name of resource object that will be used to lock
// during leader election cycles.
ResourceName string
// resourceName indicates the namespace of resource object that will be used to lock
// during leader election cycles.
ResourceNamespace string
}
// DebuggingConfiguration holds configuration for Debugging related features.
type DebuggingConfiguration struct {
// enableProfiling enables profiling via web interface host:port/debug/pprof/
EnableProfiling bool
// enableContentionProfiling enables lock contention profiling, if
// enableProfiling is true.
EnableContentionProfiling bool
}

View File

@ -0,0 +1,72 @@
// +build !ignore_autogenerated
/*
Copyright 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.
*/
// Code generated by deepcopy-gen. DO NOT EDIT.
package config
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClientConnectionConfiguration) DeepCopyInto(out *ClientConnectionConfiguration) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClientConnectionConfiguration.
func (in *ClientConnectionConfiguration) DeepCopy() *ClientConnectionConfiguration {
if in == nil {
return nil
}
out := new(ClientConnectionConfiguration)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DebuggingConfiguration) DeepCopyInto(out *DebuggingConfiguration) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DebuggingConfiguration.
func (in *DebuggingConfiguration) DeepCopy() *DebuggingConfiguration {
if in == nil {
return nil
}
out := new(DebuggingConfiguration)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *LeaderElectionConfiguration) DeepCopyInto(out *LeaderElectionConfiguration) {
*out = *in
out.LeaseDuration = in.LeaseDuration
out.RenewDeadline = in.RenewDeadline
out.RetryPeriod = in.RetryPeriod
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LeaderElectionConfiguration.
func (in *LeaderElectionConfiguration) DeepCopy() *LeaderElectionConfiguration {
if in == nil {
return nil
}
out := new(LeaderElectionConfiguration)
in.DeepCopyInto(out)
return out
}

View File

@ -3,6 +3,7 @@
approvers:
- sig-instrumentation-approvers
- logicalhan
- RainbowMango
reviewers:
- sig-instrumentation-reviewers
labels:

View File

@ -37,14 +37,21 @@ type StableCollector interface {
// Create will initialize all Desc and it intends to be called by registry.
Create(version *semver.Version, self StableCollector) bool
// ClearState will clear all the states marked by Create.
ClearState()
// HiddenMetrics tells the list of hidden metrics with fqName.
HiddenMetrics() []string
}
// BaseStableCollector which implements almost all of the methods defined by StableCollector
// is a convenient assistant for custom collectors.
// It is recommend that inherit BaseStableCollector when implementing custom collectors.
type BaseStableCollector struct {
descriptors []*Desc // stores all Desc collected from DescribeWithStability().
registrable []*Desc // stores registrable Desc(not be hidden), is a subset of descriptors.
descriptors map[string]*Desc // stores all descriptors by pair<fqName, Desc>, these are collected from DescribeWithStability().
registrable map[string]*Desc // stores registrable descriptors by pair<fqName, Desc>, is a subset of descriptors.
hidden map[string]*Desc // stores hidden descriptors by pair<fqName, Desc>, is a subset of descriptors.
self StableCollector
}
@ -88,7 +95,19 @@ func (bsc *BaseStableCollector) Collect(ch chan<- prometheus.Metric) {
}
func (bsc *BaseStableCollector) add(d *Desc) {
bsc.descriptors = append(bsc.descriptors, d)
if len(d.fqName) == 0 {
panic("nameless metrics will be not allowed")
}
if bsc.descriptors == nil {
bsc.descriptors = make(map[string]*Desc)
}
if _, exist := bsc.descriptors[d.fqName]; exist {
panic(fmt.Sprintf("duplicate metrics (%s) will be not allowed", d.fqName))
}
bsc.descriptors[d.fqName] = d
}
// Init intends to be called by registry.
@ -108,6 +127,22 @@ func (bsc *BaseStableCollector) init(self StableCollector) {
}
}
func (bsc *BaseStableCollector) trackRegistrableDescriptor(d *Desc) {
if bsc.registrable == nil {
bsc.registrable = make(map[string]*Desc)
}
bsc.registrable[d.fqName] = d
}
func (bsc *BaseStableCollector) trackHiddenDescriptor(d *Desc) {
if bsc.hidden == nil {
bsc.hidden = make(map[string]*Desc)
}
bsc.hidden[d.fqName] = d
}
// Create intends to be called by registry.
// Create will return true as long as there is one or more metrics not be hidden.
// Otherwise return false, that means the whole collector will be ignored by registry.
@ -115,26 +150,12 @@ func (bsc *BaseStableCollector) Create(version *semver.Version, self StableColle
bsc.init(self)
for _, d := range bsc.descriptors {
if version != nil {
d.determineDeprecationStatus(*version)
d.create(version)
if d.IsHidden() {
bsc.trackHiddenDescriptor(d)
} else {
bsc.trackRegistrableDescriptor(d)
}
d.createOnce.Do(func() {
d.createLock.Lock()
defer d.createLock.Unlock()
if d.IsHidden() {
// do nothing for hidden metrics
} else if d.IsDeprecated() {
d.initializeDeprecatedDesc()
bsc.registrable = append(bsc.registrable, d)
d.isCreated = true
} else {
d.initialize()
bsc.registrable = append(bsc.registrable, d)
d.isCreated = true
}
})
}
if len(bsc.registrable) > 0 {
@ -144,5 +165,26 @@ func (bsc *BaseStableCollector) Create(version *semver.Version, self StableColle
return false
}
// ClearState will clear all the states marked by Create.
// It intends to be used for re-register a hidden metric.
func (bsc *BaseStableCollector) ClearState() {
for _, d := range bsc.descriptors {
d.ClearState()
}
bsc.descriptors = nil
bsc.registrable = nil
bsc.hidden = nil
bsc.self = nil
}
// HiddenMetrics tells the list of hidden metrics with fqName.
func (bsc *BaseStableCollector) HiddenMetrics() (fqNames []string) {
for i := range bsc.hidden {
fqNames = append(fqNames, bsc.hidden[i].fqName)
}
return
}
// Check if our BaseStableCollector implements necessary interface
var _ StableCollector = &BaseStableCollector{}

View File

@ -41,7 +41,7 @@ func NewCounter(opts *CounterOpts) *Counter {
lazyMetric: lazyMetric{},
}
kc.setPrometheusCounter(noop)
kc.lazyInit(kc)
kc.lazyInit(kc, BuildFQName(opts.Namespace, opts.Subsystem, opts.Name))
return kc
}
@ -92,7 +92,7 @@ func NewCounterVec(opts *CounterOpts, labels []string) *CounterVec {
originalLabels: labels,
lazyMetric: lazyMetric{},
}
cv.lazyInit(cv)
cv.lazyInit(cv, BuildFQName(opts.Namespace, opts.Subsystem, opts.Name))
return cv
}

View File

@ -18,7 +18,6 @@ package metrics
import (
"fmt"
"strings"
"sync"
"github.com/blang/semver"
@ -42,7 +41,8 @@ type Desc struct {
variableLabels []string
// promDesc is the descriptor used by every Prometheus Metric.
promDesc *prometheus.Desc
promDesc *prometheus.Desc
annotatedHelp string
// stabilityLevel represents the API guarantees for a given defined metric.
stabilityLevel StabilityLevel
@ -73,6 +73,7 @@ func NewDesc(fqName string, help string, variableLabels []string, constLabels La
d := &Desc{
fqName: fqName,
help: help,
annotatedHelp: help,
variableLabels: variableLabels,
constLabels: constLabels,
stabilityLevel: stabilityLevel,
@ -86,6 +87,7 @@ func NewDesc(fqName string, help string, variableLabels []string, constLabels La
// String formats the Desc as a string.
// The stability metadata maybe annotated in 'HELP' section if called after registry,
// otherwise not.
// e.g. "Desc{fqName: "normal_stable_descriptor", help: "[STABLE] this is a stable descriptor", constLabels: {}, variableLabels: []}"
func (d *Desc) String() string {
if d.isCreated {
return d.promDesc.String()
@ -136,22 +138,76 @@ func (d *Desc) IsDeprecated() bool {
return d.isDeprecated
}
// IsCreated returns if metric has been created.
func (d *Desc) IsCreated() bool {
d.createLock.RLock()
defer d.createLock.RUnlock()
return d.isCreated
}
// create forces the initialization of Desc which has been deferred until
// the point at which this method is invoked. This method will determine whether
// the Desc is deprecated or hidden, no-opting if the Desc should be considered
// hidden. Furthermore, this function no-opts and returns true if Desc is already
// created.
func (d *Desc) create(version *semver.Version) bool {
if version != nil {
d.determineDeprecationStatus(*version)
}
// let's not create if this metric is slated to be hidden
if d.IsHidden() {
return false
}
d.createOnce.Do(func() {
d.createLock.Lock()
defer d.createLock.Unlock()
d.isCreated = true
if d.IsDeprecated() {
d.initializeDeprecatedDesc()
} else {
d.initialize()
}
})
return d.IsCreated()
}
// ClearState will clear all the states marked by Create.
// It intends to be used for re-register a hidden metric.
func (d *Desc) ClearState() {
d.isDeprecated = false
d.isHidden = false
d.isCreated = false
d.markDeprecationOnce = *new(sync.Once)
d.createOnce = *new(sync.Once)
d.deprecateOnce = *new(sync.Once)
d.hideOnce = *new(sync.Once)
d.annotateOnce = *new(sync.Once)
d.annotatedHelp = d.help
d.promDesc = nil
}
func (d *Desc) markDeprecated() {
d.deprecateOnce.Do(func() {
d.help = fmt.Sprintf("(Deprecated since %s) %s", d.deprecatedVersion, d.help)
d.annotatedHelp = fmt.Sprintf("(Deprecated since %s) %s", d.deprecatedVersion, d.annotatedHelp)
})
}
func (d *Desc) annotateStabilityLevel() {
d.annotateOnce.Do(func() {
d.help = fmt.Sprintf("[%v] %v", d.stabilityLevel, d.help)
d.annotatedHelp = fmt.Sprintf("[%v] %v", d.stabilityLevel, d.annotatedHelp)
})
}
func (d *Desc) initialize() {
d.annotateStabilityLevel()
// this actually creates the underlying prometheus desc.
d.promDesc = prometheus.NewDesc(d.fqName, d.help, d.variableLabels, prometheus.Labels(d.constLabels))
d.promDesc = prometheus.NewDesc(d.fqName, d.annotatedHelp, d.variableLabels, prometheus.Labels(d.constLabels))
}
func (d *Desc) initializeDeprecatedDesc() {
@ -165,9 +221,5 @@ func (d *Desc) initializeDeprecatedDesc() {
// 1. Desc `D` is registered to registry 'A' in TestA (Note: `D` maybe created)
// 2. Desc `D` is registered to registry 'B' in TestB (Note: since 'D' has been created once, thus will be ignored by registry 'B')
func (d *Desc) GetRawDesc() *Desc {
// remove stability from help if any
stabilityStr := fmt.Sprintf("[%v] ", d.stabilityLevel)
rawHelp := strings.Replace(d.help, stabilityStr, "", -1)
return NewDesc(d.fqName, rawHelp, d.variableLabels, d.constLabels, d.stabilityLevel, d.deprecatedVersion)
return NewDesc(d.fqName, d.help, d.variableLabels, d.constLabels, d.stabilityLevel, d.deprecatedVersion)
}

View File

@ -43,7 +43,7 @@ func NewGauge(opts *GaugeOpts) *Gauge {
lazyMetric: lazyMetric{},
}
kc.setPrometheusGauge(noop)
kc.lazyInit(kc)
kc.lazyInit(kc, BuildFQName(opts.Namespace, opts.Subsystem, opts.Name))
return kc
}
@ -94,7 +94,7 @@ func NewGaugeVec(opts *GaugeOpts, labels []string) *GaugeVec {
originalLabels: labels,
lazyMetric: lazyMetric{},
}
cv.lazyInit(cv)
cv.lazyInit(cv, BuildFQName(opts.Namespace, opts.Subsystem, opts.Name))
return cv
}

View File

@ -53,7 +53,7 @@ func NewHistogram(opts *HistogramOpts) *Histogram {
lazyMetric: lazyMetric{},
}
h.setPrometheusHistogram(noopMetric{})
h.lazyInit(h)
h.lazyInit(h, BuildFQName(opts.Namespace, opts.Subsystem, opts.Name))
return h
}
@ -104,7 +104,7 @@ func NewHistogramVec(opts *HistogramOpts, labels []string) *HistogramVec {
originalLabels: labels,
lazyMetric: lazyMetric{},
}
v.lazyInit(v)
v.lazyInit(v, BuildFQName(opts.Namespace, opts.Subsystem, opts.Name))
return v
}

View File

@ -65,15 +65,6 @@ func RawMustRegister(cs ...prometheus.Collector) {
defaultRegistry.RawMustRegister(cs...)
}
// RawRegister registers a prometheus collector but uses the global registry, this
// bypasses the metric stability framework
//
// Deprecated
func RawRegister(c prometheus.Collector) error {
err := defaultRegistry.RawRegister(c)
return err
}
// CustomRegister registers a custom collector but uses the global registry.
func CustomRegister(c metrics.StableCollector) error {
err := defaultRegistry.CustomRegister(c)

View File

@ -63,6 +63,7 @@ implements kubeCollector to get deferred registration behavior. You must call la
with the kubeCollector itself as an argument.
*/
type lazyMetric struct {
fqName string
isDeprecated bool
isHidden bool
isCreated bool
@ -81,7 +82,8 @@ func (r *lazyMetric) IsCreated() bool {
// 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) {
func (r *lazyMetric) lazyInit(self kubeCollector, fqName string) {
r.fqName = fqName
r.self = self
}
@ -98,7 +100,7 @@ func (r *lazyMetric) determineDeprecationStatus(version semver.Version) {
r.isDeprecated = true
}
if ShouldShowHidden() {
klog.Warningf("Hidden metrics have been manually overridden, showing this very deprecated metric.")
klog.Warningf("Hidden metrics (%s) have been manually overridden, showing this very deprecated metric.", r.fqName)
return
}
if shouldHide(&version, selfVersion) {
@ -156,6 +158,11 @@ func (r *lazyMetric) ClearState() {
r.createOnce = *(new(sync.Once))
}
// FQName returns the fully-qualified metric name of the collector.
func (r *lazyMetric) FQName() string {
return r.fqName
}
/*
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.
@ -181,6 +188,8 @@ func (c *selfCollector) Collect(ch chan<- prometheus.Metric) {
// no-op vecs for convenience
var noopCounterVec = &prometheus.CounterVec{}
var noopHistogramVec = &prometheus.HistogramVec{}
// lint:ignore U1000 Keep it for future use
var noopSummaryVec = &prometheus.SummaryVec{}
var noopGaugeVec = &prometheus.GaugeVec{}
var noopObserverVec = &noopObserverVector{}

View File

@ -20,29 +20,24 @@ import (
"os"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/procfs"
"k8s.io/klog"
)
var processStartTime = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "process_start_time_seconds",
Help: "Start time of the process since unix epoch in seconds.",
var processStartTime = NewGaugeVec(
&GaugeOpts{
Name: "process_start_time_seconds",
Help: "Start time of the process since unix epoch in seconds.",
StabilityLevel: ALPHA,
},
[]string{},
)
// Registerer is an interface expected by RegisterProcessStartTime in order to register the metric
type Registerer interface {
Register(prometheus.Collector) error
}
// RegisterProcessStartTime registers the process_start_time_seconds to
// a prometheus registry. This metric needs to be included to ensure counter
// data fidelity.
func RegisterProcessStartTime(registrationFunc func(prometheus.Collector) error) error {
func RegisterProcessStartTime(registrationFunc func(Registerable) error) error {
start, err := getProcessStart()
if err != nil {
klog.Errorf("Could not get process start time, %v", err)

View File

@ -83,6 +83,7 @@ func SetShowHidden() {
// re-register collectors that has been hidden in phase of last registry.
for _, r := range registries {
r.enableHiddenCollectors()
r.enableHiddenStableCollectors()
}
})
}
@ -104,20 +105,21 @@ type Registerable interface {
// ClearState will clear all the states marked by Create.
ClearState()
// FQName returns the fully-qualified metric name of the collector.
FQName() string
}
// KubeRegistry is an interface which implements a subset of prometheus.Registerer and
// prometheus.Gatherer interfaces
type KubeRegistry interface {
// Deprecated
RawRegister(prometheus.Collector) error
// Deprecated
RawMustRegister(...prometheus.Collector)
CustomRegister(c StableCollector) error
CustomMustRegister(cs ...StableCollector)
Register(Registerable) error
MustRegister(...Registerable)
Unregister(Registerable) bool
Unregister(collector Collector) bool
Gather() ([]*dto.MetricFamily, error)
}
@ -127,8 +129,10 @@ type KubeRegistry interface {
type kubeRegistry struct {
PromRegistry
version semver.Version
hiddenCollectors []Registerable // stores all collectors that has been hidden
hiddenCollectors map[string]Registerable // stores all collectors that has been hidden
stableCollectors []StableCollector // stores all stable collector
hiddenCollectorsLock sync.RWMutex
stableCollectorsLock sync.RWMutex
}
// Register registers a new Collector to be included in metrics
@ -163,10 +167,11 @@ func (kr *kubeRegistry) MustRegister(cs ...Registerable) {
// CustomRegister registers a new custom collector.
func (kr *kubeRegistry) CustomRegister(c StableCollector) error {
kr.trackStableCollectors(c)
if c.Create(&kr.version, c) {
return kr.PromRegistry.Register(c)
}
return nil
}
@ -174,6 +179,8 @@ func (kr *kubeRegistry) CustomRegister(c StableCollector) error {
// StableCollectors and panics upon the first registration that causes an
// error.
func (kr *kubeRegistry) CustomMustRegister(cs ...StableCollector) {
kr.trackStableCollectors(cs...)
collectors := make([]prometheus.Collector, 0, len(cs))
for _, c := range cs {
if c.Create(&kr.version, c) {
@ -184,15 +191,6 @@ func (kr *kubeRegistry) CustomMustRegister(cs ...StableCollector) {
kr.PromRegistry.MustRegister(collectors...)
}
// RawRegister takes a native prometheus.Collector and registers the collector
// to the registry. This bypasses metrics safety checks, so should only be used
// to register custom prometheus collectors.
//
// Deprecated
func (kr *kubeRegistry) RawRegister(c prometheus.Collector) error {
return kr.PromRegistry.Register(c)
}
// RawMustRegister takes a native prometheus.Collector and registers the collector
// to the registry. This bypasses metrics safety checks, so should only be used
// to register custom prometheus collectors.
@ -208,7 +206,7 @@ func (kr *kubeRegistry) RawMustRegister(cs ...prometheus.Collector) {
// 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 Registerable) bool {
func (kr *kubeRegistry) Unregister(collector Collector) bool {
return kr.PromRegistry.Unregister(collector)
}
@ -228,25 +226,67 @@ func (kr *kubeRegistry) trackHiddenCollector(c Registerable) {
kr.hiddenCollectorsLock.Lock()
defer kr.hiddenCollectorsLock.Unlock()
kr.hiddenCollectors = append(kr.hiddenCollectors, c)
kr.hiddenCollectors[c.FQName()] = c
}
// trackStableCollectors stores all custom collectors.
func (kr *kubeRegistry) trackStableCollectors(cs ...StableCollector) {
kr.stableCollectorsLock.Lock()
defer kr.stableCollectorsLock.Unlock()
kr.stableCollectors = append(kr.stableCollectors, cs...)
}
// enableHiddenCollectors will re-register all of the hidden collectors.
func (kr *kubeRegistry) enableHiddenCollectors() {
if len(kr.hiddenCollectors) == 0 {
return
}
kr.hiddenCollectorsLock.Lock()
defer kr.hiddenCollectorsLock.Unlock()
cs := make([]Registerable, 0, len(kr.hiddenCollectors))
for _, c := range kr.hiddenCollectors {
c.ClearState()
kr.MustRegister(c)
cs = append(cs, c)
}
kr.hiddenCollectors = nil
kr.hiddenCollectorsLock.Unlock()
kr.MustRegister(cs...)
}
// enableHiddenStableCollectors will re-register the stable collectors if there is one or more hidden metrics in it.
// Since we can not register a metrics twice, so we have to unregister first then register again.
func (kr *kubeRegistry) enableHiddenStableCollectors() {
if len(kr.stableCollectors) == 0 {
return
}
kr.stableCollectorsLock.Lock()
cs := make([]StableCollector, 0, len(kr.stableCollectors))
for _, c := range kr.stableCollectors {
if len(c.HiddenMetrics()) > 0 {
kr.Unregister(c) // unregister must happens before clear state, otherwise no metrics would be unregister
c.ClearState()
cs = append(cs, c)
}
}
kr.stableCollectors = nil
kr.stableCollectorsLock.Unlock()
kr.CustomMustRegister(cs...)
}
// BuildVersion is a helper function that can be easily mocked.
var BuildVersion = version.Get
func newKubeRegistry(v apimachineryversion.Info) *kubeRegistry {
r := &kubeRegistry{
PromRegistry: prometheus.NewRegistry(),
version: parseVersion(v),
PromRegistry: prometheus.NewRegistry(),
version: parseVersion(v),
hiddenCollectors: make(map[string]Registerable),
}
registriesLock.Lock()
@ -259,7 +299,7 @@ func newKubeRegistry(v apimachineryversion.Info) *kubeRegistry {
// NewKubeRegistry creates a new vanilla Registry without any Collectors
// pre-registered.
func NewKubeRegistry() KubeRegistry {
r := newKubeRegistry(version.Get())
r := newKubeRegistry(BuildVersion())
return r
}

View File

@ -44,7 +44,7 @@ func NewSummary(opts *SummaryOpts) *Summary {
lazyMetric: lazyMetric{},
}
s.setPrometheusSummary(noopMetric{})
s.lazyInit(s)
s.lazyInit(s, BuildFQName(opts.Namespace, opts.Subsystem, opts.Name))
return s
}
@ -98,7 +98,7 @@ func NewSummaryVec(opts *SummaryOpts, labels []string) *SummaryVec {
originalLabels: labels,
lazyMetric: lazyMetric{},
}
v.lazyInit(v)
v.lazyInit(v, BuildFQName(opts.Namespace, opts.Subsystem, opts.Name))
return v
}

View File

@ -19,11 +19,16 @@ package testutil
import (
"fmt"
"io"
"math"
"reflect"
"sort"
"strings"
dto "github.com/prometheus/client_model/go"
"github.com/prometheus/common/expfmt"
"github.com/prometheus/common/model"
"k8s.io/component-base/metrics"
)
var (
@ -147,3 +152,198 @@ func ComputeHistogramDelta(before, after model.Samples, label model.LabelName) {
func makeKey(a, b model.LabelValue) string {
return string(a) + "___" + string(b)
}
// GetMetricValuesForLabel returns value of metric for a given dimension
func GetMetricValuesForLabel(ms Metrics, metricName, label string) map[string]int64 {
samples, found := ms[metricName]
result := make(map[string]int64, len(samples))
if !found {
return result
}
for _, sample := range samples {
count := int64(sample.Value)
dimensionName := string(sample.Metric[model.LabelName(label)])
result[dimensionName] = count
}
return result
}
// ValidateMetrics verifies if every sample of metric has all expected labels
func ValidateMetrics(metrics Metrics, metricName string, expectedLabels ...string) error {
samples, ok := metrics[metricName]
if !ok {
return fmt.Errorf("metric %q was not found in metrics", metricName)
}
for _, sample := range samples {
for _, l := range expectedLabels {
if _, ok := sample.Metric[model.LabelName(l)]; !ok {
return fmt.Errorf("metric %q is missing label %q, sample: %q", metricName, l, sample.String())
}
}
}
return nil
}
// Histogram wraps prometheus histogram DTO (data transfer object)
type Histogram struct {
*dto.Histogram
}
// GetHistogramFromGatherer collects a metric from a gatherer implementing k8s.io/component-base/metrics.Gatherer interface.
// Used only for testing purposes where we need to gather metrics directly from a running binary (without metrics endpoint).
func GetHistogramFromGatherer(gatherer metrics.Gatherer, metricName string) (Histogram, error) {
var metricFamily *dto.MetricFamily
m, err := gatherer.Gather()
if err != nil {
return Histogram{}, err
}
for _, mFamily := range m {
if mFamily.Name != nil && *mFamily.Name == metricName {
metricFamily = mFamily
break
}
}
if metricFamily == nil {
return Histogram{}, fmt.Errorf("Metric %q not found", metricName)
}
if metricFamily.GetMetric() == nil {
return Histogram{}, fmt.Errorf("Metric %q is empty", metricName)
}
if len(metricFamily.GetMetric()) == 0 {
return Histogram{}, fmt.Errorf("Metric %q is empty", metricName)
}
return Histogram{
// Histograms are stored under the first index (based on observation).
// Given there's only one histogram registered per each metric name, accessing
// the first index is sufficient.
metricFamily.GetMetric()[0].GetHistogram(),
}, nil
}
func uint64Ptr(u uint64) *uint64 {
return &u
}
// Bucket of a histogram
type bucket struct {
upperBound float64
count float64
}
func bucketQuantile(q float64, buckets []bucket) float64 {
if q < 0 {
return math.Inf(-1)
}
if q > 1 {
return math.Inf(+1)
}
if len(buckets) < 2 {
return math.NaN()
}
rank := q * buckets[len(buckets)-1].count
b := sort.Search(len(buckets)-1, func(i int) bool { return buckets[i].count >= rank })
if b == 0 {
return buckets[0].upperBound * (rank / buckets[0].count)
}
// linear approximation of b-th bucket
brank := rank - buckets[b-1].count
bSize := buckets[b].upperBound - buckets[b-1].upperBound
bCount := buckets[b].count - buckets[b-1].count
return buckets[b-1].upperBound + bSize*(brank/bCount)
}
// Quantile computes q-th quantile of a cumulative histogram.
// It's expected the histogram is valid (by calling Validate)
func (hist *Histogram) Quantile(q float64) float64 {
buckets := []bucket{}
for _, bckt := range hist.Bucket {
buckets = append(buckets, bucket{
count: float64(*bckt.CumulativeCount),
upperBound: *bckt.UpperBound,
})
}
// bucketQuantile expects the upper bound of the last bucket to be +inf
// buckets[len(buckets)-1].upperBound = math.Inf(+1)
return bucketQuantile(q, buckets)
}
// Average computes histogram's average value
func (hist *Histogram) Average() float64 {
return *hist.SampleSum / float64(*hist.SampleCount)
}
// Clear clears all fields of the wrapped histogram
func (hist *Histogram) Clear() {
if hist.SampleCount != nil {
*hist.SampleCount = 0
}
if hist.SampleSum != nil {
*hist.SampleSum = 0
}
for _, b := range hist.Bucket {
if b.CumulativeCount != nil {
*b.CumulativeCount = 0
}
}
}
// Validate makes sure the wrapped histogram has all necessary fields set and with valid values.
func (hist *Histogram) Validate() error {
if hist.SampleCount == nil || *hist.SampleCount == 0 {
return fmt.Errorf("nil or empty histogram SampleCount")
}
if hist.SampleSum == nil || *hist.SampleSum == 0 {
return fmt.Errorf("nil or empty histogram SampleSum")
}
for _, bckt := range hist.Bucket {
if bckt == nil {
return fmt.Errorf("empty histogram bucket")
}
if bckt.UpperBound == nil || *bckt.UpperBound < 0 {
return fmt.Errorf("nil or negative histogram bucket UpperBound")
}
}
return nil
}
// GetGaugeMetricValue extract metric value from GaugeMetric
func GetGaugeMetricValue(m metrics.GaugeMetric) (float64, error) {
metricProto := &dto.Metric{}
if err := m.Write(metricProto); err != nil {
return 0, fmt.Errorf("Error writing m: %v", err)
}
return metricProto.Gauge.GetValue(), nil
}
// GetCounterMetricValue extract metric value from CounterMetric
func GetCounterMetricValue(m metrics.CounterMetric) (float64, error) {
metricProto := &dto.Metric{}
if err := m.(metrics.Metric).Write(metricProto); err != nil {
return 0, fmt.Errorf("Error writing m: %v", err)
}
return metricProto.Counter.GetValue(), nil
}
// GetHistogramMetricValue extract sum of all samples from ObserverMetric
func GetHistogramMetricValue(m metrics.ObserverMetric) (float64, error) {
metricProto := &dto.Metric{}
if err := m.(metrics.Metric).Write(metricProto); err != nil {
return 0, fmt.Errorf("Error writing m: %v", err)
}
return metricProto.Histogram.GetSampleSum(), nil
}

View File

@ -17,10 +17,12 @@ limitations under the License.
package testutil
import (
"fmt"
"io"
"github.com/prometheus/client_golang/prometheus/testutil"
apimachineryversion "k8s.io/apimachinery/pkg/version"
"k8s.io/component-base/metrics"
)
@ -48,3 +50,21 @@ func CustomCollectAndCompare(c metrics.StableCollector, expected io.Reader, metr
return GatherAndCompare(registry, expected, metricNames...)
}
// NewFakeKubeRegistry creates a fake `KubeRegistry` that takes the input version as `build in version`.
// It should only be used in testing scenario especially for the deprecated metrics.
// The input version format should be `major.minor.patch`, e.g. '1.18.0'.
func NewFakeKubeRegistry(ver string) metrics.KubeRegistry {
backup := metrics.BuildVersion
defer func() {
metrics.BuildVersion = backup
}()
metrics.BuildVersion = func() apimachineryversion.Info {
return apimachineryversion.Info{
GitVersion: fmt.Sprintf("v%s-alpha+1.12345", ver),
}
}
return metrics.NewKubeRegistry()
}