From 39dbb24d13f7528d65c323822259c8bf3d09fd84 Mon Sep 17 00:00:00 2001 From: beorn7 Date: Fri, 24 Apr 2020 23:44:21 +0200 Subject: [PATCH] Add helper functions for linting to testutil Signed-off-by: beorn7 --- prometheus/testutil/lint.go | 46 +++++++++++++++++++++ prometheus/testutil/lint_test.go | 70 ++++++++++++++++++++++++++++++++ prometheus/testutil/testutil.go | 4 ++ 3 files changed, 120 insertions(+) create mode 100644 prometheus/testutil/lint.go create mode 100644 prometheus/testutil/lint_test.go diff --git a/prometheus/testutil/lint.go b/prometheus/testutil/lint.go new file mode 100644 index 0000000..1e006f3 --- /dev/null +++ b/prometheus/testutil/lint.go @@ -0,0 +1,46 @@ +// Copyright 2020 The Prometheus 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" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/testutil/promlint" +) + +// CollectAndLint registers the provided Collector with a newly created +// pedantic Registry. It then does the same as GatherAndLint, gathering the +// metrics from the pedantic Registry. +func CollectAndLint(c prometheus.Collector, metricNames ...string) ([]promlint.Problem, error) { + reg := prometheus.NewPedanticRegistry() + if err := reg.Register(c); err != nil { + return nil, fmt.Errorf("registering collector failed: %s", err) + } + return GatherAndLint(reg, metricNames...) +} + +// GatherAndLint gathers all metrics from the provided Gatherer and checks them +// with the linter in the promlint package. If any metricNames are provided, +// only metrics with those names are checked. +func GatherAndLint(g prometheus.Gatherer, metricNames ...string) ([]promlint.Problem, error) { + got, err := g.Gather() + if err != nil { + return nil, fmt.Errorf("gathering metrics failed: %s", err) + } + if metricNames != nil { + got = filterMetrics(got, metricNames) + } + return promlint.NewWithMetricFamilies(got).Lint() +} diff --git a/prometheus/testutil/lint_test.go b/prometheus/testutil/lint_test.go new file mode 100644 index 0000000..1230cc6 --- /dev/null +++ b/prometheus/testutil/lint_test.go @@ -0,0 +1,70 @@ +// Copyright 2020 The Prometheus 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 ( + "testing" + + "github.com/prometheus/client_golang/prometheus" +) + +func TestCollectAndLintGood(t *testing.T) { + cnt := prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "some_total", + Help: "A value that represents a counter.", + ConstLabels: prometheus.Labels{ + "label1": "value1", + }, + }, + []string{"foo"}, + ) + cnt.WithLabelValues("bar") + cnt.WithLabelValues("baz") + + problems, err := CollectAndLint(cnt) + if err != nil { + t.Error("Unexpected error:", err) + } + if len(problems) > 0 { + t.Error("Unexpected lint problems:", problems) + } +} + +func TestCollectAndLintBad(t *testing.T) { + cnt := prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "someThing_ms", + Help: "A value that represents a counter.", + ConstLabels: prometheus.Labels{ + "label1": "value1", + }, + }, + []string{"fooBar"}, + ) + cnt.WithLabelValues("bar") + cnt.WithLabelValues("baz") + + problems, err := CollectAndLint(cnt) + if err != nil { + t.Error("Unexpected error:", err) + } + if len(problems) < 5 { + // The exact nature of the lint problems found is tested within + // the promlint package itself. Here we only want to make sure + // that the collector successfully hit the linter and got enough + // problems flagged. + t.Error("Not enough lint problems found.") + } +} diff --git a/prometheus/testutil/testutil.go b/prometheus/testutil/testutil.go index cb09839..0e32d9d 100644 --- a/prometheus/testutil/testutil.go +++ b/prometheus/testutil/testutil.go @@ -31,6 +31,10 @@ // testing custom prometheus.Collector implementations and in particular whole // exporters, i.e. programs that retrieve telemetry data from a 3rd party source // and convert it into Prometheus metrics. +// +// In a similar pattern, CollectAndLint and GatherAndLint can be used to detect +// metrics that have issues with their name, type, or metadata without being +// necessarily invalid, e.g. a counter with a name missing the “_total” suffix. package testutil import (