k8s.io/apiserver@v0.31.1/pkg/storage/etcd3/metrics/metrics_test.go (about)

     1  /*
     2  Copyright 2023 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  	"context"
    21  	"errors"
    22  	"strings"
    23  	"testing"
    24  	"time"
    25  
    26  	"k8s.io/component-base/metrics"
    27  	"k8s.io/component-base/metrics/testutil"
    28  )
    29  
    30  func TestRecordDecodeError(t *testing.T) {
    31  	registry := metrics.NewKubeRegistry()
    32  	defer registry.Reset()
    33  	registry.Register(decodeErrorCounts)
    34  	resourceName := "pods"
    35  	testedMetrics := "apiserver_storage_decode_errors_total"
    36  	testCases := []struct {
    37  		desc     string
    38  		resource string
    39  		want     string
    40  	}{
    41  		{
    42  			desc:     "test success",
    43  			resource: resourceName,
    44  			want: `
    45  		# HELP apiserver_storage_decode_errors_total [ALPHA] Number of stored object decode errors split by object type
    46  		# TYPE apiserver_storage_decode_errors_total counter
    47  		apiserver_storage_decode_errors_total{resource="pods"} 1
    48  `,
    49  		},
    50  	}
    51  
    52  	for _, test := range testCases {
    53  		t.Run(test.desc, func(t *testing.T) {
    54  			RecordDecodeError(test.resource)
    55  			if err := testutil.GatherAndCompare(registry, strings.NewReader(test.want), testedMetrics); err != nil {
    56  				t.Fatal(err)
    57  			}
    58  		})
    59  	}
    60  }
    61  
    62  func TestRecordEtcdRequest(t *testing.T) {
    63  	registry := metrics.NewKubeRegistry()
    64  
    65  	// modify default sinceInSeconds to constant NOW
    66  	sinceInSeconds = func(t time.Time) float64 {
    67  		return time.Unix(0, 300*int64(time.Millisecond)).Sub(t).Seconds()
    68  	}
    69  
    70  	testedMetrics := []metrics.Registerable{
    71  		etcdRequestCounts,
    72  		etcdRequestErrorCounts,
    73  		etcdRequestLatency,
    74  	}
    75  
    76  	testedMetricsName := make([]string, 0, len(testedMetrics))
    77  	for _, m := range testedMetrics {
    78  		registry.MustRegister(m)
    79  		testedMetricsName = append(testedMetricsName, m.FQName())
    80  	}
    81  
    82  	testCases := []struct {
    83  		desc      string
    84  		operation string
    85  		typ       string
    86  		err       error
    87  		startTime time.Time
    88  		want      string
    89  	}{
    90  		{
    91  			desc:      "success_request",
    92  			operation: "foo",
    93  			typ:       "bar",
    94  			err:       nil,
    95  			startTime: time.Unix(0, 0), // 0.3s
    96  			want: `# HELP etcd_request_duration_seconds [ALPHA] Etcd request latency in seconds for each operation and object type.
    97  # TYPE etcd_request_duration_seconds histogram
    98  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="0.005"} 0
    99  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="0.025"} 0
   100  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="0.05"} 0
   101  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="0.1"} 0
   102  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="0.2"} 0
   103  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="0.4"} 1
   104  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="0.6"} 1
   105  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="0.8"} 1
   106  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="1"} 1
   107  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="1.25"} 1
   108  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="1.5"} 1
   109  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="2"} 1
   110  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="3"} 1
   111  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="4"} 1
   112  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="5"} 1
   113  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="6"} 1
   114  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="8"} 1
   115  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="10"} 1
   116  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="15"} 1
   117  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="20"} 1
   118  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="30"} 1
   119  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="45"} 1
   120  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="60"} 1
   121  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="+Inf"} 1
   122  etcd_request_duration_seconds_sum{operation="foo",type="bar"} 0.3
   123  etcd_request_duration_seconds_count{operation="foo",type="bar"} 1
   124  # HELP etcd_requests_total [ALPHA] Etcd request counts for each operation and object type.
   125  # TYPE etcd_requests_total counter
   126  etcd_requests_total{operation="foo",type="bar"} 1
   127  `,
   128  		},
   129  		{
   130  			desc:      "failed_request",
   131  			operation: "foo",
   132  			typ:       "bar",
   133  			err:       errors.New("some error"),
   134  			startTime: time.Unix(0, 0), // 0.3s
   135  			want: `# HELP etcd_request_duration_seconds [ALPHA] Etcd request latency in seconds for each operation and object type.
   136  # TYPE etcd_request_duration_seconds histogram
   137  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="0.005"} 0
   138  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="0.025"} 0
   139  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="0.05"} 0
   140  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="0.1"} 0
   141  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="0.2"} 0
   142  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="0.4"} 1
   143  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="0.6"} 1
   144  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="0.8"} 1
   145  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="1"} 1
   146  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="1.25"} 1
   147  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="1.5"} 1
   148  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="2"} 1
   149  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="3"} 1
   150  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="4"} 1
   151  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="5"} 1
   152  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="6"} 1
   153  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="8"} 1
   154  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="10"} 1
   155  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="15"} 1
   156  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="20"} 1
   157  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="30"} 1
   158  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="45"} 1
   159  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="60"} 1
   160  etcd_request_duration_seconds_bucket{operation="foo",type="bar",le="+Inf"} 1
   161  etcd_request_duration_seconds_sum{operation="foo",type="bar"} 0.3
   162  etcd_request_duration_seconds_count{operation="foo",type="bar"} 1
   163  # HELP etcd_requests_total [ALPHA] Etcd request counts for each operation and object type.
   164  # TYPE etcd_requests_total counter
   165  etcd_requests_total{operation="foo",type="bar"} 1
   166  # HELP etcd_request_errors_total [ALPHA] Etcd failed request counts for each operation and object type.
   167  # TYPE etcd_request_errors_total counter
   168  etcd_request_errors_total{operation="foo",type="bar"} 1
   169  `,
   170  		},
   171  	}
   172  
   173  	for _, test := range testCases {
   174  		t.Run(test.desc, func(t *testing.T) {
   175  			defer registry.Reset()
   176  			RecordEtcdRequest(test.operation, test.typ, test.err, test.startTime)
   177  			if err := testutil.GatherAndCompare(registry, strings.NewReader(test.want), testedMetricsName...); err != nil {
   178  				t.Fatal(err)
   179  			}
   180  		})
   181  	}
   182  }
   183  
   184  func TestStorageSizeCollector(t *testing.T) {
   185  	registry := metrics.NewKubeRegistry()
   186  	registry.CustomMustRegister(storageMonitor)
   187  
   188  	testCases := []struct {
   189  		desc           string
   190  		getterOverride func() ([]Monitor, error)
   191  		err            error
   192  		want           string
   193  	}{
   194  		{
   195  			desc: "fake etcd getter",
   196  			getterOverride: func() ([]Monitor, error) {
   197  				return []Monitor{fakeEtcdMonitor{storageSize: 1e9}}, nil
   198  			},
   199  			err: nil,
   200  			want: `# HELP apiserver_storage_size_bytes [STABLE] Size of the storage database file physically allocated in bytes.
   201  			# TYPE apiserver_storage_size_bytes gauge
   202  			apiserver_storage_size_bytes{storage_cluster_id="etcd-0"} 1e+09
   203  			`,
   204  		},
   205  		{
   206  			desc:           "getters not configured",
   207  			getterOverride: nil,
   208  			err:            nil,
   209  			want:           ``,
   210  		},
   211  	}
   212  
   213  	for _, test := range testCases {
   214  		t.Run(test.desc, func(t *testing.T) {
   215  			defer registry.Reset()
   216  			if test.getterOverride != nil {
   217  				oldGetter := storageMonitor.monitorGetter
   218  				defer SetStorageMonitorGetter(oldGetter)
   219  				SetStorageMonitorGetter(test.getterOverride)
   220  			}
   221  			if err := testutil.GatherAndCompare(registry, strings.NewReader(test.want), "apiserver_storage_size_bytes"); err != nil {
   222  				t.Fatal(err)
   223  			}
   224  		})
   225  	}
   226  
   227  }
   228  
   229  func TestUpdateObjectCount(t *testing.T) {
   230  	registry := metrics.NewKubeRegistry()
   231  	registry.Register(objectCounts)
   232  	testedMetrics := "apiserver_storage_objects"
   233  
   234  	testCases := []struct {
   235  		desc     string
   236  		resource string
   237  		count    int64
   238  		want     string
   239  	}{
   240  		{
   241  			desc:     "successful fetch",
   242  			resource: "foo",
   243  			count:    10,
   244  			want: `# HELP apiserver_storage_objects [STABLE] Number of stored objects at the time of last check split by kind. In case of a fetching error, the value will be -1.
   245  # TYPE apiserver_storage_objects gauge
   246  apiserver_storage_objects{resource="foo"} 10
   247  `,
   248  		},
   249  		{
   250  			desc:     "failed fetch",
   251  			resource: "bar",
   252  			count:    -1,
   253  			want: `# HELP apiserver_storage_objects [STABLE] Number of stored objects at the time of last check split by kind. In case of a fetching error, the value will be -1.
   254  # TYPE apiserver_storage_objects gauge
   255  apiserver_storage_objects{resource="bar"} -1
   256  `,
   257  		},
   258  	}
   259  
   260  	for _, test := range testCases {
   261  		t.Run(test.desc, func(t *testing.T) {
   262  			defer registry.Reset()
   263  			UpdateObjectCount(test.resource, test.count)
   264  			if err := testutil.GatherAndCompare(registry, strings.NewReader(test.want), testedMetrics); err != nil {
   265  				t.Fatal(err)
   266  			}
   267  		})
   268  	}
   269  }
   270  
   271  type fakeEtcdMonitor struct {
   272  	storageSize int64
   273  }
   274  
   275  func (m fakeEtcdMonitor) Monitor(_ context.Context) (StorageMetrics, error) {
   276  	return StorageMetrics{Size: m.storageSize}, nil
   277  }
   278  
   279  func (m fakeEtcdMonitor) Close() error {
   280  	return nil
   281  }