/* Copyright 2017 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package stats import ( "math/rand" "testing" "time" cadvisorfs "github.com/google/cadvisor/fs" "github.com/stretchr/testify/assert" cadvisorapiv2 "github.com/google/cadvisor/info/v2" critest "k8s.io/kubernetes/pkg/kubelet/apis/cri/testing" runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime" statsapi "k8s.io/kubernetes/pkg/kubelet/apis/stats/v1alpha1" cadvisortest "k8s.io/kubernetes/pkg/kubelet/cadvisor/testing" kubecontainertest "k8s.io/kubernetes/pkg/kubelet/container/testing" kubepodtest "k8s.io/kubernetes/pkg/kubelet/pod/testing" ) func TestCRIListPodStats(t *testing.T) { var ( imageFsStorageUUID = "imagefs-storage-uuid" unknownStorageUUID = "unknown-storage-uuid" imageFsInfo = getTestFsInfo(2000) rootFsInfo = getTestFsInfo(1000) sandbox0 = makeFakePodSandbox("sandbox0-name", "sandbox0-uid", "sandbox0-ns") container0 = makeFakeContainer(sandbox0, "container0-name") containerStats0 = makeFakeContainerStats(container0, imageFsStorageUUID) container1 = makeFakeContainer(sandbox0, "container1-name") containerStats1 = makeFakeContainerStats(container1, unknownStorageUUID) sandbox1 = makeFakePodSandbox("sandbox1-name", "sandbox1-uid", "sandbox1-ns") container2 = makeFakeContainer(sandbox1, "container2-name") containerStats2 = makeFakeContainerStats(container2, imageFsStorageUUID) ) var ( mockCadvisor = new(cadvisortest.Mock) mockRuntimeCache = new(kubecontainertest.MockRuntimeCache) mockPodManager = new(kubepodtest.MockManager) resourceAnalyzer = new(fakeResourceAnalyzer) fakeRuntimeService = critest.NewFakeRuntimeService() fakeImageService = critest.NewFakeImageService() ) mockCadvisor. On("RootFsInfo").Return(rootFsInfo, nil). On("GetFsInfoByFsUUID", imageFsStorageUUID).Return(imageFsInfo, nil). On("GetFsInfoByFsUUID", unknownStorageUUID).Return(cadvisorapiv2.FsInfo{}, cadvisorfs.ErrNoSuchDevice) fakeRuntimeService.SetFakeSandboxes([]*critest.FakePodSandbox{ sandbox0, sandbox1, }) fakeRuntimeService.SetFakeContainers([]*critest.FakeContainer{ container0, container1, container2, }) fakeRuntimeService.SetFakeContainerStats([]*runtimeapi.ContainerStats{ containerStats0, containerStats1, containerStats2, }) provider := NewCRIStatsProvider( mockCadvisor, resourceAnalyzer, mockPodManager, mockRuntimeCache, fakeRuntimeService, fakeImageService) stats, err := provider.ListPodStats() assert := assert.New(t) assert.NoError(err) assert.Equal(2, len(stats)) podStatsMap := make(map[statsapi.PodReference]statsapi.PodStats) for _, s := range stats { podStatsMap[s.PodRef] = s } p0 := podStatsMap[statsapi.PodReference{Name: "sandbox0-name", UID: "sandbox0-uid", Namespace: "sandbox0-ns"}] assert.Equal(sandbox0.CreatedAt, p0.StartTime.UnixNano()) assert.Equal(2, len(p0.Containers)) containerStatsMap := make(map[string]statsapi.ContainerStats) for _, s := range p0.Containers { containerStatsMap[s.Name] = s } c1 := containerStatsMap["container0-name"] assert.Equal(container0.CreatedAt, c1.StartTime.UnixNano()) checkCRICPUAndMemoryStats(assert, c1, containerStats0) checkCRIRootfsStats(assert, c1, containerStats0, &imageFsInfo) checkCRILogsStats(assert, c1, &rootFsInfo) c2 := containerStatsMap["container1-name"] assert.Equal(container1.CreatedAt, c2.StartTime.UnixNano()) checkCRICPUAndMemoryStats(assert, c2, containerStats1) checkCRIRootfsStats(assert, c2, containerStats1, nil) checkCRILogsStats(assert, c2, &rootFsInfo) p1 := podStatsMap[statsapi.PodReference{Name: "sandbox1-name", UID: "sandbox1-uid", Namespace: "sandbox1-ns"}] assert.Equal(sandbox1.CreatedAt, p1.StartTime.UnixNano()) assert.Equal(1, len(p1.Containers)) c3 := p1.Containers[0] assert.Equal("container2-name", c3.Name) assert.Equal(container2.CreatedAt, c3.StartTime.UnixNano()) checkCRICPUAndMemoryStats(assert, c3, containerStats2) checkCRIRootfsStats(assert, c3, containerStats2, &imageFsInfo) checkCRILogsStats(assert, c3, &rootFsInfo) mockCadvisor.AssertExpectations(t) } func TestCRIImagesFsStats(t *testing.T) { var ( imageFsStorageUUID = "imagefs-storage-uuid" imageFsInfo = getTestFsInfo(2000) imageFsUsage = makeFakeImageFsUsage(imageFsStorageUUID) ) var ( mockCadvisor = new(cadvisortest.Mock) mockRuntimeCache = new(kubecontainertest.MockRuntimeCache) mockPodManager = new(kubepodtest.MockManager) resourceAnalyzer = new(fakeResourceAnalyzer) fakeRuntimeService = critest.NewFakeRuntimeService() fakeImageService = critest.NewFakeImageService() ) mockCadvisor.On("GetFsInfoByFsUUID", imageFsStorageUUID).Return(imageFsInfo, nil) fakeImageService.SetFakeFilesystemUsage([]*runtimeapi.FilesystemUsage{ imageFsUsage, }) provider := NewCRIStatsProvider( mockCadvisor, resourceAnalyzer, mockPodManager, mockRuntimeCache, fakeRuntimeService, fakeImageService) stats, err := provider.ImageFsStats() assert := assert.New(t) assert.NoError(err) assert.Equal(imageFsUsage.Timestamp, stats.Time.UnixNano()) assert.Equal(imageFsInfo.Available, *stats.AvailableBytes) assert.Equal(imageFsInfo.Capacity, *stats.CapacityBytes) assert.Equal(imageFsInfo.InodesFree, stats.InodesFree) assert.Equal(imageFsInfo.Inodes, stats.Inodes) assert.Equal(imageFsUsage.UsedBytes.Value, *stats.UsedBytes) assert.Equal(imageFsUsage.InodesUsed.Value, *stats.InodesUsed) mockCadvisor.AssertExpectations(t) } func makeFakePodSandbox(name, uid, namespace string) *critest.FakePodSandbox { p := &critest.FakePodSandbox{ PodSandboxStatus: runtimeapi.PodSandboxStatus{ Metadata: &runtimeapi.PodSandboxMetadata{ Name: name, Uid: uid, Namespace: namespace, }, State: runtimeapi.PodSandboxState_SANDBOX_READY, CreatedAt: time.Now().UnixNano(), }, } p.PodSandboxStatus.Id = critest.BuildSandboxName(p.PodSandboxStatus.Metadata) return p } func makeFakeContainer(sandbox *critest.FakePodSandbox, name string) *critest.FakeContainer { sandboxID := sandbox.PodSandboxStatus.Id c := &critest.FakeContainer{ SandboxID: sandboxID, ContainerStatus: runtimeapi.ContainerStatus{ Metadata: &runtimeapi.ContainerMetadata{Name: name}, Image: &runtimeapi.ImageSpec{}, ImageRef: "fake-image-ref", CreatedAt: time.Now().UnixNano(), State: runtimeapi.ContainerState_CONTAINER_RUNNING, }, } c.ContainerStatus.Id = critest.BuildContainerName(c.ContainerStatus.Metadata, sandboxID) return c } func makeFakeContainerStats(container *critest.FakeContainer, imageFsUUID string) *runtimeapi.ContainerStats { return &runtimeapi.ContainerStats{ Attributes: &runtimeapi.ContainerAttributes{ Id: container.ContainerStatus.Id, Metadata: container.ContainerStatus.Metadata, }, Cpu: &runtimeapi.CpuUsage{ Timestamp: time.Now().UnixNano(), UsageCoreNanoSeconds: &runtimeapi.UInt64Value{Value: rand.Uint64()}, }, Memory: &runtimeapi.MemoryUsage{ Timestamp: time.Now().UnixNano(), WorkingSetBytes: &runtimeapi.UInt64Value{Value: rand.Uint64()}, }, WritableLayer: &runtimeapi.FilesystemUsage{ Timestamp: time.Now().UnixNano(), StorageId: &runtimeapi.StorageIdentifier{Uuid: imageFsUUID}, UsedBytes: &runtimeapi.UInt64Value{Value: rand.Uint64()}, InodesUsed: &runtimeapi.UInt64Value{Value: rand.Uint64()}, }, } } func makeFakeImageFsUsage(fsUUID string) *runtimeapi.FilesystemUsage { return &runtimeapi.FilesystemUsage{ Timestamp: time.Now().UnixNano(), StorageId: &runtimeapi.StorageIdentifier{Uuid: fsUUID}, UsedBytes: &runtimeapi.UInt64Value{Value: rand.Uint64()}, InodesUsed: &runtimeapi.UInt64Value{Value: rand.Uint64()}, } } func checkCRICPUAndMemoryStats(assert *assert.Assertions, actual statsapi.ContainerStats, cs *runtimeapi.ContainerStats) { assert.Equal(cs.Cpu.Timestamp, actual.CPU.Time.UnixNano()) assert.Equal(cs.Cpu.UsageCoreNanoSeconds.Value, *actual.CPU.UsageCoreNanoSeconds) assert.Nil(actual.CPU.UsageNanoCores) assert.Equal(cs.Memory.Timestamp, actual.Memory.Time.UnixNano()) assert.Nil(actual.Memory.AvailableBytes) assert.Nil(actual.Memory.UsageBytes) assert.Equal(cs.Memory.WorkingSetBytes.Value, *actual.Memory.WorkingSetBytes) assert.Nil(actual.Memory.RSSBytes) assert.Nil(actual.Memory.PageFaults) assert.Nil(actual.Memory.MajorPageFaults) } func checkCRIRootfsStats(assert *assert.Assertions, actual statsapi.ContainerStats, cs *runtimeapi.ContainerStats, imageFsInfo *cadvisorapiv2.FsInfo) { assert.Equal(cs.WritableLayer.Timestamp, actual.Rootfs.Time.UnixNano()) if imageFsInfo != nil { assert.Equal(imageFsInfo.Available, *actual.Rootfs.AvailableBytes) assert.Equal(imageFsInfo.Capacity, *actual.Rootfs.CapacityBytes) assert.Equal(*imageFsInfo.InodesFree, *actual.Rootfs.InodesFree) assert.Equal(*imageFsInfo.Inodes, *actual.Rootfs.Inodes) } else { assert.Nil(actual.Rootfs.AvailableBytes) assert.Nil(actual.Rootfs.CapacityBytes) assert.Nil(actual.Rootfs.InodesFree) assert.Nil(actual.Rootfs.Inodes) } assert.Equal(cs.WritableLayer.UsedBytes.Value, *actual.Rootfs.UsedBytes) assert.Equal(cs.WritableLayer.InodesUsed.Value, *actual.Rootfs.InodesUsed) } func checkCRILogsStats(assert *assert.Assertions, actual statsapi.ContainerStats, rootFsInfo *cadvisorapiv2.FsInfo) { assert.Equal(rootFsInfo.Timestamp, actual.Logs.Time.Time) assert.Equal(rootFsInfo.Available, *actual.Logs.AvailableBytes) assert.Equal(rootFsInfo.Capacity, *actual.Logs.CapacityBytes) assert.Equal(*rootFsInfo.InodesFree, *actual.Logs.InodesFree) assert.Equal(*rootFsInfo.Inodes, *actual.Logs.Inodes) assert.Nil(actual.Logs.UsedBytes) assert.Nil(actual.Logs.InodesUsed) }