diff --git a/staging/src/k8s.io/apiserver/go.mod b/staging/src/k8s.io/apiserver/go.mod index 15160933af1..4675d0c9a50 100644 --- a/staging/src/k8s.io/apiserver/go.mod +++ b/staging/src/k8s.io/apiserver/go.mod @@ -27,7 +27,6 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 github.com/pkg/errors v0.9.1 github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021 // indirect - github.com/prometheus/client_model v0.2.0 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.4.0 go.etcd.io/etcd v0.5.0-alpha.5.0.20200401174654-e694b7bb0875 diff --git a/staging/src/k8s.io/apiserver/pkg/admission/metrics/BUILD b/staging/src/k8s.io/apiserver/pkg/admission/metrics/BUILD index 98e129cb9d5..22036f9b957 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/metrics/BUILD +++ b/staging/src/k8s.io/apiserver/pkg/admission/metrics/BUILD @@ -26,7 +26,7 @@ go_test( "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", "//staging/src/k8s.io/component-base/metrics/legacyregistry:go_default_library", - "//vendor/github.com/prometheus/client_model/go:go_default_library", + "//staging/src/k8s.io/component-base/metrics/testutil:go_default_library", ], ) diff --git a/staging/src/k8s.io/apiserver/pkg/admission/metrics/testutil_test.go b/staging/src/k8s.io/apiserver/pkg/admission/metrics/testutil_test.go index b31739a6c4a..121778b8e25 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/metrics/testutil_test.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/metrics/testutil_test.go @@ -19,43 +19,36 @@ package metrics import ( "testing" - ptype "github.com/prometheus/client_model/go" "k8s.io/component-base/metrics/legacyregistry" + "k8s.io/component-base/metrics/testutil" ) -func labelsMatch(metric *ptype.Metric, labelFilter map[string]string) bool { - for _, lp := range metric.GetLabel() { - if value, ok := labelFilter[lp.GetName()]; ok && lp.GetValue() != value { - return false - } - } - return true -} - // expectFindMetric find a metric with the given name nad labels or reports a fatal test error. -func expectFindMetric(t *testing.T, name string, expectedLabels map[string]string) *ptype.Metric { +func expectFindMetric(t *testing.T, name string, expectedLabels map[string]string) { metrics, err := legacyregistry.DefaultGatherer.Gather() if err != nil { t.Fatalf("Failed to gather metrics: %s", err) } + if len(metrics) == 0 { + t.Fatalf("No metric found with name %s and labels %#+v", name, expectedLabels) + } + for _, mf := range metrics { if mf.GetName() == name { for _, metric := range mf.GetMetric() { - if labelsMatch(metric, expectedLabels) { + if testutil.LabelsMatch(metric, expectedLabels) { gotLabelCount := len(metric.GetLabel()) wantLabelCount := len(expectedLabels) if wantLabelCount != gotLabelCount { t.Errorf("Got metric with %d labels, but wanted %d labels. Wanted %#+v for %s", gotLabelCount, wantLabelCount, expectedLabels, metric.String()) + continue } - return metric } } } } - t.Fatalf("No metric found with name %s and labels %#+v", name, expectedLabels) - return nil } // expectHistogramCountTotal ensures that the sum of counts of metrics matching the labelFilter is as @@ -72,7 +65,7 @@ func expectHistogramCountTotal(t *testing.T, name string, labelFilter map[string continue // Ignore other metrics. } for _, metric := range mf.GetMetric() { - if !labelsMatch(metric, labelFilter) { + if !testutil.LabelsMatch(metric, labelFilter) { continue } counterSum += int(metric.GetHistogram().GetSampleCount()) @@ -104,7 +97,7 @@ func expectCounterValue(t *testing.T, name string, labelFilter map[string]string continue // Ignore other metrics. } for _, metric := range mf.GetMetric() { - if !labelsMatch(metric, labelFilter) { + if !testutil.LabelsMatch(metric, labelFilter) { continue } counterSum += int(metric.GetCounter().GetValue()) diff --git a/staging/src/k8s.io/component-base/metrics/testutil/metrics.go b/staging/src/k8s.io/component-base/metrics/testutil/metrics.go index a3ae816b41a..9d3a1c897a1 100644 --- a/staging/src/k8s.io/component-base/metrics/testutil/metrics.go +++ b/staging/src/k8s.io/component-base/metrics/testutil/metrics.go @@ -360,3 +360,25 @@ func GetHistogramMetricValue(m metrics.ObserverMetric) (float64, error) { } 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 +} diff --git a/staging/src/k8s.io/component-base/metrics/testutil/metrics_test.go b/staging/src/k8s.io/component-base/metrics/testutil/metrics_test.go index 51fb49ffee6..81afe8c8cb9 100644 --- a/staging/src/k8s.io/component-base/metrics/testutil/metrics_test.go +++ b/staging/src/k8s.io/component-base/metrics/testutil/metrics_test.go @@ -265,3 +265,91 @@ func TestHistogramValidate(t *testing.T) { } } } + +func TestLabelsMatch(t *testing.T) { + cases := []struct { + name string + metric *dto.Metric + labelFilter map[string]string + expectedMatch bool + }{ + {name: "metric labels and labelFilter have the same labels and values", metric: &dto.Metric{ + Label: []*dto.LabelPair{ + {Name: pointer.StringPtr("a"), Value: pointer.StringPtr("1")}, + {Name: pointer.StringPtr("b"), Value: pointer.StringPtr("2")}, + {Name: pointer.StringPtr("c"), Value: pointer.StringPtr("3")}, + }}, labelFilter: map[string]string{ + "a": "1", + "b": "2", + "c": "3", + }, expectedMatch: true}, + {name: "metric labels contain all labelFilter labels, and labelFilter is a subset of metric labels", metric: &dto.Metric{ + Label: []*dto.LabelPair{ + {Name: pointer.StringPtr("a"), Value: pointer.StringPtr("1")}, + {Name: pointer.StringPtr("b"), Value: pointer.StringPtr("2")}, + {Name: pointer.StringPtr("c"), Value: pointer.StringPtr("3")}, + }}, labelFilter: map[string]string{ + "a": "1", + "b": "2", + }, expectedMatch: true}, + {name: "metric labels don't have all labelFilter labels and value", metric: &dto.Metric{ + Label: []*dto.LabelPair{ + {Name: pointer.StringPtr("a"), Value: pointer.StringPtr("1")}, + {Name: pointer.StringPtr("b"), Value: pointer.StringPtr("2")}, + }}, labelFilter: map[string]string{ + "a": "1", + "b": "2", + "c": "3", + }, expectedMatch: false}, + {name: "The intersection of metric labels and labelFilter labels is empty", metric: &dto.Metric{ + Label: []*dto.LabelPair{ + {Name: pointer.StringPtr("aa"), Value: pointer.StringPtr("11")}, + {Name: pointer.StringPtr("bb"), Value: pointer.StringPtr("22")}, + {Name: pointer.StringPtr("cc"), Value: pointer.StringPtr("33")}, + }}, labelFilter: map[string]string{ + "a": "1", + "b": "2", + "c": "3", + }, expectedMatch: false}, + {name: "metric labels have the same labels names but different values with labelFilter labels and value", metric: &dto.Metric{ + Label: []*dto.LabelPair{ + {Name: pointer.StringPtr("a"), Value: pointer.StringPtr("1")}, + {Name: pointer.StringPtr("b"), Value: pointer.StringPtr("2")}, + {Name: pointer.StringPtr("c"), Value: pointer.StringPtr("3")}, + }}, labelFilter: map[string]string{ + "a": "11", + "b": "2", + "c": "3", + }, expectedMatch: false}, + {name: "metric labels contain label name but different values with labelFilter labels and value", metric: &dto.Metric{ + Label: []*dto.LabelPair{ + {Name: pointer.StringPtr("a"), Value: pointer.StringPtr("1")}, + {Name: pointer.StringPtr("b"), Value: pointer.StringPtr("2")}, + {Name: pointer.StringPtr("c"), Value: pointer.StringPtr("33")}, + {Name: pointer.StringPtr("d"), Value: pointer.StringPtr("4")}, + }}, labelFilter: map[string]string{ + "a": "1", + "b": "2", + "c": "3", + }, expectedMatch: false}, + {name: "metric labels is empty and labelFilter is not empty", metric: &dto.Metric{ + Label: []*dto.LabelPair{}}, labelFilter: map[string]string{ + "a": "1", + "b": "2", + "c": "3", + }, expectedMatch: false}, + {name: "metric labels is not empty and labelFilter is empty", metric: &dto.Metric{ + Label: []*dto.LabelPair{ + {Name: pointer.StringPtr("a"), Value: pointer.StringPtr("1")}, + {Name: pointer.StringPtr("b"), Value: pointer.StringPtr("2")}, + }}, labelFilter: map[string]string{}, expectedMatch: true}, + } + for _, tt := range cases { + t.Run(tt.name, func(t *testing.T) { + got := LabelsMatch(tt.metric, tt.labelFilter) + if got != tt.expectedMatch { + t.Errorf("Expected %v, got %v instead", tt.expectedMatch, got) + } + }) + } +}