k8s.io/kubernetes@v1.29.3/pkg/kubelet/stats/cadvisor_stats_provider_test.go (about) 1 /* 2 Copyright 2017 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 stats 18 19 import ( 20 "context" 21 "runtime" 22 "testing" 23 24 "github.com/golang/mock/gomock" 25 cadvisorapiv2 "github.com/google/cadvisor/info/v2" 26 "github.com/stretchr/testify/assert" 27 28 v1 "k8s.io/api/core/v1" 29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 "k8s.io/apimachinery/pkg/types" 31 utilfeature "k8s.io/apiserver/pkg/util/feature" 32 featuregatetesting "k8s.io/component-base/featuregate/testing" 33 runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1" 34 statsapi "k8s.io/kubelet/pkg/apis/stats/v1alpha1" 35 "k8s.io/kubernetes/pkg/features" 36 cadvisortest "k8s.io/kubernetes/pkg/kubelet/cadvisor/testing" 37 kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" 38 containertest "k8s.io/kubernetes/pkg/kubelet/container/testing" 39 "k8s.io/kubernetes/pkg/kubelet/kuberuntime" 40 "k8s.io/kubernetes/pkg/kubelet/leaky" 41 serverstats "k8s.io/kubernetes/pkg/kubelet/server/stats" 42 statustest "k8s.io/kubernetes/pkg/kubelet/status/testing" 43 "k8s.io/kubernetes/pkg/volume" 44 ) 45 46 func TestFilterTerminatedContainerInfoAndAssembleByPodCgroupKey(t *testing.T) { 47 const ( 48 seedPastPod0Infra = 1000 49 seedPastPod0Container0 = 2000 50 seedPod0Infra = 3000 51 seedPod0Container0 = 4000 52 ) 53 const ( 54 namespace = "test" 55 pName0 = "pod0" 56 cName00 = "c0" 57 pName1 = "pod1" 58 cName11 = "c1" 59 pName2 = "pod2" 60 cName22 = "c2" 61 cName222 = "c222" 62 ) 63 infos := map[string]cadvisorapiv2.ContainerInfo{ 64 // ContainerInfo with past creation time and no CPU/memory usage for 65 // simulating uncleaned cgroups of already terminated containers, which 66 // should not be shown in the results. 67 "/pod0-i-terminated-1": getTerminatedContainerInfo(seedPastPod0Infra, pName0, namespace, leaky.PodInfraContainerName), 68 "/pod0-c0-terminated-1": getTerminatedContainerInfo(seedPastPod0Container0, pName0, namespace, cName00), 69 70 // Same as above but uses the same creation time as the latest 71 // containers. They are terminated containers, so they should not be in 72 // the results. 73 "/pod0-i-terminated-2": getTerminatedContainerInfo(seedPod0Infra, pName0, namespace, leaky.PodInfraContainerName), 74 "/pod0-c0-terminated-2": getTerminatedContainerInfo(seedPod0Container0, pName0, namespace, cName00), 75 76 // The latest containers, which should be in the results. 77 "/pod0-i": getTestContainerInfo(seedPod0Infra, pName0, namespace, leaky.PodInfraContainerName), 78 "/pod0-c0": getTestContainerInfo(seedPod0Container0, pName0, namespace, cName00), 79 80 "/pod1-i.slice": getTestContainerInfo(seedPod0Infra, pName1, namespace, leaky.PodInfraContainerName), 81 "/pod1-c1.slice": getTestContainerInfo(seedPod0Container0, pName1, namespace, cName11), 82 83 "/pod2-i-terminated-1": getTerminatedContainerInfo(seedPastPod0Infra, pName2, namespace, leaky.PodInfraContainerName), 84 // ContainerInfo with past creation time and no CPU/memory usage for 85 // simulating uncleaned cgroups of already terminated containers, which 86 // should not be shown in the results. 87 "/pod2-c2-terminated-1": getTerminatedContainerInfo(seedPastPod0Container0, pName2, namespace, cName22), 88 89 //ContainerInfo with no CPU/memory usage but has network usage for uncleaned cgroups, should not be filtered out 90 "/pod2-c222-zerocpumem-1": getContainerInfoWithZeroCpuMem(seedPastPod0Container0, pName2, namespace, cName222), 91 } 92 filteredInfos, allInfos := filterTerminatedContainerInfoAndAssembleByPodCgroupKey(infos) 93 assert.Len(t, filteredInfos, 5) 94 assert.Len(t, allInfos, 11) 95 for _, c := range []string{"/pod0-i", "/pod0-c0"} { 96 if _, found := filteredInfos[c]; !found { 97 t.Errorf("%q is expected to be in the output\n", c) 98 } 99 } 100 101 expectedInfoKeys := []string{"pod0-i-terminated-1", "pod0-c0-terminated-1", "pod0-i-terminated-2", "pod0-c0-terminated-2", "pod0-i", "pod0-c0"} 102 // NOTE: on Windows, IsSystemdStyleName will return false, which means that the Container Info will 103 // not be assembled by cgroup key. 104 if runtime.GOOS != "windows" { 105 expectedInfoKeys = append(expectedInfoKeys, "c1") 106 } else { 107 expectedInfoKeys = append(expectedInfoKeys, "pod1-c1.slice") 108 } 109 for _, c := range expectedInfoKeys { 110 if _, found := allInfos[c]; !found { 111 t.Errorf("%q is expected to be in the output\n", c) 112 } 113 } 114 } 115 116 func TestCadvisorListPodStats(t *testing.T) { 117 ctx := context.Background() 118 const ( 119 namespace0 = "test0" 120 namespace2 = "test2" 121 ) 122 const ( 123 seedRoot = 0 124 seedRuntime = 100 125 seedKubelet = 200 126 seedMisc = 300 127 seedPod0Infra = 1000 128 seedPod0Container0 = 2000 129 seedPod0Container1 = 2001 130 seedPod1Infra = 3000 131 seedPod1Container = 4000 132 seedPod2Infra = 5000 133 seedPod2Container = 6000 134 seedPod3Infra = 7000 135 seedPod3Container0 = 8000 136 seedPod3Container1 = 8001 137 seedEphemeralVolume1 = 10000 138 seedEphemeralVolume2 = 10001 139 seedPersistentVolume1 = 20000 140 seedPersistentVolume2 = 20001 141 ) 142 const ( 143 pName0 = "pod0" 144 pName1 = "pod1" 145 pName2 = "pod0" // ensure pName2 conflicts with pName0, but is in a different namespace 146 pName3 = "pod3" 147 ) 148 const ( 149 cName00 = "c0" 150 cName01 = "c1" 151 cName10 = "c0" // ensure cName10 conflicts with cName02, but is in a different pod 152 cName20 = "c1" // ensure cName20 conflicts with cName01, but is in a different pod + namespace 153 cName30 = "c0-init" 154 cName31 = "c1" 155 ) 156 const ( 157 rootfsCapacity = uint64(10000000) 158 rootfsAvailable = uint64(5000000) 159 rootfsInodesFree = uint64(1000) 160 rootfsInodes = uint64(2000) 161 imagefsCapacity = uint64(20000000) 162 imagefsAvailable = uint64(8000000) 163 imagefsInodesFree = uint64(2000) 164 imagefsInodes = uint64(4000) 165 ) 166 167 prf0 := statsapi.PodReference{Name: pName0, Namespace: namespace0, UID: "UID" + pName0} 168 prf1 := statsapi.PodReference{Name: pName1, Namespace: namespace0, UID: "UID" + pName1} 169 prf2 := statsapi.PodReference{Name: pName2, Namespace: namespace2, UID: "UID" + pName2} 170 prf3 := statsapi.PodReference{Name: pName3, Namespace: namespace0, UID: "UID" + pName3} 171 infos := map[string]cadvisorapiv2.ContainerInfo{ 172 "/": getTestContainerInfo(seedRoot, "", "", ""), 173 "/docker-daemon": getTestContainerInfo(seedRuntime, "", "", ""), 174 "/kubelet": getTestContainerInfo(seedKubelet, "", "", ""), 175 "/system": getTestContainerInfo(seedMisc, "", "", ""), 176 // Pod0 - Namespace0 177 "/pod0-i": getTestContainerInfo(seedPod0Infra, pName0, namespace0, leaky.PodInfraContainerName), 178 "/pod0-c0": getTestContainerInfo(seedPod0Container0, pName0, namespace0, cName00), 179 "/pod0-c1": getTestContainerInfo(seedPod0Container1, pName0, namespace0, cName01), 180 // Pod1 - Namespace0 181 "/pod1-i": getTestContainerInfo(seedPod1Infra, pName1, namespace0, leaky.PodInfraContainerName), 182 "/pod1-c0": getTestContainerInfo(seedPod1Container, pName1, namespace0, cName10), 183 // Pod2 - Namespace2 184 "/pod2-i": getTestContainerInfo(seedPod2Infra, pName2, namespace2, leaky.PodInfraContainerName), 185 "/pod2-c0": getTestContainerInfo(seedPod2Container, pName2, namespace2, cName20), 186 "/kubepods/burstable/podUIDpod0": getTestContainerInfo(seedPod0Infra, pName0, namespace0, leaky.PodInfraContainerName), 187 "/kubepods/podUIDpod1": getTestContainerInfo(seedPod1Infra, pName1, namespace0, leaky.PodInfraContainerName), 188 // Pod3 - Namespace0 189 "/pod3-i": getTestContainerInfo(seedPod3Infra, pName3, namespace0, leaky.PodInfraContainerName), 190 "/pod3-c0-init": getTestContainerInfo(seedPod3Container0, pName3, namespace0, cName30), 191 "/pod3-c1": getTestContainerInfo(seedPod3Container1, pName3, namespace0, cName31), 192 } 193 194 freeRootfsInodes := rootfsInodesFree 195 totalRootfsInodes := rootfsInodes 196 rootfs := cadvisorapiv2.FsInfo{ 197 Capacity: rootfsCapacity, 198 Available: rootfsAvailable, 199 InodesFree: &freeRootfsInodes, 200 Inodes: &totalRootfsInodes, 201 } 202 203 freeImagefsInodes := imagefsInodesFree 204 totalImagefsInodes := imagefsInodes 205 imagefs := cadvisorapiv2.FsInfo{ 206 Capacity: imagefsCapacity, 207 Available: imagefsAvailable, 208 InodesFree: &freeImagefsInodes, 209 Inodes: &totalImagefsInodes, 210 } 211 212 // memory limit overrides for each container (used to test available bytes if a memory limit is known) 213 memoryLimitOverrides := map[string]uint64{ 214 "/": uint64(1 << 30), 215 "/pod2-c0": uint64(1 << 15), 216 } 217 for name, memoryLimitOverride := range memoryLimitOverrides { 218 info, found := infos[name] 219 if !found { 220 t.Errorf("No container defined with name %v", name) 221 } 222 info.Spec.Memory.Limit = memoryLimitOverride 223 infos[name] = info 224 } 225 // any container for which cadvisor should return no stats (as might be the case for an exited init container) 226 nostatsOverrides := []string{ 227 "/pod3-c0-init", 228 } 229 for _, name := range nostatsOverrides { 230 info, found := infos[name] 231 if !found { 232 t.Errorf("No container defined with name %v", name) 233 } 234 info.Spec.Memory = cadvisorapiv2.MemorySpec{} 235 info.Spec.Cpu = cadvisorapiv2.CpuSpec{} 236 info.Spec.HasMemory = false 237 info.Spec.HasCpu = false 238 info.Spec.HasNetwork = false 239 infos[name] = info 240 } 241 242 options := cadvisorapiv2.RequestOptions{ 243 IdType: cadvisorapiv2.TypeName, 244 Count: 2, 245 Recursive: true, 246 } 247 248 mockCtrl := gomock.NewController(t) 249 defer mockCtrl.Finish() 250 251 mockCadvisor := cadvisortest.NewMockInterface(mockCtrl) 252 mockCadvisor.EXPECT().ContainerInfoV2("/", options).Return(infos, nil) 253 mockCadvisor.EXPECT().RootFsInfo().Return(rootfs, nil) 254 mockCadvisor.EXPECT().ImagesFsInfo().Return(imagefs, nil) 255 256 mockRuntime := containertest.NewMockRuntime(mockCtrl) 257 258 ephemeralVolumes := []statsapi.VolumeStats{getPodVolumeStats(seedEphemeralVolume1, "ephemeralVolume1"), 259 getPodVolumeStats(seedEphemeralVolume2, "ephemeralVolume2")} 260 persistentVolumes := []statsapi.VolumeStats{getPodVolumeStats(seedPersistentVolume1, "persistentVolume1"), 261 getPodVolumeStats(seedPersistentVolume2, "persistentVolume2")} 262 volumeStats := serverstats.PodVolumeStats{ 263 EphemeralVolumes: ephemeralVolumes, 264 PersistentVolumes: persistentVolumes, 265 } 266 p0Time := metav1.Now() 267 p1Time := metav1.Now() 268 p2Time := metav1.Now() 269 p3Time := metav1.Now() 270 mockStatus := statustest.NewMockPodStatusProvider(mockCtrl) 271 mockStatus.EXPECT().GetPodStatus(types.UID("UID"+pName0)).Return(v1.PodStatus{StartTime: &p0Time}, true) 272 mockStatus.EXPECT().GetPodStatus(types.UID("UID"+pName1)).Return(v1.PodStatus{StartTime: &p1Time}, true) 273 mockStatus.EXPECT().GetPodStatus(types.UID("UID"+pName2)).Return(v1.PodStatus{StartTime: &p2Time}, true) 274 mockStatus.EXPECT().GetPodStatus(types.UID("UID"+pName3)).Return(v1.PodStatus{StartTime: &p3Time}, true) 275 276 resourceAnalyzer := &fakeResourceAnalyzer{podVolumeStats: volumeStats} 277 278 p := NewCadvisorStatsProvider(mockCadvisor, resourceAnalyzer, nil, nil, mockRuntime, mockStatus, NewFakeHostStatsProvider()) 279 pods, err := p.ListPodStats(ctx) 280 assert.NoError(t, err) 281 282 assert.Equal(t, 4, len(pods)) 283 indexPods := make(map[statsapi.PodReference]statsapi.PodStats, len(pods)) 284 for _, pod := range pods { 285 indexPods[pod.PodRef] = pod 286 } 287 288 // Validate Pod0 Results 289 ps, found := indexPods[prf0] 290 assert.True(t, found) 291 assert.Len(t, ps.Containers, 2) 292 indexCon := make(map[string]statsapi.ContainerStats, len(ps.Containers)) 293 for _, con := range ps.Containers { 294 indexCon[con.Name] = con 295 } 296 con := indexCon[cName00] 297 assert.EqualValues(t, testTime(creationTime, seedPod0Container0).Unix(), con.StartTime.Time.Unix()) 298 checkCPUStats(t, "Pod0Container0", seedPod0Container0, con.CPU) 299 checkMemoryStats(t, "Pod0Conainer0", seedPod0Container0, infos["/pod0-c0"], con.Memory) 300 checkSwapStats(t, "Pod0Conainer0", seedPod0Container0, infos["/pod0-c0"], con.Swap) 301 302 con = indexCon[cName01] 303 assert.EqualValues(t, testTime(creationTime, seedPod0Container1).Unix(), con.StartTime.Time.Unix()) 304 checkCPUStats(t, "Pod0Container1", seedPod0Container1, con.CPU) 305 checkMemoryStats(t, "Pod0Container1", seedPod0Container1, infos["/pod0-c1"], con.Memory) 306 checkSwapStats(t, "Pod0Container1", seedPod0Container1, infos["/pod0-c1"], con.Swap) 307 308 assert.EqualValues(t, p0Time.Unix(), ps.StartTime.Time.Unix()) 309 checkNetworkStats(t, "Pod0", seedPod0Infra, ps.Network) 310 checkEphemeralStats(t, "Pod0", []int{seedPod0Container0, seedPod0Container1}, []int{seedEphemeralVolume1, seedEphemeralVolume2}, nil, ps.EphemeralStorage) 311 if ps.CPU != nil { 312 checkCPUStats(t, "Pod0", seedPod0Infra, ps.CPU) 313 } 314 if ps.Memory != nil { 315 checkMemoryStats(t, "Pod0", seedPod0Infra, infos["/pod0-i"], ps.Memory) 316 } 317 if ps.Swap != nil { 318 checkSwapStats(t, "Pod0", seedPod0Infra, infos["/pod0-i"], ps.Swap) 319 } 320 321 // Validate Pod1 Results 322 ps, found = indexPods[prf1] 323 assert.True(t, found) 324 assert.Len(t, ps.Containers, 1) 325 con = ps.Containers[0] 326 assert.Equal(t, cName10, con.Name) 327 checkCPUStats(t, "Pod1Container0", seedPod1Container, con.CPU) 328 checkMemoryStats(t, "Pod1Container0", seedPod1Container, infos["/pod1-c0"], con.Memory) 329 checkSwapStats(t, "Pod1Container0", seedPod1Container, infos["/pod1-c0"], con.Swap) 330 checkNetworkStats(t, "Pod1", seedPod1Infra, ps.Network) 331 332 // Validate Pod2 Results 333 ps, found = indexPods[prf2] 334 assert.True(t, found) 335 assert.Len(t, ps.Containers, 1) 336 con = ps.Containers[0] 337 assert.Equal(t, cName20, con.Name) 338 checkCPUStats(t, "Pod2Container0", seedPod2Container, con.CPU) 339 checkMemoryStats(t, "Pod2Container0", seedPod2Container, infos["/pod2-c0"], con.Memory) 340 checkSwapStats(t, "Pod2Container0", seedPod2Container, infos["/pod2-c0"], con.Swap) 341 checkNetworkStats(t, "Pod2", seedPod2Infra, ps.Network) 342 343 // Validate Pod3 Results 344 345 ps, found = indexPods[prf3] 346 assert.True(t, found) 347 // /pod3-c0-init has no stats should be filtered 348 assert.Len(t, ps.Containers, 1) 349 indexCon = make(map[string]statsapi.ContainerStats, len(ps.Containers)) 350 for _, con := range ps.Containers { 351 indexCon[con.Name] = con 352 } 353 con = indexCon[cName31] 354 assert.Equal(t, cName31, con.Name) 355 checkCPUStats(t, "Pod3Container1", seedPod3Container1, con.CPU) 356 checkMemoryStats(t, "Pod3Container1", seedPod3Container1, infos["/pod3-c1"], con.Memory) 357 checkSwapStats(t, "Pod3Container1", seedPod3Container1, infos["/pod3-c1"], con.Swap) 358 } 359 360 func TestCadvisorListPodCPUAndMemoryStats(t *testing.T) { 361 ctx := context.Background() 362 const ( 363 namespace0 = "test0" 364 namespace2 = "test2" 365 ) 366 const ( 367 seedRoot = 0 368 seedRuntime = 100 369 seedKubelet = 200 370 seedMisc = 300 371 seedPod0Infra = 1000 372 seedPod0Container0 = 2000 373 seedPod0Container1 = 2001 374 seedPod1Infra = 3000 375 seedPod1Container = 4000 376 seedPod2Infra = 5000 377 seedPod2Container = 6000 378 seedEphemeralVolume1 = 10000 379 seedEphemeralVolume2 = 10001 380 seedPersistentVolume1 = 20000 381 seedPersistentVolume2 = 20001 382 ) 383 const ( 384 pName0 = "pod0" 385 pName1 = "pod1" 386 pName2 = "pod0" // ensure pName2 conflicts with pName0, but is in a different namespace 387 ) 388 const ( 389 cName00 = "c0" 390 cName01 = "c1" 391 cName10 = "c0" // ensure cName10 conflicts with cName02, but is in a different pod 392 cName20 = "c1" // ensure cName20 conflicts with cName01, but is in a different pod + namespace 393 ) 394 395 prf0 := statsapi.PodReference{Name: pName0, Namespace: namespace0, UID: "UID" + pName0} 396 prf1 := statsapi.PodReference{Name: pName1, Namespace: namespace0, UID: "UID" + pName1} 397 prf2 := statsapi.PodReference{Name: pName2, Namespace: namespace2, UID: "UID" + pName2} 398 infos := map[string]cadvisorapiv2.ContainerInfo{ 399 "/": getTestContainerInfo(seedRoot, "", "", ""), 400 "/docker-daemon": getTestContainerInfo(seedRuntime, "", "", ""), 401 "/kubelet": getTestContainerInfo(seedKubelet, "", "", ""), 402 "/system": getTestContainerInfo(seedMisc, "", "", ""), 403 // Pod0 - Namespace0 404 "/pod0-i": getTestContainerInfo(seedPod0Infra, pName0, namespace0, leaky.PodInfraContainerName), 405 "/pod0-c0": getTestContainerInfo(seedPod0Container0, pName0, namespace0, cName00), 406 "/pod0-c1": getTestContainerInfo(seedPod0Container1, pName0, namespace0, cName01), 407 // Pod1 - Namespace0 408 "/pod1-i": getTestContainerInfo(seedPod1Infra, pName1, namespace0, leaky.PodInfraContainerName), 409 "/pod1-c0": getTestContainerInfo(seedPod1Container, pName1, namespace0, cName10), 410 // Pod2 - Namespace2 411 "/pod2-i": getTestContainerInfo(seedPod2Infra, pName2, namespace2, leaky.PodInfraContainerName), 412 "/pod2-c0": getTestContainerInfo(seedPod2Container, pName2, namespace2, cName20), 413 "/kubepods/burstable/podUIDpod0": getTestContainerInfo(seedPod0Infra, pName0, namespace0, leaky.PodInfraContainerName), 414 "/kubepods/podUIDpod1": getTestContainerInfo(seedPod1Infra, pName1, namespace0, leaky.PodInfraContainerName), 415 } 416 417 // memory limit overrides for each container (used to test available bytes if a memory limit is known) 418 memoryLimitOverrides := map[string]uint64{ 419 "/": uint64(1 << 30), 420 "/pod2-c0": uint64(1 << 15), 421 } 422 for name, memoryLimitOverride := range memoryLimitOverrides { 423 info, found := infos[name] 424 if !found { 425 t.Errorf("No container defined with name %v", name) 426 } 427 info.Spec.Memory.Limit = memoryLimitOverride 428 infos[name] = info 429 } 430 431 options := cadvisorapiv2.RequestOptions{ 432 IdType: cadvisorapiv2.TypeName, 433 Count: 2, 434 Recursive: true, 435 } 436 437 mockCtrl := gomock.NewController(t) 438 defer mockCtrl.Finish() 439 440 mockCadvisor := cadvisortest.NewMockInterface(mockCtrl) 441 mockCadvisor.EXPECT().ContainerInfoV2("/", options).Return(infos, nil) 442 443 ephemeralVolumes := []statsapi.VolumeStats{getPodVolumeStats(seedEphemeralVolume1, "ephemeralVolume1"), 444 getPodVolumeStats(seedEphemeralVolume2, "ephemeralVolume2")} 445 persistentVolumes := []statsapi.VolumeStats{getPodVolumeStats(seedPersistentVolume1, "persistentVolume1"), 446 getPodVolumeStats(seedPersistentVolume2, "persistentVolume2")} 447 volumeStats := serverstats.PodVolumeStats{ 448 EphemeralVolumes: ephemeralVolumes, 449 PersistentVolumes: persistentVolumes, 450 } 451 452 resourceAnalyzer := &fakeResourceAnalyzer{podVolumeStats: volumeStats} 453 454 p := NewCadvisorStatsProvider(mockCadvisor, resourceAnalyzer, nil, nil, nil, nil, NewFakeHostStatsProvider()) 455 pods, err := p.ListPodCPUAndMemoryStats(ctx) 456 assert.NoError(t, err) 457 458 assert.Equal(t, 3, len(pods)) 459 indexPods := make(map[statsapi.PodReference]statsapi.PodStats, len(pods)) 460 for _, pod := range pods { 461 indexPods[pod.PodRef] = pod 462 } 463 464 // Validate Pod0 Results 465 ps, found := indexPods[prf0] 466 assert.True(t, found) 467 assert.Len(t, ps.Containers, 2) 468 indexCon := make(map[string]statsapi.ContainerStats, len(ps.Containers)) 469 for _, con := range ps.Containers { 470 indexCon[con.Name] = con 471 } 472 con := indexCon[cName00] 473 assert.EqualValues(t, testTime(creationTime, seedPod0Container0).Unix(), con.StartTime.Time.Unix()) 474 checkCPUStats(t, "Pod0Container0", seedPod0Container0, con.CPU) 475 checkMemoryStats(t, "Pod0Conainer0", seedPod0Container0, infos["/pod0-c0"], con.Memory) 476 assert.Nil(t, con.Rootfs) 477 assert.Nil(t, con.Logs) 478 assert.Nil(t, con.Accelerators) 479 assert.Nil(t, con.UserDefinedMetrics) 480 481 con = indexCon[cName01] 482 assert.EqualValues(t, testTime(creationTime, seedPod0Container1).Unix(), con.StartTime.Time.Unix()) 483 checkCPUStats(t, "Pod0Container1", seedPod0Container1, con.CPU) 484 checkMemoryStats(t, "Pod0Container1", seedPod0Container1, infos["/pod0-c1"], con.Memory) 485 assert.Nil(t, con.Rootfs) 486 assert.Nil(t, con.Logs) 487 assert.Nil(t, con.Accelerators) 488 assert.Nil(t, con.UserDefinedMetrics) 489 490 assert.EqualValues(t, testTime(creationTime, seedPod0Infra).Unix(), ps.StartTime.Time.Unix()) 491 assert.Nil(t, ps.EphemeralStorage) 492 assert.Nil(t, ps.VolumeStats) 493 assert.Nil(t, ps.Network) 494 if ps.CPU != nil { 495 checkCPUStats(t, "Pod0", seedPod0Infra, ps.CPU) 496 } 497 if ps.Memory != nil { 498 checkMemoryStats(t, "Pod0", seedPod0Infra, infos["/pod0-i"], ps.Memory) 499 } 500 501 // Validate Pod1 Results 502 ps, found = indexPods[prf1] 503 assert.True(t, found) 504 assert.Len(t, ps.Containers, 1) 505 con = ps.Containers[0] 506 assert.Equal(t, cName10, con.Name) 507 checkCPUStats(t, "Pod1Container0", seedPod1Container, con.CPU) 508 checkMemoryStats(t, "Pod1Container0", seedPod1Container, infos["/pod1-c0"], con.Memory) 509 assert.Nil(t, ps.EphemeralStorage) 510 assert.Nil(t, ps.VolumeStats) 511 assert.Nil(t, ps.Network) 512 513 // Validate Pod2 Results 514 ps, found = indexPods[prf2] 515 assert.True(t, found) 516 assert.Len(t, ps.Containers, 1) 517 con = ps.Containers[0] 518 assert.Equal(t, cName20, con.Name) 519 checkCPUStats(t, "Pod2Container0", seedPod2Container, con.CPU) 520 checkMemoryStats(t, "Pod2Container0", seedPod2Container, infos["/pod2-c0"], con.Memory) 521 assert.Nil(t, ps.EphemeralStorage) 522 assert.Nil(t, ps.VolumeStats) 523 assert.Nil(t, ps.Network) 524 } 525 526 func TestCadvisorImagesFsStatsKubeletSeparateDiskOff(t *testing.T) { 527 ctx := context.Background() 528 mockCtrl := gomock.NewController(t) 529 defer mockCtrl.Finish() 530 var ( 531 assert = assert.New(t) 532 mockCadvisor = cadvisortest.NewMockInterface(mockCtrl) 533 mockRuntime = containertest.NewMockRuntime(mockCtrl) 534 535 seed = 1000 536 imageFsInfo = getTestFsInfo(seed) 537 imageStats = &kubecontainer.ImageStats{TotalStorageBytes: 100} 538 ) 539 540 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.KubeletSeparateDiskGC, false)() 541 542 mockCadvisor.EXPECT().ImagesFsInfo().Return(imageFsInfo, nil) 543 mockRuntime.EXPECT().ImageStats(ctx).Return(imageStats, nil) 544 545 provider := newCadvisorStatsProvider(mockCadvisor, &fakeResourceAnalyzer{}, mockRuntime, nil, NewFakeHostStatsProvider()) 546 stats, _, err := provider.ImageFsStats(ctx) 547 assert.NoError(err) 548 549 assert.Equal(imageFsInfo.Timestamp, stats.Time.Time) 550 assert.Equal(imageFsInfo.Available, *stats.AvailableBytes) 551 assert.Equal(imageFsInfo.Capacity, *stats.CapacityBytes) 552 assert.Equal(imageStats.TotalStorageBytes, *stats.UsedBytes) 553 assert.Equal(imageFsInfo.InodesFree, stats.InodesFree) 554 assert.Equal(imageFsInfo.Inodes, stats.Inodes) 555 assert.Equal(*imageFsInfo.Inodes-*imageFsInfo.InodesFree, *stats.InodesUsed) 556 } 557 558 func TestCadvisorImagesFsStats(t *testing.T) { 559 ctx := context.Background() 560 mockCtrl := gomock.NewController(t) 561 defer mockCtrl.Finish() 562 var ( 563 assert = assert.New(t) 564 mockCadvisor = cadvisortest.NewMockInterface(mockCtrl) 565 mockRuntime = containertest.NewMockRuntime(mockCtrl) 566 567 seed = 1000 568 imageFsInfo = getTestFsInfo(seed) 569 ) 570 imageFsInfoCRI := &runtimeapi.FilesystemUsage{ 571 Timestamp: imageFsInfo.Timestamp.Unix(), 572 FsId: &runtimeapi.FilesystemIdentifier{Mountpoint: "images"}, 573 UsedBytes: &runtimeapi.UInt64Value{Value: imageFsInfo.Usage}, 574 InodesUsed: &runtimeapi.UInt64Value{Value: *imageFsInfo.Inodes}, 575 } 576 imageFsInfoResponse := &runtimeapi.ImageFsInfoResponse{ 577 ImageFilesystems: []*runtimeapi.FilesystemUsage{imageFsInfoCRI}, 578 ContainerFilesystems: []*runtimeapi.FilesystemUsage{imageFsInfoCRI}, 579 } 580 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.KubeletSeparateDiskGC, true)() 581 582 mockCadvisor.EXPECT().ImagesFsInfo().Return(imageFsInfo, nil) 583 mockCadvisor.EXPECT().ContainerFsInfo().Return(imageFsInfo, nil) 584 mockRuntime.EXPECT().ImageFsInfo(ctx).Return(imageFsInfoResponse, nil) 585 586 provider := newCadvisorStatsProvider(mockCadvisor, &fakeResourceAnalyzer{}, mockRuntime, nil, NewFakeHostStatsProvider()) 587 stats, containerfs, err := provider.ImageFsStats(ctx) 588 assert.NoError(err) 589 590 assert.Equal(imageFsInfo.Timestamp, stats.Time.Time) 591 assert.Equal(imageFsInfo.Available, *stats.AvailableBytes) 592 assert.Equal(imageFsInfo.Capacity, *stats.CapacityBytes) 593 assert.Equal(imageFsInfo.InodesFree, stats.InodesFree) 594 assert.Equal(imageFsInfo.Inodes, stats.Inodes) 595 assert.Equal(*imageFsInfo.Inodes-*imageFsInfo.InodesFree, *stats.InodesUsed) 596 597 assert.Equal(imageFsInfo.Timestamp, containerfs.Time.Time) 598 assert.Equal(imageFsInfo.Available, *containerfs.AvailableBytes) 599 assert.Equal(imageFsInfo.Capacity, *containerfs.CapacityBytes) 600 assert.Equal(imageFsInfo.InodesFree, containerfs.InodesFree) 601 assert.Equal(imageFsInfo.Inodes, containerfs.Inodes) 602 assert.Equal(*imageFsInfo.Inodes-*imageFsInfo.InodesFree, *containerfs.InodesUsed) 603 604 } 605 606 func TestCadvisorSplitImagesFsStats(t *testing.T) { 607 ctx := context.Background() 608 mockCtrl := gomock.NewController(t) 609 defer mockCtrl.Finish() 610 var ( 611 assert = assert.New(t) 612 mockCadvisor = cadvisortest.NewMockInterface(mockCtrl) 613 mockRuntime = containertest.NewMockRuntime(mockCtrl) 614 615 seed = 1000 616 imageFsInfo = getTestFsInfo(seed) 617 containerSeed = 1001 618 containerFsInfo = getTestFsInfo(containerSeed) 619 ) 620 imageFsInfoCRI := &runtimeapi.FilesystemUsage{ 621 Timestamp: imageFsInfo.Timestamp.Unix(), 622 FsId: &runtimeapi.FilesystemIdentifier{Mountpoint: "images"}, 623 UsedBytes: &runtimeapi.UInt64Value{Value: imageFsInfo.Usage}, 624 InodesUsed: &runtimeapi.UInt64Value{Value: *imageFsInfo.Inodes}, 625 } 626 containerFsInfoCRI := &runtimeapi.FilesystemUsage{ 627 Timestamp: containerFsInfo.Timestamp.Unix(), 628 FsId: &runtimeapi.FilesystemIdentifier{Mountpoint: "containers"}, 629 UsedBytes: &runtimeapi.UInt64Value{Value: containerFsInfo.Usage}, 630 InodesUsed: &runtimeapi.UInt64Value{Value: *containerFsInfo.Inodes}, 631 } 632 imageFsInfoResponse := &runtimeapi.ImageFsInfoResponse{ 633 ImageFilesystems: []*runtimeapi.FilesystemUsage{imageFsInfoCRI}, 634 ContainerFilesystems: []*runtimeapi.FilesystemUsage{containerFsInfoCRI}, 635 } 636 637 mockCadvisor.EXPECT().ImagesFsInfo().Return(imageFsInfo, nil) 638 mockCadvisor.EXPECT().ContainerFsInfo().Return(containerFsInfo, nil) 639 mockRuntime.EXPECT().ImageFsInfo(ctx).Return(imageFsInfoResponse, nil) 640 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.KubeletSeparateDiskGC, true)() 641 642 provider := newCadvisorStatsProvider(mockCadvisor, &fakeResourceAnalyzer{}, mockRuntime, nil, NewFakeHostStatsProvider()) 643 stats, containerfs, err := provider.ImageFsStats(ctx) 644 assert.NoError(err) 645 646 assert.Equal(imageFsInfo.Timestamp, stats.Time.Time) 647 assert.Equal(imageFsInfo.Available, *stats.AvailableBytes) 648 assert.Equal(imageFsInfo.Capacity, *stats.CapacityBytes) 649 assert.Equal(imageFsInfo.InodesFree, stats.InodesFree) 650 assert.Equal(imageFsInfo.Inodes, stats.Inodes) 651 assert.Equal(*imageFsInfo.Inodes-*imageFsInfo.InodesFree, *stats.InodesUsed) 652 653 assert.Equal(containerFsInfo.Timestamp, containerfs.Time.Time) 654 assert.Equal(containerFsInfo.Available, *containerfs.AvailableBytes) 655 assert.Equal(containerFsInfo.Capacity, *containerfs.CapacityBytes) 656 assert.Equal(containerFsInfo.InodesFree, containerfs.InodesFree) 657 assert.Equal(containerFsInfo.Inodes, containerfs.Inodes) 658 assert.Equal(*containerFsInfo.Inodes-*containerFsInfo.InodesFree, *containerfs.InodesUsed) 659 660 } 661 662 func TestCadvisorListPodStatsWhenContainerLogFound(t *testing.T) { 663 ctx := context.Background() 664 const ( 665 namespace0 = "test0" 666 ) 667 const ( 668 seedRoot = 0 669 seedRuntime = 100 670 seedKubelet = 200 671 seedMisc = 300 672 seedPod0Infra = 1000 673 seedPod0Container0 = 0 674 seedPod0Container1 = 0 675 ) 676 const ( 677 pName0 = "pod0" 678 ) 679 const ( 680 cName00 = "c0" 681 cName01 = "c1" 682 ) 683 const ( 684 rootfsCapacity = uint64(10000000) 685 rootfsAvailable = uint64(5000000) 686 rootfsInodesFree = uint64(1000) 687 rootfsInodes = uint64(2000) 688 imagefsCapacity = uint64(20000000) 689 imagefsAvailable = uint64(8000000) 690 imagefsInodesFree = uint64(2000) 691 imagefsInodes = uint64(4000) 692 ) 693 694 prf0 := statsapi.PodReference{Name: pName0, Namespace: namespace0, UID: "UID" + pName0} 695 infos := map[string]cadvisorapiv2.ContainerInfo{ 696 "/": getTestContainerInfo(seedRoot, "", "", ""), 697 "/docker-daemon": getTestContainerInfo(seedRuntime, "", "", ""), 698 "/kubelet": getTestContainerInfo(seedKubelet, "", "", ""), 699 "/system": getTestContainerInfo(seedMisc, "", "", ""), 700 // Pod0 - Namespace0 701 "/pod0-i": getTestContainerInfo(seedPod0Infra, pName0, namespace0, leaky.PodInfraContainerName), 702 "/pod0-c0": getTestContainerInfo(seedPod0Container0, pName0, namespace0, cName00), 703 "/pod0-c1": getTestContainerInfo(seedPod0Container1, pName0, namespace0, cName01), 704 } 705 706 containerLogStats0 := makeFakeLogStats(0) 707 containerLogStats1 := makeFakeLogStats(0) 708 fakeStats := map[string]*volume.Metrics{ 709 kuberuntime.BuildContainerLogsDirectory(prf0.Namespace, prf0.Name, types.UID(prf0.UID), cName00): containerLogStats0, 710 kuberuntime.BuildContainerLogsDirectory(prf0.Namespace, prf0.Name, types.UID(prf0.UID), cName01): containerLogStats1, 711 } 712 fakeStatsSlice := []*volume.Metrics{containerLogStats0, containerLogStats1} 713 fakeOS := &containertest.FakeOS{} 714 715 freeRootfsInodes := rootfsInodesFree 716 totalRootfsInodes := rootfsInodes 717 rootfs := cadvisorapiv2.FsInfo{ 718 Capacity: rootfsCapacity, 719 Available: rootfsAvailable, 720 InodesFree: &freeRootfsInodes, 721 Inodes: &totalRootfsInodes, 722 } 723 724 freeImagefsInodes := imagefsInodesFree 725 totalImagefsInodes := imagefsInodes 726 imagefs := cadvisorapiv2.FsInfo{ 727 Capacity: imagefsCapacity, 728 Available: imagefsAvailable, 729 InodesFree: &freeImagefsInodes, 730 Inodes: &totalImagefsInodes, 731 } 732 733 options := cadvisorapiv2.RequestOptions{ 734 IdType: cadvisorapiv2.TypeName, 735 Count: 2, 736 Recursive: true, 737 } 738 739 mockCtrl := gomock.NewController(t) 740 defer mockCtrl.Finish() 741 742 mockCadvisor := cadvisortest.NewMockInterface(mockCtrl) 743 mockCadvisor.EXPECT().ContainerInfoV2("/", options).Return(infos, nil) 744 mockCadvisor.EXPECT().RootFsInfo().Return(rootfs, nil) 745 mockCadvisor.EXPECT().ImagesFsInfo().Return(imagefs, nil) 746 747 mockRuntime := containertest.NewMockRuntime(mockCtrl) 748 mockRuntime.EXPECT().ImageStats(ctx).Return(&kubecontainer.ImageStats{TotalStorageBytes: 123}, nil).AnyTimes() 749 750 volumeStats := serverstats.PodVolumeStats{} 751 p0Time := metav1.Now() 752 mockStatus := statustest.NewMockPodStatusProvider(mockCtrl) 753 mockStatus.EXPECT().GetPodStatus(types.UID("UID"+pName0)).Return(v1.PodStatus{StartTime: &p0Time}, true) 754 755 resourceAnalyzer := &fakeResourceAnalyzer{podVolumeStats: volumeStats} 756 757 p := NewCadvisorStatsProvider(mockCadvisor, resourceAnalyzer, nil, nil, mockRuntime, mockStatus, NewFakeHostStatsProviderWithData(fakeStats, fakeOS)) 758 pods, err := p.ListPodStats(ctx) 759 assert.NoError(t, err) 760 761 assert.Equal(t, 1, len(pods)) 762 // Validate Pod0 Results 763 checkEphemeralStats(t, "Pod0", []int{seedPod0Container0, seedPod0Container1}, nil, fakeStatsSlice, pods[0].EphemeralStorage) 764 }