From 68c8bda0de68353153f4f6980cdf85d3d759580d Mon Sep 17 00:00:00 2001 From: Catherine Fang Date: Tue, 13 Sep 2022 21:27:30 -0400 Subject: [PATCH 1/6] Add presubmit to check stable metrics are stable Add presubmit to check stable metrics are stable --- internal/store/stable_metrics_test.go | 108 +++++++++++++++++++++++++ internal/store/testutils.go | 53 +++++++++--- tests/testdata/stable_node_metrics.txt | 6 ++ 3 files changed, 154 insertions(+), 13 deletions(-) create mode 100644 internal/store/stable_metrics_test.go create mode 100644 tests/testdata/stable_node_metrics.txt diff --git a/internal/store/stable_metrics_test.go b/internal/store/stable_metrics_test.go new file mode 100644 index 0000000000..ae54a75c53 --- /dev/null +++ b/internal/store/stable_metrics_test.go @@ -0,0 +1,108 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +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 store + +import ( + _ "embed" + "flag" + "os" + "testing" + + v1 "k8s.io/api/core/v1" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + generator "k8s.io/kube-state-metrics/v2/pkg/metric_generator" +) + +var ( + update = flag.Bool("update", false, "update the golden files") +) + +func filterStableMetrics(familyGenerator []generator.FamilyGenerator) []generator.FamilyGenerator { + filtered := []generator.FamilyGenerator{} + for _, f := range familyGenerator { + if f.StabilityLevel == generator.STABLE { + filtered = append(filtered, f) + } + } + return filtered +} + +func TestStableMetrics(t *testing.T) { + flag.Parse() + cases := []generateStableMetricsTestCase{ + { + Name: "Node stable metrics", + FilePath: "../../tests/testdata/stable_node_metrics.txt", + generateMetricsTestCase: generateMetricsTestCase{ + DropHelp: true, + Obj: &v1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: "", + }, + Status: v1.NodeStatus{ + Conditions: []v1.NodeCondition{ + {Type: v1.NodeNetworkUnavailable}, + }, + }, + }, + Want: ``, // will be replaced by data from filePath + }, + }, + } + + for i, cs := range cases { + var err error + c := cs.generateMetricsTestCase + + metrics := filterStableMetrics(nodeMetricFamilies(nil, nil)) + c.Func = generator.ComposeMetricGenFuncs(metrics) + c.Headers = generator.ExtractMetricFamilyHeaders(metrics) + path := cs.FilePath + + if *update { + writeFile(c.runWithOutput(), path) + } else { + c.Want, err = readFile(path) + if err != nil { + t.Errorf("Can't open file %v", path) + } + err = c.run() + if err != nil { + t.Errorf("unexpected collecting result in %vth run:\n%s", i, err) + } + } + } +} + +func readFile(fileName string) (string, error) { + b, err := os.ReadFile(fileName) + if err != nil { + return "", err + } + return string(b), err +} + +func writeFile(data string, fileName string) { + file, err := os.Create(fileName) + if err != nil { + return + } + defer file.Close() + + file.WriteString(data) +} diff --git a/internal/store/testutils.go b/internal/store/testutils.go index 8de6c52ae2..ce6bf4fee9 100644 --- a/internal/store/testutils.go +++ b/internal/store/testutils.go @@ -37,9 +37,32 @@ type generateMetricsTestCase struct { Want string Headers []string Func func(interface{}) []metric.FamilyInterface + DropHelp bool +} + +type generateStableMetricsTestCase struct { + Name string + FilePath string + generateMetricsTestCase } func (testCase *generateMetricsTestCase) run() error { + out := testCase.generateOut() + testCase.Want, out = alignResult(testCase.Want, out) + + if diff := cmp.Diff(testCase.Want, out); diff != "" { + return fmt.Errorf("expected wanted output to equal output: %w", fmt.Errorf("(-want, +got):\n%s", diff)) + } + return nil +} + +func (testCase *generateMetricsTestCase) runWithOutput() string { + out := testCase.generateOut() + out, _ = alignResult(out, out) + return out +} + +func (testCase *generateMetricsTestCase) generateOut() string { metricFamilies := testCase.Func(testCase.Obj) metricFamilyStrings := []string{} for _, f := range metricFamilies { @@ -48,18 +71,16 @@ func (testCase *generateMetricsTestCase) run() error { metric := strings.Split(strings.Join(metricFamilyStrings, ""), "\n") filteredMetrics := filterMetricNames(metric, testCase.MetricNames) filteredHeaders := filterMetricNames(testCase.Headers, testCase.MetricNames) + if testCase.DropHelp { + filteredHeaders = dropHELP(filteredHeaders) + } headers := strings.Join(filteredHeaders, "\n") metrics := strings.Join(filteredMetrics, "\n") out := headers + "\n" + metrics - - if err := compareOutput(testCase.Want, out); err != nil { - return fmt.Errorf("expected wanted output to equal output: %w", err) - } - - return nil + return out } -func compareOutput(expected, actual string) error { +func alignResult(expected, actual string) (string, string) { entities := []string{expected, actual} // Align wanted and actual for i := 0; i < len(entities); i++ { @@ -67,12 +88,7 @@ func compareOutput(expected, actual string) error { entities[i] = f(entities[i]) } } - - if diff := cmp.Diff(entities[0], entities[1]); diff != "" { - return fmt.Errorf("(-want, +got):\n%s", diff) - } - - return nil + return entities[0], entities[1] } // sortLabels sorts the order of labels in each line of the given metric. The @@ -141,6 +157,17 @@ func filterMetricNames(ms []string, names []string) []string { return filtered } +func dropHELP(header []string) []string { + filtered := []string{} + for _, ms := range header { + m := strings.Split(ms, "\n") + if len(m) == 2 && m[1][:6] == "# TYPE" { + filtered = append(filtered, m[1]) + } + } + return filtered +} + func removeUnusedWhitespace(s string) string { var ( trimmedLine string diff --git a/tests/testdata/stable_node_metrics.txt b/tests/testdata/stable_node_metrics.txt new file mode 100644 index 0000000000..97d5b4bb17 --- /dev/null +++ b/tests/testdata/stable_node_metrics.txt @@ -0,0 +1,6 @@ +# TYPE kube_node_info gauge +# TYPE kube_node_status_condition gauge +kube_node_info{container_runtime_version="",internal_ip="",kernel_version="",kubelet_version="",kubeproxy_version="",node="",os_image="",pod_cidr="",provider_id="",system_uuid=""} 1 +kube_node_status_condition{condition="NetworkUnavailable",node="",status="false"} 0 +kube_node_status_condition{condition="NetworkUnavailable",node="",status="true"} 0 +kube_node_status_condition{condition="NetworkUnavailable",node="",status="unknown"} 0 \ No newline at end of file From abeb51905fe7c65edeb2e57ddd49b5d2a16a3b60 Mon Sep 17 00:00:00 2001 From: Catherine Fang Date: Wed, 19 Oct 2022 14:34:58 -0400 Subject: [PATCH 2/6] Revert "Add presubmit to check stable metrics are stable" This reverts commit 68c8bda0de68353153f4f6980cdf85d3d759580d. --- internal/store/stable_metrics_test.go | 108 ------------------------- internal/store/testutils.go | 53 +++--------- tests/testdata/stable_node_metrics.txt | 6 -- 3 files changed, 13 insertions(+), 154 deletions(-) delete mode 100644 internal/store/stable_metrics_test.go delete mode 100644 tests/testdata/stable_node_metrics.txt diff --git a/internal/store/stable_metrics_test.go b/internal/store/stable_metrics_test.go deleted file mode 100644 index ae54a75c53..0000000000 --- a/internal/store/stable_metrics_test.go +++ /dev/null @@ -1,108 +0,0 @@ -/* -Copyright 2016 The Kubernetes Authors All rights reserved. - -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 store - -import ( - _ "embed" - "flag" - "os" - "testing" - - v1 "k8s.io/api/core/v1" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - generator "k8s.io/kube-state-metrics/v2/pkg/metric_generator" -) - -var ( - update = flag.Bool("update", false, "update the golden files") -) - -func filterStableMetrics(familyGenerator []generator.FamilyGenerator) []generator.FamilyGenerator { - filtered := []generator.FamilyGenerator{} - for _, f := range familyGenerator { - if f.StabilityLevel == generator.STABLE { - filtered = append(filtered, f) - } - } - return filtered -} - -func TestStableMetrics(t *testing.T) { - flag.Parse() - cases := []generateStableMetricsTestCase{ - { - Name: "Node stable metrics", - FilePath: "../../tests/testdata/stable_node_metrics.txt", - generateMetricsTestCase: generateMetricsTestCase{ - DropHelp: true, - Obj: &v1.Node{ - ObjectMeta: metav1.ObjectMeta{ - Name: "", - }, - Status: v1.NodeStatus{ - Conditions: []v1.NodeCondition{ - {Type: v1.NodeNetworkUnavailable}, - }, - }, - }, - Want: ``, // will be replaced by data from filePath - }, - }, - } - - for i, cs := range cases { - var err error - c := cs.generateMetricsTestCase - - metrics := filterStableMetrics(nodeMetricFamilies(nil, nil)) - c.Func = generator.ComposeMetricGenFuncs(metrics) - c.Headers = generator.ExtractMetricFamilyHeaders(metrics) - path := cs.FilePath - - if *update { - writeFile(c.runWithOutput(), path) - } else { - c.Want, err = readFile(path) - if err != nil { - t.Errorf("Can't open file %v", path) - } - err = c.run() - if err != nil { - t.Errorf("unexpected collecting result in %vth run:\n%s", i, err) - } - } - } -} - -func readFile(fileName string) (string, error) { - b, err := os.ReadFile(fileName) - if err != nil { - return "", err - } - return string(b), err -} - -func writeFile(data string, fileName string) { - file, err := os.Create(fileName) - if err != nil { - return - } - defer file.Close() - - file.WriteString(data) -} diff --git a/internal/store/testutils.go b/internal/store/testutils.go index ce6bf4fee9..8de6c52ae2 100644 --- a/internal/store/testutils.go +++ b/internal/store/testutils.go @@ -37,32 +37,9 @@ type generateMetricsTestCase struct { Want string Headers []string Func func(interface{}) []metric.FamilyInterface - DropHelp bool -} - -type generateStableMetricsTestCase struct { - Name string - FilePath string - generateMetricsTestCase } func (testCase *generateMetricsTestCase) run() error { - out := testCase.generateOut() - testCase.Want, out = alignResult(testCase.Want, out) - - if diff := cmp.Diff(testCase.Want, out); diff != "" { - return fmt.Errorf("expected wanted output to equal output: %w", fmt.Errorf("(-want, +got):\n%s", diff)) - } - return nil -} - -func (testCase *generateMetricsTestCase) runWithOutput() string { - out := testCase.generateOut() - out, _ = alignResult(out, out) - return out -} - -func (testCase *generateMetricsTestCase) generateOut() string { metricFamilies := testCase.Func(testCase.Obj) metricFamilyStrings := []string{} for _, f := range metricFamilies { @@ -71,16 +48,18 @@ func (testCase *generateMetricsTestCase) generateOut() string { metric := strings.Split(strings.Join(metricFamilyStrings, ""), "\n") filteredMetrics := filterMetricNames(metric, testCase.MetricNames) filteredHeaders := filterMetricNames(testCase.Headers, testCase.MetricNames) - if testCase.DropHelp { - filteredHeaders = dropHELP(filteredHeaders) - } headers := strings.Join(filteredHeaders, "\n") metrics := strings.Join(filteredMetrics, "\n") out := headers + "\n" + metrics - return out + + if err := compareOutput(testCase.Want, out); err != nil { + return fmt.Errorf("expected wanted output to equal output: %w", err) + } + + return nil } -func alignResult(expected, actual string) (string, string) { +func compareOutput(expected, actual string) error { entities := []string{expected, actual} // Align wanted and actual for i := 0; i < len(entities); i++ { @@ -88,7 +67,12 @@ func alignResult(expected, actual string) (string, string) { entities[i] = f(entities[i]) } } - return entities[0], entities[1] + + if diff := cmp.Diff(entities[0], entities[1]); diff != "" { + return fmt.Errorf("(-want, +got):\n%s", diff) + } + + return nil } // sortLabels sorts the order of labels in each line of the given metric. The @@ -157,17 +141,6 @@ func filterMetricNames(ms []string, names []string) []string { return filtered } -func dropHELP(header []string) []string { - filtered := []string{} - for _, ms := range header { - m := strings.Split(ms, "\n") - if len(m) == 2 && m[1][:6] == "# TYPE" { - filtered = append(filtered, m[1]) - } - } - return filtered -} - func removeUnusedWhitespace(s string) string { var ( trimmedLine string diff --git a/tests/testdata/stable_node_metrics.txt b/tests/testdata/stable_node_metrics.txt deleted file mode 100644 index 97d5b4bb17..0000000000 --- a/tests/testdata/stable_node_metrics.txt +++ /dev/null @@ -1,6 +0,0 @@ -# TYPE kube_node_info gauge -# TYPE kube_node_status_condition gauge -kube_node_info{container_runtime_version="",internal_ip="",kernel_version="",kubelet_version="",kubeproxy_version="",node="",os_image="",pod_cidr="",provider_id="",system_uuid=""} 1 -kube_node_status_condition{condition="NetworkUnavailable",node="",status="false"} 0 -kube_node_status_condition{condition="NetworkUnavailable",node="",status="true"} 0 -kube_node_status_condition{condition="NetworkUnavailable",node="",status="unknown"} 0 \ No newline at end of file From 3f7cbbd4870018a25c80902f302bc2ef97a41a3c Mon Sep 17 00:00:00 2001 From: Catherine Fang Date: Sat, 15 Oct 2022 14:32:38 -0400 Subject: [PATCH 3/6] Add presubmit to check stable metrics aren't changed --- Makefile | 2 +- tests/e2e.sh | 5 + tests/stable/check_test.go | 193 ++++ tests/stable/testdata/stablemetrics.yaml | 1041 ++++++++++++++++++++++ 4 files changed, 1240 insertions(+), 1 deletion(-) create mode 100644 tests/stable/check_test.go create mode 100644 tests/stable/testdata/stablemetrics.yaml diff --git a/Makefile b/Makefile index c33e9a177a..186a184d02 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ LATEST_RELEASE_BRANCH := release-$(shell grep -ohE "[0-9]+.[0-9]+" VERSION) BRANCH = $(strip $(shell git rev-parse --abbrev-ref HEAD)) DOCKER_CLI ?= docker PROMTOOL_CLI ?= promtool -PKGS = $(shell go list ./... | grep -v /vendor/ | grep -v /tests/e2e) +PKGS = $(shell go list ./... | grep -v /vendor/ | grep -v /tests/e2e | grep -v /tests/stable) ARCH ?= $(shell go env GOARCH) BUILD_DATE = $(shell date -u +'%Y-%m-%dT%H:%M:%SZ') GIT_COMMIT ?= $(shell git rev-parse --short HEAD) diff --git a/tests/e2e.sh b/tests/e2e.sh index e286c6431a..3703caa94b 100755 --- a/tests/e2e.sh +++ b/tests/e2e.sh @@ -15,6 +15,7 @@ # limitations under the License. set -e +set -x set -o pipefail case $(uname -m) in @@ -163,6 +164,10 @@ mkdir -p ${KUBE_STATE_METRICS_LOG_DIR} echo "access kube-state-metrics metrics endpoint" curl -s "http://localhost:8001/api/v1/namespaces/kube-system/services/kube-state-metrics:http-metrics/proxy/metrics" >${KUBE_STATE_METRICS_LOG_DIR}/metrics +cat ${KUBE_STATE_METRICS_LOG_DIR}/metrics +# check stable metrics aren't changed +go test ./tests/stable/check_test.go --collectedMetricsFile "$(realpath ${KUBE_STATE_METRICS_LOG_DIR}/metrics)" --stableMetricsFile "$(realpath tests/stable/testdata/stablemetrics.yaml)" + KUBE_STATE_METRICS_STATUS=$(curl -s "http://localhost:8001/api/v1/namespaces/kube-system/services/kube-state-metrics:http-metrics/proxy/healthz") if [[ "${KUBE_STATE_METRICS_STATUS}" == "OK" ]]; then echo "kube-state-metrics is still running after accessing metrics endpoint" diff --git a/tests/stable/check_test.go b/tests/stable/check_test.go new file mode 100644 index 0000000000..68039ee85c --- /dev/null +++ b/tests/stable/check_test.go @@ -0,0 +1,193 @@ +/* +Copyright 2022 The Kubernetes Authors All rights reserved. + +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 stable + +import ( + "flag" + "fmt" + "testing" + + "log" + "os" + "strings" + + "github.com/google/go-cmp/cmp" + prommodel "github.com/prometheus/client_model/go" + "github.com/prometheus/common/expfmt" + "gopkg.in/yaml.v3" + + "github.com/google/go-cmp/cmp/cmpopts" +) + +var promText string +var stableYaml string + +var skipStableMetrics = []string{} + +type Metric struct { + Name string `yaml:"name"` + Help string `yaml:"help"` + Type string + Labels []string + // Histogram type + Buckets []float64 `yaml:"buckets,omitempty"` +} + +func TestMain(m *testing.M) { + flag.StringVar(&promText, "collectedMetricsFile", "", "input prometheus metrics text file, text format") + flag.StringVar(&stableYaml, "stableMetricsFile", "", "expected stable metrics yaml file, yaml format") + flag.Parse() + m.Run() +} + +func TestStableMetrics(t *testing.T) { + mf, err := parsePromText(promText) + fatal(err) + collectedStableMetrics := extractStableMetrics(mf) + printMetric(collectedStableMetrics) + + expectedStableMetrics, err := readYaml(stableYaml) + if err != nil { + t.Fatalf("Can't read stable metrics from file. err = %v", err) + } + + err = compare(collectedStableMetrics, *expectedStableMetrics, skipStableMetrics) + if err != nil { + t.Fatalf("Stable metrics changed: err = %v", err) + } else { + fmt.Println("passed") + } + +} + +func fatal(err error) { + if err != nil { + log.Fatalln(err) + } +} + +func printMetric(metrics []Metric) { + yamlData, err := yaml.Marshal(metrics) + if err != nil { + fmt.Printf("error while Marshaling. %v", err) + } + fmt.Println("---begin YAML file---") + fmt.Println(string(yamlData)) + fmt.Println("---end YAML file---") +} + +func parsePromText(path string) (map[string]*prommodel.MetricFamily, error) { + reader, err := os.Open(path) + if err != nil { + return nil, err + } + var parser expfmt.TextParser + mf, err := parser.TextToMetricFamilies(reader) + if err != nil { + return nil, err + } + return mf, nil +} + +func getBuckets(v *prommodel.MetricFamily) []float64 { + buckets := []float64{} + if v.GetType() == prommodel.MetricType_HISTOGRAM { + for _, bucket := range v.Metric[0].GetHistogram().GetBucket() { + buckets = append(buckets, *bucket.UpperBound) + } + } else { + buckets = nil + } + return buckets +} + +func extractStableMetrics(mf map[string]*prommodel.MetricFamily) []Metric { + metrics := []Metric{} + for _, v := range mf { + // Find stable metrics + if !strings.Contains(*(v.Help), "[STABLE]") { + continue + } + + m := Metric{ + Name: *(v.Name), + Help: *(v.Help), + Type: (v.Type).String(), + Buckets: getBuckets(v), + } + labels := []string{} + for _, y := range v.Metric[0].Label { + labels = append(labels, y.GetName()) + } + m.Labels = labels + metrics = append(metrics, m) + } + return metrics +} + +func readYaml(filename string) (*[]Metric, error) { + buf, err := os.ReadFile(filename) + if err != nil { + return nil, err + } + c := &[]Metric{} + err = yaml.Unmarshal(buf, c) + if err != nil { + return nil, fmt.Errorf("error %q: %w", filename, err) + } + return c, err +} + +func convert2Map(metrics []Metric, skipMap map[string]int) map[string]Metric { + name2Metric := map[string]Metric{} + for _, v := range metrics { + if _, ok := skipMap[v.Name]; ok { + fmt.Printf("skip, metric %s is in skip list\n", v.Name) + continue + } + name2Metric[v.Name] = v + } + return name2Metric + +} + +func compare(collectedStableMetrics []Metric, expectedStableMetrics []Metric, skipStableMetrics []string) error { + skipMap := map[string]int{} + for _, v := range skipStableMetrics { + skipMap[v] = 1 + } + collected := convert2Map(collectedStableMetrics, skipMap) + expected := convert2Map(expectedStableMetrics, skipMap) + + var ok bool + + for name, metric := range expected { + var expectedMetric Metric + if expectedMetric, ok = collected[name]; !ok { + return fmt.Errorf("not found stable metric %s", name) + } + if diff := cmp.Diff(metric, expectedMetric, cmpopts.IgnoreFields(Metric{}, "Help")); diff != "" { + return fmt.Errorf("stable metric %s mismatch (-want +got):\n%s", name, diff) + } + } + for name := range collected { + if _, ok = expected[name]; !ok { + return fmt.Errorf("detected new stable metric %s which isn't in testdata ", name) + } + } + return nil +} diff --git a/tests/stable/testdata/stablemetrics.yaml b/tests/stable/testdata/stablemetrics.yaml new file mode 100644 index 0000000000..4e25e152bc --- /dev/null +++ b/tests/stable/testdata/stablemetrics.yaml @@ -0,0 +1,1041 @@ +- name: kube_daemonset_metadata_generation + help: '[STABLE] Sequence number representing a specific generation of the desired + state.' + type: GAUGE + labels: + - namespace + - daemonset +- name: kube_horizontalpodautoscaler_status_desired_replicas + help: '[STABLE] Desired number of replicas of pods managed by this autoscaler.' + type: GAUGE + labels: + - namespace + - horizontalpodautoscaler +- name: kube_statefulset_status_update_revision + help: '[STABLE] Indicates the version of the StatefulSet used to generate Pods in + the sequence [replicas-updatedReplicas,replicas)' + type: GAUGE + labels: + - namespace + - statefulset + - revision +- name: kube_deployment_labels + help: '[STABLE] Kubernetes labels converted to Prometheus labels.' + type: GAUGE + labels: + - namespace + - deployment +- name: kube_endpoint_address + help: '[STABLE] Information about Endpoint available and non available addresses.' + type: GAUGE + labels: + - namespace + - endpoint + - ip + - ready +- name: kube_pod_container_status_terminated + help: '[STABLE] Describes whether the container is currently in terminated state.' + type: GAUGE + labels: + - namespace + - pod + - uid + - container +- name: kube_replicaset_status_fully_labeled_replicas + help: '[STABLE] The number of fully labeled replicas per ReplicaSet.' + type: GAUGE + labels: + - namespace + - replicaset +- name: kube_deployment_spec_replicas + help: '[STABLE] Number of desired pods for a deployment.' + type: GAUGE + labels: + - namespace + - deployment +- name: kube_cronjob_info + help: '[STABLE] Info about cronjob.' + type: GAUGE + labels: + - namespace + - cronjob + - schedule + - concurrency_policy +- name: kube_pod_labels + help: '[STABLE] Kubernetes labels converted to Prometheus labels.' + type: GAUGE + labels: + - namespace + - pod + - uid +- name: kube_replicaset_labels + help: '[STABLE] Kubernetes labels converted to Prometheus labels.' + type: GAUGE + labels: + - namespace + - replicaset +- name: kube_replicaset_status_observed_generation + help: '[STABLE] The generation observed by the ReplicaSet controller.' + type: GAUGE + labels: + - namespace + - replicaset +- name: kube_daemonset_status_observed_generation + help: '[STABLE] The most recent generation observed by the daemon set controller.' + type: GAUGE + labels: + - namespace + - daemonset +- name: kube_horizontalpodautoscaler_status_current_replicas + help: '[STABLE] Current number of replicas of pods managed by this autoscaler.' + type: GAUGE + labels: + - namespace + - horizontalpodautoscaler +- name: kube_node_status_capacity + help: '[STABLE] The capacity for different resources of a node.' + type: GAUGE + labels: + - node + - resource + - unit +- name: kube_pod_info + help: '[STABLE] Information about pod.' + type: GAUGE + labels: + - namespace + - pod + - uid + - host_ip + - pod_ip + - node + - created_by_kind + - created_by_name + - priority_class + - host_network +- name: kube_secret_type + help: '[STABLE] Type about secret.' + type: GAUGE + labels: + - namespace + - secret + - type +- name: kube_job_status_start_time + help: '[STABLE] StartTime represents time when the job was acknowledged by the Job + Manager.' + type: GAUGE + labels: + - namespace + - job_name +- name: kube_pod_container_status_waiting_reason + help: '[STABLE] Describes the reason the container is currently in waiting state.' + type: GAUGE + labels: + - namespace + - pod + - uid + - container + - reason +- name: kube_statefulset_status_replicas_current + help: '[STABLE] The number of current replicas per StatefulSet.' + type: GAUGE + labels: + - namespace + - statefulset +- name: kube_storageclass_info + help: '[STABLE] Information about storageclass.' + type: GAUGE + labels: + - storageclass + - provisioner + - reclaim_policy + - volume_binding_mode +- name: kube_pod_container_info + help: '[STABLE] Information about a container in a pod.' + type: GAUGE + labels: + - namespace + - pod + - uid + - container + - image_spec + - image + - image_id + - container_id +- name: kube_limitrange_created + help: '[STABLE] Unix creation timestamp' + type: GAUGE + labels: + - namespace + - limitrange +- name: kube_namespace_status_phase + help: '[STABLE] kubernetes namespace status phase.' + type: GAUGE + labels: + - namespace + - phase +- name: kube_replicationcontroller_status_available_replicas + help: '[STABLE] The number of available replicas per ReplicationController.' + type: GAUGE + labels: + - namespace + - replicationcontroller +- name: kube_service_info + help: '[STABLE] Information about service.' + type: GAUGE + labels: + - namespace + - service + - uid + - cluster_ip + - external_name + - load_balancer_ip +- name: kube_replicaset_status_replicas + help: '[STABLE] The number of replicas per ReplicaSet.' + type: GAUGE + labels: + - namespace + - replicaset +- name: kube_configmap_labels + help: '[STABLE] Kubernetes labels converted to Prometheus labels.' + type: GAUGE + labels: + - namespace + - configmap +- name: kube_horizontalpodautoscaler_spec_min_replicas + help: '[STABLE] Lower limit for the number of pods that can be set by the autoscaler, + default 1.' + type: GAUGE + labels: + - namespace + - horizontalpodautoscaler +- name: kube_ingress_path + help: '[STABLE] Ingress host, paths and backend service information.' + type: GAUGE + labels: + - namespace + - ingress + - host + - path + - service_name + - service_port +- name: kube_statefulset_status_observed_generation + help: '[STABLE] The generation observed by the StatefulSet controller.' + type: GAUGE + labels: + - namespace + - statefulset +- name: kube_configmap_info + help: '[STABLE] Information about configmap.' + type: GAUGE + labels: + - namespace + - configmap +- name: kube_persistentvolume_info + help: '[STABLE] Information about persistentvolume.' + type: GAUGE + labels: + - persistentvolume + - storageclass + - gce_persistent_disk_name + - ebs_volume_id + - azure_disk_name + - fc_wwids + - fc_lun + - fc_target_wwns + - iscsi_target_portal + - iscsi_iqn + - iscsi_lun + - iscsi_initiator_name + - nfs_server + - nfs_path + - csi_driver + - csi_volume_handle + - local_path + - local_fs + - host_path + - host_path_type +- name: kube_poddisruptionbudget_status_desired_healthy + help: '[STABLE] Minimum desired number of healthy pods' + type: GAUGE + labels: + - namespace + - poddisruptionbudget +- name: kube_ingress_info + help: '[STABLE] Information about ingress.' + type: GAUGE + labels: + - namespace + - ingress + - ingressclass +- name: kube_poddisruptionbudget_created + help: '[STABLE] Unix creation timestamp' + type: GAUGE + labels: + - namespace + - poddisruptionbudget +- name: kube_statefulset_metadata_generation + help: '[STABLE] Sequence number representing a specific generation of the desired + state for the StatefulSet.' + type: GAUGE + labels: + - namespace + - statefulset +- name: kube_certificatesigningrequest_created + help: '[STABLE] Unix creation timestamp' + type: GAUGE + labels: + - certificatesigningrequest + - signer_name +- name: kube_statefulset_status_replicas + help: '[STABLE] The number of replicas per StatefulSet.' + type: GAUGE + labels: + - namespace + - statefulset +- name: kube_namespace_created + help: '[STABLE] Unix creation timestamp' + type: GAUGE + labels: + - namespace +- name: kube_node_spec_unschedulable + help: '[STABLE] Whether a node can schedule new pods.' + type: GAUGE + labels: + - node +- name: kube_service_labels + help: '[STABLE] Kubernetes labels converted to Prometheus labels.' + type: GAUGE + labels: + - namespace + - service + - uid +- name: kube_replicationcontroller_spec_replicas + help: '[STABLE] Number of desired pods for a ReplicationController.' + type: GAUGE + labels: + - namespace + - replicationcontroller +- name: kube_statefulset_status_current_revision + help: '[STABLE] Indicates the version of the StatefulSet used to generate Pods in + the sequence [0,currentReplicas).' + type: GAUGE + labels: + - namespace + - statefulset + - revision +- name: kube_deployment_spec_strategy_rollingupdate_max_unavailable + help: '[STABLE] Maximum number of unavailable replicas during a rolling update of + a deployment.' + type: GAUGE + labels: + - namespace + - deployment +- name: kube_node_labels + help: '[STABLE] Kubernetes labels converted to Prometheus labels.' + type: GAUGE + labels: + - node +- name: kube_replicationcontroller_status_replicas + help: '[STABLE] The number of replicas per ReplicationController.' + type: GAUGE + labels: + - namespace + - replicationcontroller +- name: kube_horizontalpodautoscaler_status_condition + help: '[STABLE] The condition of this autoscaler.' + type: GAUGE + labels: + - namespace + - horizontalpodautoscaler + - condition + - status +- name: kube_job_spec_parallelism + help: '[STABLE] The maximum desired number of pods the job should run at any given + time.' + type: GAUGE + labels: + - namespace + - job_name +- name: kube_deployment_status_replicas_ready + help: '[STABLE] The number of ready replicas per deployment.' + type: GAUGE + labels: + - namespace + - deployment +- name: kube_storageclass_created + help: '[STABLE] Unix creation timestamp' + type: GAUGE + labels: + - storageclass +- name: kube_secret_info + help: '[STABLE] Information about secret.' + type: GAUGE + labels: + - namespace + - secret +- name: kube_deployment_status_replicas_updated + help: '[STABLE] The number of updated replicas per deployment.' + type: GAUGE + labels: + - namespace + - deployment +- name: kube_deployment_metadata_generation + help: '[STABLE] Sequence number representing a specific generation of the desired + state.' + type: GAUGE + labels: + - namespace + - deployment +- name: kube_statefulset_labels + help: '[STABLE] Kubernetes labels converted to Prometheus labels.' + type: GAUGE + labels: + - namespace + - statefulset +- name: kube_persistentvolumeclaim_resource_requests_storage_bytes + help: '[STABLE] The capacity of storage requested by the persistent volume claim.' + type: GAUGE + labels: + - namespace + - persistentvolumeclaim +- name: kube_pod_container_status_running + help: '[STABLE] Describes whether the container is currently in running state.' + type: GAUGE + labels: + - namespace + - pod + - uid + - container +- name: kube_resourcequota_created + help: '[STABLE] Unix creation timestamp' + type: GAUGE + labels: + - namespace + - resourcequota +- name: kube_deployment_spec_paused + help: '[STABLE] Whether the deployment is paused and will not be processed by the + deployment controller.' + type: GAUGE + labels: + - namespace + - deployment +- name: kube_deployment_spec_strategy_rollingupdate_max_surge + help: '[STABLE] Maximum number of replicas that can be scheduled above the desired + number of replicas during a rolling update of a deployment.' + type: GAUGE + labels: + - namespace + - deployment +- name: kube_certificatesigningrequest_cert_length + help: '[STABLE] Length of the issued cert' + type: GAUGE + labels: + - certificatesigningrequest + - signer_name +- name: kube_replicationcontroller_metadata_generation + help: '[STABLE] Sequence number representing a specific generation of the desired + state.' + type: GAUGE + labels: + - namespace + - replicationcontroller +- name: kube_resourcequota + help: '[STABLE] Information about resource quota.' + type: GAUGE + labels: + - namespace + - resourcequota + - resource + - type +- name: kube_cronjob_labels + help: '[STABLE] Kubernetes labels converted to Prometheus labels.' + type: GAUGE + labels: + - namespace + - cronjob +- name: kube_endpoint_created + help: '[STABLE] Unix creation timestamp' + type: GAUGE + labels: + - namespace + - endpoint +- name: kube_statefulset_status_replicas_ready + help: '[STABLE] The number of ready replicas per StatefulSet.' + type: GAUGE + labels: + - namespace + - statefulset +- name: kube_job_status_succeeded + help: '[STABLE] The number of pods which reached Phase Succeeded.' + type: GAUGE + labels: + - namespace + - job_name +- name: kube_node_created + help: '[STABLE] Unix creation timestamp' + type: GAUGE + labels: + - node +- name: kube_poddisruptionbudget_status_pod_disruptions_allowed + help: '[STABLE] Number of pod disruptions that are currently allowed' + type: GAUGE + labels: + - namespace + - poddisruptionbudget +- name: kube_horizontalpodautoscaler_labels + help: '[STABLE] Kubernetes labels converted to Prometheus labels.' + type: GAUGE + labels: + - namespace + - horizontalpodautoscaler +- name: kube_persistentvolumeclaim_access_mode + help: '[STABLE] The access mode(s) specified by the persistent volume claim.' + type: GAUGE + labels: + - namespace + - persistentvolumeclaim + - access_mode +- name: kube_replicaset_metadata_generation + help: '[STABLE] Sequence number representing a specific generation of the desired + state.' + type: GAUGE + labels: + - namespace + - replicaset +- name: kube_configmap_created + help: '[STABLE] Unix creation timestamp' + type: GAUGE + labels: + - namespace + - configmap +- name: kube_replicationcontroller_created + help: '[STABLE] Unix creation timestamp' + type: GAUGE + labels: + - namespace + - replicationcontroller +- name: kube_pod_restart_policy + help: '[STABLE] Describes the restart policy in use by this pod.' + type: GAUGE + labels: + - namespace + - pod + - uid + - type +- name: kube_daemonset_status_current_number_scheduled + help: '[STABLE] The number of nodes running at least one daemon pod and are supposed + to.' + type: GAUGE + labels: + - namespace + - daemonset +- name: kube_job_status_failed + help: '[STABLE] The number of pods which reached Phase Failed and the reason for + failure.' + type: GAUGE + labels: + - namespace + - job_name +- name: kube_daemonset_status_number_unavailable + help: '[STABLE] The number of nodes that should be running the daemon pod and have + none of the daemon pod running and available' + type: GAUGE + labels: + - namespace + - daemonset +- name: kube_service_created + help: '[STABLE] Unix creation timestamp' + type: GAUGE + labels: + - namespace + - service + - uid +- name: kube_job_labels + help: '[STABLE] Kubernetes labels converted to Prometheus labels.' + type: GAUGE + labels: + - namespace + - job_name +- name: kube_job_created + help: '[STABLE] Unix creation timestamp' + type: GAUGE + labels: + - namespace + - job_name +- name: kube_poddisruptionbudget_status_observed_generation + help: '[STABLE] Most recent generation observed when updating this PDB status' + type: GAUGE + labels: + - namespace + - poddisruptionbudget +- name: kube_replicaset_owner + help: '[STABLE] Information about the ReplicaSet''s owner.' + type: GAUGE + labels: + - namespace + - replicaset + - owner_kind + - owner_name + - owner_is_controller +- name: kube_cronjob_metadata_resource_version + help: '[STABLE] Resource version representing a specific version of the cronjob.' + type: GAUGE + labels: + - namespace + - cronjob +- name: kube_ingress_created + help: '[STABLE] Unix creation timestamp' + type: GAUGE + labels: + - namespace + - ingress +- name: kube_pod_status_ready + help: '[STABLE] Describes whether the pod is ready to serve requests.' + type: GAUGE + labels: + - namespace + - pod + - uid + - condition +- name: kube_statefulset_status_replicas_updated + help: '[STABLE] The number of updated replicas per StatefulSet.' + type: GAUGE + labels: + - namespace + - statefulset +- name: kube_daemonset_labels + help: '[STABLE] Kubernetes labels converted to Prometheus labels.' + type: GAUGE + labels: + - namespace + - daemonset +- name: kube_persistentvolumeclaim_status_phase + help: '[STABLE] The phase the persistent volume claim is currently in.' + type: GAUGE + labels: + - namespace + - persistentvolumeclaim + - phase +- name: kube_deployment_status_replicas + help: '[STABLE] The number of replicas per deployment.' + type: GAUGE + labels: + - namespace + - deployment +- name: kube_persistentvolume_status_phase + help: '[STABLE] The phase indicates if a volume is available, bound to a claim, + or released by a claim.' + type: GAUGE + labels: + - persistentvolume + - phase +- name: kube_cronjob_status_active + help: '[STABLE] Active holds pointers to currently running jobs.' + type: GAUGE + labels: + - namespace + - cronjob +- name: kube_replicaset_status_ready_replicas + help: '[STABLE] The number of ready replicas per ReplicaSet.' + type: GAUGE + labels: + - namespace + - replicaset +- name: kube_replicaset_spec_replicas + help: '[STABLE] Number of desired pods for a ReplicaSet.' + type: GAUGE + labels: + - namespace + - replicaset +- name: kube_pod_owner + help: '[STABLE] Information about the Pod''s owner.' + type: GAUGE + labels: + - namespace + - pod + - uid + - owner_kind + - owner_name + - owner_is_controller +- name: kube_secret_labels + help: '[STABLE] Kubernetes labels converted to Prometheus labels.' + type: GAUGE + labels: + - namespace + - secret +- name: kube_job_spec_completions + help: '[STABLE] The desired number of successfully finished pods the job should + be run with.' + type: GAUGE + labels: + - namespace + - job_name +- name: kube_node_info + help: '[STABLE] Information about a cluster node.' + type: GAUGE + labels: + - node + - kernel_version + - os_image + - container_runtime_version + - kubelet_version + - kubeproxy_version + - provider_id + - pod_cidr + - system_uuid + - internal_ip +- name: kube_statefulset_created + help: '[STABLE] Unix creation timestamp' + type: GAUGE + labels: + - namespace + - statefulset +- name: kube_certificatesigningrequest_labels + help: '[STABLE] Kubernetes labels converted to Prometheus labels.' + type: GAUGE + labels: + - certificatesigningrequest + - signer_name +- name: kube_daemonset_status_number_ready + help: '[STABLE] The number of nodes that should be running the daemon pod and have + one or more of the daemon pod running and ready.' + type: GAUGE + labels: + - namespace + - daemonset +- name: kube_pod_status_phase + help: '[STABLE] The pods current phase.' + type: GAUGE + labels: + - namespace + - pod + - uid + - phase +- name: kube_pod_container_status_ready + help: '[STABLE] Describes whether the containers readiness check succeeded.' + type: GAUGE + labels: + - namespace + - pod + - uid + - container +- name: kube_pod_start_time + help: '[STABLE] Start time in unix timestamp for a pod.' + type: GAUGE + labels: + - namespace + - pod + - uid +- name: kube_limitrange + help: '[STABLE] Information about limit range.' + type: GAUGE + labels: + - namespace + - limitrange + - resource + - type + - constraint +- name: kube_node_status_condition + help: '[STABLE] The condition of a cluster node.' + type: GAUGE + labels: + - node + - condition + - status +- name: kube_persistentvolumeclaim_info + help: '[STABLE] Information about persistent volume claim.' + type: GAUGE + labels: + - namespace + - persistentvolumeclaim + - storageclass + - volumename +- name: kube_replicationcontroller_status_fully_labeled_replicas + help: '[STABLE] The number of fully labeled replicas per ReplicationController.' + type: GAUGE + labels: + - namespace + - replicationcontroller +- name: kube_cronjob_spec_suspend + help: '[STABLE] Suspend flag tells the controller to suspend subsequent executions.' + type: GAUGE + labels: + - namespace + - cronjob +- name: kube_daemonset_status_updated_number_scheduled + help: '[STABLE] The total number of nodes that are running updated daemon pod' + type: GAUGE + labels: + - namespace + - daemonset +- name: kube_deployment_status_replicas_unavailable + help: '[STABLE] The number of unavailable replicas per deployment.' + type: GAUGE + labels: + - namespace + - deployment +- name: kube_pod_container_status_restarts_total + help: '[STABLE] The number of container restarts per container.' + type: COUNTER + labels: + - namespace + - pod + - uid + - container +- name: kube_daemonset_status_number_available + help: '[STABLE] The number of nodes that should be running the daemon pod and have + one or more of the daemon pod running and available' + type: GAUGE + labels: + - namespace + - daemonset +- name: kube_deployment_created + help: '[STABLE] Unix creation timestamp' + type: GAUGE + labels: + - namespace + - deployment +- name: kube_endpoint_ports + help: '[STABLE] Information about the Endpoint ports.' + type: GAUGE + labels: + - namespace + - endpoint + - port_name + - port_protocol + - port_number +- name: kube_job_owner + help: '[STABLE] Information about the Job''s owner.' + type: GAUGE + labels: + - namespace + - job_name + - owner_kind + - owner_name + - owner_is_controller +- name: kube_node_status_allocatable + help: '[STABLE] The allocatable for different resources of a node that are available + for scheduling.' + type: GAUGE + labels: + - node + - resource + - unit +- name: kube_daemonset_created + help: '[STABLE] Unix creation timestamp' + type: GAUGE + labels: + - namespace + - daemonset +- name: kube_deployment_status_observed_generation + help: '[STABLE] The generation observed by the deployment controller.' + type: GAUGE + labels: + - namespace + - deployment +- name: kube_horizontalpodautoscaler_spec_max_replicas + help: '[STABLE] Upper limit for the number of pods that can be set by the autoscaler; + cannot be smaller than MinReplicas.' + type: GAUGE + labels: + - namespace + - horizontalpodautoscaler +- name: kube_ingress_labels + help: '[STABLE] Kubernetes labels converted to Prometheus labels.' + type: GAUGE + labels: + - namespace + - ingress +- name: kube_replicationcontroller_status_observed_generation + help: '[STABLE] The generation observed by the ReplicationController controller.' + type: GAUGE + labels: + - namespace + - replicationcontroller +- name: kube_storageclass_labels + help: '[STABLE] Kubernetes labels converted to Prometheus labels.' + type: GAUGE + labels: + - storageclass +- name: kube_cronjob_next_schedule_time + help: '[STABLE] Next time the cronjob should be scheduled. The time after lastScheduleTime, + or after the cron job''s creation time if it''s never been scheduled. Use this + to determine if the job is delayed.' + type: GAUGE + labels: + - namespace + - cronjob +- name: kube_daemonset_status_desired_number_scheduled + help: '[STABLE] The number of nodes that should be running the daemon pod.' + type: GAUGE + labels: + - namespace + - daemonset +- name: kube_deployment_status_condition + help: '[STABLE] The current status conditions of a deployment.' + type: GAUGE + labels: + - namespace + - deployment + - condition + - status +- name: kube_endpoint_info + help: '[STABLE] Information about endpoint.' + type: GAUGE + labels: + - namespace + - endpoint +- name: kube_pod_container_status_waiting + help: '[STABLE] Describes whether the container is currently in waiting state.' + type: GAUGE + labels: + - namespace + - pod + - uid + - container +- name: kube_replicaset_created + help: '[STABLE] Unix creation timestamp' + type: GAUGE + labels: + - namespace + - replicaset +- name: kube_secret_created + help: '[STABLE] Unix creation timestamp' + type: GAUGE + labels: + - namespace + - secret +- name: kube_statefulset_replicas + help: '[STABLE] Number of desired pods for a StatefulSet.' + type: GAUGE + labels: + - namespace + - statefulset +- name: kube_endpoint_labels + help: '[STABLE] Kubernetes labels converted to Prometheus labels.' + type: GAUGE + labels: + - namespace + - endpoint +- name: kube_job_info + help: '[STABLE] Information about job.' + type: GAUGE + labels: + - namespace + - job_name +- name: kube_poddisruptionbudget_status_expected_pods + help: '[STABLE] Total number of pods counted by this disruption budget' + type: GAUGE + labels: + - namespace + - poddisruptionbudget +- name: kube_daemonset_status_number_misscheduled + help: '[STABLE] The number of nodes running a daemon pod but are not supposed to.' + type: GAUGE + labels: + - namespace + - daemonset +- name: kube_poddisruptionbudget_status_current_healthy + help: '[STABLE] Current number of healthy pods' + type: GAUGE + labels: + - namespace + - poddisruptionbudget +- name: kube_pod_status_scheduled_time + help: '[STABLE] Unix timestamp when pod moved into scheduled status' + type: GAUGE + labels: + - namespace + - pod + - uid +- name: kube_certificatesigningrequest_condition + help: '[STABLE] The number of each certificatesigningrequest condition' + type: GAUGE + labels: + - certificatesigningrequest + - signer_name + - condition +- name: kube_deployment_status_replicas_available + help: '[STABLE] The number of available replicas per deployment.' + type: GAUGE + labels: + - namespace + - deployment +- name: kube_namespace_labels + help: '[STABLE] Kubernetes labels converted to Prometheus labels.' + type: GAUGE + labels: + - namespace +- name: kube_pod_status_scheduled + help: '[STABLE] Describes the status of the scheduling process for the pod.' + type: GAUGE + labels: + - namespace + - pod + - uid + - condition +- name: kube_cronjob_created + help: '[STABLE] Unix creation timestamp' + type: GAUGE + labels: + - namespace + - cronjob +- name: kube_pod_container_state_started + help: '[STABLE] Start time in unix timestamp for a pod container.' + type: GAUGE + labels: + - namespace + - pod + - uid + - container +- name: kube_service_spec_type + help: '[STABLE] Type about service.' + type: GAUGE + labels: + - namespace + - service + - uid + - type +- name: kube_job_status_active + help: '[STABLE] The number of actively running pods.' + type: GAUGE + labels: + - namespace + - job_name +- name: kube_horizontalpodautoscaler_metadata_generation + help: '[STABLE] The generation observed by the HorizontalPodAutoscaler controller.' + type: GAUGE + labels: + - namespace + - horizontalpodautoscaler +- name: kube_pod_created + help: '[STABLE] Unix creation timestamp' + type: GAUGE + labels: + - namespace + - pod + - uid +- name: kube_replicationcontroller_status_ready_replicas + help: '[STABLE] The number of ready replicas per ReplicationController.' + type: GAUGE + labels: + - namespace + - replicationcontroller +- name: kube_persistentvolumeclaim_labels + help: '[STABLE] Kubernetes labels converted to Prometheus labels.' + type: GAUGE + labels: + - namespace + - persistentvolumeclaim +- name: kube_persistentvolume_labels + help: '[STABLE] Kubernetes labels converted to Prometheus labels.' + type: GAUGE + labels: + - persistentvolume +- name: kube_persistentvolume_capacity_bytes + help: '[STABLE] Persistentvolume capacity in bytes.' + type: GAUGE + labels: + - persistentvolume + From 91276419813dcb2ac8d4f391c7cdc1eb119f936a Mon Sep 17 00:00:00 2001 From: Catherine Fang Date: Wed, 19 Oct 2022 20:09:43 -0400 Subject: [PATCH 4/6] Sort metric name --- tests/stable/check_test.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/tests/stable/check_test.go b/tests/stable/check_test.go index 68039ee85c..c32302da3c 100644 --- a/tests/stable/check_test.go +++ b/tests/stable/check_test.go @@ -19,6 +19,7 @@ package stable import ( "flag" "fmt" + "sort" "testing" "log" @@ -165,6 +166,15 @@ func convert2Map(metrics []Metric, skipMap map[string]int) map[string]Metric { } +func sortedKeys(m map[string]Metric) []string { + keys := []string{} + for name := range m { + keys = append(keys, name) + } + sort.Strings(keys) + return keys +} + func compare(collectedStableMetrics []Metric, expectedStableMetrics []Metric, skipStableMetrics []string) error { skipMap := map[string]int{} for _, v := range skipStableMetrics { @@ -175,7 +185,8 @@ func compare(collectedStableMetrics []Metric, expectedStableMetrics []Metric, sk var ok bool - for name, metric := range expected { + for _, name := range sortedKeys(expected) { + metric := expected[name] var expectedMetric Metric if expectedMetric, ok = collected[name]; !ok { return fmt.Errorf("not found stable metric %s", name) @@ -184,7 +195,7 @@ func compare(collectedStableMetrics []Metric, expectedStableMetrics []Metric, sk return fmt.Errorf("stable metric %s mismatch (-want +got):\n%s", name, diff) } } - for name := range collected { + for _, name := range sortedKeys(collected) { if _, ok = expected[name]; !ok { return fmt.Errorf("detected new stable metric %s which isn't in testdata ", name) } From cccb3cf393ac6ffdb05af60e71a7fcaab4ed01e5 Mon Sep 17 00:00:00 2001 From: Catherine Fang Date: Wed, 19 Oct 2022 20:21:00 -0400 Subject: [PATCH 5/6] Sort metric labels --- tests/stable/check_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/stable/check_test.go b/tests/stable/check_test.go index c32302da3c..222e351def 100644 --- a/tests/stable/check_test.go +++ b/tests/stable/check_test.go @@ -191,9 +191,12 @@ func compare(collectedStableMetrics []Metric, expectedStableMetrics []Metric, sk if expectedMetric, ok = collected[name]; !ok { return fmt.Errorf("not found stable metric %s", name) } - if diff := cmp.Diff(metric, expectedMetric, cmpopts.IgnoreFields(Metric{}, "Help")); diff != "" { + if diff := cmp.Diff(metric, expectedMetric, cmpopts.IgnoreFields(Metric{}, "Help", "Labels")); diff != "" { return fmt.Errorf("stable metric %s mismatch (-want +got):\n%s", name, diff) } + if diff := cmp.Diff(metric.Labels, expectedMetric.Labels, cmpopts.SortSlices(func(l1, l2 string) bool { return l1 > l2 })); diff != "" { + return fmt.Errorf("stable metric label %s mismatch (-want +got):\n%s", name, diff) + } } for _, name := range sortedKeys(collected) { if _, ok = expected[name]; !ok { From 1bd69011374a4c0eaa318efcfacf1a1181771532 Mon Sep 17 00:00:00 2001 From: Catherine Fang Date: Thu, 20 Oct 2022 10:52:48 -0400 Subject: [PATCH 6/6] refactoring --- tests/e2e.sh | 2 - tests/stable/check_test.go | 131 ++++++++++++++++---------------- tests/stable/testdata/README.md | 23 ++++++ 3 files changed, 88 insertions(+), 68 deletions(-) create mode 100644 tests/stable/testdata/README.md diff --git a/tests/e2e.sh b/tests/e2e.sh index 3703caa94b..a5cf7ad0b3 100755 --- a/tests/e2e.sh +++ b/tests/e2e.sh @@ -15,7 +15,6 @@ # limitations under the License. set -e -set -x set -o pipefail case $(uname -m) in @@ -164,7 +163,6 @@ mkdir -p ${KUBE_STATE_METRICS_LOG_DIR} echo "access kube-state-metrics metrics endpoint" curl -s "http://localhost:8001/api/v1/namespaces/kube-system/services/kube-state-metrics:http-metrics/proxy/metrics" >${KUBE_STATE_METRICS_LOG_DIR}/metrics -cat ${KUBE_STATE_METRICS_LOG_DIR}/metrics # check stable metrics aren't changed go test ./tests/stable/check_test.go --collectedMetricsFile "$(realpath ${KUBE_STATE_METRICS_LOG_DIR}/metrics)" --stableMetricsFile "$(realpath tests/stable/testdata/stablemetrics.yaml)" diff --git a/tests/stable/check_test.go b/tests/stable/check_test.go index 222e351def..2dcfec7607 100644 --- a/tests/stable/check_test.go +++ b/tests/stable/check_test.go @@ -14,36 +14,37 @@ See the License for the specific language governing permissions and limitations under the License. */ +// To skip this test, put metric name into skippedStableMetrics. package stable import ( "flag" "fmt" - "sort" - "testing" - - "log" "os" + "path/filepath" + "sort" "strings" + "testing" "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" prommodel "github.com/prometheus/client_model/go" "github.com/prometheus/common/expfmt" "gopkg.in/yaml.v3" - "github.com/google/go-cmp/cmp/cmpopts" + "k8s.io/klog/v2" ) var promText string var stableYaml string -var skipStableMetrics = []string{} +var skippedStableMetrics = []string{} type Metric struct { - Name string `yaml:"name"` - Help string `yaml:"help"` - Type string - Labels []string + Name string `yaml:"name"` + Help string `yaml:"help"` + Type string `yaml:"type"` + Labels []string `yaml:"labels"` // Histogram type Buckets []float64 `yaml:"buckets,omitempty"` } @@ -57,7 +58,9 @@ func TestMain(m *testing.M) { func TestStableMetrics(t *testing.T) { mf, err := parsePromText(promText) - fatal(err) + if err != nil { + t.Fatalf("Can't parse collected prometheus metrics text. err = %v", err) + } collectedStableMetrics := extractStableMetrics(mf) printMetric(collectedStableMetrics) @@ -66,33 +69,59 @@ func TestStableMetrics(t *testing.T) { t.Fatalf("Can't read stable metrics from file. err = %v", err) } - err = compare(collectedStableMetrics, *expectedStableMetrics, skipStableMetrics) + err = compare(collectedStableMetrics, *expectedStableMetrics, skippedStableMetrics) if err != nil { t.Fatalf("Stable metrics changed: err = %v", err) } else { - fmt.Println("passed") + klog.Infoln("Passed") } - } -func fatal(err error) { - if err != nil { - log.Fatalln(err) +func compare(collectedStableMetrics []Metric, expectedStableMetrics []Metric, skippedStableMetrics []string) error { + skipMap := map[string]int{} + for _, v := range skippedStableMetrics { + skipMap[v] = 1 } + collected := convertToMap(collectedStableMetrics, skipMap) + expected := convertToMap(expectedStableMetrics, skipMap) + + var ok bool + + for _, name := range sortedKeys(expected) { + metric := expected[name] + var expectedMetric Metric + if expectedMetric, ok = collected[name]; !ok { + return fmt.Errorf("not found stable metric %s", name) + } + // Ingore Labels field due to ordering issue + if diff := cmp.Diff(metric, expectedMetric, cmpopts.IgnoreFields(Metric{}, "Help", "Labels")); diff != "" { + return fmt.Errorf("stable metric %s mismatch (-want +got):\n%s", name, diff) + } + // Compare Labels field after sorting + if diff := cmp.Diff(metric.Labels, expectedMetric.Labels, cmpopts.SortSlices(func(l1, l2 string) bool { return l1 > l2 })); diff != "" { + return fmt.Errorf("stable metric label %s mismatch (-want +got):\n%s", name, diff) + } + } + for _, name := range sortedKeys(collected) { + if _, ok = expected[name]; !ok { + return fmt.Errorf("detected new stable metric %s which isn't in testdata ", name) + } + } + return nil } func printMetric(metrics []Metric) { yamlData, err := yaml.Marshal(metrics) if err != nil { - fmt.Printf("error while Marshaling. %v", err) + klog.Errorf("error while Marshaling. %v", err) } - fmt.Println("---begin YAML file---") - fmt.Println(string(yamlData)) - fmt.Println("---end YAML file---") + klog.Infoln("---begin YAML file---") + klog.Infoln(string(yamlData)) + klog.Infoln("---end YAML file---") } func parsePromText(path string) (map[string]*prommodel.MetricFamily, error) { - reader, err := os.Open(path) + reader, err := os.Open(filepath.Clean(path)) if err != nil { return nil, err } @@ -116,6 +145,14 @@ func getBuckets(v *prommodel.MetricFamily) []float64 { return buckets } +func getLabels(m *prommodel.Metric) []string { + labels := []string{} + for _, y := range m.Label { + labels = append(labels, y.GetName()) + } + return labels +} + func extractStableMetrics(mf map[string]*prommodel.MetricFamily) []Metric { metrics := []Metric{} for _, v := range mf { @@ -123,25 +160,19 @@ func extractStableMetrics(mf map[string]*prommodel.MetricFamily) []Metric { if !strings.Contains(*(v.Help), "[STABLE]") { continue } - - m := Metric{ + metrics = append(metrics, Metric{ Name: *(v.Name), Help: *(v.Help), Type: (v.Type).String(), Buckets: getBuckets(v), - } - labels := []string{} - for _, y := range v.Metric[0].Label { - labels = append(labels, y.GetName()) - } - m.Labels = labels - metrics = append(metrics, m) + Labels: getLabels(v.Metric[0]), + }) } return metrics } func readYaml(filename string) (*[]Metric, error) { - buf, err := os.ReadFile(filename) + buf, err := os.ReadFile(filepath.Clean(filename)) if err != nil { return nil, err } @@ -153,17 +184,16 @@ func readYaml(filename string) (*[]Metric, error) { return c, err } -func convert2Map(metrics []Metric, skipMap map[string]int) map[string]Metric { +func convertToMap(metrics []Metric, skipMap map[string]int) map[string]Metric { name2Metric := map[string]Metric{} for _, v := range metrics { if _, ok := skipMap[v.Name]; ok { - fmt.Printf("skip, metric %s is in skip list\n", v.Name) + klog.Infof("skip, metric %s is in skip list\n", v.Name) continue } name2Metric[v.Name] = v } return name2Metric - } func sortedKeys(m map[string]Metric) []string { @@ -174,34 +204,3 @@ func sortedKeys(m map[string]Metric) []string { sort.Strings(keys) return keys } - -func compare(collectedStableMetrics []Metric, expectedStableMetrics []Metric, skipStableMetrics []string) error { - skipMap := map[string]int{} - for _, v := range skipStableMetrics { - skipMap[v] = 1 - } - collected := convert2Map(collectedStableMetrics, skipMap) - expected := convert2Map(expectedStableMetrics, skipMap) - - var ok bool - - for _, name := range sortedKeys(expected) { - metric := expected[name] - var expectedMetric Metric - if expectedMetric, ok = collected[name]; !ok { - return fmt.Errorf("not found stable metric %s", name) - } - if diff := cmp.Diff(metric, expectedMetric, cmpopts.IgnoreFields(Metric{}, "Help", "Labels")); diff != "" { - return fmt.Errorf("stable metric %s mismatch (-want +got):\n%s", name, diff) - } - if diff := cmp.Diff(metric.Labels, expectedMetric.Labels, cmpopts.SortSlices(func(l1, l2 string) bool { return l1 > l2 })); diff != "" { - return fmt.Errorf("stable metric label %s mismatch (-want +got):\n%s", name, diff) - } - } - for _, name := range sortedKeys(collected) { - if _, ok = expected[name]; !ok { - return fmt.Errorf("detected new stable metric %s which isn't in testdata ", name) - } - } - return nil -} diff --git a/tests/stable/testdata/README.md b/tests/stable/testdata/README.md new file mode 100644 index 0000000000..991150114c --- /dev/null +++ b/tests/stable/testdata/README.md @@ -0,0 +1,23 @@ +These stable metrics aren't covered in stablemetrics.yaml, because they aren't +exposed in KSM in the test cluster: +- kube_cronjob_spec_starting_deadline_seconds +- kube_cronjob_status_last_schedule_time +- kube_ingress_tls +- kube_job_complete +- kube_job_failed +- kube_job_spec_active_deadline_seconds +- kube_job_status_completion_time +- kube_node_spec_taint +- kube_persistentvolume_claim_ref +- kube_pod_completion_time +- kube_pod_init_container_info +- kube_pod_init_container_status_ready +- kube_pod_init_container_status_restarts_total +- kube_pod_init_container_status_running +- kube_pod_init_container_status_terminated +- kube_pod_init_container_status_waiting +- kube_pod_spec_volumes_persistentvolumeclaims_info +- kube_pod_spec_volumes_persistentvolumeclaims_readonly +- kube_pod_status_unschedulable +- kube_service_spec_external_ip +- kube_service_status_load_balancer_ingress