update vendor

Signed-off-by: Roland.Ma <rolandma@yunify.com>
This commit is contained in:
Roland.Ma
2021-08-11 07:10:14 +00:00
parent a18f72b565
commit ea8f47c73a
2901 changed files with 269317 additions and 43103 deletions

View File

@@ -6,5 +6,6 @@ approvers:
- RainbowMango
reviewers:
- sig-instrumentation-reviewers
- YoyinZyc
labels:
- sig/instrumentation

View File

@@ -49,10 +49,10 @@ type StableCollector interface {
// is a convenient assistant for custom collectors.
// It is recommend that inherit BaseStableCollector when implementing custom collectors.
type BaseStableCollector struct {
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
descriptors map[string]*Desc // stores all descriptors by pair<fqName, Desc>, these are collected from DescribeWithStability().
registerable map[string]*Desc // stores registerable 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
}
// DescribeWithStability sends all descriptors to the provided channel.
@@ -64,7 +64,7 @@ func (bsc *BaseStableCollector) DescribeWithStability(ch chan<- *Desc) {
// Describe sends all descriptors to the provided channel.
// It intend to be called by prometheus registry.
func (bsc *BaseStableCollector) Describe(ch chan<- *prometheus.Desc) {
for _, d := range bsc.registrable {
for _, d := range bsc.registerable {
ch <- d.toPrometheusDesc()
}
}
@@ -128,11 +128,11 @@ func (bsc *BaseStableCollector) init(self StableCollector) {
}
func (bsc *BaseStableCollector) trackRegistrableDescriptor(d *Desc) {
if bsc.registrable == nil {
bsc.registrable = make(map[string]*Desc)
if bsc.registerable == nil {
bsc.registerable = make(map[string]*Desc)
}
bsc.registrable[d.fqName] = d
bsc.registerable[d.fqName] = d
}
func (bsc *BaseStableCollector) trackHiddenDescriptor(d *Desc) {
@@ -158,7 +158,7 @@ func (bsc *BaseStableCollector) Create(version *semver.Version, self StableColle
}
}
if len(bsc.registrable) > 0 {
if len(bsc.registerable) > 0 {
return true
}
@@ -173,7 +173,7 @@ func (bsc *BaseStableCollector) ClearState() {
}
bsc.descriptors = nil
bsc.registrable = nil
bsc.registerable = nil
bsc.hidden = nil
bsc.self = nil
}

View File

@@ -17,8 +17,10 @@ limitations under the License.
package metrics
import (
"context"
"github.com/blang/semver"
"github.com/prometheus/client_golang/prometheus"
dto "github.com/prometheus/client_model/go"
)
// Counter is our internal representation for our wrapping struct around prometheus
@@ -30,6 +32,9 @@ type Counter struct {
selfCollector
}
// The implementation of the Metric interface is expected by testutil.GetCounterMetricValue.
var _ Metric = &Counter{}
// 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.
@@ -45,6 +50,22 @@ func NewCounter(opts *CounterOpts) *Counter {
return kc
}
func (c *Counter) Desc() *prometheus.Desc {
return c.metric.Desc()
}
func (c *Counter) Write(to *dto.Metric) error {
return c.metric.Write(to)
}
// Reset resets the underlying prometheus Counter to start counting from 0 again
func (c *Counter) Reset() {
if !c.IsCreated() {
return
}
c.setPrometheusCounter(prometheus.NewCounter(c.CounterOpts.toPromCounterOpts()))
}
// setPrometheusCounter sets the underlying CounterMetric object, i.e. the thing that does the measurement.
func (c *Counter) setPrometheusCounter(counter prometheus.Counter) {
c.CounterMetric = counter
@@ -71,6 +92,11 @@ func (c *Counter) initializeDeprecatedMetric() {
c.initializeMetric()
}
// WithContext allows the normal Counter metric to pass in context. The context is no-op now.
func (c *Counter) WithContext(ctx context.Context) CounterMetric {
return c.CounterMetric
}
// CounterVec is the internal representation of our wrapping struct around prometheus
// counterVecs. CounterVec implements both kubeCollector and CounterVecMetric.
type CounterVec struct {
@@ -86,13 +112,20 @@ type CounterVec struct {
func NewCounterVec(opts *CounterOpts, labels []string) *CounterVec {
opts.StabilityLevel.setDefaults()
fqName := BuildFQName(opts.Namespace, opts.Subsystem, opts.Name)
allowListLock.RLock()
if allowList, ok := labelValueAllowLists[fqName]; ok {
opts.LabelValueAllowLists = allowList
}
allowListLock.RUnlock()
cv := &CounterVec{
CounterVec: noopCounterVec,
CounterOpts: opts,
originalLabels: labels,
lazyMetric: lazyMetric{},
}
cv.lazyInit(cv, BuildFQName(opts.Namespace, opts.Subsystem, opts.Name))
cv.lazyInit(cv, fqName)
return cv
}
@@ -132,6 +165,9 @@ func (v *CounterVec) WithLabelValues(lvs ...string) CounterMetric {
if !v.IsCreated() {
return noop // return no-op counter
}
if v.LabelValueAllowLists != nil {
v.LabelValueAllowLists.ConstrainToAllowedList(v.originalLabels, lvs)
}
return v.CounterVec.WithLabelValues(lvs...)
}
@@ -143,6 +179,9 @@ func (v *CounterVec) With(labels map[string]string) CounterMetric {
if !v.IsCreated() {
return noop // return no-op counter
}
if v.LabelValueAllowLists != nil {
v.LabelValueAllowLists.ConstrainLabelMap(labels)
}
return v.CounterVec.With(labels)
}
@@ -168,3 +207,27 @@ func (v *CounterVec) Reset() {
v.CounterVec.Reset()
}
// WithContext returns wrapped CounterVec with context
func (v *CounterVec) WithContext(ctx context.Context) *CounterVecWithContext {
return &CounterVecWithContext{
ctx: ctx,
CounterVec: *v,
}
}
// CounterVecWithContext is the wrapper of CounterVec with context.
type CounterVecWithContext struct {
CounterVec
ctx context.Context
}
// WithLabelValues is the wrapper of CounterVec.WithLabelValues.
func (vc *CounterVecWithContext) WithLabelValues(lvs ...string) CounterMetric {
return vc.CounterVec.WithLabelValues(lvs...)
}
// With is the wrapper of CounterVec.With.
func (vc *CounterVecWithContext) With(labels map[string]string) CounterMetric {
return vc.CounterVec.With(labels)
}

View File

@@ -23,7 +23,7 @@ import (
"github.com/blang/semver"
"github.com/prometheus/client_golang/prometheus"
"k8s.io/klog"
"k8s.io/klog/v2"
)
// Desc is a prometheus.Desc extension.

View File

@@ -17,6 +17,7 @@ limitations under the License.
package metrics
import (
"context"
"github.com/blang/semver"
"github.com/prometheus/client_golang/prometheus"
@@ -73,6 +74,11 @@ func (g *Gauge) initializeDeprecatedMetric() {
g.initializeMetric()
}
// WithContext allows the normal Gauge metric to pass in context. The context is no-op now.
func (g *Gauge) WithContext(ctx context.Context) GaugeMetric {
return g.GaugeMetric
}
// GaugeVec is the internal representation of our wrapping struct around prometheus
// gaugeVecs. kubeGaugeVec implements both kubeCollector and KubeGaugeVec.
type GaugeVec struct {
@@ -88,13 +94,20 @@ type GaugeVec struct {
func NewGaugeVec(opts *GaugeOpts, labels []string) *GaugeVec {
opts.StabilityLevel.setDefaults()
fqName := BuildFQName(opts.Namespace, opts.Subsystem, opts.Name)
allowListLock.RLock()
if allowList, ok := labelValueAllowLists[fqName]; ok {
opts.LabelValueAllowLists = allowList
}
allowListLock.RUnlock()
cv := &GaugeVec{
GaugeVec: noopGaugeVec,
GaugeOpts: opts,
originalLabels: labels,
lazyMetric: lazyMetric{},
}
cv.lazyInit(cv, BuildFQName(opts.Namespace, opts.Subsystem, opts.Name))
cv.lazyInit(cv, fqName)
return cv
}
@@ -133,6 +146,9 @@ func (v *GaugeVec) WithLabelValues(lvs ...string) GaugeMetric {
if !v.IsCreated() {
return noop // return no-op gauge
}
if v.LabelValueAllowLists != nil {
v.LabelValueAllowLists.ConstrainToAllowedList(v.originalLabels, lvs)
}
return v.GaugeVec.WithLabelValues(lvs...)
}
@@ -144,6 +160,9 @@ func (v *GaugeVec) With(labels map[string]string) GaugeMetric {
if !v.IsCreated() {
return noop // return no-op gauge
}
if v.LabelValueAllowLists != nil {
v.LabelValueAllowLists.ConstrainLabelMap(labels)
}
return v.GaugeVec.With(labels)
}
@@ -191,3 +210,27 @@ func NewGaugeFunc(opts GaugeOpts, function func() float64) GaugeFunc {
return newGaugeFunc(opts, function, v)
}
// WithContext returns wrapped GaugeVec with context
func (v *GaugeVec) WithContext(ctx context.Context) *GaugeVecWithContext {
return &GaugeVecWithContext{
ctx: ctx,
GaugeVec: *v,
}
}
// GaugeVecWithContext is the wrapper of GaugeVec with context.
type GaugeVecWithContext struct {
GaugeVec
ctx context.Context
}
// WithLabelValues is the wrapper of GaugeVec.WithLabelValues.
func (vc *GaugeVecWithContext) WithLabelValues(lvs ...string) GaugeMetric {
return vc.GaugeVec.WithLabelValues(lvs...)
}
// With is the wrapper of GaugeVec.With.
func (vc *GaugeVecWithContext) With(labels map[string]string) GaugeMetric {
return vc.GaugeVec.With(labels)
}

View File

@@ -17,6 +17,7 @@ limitations under the License.
package metrics
import (
"context"
"github.com/blang/semver"
"github.com/prometheus/client_golang/prometheus"
)
@@ -83,6 +84,11 @@ func (h *Histogram) initializeDeprecatedMetric() {
h.initializeMetric()
}
// WithContext allows the normal Histogram metric to pass in context. The context is no-op now.
func (h *Histogram) WithContext(ctx context.Context) ObserverMetric {
return h.ObserverMetric
}
// HistogramVec is the internal representation of our wrapping struct around prometheus
// histogramVecs.
type HistogramVec struct {
@@ -98,13 +104,20 @@ type HistogramVec struct {
func NewHistogramVec(opts *HistogramOpts, labels []string) *HistogramVec {
opts.StabilityLevel.setDefaults()
fqName := BuildFQName(opts.Namespace, opts.Subsystem, opts.Name)
allowListLock.RLock()
if allowList, ok := labelValueAllowLists[fqName]; ok {
opts.LabelValueAllowLists = allowList
}
allowListLock.RUnlock()
v := &HistogramVec{
HistogramVec: noopHistogramVec,
HistogramOpts: opts,
originalLabels: labels,
lazyMetric: lazyMetric{},
}
v.lazyInit(v, BuildFQName(opts.Namespace, opts.Subsystem, opts.Name))
v.lazyInit(v, fqName)
return v
}
@@ -139,6 +152,9 @@ func (v *HistogramVec) WithLabelValues(lvs ...string) ObserverMetric {
if !v.IsCreated() {
return noop
}
if v.LabelValueAllowLists != nil {
v.LabelValueAllowLists.ConstrainToAllowedList(v.originalLabels, lvs)
}
return v.HistogramVec.WithLabelValues(lvs...)
}
@@ -150,6 +166,9 @@ func (v *HistogramVec) With(labels map[string]string) ObserverMetric {
if !v.IsCreated() {
return noop
}
if v.LabelValueAllowLists != nil {
v.LabelValueAllowLists.ConstrainLabelMap(labels)
}
return v.HistogramVec.With(labels)
}
@@ -175,3 +194,27 @@ func (v *HistogramVec) Reset() {
v.HistogramVec.Reset()
}
// WithContext returns wrapped HistogramVec with context
func (v *HistogramVec) WithContext(ctx context.Context) *HistogramVecWithContext {
return &HistogramVecWithContext{
ctx: ctx,
HistogramVec: *v,
}
}
// HistogramVecWithContext is the wrapper of HistogramVec with context.
type HistogramVecWithContext struct {
HistogramVec
ctx context.Context
}
// WithLabelValues is the wrapper of HistogramVec.WithLabelValues.
func (vc *HistogramVecWithContext) WithLabelValues(lvs ...string) ObserverMetric {
return vc.HistogramVec.WithLabelValues(lvs...)
}
// With is the wrapper of HistogramVec.With.
func (vc *HistogramVecWithContext) With(labels map[string]string) ObserverMetric {
return vc.HistogramVec.With(labels)
}

View File

@@ -17,6 +17,7 @@ limitations under the License.
package metrics
import (
"io"
"net/http"
"github.com/prometheus/client_golang/prometheus/promhttp"
@@ -61,3 +62,16 @@ func (ho *HandlerOpts) toPromhttpHandlerOpts() promhttp.HandlerOpts {
func HandlerFor(reg Gatherer, opts HandlerOpts) http.Handler {
return promhttp.HandlerFor(reg, opts.toPromhttpHandlerOpts())
}
// HandlerWithReset return an http.Handler with Reset
func HandlerWithReset(reg KubeRegistry, opts HandlerOpts) http.Handler {
defaultHandler := promhttp.HandlerFor(reg, opts.toPromhttpHandlerOpts())
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodDelete {
reg.Reset()
io.WriteString(w, "metrics reset\n")
return
}
defaultHandler.ServeHTTP(w, r)
})
}

View File

@@ -29,6 +29,18 @@ var (
defaultRegistry = metrics.NewKubeRegistry()
// DefaultGatherer exposes the global registry gatherer
DefaultGatherer metrics.Gatherer = defaultRegistry
// Reset calls reset on the global registry
Reset = defaultRegistry.Reset
// MustRegister registers registerable metrics but uses the global registry.
MustRegister = defaultRegistry.MustRegister
// RawMustRegister registers prometheus collectors but uses the global registry, this
// bypasses the metric stability framework
//
// Deprecated
RawMustRegister = defaultRegistry.RawMustRegister
// Register registers a collectable metric but uses the global registry
Register = defaultRegistry.Register
)
func init() {
@@ -46,23 +58,12 @@ func Handler() http.Handler {
return promhttp.InstrumentMetricHandler(prometheus.DefaultRegisterer, promhttp.HandlerFor(defaultRegistry, promhttp.HandlerOpts{}))
}
// Register registers a collectable metric but uses the global registry
func Register(c metrics.Registerable) error {
err := defaultRegistry.Register(c)
return err
}
// MustRegister registers registerable metrics but uses the global registry.
func MustRegister(cs ...metrics.Registerable) {
defaultRegistry.MustRegister(cs...)
}
// RawMustRegister registers prometheus collectors but uses the global registry, this
// bypasses the metric stability framework
//
// Deprecated
func RawMustRegister(cs ...prometheus.Collector) {
defaultRegistry.RawMustRegister(cs...)
// HandlerWithReset returns an HTTP handler for the DefaultGatherer but invokes
// registry reset if the http method is DELETE.
func HandlerWithReset() http.Handler {
return promhttp.InstrumentMetricHandler(
prometheus.DefaultRegisterer,
metrics.HandlerWithReset(defaultRegistry, metrics.HandlerOpts{}))
}
// CustomRegister registers a custom collector but uses the global registry.

View File

@@ -23,7 +23,7 @@ import (
"github.com/prometheus/client_golang/prometheus"
dto "github.com/prometheus/client_model/go"
"k8s.io/klog"
"k8s.io/klog/v2"
)
/*
@@ -87,10 +87,24 @@ func (r *lazyMetric) lazyInit(self kubeCollector, fqName string) {
r.self = self
}
// determineDeprecationStatus figures out whether the lazy metric should be deprecated or not.
// preprocessMetric figures out whether the lazy metric should be hidden 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) {
// this code is currently being executed. A metric can be hidden under two conditions:
// 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.
//
// Disclaimer: disabling a metric via a CLI flag has higher precedence than
// deprecation and will override show-hidden-metrics for the explicitly
// disabled metric.
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
}
selfVersion := r.self.DeprecatedVersion()
if selfVersion == nil {
return
@@ -99,6 +113,7 @@ func (r *lazyMetric) determineDeprecationStatus(version semver.Version) {
if selfVersion.LTE(version) {
r.isDeprecated = true
}
if ShouldShowHidden() {
klog.Warningf("Hidden metrics (%s) have been manually overridden, showing this very deprecated metric.", r.fqName)
return
@@ -126,7 +141,7 @@ func (r *lazyMetric) IsDeprecated() bool {
// created.
func (r *lazyMetric) Create(version *semver.Version) bool {
if version != nil {
r.determineDeprecationStatus(*version)
r.preprocessMetric(*version)
}
// let's not create if this metric is slated to be hidden
if r.IsHidden() {
@@ -188,9 +203,6 @@ 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{}

125
vendor/k8s.io/component-base/metrics/options.go generated vendored Normal file
View File

@@ -0,0 +1,125 @@
/*
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"
"regexp"
"github.com/blang/semver"
"github.com/spf13/pflag"
"k8s.io/component-base/version"
)
// Options has all parameters needed for exposing metrics from components
type Options struct {
ShowHiddenMetricsForVersion string
DisabledMetrics []string
AllowListMapping map[string]string
}
// NewOptions returns default metrics options
func NewOptions() *Options {
return &Options{}
}
// Validate validates metrics flags options.
func (o *Options) Validate() []error {
var errs []error
err := validateShowHiddenMetricsVersion(parseVersion(version.Get()), o.ShowHiddenMetricsForVersion)
if err != nil {
errs = append(errs, err)
}
if err := validateAllowMetricLabel(o.AllowListMapping); err != nil {
errs = append(errs, err)
}
if len(errs) == 0 {
return nil
}
return errs
}
// AddFlags adds flags for exposing component metrics.
func (o *Options) AddFlags(fs *pflag.FlagSet) {
if o != nil {
o = NewOptions()
}
fs.StringVar(&o.ShowHiddenMetricsForVersion, "show-hidden-metrics-for-version", o.ShowHiddenMetricsForVersion,
"The previous version for which you want to show hidden metrics. "+
"Only the previous minor version is meaningful, other values will not be allowed. "+
"The format is <major>.<minor>, e.g.: '1.16'. "+
"The purpose of this format is make sure you have the opportunity to notice if the next release hides additional metrics, "+
"rather than being surprised when they are permanently removed in the release after that.")
fs.StringSliceVar(&o.DisabledMetrics,
"disabled-metrics",
o.DisabledMetrics,
"This flag provides an escape hatch for misbehaving metrics. "+
"You must provide the fully qualified metric name in order to disable it. "+
"Disclaimer: disabling metrics is higher in precedence than showing hidden metrics.")
fs.StringToStringVar(&o.AllowListMapping, "allow-metric-labels", o.AllowListMapping,
"The map from metric-label to value allow-list of this label. The key's format is <MetricName>,<LabelName>. "+
"The value's format is <allowed_value>,<allowed_value>..."+
"e.g. metric1,label1='v1,v2,v3', metric1,label2='v1,v2,v3' metric2,label1='v1,v2,v3'.")
}
// Apply applies parameters into global configuration of metrics.
func (o *Options) Apply() {
if o == nil {
return
}
if len(o.ShowHiddenMetricsForVersion) > 0 {
SetShowHidden()
}
// set disabled metrics
for _, metricName := range o.DisabledMetrics {
SetDisabledMetric(metricName)
}
if o.AllowListMapping != nil {
SetLabelAllowListFromCLI(o.AllowListMapping)
}
}
func validateShowHiddenMetricsVersion(currentVersion semver.Version, targetVersionStr string) error {
if targetVersionStr == "" {
return nil
}
validVersionStr := fmt.Sprintf("%d.%d", currentVersion.Major, currentVersion.Minor-1)
if targetVersionStr != validVersionStr {
return fmt.Errorf("--show-hidden-metrics-for-version must be omitted or have the value '%v'. Only the previous minor version is allowed", validVersionStr)
}
return nil
}
func validateAllowMetricLabel(allowListMapping map[string]string) error {
if allowListMapping == nil {
return nil
}
metricNameRegex := `[a-zA-Z_:][a-zA-Z0-9_:]*`
labelRegex := `[a-zA-Z_][a-zA-Z0-9_]*`
for k := range allowListMapping {
reg := regexp.MustCompile(metricNameRegex + `,` + labelRegex)
if reg.FindString(k) != k {
return fmt.Errorf("--allow-metric-labels must has a list of kv pair with format `metricName:labelName=labelValue, labelValue,...`")
}
}
return nil
}

View File

@@ -18,10 +18,17 @@ package metrics
import (
"fmt"
"strings"
"sync"
"time"
"github.com/prometheus/client_golang/prometheus"
"k8s.io/apimachinery/pkg/util/sets"
)
var (
labelValueAllowLists = map[string]*MetricLabelAllowList{}
allowListLock sync.RWMutex
)
// KubeOpts is superset struct for prometheus.Opts. The prometheus Opts structure
@@ -31,15 +38,16 @@ import (
// 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 map[string]string
DeprecatedVersion string
deprecateOnce sync.Once
annotateOnce sync.Once
StabilityLevel StabilityLevel
Namespace string
Subsystem string
Name string
Help string
ConstLabels map[string]string
DeprecatedVersion string
deprecateOnce sync.Once
annotateOnce sync.Once
StabilityLevel StabilityLevel
LabelValueAllowLists *MetricLabelAllowList
}
// BuildFQName joins the given three name components by "_". Empty name
@@ -125,7 +133,7 @@ func (o *GaugeOpts) annotateStabilityLevel() {
// convenience function to allow easy transformation to the prometheus
// counterpart. This will do more once we have a proper label abstraction
func (o GaugeOpts) toPromGaugeOpts() prometheus.GaugeOpts {
func (o *GaugeOpts) toPromGaugeOpts() prometheus.GaugeOpts {
return prometheus.GaugeOpts{
Namespace: o.Namespace,
Subsystem: o.Subsystem,
@@ -140,16 +148,17 @@ func (o GaugeOpts) toPromGaugeOpts() prometheus.GaugeOpts {
// and can safely be left at their zero value, although it is strongly
// encouraged to set a Help string.
type HistogramOpts struct {
Namespace string
Subsystem string
Name string
Help string
ConstLabels map[string]string
Buckets []float64
DeprecatedVersion string
deprecateOnce sync.Once
annotateOnce sync.Once
StabilityLevel StabilityLevel
Namespace string
Subsystem string
Name string
Help string
ConstLabels map[string]string
Buckets []float64
DeprecatedVersion string
deprecateOnce sync.Once
annotateOnce sync.Once
StabilityLevel StabilityLevel
LabelValueAllowLists *MetricLabelAllowList
}
// Modify help description on the metric description.
@@ -169,7 +178,7 @@ func (o *HistogramOpts) annotateStabilityLevel() {
// convenience function to allow easy transformation to the prometheus
// counterpart. This will do more once we have a proper label abstraction
func (o HistogramOpts) toPromHistogramOpts() prometheus.HistogramOpts {
func (o *HistogramOpts) toPromHistogramOpts() prometheus.HistogramOpts {
return prometheus.HistogramOpts{
Namespace: o.Namespace,
Subsystem: o.Subsystem,
@@ -186,19 +195,20 @@ func (o HistogramOpts) toPromHistogramOpts() prometheus.HistogramOpts {
// a help string and to explicitly set the Objectives field to the desired value
// as the default value will change in the upcoming v0.10 of the library.
type SummaryOpts struct {
Namespace string
Subsystem string
Name string
Help string
ConstLabels map[string]string
Objectives map[float64]float64
MaxAge time.Duration
AgeBuckets uint32
BufCap uint32
DeprecatedVersion string
deprecateOnce sync.Once
annotateOnce sync.Once
StabilityLevel StabilityLevel
Namespace string
Subsystem string
Name string
Help string
ConstLabels map[string]string
Objectives map[float64]float64
MaxAge time.Duration
AgeBuckets uint32
BufCap uint32
DeprecatedVersion string
deprecateOnce sync.Once
annotateOnce sync.Once
StabilityLevel StabilityLevel
LabelValueAllowLists *MetricLabelAllowList
}
// Modify help description on the metric description.
@@ -224,7 +234,7 @@ var (
// convenience function to allow easy transformation to the prometheus
// counterpart. This will do more once we have a proper label abstraction
func (o SummaryOpts) toPromSummaryOpts() prometheus.SummaryOpts {
func (o *SummaryOpts) toPromSummaryOpts() prometheus.SummaryOpts {
// we need to retain existing quantile behavior for backwards compatibility,
// so let's do what prometheus used to do prior to v1.
objectives := o.Objectives
@@ -243,3 +253,49 @@ func (o SummaryOpts) toPromSummaryOpts() prometheus.SummaryOpts {
BufCap: o.BufCap,
}
}
type MetricLabelAllowList struct {
labelToAllowList map[string]sets.String
}
func (allowList *MetricLabelAllowList) ConstrainToAllowedList(labelNameList, labelValueList []string) {
for index, value := range labelValueList {
name := labelNameList[index]
if allowValues, ok := allowList.labelToAllowList[name]; ok {
if !allowValues.Has(value) {
labelValueList[index] = "unexpected"
}
}
}
}
func (allowList *MetricLabelAllowList) ConstrainLabelMap(labels map[string]string) {
for name, value := range labels {
if allowValues, ok := allowList.labelToAllowList[name]; ok {
if !allowValues.Has(value) {
labels[name] = "unexpected"
}
}
}
}
func SetLabelAllowListFromCLI(allowListMapping map[string]string) {
allowListLock.Lock()
defer allowListLock.Unlock()
for metricLabelName, labelValues := range allowListMapping {
metricName := strings.Split(metricLabelName, ",")[0]
labelName := strings.Split(metricLabelName, ",")[1]
valueSet := sets.NewString(strings.Split(labelValues, ",")...)
allowList, ok := labelValueAllowLists[metricName]
if ok {
allowList.labelToAllowList[labelName] = valueSet
} else {
labelToAllowList := make(map[string]sets.String)
labelToAllowList[labelName] = valueSet
labelValueAllowLists[metricName] = &MetricLabelAllowList{
labelToAllowList,
}
}
}
}

View File

@@ -17,12 +17,9 @@ limitations under the License.
package metrics
import (
"os"
"time"
"github.com/prometheus/procfs"
"k8s.io/klog"
"k8s.io/klog/v2"
)
var processStartTime = NewGaugeVec(
@@ -43,19 +40,12 @@ func RegisterProcessStartTime(registrationFunc func(Registerable) error) error {
klog.Errorf("Could not get process start time, %v", err)
start = float64(time.Now().Unix())
}
// processStartTime is a lazy metric which only get initialized after registered.
// so we need to register the metric first and then set the value for it
if err = registrationFunc(processStartTime); err != nil {
return err
}
processStartTime.WithLabelValues().Set(start)
return registrationFunc(processStartTime)
}
func getProcessStart() (float64, error) {
pid := os.Getpid()
p, err := procfs.NewProc(pid)
if err != nil {
return 0, err
}
if stat, err := p.NewStat(); err == nil {
return stat.StartTime()
}
return 0, err
return nil
}

View File

@@ -0,0 +1,38 @@
// +build !windows
/*
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 (
"os"
"github.com/prometheus/procfs"
)
func getProcessStart() (float64, error) {
pid := os.Getpid()
p, err := procfs.NewProc(pid)
if err != nil {
return 0, err
}
if stat, err := p.Stat(); err == nil {
return stat.StartTime()
}
return 0, err
}

View File

@@ -0,0 +1,33 @@
// +build windows
/*
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 (
"golang.org/x/sys/windows"
)
func getProcessStart() (float64, error) {
processHandle := windows.CurrentProcess()
var creationTime, exitTime, kernelTime, userTime windows.Filetime
if err := windows.GetProcessTimes(processHandle, &creationTime, &exitTime, &kernelTime, &userTime); err != nil {
return 0, err
}
return float64(creationTime.Nanoseconds() / 1e9), nil
}

View File

@@ -0,0 +1,130 @@
/*
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 workqueue
import (
"k8s.io/client-go/util/workqueue"
k8smetrics "k8s.io/component-base/metrics"
"k8s.io/component-base/metrics/legacyregistry"
)
// Package prometheus sets the workqueue DefaultMetricsFactory to produce
// prometheus metrics. To use this package, you just have to import it.
// Metrics subsystem and keys used by the workqueue.
const (
WorkQueueSubsystem = "workqueue"
DepthKey = "depth"
AddsKey = "adds_total"
QueueLatencyKey = "queue_duration_seconds"
WorkDurationKey = "work_duration_seconds"
UnfinishedWorkKey = "unfinished_work_seconds"
LongestRunningProcessorKey = "longest_running_processor_seconds"
RetriesKey = "retries_total"
)
var (
depth = k8smetrics.NewGaugeVec(&k8smetrics.GaugeOpts{
Subsystem: WorkQueueSubsystem,
Name: DepthKey,
Help: "Current depth of workqueue",
}, []string{"name"})
adds = k8smetrics.NewCounterVec(&k8smetrics.CounterOpts{
Subsystem: WorkQueueSubsystem,
Name: AddsKey,
Help: "Total number of adds handled by workqueue",
}, []string{"name"})
latency = k8smetrics.NewHistogramVec(&k8smetrics.HistogramOpts{
Subsystem: WorkQueueSubsystem,
Name: QueueLatencyKey,
Help: "How long in seconds an item stays in workqueue before being requested.",
Buckets: k8smetrics.ExponentialBuckets(10e-9, 10, 10),
}, []string{"name"})
workDuration = k8smetrics.NewHistogramVec(&k8smetrics.HistogramOpts{
Subsystem: WorkQueueSubsystem,
Name: WorkDurationKey,
Help: "How long in seconds processing an item from workqueue takes.",
Buckets: k8smetrics.ExponentialBuckets(10e-9, 10, 10),
}, []string{"name"})
unfinished = k8smetrics.NewGaugeVec(&k8smetrics.GaugeOpts{
Subsystem: WorkQueueSubsystem,
Name: UnfinishedWorkKey,
Help: "How many seconds of work has done that " +
"is in progress and hasn't been observed by work_duration. Large " +
"values indicate stuck threads. One can deduce the number of stuck " +
"threads by observing the rate at which this increases.",
}, []string{"name"})
longestRunningProcessor = k8smetrics.NewGaugeVec(&k8smetrics.GaugeOpts{
Subsystem: WorkQueueSubsystem,
Name: LongestRunningProcessorKey,
Help: "How many seconds has the longest running " +
"processor for workqueue been running.",
}, []string{"name"})
retries = k8smetrics.NewCounterVec(&k8smetrics.CounterOpts{
Subsystem: WorkQueueSubsystem,
Name: RetriesKey,
Help: "Total number of retries handled by workqueue",
}, []string{"name"})
metrics = []k8smetrics.Registerable{
depth, adds, latency, workDuration, unfinished, longestRunningProcessor, retries,
}
)
type prometheusMetricsProvider struct {
}
func init() {
for _, m := range metrics {
legacyregistry.MustRegister(m)
}
workqueue.SetProvider(prometheusMetricsProvider{})
}
func (prometheusMetricsProvider) NewDepthMetric(name string) workqueue.GaugeMetric {
return depth.WithLabelValues(name)
}
func (prometheusMetricsProvider) NewAddsMetric(name string) workqueue.CounterMetric {
return adds.WithLabelValues(name)
}
func (prometheusMetricsProvider) NewLatencyMetric(name string) workqueue.HistogramMetric {
return latency.WithLabelValues(name)
}
func (prometheusMetricsProvider) NewWorkDurationMetric(name string) workqueue.HistogramMetric {
return workDuration.WithLabelValues(name)
}
func (prometheusMetricsProvider) NewUnfinishedWorkSecondsMetric(name string) workqueue.SettableGaugeMetric {
return unfinished.WithLabelValues(name)
}
func (prometheusMetricsProvider) NewLongestRunningProcessorSecondsMetric(name string) workqueue.SettableGaugeMetric {
return longestRunningProcessor.WithLabelValues(name)
}
func (prometheusMetricsProvider) NewRetriesMetric(name string) workqueue.CounterMetric {
return retries.WithLabelValues(name)
}

View File

@@ -30,10 +30,12 @@ import (
)
var (
showHiddenOnce sync.Once
showHidden atomic.Value
registries []*kubeRegistry // stores all registries created by NewKubeRegistry()
registriesLock sync.RWMutex
showHiddenOnce sync.Once
disabledMetricsLock sync.RWMutex
showHidden atomic.Value
registries []*kubeRegistry // stores all registries created by NewKubeRegistry()
registriesLock sync.RWMutex
disabledMetrics = map[string]struct{}{}
)
// shouldHide be used to check if a specific metric with deprecated version should be hidden
@@ -51,19 +53,6 @@ func shouldHide(currentVersion *semver.Version, deprecatedVersion *semver.Versio
return false
}
func validateShowHiddenMetricsVersion(currentVersion semver.Version, targetVersionStr string) error {
if targetVersionStr == "" {
return nil
}
validVersionStr := fmt.Sprintf("%d.%d", currentVersion.Major, currentVersion.Minor-1)
if targetVersionStr != validVersionStr {
return fmt.Errorf("--show-hidden-metrics-for-version must be omitted or have the value '%v'. Only the previous minor version is allowed", validVersionStr)
}
return nil
}
// ValidateShowHiddenMetricsVersion checks invalid version for which show hidden metrics.
func ValidateShowHiddenMetricsVersion(v string) []error {
err := validateShowHiddenMetricsVersion(parseVersion(version.Get()), v)
@@ -74,6 +63,12 @@ func ValidateShowHiddenMetricsVersion(v string) []error {
return nil
}
func SetDisabledMetric(name string) {
disabledMetricsLock.Lock()
defer disabledMetricsLock.Unlock()
disabledMetrics[name] = struct{}{}
}
// SetShowHidden will enable showing hidden metrics. This will no-opt
// after the initial call
func SetShowHidden() {
@@ -110,17 +105,30 @@ type Registerable interface {
FQName() string
}
type resettable interface {
Reset()
}
// KubeRegistry is an interface which implements a subset of prometheus.Registerer and
// prometheus.Gatherer interfaces
type KubeRegistry interface {
// Deprecated
RawMustRegister(...prometheus.Collector)
// CustomRegister is our internal variant of Prometheus registry.Register
CustomRegister(c StableCollector) error
// CustomMustRegister is our internal variant of Prometheus registry.MustRegister
CustomMustRegister(cs ...StableCollector)
// Register conforms to Prometheus registry.Register
Register(Registerable) error
// MustRegister conforms to Prometheus registry.MustRegister
MustRegister(...Registerable)
// Unregister conforms to Prometheus registry.Unregister
Unregister(collector Collector) bool
// Gather conforms to Prometheus gatherer.Gather
Gather() ([]*dto.MetricFamily, error)
// Reset invokes the Reset() function on all items in the registry
// which are added as resettables.
Reset()
}
// kubeRegistry is a wrapper around a prometheus registry-type object. Upon initialization
@@ -133,6 +141,8 @@ type kubeRegistry struct {
stableCollectors []StableCollector // stores all stable collector
hiddenCollectorsLock sync.RWMutex
stableCollectorsLock sync.RWMutex
resetLock sync.RWMutex
resettables []resettable
}
// Register registers a new Collector to be included in metrics
@@ -142,11 +152,11 @@ type kubeRegistry struct {
// uniqueness criteria described in the documentation of metric.Desc.
func (kr *kubeRegistry) Register(c Registerable) error {
if c.Create(&kr.version) {
defer kr.addResettable(c)
return kr.PromRegistry.Register(c)
}
kr.trackHiddenCollector(c)
return nil
}
@@ -158,6 +168,7 @@ func (kr *kubeRegistry) MustRegister(cs ...Registerable) {
for _, c := range cs {
if c.Create(&kr.version) {
metrics = append(metrics, c)
kr.addResettable(c)
} else {
kr.trackHiddenCollector(c)
}
@@ -168,7 +179,7 @@ func (kr *kubeRegistry) MustRegister(cs ...Registerable) {
// CustomRegister registers a new custom collector.
func (kr *kubeRegistry) CustomRegister(c StableCollector) error {
kr.trackStableCollectors(c)
defer kr.addResettable(c)
if c.Create(&kr.version, c) {
return kr.PromRegistry.Register(c)
}
@@ -180,14 +191,13 @@ func (kr *kubeRegistry) CustomRegister(c StableCollector) error {
// 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) {
kr.addResettable(c)
collectors = append(collectors, c)
}
}
kr.PromRegistry.MustRegister(collectors...)
}
@@ -198,6 +208,19 @@ func (kr *kubeRegistry) CustomMustRegister(cs ...StableCollector) {
// Deprecated
func (kr *kubeRegistry) RawMustRegister(cs ...prometheus.Collector) {
kr.PromRegistry.MustRegister(cs...)
for _, c := range cs {
kr.addResettable(c)
}
}
// addResettable will automatically add our metric to our reset
// list if it satisfies the interface
func (kr *kubeRegistry) addResettable(i interface{}) {
kr.resetLock.Lock()
defer kr.resetLock.Unlock()
if resettable, ok := i.(resettable); ok {
kr.resettables = append(kr.resettables, resettable)
}
}
// Unregister unregisters the Collector that equals the Collector passed
@@ -279,6 +302,15 @@ func (kr *kubeRegistry) enableHiddenStableCollectors() {
kr.CustomMustRegister(cs...)
}
// Reset invokes Reset on all metrics that are resettable.
func (kr *kubeRegistry) Reset() {
kr.resetLock.RLock()
defer kr.resetLock.RUnlock()
for _, r := range kr.resettables {
r.Reset()
}
}
// BuildVersion is a helper function that can be easily mocked.
var BuildVersion = version.Get
@@ -287,6 +319,7 @@ func newKubeRegistry(v apimachineryversion.Info) *kubeRegistry {
PromRegistry: prometheus.NewRegistry(),
version: parseVersion(v),
hiddenCollectors: make(map[string]Registerable),
resettables: make([]resettable, 0),
}
registriesLock.Lock()
@@ -300,6 +333,5 @@ func newKubeRegistry(v apimachineryversion.Info) *kubeRegistry {
// pre-registered.
func NewKubeRegistry() KubeRegistry {
r := newKubeRegistry(BuildVersion())
return r
}

View File

@@ -17,6 +17,7 @@ limitations under the License.
package metrics
import (
"context"
"github.com/blang/semver"
"github.com/prometheus/client_golang/prometheus"
)
@@ -74,6 +75,11 @@ func (s *Summary) initializeDeprecatedMetric() {
s.initializeMetric()
}
// WithContext allows the normal Summary metric to pass in context. The context is no-op now.
func (s *Summary) WithContext(ctx context.Context) ObserverMetric {
return s.ObserverMetric
}
// SummaryVec is the internal representation of our wrapping struct around prometheus
// summaryVecs.
//
@@ -93,12 +99,19 @@ type SummaryVec struct {
func NewSummaryVec(opts *SummaryOpts, labels []string) *SummaryVec {
opts.StabilityLevel.setDefaults()
fqName := BuildFQName(opts.Namespace, opts.Subsystem, opts.Name)
allowListLock.RLock()
if allowList, ok := labelValueAllowLists[fqName]; ok {
opts.LabelValueAllowLists = allowList
}
allowListLock.RUnlock()
v := &SummaryVec{
SummaryOpts: opts,
originalLabels: labels,
lazyMetric: lazyMetric{},
}
v.lazyInit(v, BuildFQName(opts.Namespace, opts.Subsystem, opts.Name))
v.lazyInit(v, fqName)
return v
}
@@ -133,6 +146,9 @@ func (v *SummaryVec) WithLabelValues(lvs ...string) ObserverMetric {
if !v.IsCreated() {
return noop
}
if v.LabelValueAllowLists != nil {
v.LabelValueAllowLists.ConstrainToAllowedList(v.originalLabels, lvs)
}
return v.SummaryVec.WithLabelValues(lvs...)
}
@@ -144,6 +160,9 @@ func (v *SummaryVec) With(labels map[string]string) ObserverMetric {
if !v.IsCreated() {
return noop
}
if v.LabelValueAllowLists != nil {
v.LabelValueAllowLists.ConstrainLabelMap(labels)
}
return v.SummaryVec.With(labels)
}
@@ -169,3 +188,27 @@ func (v *SummaryVec) Reset() {
v.SummaryVec.Reset()
}
// WithContext returns wrapped SummaryVec with context
func (v *SummaryVec) WithContext(ctx context.Context) *SummaryVecWithContext {
return &SummaryVecWithContext{
ctx: ctx,
SummaryVec: *v,
}
}
// SummaryVecWithContext is the wrapper of SummaryVec with context.
type SummaryVecWithContext struct {
SummaryVec
ctx context.Context
}
// WithLabelValues is the wrapper of SummaryVec.WithLabelValues.
func (vc *SummaryVecWithContext) WithLabelValues(lvs ...string) ObserverMetric {
return vc.SummaryVec.WithLabelValues(lvs...)
}
// With is the wrapper of SummaryVec.With.
func (vc *SummaryVecWithContext) With(labels map[string]string) ObserverMetric {
return vc.SummaryVec.With(labels)
}

View File

@@ -43,8 +43,8 @@ type Metrics map[string]model.Samples
// Equal returns true if all metrics are the same as the arguments.
func (m *Metrics) Equal(o Metrics) bool {
leftKeySet := []string{}
rightKeySet := []string{}
var leftKeySet []string
var rightKeySet []string
for k := range *m {
leftKeySet = append(leftKeySet, k)
}
@@ -86,35 +86,22 @@ func ParseMetrics(data string, output *Metrics) error {
continue
}
for _, metric := range v {
name := string(metric.Metric[model.MetricNameLabel])
name := string(metric.Metric[MetricNameLabel])
(*output)[name] = append((*output)[name], metric)
}
}
}
// ExtractMetricSamples parses the prometheus metric samples from the input string.
func ExtractMetricSamples(metricsBlob string) ([]*model.Sample, error) {
dec := expfmt.NewDecoder(strings.NewReader(metricsBlob), expfmt.FmtText)
decoder := expfmt.SampleDecoder{
Dec: dec,
Opts: &expfmt.DecodeOptions{},
}
var samples []*model.Sample
for {
var v model.Vector
if err := decoder.Decode(&v); err != nil {
if err == io.EOF {
// Expected loop termination condition.
return samples, nil
}
return nil, err
}
samples = append(samples, v...)
}
// TextToMetricFamilies reads 'in' as the simple and flat text-based exchange
// format and creates MetricFamily proto messages. It returns the MetricFamily
// proto messages in a map where the metric names are the keys, along with any
// error encountered.
func TextToMetricFamilies(in io.Reader) (map[string]*dto.MetricFamily, error) {
var textParser expfmt.TextParser
return textParser.TextToMetricFamilies(in)
}
// PrintSample returns formated representation of metric Sample
// PrintSample returns formatted representation of metric Sample
func PrintSample(sample *model.Sample) string {
buf := make([]string, 0)
// Id is a VERY special label. For 'normal' container it's useless, but it's necessary
@@ -198,22 +185,22 @@ func GetHistogramFromGatherer(gatherer metrics.Gatherer, metricName string) (His
return Histogram{}, err
}
for _, mFamily := range m {
if mFamily.Name != nil && *mFamily.Name == metricName {
if mFamily.GetName() == metricName {
metricFamily = mFamily
break
}
}
if metricFamily == nil {
return Histogram{}, fmt.Errorf("Metric %q not found", metricName)
return Histogram{}, fmt.Errorf("metric %q not found", metricName)
}
if metricFamily.GetMetric() == nil {
return Histogram{}, fmt.Errorf("Metric %q is empty", metricName)
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{}, fmt.Errorf("metric %q is empty", metricName)
}
return Histogram{
@@ -253,6 +240,10 @@ func bucketQuantile(q float64, buckets []bucket) float64 {
return buckets[0].upperBound * (rank / buckets[0].count)
}
if b == len(buckets)-1 && math.IsInf(buckets[b].upperBound, 1) {
return buckets[len(buckets)-2].upperBound
}
// linear approximation of b-th bucket
brank := rank - buckets[b-1].count
bSize := buckets[b].upperBound - buckets[b-1].upperBound
@@ -264,48 +255,39 @@ func bucketQuantile(q float64, buckets []bucket) float64 {
// 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{}
var buckets []bucket
for _, bckt := range hist.Bucket {
buckets = append(buckets, bucket{
count: float64(*bckt.CumulativeCount),
upperBound: *bckt.UpperBound,
count: float64(bckt.GetCumulativeCount()),
upperBound: bckt.GetUpperBound(),
})
}
// bucketQuantile expects the upper bound of the last bucket to be +inf
// buckets[len(buckets)-1].upperBound = math.Inf(+1)
if len(buckets) == 0 || buckets[len(buckets)-1].upperBound != math.Inf(+1) {
// The list of buckets in dto.Histogram doesn't include the final +Inf bucket, so we
// add it here for the reset of the samples.
buckets = append(buckets, bucket{
count: float64(hist.GetSampleCount()),
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
}
}
return hist.GetSampleSum() / float64(hist.GetSampleCount())
}
// 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 {
if hist.SampleCount == nil || hist.GetSampleCount() == 0 {
return fmt.Errorf("nil or empty histogram SampleCount")
}
if hist.SampleSum == nil || *hist.SampleSum == 0 {
if hist.SampleSum == nil || hist.GetSampleSum() == 0 {
return fmt.Errorf("nil or empty histogram SampleSum")
}
@@ -313,7 +295,7 @@ func (hist *Histogram) Validate() error {
if bckt == nil {
return fmt.Errorf("empty histogram bucket")
}
if bckt.UpperBound == nil || *bckt.UpperBound < 0 {
if bckt.UpperBound == nil || bckt.GetUpperBound() < 0 {
return fmt.Errorf("nil or negative histogram bucket UpperBound")
}
}
@@ -321,29 +303,60 @@ func (hist *Histogram) Validate() error {
return nil
}
// GetGaugeMetricValue extract metric value from GaugeMetric
// GetGaugeMetricValue extracts 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 0, fmt.Errorf("error writing m: %v", err)
}
return metricProto.Gauge.GetValue(), nil
}
// GetCounterMetricValue extract metric value from CounterMetric
// GetCounterMetricValue extracts 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 0, fmt.Errorf("error writing m: %v", err)
}
return metricProto.Counter.GetValue(), nil
}
// GetHistogramMetricValue extract sum of all samples from ObserverMetric
// GetHistogramMetricValue extracts 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 0, fmt.Errorf("error writing m: %v", err)
}
return metricProto.Histogram.GetSampleSum(), nil
}
// GetHistogramMetricCount extracts count of all samples from ObserverMetric
func GetHistogramMetricCount(m metrics.ObserverMetric) (uint64, 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.GetSampleCount(), nil
}
// LabelsMatch returns true if metric has all expected labels otherwise false
func LabelsMatch(metric *dto.Metric, labelFilter map[string]string) bool {
metricLabels := map[string]string{}
for _, labelPair := range metric.Label {
metricLabels[labelPair.GetName()] = labelPair.GetValue()
}
// length comparison then match key to values in the maps
if len(labelFilter) > len(metricLabels) {
return false
}
for labelName, labelValue := range labelFilter {
if value, ok := metricLabels[labelName]; !ok || value != labelValue {
return false
}
}
return true
}

View File

@@ -0,0 +1,156 @@
/*
Copyright 2020 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 testutil
import (
"fmt"
"io"
"strings"
"github.com/prometheus/client_golang/prometheus/testutil/promlint"
)
// exceptionMetrics is an exception list of metrics which violates promlint rules.
//
// The original entries come from the existing metrics when we introduce promlint.
// We setup this list for allow and not fail on the current violations.
// Generally speaking, you need to fix the problem for a new metric rather than add it into the list.
var exceptionMetrics = []string{
// k8s.io/kubernetes/vendor/k8s.io/apiserver/pkg/server/egressselector
"apiserver_egress_dialer_dial_failure_count", // counter metrics should have "_total" suffix
// k8s.io/kubernetes/vendor/k8s.io/apiserver/pkg/server/healthz
"apiserver_request_total", // label names should be written in 'snake_case' not 'camelCase'
// k8s.io/kubernetes/vendor/k8s.io/apiserver/pkg/endpoints/filters
"authenticated_user_requests", // counter metrics should have "_total" suffix
"authentication_attempts", // counter metrics should have "_total" suffix
// kube-apiserver
"aggregator_openapi_v2_regeneration_count",
"apiserver_admission_step_admission_duration_seconds_summary",
"apiserver_current_inflight_requests",
"apiserver_longrunning_gauge",
"get_token_count",
"get_token_fail_count",
"ssh_tunnel_open_count",
"ssh_tunnel_open_fail_count",
// kube-controller-manager
"attachdetach_controller_forced_detaches",
"authenticated_user_requests",
"authentication_attempts",
"get_token_count",
"get_token_fail_count",
"node_collector_evictions_number",
// k8s.io/kubernetes/pkg/kubelet/server/stats
// The two metrics have been deprecated and will be removed in release v1.20+.
"container_cpu_usage_seconds_total", // non-counter metrics should not have "_total" suffix
"node_cpu_usage_seconds_total", // non-counter metrics should not have "_total" suffix
}
// A Problem is an issue detected by a Linter.
type Problem promlint.Problem
func (p *Problem) String() string {
return fmt.Sprintf("%s:%s", p.Metric, p.Text)
}
// A Linter is a Prometheus metrics linter. It identifies issues with metric
// names, types, and metadata, and reports them to the caller.
type Linter struct {
promLinter *promlint.Linter
}
// Lint performs a linting pass, returning a slice of Problems indicating any
// issues found in the metrics stream. The slice is sorted by metric name
// and issue description.
func (l *Linter) Lint() ([]Problem, error) {
promProblems, err := l.promLinter.Lint()
if err != nil {
return nil, err
}
// Ignore problems those in exception list
problems := make([]Problem, 0, len(promProblems))
for i := range promProblems {
if !l.shouldIgnore(promProblems[i].Metric) {
problems = append(problems, Problem(promProblems[i]))
}
}
return problems, nil
}
// shouldIgnore returns true if metric in the exception list, otherwise returns false.
func (l *Linter) shouldIgnore(metricName string) bool {
for i := range exceptionMetrics {
if metricName == exceptionMetrics[i] {
return true
}
}
return false
}
// NewPromLinter creates a new Linter that reads an input stream of Prometheus metrics.
// Only the text exposition format is supported.
func NewPromLinter(r io.Reader) *Linter {
return &Linter{
promLinter: promlint.New(r),
}
}
func mergeProblems(problems []Problem) string {
var problemsMsg []string
for index := range problems {
problemsMsg = append(problemsMsg, problems[index].String())
}
return strings.Join(problemsMsg, ",")
}
// shouldIgnore returns true if metric in the exception list, otherwise returns false.
func shouldIgnore(metricName string) bool {
for i := range exceptionMetrics {
if metricName == exceptionMetrics[i] {
return true
}
}
return false
}
// getLintError will ignore the metrics in exception list and converts lint problem to error.
func getLintError(problems []promlint.Problem) error {
var filteredProblems []Problem
for _, problem := range problems {
if shouldIgnore(problem.Metric) {
continue
}
filteredProblems = append(filteredProblems, Problem(problem))
}
if len(filteredProblems) == 0 {
return nil
}
return fmt.Errorf("lint error: %s", mergeProblems(filteredProblems))
}

View File

@@ -30,6 +30,14 @@ import (
// pedantic Registry. It then does the same as GatherAndCompare, gathering the
// metrics from the pedantic Registry.
func CollectAndCompare(c metrics.Collector, expected io.Reader, metricNames ...string) error {
lintProblems, err := testutil.CollectAndLint(c, metricNames...)
if err != nil {
return err
}
if err := getLintError(lintProblems); err != nil {
return err
}
return testutil.CollectAndCompare(c, expected, metricNames...)
}
@@ -38,6 +46,14 @@ func CollectAndCompare(c metrics.Collector, expected io.Reader, metricNames ...s
// exposition format. If any metricNames are provided, only metrics with those
// names are compared.
func GatherAndCompare(g metrics.Gatherer, expected io.Reader, metricNames ...string) error {
lintProblems, err := testutil.GatherAndLint(g, metricNames...)
if err != nil {
return err
}
if err := getLintError(lintProblems); err != nil {
return err
}
return testutil.GatherAndCompare(g, expected, metricNames...)
}

View File

@@ -25,7 +25,7 @@ var (
Help: "A metric with a constant '1' value labeled by major, minor, git version, git commit, git tree state, build date, Go version, and compiler from which Kubernetes was built, and platform on which it is running.",
StabilityLevel: ALPHA,
},
[]string{"major", "minor", "gitVersion", "gitCommit", "gitTreeState", "buildDate", "goVersion", "compiler", "platform"},
[]string{"major", "minor", "git_version", "git_commit", "git_tree_state", "build_date", "go_version", "compiler", "platform"},
)
)