...

Source file src/k8s.io/component-base/metrics/registry_test.go

Documentation: k8s.io/component-base/metrics

     1  /*
     2  Copyright 2019 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package metrics
    18  
    19  import (
    20  	"strings"
    21  	"sync"
    22  	"testing"
    23  
    24  	"github.com/blang/semver/v4"
    25  	"github.com/prometheus/client_golang/prometheus"
    26  	"github.com/prometheus/client_golang/prometheus/testutil"
    27  	"github.com/stretchr/testify/assert"
    28  
    29  	apimachineryversion "k8s.io/apimachinery/pkg/version"
    30  )
    31  
    32  var (
    33  	v115         = semver.MustParse("1.15.0")
    34  	alphaCounter = NewCounter(
    35  		&CounterOpts{
    36  			Namespace:      "some_namespace",
    37  			Name:           "test_counter_name",
    38  			Subsystem:      "subsystem",
    39  			StabilityLevel: ALPHA,
    40  			Help:           "counter help",
    41  		},
    42  	)
    43  	alphaDeprecatedCounter = NewCounter(
    44  		&CounterOpts{
    45  			Namespace:         "some_namespace",
    46  			Name:              "test_alpha_dep_counter",
    47  			Subsystem:         "subsystem",
    48  			StabilityLevel:    ALPHA,
    49  			Help:              "counter help",
    50  			DeprecatedVersion: "1.15.0",
    51  		},
    52  	)
    53  	alphaHiddenCounter = NewCounter(
    54  		&CounterOpts{
    55  			Namespace:         "some_namespace",
    56  			Name:              "test_alpha_hidden_counter",
    57  			Subsystem:         "subsystem",
    58  			StabilityLevel:    ALPHA,
    59  			Help:              "counter help",
    60  			DeprecatedVersion: "1.14.0",
    61  		},
    62  	)
    63  )
    64  
    65  func TestShouldHide(t *testing.T) {
    66  	currentVersion := parseVersion(apimachineryversion.Info{
    67  		Major:      "1",
    68  		Minor:      "17",
    69  		GitVersion: "v1.17.1-alpha-1.12345",
    70  	})
    71  
    72  	var tests = []struct {
    73  		desc              string
    74  		deprecatedVersion string
    75  		shouldHide        bool
    76  	}{
    77  		{
    78  			desc:              "current minor release should not be hidden",
    79  			deprecatedVersion: "1.17.0",
    80  			shouldHide:        false,
    81  		},
    82  		{
    83  			desc:              "older minor release should be hidden",
    84  			deprecatedVersion: "1.16.0",
    85  			shouldHide:        true,
    86  		},
    87  	}
    88  
    89  	for _, test := range tests {
    90  		tc := test
    91  		t.Run(tc.desc, func(t *testing.T) {
    92  			result := shouldHide(&currentVersion, parseSemver(tc.deprecatedVersion))
    93  			assert.Equalf(t, tc.shouldHide, result, "expected should hide %v, but got %v", tc.shouldHide, result)
    94  		})
    95  	}
    96  }
    97  
    98  func TestRegister(t *testing.T) {
    99  	var tests = []struct {
   100  		desc                    string
   101  		metrics                 []*Counter
   102  		expectedErrors          []error
   103  		expectedIsCreatedValues []bool
   104  		expectedIsDeprecated    []bool
   105  		expectedIsHidden        []bool
   106  	}{
   107  		{
   108  			desc:                    "test alpha metric",
   109  			metrics:                 []*Counter{alphaCounter},
   110  			expectedErrors:          []error{nil},
   111  			expectedIsCreatedValues: []bool{true},
   112  			expectedIsDeprecated:    []bool{false},
   113  			expectedIsHidden:        []bool{false},
   114  		},
   115  		{
   116  			desc:                    "test registering same metric multiple times",
   117  			metrics:                 []*Counter{alphaCounter, alphaCounter},
   118  			expectedErrors:          []error{nil, prometheus.AlreadyRegisteredError{}},
   119  			expectedIsCreatedValues: []bool{true, true},
   120  			expectedIsDeprecated:    []bool{false, false},
   121  			expectedIsHidden:        []bool{false, false},
   122  		},
   123  		{
   124  			desc:                    "test alpha deprecated metric",
   125  			metrics:                 []*Counter{alphaDeprecatedCounter},
   126  			expectedErrors:          []error{nil},
   127  			expectedIsCreatedValues: []bool{true},
   128  			expectedIsDeprecated:    []bool{true},
   129  			expectedIsHidden:        []bool{false},
   130  		},
   131  		{
   132  			desc:                    "test alpha hidden metric",
   133  			metrics:                 []*Counter{alphaHiddenCounter},
   134  			expectedErrors:          []error{nil},
   135  			expectedIsCreatedValues: []bool{false},
   136  			expectedIsDeprecated:    []bool{true},
   137  			expectedIsHidden:        []bool{true},
   138  		},
   139  	}
   140  
   141  	for _, test := range tests {
   142  		t.Run(test.desc, func(t *testing.T) {
   143  			registry := newKubeRegistry(apimachineryversion.Info{
   144  				Major:      "1",
   145  				Minor:      "15",
   146  				GitVersion: "v1.15.0-alpha-1.12345",
   147  			})
   148  			for i, m := range test.metrics {
   149  				err := registry.Register(m)
   150  				if err != nil && err.Error() != test.expectedErrors[i].Error() {
   151  					t.Errorf("Got unexpected error %v, wanted %v", err, test.expectedErrors[i])
   152  				}
   153  				if m.IsCreated() != test.expectedIsCreatedValues[i] {
   154  					t.Errorf("Got isCreated == %v, wanted isCreated to be %v", m.IsCreated(), test.expectedIsCreatedValues[i])
   155  				}
   156  				if m.IsDeprecated() != test.expectedIsDeprecated[i] {
   157  					t.Errorf("Got IsDeprecated == %v, wanted IsDeprecated to be %v", m.IsDeprecated(), test.expectedIsDeprecated[i])
   158  				}
   159  				if m.IsHidden() != test.expectedIsHidden[i] {
   160  					t.Errorf("Got IsHidden == %v, wanted IsHidden to be %v", m.IsHidden(), test.expectedIsDeprecated[i])
   161  				}
   162  			}
   163  		})
   164  	}
   165  }
   166  
   167  func TestMustRegister(t *testing.T) {
   168  	var tests = []struct {
   169  		desc            string
   170  		metrics         []*Counter
   171  		registryVersion *semver.Version
   172  		expectedPanics  []bool
   173  	}{
   174  		{
   175  			desc:            "test alpha metric",
   176  			metrics:         []*Counter{alphaCounter},
   177  			registryVersion: &v115,
   178  			expectedPanics:  []bool{false},
   179  		},
   180  		{
   181  			desc:            "test registering same metric multiple times",
   182  			metrics:         []*Counter{alphaCounter, alphaCounter},
   183  			registryVersion: &v115,
   184  			expectedPanics:  []bool{false, true},
   185  		},
   186  		{
   187  			desc:            "test alpha deprecated metric",
   188  			metrics:         []*Counter{alphaDeprecatedCounter},
   189  			registryVersion: &v115,
   190  			expectedPanics:  []bool{false},
   191  		},
   192  		{
   193  			desc:            "test must registering same deprecated metric",
   194  			metrics:         []*Counter{alphaDeprecatedCounter, alphaDeprecatedCounter},
   195  			registryVersion: &v115,
   196  			expectedPanics:  []bool{false, true},
   197  		},
   198  		{
   199  			desc:            "test alpha hidden metric",
   200  			metrics:         []*Counter{alphaHiddenCounter},
   201  			registryVersion: &v115,
   202  			expectedPanics:  []bool{false},
   203  		},
   204  	}
   205  
   206  	for _, test := range tests {
   207  		t.Run(test.desc, func(t *testing.T) {
   208  			registry := newKubeRegistry(apimachineryversion.Info{
   209  				Major:      "1",
   210  				Minor:      "15",
   211  				GitVersion: "v1.15.0-alpha-1.12345",
   212  			})
   213  			for i, m := range test.metrics {
   214  				if test.expectedPanics[i] {
   215  					assert.Panics(t,
   216  						func() { registry.MustRegister(m) },
   217  						"Did not panic even though we expected it.")
   218  				} else {
   219  					registry.MustRegister(m)
   220  				}
   221  			}
   222  		})
   223  	}
   224  
   225  }
   226  func TestShowHiddenMetric(t *testing.T) {
   227  	registry := newKubeRegistry(apimachineryversion.Info{
   228  		Major:      "1",
   229  		Minor:      "15",
   230  		GitVersion: "v1.15.0-alpha-1.12345",
   231  	})
   232  
   233  	expectedMetricCount := 0
   234  	registry.MustRegister(alphaHiddenCounter)
   235  
   236  	ms, err := registry.Gather()
   237  	assert.Nil(t, err, "Gather failed %v", err)
   238  	assert.Equalf(t, expectedMetricCount, len(ms), "Got %v metrics, Want: %v metrics", len(ms), expectedMetricCount)
   239  
   240  	showHidden.Store(true)
   241  	defer showHidden.Store(false)
   242  	registry.MustRegister(NewCounter(
   243  		&CounterOpts{
   244  			Namespace:         "some_namespace",
   245  			Name:              "test_alpha_show_hidden_counter",
   246  			Subsystem:         "subsystem",
   247  			StabilityLevel:    ALPHA,
   248  			Help:              "counter help",
   249  			DeprecatedVersion: "1.14.0",
   250  		},
   251  	))
   252  	expectedMetricCount = 1
   253  
   254  	ms, err = registry.Gather()
   255  	assert.Nil(t, err, "Gather failed %v", err)
   256  	assert.Equalf(t, expectedMetricCount, len(ms), "Got %v metrics, Want: %v metrics", len(ms), expectedMetricCount)
   257  }
   258  
   259  func TestValidateShowHiddenMetricsVersion(t *testing.T) {
   260  	currentVersion := parseVersion(apimachineryversion.Info{
   261  		Major:      "1",
   262  		Minor:      "17",
   263  		GitVersion: "v1.17.1-alpha-1.12345",
   264  	})
   265  
   266  	var tests = []struct {
   267  		desc          string
   268  		targetVersion string
   269  		expectedError bool
   270  	}{
   271  		{
   272  			desc:          "invalid version is not allowed",
   273  			targetVersion: "1.invalid",
   274  			expectedError: true,
   275  		},
   276  		{
   277  			desc:          "patch version is not allowed",
   278  			targetVersion: "1.16.0",
   279  			expectedError: true,
   280  		},
   281  		{
   282  			desc:          "old version is not allowed",
   283  			targetVersion: "1.15",
   284  			expectedError: true,
   285  		},
   286  		{
   287  			desc:          "new version is not allowed",
   288  			targetVersion: "1.17",
   289  			expectedError: true,
   290  		},
   291  		{
   292  			desc:          "valid version is allowed",
   293  			targetVersion: "1.16",
   294  			expectedError: false,
   295  		},
   296  	}
   297  
   298  	for _, test := range tests {
   299  		tc := test
   300  		t.Run(tc.desc, func(t *testing.T) {
   301  			err := validateShowHiddenMetricsVersion(currentVersion, tc.targetVersion)
   302  
   303  			if tc.expectedError {
   304  				assert.Errorf(t, err, "Failed to test: %s", tc.desc)
   305  			} else {
   306  				assert.NoErrorf(t, err, "Failed to test: %s", tc.desc)
   307  			}
   308  		})
   309  	}
   310  }
   311  
   312  func TestEnableHiddenMetrics(t *testing.T) {
   313  	currentVersion := apimachineryversion.Info{
   314  		Major:      "1",
   315  		Minor:      "17",
   316  		GitVersion: "v1.17.1-alpha-1.12345",
   317  	}
   318  
   319  	var tests = []struct {
   320  		name           string
   321  		fqName         string
   322  		counter        *Counter
   323  		mustRegister   bool
   324  		expectedMetric string
   325  	}{
   326  		{
   327  			name:   "hide by register",
   328  			fqName: "hidden_metric_register",
   329  			counter: NewCounter(&CounterOpts{
   330  				Name:              "hidden_metric_register",
   331  				Help:              "counter help",
   332  				StabilityLevel:    STABLE,
   333  				DeprecatedVersion: "1.16.0",
   334  			}),
   335  			mustRegister: false,
   336  			expectedMetric: `
   337  				# HELP hidden_metric_register [STABLE] (Deprecated since 1.16.0) counter help
   338  				# TYPE hidden_metric_register counter
   339  				hidden_metric_register 1
   340  				`,
   341  		},
   342  		{
   343  			name:   "hide by must register",
   344  			fqName: "hidden_metric_must_register",
   345  			counter: NewCounter(&CounterOpts{
   346  				Name:              "hidden_metric_must_register",
   347  				Help:              "counter help",
   348  				StabilityLevel:    STABLE,
   349  				DeprecatedVersion: "1.16.0",
   350  			}),
   351  			mustRegister: true,
   352  			expectedMetric: `
   353  				# HELP hidden_metric_must_register [STABLE] (Deprecated since 1.16.0) counter help
   354  				# TYPE hidden_metric_must_register counter
   355  				hidden_metric_must_register 1
   356  				`,
   357  		},
   358  	}
   359  
   360  	for _, test := range tests {
   361  		tc := test
   362  		t.Run(tc.name, func(t *testing.T) {
   363  			registry := newKubeRegistry(currentVersion)
   364  			if tc.mustRegister {
   365  				registry.MustRegister(tc.counter)
   366  			} else {
   367  				_ = registry.Register(tc.counter)
   368  			}
   369  
   370  			tc.counter.Inc() // no-ops, because counter hasn't been initialized
   371  			if err := testutil.GatherAndCompare(registry, strings.NewReader(""), tc.fqName); err != nil {
   372  				t.Fatal(err)
   373  			}
   374  
   375  			SetShowHidden()
   376  			defer func() {
   377  				showHiddenOnce = *new(sync.Once)
   378  				showHidden.Store(false)
   379  			}()
   380  
   381  			tc.counter.Inc()
   382  			if err := testutil.GatherAndCompare(registry, strings.NewReader(tc.expectedMetric), tc.fqName); err != nil {
   383  				t.Fatal(err)
   384  			}
   385  		})
   386  	}
   387  }
   388  
   389  func TestEnableHiddenStableCollector(t *testing.T) {
   390  	var currentVersion = apimachineryversion.Info{
   391  		Major:      "1",
   392  		Minor:      "17",
   393  		GitVersion: "v1.17.0-alpha-1.12345",
   394  	}
   395  	var normal = NewDesc("test_enable_hidden_custom_metric_normal", "this is a normal metric", []string{"name"}, nil, STABLE, "")
   396  	var hiddenA = NewDesc("test_enable_hidden_custom_metric_hidden_a", "this is the hidden metric A", []string{"name"}, nil, STABLE, "1.16.0")
   397  	var hiddenB = NewDesc("test_enable_hidden_custom_metric_hidden_b", "this is the hidden metric B", []string{"name"}, nil, STABLE, "1.16.0")
   398  
   399  	var tests = []struct {
   400  		name                      string
   401  		descriptors               []*Desc
   402  		metricNames               []string
   403  		expectMetricsBeforeEnable string
   404  		expectMetricsAfterEnable  string
   405  	}{
   406  		{
   407  			name:        "all hidden",
   408  			descriptors: []*Desc{hiddenA, hiddenB},
   409  			metricNames: []string{"test_enable_hidden_custom_metric_hidden_a",
   410  				"test_enable_hidden_custom_metric_hidden_b"},
   411  			expectMetricsBeforeEnable: "",
   412  			expectMetricsAfterEnable: `
   413          		# HELP test_enable_hidden_custom_metric_hidden_a [STABLE] (Deprecated since 1.16.0) this is the hidden metric A
   414          		# TYPE test_enable_hidden_custom_metric_hidden_a gauge
   415          		test_enable_hidden_custom_metric_hidden_a{name="value"} 1
   416          		# HELP test_enable_hidden_custom_metric_hidden_b [STABLE] (Deprecated since 1.16.0) this is the hidden metric B
   417          		# TYPE test_enable_hidden_custom_metric_hidden_b gauge
   418          		test_enable_hidden_custom_metric_hidden_b{name="value"} 1
   419  			`,
   420  		},
   421  		{
   422  			name:        "partial hidden",
   423  			descriptors: []*Desc{normal, hiddenA, hiddenB},
   424  			metricNames: []string{"test_enable_hidden_custom_metric_normal",
   425  				"test_enable_hidden_custom_metric_hidden_a",
   426  				"test_enable_hidden_custom_metric_hidden_b"},
   427  			expectMetricsBeforeEnable: `
   428          		# HELP test_enable_hidden_custom_metric_normal [STABLE] this is a normal metric
   429          		# TYPE test_enable_hidden_custom_metric_normal gauge
   430          		test_enable_hidden_custom_metric_normal{name="value"} 1
   431  			`,
   432  			expectMetricsAfterEnable: `
   433          		# HELP test_enable_hidden_custom_metric_normal [STABLE] this is a normal metric
   434          		# TYPE test_enable_hidden_custom_metric_normal gauge
   435          		test_enable_hidden_custom_metric_normal{name="value"} 1
   436          		# HELP test_enable_hidden_custom_metric_hidden_a [STABLE] (Deprecated since 1.16.0) this is the hidden metric A
   437          		# TYPE test_enable_hidden_custom_metric_hidden_a gauge
   438          		test_enable_hidden_custom_metric_hidden_a{name="value"} 1
   439          		# HELP test_enable_hidden_custom_metric_hidden_b [STABLE] (Deprecated since 1.16.0) this is the hidden metric B
   440          		# TYPE test_enable_hidden_custom_metric_hidden_b gauge
   441          		test_enable_hidden_custom_metric_hidden_b{name="value"} 1
   442  			`,
   443  		},
   444  	}
   445  
   446  	for _, test := range tests {
   447  		tc := test
   448  		t.Run(tc.name, func(t *testing.T) {
   449  			registry := newKubeRegistry(currentVersion)
   450  			customCollector := newTestCustomCollector(tc.descriptors...)
   451  			registry.CustomMustRegister(customCollector)
   452  
   453  			if err := testutil.GatherAndCompare(registry, strings.NewReader(tc.expectMetricsBeforeEnable), tc.metricNames...); err != nil {
   454  				t.Fatalf("before enable test failed: %v", err)
   455  			}
   456  
   457  			SetShowHidden()
   458  			defer func() {
   459  				showHiddenOnce = *new(sync.Once)
   460  				showHidden.Store(false)
   461  			}()
   462  
   463  			if err := testutil.GatherAndCompare(registry, strings.NewReader(tc.expectMetricsAfterEnable), tc.metricNames...); err != nil {
   464  				t.Fatalf("after enable test failed: %v", err)
   465  			}
   466  
   467  			// refresh descriptors so as to share with cases.
   468  			for _, d := range tc.descriptors {
   469  				d.ClearState()
   470  			}
   471  		})
   472  	}
   473  }
   474  
   475  func TestRegistryReset(t *testing.T) {
   476  	currentVersion := apimachineryversion.Info{
   477  		Major:      "1",
   478  		Minor:      "17",
   479  		GitVersion: "v1.17.1-alpha-1.12345",
   480  	}
   481  	registry := newKubeRegistry(currentVersion)
   482  	resettableMetric := NewCounterVec(&CounterOpts{
   483  		Name: "reset_metric",
   484  		Help: "this metric can be reset",
   485  	}, []string{"label"})
   486  	// gauges cannot be reset
   487  	nonResettableMetric := NewGauge(&GaugeOpts{
   488  		Name: "not_reset_metric",
   489  		Help: "this metric cannot be reset",
   490  	})
   491  
   492  	registry.MustRegister(resettableMetric)
   493  	registry.MustRegister(nonResettableMetric)
   494  	resettableMetric.WithLabelValues("one").Inc()
   495  	resettableMetric.WithLabelValues("two").Inc()
   496  	resettableMetric.WithLabelValues("two").Inc()
   497  	nonResettableMetric.Inc()
   498  
   499  	nonResettableOutput := `
   500          # HELP not_reset_metric [ALPHA] this metric cannot be reset
   501          # TYPE not_reset_metric gauge
   502          not_reset_metric 1
   503  `
   504  	resettableOutput := `
   505          # HELP reset_metric [ALPHA] this metric can be reset
   506          # TYPE reset_metric counter
   507          reset_metric{label="one"} 1
   508          reset_metric{label="two"} 2
   509  `
   510  	if err := testutil.GatherAndCompare(registry, strings.NewReader(nonResettableOutput+resettableOutput), "reset_metric", "not_reset_metric"); err != nil {
   511  		t.Fatal(err)
   512  	}
   513  	registry.Reset()
   514  	if err := testutil.GatherAndCompare(registry, strings.NewReader(nonResettableOutput), "reset_metric", "not_reset_metric"); err != nil {
   515  		t.Fatal(err)
   516  	}
   517  }
   518  
   519  func TestDisabledMetrics(t *testing.T) {
   520  	o := NewOptions()
   521  	o.DisabledMetrics = []string{"should_be_disabled"}
   522  	o.Apply()
   523  	currentVersion := apimachineryversion.Info{
   524  		Major:      "1",
   525  		Minor:      "17",
   526  		GitVersion: "v1.17.1-alpha-1.12345",
   527  	}
   528  	registry := newKubeRegistry(currentVersion)
   529  	disabledMetric := NewCounterVec(&CounterOpts{
   530  		Name: "should_be_disabled",
   531  		Help: "this metric should be disabled",
   532  	}, []string{"label"})
   533  	// gauges cannot be reset
   534  	enabledMetric := NewGauge(&GaugeOpts{
   535  		Name: "should_be_enabled",
   536  		Help: "this metric should not be disabled",
   537  	})
   538  
   539  	registry.MustRegister(disabledMetric)
   540  	registry.MustRegister(enabledMetric)
   541  	disabledMetric.WithLabelValues("one").Inc()
   542  	disabledMetric.WithLabelValues("two").Inc()
   543  	disabledMetric.WithLabelValues("two").Inc()
   544  	enabledMetric.Inc()
   545  
   546  	enabledMetricOutput := `
   547          # HELP should_be_enabled [ALPHA] this metric should not be disabled
   548          # TYPE should_be_enabled gauge
   549          should_be_enabled 1
   550  `
   551  
   552  	if err := testutil.GatherAndCompare(registry, strings.NewReader(enabledMetricOutput), "should_be_disabled", "should_be_enabled"); err != nil {
   553  		t.Fatal(err)
   554  	}
   555  }
   556  

View as plain text