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 }