k8s.io/kubernetes@v1.29.3/pkg/kubelet/server/stats/summary_test.go (about) 1 //go:build !windows 2 // +build !windows 3 4 /* 5 Copyright 2016 The Kubernetes Authors. 6 7 Licensed under the Apache License, Version 2.0 (the "License"); 8 you may not use this file except in compliance with the License. 9 You may obtain a copy of the License at 10 11 http://www.apache.org/licenses/LICENSE-2.0 12 13 Unless required by applicable law or agreed to in writing, software 14 distributed under the License is distributed on an "AS IS" BASIS, 15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 See the License for the specific language governing permissions and 17 limitations under the License. 18 */ 19 20 package stats 21 22 import ( 23 "context" 24 "testing" 25 "time" 26 27 "github.com/golang/mock/gomock" 28 fuzz "github.com/google/gofuzz" 29 "github.com/stretchr/testify/assert" 30 31 v1 "k8s.io/api/core/v1" 32 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 33 statsapi "k8s.io/kubelet/pkg/apis/stats/v1alpha1" 34 "k8s.io/kubernetes/pkg/kubelet/cm" 35 statstest "k8s.io/kubernetes/pkg/kubelet/server/stats/testing" 36 ) 37 38 var ( 39 imageFsStats = getFsStats() 40 rootFsStats = getFsStats() 41 node = &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "test-node"}} 42 nodeConfig = cm.NodeConfig{ 43 RuntimeCgroupsName: "/runtime", 44 SystemCgroupsName: "/misc", 45 KubeletCgroupsName: "/kubelet", 46 } 47 cgroupRoot = "/kubepods" 48 rlimitStats = getRlimitStats() 49 ) 50 51 func TestSummaryProviderGetStatsNoSplitFileSystem(t *testing.T) { 52 ctx := context.Background() 53 assert := assert.New(t) 54 55 podStats := []statsapi.PodStats{ 56 { 57 PodRef: statsapi.PodReference{Name: "test-pod", Namespace: "test-namespace", UID: "UID_test-pod"}, 58 StartTime: metav1.NewTime(time.Now()), 59 Containers: []statsapi.ContainerStats{*getContainerStats()}, 60 Network: getNetworkStats(), 61 VolumeStats: []statsapi.VolumeStats{*getVolumeStats()}, 62 }, 63 } 64 cgroupStatsMap := map[string]struct { 65 cs *statsapi.ContainerStats 66 ns *statsapi.NetworkStats 67 }{ 68 "/": {cs: getContainerStats(), ns: getNetworkStats()}, 69 "/runtime": {cs: getContainerStats(), ns: getNetworkStats()}, 70 "/misc": {cs: getContainerStats(), ns: getNetworkStats()}, 71 "/kubelet": {cs: getContainerStats(), ns: getNetworkStats()}, 72 "/pods": {cs: getContainerStats(), ns: getNetworkStats()}, 73 } 74 75 mockCtrl := gomock.NewController(t) 76 defer mockCtrl.Finish() 77 mockStatsProvider := statstest.NewMockProvider(mockCtrl) 78 79 mockStatsProvider.EXPECT().GetNode().Return(node, nil) 80 mockStatsProvider.EXPECT().GetNodeConfig().Return(nodeConfig) 81 mockStatsProvider.EXPECT().GetPodCgroupRoot().Return(cgroupRoot) 82 mockStatsProvider.EXPECT().ListPodStats(ctx).Return(podStats, nil).AnyTimes() 83 mockStatsProvider.EXPECT().ListPodStatsAndUpdateCPUNanoCoreUsage(ctx).Return(podStats, nil) 84 mockStatsProvider.EXPECT().ImageFsStats(ctx).Return(imageFsStats, imageFsStats, nil) 85 mockStatsProvider.EXPECT().RootFsStats().Return(rootFsStats, nil) 86 mockStatsProvider.EXPECT().RlimitStats().Return(rlimitStats, nil) 87 mockStatsProvider.EXPECT().GetCgroupStats("/", true).Return(cgroupStatsMap["/"].cs, cgroupStatsMap["/"].ns, nil) 88 mockStatsProvider.EXPECT().GetCgroupStats("/runtime", false).Return(cgroupStatsMap["/runtime"].cs, cgroupStatsMap["/runtime"].ns, nil) 89 mockStatsProvider.EXPECT().GetCgroupStats("/misc", false).Return(cgroupStatsMap["/misc"].cs, cgroupStatsMap["/misc"].ns, nil) 90 mockStatsProvider.EXPECT().GetCgroupStats("/kubelet", false).Return(cgroupStatsMap["/kubelet"].cs, cgroupStatsMap["/kubelet"].ns, nil) 91 mockStatsProvider.EXPECT().GetCgroupStats("/kubepods", true).Return(cgroupStatsMap["/pods"].cs, cgroupStatsMap["/pods"].ns, nil) 92 93 kubeletCreationTime := metav1.Now() 94 systemBootTime := metav1.Now() 95 provider := summaryProviderImpl{kubeletCreationTime: kubeletCreationTime, systemBootTime: systemBootTime, provider: mockStatsProvider} 96 summary, err := provider.Get(ctx, true) 97 assert.NoError(err) 98 99 assert.Equal(summary.Node.NodeName, "test-node") 100 assert.Equal(summary.Node.StartTime, systemBootTime) 101 assert.Equal(summary.Node.CPU, cgroupStatsMap["/"].cs.CPU) 102 assert.Equal(summary.Node.Memory, cgroupStatsMap["/"].cs.Memory) 103 assert.Equal(summary.Node.Swap, cgroupStatsMap["/"].cs.Swap) 104 assert.Equal(summary.Node.Network, cgroupStatsMap["/"].ns) 105 assert.Equal(summary.Node.Fs, rootFsStats) 106 assert.Equal(summary.Node.Runtime, &statsapi.RuntimeStats{ContainerFs: imageFsStats, ImageFs: imageFsStats}) 107 108 assert.Equal(len(summary.Node.SystemContainers), 4) 109 assert.Contains(summary.Node.SystemContainers, statsapi.ContainerStats{ 110 Name: "kubelet", 111 StartTime: kubeletCreationTime, 112 CPU: cgroupStatsMap["/kubelet"].cs.CPU, 113 Memory: cgroupStatsMap["/kubelet"].cs.Memory, 114 Accelerators: cgroupStatsMap["/kubelet"].cs.Accelerators, 115 UserDefinedMetrics: cgroupStatsMap["/kubelet"].cs.UserDefinedMetrics, 116 Swap: cgroupStatsMap["/kubelet"].cs.Swap, 117 }) 118 assert.Contains(summary.Node.SystemContainers, statsapi.ContainerStats{ 119 Name: "misc", 120 StartTime: cgroupStatsMap["/misc"].cs.StartTime, 121 CPU: cgroupStatsMap["/misc"].cs.CPU, 122 Memory: cgroupStatsMap["/misc"].cs.Memory, 123 Accelerators: cgroupStatsMap["/misc"].cs.Accelerators, 124 UserDefinedMetrics: cgroupStatsMap["/misc"].cs.UserDefinedMetrics, 125 Swap: cgroupStatsMap["/misc"].cs.Swap, 126 }) 127 assert.Contains(summary.Node.SystemContainers, statsapi.ContainerStats{ 128 Name: "runtime", 129 StartTime: cgroupStatsMap["/runtime"].cs.StartTime, 130 CPU: cgroupStatsMap["/runtime"].cs.CPU, 131 Memory: cgroupStatsMap["/runtime"].cs.Memory, 132 Accelerators: cgroupStatsMap["/runtime"].cs.Accelerators, 133 UserDefinedMetrics: cgroupStatsMap["/runtime"].cs.UserDefinedMetrics, 134 Swap: cgroupStatsMap["/runtime"].cs.Swap, 135 }) 136 assert.Contains(summary.Node.SystemContainers, statsapi.ContainerStats{ 137 Name: "pods", 138 StartTime: cgroupStatsMap["/pods"].cs.StartTime, 139 CPU: cgroupStatsMap["/pods"].cs.CPU, 140 Memory: cgroupStatsMap["/pods"].cs.Memory, 141 Accelerators: cgroupStatsMap["/pods"].cs.Accelerators, 142 UserDefinedMetrics: cgroupStatsMap["/pods"].cs.UserDefinedMetrics, 143 Swap: cgroupStatsMap["/pods"].cs.Swap, 144 }) 145 assert.Equal(summary.Pods, podStats) 146 } 147 148 func TestSummaryProviderGetStatsSplitImageFs(t *testing.T) { 149 ctx := context.Background() 150 assert := assert.New(t) 151 152 podStats := []statsapi.PodStats{ 153 { 154 PodRef: statsapi.PodReference{Name: "test-pod", Namespace: "test-namespace", UID: "UID_test-pod"}, 155 StartTime: metav1.NewTime(time.Now()), 156 Containers: []statsapi.ContainerStats{*getContainerStats()}, 157 Network: getNetworkStats(), 158 VolumeStats: []statsapi.VolumeStats{*getVolumeStats()}, 159 }, 160 } 161 cgroupStatsMap := map[string]struct { 162 cs *statsapi.ContainerStats 163 ns *statsapi.NetworkStats 164 }{ 165 "/": {cs: getContainerStats(), ns: getNetworkStats()}, 166 "/runtime": {cs: getContainerStats(), ns: getNetworkStats()}, 167 "/misc": {cs: getContainerStats(), ns: getNetworkStats()}, 168 "/kubelet": {cs: getContainerStats(), ns: getNetworkStats()}, 169 "/pods": {cs: getContainerStats(), ns: getNetworkStats()}, 170 } 171 172 mockCtrl := gomock.NewController(t) 173 defer mockCtrl.Finish() 174 mockStatsProvider := statstest.NewMockProvider(mockCtrl) 175 176 mockStatsProvider.EXPECT().GetNode().Return(node, nil) 177 mockStatsProvider.EXPECT().GetNodeConfig().Return(nodeConfig) 178 mockStatsProvider.EXPECT().GetPodCgroupRoot().Return(cgroupRoot) 179 mockStatsProvider.EXPECT().ListPodStats(ctx).Return(podStats, nil).AnyTimes() 180 mockStatsProvider.EXPECT().ListPodStatsAndUpdateCPUNanoCoreUsage(ctx).Return(podStats, nil) 181 mockStatsProvider.EXPECT().RootFsStats().Return(rootFsStats, nil) 182 mockStatsProvider.EXPECT().RlimitStats().Return(rlimitStats, nil) 183 mockStatsProvider.EXPECT().GetCgroupStats("/", true).Return(cgroupStatsMap["/"].cs, cgroupStatsMap["/"].ns, nil) 184 mockStatsProvider.EXPECT().GetCgroupStats("/runtime", false).Return(cgroupStatsMap["/runtime"].cs, cgroupStatsMap["/runtime"].ns, nil) 185 mockStatsProvider.EXPECT().GetCgroupStats("/misc", false).Return(cgroupStatsMap["/misc"].cs, cgroupStatsMap["/misc"].ns, nil) 186 mockStatsProvider.EXPECT().GetCgroupStats("/kubelet", false).Return(cgroupStatsMap["/kubelet"].cs, cgroupStatsMap["/kubelet"].ns, nil) 187 mockStatsProvider.EXPECT().GetCgroupStats("/kubepods", true).Return(cgroupStatsMap["/pods"].cs, cgroupStatsMap["/pods"].ns, nil) 188 189 mockStatsProvider.EXPECT().ImageFsStats(ctx).Return(imageFsStats, rootFsStats, nil) 190 191 kubeletCreationTime := metav1.Now() 192 systemBootTime := metav1.Now() 193 provider := summaryProviderImpl{kubeletCreationTime: kubeletCreationTime, systemBootTime: systemBootTime, provider: mockStatsProvider} 194 summary, err := provider.Get(ctx, true) 195 assert.NoError(err) 196 197 assert.Equal(summary.Node.NodeName, "test-node") 198 assert.Equal(summary.Node.StartTime, systemBootTime) 199 assert.Equal(summary.Node.CPU, cgroupStatsMap["/"].cs.CPU) 200 assert.Equal(summary.Node.Memory, cgroupStatsMap["/"].cs.Memory) 201 assert.Equal(summary.Node.Swap, cgroupStatsMap["/"].cs.Swap) 202 assert.Equal(summary.Node.Network, cgroupStatsMap["/"].ns) 203 assert.Equal(summary.Node.Fs, rootFsStats) 204 // Since we are a split filesystem we want root filesystem to be container fs and image to be image filesystem 205 assert.Equal(summary.Node.Runtime, &statsapi.RuntimeStats{ContainerFs: rootFsStats, ImageFs: imageFsStats}) 206 207 assert.Equal(len(summary.Node.SystemContainers), 4) 208 assert.Contains(summary.Node.SystemContainers, statsapi.ContainerStats{ 209 Name: "kubelet", 210 StartTime: kubeletCreationTime, 211 CPU: cgroupStatsMap["/kubelet"].cs.CPU, 212 Memory: cgroupStatsMap["/kubelet"].cs.Memory, 213 Accelerators: cgroupStatsMap["/kubelet"].cs.Accelerators, 214 UserDefinedMetrics: cgroupStatsMap["/kubelet"].cs.UserDefinedMetrics, 215 Swap: cgroupStatsMap["/kubelet"].cs.Swap, 216 }) 217 assert.Contains(summary.Node.SystemContainers, statsapi.ContainerStats{ 218 Name: "misc", 219 StartTime: cgroupStatsMap["/misc"].cs.StartTime, 220 CPU: cgroupStatsMap["/misc"].cs.CPU, 221 Memory: cgroupStatsMap["/misc"].cs.Memory, 222 Accelerators: cgroupStatsMap["/misc"].cs.Accelerators, 223 UserDefinedMetrics: cgroupStatsMap["/misc"].cs.UserDefinedMetrics, 224 Swap: cgroupStatsMap["/misc"].cs.Swap, 225 }) 226 assert.Contains(summary.Node.SystemContainers, statsapi.ContainerStats{ 227 Name: "runtime", 228 StartTime: cgroupStatsMap["/runtime"].cs.StartTime, 229 CPU: cgroupStatsMap["/runtime"].cs.CPU, 230 Memory: cgroupStatsMap["/runtime"].cs.Memory, 231 Accelerators: cgroupStatsMap["/runtime"].cs.Accelerators, 232 UserDefinedMetrics: cgroupStatsMap["/runtime"].cs.UserDefinedMetrics, 233 Swap: cgroupStatsMap["/runtime"].cs.Swap, 234 }) 235 assert.Contains(summary.Node.SystemContainers, statsapi.ContainerStats{ 236 Name: "pods", 237 StartTime: cgroupStatsMap["/pods"].cs.StartTime, 238 CPU: cgroupStatsMap["/pods"].cs.CPU, 239 Memory: cgroupStatsMap["/pods"].cs.Memory, 240 Accelerators: cgroupStatsMap["/pods"].cs.Accelerators, 241 UserDefinedMetrics: cgroupStatsMap["/pods"].cs.UserDefinedMetrics, 242 Swap: cgroupStatsMap["/pods"].cs.Swap, 243 }) 244 assert.Equal(summary.Pods, podStats) 245 } 246 247 func TestSummaryProviderGetCPUAndMemoryStats(t *testing.T) { 248 ctx := context.Background() 249 assert := assert.New(t) 250 251 podStats := []statsapi.PodStats{ 252 { 253 PodRef: statsapi.PodReference{Name: "test-pod", Namespace: "test-namespace", UID: "UID_test-pod"}, 254 StartTime: metav1.NewTime(time.Now()), 255 Containers: []statsapi.ContainerStats{*getContainerStats()}, 256 }, 257 } 258 cgroupStatsMap := map[string]struct { 259 cs *statsapi.ContainerStats 260 }{ 261 "/": {cs: getVolumeCPUAndMemoryStats()}, 262 "/runtime": {cs: getVolumeCPUAndMemoryStats()}, 263 "/misc": {cs: getVolumeCPUAndMemoryStats()}, 264 "/kubelet": {cs: getVolumeCPUAndMemoryStats()}, 265 "/pods": {cs: getVolumeCPUAndMemoryStats()}, 266 } 267 268 mockCtrl := gomock.NewController(t) 269 defer mockCtrl.Finish() 270 mockStatsProvider := statstest.NewMockProvider(mockCtrl) 271 272 mockStatsProvider.EXPECT().GetNode().Return(node, nil) 273 mockStatsProvider.EXPECT().GetNodeConfig().Return(nodeConfig) 274 mockStatsProvider.EXPECT().GetPodCgroupRoot().Return(cgroupRoot) 275 mockStatsProvider.EXPECT().ListPodCPUAndMemoryStats(ctx).Return(podStats, nil) 276 mockStatsProvider.EXPECT().GetCgroupCPUAndMemoryStats("/", false).Return(cgroupStatsMap["/"].cs, nil) 277 mockStatsProvider.EXPECT().GetCgroupCPUAndMemoryStats("/runtime", false).Return(cgroupStatsMap["/runtime"].cs, nil) 278 mockStatsProvider.EXPECT().GetCgroupCPUAndMemoryStats("/misc", false).Return(cgroupStatsMap["/misc"].cs, nil) 279 mockStatsProvider.EXPECT().GetCgroupCPUAndMemoryStats("/kubelet", false).Return(cgroupStatsMap["/kubelet"].cs, nil) 280 mockStatsProvider.EXPECT().GetCgroupCPUAndMemoryStats("/kubepods", false).Return(cgroupStatsMap["/pods"].cs, nil) 281 282 provider := NewSummaryProvider(mockStatsProvider) 283 summary, err := provider.GetCPUAndMemoryStats(ctx) 284 assert.NoError(err) 285 286 assert.Equal(summary.Node.NodeName, "test-node") 287 assert.Equal(summary.Node.StartTime, cgroupStatsMap["/"].cs.StartTime) 288 assert.Equal(summary.Node.CPU, cgroupStatsMap["/"].cs.CPU) 289 assert.Equal(summary.Node.Memory, cgroupStatsMap["/"].cs.Memory) 290 assert.Nil(summary.Node.Network) 291 assert.Nil(summary.Node.Fs) 292 assert.Nil(summary.Node.Runtime) 293 294 assert.Equal(len(summary.Node.SystemContainers), 4) 295 assert.Contains(summary.Node.SystemContainers, statsapi.ContainerStats{ 296 Name: "kubelet", 297 StartTime: cgroupStatsMap["/kubelet"].cs.StartTime, 298 CPU: cgroupStatsMap["/kubelet"].cs.CPU, 299 Memory: cgroupStatsMap["/kubelet"].cs.Memory, 300 }) 301 assert.Contains(summary.Node.SystemContainers, statsapi.ContainerStats{ 302 Name: "misc", 303 StartTime: cgroupStatsMap["/misc"].cs.StartTime, 304 CPU: cgroupStatsMap["/misc"].cs.CPU, 305 Memory: cgroupStatsMap["/misc"].cs.Memory, 306 }) 307 assert.Contains(summary.Node.SystemContainers, statsapi.ContainerStats{ 308 Name: "runtime", 309 StartTime: cgroupStatsMap["/runtime"].cs.StartTime, 310 CPU: cgroupStatsMap["/runtime"].cs.CPU, 311 Memory: cgroupStatsMap["/runtime"].cs.Memory, 312 }) 313 assert.Contains(summary.Node.SystemContainers, statsapi.ContainerStats{ 314 Name: "pods", 315 StartTime: cgroupStatsMap["/pods"].cs.StartTime, 316 CPU: cgroupStatsMap["/pods"].cs.CPU, 317 Memory: cgroupStatsMap["/pods"].cs.Memory, 318 }) 319 assert.Equal(summary.Pods, podStats) 320 } 321 322 func getFsStats() *statsapi.FsStats { 323 f := fuzz.New().NilChance(0) 324 v := &statsapi.FsStats{} 325 f.Fuzz(v) 326 return v 327 } 328 329 func getContainerStats() *statsapi.ContainerStats { 330 f := fuzz.New().NilChance(0) 331 v := &statsapi.ContainerStats{} 332 f.Fuzz(v) 333 return v 334 } 335 func getVolumeCPUAndMemoryStats() *statsapi.ContainerStats { 336 f := fuzz.New().NilChance(0) 337 v := &statsapi.ContainerStats{} 338 f.Fuzz(&v.Name) 339 f.Fuzz(&v.StartTime) 340 f.Fuzz(v.CPU) 341 f.Fuzz(v.Memory) 342 return v 343 } 344 345 func getVolumeStats() *statsapi.VolumeStats { 346 f := fuzz.New().NilChance(0) 347 v := &statsapi.VolumeStats{} 348 f.Fuzz(v) 349 return v 350 } 351 352 func getNetworkStats() *statsapi.NetworkStats { 353 f := fuzz.New().NilChance(0) 354 v := &statsapi.NetworkStats{} 355 f.Fuzz(v) 356 return v 357 } 358 359 func getRlimitStats() *statsapi.RlimitStats { 360 f := fuzz.New().NilChance(0) 361 v := &statsapi.RlimitStats{} 362 f.Fuzz(v) 363 return v 364 }