k8s.io/kubernetes@v1.29.3/pkg/kubelet/metrics/collectors/volume_stats_test.go (about) 1 /* 2 Copyright 2018 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 collectors 18 19 import ( 20 "context" 21 "strings" 22 "testing" 23 24 "github.com/golang/mock/gomock" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/component-base/metrics/testutil" 27 statsapi "k8s.io/kubelet/pkg/apis/stats/v1alpha1" 28 statstest "k8s.io/kubernetes/pkg/kubelet/server/stats/testing" 29 ) 30 31 func newUint64Pointer(i uint64) *uint64 { 32 return &i 33 } 34 35 func TestVolumeStatsCollector(t *testing.T) { 36 ctx := context.Background() 37 // Fixed metadata on type and help text. We prepend this to every expected 38 // output so we only have to modify a single place when doing adjustments. 39 const metadata = ` 40 # HELP kubelet_volume_stats_available_bytes [ALPHA] Number of available bytes in the volume 41 # TYPE kubelet_volume_stats_available_bytes gauge 42 # HELP kubelet_volume_stats_capacity_bytes [ALPHA] Capacity in bytes of the volume 43 # TYPE kubelet_volume_stats_capacity_bytes gauge 44 # HELP kubelet_volume_stats_inodes [ALPHA] Maximum number of inodes in the volume 45 # TYPE kubelet_volume_stats_inodes gauge 46 # HELP kubelet_volume_stats_inodes_free [ALPHA] Number of free inodes in the volume 47 # TYPE kubelet_volume_stats_inodes_free gauge 48 # HELP kubelet_volume_stats_inodes_used [ALPHA] Number of used inodes in the volume 49 # TYPE kubelet_volume_stats_inodes_used gauge 50 # HELP kubelet_volume_stats_used_bytes [ALPHA] Number of used bytes in the volume 51 # TYPE kubelet_volume_stats_used_bytes gauge 52 # HELP kubelet_volume_stats_health_status_abnormal [ALPHA] Abnormal volume health status. The count is either 1 or 0. 1 indicates the volume is unhealthy, 0 indicates volume is healthy 53 # TYPE kubelet_volume_stats_health_status_abnormal gauge 54 ` 55 56 var ( 57 podStats = []statsapi.PodStats{ 58 { 59 PodRef: statsapi.PodReference{Name: "test-pod", Namespace: "test-namespace", UID: "UID_test-pod"}, 60 StartTime: metav1.Now(), 61 VolumeStats: []statsapi.VolumeStats{ 62 { 63 FsStats: statsapi.FsStats{ 64 Time: metav1.Now(), 65 AvailableBytes: newUint64Pointer(5.663154176e+09), 66 CapacityBytes: newUint64Pointer(1.0434699264e+10), 67 UsedBytes: newUint64Pointer(4.21789696e+09), 68 InodesFree: newUint64Pointer(655344), 69 Inodes: newUint64Pointer(655360), 70 InodesUsed: newUint64Pointer(16), 71 }, 72 Name: "test", 73 PVCRef: nil, 74 }, 75 { 76 FsStats: statsapi.FsStats{ 77 Time: metav1.Now(), 78 AvailableBytes: newUint64Pointer(5.663154176e+09), 79 CapacityBytes: newUint64Pointer(1.0434699264e+10), 80 UsedBytes: newUint64Pointer(4.21789696e+09), 81 InodesFree: newUint64Pointer(655344), 82 Inodes: newUint64Pointer(655360), 83 InodesUsed: newUint64Pointer(16), 84 }, 85 Name: "test", 86 PVCRef: &statsapi.PVCReference{ 87 Name: "testpvc", 88 Namespace: "testns", 89 }, 90 VolumeHealthStats: &statsapi.VolumeHealthStats{ 91 Abnormal: true, 92 }, 93 }, 94 }, 95 }, 96 { 97 // Another pod references the same PVC (test-namespace/testpvc). 98 PodRef: statsapi.PodReference{Name: "test-pod-2", Namespace: "test-namespace", UID: "UID_test-pod"}, 99 StartTime: metav1.Now(), 100 VolumeStats: []statsapi.VolumeStats{ 101 { 102 FsStats: statsapi.FsStats{ 103 Time: metav1.Now(), 104 AvailableBytes: newUint64Pointer(5.663154176e+09), 105 CapacityBytes: newUint64Pointer(1.0434699264e+10), 106 UsedBytes: newUint64Pointer(4.21789696e+09), 107 InodesFree: newUint64Pointer(655344), 108 Inodes: newUint64Pointer(655360), 109 InodesUsed: newUint64Pointer(16), 110 }, 111 Name: "test", 112 PVCRef: &statsapi.PVCReference{ 113 Name: "testpvc", 114 Namespace: "testns", 115 }, 116 VolumeHealthStats: &statsapi.VolumeHealthStats{ 117 Abnormal: true, 118 }, 119 }, 120 }, 121 }, 122 } 123 124 want = metadata + ` 125 kubelet_volume_stats_available_bytes{namespace="testns",persistentvolumeclaim="testpvc"} 5.663154176e+09 126 kubelet_volume_stats_capacity_bytes{namespace="testns",persistentvolumeclaim="testpvc"} 1.0434699264e+10 127 kubelet_volume_stats_inodes{namespace="testns",persistentvolumeclaim="testpvc"} 655360 128 kubelet_volume_stats_inodes_free{namespace="testns",persistentvolumeclaim="testpvc"} 655344 129 kubelet_volume_stats_inodes_used{namespace="testns",persistentvolumeclaim="testpvc"} 16 130 kubelet_volume_stats_used_bytes{namespace="testns",persistentvolumeclaim="testpvc"} 4.21789696e+09 131 kubelet_volume_stats_health_status_abnormal{namespace="testns",persistentvolumeclaim="testpvc"} 1 132 ` 133 134 metrics = []string{ 135 "kubelet_volume_stats_available_bytes", 136 "kubelet_volume_stats_capacity_bytes", 137 "kubelet_volume_stats_inodes", 138 "kubelet_volume_stats_inodes_free", 139 "kubelet_volume_stats_inodes_used", 140 "kubelet_volume_stats_used_bytes", 141 "kubelet_volume_stats_health_status_abnormal", 142 } 143 ) 144 145 mockCtrl := gomock.NewController(t) 146 defer mockCtrl.Finish() 147 mockStatsProvider := statstest.NewMockProvider(mockCtrl) 148 149 mockStatsProvider.EXPECT().ListPodStats(ctx).Return(podStats, nil).AnyTimes() 150 mockStatsProvider.EXPECT().ListPodStatsAndUpdateCPUNanoCoreUsage(ctx).Return(podStats, nil).AnyTimes() 151 if err := testutil.CustomCollectAndCompare(&volumeStatsCollector{statsProvider: mockStatsProvider}, strings.NewReader(want), metrics...); err != nil { 152 t.Errorf("unexpected collecting result:\n%s", err) 153 } 154 } 155 156 func TestVolumeStatsCollectorWithNullVolumeStatus(t *testing.T) { 157 ctx := context.Background() 158 // Fixed metadata on type and help text. We prepend this to every expected 159 // output so we only have to modify a single place when doing adjustments. 160 const metadata = ` 161 # HELP kubelet_volume_stats_available_bytes [ALPHA] Number of available bytes in the volume 162 # TYPE kubelet_volume_stats_available_bytes gauge 163 # HELP kubelet_volume_stats_capacity_bytes [ALPHA] Capacity in bytes of the volume 164 # TYPE kubelet_volume_stats_capacity_bytes gauge 165 # HELP kubelet_volume_stats_inodes [ALPHA] Maximum number of inodes in the volume 166 # TYPE kubelet_volume_stats_inodes gauge 167 # HELP kubelet_volume_stats_inodes_free [ALPHA] Number of free inodes in the volume 168 # TYPE kubelet_volume_stats_inodes_free gauge 169 # HELP kubelet_volume_stats_inodes_used [ALPHA] Number of used inodes in the volume 170 # TYPE kubelet_volume_stats_inodes_used gauge 171 # HELP kubelet_volume_stats_used_bytes [ALPHA] Number of used bytes in the volume 172 # TYPE kubelet_volume_stats_used_bytes gauge 173 ` 174 175 var ( 176 podStats = []statsapi.PodStats{ 177 { 178 PodRef: statsapi.PodReference{Name: "test-pod", Namespace: "test-namespace", UID: "UID_test-pod"}, 179 StartTime: metav1.Now(), 180 VolumeStats: []statsapi.VolumeStats{ 181 { 182 FsStats: statsapi.FsStats{ 183 Time: metav1.Now(), 184 AvailableBytes: newUint64Pointer(5.663154176e+09), 185 CapacityBytes: newUint64Pointer(1.0434699264e+10), 186 UsedBytes: newUint64Pointer(4.21789696e+09), 187 InodesFree: newUint64Pointer(655344), 188 Inodes: newUint64Pointer(655360), 189 InodesUsed: newUint64Pointer(16), 190 }, 191 Name: "test", 192 PVCRef: nil, 193 }, 194 { 195 FsStats: statsapi.FsStats{ 196 Time: metav1.Now(), 197 AvailableBytes: newUint64Pointer(5.663154176e+09), 198 CapacityBytes: newUint64Pointer(1.0434699264e+10), 199 UsedBytes: newUint64Pointer(4.21789696e+09), 200 InodesFree: newUint64Pointer(655344), 201 Inodes: newUint64Pointer(655360), 202 InodesUsed: newUint64Pointer(16), 203 }, 204 Name: "test", 205 PVCRef: &statsapi.PVCReference{ 206 Name: "testpvc", 207 Namespace: "testns", 208 }, 209 }, 210 }, 211 }, 212 } 213 214 want = metadata + ` 215 kubelet_volume_stats_available_bytes{namespace="testns",persistentvolumeclaim="testpvc"} 5.663154176e+09 216 kubelet_volume_stats_capacity_bytes{namespace="testns",persistentvolumeclaim="testpvc"} 1.0434699264e+10 217 kubelet_volume_stats_inodes{namespace="testns",persistentvolumeclaim="testpvc"} 655360 218 kubelet_volume_stats_inodes_free{namespace="testns",persistentvolumeclaim="testpvc"} 655344 219 kubelet_volume_stats_inodes_used{namespace="testns",persistentvolumeclaim="testpvc"} 16 220 kubelet_volume_stats_used_bytes{namespace="testns",persistentvolumeclaim="testpvc"} 4.21789696e+09 221 ` 222 223 metrics = []string{ 224 "kubelet_volume_stats_available_bytes", 225 "kubelet_volume_stats_capacity_bytes", 226 "kubelet_volume_stats_inodes", 227 "kubelet_volume_stats_inodes_free", 228 "kubelet_volume_stats_inodes_used", 229 "kubelet_volume_stats_used_bytes", 230 } 231 ) 232 233 mockCtrl := gomock.NewController(t) 234 defer mockCtrl.Finish() 235 mockStatsProvider := statstest.NewMockProvider(mockCtrl) 236 237 mockStatsProvider.EXPECT().ListPodStats(ctx).Return(podStats, nil).AnyTimes() 238 mockStatsProvider.EXPECT().ListPodStatsAndUpdateCPUNanoCoreUsage(ctx).Return(podStats, nil).AnyTimes() 239 if err := testutil.CustomCollectAndCompare(&volumeStatsCollector{statsProvider: mockStatsProvider}, strings.NewReader(want), metrics...); err != nil { 240 t.Errorf("unexpected collecting result:\n%s", err) 241 } 242 }