rebase: update kubernetes to v1.20.0

updated kubernetes packages to latest
release.

Signed-off-by: Madhu Rajanna <madhupr007@gmail.com>
This commit is contained in:
Madhu Rajanna
2020-12-17 17:58:29 +05:30
committed by mergify[bot]
parent 4abe128bd8
commit 83559144b1
1624 changed files with 247222 additions and 160270 deletions

View File

@ -45,6 +45,14 @@ func NewCounter(opts *CounterOpts) *Counter {
return kc
}
// 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

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 (
"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"
)
/*
@ -188,9 +188,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{}

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

@ -0,0 +1,79 @@
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package metrics
import (
"fmt"
"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
}
// NewOptions returns default metrics options
func NewOptions() *Options {
return &Options{}
}
// Validate validates metrics flags options.
func (o *Options) Validate() []error {
err := validateShowHiddenMetricsVersion(parseVersion(version.Get()), o.ShowHiddenMetricsForVersion)
if err != nil {
return []error{err}
}
return nil
}
// 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.")
}
// Apply applies parameters into global configuration of metrics.
func (o *Options) Apply() {
if o != nil && len(o.ShowHiddenMetricsForVersion) > 0 {
SetShowHidden()
}
}
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
}

View File

@ -125,7 +125,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,
@ -169,7 +169,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,
@ -224,7 +224,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

View File

@ -22,7 +22,7 @@ import (
"github.com/prometheus/procfs"
"k8s.io/klog"
"k8s.io/klog/v2"
)
var processStartTime = NewGaugeVec(
@ -43,6 +43,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 have to explicitly create it before setting the label value. Otherwise
// it is a noop.
if !processStartTime.IsCreated() {
processStartTime.initializeMetric()
}
processStartTime.WithLabelValues().Set(start)
return registrationFunc(processStartTime)
}
@ -54,7 +60,7 @@ func getProcessStart() (float64, error) {
return 0, err
}
if stat, err := p.NewStat(); err == nil {
if stat, err := p.Stat(); err == nil {
return stat.StartTime()
}
return 0, err

View File

@ -1,9 +0,0 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- sig-instrumentation-approvers
- logicalhan
reviewers:
- sig-instrumentation-reviewers
labels:
- sig/instrumentation

View File

@ -1,77 +0,0 @@
/*
Copyright 2015 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 ratelimiter
import (
"fmt"
"sync"
"k8s.io/client-go/util/flowcontrol"
"k8s.io/component-base/metrics"
"k8s.io/component-base/metrics/legacyregistry"
)
var (
metricsLock sync.Mutex
rateLimiterMetrics = make(map[string]*rateLimiterMetric)
)
type rateLimiterMetric struct {
metric metrics.GaugeMetric
stopCh chan struct{}
}
func registerRateLimiterMetric(ownerName string) error {
metricsLock.Lock()
defer metricsLock.Unlock()
if _, ok := rateLimiterMetrics[ownerName]; ok {
// only register once in Prometheus. We happen to see an ownerName reused in parallel integration tests.
return nil
}
metric := metrics.NewGauge(&metrics.GaugeOpts{
Name: "rate_limiter_use",
Subsystem: ownerName,
Help: fmt.Sprintf("A metric measuring the saturation of the rate limiter for %v", ownerName),
StabilityLevel: metrics.ALPHA,
})
if err := legacyregistry.Register(metric); err != nil {
return fmt.Errorf("error registering rate limiter usage metric: %v", err)
}
stopCh := make(chan struct{})
rateLimiterMetrics[ownerName] = &rateLimiterMetric{
metric: metric,
stopCh: stopCh,
}
return nil
}
// RegisterMetricAndTrackRateLimiterUsage registers a metric ownerName_rate_limiter_use in prometheus to track
// how much used rateLimiter is and starts a goroutine that updates this metric every updatePeriod
func RegisterMetricAndTrackRateLimiterUsage(ownerName string, rateLimiter flowcontrol.RateLimiter) error {
if err := registerRateLimiterMetric(ownerName); err != nil {
return err
}
// TODO: determine how to track rate limiter saturation
// See discussion at https://go-review.googlesource.com/c/time/+/29958#message-4caffc11669cadd90e2da4c05122cfec50ea6a22
// go wait.Until(func() {
// metricsLock.Lock()
// defer metricsLock.Unlock()
// rateLimiterMetrics[ownerName].metric.Set()
// }, updatePeriod, rateLimiterMetrics[ownerName].stopCh)
return nil
}

View File

@ -51,19 +51,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)
@ -110,17 +97,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 +133,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 +144,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 +160,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 +171,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 +183,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 +200,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 +294,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 +311,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 +325,5 @@ func newKubeRegistry(v apimachineryversion.Info) *kubeRegistry {
// pre-registered.
func NewKubeRegistry() KubeRegistry {
r := newKubeRegistry(BuildVersion())
return r
}

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,24 +255,30 @@ 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)
return hist.GetSampleSum() / float64(hist.GetSampleCount())
}
// Clear clears all fields of the wrapped histogram
@ -301,11 +298,11 @@ func (hist *Histogram) Clear() {
// 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 +310,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")
}
}
@ -325,7 +322,7 @@ func (hist *Histogram) Validate() error {
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
}
@ -334,7 +331,7 @@ func GetGaugeMetricValue(m metrics.GaugeMetric) (float64, error) {
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
}
@ -343,7 +340,29 @@ func GetCounterMetricValue(m metrics.CounterMetric) (float64, error) {
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
}
// 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"},
)
)