//go:build !windows // +build !windows /* Copyright 2016 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 ( "context" "testing" "time" "github.com/golang/mock/gomock" fuzz "github.com/google/gofuzz" "github.com/stretchr/testify/assert" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" statsapi "k8s.io/kubelet/pkg/apis/stats/v1alpha1" "k8s.io/kubernetes/pkg/kubelet/cm" statstest "k8s.io/kubernetes/pkg/kubelet/server/stats/testing" ) var ( imageFsStats = getFsStats() rootFsStats = getFsStats() node = &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "test-node"}} nodeConfig = cm.NodeConfig{ RuntimeCgroupsName: "/runtime", SystemCgroupsName: "/misc", KubeletCgroupsName: "/kubelet", } cgroupRoot = "/kubepods" rlimitStats = getRlimitStats() ) func TestSummaryProviderGetStatsNoSplitFileSystem(t *testing.T) { ctx := context.Background() assert := assert.New(t) podStats := []statsapi.PodStats{ { PodRef: statsapi.PodReference{Name: "test-pod", Namespace: "test-namespace", UID: "UID_test-pod"}, StartTime: metav1.NewTime(time.Now()), Containers: []statsapi.ContainerStats{*getContainerStats()}, Network: getNetworkStats(), VolumeStats: []statsapi.VolumeStats{*getVolumeStats()}, }, } cgroupStatsMap := map[string]struct { cs *statsapi.ContainerStats ns *statsapi.NetworkStats }{ "/": {cs: getContainerStats(), ns: getNetworkStats()}, "/runtime": {cs: getContainerStats(), ns: getNetworkStats()}, "/misc": {cs: getContainerStats(), ns: getNetworkStats()}, "/kubelet": {cs: getContainerStats(), ns: getNetworkStats()}, "/pods": {cs: getContainerStats(), ns: getNetworkStats()}, } mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() mockStatsProvider := statstest.NewMockProvider(mockCtrl) mockStatsProvider.EXPECT().GetNode().Return(node, nil) mockStatsProvider.EXPECT().GetNodeConfig().Return(nodeConfig) mockStatsProvider.EXPECT().GetPodCgroupRoot().Return(cgroupRoot) mockStatsProvider.EXPECT().ListPodStats(ctx).Return(podStats, nil).AnyTimes() mockStatsProvider.EXPECT().ListPodStatsAndUpdateCPUNanoCoreUsage(ctx).Return(podStats, nil) mockStatsProvider.EXPECT().ImageFsStats(ctx).Return(imageFsStats, imageFsStats, nil) mockStatsProvider.EXPECT().RootFsStats().Return(rootFsStats, nil) mockStatsProvider.EXPECT().RlimitStats().Return(rlimitStats, nil) mockStatsProvider.EXPECT().GetCgroupStats("/", true).Return(cgroupStatsMap["/"].cs, cgroupStatsMap["/"].ns, nil) mockStatsProvider.EXPECT().GetCgroupStats("/runtime", false).Return(cgroupStatsMap["/runtime"].cs, cgroupStatsMap["/runtime"].ns, nil) mockStatsProvider.EXPECT().GetCgroupStats("/misc", false).Return(cgroupStatsMap["/misc"].cs, cgroupStatsMap["/misc"].ns, nil) mockStatsProvider.EXPECT().GetCgroupStats("/kubelet", false).Return(cgroupStatsMap["/kubelet"].cs, cgroupStatsMap["/kubelet"].ns, nil) mockStatsProvider.EXPECT().GetCgroupStats("/kubepods", true).Return(cgroupStatsMap["/pods"].cs, cgroupStatsMap["/pods"].ns, nil) kubeletCreationTime := metav1.Now() systemBootTime := metav1.Now() provider := summaryProviderImpl{kubeletCreationTime: kubeletCreationTime, systemBootTime: systemBootTime, provider: mockStatsProvider} summary, err := provider.Get(ctx, true) assert.NoError(err) assert.Equal(summary.Node.NodeName, "test-node") assert.Equal(summary.Node.StartTime, systemBootTime) assert.Equal(summary.Node.CPU, cgroupStatsMap["/"].cs.CPU) assert.Equal(summary.Node.Memory, cgroupStatsMap["/"].cs.Memory) assert.Equal(summary.Node.Swap, cgroupStatsMap["/"].cs.Swap) assert.Equal(summary.Node.Network, cgroupStatsMap["/"].ns) assert.Equal(summary.Node.Fs, rootFsStats) assert.Equal(summary.Node.Runtime, &statsapi.RuntimeStats{ContainerFs: imageFsStats, ImageFs: imageFsStats}) assert.Equal(len(summary.Node.SystemContainers), 4) assert.Contains(summary.Node.SystemContainers, statsapi.ContainerStats{ Name: "kubelet", StartTime: kubeletCreationTime, CPU: cgroupStatsMap["/kubelet"].cs.CPU, Memory: cgroupStatsMap["/kubelet"].cs.Memory, Accelerators: cgroupStatsMap["/kubelet"].cs.Accelerators, UserDefinedMetrics: cgroupStatsMap["/kubelet"].cs.UserDefinedMetrics, Swap: cgroupStatsMap["/kubelet"].cs.Swap, }) assert.Contains(summary.Node.SystemContainers, statsapi.ContainerStats{ Name: "misc", StartTime: cgroupStatsMap["/misc"].cs.StartTime, CPU: cgroupStatsMap["/misc"].cs.CPU, Memory: cgroupStatsMap["/misc"].cs.Memory, Accelerators: cgroupStatsMap["/misc"].cs.Accelerators, UserDefinedMetrics: cgroupStatsMap["/misc"].cs.UserDefinedMetrics, Swap: cgroupStatsMap["/misc"].cs.Swap, }) assert.Contains(summary.Node.SystemContainers, statsapi.ContainerStats{ Name: "runtime", StartTime: cgroupStatsMap["/runtime"].cs.StartTime, CPU: cgroupStatsMap["/runtime"].cs.CPU, Memory: cgroupStatsMap["/runtime"].cs.Memory, Accelerators: cgroupStatsMap["/runtime"].cs.Accelerators, UserDefinedMetrics: cgroupStatsMap["/runtime"].cs.UserDefinedMetrics, Swap: cgroupStatsMap["/runtime"].cs.Swap, }) assert.Contains(summary.Node.SystemContainers, statsapi.ContainerStats{ Name: "pods", StartTime: cgroupStatsMap["/pods"].cs.StartTime, CPU: cgroupStatsMap["/pods"].cs.CPU, Memory: cgroupStatsMap["/pods"].cs.Memory, Accelerators: cgroupStatsMap["/pods"].cs.Accelerators, UserDefinedMetrics: cgroupStatsMap["/pods"].cs.UserDefinedMetrics, Swap: cgroupStatsMap["/pods"].cs.Swap, }) assert.Equal(summary.Pods, podStats) } func TestSummaryProviderGetStatsSplitImageFs(t *testing.T) { ctx := context.Background() assert := assert.New(t) podStats := []statsapi.PodStats{ { PodRef: statsapi.PodReference{Name: "test-pod", Namespace: "test-namespace", UID: "UID_test-pod"}, StartTime: metav1.NewTime(time.Now()), Containers: []statsapi.ContainerStats{*getContainerStats()}, Network: getNetworkStats(), VolumeStats: []statsapi.VolumeStats{*getVolumeStats()}, }, } cgroupStatsMap := map[string]struct { cs *statsapi.ContainerStats ns *statsapi.NetworkStats }{ "/": {cs: getContainerStats(), ns: getNetworkStats()}, "/runtime": {cs: getContainerStats(), ns: getNetworkStats()}, "/misc": {cs: getContainerStats(), ns: getNetworkStats()}, "/kubelet": {cs: getContainerStats(), ns: getNetworkStats()}, "/pods": {cs: getContainerStats(), ns: getNetworkStats()}, } mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() mockStatsProvider := statstest.NewMockProvider(mockCtrl) mockStatsProvider.EXPECT().GetNode().Return(node, nil) mockStatsProvider.EXPECT().GetNodeConfig().Return(nodeConfig) mockStatsProvider.EXPECT().GetPodCgroupRoot().Return(cgroupRoot) mockStatsProvider.EXPECT().ListPodStats(ctx).Return(podStats, nil).AnyTimes() mockStatsProvider.EXPECT().ListPodStatsAndUpdateCPUNanoCoreUsage(ctx).Return(podStats, nil) mockStatsProvider.EXPECT().RootFsStats().Return(rootFsStats, nil) mockStatsProvider.EXPECT().RlimitStats().Return(rlimitStats, nil) mockStatsProvider.EXPECT().GetCgroupStats("/", true).Return(cgroupStatsMap["/"].cs, cgroupStatsMap["/"].ns, nil) mockStatsProvider.EXPECT().GetCgroupStats("/runtime", false).Return(cgroupStatsMap["/runtime"].cs, cgroupStatsMap["/runtime"].ns, nil) mockStatsProvider.EXPECT().GetCgroupStats("/misc", false).Return(cgroupStatsMap["/misc"].cs, cgroupStatsMap["/misc"].ns, nil) mockStatsProvider.EXPECT().GetCgroupStats("/kubelet", false).Return(cgroupStatsMap["/kubelet"].cs, cgroupStatsMap["/kubelet"].ns, nil) mockStatsProvider.EXPECT().GetCgroupStats("/kubepods", true).Return(cgroupStatsMap["/pods"].cs, cgroupStatsMap["/pods"].ns, nil) mockStatsProvider.EXPECT().ImageFsStats(ctx).Return(imageFsStats, rootFsStats, nil) kubeletCreationTime := metav1.Now() systemBootTime := metav1.Now() provider := summaryProviderImpl{kubeletCreationTime: kubeletCreationTime, systemBootTime: systemBootTime, provider: mockStatsProvider} summary, err := provider.Get(ctx, true) assert.NoError(err) assert.Equal(summary.Node.NodeName, "test-node") assert.Equal(summary.Node.StartTime, systemBootTime) assert.Equal(summary.Node.CPU, cgroupStatsMap["/"].cs.CPU) assert.Equal(summary.Node.Memory, cgroupStatsMap["/"].cs.Memory) assert.Equal(summary.Node.Swap, cgroupStatsMap["/"].cs.Swap) assert.Equal(summary.Node.Network, cgroupStatsMap["/"].ns) assert.Equal(summary.Node.Fs, rootFsStats) // Since we are a split filesystem we want root filesystem to be container fs and image to be image filesystem assert.Equal(summary.Node.Runtime, &statsapi.RuntimeStats{ContainerFs: rootFsStats, ImageFs: imageFsStats}) assert.Equal(len(summary.Node.SystemContainers), 4) assert.Contains(summary.Node.SystemContainers, statsapi.ContainerStats{ Name: "kubelet", StartTime: kubeletCreationTime, CPU: cgroupStatsMap["/kubelet"].cs.CPU, Memory: cgroupStatsMap["/kubelet"].cs.Memory, Accelerators: cgroupStatsMap["/kubelet"].cs.Accelerators, UserDefinedMetrics: cgroupStatsMap["/kubelet"].cs.UserDefinedMetrics, Swap: cgroupStatsMap["/kubelet"].cs.Swap, }) assert.Contains(summary.Node.SystemContainers, statsapi.ContainerStats{ Name: "misc", StartTime: cgroupStatsMap["/misc"].cs.StartTime, CPU: cgroupStatsMap["/misc"].cs.CPU, Memory: cgroupStatsMap["/misc"].cs.Memory, Accelerators: cgroupStatsMap["/misc"].cs.Accelerators, UserDefinedMetrics: cgroupStatsMap["/misc"].cs.UserDefinedMetrics, Swap: cgroupStatsMap["/misc"].cs.Swap, }) assert.Contains(summary.Node.SystemContainers, statsapi.ContainerStats{ Name: "runtime", StartTime: cgroupStatsMap["/runtime"].cs.StartTime, CPU: cgroupStatsMap["/runtime"].cs.CPU, Memory: cgroupStatsMap["/runtime"].cs.Memory, Accelerators: cgroupStatsMap["/runtime"].cs.Accelerators, UserDefinedMetrics: cgroupStatsMap["/runtime"].cs.UserDefinedMetrics, Swap: cgroupStatsMap["/runtime"].cs.Swap, }) assert.Contains(summary.Node.SystemContainers, statsapi.ContainerStats{ Name: "pods", StartTime: cgroupStatsMap["/pods"].cs.StartTime, CPU: cgroupStatsMap["/pods"].cs.CPU, Memory: cgroupStatsMap["/pods"].cs.Memory, Accelerators: cgroupStatsMap["/pods"].cs.Accelerators, UserDefinedMetrics: cgroupStatsMap["/pods"].cs.UserDefinedMetrics, Swap: cgroupStatsMap["/pods"].cs.Swap, }) assert.Equal(summary.Pods, podStats) } func TestSummaryProviderGetCPUAndMemoryStats(t *testing.T) { ctx := context.Background() assert := assert.New(t) podStats := []statsapi.PodStats{ { PodRef: statsapi.PodReference{Name: "test-pod", Namespace: "test-namespace", UID: "UID_test-pod"}, StartTime: metav1.NewTime(time.Now()), Containers: []statsapi.ContainerStats{*getContainerStats()}, }, } cgroupStatsMap := map[string]struct { cs *statsapi.ContainerStats }{ "/": {cs: getVolumeCPUAndMemoryStats()}, "/runtime": {cs: getVolumeCPUAndMemoryStats()}, "/misc": {cs: getVolumeCPUAndMemoryStats()}, "/kubelet": {cs: getVolumeCPUAndMemoryStats()}, "/pods": {cs: getVolumeCPUAndMemoryStats()}, } mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() mockStatsProvider := statstest.NewMockProvider(mockCtrl) mockStatsProvider.EXPECT().GetNode().Return(node, nil) mockStatsProvider.EXPECT().GetNodeConfig().Return(nodeConfig) mockStatsProvider.EXPECT().GetPodCgroupRoot().Return(cgroupRoot) mockStatsProvider.EXPECT().ListPodCPUAndMemoryStats(ctx).Return(podStats, nil) mockStatsProvider.EXPECT().GetCgroupCPUAndMemoryStats("/", false).Return(cgroupStatsMap["/"].cs, nil) mockStatsProvider.EXPECT().GetCgroupCPUAndMemoryStats("/runtime", false).Return(cgroupStatsMap["/runtime"].cs, nil) mockStatsProvider.EXPECT().GetCgroupCPUAndMemoryStats("/misc", false).Return(cgroupStatsMap["/misc"].cs, nil) mockStatsProvider.EXPECT().GetCgroupCPUAndMemoryStats("/kubelet", false).Return(cgroupStatsMap["/kubelet"].cs, nil) mockStatsProvider.EXPECT().GetCgroupCPUAndMemoryStats("/kubepods", false).Return(cgroupStatsMap["/pods"].cs, nil) provider := NewSummaryProvider(mockStatsProvider) summary, err := provider.GetCPUAndMemoryStats(ctx) assert.NoError(err) assert.Equal(summary.Node.NodeName, "test-node") assert.Equal(summary.Node.StartTime, cgroupStatsMap["/"].cs.StartTime) assert.Equal(summary.Node.CPU, cgroupStatsMap["/"].cs.CPU) assert.Equal(summary.Node.Memory, cgroupStatsMap["/"].cs.Memory) assert.Nil(summary.Node.Network) assert.Nil(summary.Node.Fs) assert.Nil(summary.Node.Runtime) assert.Equal(len(summary.Node.SystemContainers), 4) assert.Contains(summary.Node.SystemContainers, statsapi.ContainerStats{ Name: "kubelet", StartTime: cgroupStatsMap["/kubelet"].cs.StartTime, CPU: cgroupStatsMap["/kubelet"].cs.CPU, Memory: cgroupStatsMap["/kubelet"].cs.Memory, }) assert.Contains(summary.Node.SystemContainers, statsapi.ContainerStats{ Name: "misc", StartTime: cgroupStatsMap["/misc"].cs.StartTime, CPU: cgroupStatsMap["/misc"].cs.CPU, Memory: cgroupStatsMap["/misc"].cs.Memory, }) assert.Contains(summary.Node.SystemContainers, statsapi.ContainerStats{ Name: "runtime", StartTime: cgroupStatsMap["/runtime"].cs.StartTime, CPU: cgroupStatsMap["/runtime"].cs.CPU, Memory: cgroupStatsMap["/runtime"].cs.Memory, }) assert.Contains(summary.Node.SystemContainers, statsapi.ContainerStats{ Name: "pods", StartTime: cgroupStatsMap["/pods"].cs.StartTime, CPU: cgroupStatsMap["/pods"].cs.CPU, Memory: cgroupStatsMap["/pods"].cs.Memory, }) assert.Equal(summary.Pods, podStats) } func getFsStats() *statsapi.FsStats { f := fuzz.New().NilChance(0) v := &statsapi.FsStats{} f.Fuzz(v) return v } func getContainerStats() *statsapi.ContainerStats { f := fuzz.New().NilChance(0) v := &statsapi.ContainerStats{} f.Fuzz(v) return v } func getVolumeCPUAndMemoryStats() *statsapi.ContainerStats { f := fuzz.New().NilChance(0) v := &statsapi.ContainerStats{} f.Fuzz(&v.Name) f.Fuzz(&v.StartTime) f.Fuzz(v.CPU) f.Fuzz(v.Memory) return v } func getVolumeStats() *statsapi.VolumeStats { f := fuzz.New().NilChance(0) v := &statsapi.VolumeStats{} f.Fuzz(v) return v } func getNetworkStats() *statsapi.NetworkStats { f := fuzz.New().NilChance(0) v := &statsapi.NetworkStats{} f.Fuzz(v) return v } func getRlimitStats() *statsapi.RlimitStats { f := fuzz.New().NilChance(0) v := &statsapi.RlimitStats{} f.Fuzz(v) return v }