k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/kubelet/stats/cri_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  	"math/rand"
    22  	"os"
    23  	"path/filepath"
    24  	"runtime"
    25  	"strings"
    26  	"testing"
    27  	"time"
    28  
    29  	cadvisorfs "github.com/google/cadvisor/fs"
    30  	cadvisorapiv2 "github.com/google/cadvisor/info/v2"
    31  	"github.com/stretchr/testify/assert"
    32  	gomock "go.uber.org/mock/gomock"
    33  	"k8s.io/apimachinery/pkg/api/resource"
    34  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    35  	"k8s.io/apimachinery/pkg/types"
    36  	"k8s.io/apimachinery/pkg/util/uuid"
    37  	runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
    38  	critest "k8s.io/cri-api/pkg/apis/testing"
    39  	statsapi "k8s.io/kubelet/pkg/apis/stats/v1alpha1"
    40  	kubelettypes "k8s.io/kubelet/pkg/types"
    41  	cadvisortest "k8s.io/kubernetes/pkg/kubelet/cadvisor/testing"
    42  	"k8s.io/kubernetes/pkg/kubelet/cm"
    43  	kubecontainertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
    44  	"k8s.io/kubernetes/pkg/kubelet/kuberuntime"
    45  	kubepodtest "k8s.io/kubernetes/pkg/kubelet/pod/testing"
    46  	serverstats "k8s.io/kubernetes/pkg/kubelet/server/stats"
    47  	"k8s.io/kubernetes/pkg/volume"
    48  )
    49  
    50  const (
    51  	offsetInodeUsage = iota
    52  	offsetUsage
    53  )
    54  
    55  const (
    56  	// This offset offsetCRI is to distinguish it from Cadvisor stats
    57  	offsetCRI = 1000
    58  )
    59  
    60  const (
    61  	seedRoot       = 0
    62  	seedKubelet    = 200
    63  	seedMisc       = 300
    64  	seedSandbox0   = 1000
    65  	seedContainer0 = 2000
    66  	seedSandbox1   = 3000
    67  	seedContainer1 = 4000
    68  	seedContainer2 = 5000
    69  	seedSandbox2   = 6000
    70  	seedContainer3 = 7000
    71  	seedSandbox3   = 8000
    72  )
    73  
    74  const (
    75  	pName0 = "pod0"
    76  	pName1 = "pod1"
    77  	pName2 = "pod2"
    78  )
    79  
    80  const (
    81  	cName0 = "container0-name"
    82  	cName1 = "container1-name"
    83  	cName2 = "container2-name"
    84  	cName3 = "container3-name"
    85  	cName5 = "container5-name"
    86  	cName6 = "container6-name"
    87  	cName7 = "container7-name"
    88  	cName8 = "container8-name"
    89  	cName9 = "container9-name"
    90  )
    91  
    92  const testPodLogDirectory = "/var/log/kube/pods/" // Use non-default path to ensure stats are collected properly
    93  
    94  func TestCRIListPodStats(t *testing.T) {
    95  	ctx := context.Background()
    96  	var (
    97  		imageFsMountpoint = "/test/mount/point"
    98  		unknownMountpoint = "/unknown/mount/point"
    99  		imageFsInfo       = getTestFsInfo(2000)
   100  		rootFsInfo        = getTestFsInfo(1000)
   101  
   102  		sandbox0           = makeFakePodSandbox("sandbox0-name", "sandbox0-uid", "sandbox0-ns", false)
   103  		sandbox0Cgroup     = "/" + cm.GetPodCgroupNameSuffix(types.UID(sandbox0.PodSandboxStatus.Metadata.Uid))
   104  		container0         = makeFakeContainer(sandbox0, cName0, 0, false)
   105  		containerStats0    = makeFakeContainerStats(container0, imageFsMountpoint)
   106  		containerLogStats0 = makeFakeLogStats(1000)
   107  		container1         = makeFakeContainer(sandbox0, cName1, 0, false)
   108  		containerStats1    = makeFakeContainerStats(container1, unknownMountpoint)
   109  		containerLogStats1 = makeFakeLogStats(2000)
   110  
   111  		sandbox1           = makeFakePodSandbox("sandbox1-name", "sandbox1-uid", "sandbox1-ns", false)
   112  		sandbox1Cgroup     = "/" + cm.GetPodCgroupNameSuffix(types.UID(sandbox1.PodSandboxStatus.Metadata.Uid))
   113  		container2         = makeFakeContainer(sandbox1, cName2, 0, false)
   114  		containerStats2    = makeFakeContainerStats(container2, imageFsMountpoint)
   115  		containerLogStats2 = makeFakeLogStats(3000)
   116  
   117  		sandbox2           = makeFakePodSandbox("sandbox2-name", "sandbox2-uid", "sandbox2-ns", false)
   118  		sandbox2Cgroup     = "/" + cm.GetPodCgroupNameSuffix(types.UID(sandbox2.PodSandboxStatus.Metadata.Uid))
   119  		container3         = makeFakeContainer(sandbox2, cName3, 0, true)
   120  		containerStats3    = makeFakeContainerStats(container3, imageFsMountpoint)
   121  		container4         = makeFakeContainer(sandbox2, cName3, 1, false)
   122  		containerStats4    = makeFakeContainerStats(container4, imageFsMountpoint)
   123  		containerLogStats4 = makeFakeLogStats(4000)
   124  
   125  		// Running pod with a terminated container and a running container
   126  		sandbox3           = makeFakePodSandbox("sandbox3-name", "sandbox3-uid", "sandbox3-ns", false)
   127  		sandbox3Cgroup     = "/" + cm.GetPodCgroupNameSuffix(types.UID(sandbox3.PodSandboxStatus.Metadata.Uid))
   128  		container5         = makeFakeContainer(sandbox3, cName5, 0, true)
   129  		containerStats5    = makeFakeContainerStats(container5, imageFsMountpoint)
   130  		containerLogStats5 = makeFakeLogStats(5000)
   131  		container8         = makeFakeContainer(sandbox3, cName8, 0, false)
   132  		containerStats8    = makeFakeContainerStats(container8, imageFsMountpoint)
   133  		containerLogStats8 = makeFakeLogStats(6000)
   134  
   135  		// Terminated pod sandbox
   136  		sandbox4        = makeFakePodSandbox("sandbox1-name", "sandbox1-uid", "sandbox1-ns", true)
   137  		container6      = makeFakeContainer(sandbox4, cName6, 0, true)
   138  		containerStats6 = makeFakeContainerStats(container6, imageFsMountpoint)
   139  
   140  		// Terminated pod
   141  		sandbox5        = makeFakePodSandbox("sandbox1-name", "sandbox5-uid", "sandbox1-ns", true)
   142  		container7      = makeFakeContainer(sandbox5, cName7, 0, true)
   143  		containerStats7 = makeFakeContainerStats(container7, imageFsMountpoint)
   144  
   145  		podLogName0  = "pod-log-0"
   146  		podLogName1  = "pod-log-1"
   147  		podLogStats0 = makeFakeLogStats(5000)
   148  		podLogStats1 = makeFakeLogStats(6000)
   149  	)
   150  
   151  	mockCtrl := gomock.NewController(t)
   152  	defer mockCtrl.Finish()
   153  
   154  	var (
   155  		mockCadvisor       = cadvisortest.NewMockInterface(mockCtrl)
   156  		mockRuntimeCache   = new(kubecontainertest.MockRuntimeCache)
   157  		mockPodManager     = new(kubepodtest.MockManager)
   158  		resourceAnalyzer   = new(fakeResourceAnalyzer)
   159  		fakeRuntimeService = critest.NewFakeRuntimeService()
   160  		fakeImageService   = critest.NewFakeImageService()
   161  	)
   162  
   163  	infos := map[string]cadvisorapiv2.ContainerInfo{
   164  		"/":                           getTestContainerInfo(seedRoot, "", "", ""),
   165  		"/kubelet":                    getTestContainerInfo(seedKubelet, "", "", ""),
   166  		"/system":                     getTestContainerInfo(seedMisc, "", "", ""),
   167  		sandbox0.PodSandboxStatus.Id:  getTestContainerInfo(seedSandbox0, pName0, sandbox0.PodSandboxStatus.Metadata.Namespace, kubelettypes.PodInfraContainerName),
   168  		sandbox0Cgroup:                getTestContainerInfo(seedSandbox0, "", "", ""),
   169  		container0.ContainerStatus.Id: getTestContainerInfo(seedContainer0, pName0, sandbox0.PodSandboxStatus.Metadata.Namespace, cName0),
   170  		container1.ContainerStatus.Id: getTestContainerInfo(seedContainer1, pName0, sandbox0.PodSandboxStatus.Metadata.Namespace, cName1),
   171  		sandbox1.PodSandboxStatus.Id:  getTestContainerInfo(seedSandbox1, pName1, sandbox1.PodSandboxStatus.Metadata.Namespace, kubelettypes.PodInfraContainerName),
   172  		sandbox1Cgroup:                getTestContainerInfo(seedSandbox1, "", "", ""),
   173  		container2.ContainerStatus.Id: getTestContainerInfo(seedContainer2, pName1, sandbox1.PodSandboxStatus.Metadata.Namespace, cName2),
   174  		sandbox2.PodSandboxStatus.Id:  getTestContainerInfo(seedSandbox2, pName2, sandbox2.PodSandboxStatus.Metadata.Namespace, kubelettypes.PodInfraContainerName),
   175  		sandbox2Cgroup:                getTestContainerInfo(seedSandbox2, "", "", ""),
   176  		container4.ContainerStatus.Id: getTestContainerInfo(seedContainer3, pName2, sandbox2.PodSandboxStatus.Metadata.Namespace, cName3),
   177  		sandbox3Cgroup:                getTestContainerInfo(seedSandbox3, "", "", ""),
   178  	}
   179  
   180  	options := cadvisorapiv2.RequestOptions{
   181  		IdType:    cadvisorapiv2.TypeName,
   182  		Count:     2,
   183  		Recursive: true,
   184  	}
   185  
   186  	mockCadvisor.EXPECT().ContainerInfoV2("/", options).Return(infos, nil)
   187  	mockCadvisor.EXPECT().RootFsInfo().Return(rootFsInfo, nil)
   188  	mockCadvisor.EXPECT().GetDirFsInfo(imageFsMountpoint).Return(imageFsInfo, nil)
   189  	mockCadvisor.EXPECT().GetDirFsInfo(unknownMountpoint).Return(cadvisorapiv2.FsInfo{}, cadvisorfs.ErrNoSuchDevice)
   190  
   191  	fakeRuntimeService.SetFakeSandboxes([]*critest.FakePodSandbox{
   192  		sandbox0, sandbox1, sandbox2, sandbox3, sandbox4, sandbox5,
   193  	})
   194  	fakeRuntimeService.SetFakeContainers([]*critest.FakeContainer{
   195  		container0, container1, container2, container3, container4, container5, container6, container7, container8,
   196  	})
   197  	fakeRuntimeService.SetFakeContainerStats([]*runtimeapi.ContainerStats{
   198  		containerStats0, containerStats1, containerStats2, containerStats3, containerStats4, containerStats5, containerStats6, containerStats7, containerStats8,
   199  	})
   200  
   201  	ephemeralVolumes := makeFakeVolumeStats([]string{"ephVolume1, ephVolumes2"})
   202  	persistentVolumes := makeFakeVolumeStats([]string{"persisVolume1, persisVolumes2"})
   203  	resourceAnalyzer.podVolumeStats = serverstats.PodVolumeStats{
   204  		EphemeralVolumes:  ephemeralVolumes,
   205  		PersistentVolumes: persistentVolumes,
   206  	}
   207  
   208  	fakeStats := map[string]*volume.Metrics{
   209  		kuberuntime.BuildContainerLogsDirectory(testPodLogDirectory, "sandbox0-ns", "sandbox0-name", types.UID("sandbox0-uid"), cName0):               containerLogStats0,
   210  		kuberuntime.BuildContainerLogsDirectory(testPodLogDirectory, "sandbox0-ns", "sandbox0-name", types.UID("sandbox0-uid"), cName1):               containerLogStats1,
   211  		kuberuntime.BuildContainerLogsDirectory(testPodLogDirectory, "sandbox1-ns", "sandbox1-name", types.UID("sandbox1-uid"), cName2):               containerLogStats2,
   212  		kuberuntime.BuildContainerLogsDirectory(testPodLogDirectory, "sandbox2-ns", "sandbox2-name", types.UID("sandbox2-uid"), cName3):               containerLogStats4,
   213  		kuberuntime.BuildContainerLogsDirectory(testPodLogDirectory, "sandbox3-ns", "sandbox3-name", types.UID("sandbox3-uid"), cName5):               containerLogStats5,
   214  		kuberuntime.BuildContainerLogsDirectory(testPodLogDirectory, "sandbox3-ns", "sandbox3-name", types.UID("sandbox3-uid"), cName8):               containerLogStats8,
   215  		filepath.Join(kuberuntime.BuildPodLogsDirectory(testPodLogDirectory, "sandbox0-ns", "sandbox0-name", types.UID("sandbox0-uid")), podLogName0): podLogStats0,
   216  		filepath.Join(kuberuntime.BuildPodLogsDirectory(testPodLogDirectory, "sandbox1-ns", "sandbox1-name", types.UID("sandbox1-uid")), podLogName1): podLogStats1,
   217  	}
   218  
   219  	ctrl := gomock.NewController(t)
   220  	defer ctrl.Finish()
   221  
   222  	fakeOS := &kubecontainertest.FakeOS{}
   223  	fakeOS.ReadDirFn = func(path string) ([]os.DirEntry, error) {
   224  		var dirEntries []os.DirEntry
   225  		mockDE := kubecontainertest.NewMockDirEntry(ctrl)
   226  		switch path {
   227  		case kuberuntime.BuildPodLogsDirectory(testPodLogDirectory, "sandbox0-ns", "sandbox0-name", types.UID("sandbox0-uid")):
   228  			mockDE.EXPECT().Name().Return(podLogName0)
   229  		case kuberuntime.BuildPodLogsDirectory(testPodLogDirectory, "sandbox1-ns", "sandbox1-name", types.UID("sandbox1-uid")):
   230  			mockDE.EXPECT().Name().Return(podLogName1)
   231  		default:
   232  			return nil, nil
   233  		}
   234  		mockDE.EXPECT().IsDir().Return(false)
   235  		dirEntries = append(dirEntries, mockDE)
   236  		return dirEntries, nil
   237  	}
   238  
   239  	provider := NewCRIStatsProvider(
   240  		mockCadvisor,
   241  		resourceAnalyzer,
   242  		mockPodManager,
   243  		mockRuntimeCache,
   244  		fakeRuntimeService,
   245  		fakeImageService,
   246  		NewFakeHostStatsProviderWithData(fakeStats, fakeOS),
   247  		false,
   248  	)
   249  
   250  	stats, err := provider.ListPodStats(ctx)
   251  	assert := assert.New(t)
   252  	assert.NoError(err)
   253  	assert.Equal(4, len(stats))
   254  
   255  	podStatsMap := make(map[statsapi.PodReference]statsapi.PodStats)
   256  	for _, s := range stats {
   257  		podStatsMap[s.PodRef] = s
   258  	}
   259  
   260  	p0 := podStatsMap[statsapi.PodReference{Name: "sandbox0-name", UID: "sandbox0-uid", Namespace: "sandbox0-ns"}]
   261  	assert.Equal(sandbox0.CreatedAt, p0.StartTime.UnixNano())
   262  	assert.Equal(2, len(p0.Containers))
   263  
   264  	checkEphemeralStorageStats(assert, p0, ephemeralVolumes, []*runtimeapi.ContainerStats{containerStats0, containerStats1},
   265  		[]*volume.Metrics{containerLogStats0, containerLogStats1}, podLogStats0)
   266  
   267  	containerStatsMap := make(map[string]statsapi.ContainerStats)
   268  	for _, s := range p0.Containers {
   269  		containerStatsMap[s.Name] = s
   270  	}
   271  
   272  	c0 := containerStatsMap[cName0]
   273  	assert.Equal(container0.CreatedAt, c0.StartTime.UnixNano())
   274  	checkCRICPUAndMemoryStats(assert, c0, infos[container0.ContainerStatus.Id].Stats[0])
   275  	assert.Nil(c0.Accelerators)
   276  	checkCRIRootfsStats(assert, c0, containerStats0, &imageFsInfo)
   277  	checkCRILogsStats(assert, c0, &rootFsInfo, containerLogStats0)
   278  
   279  	c1 := containerStatsMap[cName1]
   280  	assert.Equal(container1.CreatedAt, c1.StartTime.UnixNano())
   281  	checkCRICPUAndMemoryStats(assert, c1, infos[container1.ContainerStatus.Id].Stats[0])
   282  	assert.Nil(c0.Accelerators)
   283  	checkCRIRootfsStats(assert, c1, containerStats1, nil)
   284  	checkCRILogsStats(assert, c1, &rootFsInfo, containerLogStats1)
   285  	checkCRINetworkStats(assert, p0.Network, infos[sandbox0.PodSandboxStatus.Id].Stats[0].Network)
   286  	checkCRIPodCPUAndMemoryStats(assert, p0, infos[sandbox0Cgroup].Stats[0])
   287  	checkCRIPodSwapStats(assert, p0, infos[sandbox0Cgroup].Stats[0])
   288  
   289  	p1 := podStatsMap[statsapi.PodReference{Name: "sandbox1-name", UID: "sandbox1-uid", Namespace: "sandbox1-ns"}]
   290  	assert.Equal(sandbox1.CreatedAt, p1.StartTime.UnixNano())
   291  	assert.Equal(1, len(p1.Containers))
   292  
   293  	checkEphemeralStorageStats(assert, p1, ephemeralVolumes, []*runtimeapi.ContainerStats{containerStats2},
   294  		[]*volume.Metrics{containerLogStats2}, podLogStats1)
   295  	c2 := p1.Containers[0]
   296  	assert.Equal(cName2, c2.Name)
   297  	assert.Equal(container2.CreatedAt, c2.StartTime.UnixNano())
   298  	checkCRICPUAndMemoryStats(assert, c2, infos[container2.ContainerStatus.Id].Stats[0])
   299  	assert.Nil(c0.Accelerators)
   300  	checkCRIRootfsStats(assert, c2, containerStats2, &imageFsInfo)
   301  	checkCRILogsStats(assert, c2, &rootFsInfo, containerLogStats2)
   302  	checkCRINetworkStats(assert, p1.Network, infos[sandbox1.PodSandboxStatus.Id].Stats[0].Network)
   303  	checkCRIPodCPUAndMemoryStats(assert, p1, infos[sandbox1Cgroup].Stats[0])
   304  	checkCRIPodSwapStats(assert, p1, infos[sandbox1Cgroup].Stats[0])
   305  
   306  	p2 := podStatsMap[statsapi.PodReference{Name: "sandbox2-name", UID: "sandbox2-uid", Namespace: "sandbox2-ns"}]
   307  	assert.Equal(sandbox2.CreatedAt, p2.StartTime.UnixNano())
   308  	assert.Equal(1, len(p2.Containers))
   309  
   310  	checkEphemeralStorageStats(assert, p2, ephemeralVolumes, []*runtimeapi.ContainerStats{containerStats4},
   311  		[]*volume.Metrics{containerLogStats4}, nil)
   312  
   313  	c3 := p2.Containers[0]
   314  	assert.Equal(cName3, c3.Name)
   315  	assert.Equal(container4.CreatedAt, c3.StartTime.UnixNano())
   316  	checkCRICPUAndMemoryStats(assert, c3, infos[container4.ContainerStatus.Id].Stats[0])
   317  	assert.Nil(c0.Accelerators)
   318  	checkCRIRootfsStats(assert, c3, containerStats4, &imageFsInfo)
   319  
   320  	checkCRILogsStats(assert, c3, &rootFsInfo, containerLogStats4)
   321  	checkCRINetworkStats(assert, p2.Network, infos[sandbox2.PodSandboxStatus.Id].Stats[0].Network)
   322  	checkCRIPodCPUAndMemoryStats(assert, p2, infos[sandbox2Cgroup].Stats[0])
   323  	checkCRIPodSwapStats(assert, p2, infos[sandbox2Cgroup].Stats[0])
   324  
   325  	p3 := podStatsMap[statsapi.PodReference{Name: "sandbox3-name", UID: "sandbox3-uid", Namespace: "sandbox3-ns"}]
   326  	assert.Equal(sandbox3.CreatedAt, p3.StartTime.UnixNano())
   327  	assert.Equal(1, len(p3.Containers))
   328  
   329  	c8 := p3.Containers[0]
   330  	assert.Equal(cName8, c8.Name)
   331  	assert.Equal(container8.CreatedAt, c8.StartTime.UnixNano())
   332  	assert.NotNil(c8.CPU.Time)
   333  	assert.NotNil(c8.Memory.Time)
   334  	checkCRIPodCPUAndMemoryStats(assert, p3, infos[sandbox3Cgroup].Stats[0])
   335  	checkCRIPodSwapStats(assert, p3, infos[sandbox3Cgroup].Stats[0])
   336  }
   337  
   338  func TestListPodStatsStrictlyFromCRI(t *testing.T) {
   339  	if runtime.GOOS == "windows" {
   340  		// TODO: remove skip once the failing test has been fixed.
   341  		t.Skip("Skip failing test on Windows.")
   342  	}
   343  	ctx := context.Background()
   344  	var (
   345  		imageFsMountpoint = "/test/mount/point"
   346  		unknownMountpoint = "/unknown/mount/point"
   347  		imageFsInfo       = getTestFsInfo(2000)
   348  		rootFsInfo        = getTestFsInfo(1000)
   349  
   350  		// A pod that CRI returns stats and cadvisor returns stats
   351  		// The pod stats from CRI stats
   352  		sandbox0           = makeFakePodSandbox("sandbox0-name", "sandbox0-uid", "sandbox0-ns", false)
   353  		sandbox0Cgroup     = "/" + cm.GetPodCgroupNameSuffix(types.UID(sandbox0.PodSandboxStatus.Metadata.Uid))
   354  		container0         = makeFakeContainer(sandbox0, cName0, 0, false)
   355  		containerStats0    = makeFakeContainerStatsStrictlyFromCRI(seedContainer0, container0, imageFsMountpoint)
   356  		containerLogStats0 = makeFakeLogStats(1000)
   357  		container1         = makeFakeContainer(sandbox0, cName1, 0, false)
   358  		containerStats1    = makeFakeContainerStatsStrictlyFromCRI(seedContainer1, container1, unknownMountpoint)
   359  		containerLogStats1 = makeFakeLogStats(2000)
   360  		sandboxPodStats0   = makeFakePodSandboxStatsStrictlyFromCRI(seedSandbox0, sandbox0, containerStats0, containerStats1)
   361  
   362  		// A pod that CRI returns stats and cadvisor returns no stats
   363  		// The pod stats from CRI stats
   364  		sandbox1           = makeFakePodSandbox("sandbox1-name", "sandbox1-uid", "sandbox1-ns", false)
   365  		sandbox1Cgroup     = "/" + cm.GetPodCgroupNameSuffix(types.UID(sandbox1.PodSandboxStatus.Metadata.Uid))
   366  		container2         = makeFakeContainer(sandbox1, cName2, 0, false)
   367  		containerStats2    = makeFakeContainerStatsStrictlyFromCRI(seedContainer2, container2, imageFsMountpoint)
   368  		containerLogStats2 = makeFakeLogStats(3000)
   369  		sandboxPodStats1   = makeFakePodSandboxStatsStrictlyFromCRI(seedSandbox1, sandbox1, containerStats2)
   370  
   371  		podLogName0  = "pod-log-0"
   372  		podLogName1  = "pod-log-1"
   373  		podLogStats0 = makeFakeLogStats(5000)
   374  		podLogStats1 = makeFakeLogStats(6000)
   375  	)
   376  	mockCtrl := gomock.NewController(t)
   377  	defer mockCtrl.Finish()
   378  	var (
   379  		mockCadvisor       = cadvisortest.NewMockInterface(mockCtrl)
   380  		mockRuntimeCache   = new(kubecontainertest.MockRuntimeCache)
   381  		mockPodManager     = new(kubepodtest.MockManager)
   382  		resourceAnalyzer   = new(fakeResourceAnalyzer)
   383  		fakeRuntimeService = critest.NewFakeRuntimeService()
   384  		fakeImageService   = critest.NewFakeImageService()
   385  	)
   386  	infos := map[string]cadvisorapiv2.ContainerInfo{
   387  		"/":                           getTestContainerInfo(seedRoot, "", "", ""),
   388  		"/kubelet":                    getTestContainerInfo(seedKubelet, "", "", ""),
   389  		"/system":                     getTestContainerInfo(seedMisc, "", "", ""),
   390  		sandbox0.PodSandboxStatus.Id:  getTestContainerInfo(seedSandbox0, pName0, sandbox0.PodSandboxStatus.Metadata.Namespace, kubelettypes.PodInfraContainerName),
   391  		sandbox0Cgroup:                getTestContainerInfo(seedSandbox0, "", "", ""),
   392  		container0.ContainerStatus.Id: getTestContainerInfo(seedContainer0, pName0, sandbox0.PodSandboxStatus.Metadata.Namespace, cName0),
   393  		container1.ContainerStatus.Id: getTestContainerInfo(seedContainer1, pName0, sandbox0.PodSandboxStatus.Metadata.Namespace, cName1),
   394  	}
   395  
   396  	exceptedContainerStatsMap := map[string]statsapi.ContainerStats{
   397  		cName0: getCRIContainerStatsStrictlyFromCRI(seedContainer0, cName0),
   398  		cName1: getCRIContainerStatsStrictlyFromCRI(seedContainer1, cName1),
   399  		cName2: getCRIContainerStatsStrictlyFromCRI(seedContainer2, cName2),
   400  	}
   401  
   402  	prf0 := statsapi.PodReference{Name: "sandbox0-name", UID: "sandbox0-uid", Namespace: "sandbox0-ns"}
   403  	prf1 := statsapi.PodReference{Name: "sandbox1-name", UID: "sandbox1-uid", Namespace: "sandbox1-ns"}
   404  
   405  	exceptedPodStatsMap := map[statsapi.PodReference]statsapi.PodStats{
   406  		prf0: getPodSandboxStatsStrictlyFromCRI(seedSandbox0, sandbox0),
   407  		prf1: getPodSandboxStatsStrictlyFromCRI(seedSandbox1, sandbox1),
   408  	}
   409  
   410  	options := cadvisorapiv2.RequestOptions{
   411  		IdType:    cadvisorapiv2.TypeName,
   412  		Count:     2,
   413  		Recursive: true,
   414  	}
   415  	mockCadvisor.EXPECT().ContainerInfoV2("/", options).Return(infos, nil)
   416  	mockCadvisor.EXPECT().RootFsInfo().Return(rootFsInfo, nil)
   417  	mockCadvisor.EXPECT().GetDirFsInfo(imageFsMountpoint).Return(imageFsInfo, nil)
   418  	mockCadvisor.EXPECT().GetDirFsInfo(unknownMountpoint).Return(cadvisorapiv2.FsInfo{}, cadvisorfs.ErrNoSuchDevice)
   419  	fakeRuntimeService.SetFakeSandboxes([]*critest.FakePodSandbox{
   420  		sandbox0, sandbox1,
   421  	})
   422  	fakeRuntimeService.SetFakeContainers([]*critest.FakeContainer{
   423  		container0, container1, container2,
   424  	})
   425  	fakeRuntimeService.SetFakeContainerStats([]*runtimeapi.ContainerStats{
   426  		containerStats0, containerStats1, containerStats2,
   427  	})
   428  
   429  	fakeRuntimeService.SetFakePodSandboxStats([]*runtimeapi.PodSandboxStats{
   430  		sandboxPodStats0, sandboxPodStats1,
   431  	})
   432  
   433  	ephemeralVolumes := makeFakeVolumeStats([]string{"ephVolume1, ephVolumes2"})
   434  	persistentVolumes := makeFakeVolumeStats([]string{"persisVolume1, persisVolumes2"})
   435  	resourceAnalyzer.podVolumeStats = serverstats.PodVolumeStats{
   436  		EphemeralVolumes:  ephemeralVolumes,
   437  		PersistentVolumes: persistentVolumes,
   438  	}
   439  	fakeStats := map[string]*volume.Metrics{
   440  		kuberuntime.BuildContainerLogsDirectory(testPodLogDirectory, "sandbox0-ns", "sandbox0-name", types.UID("sandbox0-uid"), cName0):               containerLogStats0,
   441  		kuberuntime.BuildContainerLogsDirectory(testPodLogDirectory, "sandbox0-ns", "sandbox0-name", types.UID("sandbox0-uid"), cName1):               containerLogStats1,
   442  		kuberuntime.BuildContainerLogsDirectory(testPodLogDirectory, "sandbox1-ns", "sandbox1-name", types.UID("sandbox1-uid"), cName2):               containerLogStats2,
   443  		filepath.Join(kuberuntime.BuildPodLogsDirectory(testPodLogDirectory, "sandbox0-ns", "sandbox0-name", types.UID("sandbox0-uid")), podLogName0): podLogStats0,
   444  		filepath.Join(kuberuntime.BuildPodLogsDirectory(testPodLogDirectory, "sandbox1-ns", "sandbox1-name", types.UID("sandbox1-uid")), podLogName1): podLogStats1,
   445  	}
   446  	ctrl := gomock.NewController(t)
   447  	defer ctrl.Finish()
   448  	fakeOS := &kubecontainertest.FakeOS{}
   449  	fakeOS.ReadDirFn = func(path string) ([]os.DirEntry, error) {
   450  		var dirEntries []os.DirEntry
   451  		mockDE := kubecontainertest.NewMockDirEntry(ctrl)
   452  		switch path {
   453  		case kuberuntime.BuildPodLogsDirectory(testPodLogDirectory, "sandbox0-ns", "sandbox0-name", types.UID("sandbox0-uid")):
   454  			mockDE.EXPECT().Name().Return(podLogName0)
   455  		case kuberuntime.BuildPodLogsDirectory(testPodLogDirectory, "sandbox1-ns", "sandbox1-name", types.UID("sandbox1-uid")):
   456  			mockDE.EXPECT().Name().Return(podLogName1)
   457  		default:
   458  			return nil, nil
   459  		}
   460  		mockDE.EXPECT().IsDir().Return(false)
   461  		dirEntries = append(dirEntries, mockDE)
   462  		return dirEntries, nil
   463  	}
   464  	provider := NewCRIStatsProvider(
   465  		mockCadvisor,
   466  		resourceAnalyzer,
   467  		mockPodManager,
   468  		mockRuntimeCache,
   469  		fakeRuntimeService,
   470  		fakeImageService,
   471  		NewFakeHostStatsProviderWithData(fakeStats, fakeOS),
   472  		true,
   473  	)
   474  
   475  	cadvisorInfos, err := getCadvisorContainerInfo(mockCadvisor)
   476  	if err != nil {
   477  		t.Errorf("failed to get container info from cadvisor: %v", err)
   478  	}
   479  	stats, err := provider.ListPodStats(ctx)
   480  	assert := assert.New(t)
   481  	assert.NoError(err)
   482  	assert.Equal(2, len(stats))
   483  	podStatsMap := make(map[statsapi.PodReference]statsapi.PodStats)
   484  	for _, s := range stats {
   485  		podStatsMap[s.PodRef] = s
   486  	}
   487  	p0 := podStatsMap[prf0]
   488  	assert.Equal(sandbox0.CreatedAt, p0.StartTime.UnixNano())
   489  	assert.Equal(2, len(p0.Containers))
   490  
   491  	checkEphemeralStorageStats(assert, p0, ephemeralVolumes, []*runtimeapi.ContainerStats{containerStats0, containerStats1},
   492  		[]*volume.Metrics{containerLogStats0, containerLogStats1}, podLogStats0)
   493  
   494  	containerStatsMap := make(map[string]statsapi.ContainerStats)
   495  	for _, s := range p0.Containers {
   496  		containerStatsMap[s.Name] = s
   497  	}
   498  
   499  	c0 := containerStatsMap[cName0]
   500  	assert.Equal(container0.CreatedAt, c0.StartTime.UnixNano())
   501  	checkCRICPUAndMemoryStatsForStrictlyFromCRI(assert, c0, exceptedContainerStatsMap[cName0])
   502  	assert.Nil(c0.Accelerators)
   503  	checkCRIRootfsStats(assert, c0, containerStats0, &imageFsInfo)
   504  	checkCRILogsStats(assert, c0, &rootFsInfo, containerLogStats0)
   505  
   506  	c1 := containerStatsMap[cName1]
   507  	assert.Equal(container1.CreatedAt, c1.StartTime.UnixNano())
   508  	checkCRICPUAndMemoryStatsForStrictlyFromCRI(assert, c1, exceptedContainerStatsMap[cName1])
   509  	assert.Nil(c0.Accelerators)
   510  	checkCRIRootfsStats(assert, c1, containerStats1, nil)
   511  	checkCRILogsStats(assert, c1, &rootFsInfo, containerLogStats1)
   512  	checkCRIPodCPUAndMemoryStatsStrictlyFromCRI(assert, p0, exceptedPodStatsMap[prf0])
   513  	assert.NotNil(cadvisorInfos[sandbox0Cgroup].Stats[0].Cpu)
   514  	assert.NotNil(cadvisorInfos[sandbox0Cgroup].Stats[0].Memory)
   515  
   516  	p1 := podStatsMap[prf1]
   517  	assert.Equal(sandbox1.CreatedAt, p1.StartTime.UnixNano())
   518  	assert.Equal(1, len(p1.Containers))
   519  
   520  	checkEphemeralStorageStats(assert, p1, ephemeralVolumes, []*runtimeapi.ContainerStats{containerStats2},
   521  		[]*volume.Metrics{containerLogStats2}, podLogStats1)
   522  	c2 := p1.Containers[0]
   523  	assert.Equal(cName2, c2.Name)
   524  	assert.Equal(container2.CreatedAt, c2.StartTime.UnixNano())
   525  	checkCRICPUAndMemoryStatsForStrictlyFromCRI(assert, c2, exceptedContainerStatsMap[cName2])
   526  	assert.Nil(c0.Accelerators)
   527  	checkCRIRootfsStats(assert, c2, containerStats2, &imageFsInfo)
   528  	checkCRILogsStats(assert, c2, &rootFsInfo, containerLogStats2)
   529  	checkCRIPodCPUAndMemoryStatsStrictlyFromCRI(assert, p1, exceptedPodStatsMap[prf1])
   530  
   531  	if runtime.GOOS == "linux" {
   532  		if _, ok := cadvisorInfos[sandbox1Cgroup]; ok {
   533  			t.Errorf("expect no cadvisor stats for pod %v", prf1)
   534  		}
   535  	}
   536  }
   537  func TestCRIListPodCPUAndMemoryStats(t *testing.T) {
   538  	ctx := context.Background()
   539  
   540  	var (
   541  		imageFsMountpoint = "/test/mount/point"
   542  		unknownMountpoint = "/unknown/mount/point"
   543  
   544  		sandbox0        = makeFakePodSandbox("sandbox0-name", "sandbox0-uid", "sandbox0-ns", false)
   545  		sandbox0Cgroup  = "/" + cm.GetPodCgroupNameSuffix(types.UID(sandbox0.PodSandboxStatus.Metadata.Uid))
   546  		container0      = makeFakeContainer(sandbox0, cName0, 0, false)
   547  		containerStats0 = makeFakeContainerStats(container0, imageFsMountpoint)
   548  		container1      = makeFakeContainer(sandbox0, cName1, 0, false)
   549  		containerStats1 = makeFakeContainerStats(container1, unknownMountpoint)
   550  
   551  		sandbox1        = makeFakePodSandbox("sandbox1-name", "sandbox1-uid", "sandbox1-ns", false)
   552  		sandbox1Cgroup  = "/" + cm.GetPodCgroupNameSuffix(types.UID(sandbox1.PodSandboxStatus.Metadata.Uid))
   553  		container2      = makeFakeContainer(sandbox1, cName2, 0, false)
   554  		containerStats2 = makeFakeContainerStats(container2, imageFsMountpoint)
   555  
   556  		sandbox2        = makeFakePodSandbox("sandbox2-name", "sandbox2-uid", "sandbox2-ns", false)
   557  		sandbox2Cgroup  = "/" + cm.GetPodCgroupNameSuffix(types.UID(sandbox2.PodSandboxStatus.Metadata.Uid))
   558  		container3      = makeFakeContainer(sandbox2, cName3, 0, true)
   559  		containerStats3 = makeFakeContainerStats(container3, imageFsMountpoint)
   560  		container4      = makeFakeContainer(sandbox2, cName3, 1, false)
   561  		containerStats4 = makeFakeContainerStats(container4, imageFsMountpoint)
   562  
   563  		// Running pod with a terminated container and a running container
   564  		sandbox3        = makeFakePodSandbox("sandbox3-name", "sandbox3-uid", "sandbox3-ns", false)
   565  		sandbox3Cgroup  = "/" + cm.GetPodCgroupNameSuffix(types.UID(sandbox3.PodSandboxStatus.Metadata.Uid))
   566  		container5      = makeFakeContainer(sandbox3, cName5, 0, true)
   567  		containerStats5 = makeFakeContainerStats(container5, imageFsMountpoint)
   568  		container8      = makeFakeContainer(sandbox3, cName8, 0, false)
   569  		containerStats8 = makeFakeContainerStats(container8, imageFsMountpoint)
   570  
   571  		// Terminated pod sandbox
   572  		sandbox4        = makeFakePodSandbox("sandbox1-name", "sandbox1-uid", "sandbox1-ns", true)
   573  		container6      = makeFakeContainer(sandbox4, cName6, 0, true)
   574  		containerStats6 = makeFakeContainerStats(container6, imageFsMountpoint)
   575  
   576  		// Terminated pod
   577  		sandbox5        = makeFakePodSandbox("sandbox1-name", "sandbox5-uid", "sandbox1-ns", true)
   578  		container7      = makeFakeContainer(sandbox5, cName7, 0, true)
   579  		containerStats7 = makeFakeContainerStats(container7, imageFsMountpoint)
   580  
   581  		// A pod that cadvisor returns no stats
   582  		sandbox6        = makeFakePodSandbox("sandbox6-name", "sandbox6-uid", "sandbox6-ns", false)
   583  		container9      = makeFakeContainer(sandbox6, cName9, 0, false)
   584  		containerStats9 = makeFakeContainerStats(container9, imageFsMountpoint)
   585  	)
   586  
   587  	mockCtrl := gomock.NewController(t)
   588  	defer mockCtrl.Finish()
   589  
   590  	var (
   591  		mockCadvisor       = cadvisortest.NewMockInterface(mockCtrl)
   592  		mockRuntimeCache   = new(kubecontainertest.MockRuntimeCache)
   593  		mockPodManager     = new(kubepodtest.MockManager)
   594  		resourceAnalyzer   = new(fakeResourceAnalyzer)
   595  		fakeRuntimeService = critest.NewFakeRuntimeService()
   596  	)
   597  
   598  	infos := map[string]cadvisorapiv2.ContainerInfo{
   599  		"/":                           getTestContainerInfo(seedRoot, "", "", ""),
   600  		"/kubelet":                    getTestContainerInfo(seedKubelet, "", "", ""),
   601  		"/system":                     getTestContainerInfo(seedMisc, "", "", ""),
   602  		sandbox0.PodSandboxStatus.Id:  getTestContainerInfo(seedSandbox0, pName0, sandbox0.PodSandboxStatus.Metadata.Namespace, kubelettypes.PodInfraContainerName),
   603  		sandbox0Cgroup:                getTestContainerInfo(seedSandbox0, "", "", ""),
   604  		container0.ContainerStatus.Id: getTestContainerInfo(seedContainer0, pName0, sandbox0.PodSandboxStatus.Metadata.Namespace, cName0),
   605  		container1.ContainerStatus.Id: getTestContainerInfo(seedContainer1, pName0, sandbox0.PodSandboxStatus.Metadata.Namespace, cName1),
   606  		sandbox1.PodSandboxStatus.Id:  getTestContainerInfo(seedSandbox1, pName1, sandbox1.PodSandboxStatus.Metadata.Namespace, kubelettypes.PodInfraContainerName),
   607  		sandbox1Cgroup:                getTestContainerInfo(seedSandbox1, "", "", ""),
   608  		container2.ContainerStatus.Id: getTestContainerInfo(seedContainer2, pName1, sandbox1.PodSandboxStatus.Metadata.Namespace, cName2),
   609  		sandbox2.PodSandboxStatus.Id:  getTestContainerInfo(seedSandbox2, pName2, sandbox2.PodSandboxStatus.Metadata.Namespace, kubelettypes.PodInfraContainerName),
   610  		sandbox2Cgroup:                getTestContainerInfo(seedSandbox2, "", "", ""),
   611  		container4.ContainerStatus.Id: getTestContainerInfo(seedContainer3, pName2, sandbox2.PodSandboxStatus.Metadata.Namespace, cName3),
   612  		sandbox3Cgroup:                getTestContainerInfo(seedSandbox3, "", "", ""),
   613  	}
   614  
   615  	options := cadvisorapiv2.RequestOptions{
   616  		IdType:    cadvisorapiv2.TypeName,
   617  		Count:     2,
   618  		Recursive: true,
   619  	}
   620  
   621  	mockCadvisor.EXPECT().ContainerInfoV2("/", options).Return(infos, nil)
   622  
   623  	fakeRuntimeService.SetFakeSandboxes([]*critest.FakePodSandbox{
   624  		sandbox0, sandbox1, sandbox2, sandbox3, sandbox4, sandbox5, sandbox6,
   625  	})
   626  	fakeRuntimeService.SetFakeContainers([]*critest.FakeContainer{
   627  		container0, container1, container2, container3, container4, container5, container6, container7, container8, container9,
   628  	})
   629  	fakeRuntimeService.SetFakeContainerStats([]*runtimeapi.ContainerStats{
   630  		containerStats0, containerStats1, containerStats2, containerStats3, containerStats4, containerStats5, containerStats6, containerStats7, containerStats8, containerStats9,
   631  	})
   632  
   633  	ephemeralVolumes := makeFakeVolumeStats([]string{"ephVolume1, ephVolumes2"})
   634  	persistentVolumes := makeFakeVolumeStats([]string{"persisVolume1, persisVolumes2"})
   635  	resourceAnalyzer.podVolumeStats = serverstats.PodVolumeStats{
   636  		EphemeralVolumes:  ephemeralVolumes,
   637  		PersistentVolumes: persistentVolumes,
   638  	}
   639  
   640  	provider := NewCRIStatsProvider(
   641  		mockCadvisor,
   642  		resourceAnalyzer,
   643  		mockPodManager,
   644  		mockRuntimeCache,
   645  		fakeRuntimeService,
   646  		nil,
   647  		NewFakeHostStatsProvider(),
   648  		false,
   649  	)
   650  
   651  	stats, err := provider.ListPodCPUAndMemoryStats(ctx)
   652  	assert := assert.New(t)
   653  	assert.NoError(err)
   654  	assert.Equal(5, len(stats))
   655  
   656  	podStatsMap := make(map[statsapi.PodReference]statsapi.PodStats)
   657  	for _, s := range stats {
   658  		podStatsMap[s.PodRef] = s
   659  	}
   660  
   661  	p0 := podStatsMap[statsapi.PodReference{Name: "sandbox0-name", UID: "sandbox0-uid", Namespace: "sandbox0-ns"}]
   662  	assert.Equal(sandbox0.CreatedAt, p0.StartTime.UnixNano())
   663  	assert.Equal(2, len(p0.Containers))
   664  	assert.Nil(p0.EphemeralStorage)
   665  	assert.Nil(p0.VolumeStats)
   666  	assert.Nil(p0.Network)
   667  	checkCRIPodCPUAndMemoryStats(assert, p0, infos[sandbox0Cgroup].Stats[0])
   668  
   669  	containerStatsMap := make(map[string]statsapi.ContainerStats)
   670  	for _, s := range p0.Containers {
   671  		containerStatsMap[s.Name] = s
   672  	}
   673  
   674  	c0 := containerStatsMap[cName0]
   675  	assert.Equal(container0.CreatedAt, c0.StartTime.UnixNano())
   676  	checkCRICPUAndMemoryStats(assert, c0, infos[container0.ContainerStatus.Id].Stats[0])
   677  	assert.Nil(c0.Rootfs)
   678  	assert.Nil(c0.Logs)
   679  	assert.Nil(c0.Accelerators)
   680  	assert.Nil(c0.UserDefinedMetrics)
   681  	c1 := containerStatsMap[cName1]
   682  	assert.Equal(container1.CreatedAt, c1.StartTime.UnixNano())
   683  	checkCRICPUAndMemoryStats(assert, c1, infos[container1.ContainerStatus.Id].Stats[0])
   684  	assert.Nil(c1.Rootfs)
   685  	assert.Nil(c1.Logs)
   686  	assert.Nil(c1.Accelerators)
   687  	assert.Nil(c1.UserDefinedMetrics)
   688  
   689  	p1 := podStatsMap[statsapi.PodReference{Name: "sandbox1-name", UID: "sandbox1-uid", Namespace: "sandbox1-ns"}]
   690  	assert.Equal(sandbox1.CreatedAt, p1.StartTime.UnixNano())
   691  	assert.Equal(1, len(p1.Containers))
   692  	assert.Nil(p1.EphemeralStorage)
   693  	assert.Nil(p1.VolumeStats)
   694  	assert.Nil(p1.Network)
   695  	checkCRIPodCPUAndMemoryStats(assert, p1, infos[sandbox1Cgroup].Stats[0])
   696  
   697  	c2 := p1.Containers[0]
   698  	assert.Equal(cName2, c2.Name)
   699  	assert.Equal(container2.CreatedAt, c2.StartTime.UnixNano())
   700  	checkCRICPUAndMemoryStats(assert, c2, infos[container2.ContainerStatus.Id].Stats[0])
   701  	assert.Nil(c2.Rootfs)
   702  	assert.Nil(c2.Logs)
   703  	assert.Nil(c2.Accelerators)
   704  	assert.Nil(c2.UserDefinedMetrics)
   705  
   706  	p2 := podStatsMap[statsapi.PodReference{Name: "sandbox2-name", UID: "sandbox2-uid", Namespace: "sandbox2-ns"}]
   707  	assert.Equal(sandbox2.CreatedAt, p2.StartTime.UnixNano())
   708  	assert.Equal(1, len(p2.Containers))
   709  	assert.Nil(p2.EphemeralStorage)
   710  	assert.Nil(p2.VolumeStats)
   711  	assert.Nil(p2.Network)
   712  	checkCRIPodCPUAndMemoryStats(assert, p2, infos[sandbox2Cgroup].Stats[0])
   713  
   714  	c3 := p2.Containers[0]
   715  	assert.Equal(cName3, c3.Name)
   716  	assert.Equal(container4.CreatedAt, c3.StartTime.UnixNano())
   717  	checkCRICPUAndMemoryStats(assert, c3, infos[container4.ContainerStatus.Id].Stats[0])
   718  	assert.Nil(c2.Rootfs)
   719  	assert.Nil(c2.Logs)
   720  	assert.Nil(c2.Accelerators)
   721  	assert.Nil(c2.UserDefinedMetrics)
   722  
   723  	p3 := podStatsMap[statsapi.PodReference{Name: "sandbox3-name", UID: "sandbox3-uid", Namespace: "sandbox3-ns"}]
   724  	assert.Equal(sandbox3.CreatedAt, p3.StartTime.UnixNano())
   725  	assert.Equal(1, len(p3.Containers))
   726  
   727  	c8 := p3.Containers[0]
   728  	assert.Equal(cName8, c8.Name)
   729  	assert.Equal(container8.CreatedAt, c8.StartTime.UnixNano())
   730  	assert.NotNil(c8.CPU.Time)
   731  	assert.NotNil(c8.Memory.Time)
   732  	checkCRIPodCPUAndMemoryStats(assert, p3, infos[sandbox3Cgroup].Stats[0])
   733  
   734  	p6 := podStatsMap[statsapi.PodReference{Name: "sandbox6-name", UID: "sandbox6-uid", Namespace: "sandbox6-ns"}]
   735  	assert.Equal(sandbox6.CreatedAt, p6.StartTime.UnixNano())
   736  	assert.Equal(1, len(p6.Containers))
   737  
   738  	c9 := p6.Containers[0]
   739  	assert.Equal(cName9, c9.Name)
   740  	assert.Equal(container9.CreatedAt, c9.StartTime.UnixNano())
   741  	assert.NotNil(c9.CPU.Time)
   742  	assert.Equal(containerStats9.Cpu.Timestamp, p6.CPU.Time.UnixNano())
   743  	assert.NotNil(c9.Memory.Time)
   744  	assert.Equal(containerStats9.Memory.Timestamp, p6.Memory.Time.UnixNano())
   745  }
   746  
   747  func TestCRIImagesFsStats(t *testing.T) {
   748  	ctx := context.Background()
   749  	var (
   750  		imageFsMountpoint = "/test/mount/point"
   751  		imageFsInfo       = getTestFsInfo(2000)
   752  		imageFsUsage      = makeFakeImageFsUsage(imageFsMountpoint)
   753  	)
   754  
   755  	mockCtrl := gomock.NewController(t)
   756  	defer mockCtrl.Finish()
   757  
   758  	var (
   759  		mockCadvisor       = cadvisortest.NewMockInterface(mockCtrl)
   760  		mockRuntimeCache   = new(kubecontainertest.MockRuntimeCache)
   761  		mockPodManager     = new(kubepodtest.MockManager)
   762  		resourceAnalyzer   = new(fakeResourceAnalyzer)
   763  		fakeRuntimeService = critest.NewFakeRuntimeService()
   764  		fakeImageService   = critest.NewFakeImageService()
   765  	)
   766  	mockCadvisor.EXPECT().GetDirFsInfo(imageFsMountpoint).Return(imageFsInfo, nil)
   767  	fakeImageService.SetFakeFilesystemUsage([]*runtimeapi.FilesystemUsage{
   768  		imageFsUsage,
   769  	})
   770  
   771  	provider := NewCRIStatsProvider(
   772  		mockCadvisor,
   773  		resourceAnalyzer,
   774  		mockPodManager,
   775  		mockRuntimeCache,
   776  		fakeRuntimeService,
   777  		fakeImageService,
   778  		NewFakeHostStatsProvider(),
   779  		false,
   780  	)
   781  
   782  	stats, containerStats, err := provider.ImageFsStats(ctx)
   783  	assert := assert.New(t)
   784  	assert.NoError(err)
   785  
   786  	assert.Equal(imageFsUsage.Timestamp, stats.Time.UnixNano())
   787  	assert.Equal(imageFsInfo.Available, *stats.AvailableBytes)
   788  	assert.Equal(imageFsInfo.Capacity, *stats.CapacityBytes)
   789  	assert.Equal(imageFsInfo.InodesFree, stats.InodesFree)
   790  	assert.Equal(imageFsInfo.Inodes, stats.Inodes)
   791  	assert.Equal(imageFsUsage.UsedBytes.Value, *stats.UsedBytes)
   792  	assert.Equal(imageFsUsage.InodesUsed.Value, *stats.InodesUsed)
   793  
   794  	assert.Equal(imageFsUsage.Timestamp, containerStats.Time.UnixNano())
   795  	assert.Equal(imageFsInfo.Available, *containerStats.AvailableBytes)
   796  	assert.Equal(imageFsInfo.Capacity, *containerStats.CapacityBytes)
   797  	assert.Equal(imageFsInfo.InodesFree, containerStats.InodesFree)
   798  	assert.Equal(imageFsInfo.Inodes, containerStats.Inodes)
   799  	assert.Equal(imageFsUsage.UsedBytes.Value, *containerStats.UsedBytes)
   800  	assert.Equal(imageFsUsage.InodesUsed.Value, *containerStats.InodesUsed)
   801  
   802  }
   803  
   804  func makeFakePodSandbox(name, uid, namespace string, terminated bool) *critest.FakePodSandbox {
   805  	p := &critest.FakePodSandbox{
   806  		PodSandboxStatus: runtimeapi.PodSandboxStatus{
   807  			Metadata: &runtimeapi.PodSandboxMetadata{
   808  				Name:      name,
   809  				Uid:       uid,
   810  				Namespace: namespace,
   811  			},
   812  			State:     runtimeapi.PodSandboxState_SANDBOX_READY,
   813  			CreatedAt: time.Now().UnixNano(),
   814  		},
   815  	}
   816  	if terminated {
   817  		p.PodSandboxStatus.State = runtimeapi.PodSandboxState_SANDBOX_NOTREADY
   818  	}
   819  	p.PodSandboxStatus.Id = strings.ReplaceAll(string(uuid.NewUUID()), "-", "")
   820  	return p
   821  }
   822  
   823  func makeFakeContainer(sandbox *critest.FakePodSandbox, name string, attempt uint32, terminated bool) *critest.FakeContainer {
   824  	sandboxID := sandbox.PodSandboxStatus.Id
   825  	c := &critest.FakeContainer{
   826  		SandboxID: sandboxID,
   827  		ContainerStatus: runtimeapi.ContainerStatus{
   828  			Metadata:  &runtimeapi.ContainerMetadata{Name: name, Attempt: attempt},
   829  			Image:     &runtimeapi.ImageSpec{},
   830  			ImageRef:  "fake-image-ref",
   831  			CreatedAt: time.Now().UnixNano(),
   832  		},
   833  	}
   834  	c.ContainerStatus.Labels = map[string]string{
   835  		"io.kubernetes.pod.name":       sandbox.Metadata.Name,
   836  		"io.kubernetes.pod.uid":        sandbox.Metadata.Uid,
   837  		"io.kubernetes.pod.namespace":  sandbox.Metadata.Namespace,
   838  		"io.kubernetes.container.name": name,
   839  	}
   840  	if terminated {
   841  		c.ContainerStatus.State = runtimeapi.ContainerState_CONTAINER_EXITED
   842  	} else {
   843  		c.ContainerStatus.State = runtimeapi.ContainerState_CONTAINER_RUNNING
   844  	}
   845  	c.ContainerStatus.Id = strings.ReplaceAll(string(uuid.NewUUID()), "-", "")
   846  	return c
   847  }
   848  
   849  func makeFakeContainerStats(container *critest.FakeContainer, imageFsMountpoint string) *runtimeapi.ContainerStats {
   850  	containerStats := &runtimeapi.ContainerStats{
   851  		Attributes: &runtimeapi.ContainerAttributes{
   852  			Id:       container.ContainerStatus.Id,
   853  			Metadata: container.ContainerStatus.Metadata,
   854  		},
   855  		WritableLayer: &runtimeapi.FilesystemUsage{
   856  			Timestamp:  time.Now().UnixNano(),
   857  			FsId:       &runtimeapi.FilesystemIdentifier{Mountpoint: imageFsMountpoint},
   858  			UsedBytes:  &runtimeapi.UInt64Value{Value: rand.Uint64() / 100},
   859  			InodesUsed: &runtimeapi.UInt64Value{Value: rand.Uint64() / 100},
   860  		},
   861  	}
   862  	if container.State == runtimeapi.ContainerState_CONTAINER_EXITED {
   863  		containerStats.Cpu = nil
   864  		containerStats.Memory = nil
   865  	} else {
   866  		containerStats.Cpu = &runtimeapi.CpuUsage{
   867  			Timestamp:            time.Now().UnixNano(),
   868  			UsageCoreNanoSeconds: &runtimeapi.UInt64Value{Value: rand.Uint64()},
   869  		}
   870  		containerStats.Memory = &runtimeapi.MemoryUsage{
   871  			Timestamp:       time.Now().UnixNano(),
   872  			WorkingSetBytes: &runtimeapi.UInt64Value{Value: rand.Uint64()},
   873  		}
   874  	}
   875  	return containerStats
   876  }
   877  
   878  // makeFakeContainerStatsStrictlyFromCRI use CRI offset to fake CRI container stats to distinguish cadvisor stats.
   879  func makeFakeContainerStatsStrictlyFromCRI(seed int, container *critest.FakeContainer, imageFsMountpoint string) *runtimeapi.ContainerStats {
   880  	containerStats := &runtimeapi.ContainerStats{
   881  		Attributes: &runtimeapi.ContainerAttributes{
   882  			Id:       container.ContainerStatus.Id,
   883  			Metadata: container.ContainerStatus.Metadata,
   884  		},
   885  		WritableLayer: &runtimeapi.FilesystemUsage{
   886  			Timestamp:  timestamp.UnixNano(),
   887  			FsId:       &runtimeapi.FilesystemIdentifier{Mountpoint: imageFsMountpoint},
   888  			UsedBytes:  &runtimeapi.UInt64Value{Value: uint64(seed + offsetCRI + offsetFsUsage)},
   889  			InodesUsed: &runtimeapi.UInt64Value{Value: uint64(seed + offsetCRI + offsetFsInodeUsage)},
   890  		},
   891  	}
   892  	if container.State == runtimeapi.ContainerState_CONTAINER_EXITED {
   893  		containerStats.Cpu = nil
   894  		containerStats.Memory = nil
   895  	} else {
   896  		containerStats.Cpu = &runtimeapi.CpuUsage{
   897  			Timestamp:            timestamp.UnixNano(),
   898  			UsageCoreNanoSeconds: &runtimeapi.UInt64Value{Value: uint64(seed + offsetCRI + offsetCPUUsageCoreSeconds)},
   899  		}
   900  		containerStats.Memory = &runtimeapi.MemoryUsage{
   901  			Timestamp:       timestamp.UnixNano(),
   902  			WorkingSetBytes: &runtimeapi.UInt64Value{Value: uint64(seed + offsetCRI + offsetMemWorkingSetBytes)},
   903  		}
   904  	}
   905  	return containerStats
   906  }
   907  
   908  func makeFakePodSandboxStatsStrictlyFromCRI(seed int, podSandbox *critest.FakePodSandbox, podContainerStats ...*runtimeapi.ContainerStats) *runtimeapi.PodSandboxStats {
   909  	podSandboxStats := &runtimeapi.PodSandboxStats{
   910  		Attributes: &runtimeapi.PodSandboxAttributes{
   911  			Id:       podSandbox.Id,
   912  			Metadata: podSandbox.Metadata,
   913  		},
   914  		Linux: &runtimeapi.LinuxPodSandboxStats{},
   915  	}
   916  	podSandboxStats.Linux.Containers = append(podSandboxStats.Linux.Containers, podContainerStats...)
   917  	if podSandbox.State == runtimeapi.PodSandboxState_SANDBOX_NOTREADY {
   918  		podSandboxStats.Linux.Cpu = nil
   919  		podSandboxStats.Linux.Memory = nil
   920  	} else {
   921  		podSandboxStats.Linux.Cpu = &runtimeapi.CpuUsage{
   922  			Timestamp:            timestamp.UnixNano(),
   923  			UsageCoreNanoSeconds: &runtimeapi.UInt64Value{Value: uint64(seed + offsetCRI + offsetCPUUsageCoreSeconds)},
   924  		}
   925  		podSandboxStats.Linux.Memory = &runtimeapi.MemoryUsage{
   926  			Timestamp:       timestamp.UnixNano(),
   927  			WorkingSetBytes: &runtimeapi.UInt64Value{Value: uint64(seed + offsetCRI + offsetMemWorkingSetBytes)},
   928  		}
   929  	}
   930  	return podSandboxStats
   931  }
   932  func getPodSandboxStatsStrictlyFromCRI(seed int, podSandbox *critest.FakePodSandbox) statsapi.PodStats {
   933  	podStats := statsapi.PodStats{
   934  		PodRef: statsapi.PodReference{
   935  			Name:      podSandbox.Metadata.Name,
   936  			UID:       podSandbox.Metadata.Uid,
   937  			Namespace: podSandbox.Metadata.Namespace,
   938  		},
   939  		// The StartTime in the summary API is the pod creation time.
   940  		StartTime: metav1.NewTime(time.Unix(0, podSandbox.CreatedAt)),
   941  	}
   942  	if podSandbox.State == runtimeapi.PodSandboxState_SANDBOX_NOTREADY {
   943  		podStats.CPU = nil
   944  		podStats.Memory = nil
   945  	} else {
   946  		usageCoreNanoSeconds := uint64(seed + offsetCRI + offsetCPUUsageCoreSeconds)
   947  		workingSetBytes := uint64(seed + offsetCRI + offsetMemWorkingSetBytes)
   948  		podStats.CPU = &statsapi.CPUStats{
   949  			Time:                 metav1.NewTime(timestamp),
   950  			UsageCoreNanoSeconds: &usageCoreNanoSeconds,
   951  		}
   952  		podStats.Memory = &statsapi.MemoryStats{
   953  			Time:            metav1.NewTime(timestamp),
   954  			WorkingSetBytes: &workingSetBytes,
   955  		}
   956  	}
   957  
   958  	return podStats
   959  }
   960  
   961  func makeFakeImageFsUsage(fsMountpoint string) *runtimeapi.FilesystemUsage {
   962  	return &runtimeapi.FilesystemUsage{
   963  		Timestamp:  time.Now().UnixNano(),
   964  		FsId:       &runtimeapi.FilesystemIdentifier{Mountpoint: fsMountpoint},
   965  		UsedBytes:  &runtimeapi.UInt64Value{Value: rand.Uint64()},
   966  		InodesUsed: &runtimeapi.UInt64Value{Value: rand.Uint64()},
   967  	}
   968  }
   969  
   970  func makeFakeVolumeStats(volumeNames []string) []statsapi.VolumeStats {
   971  	volumes := make([]statsapi.VolumeStats, len(volumeNames))
   972  	availableBytes := rand.Uint64()
   973  	capacityBytes := rand.Uint64()
   974  	usedBytes := rand.Uint64() / 100
   975  	inodes := rand.Uint64()
   976  	inodesFree := rand.Uint64()
   977  	inodesUsed := rand.Uint64() / 100
   978  	for i, name := range volumeNames {
   979  		fsStats := statsapi.FsStats{
   980  			Time:           metav1.NewTime(time.Now()),
   981  			AvailableBytes: &availableBytes,
   982  			CapacityBytes:  &capacityBytes,
   983  			UsedBytes:      &usedBytes,
   984  			Inodes:         &inodes,
   985  			InodesFree:     &inodesFree,
   986  			InodesUsed:     &inodesUsed,
   987  		}
   988  		volumes[i] = statsapi.VolumeStats{
   989  			FsStats: fsStats,
   990  			Name:    name,
   991  		}
   992  	}
   993  	return volumes
   994  }
   995  
   996  func checkCRICPUAndMemoryStats(assert *assert.Assertions, actual statsapi.ContainerStats, cs *cadvisorapiv2.ContainerStats) {
   997  	assert.Equal(cs.Timestamp.UnixNano(), actual.CPU.Time.UnixNano())
   998  	assert.Equal(cs.Cpu.Usage.Total, *actual.CPU.UsageCoreNanoSeconds)
   999  	assert.Equal(cs.CpuInst.Usage.Total, *actual.CPU.UsageNanoCores)
  1000  
  1001  	assert.Equal(cs.Memory.Usage, *actual.Memory.UsageBytes)
  1002  	assert.Equal(cs.Memory.WorkingSet, *actual.Memory.WorkingSetBytes)
  1003  	assert.Equal(cs.Memory.RSS, *actual.Memory.RSSBytes)
  1004  	assert.Equal(cs.Memory.ContainerData.Pgfault, *actual.Memory.PageFaults)
  1005  	assert.Equal(cs.Memory.ContainerData.Pgmajfault, *actual.Memory.MajorPageFaults)
  1006  }
  1007  
  1008  func checkCRICPUAndMemoryStatsForStrictlyFromCRI(assert *assert.Assertions, actual statsapi.ContainerStats, excepted statsapi.ContainerStats) {
  1009  	assert.Equal(excepted.CPU.Time.UnixNano(), actual.CPU.Time.UnixNano())
  1010  	assert.Equal(*excepted.CPU.UsageCoreNanoSeconds, *actual.CPU.UsageCoreNanoSeconds)
  1011  	assert.Equal(*excepted.Memory.WorkingSetBytes, *actual.Memory.WorkingSetBytes)
  1012  }
  1013  
  1014  func checkCRIRootfsStats(assert *assert.Assertions, actual statsapi.ContainerStats, cs *runtimeapi.ContainerStats, imageFsInfo *cadvisorapiv2.FsInfo) {
  1015  	assert.Equal(cs.WritableLayer.Timestamp, actual.Rootfs.Time.UnixNano())
  1016  	if imageFsInfo != nil {
  1017  		assert.Equal(imageFsInfo.Available, *actual.Rootfs.AvailableBytes)
  1018  		assert.Equal(imageFsInfo.Capacity, *actual.Rootfs.CapacityBytes)
  1019  		assert.Equal(*imageFsInfo.InodesFree, *actual.Rootfs.InodesFree)
  1020  		assert.Equal(*imageFsInfo.Inodes, *actual.Rootfs.Inodes)
  1021  	} else {
  1022  		assert.Nil(actual.Rootfs.AvailableBytes)
  1023  		assert.Nil(actual.Rootfs.CapacityBytes)
  1024  		assert.Nil(actual.Rootfs.InodesFree)
  1025  		assert.Nil(actual.Rootfs.Inodes)
  1026  	}
  1027  	assert.Equal(cs.WritableLayer.UsedBytes.Value, *actual.Rootfs.UsedBytes)
  1028  	assert.Equal(cs.WritableLayer.InodesUsed.Value, *actual.Rootfs.InodesUsed)
  1029  }
  1030  
  1031  func checkCRILogsStats(assert *assert.Assertions, actual statsapi.ContainerStats, rootFsInfo *cadvisorapiv2.FsInfo, logStats *volume.Metrics) {
  1032  	assert.Equal(rootFsInfo.Timestamp, actual.Logs.Time.Time)
  1033  	assert.Equal(rootFsInfo.Available, *actual.Logs.AvailableBytes)
  1034  	assert.Equal(rootFsInfo.Capacity, *actual.Logs.CapacityBytes)
  1035  	assert.Equal(*rootFsInfo.InodesFree, *actual.Logs.InodesFree)
  1036  	assert.Equal(*rootFsInfo.Inodes, *actual.Logs.Inodes)
  1037  	assert.Equal(uint64(logStats.Used.Value()), *actual.Logs.UsedBytes)
  1038  	assert.Equal(uint64(logStats.InodesUsed.Value()), *actual.Logs.InodesUsed)
  1039  }
  1040  
  1041  func checkEphemeralStorageStats(assert *assert.Assertions,
  1042  	actual statsapi.PodStats,
  1043  	volumes []statsapi.VolumeStats,
  1044  	containers []*runtimeapi.ContainerStats,
  1045  	containerLogStats []*volume.Metrics,
  1046  	podLogStats *volume.Metrics) {
  1047  	var totalUsed, inodesUsed uint64
  1048  	for _, container := range containers {
  1049  		totalUsed = totalUsed + container.WritableLayer.UsedBytes.Value
  1050  		inodesUsed = inodesUsed + container.WritableLayer.InodesUsed.Value
  1051  	}
  1052  
  1053  	for _, volume := range volumes {
  1054  		totalUsed = totalUsed + *volume.FsStats.UsedBytes
  1055  		inodesUsed = inodesUsed + *volume.FsStats.InodesUsed
  1056  	}
  1057  
  1058  	for _, logStats := range containerLogStats {
  1059  		totalUsed = totalUsed + uint64(logStats.Used.Value())
  1060  		inodesUsed = inodesUsed + uint64(logStats.InodesUsed.Value())
  1061  	}
  1062  
  1063  	if podLogStats != nil {
  1064  		totalUsed = totalUsed + uint64(podLogStats.Used.Value())
  1065  		inodesUsed = inodesUsed + uint64(podLogStats.InodesUsed.Value())
  1066  	}
  1067  
  1068  	assert.Equal(int(totalUsed), int(*actual.EphemeralStorage.UsedBytes))
  1069  	assert.Equal(int(inodesUsed), int(*actual.EphemeralStorage.InodesUsed))
  1070  }
  1071  
  1072  func checkCRINetworkStats(assert *assert.Assertions, actual *statsapi.NetworkStats, expected *cadvisorapiv2.NetworkStats) {
  1073  	assert.Equal(expected.Interfaces[0].RxBytes, *actual.RxBytes)
  1074  	assert.Equal(expected.Interfaces[0].RxErrors, *actual.RxErrors)
  1075  	assert.Equal(expected.Interfaces[0].TxBytes, *actual.TxBytes)
  1076  	assert.Equal(expected.Interfaces[0].TxErrors, *actual.TxErrors)
  1077  }
  1078  
  1079  func checkCRIPodCPUAndMemoryStats(assert *assert.Assertions, actual statsapi.PodStats, cs *cadvisorapiv2.ContainerStats) {
  1080  	if runtime.GOOS != "linux" {
  1081  		return
  1082  	}
  1083  	assert.Equal(cs.Timestamp.UnixNano(), actual.CPU.Time.UnixNano())
  1084  	assert.Equal(cs.Cpu.Usage.Total, *actual.CPU.UsageCoreNanoSeconds)
  1085  	assert.Equal(cs.CpuInst.Usage.Total, *actual.CPU.UsageNanoCores)
  1086  
  1087  	assert.Equal(cs.Memory.Usage, *actual.Memory.UsageBytes)
  1088  	assert.Equal(cs.Memory.WorkingSet, *actual.Memory.WorkingSetBytes)
  1089  	assert.Equal(cs.Memory.RSS, *actual.Memory.RSSBytes)
  1090  	assert.Equal(cs.Memory.ContainerData.Pgfault, *actual.Memory.PageFaults)
  1091  	assert.Equal(cs.Memory.ContainerData.Pgmajfault, *actual.Memory.MajorPageFaults)
  1092  }
  1093  
  1094  func checkCRIPodSwapStats(assert *assert.Assertions, actual statsapi.PodStats, cs *cadvisorapiv2.ContainerStats) {
  1095  	if runtime.GOOS != "linux" {
  1096  		return
  1097  	}
  1098  
  1099  	assert.Equal(cs.Timestamp.UnixNano(), actual.Swap.Time.UnixNano())
  1100  	assert.Equal(cs.Memory.Swap, *actual.Swap.SwapUsageBytes)
  1101  }
  1102  
  1103  func checkCRIPodCPUAndMemoryStatsStrictlyFromCRI(assert *assert.Assertions, actual statsapi.PodStats, excepted statsapi.PodStats) {
  1104  	if runtime.GOOS != "linux" {
  1105  		return
  1106  	}
  1107  	assert.Equal(excepted.CPU.Time.UnixNano(), actual.CPU.Time.UnixNano())
  1108  	assert.Equal(*excepted.CPU.UsageCoreNanoSeconds, *actual.CPU.UsageCoreNanoSeconds)
  1109  	assert.Equal(*excepted.Memory.WorkingSetBytes, *actual.Memory.WorkingSetBytes)
  1110  }
  1111  
  1112  func makeFakeLogStats(seed int) *volume.Metrics {
  1113  	m := &volume.Metrics{}
  1114  	m.Used = resource.NewQuantity(int64(seed+offsetUsage), resource.BinarySI)
  1115  	m.InodesUsed = resource.NewQuantity(int64(seed+offsetInodeUsage), resource.BinarySI)
  1116  	return m
  1117  }
  1118  
  1119  func TestGetContainerUsageNanoCores(t *testing.T) {
  1120  	var value0 uint64
  1121  	var value1 uint64 = 10000000000
  1122  
  1123  	// Test with a large container of 100+ CPUs
  1124  	var value2 uint64 = 188427786383
  1125  
  1126  	tests := []struct {
  1127  		desc          string
  1128  		cpuUsageCache map[string]*cpuUsageRecord
  1129  		stats         *runtimeapi.ContainerStats
  1130  		expected      *uint64
  1131  	}{
  1132  		{
  1133  			desc:          "should return nil if stats is nil",
  1134  			cpuUsageCache: map[string]*cpuUsageRecord{},
  1135  		},
  1136  		{
  1137  			desc:          "should return nil if cpu stats is nil",
  1138  			cpuUsageCache: map[string]*cpuUsageRecord{},
  1139  			stats: &runtimeapi.ContainerStats{
  1140  				Attributes: &runtimeapi.ContainerAttributes{
  1141  					Id: "1",
  1142  				},
  1143  				Cpu: nil,
  1144  			},
  1145  		},
  1146  		{
  1147  			desc:          "should return nil if usageCoreNanoSeconds is nil",
  1148  			cpuUsageCache: map[string]*cpuUsageRecord{},
  1149  			stats: &runtimeapi.ContainerStats{
  1150  				Attributes: &runtimeapi.ContainerAttributes{
  1151  					Id: "1",
  1152  				},
  1153  				Cpu: &runtimeapi.CpuUsage{
  1154  					Timestamp:            1,
  1155  					UsageCoreNanoSeconds: nil,
  1156  				},
  1157  			},
  1158  		},
  1159  		{
  1160  			desc:          "should return nil if cpu stats is not cached yet",
  1161  			cpuUsageCache: map[string]*cpuUsageRecord{},
  1162  			stats: &runtimeapi.ContainerStats{
  1163  				Attributes: &runtimeapi.ContainerAttributes{
  1164  					Id: "1",
  1165  				},
  1166  				Cpu: &runtimeapi.CpuUsage{
  1167  					Timestamp: 1,
  1168  					UsageCoreNanoSeconds: &runtimeapi.UInt64Value{
  1169  						Value: 10000000000,
  1170  					},
  1171  				},
  1172  			},
  1173  		},
  1174  		{
  1175  			desc: "should return zero value if cached cpu stats is equal to current value",
  1176  			stats: &runtimeapi.ContainerStats{
  1177  				Attributes: &runtimeapi.ContainerAttributes{
  1178  					Id: "1",
  1179  				},
  1180  				Cpu: &runtimeapi.CpuUsage{
  1181  					Timestamp: 1,
  1182  					UsageCoreNanoSeconds: &runtimeapi.UInt64Value{
  1183  						Value: 10000000000,
  1184  					},
  1185  				},
  1186  			},
  1187  			cpuUsageCache: map[string]*cpuUsageRecord{
  1188  				"1": {
  1189  					stats: &runtimeapi.CpuUsage{
  1190  						Timestamp: 0,
  1191  						UsageCoreNanoSeconds: &runtimeapi.UInt64Value{
  1192  							Value: 10000000000,
  1193  						},
  1194  					},
  1195  				},
  1196  			},
  1197  			expected: &value0,
  1198  		},
  1199  		{
  1200  			desc: "should return correct value if cached cpu stats is not equal to current value",
  1201  			stats: &runtimeapi.ContainerStats{
  1202  				Attributes: &runtimeapi.ContainerAttributes{
  1203  					Id: "1",
  1204  				},
  1205  				Cpu: &runtimeapi.CpuUsage{
  1206  					Timestamp: int64(time.Second / time.Nanosecond),
  1207  					UsageCoreNanoSeconds: &runtimeapi.UInt64Value{
  1208  						Value: 20000000000,
  1209  					},
  1210  				},
  1211  			},
  1212  			cpuUsageCache: map[string]*cpuUsageRecord{
  1213  				"1": {
  1214  					stats: &runtimeapi.CpuUsage{
  1215  						Timestamp: 0,
  1216  						UsageCoreNanoSeconds: &runtimeapi.UInt64Value{
  1217  							Value: 10000000000,
  1218  						},
  1219  					},
  1220  				},
  1221  			},
  1222  			expected: &value1,
  1223  		},
  1224  		{
  1225  			desc: "should return correct value if elapsed UsageCoreNanoSeconds exceeds 18446744073",
  1226  			stats: &runtimeapi.ContainerStats{
  1227  				Attributes: &runtimeapi.ContainerAttributes{
  1228  					Id: "1",
  1229  				},
  1230  				Cpu: &runtimeapi.CpuUsage{
  1231  					Timestamp: int64(time.Second / time.Nanosecond),
  1232  					UsageCoreNanoSeconds: &runtimeapi.UInt64Value{
  1233  						Value: 68172016162105,
  1234  					},
  1235  				},
  1236  			},
  1237  			cpuUsageCache: map[string]*cpuUsageRecord{
  1238  				"1": {
  1239  					stats: &runtimeapi.CpuUsage{
  1240  						Timestamp: 0,
  1241  						UsageCoreNanoSeconds: &runtimeapi.UInt64Value{
  1242  							Value: 67983588375722,
  1243  						},
  1244  					},
  1245  				},
  1246  			},
  1247  			expected: &value2,
  1248  		},
  1249  		{
  1250  			desc: "should return nil if cpuacct is reset to 0 in a live container",
  1251  			stats: &runtimeapi.ContainerStats{
  1252  				Attributes: &runtimeapi.ContainerAttributes{
  1253  					Id: "1",
  1254  				},
  1255  				Cpu: &runtimeapi.CpuUsage{
  1256  					Timestamp: 2,
  1257  					UsageCoreNanoSeconds: &runtimeapi.UInt64Value{
  1258  						Value: 0,
  1259  					},
  1260  				},
  1261  			},
  1262  			cpuUsageCache: map[string]*cpuUsageRecord{
  1263  				"1": {
  1264  					stats: &runtimeapi.CpuUsage{
  1265  						Timestamp: 1,
  1266  						UsageCoreNanoSeconds: &runtimeapi.UInt64Value{
  1267  							Value: 10000000000,
  1268  						},
  1269  					},
  1270  				},
  1271  			},
  1272  			expected: nil,
  1273  		},
  1274  	}
  1275  	for _, test := range tests {
  1276  		provider := &criStatsProvider{cpuUsageCache: test.cpuUsageCache}
  1277  		// Before the update, the cached value should be nil
  1278  		cached := provider.getContainerUsageNanoCores(test.stats)
  1279  		assert.Nil(t, cached)
  1280  
  1281  		// Update the cache and get the latest value.
  1282  		real := provider.getAndUpdateContainerUsageNanoCores(test.stats)
  1283  		assert.Equal(t, test.expected, real, test.desc)
  1284  
  1285  		// After the update, the cached value should be up-to-date
  1286  		cached = provider.getContainerUsageNanoCores(test.stats)
  1287  		assert.Equal(t, test.expected, cached, test.desc)
  1288  	}
  1289  }
  1290  
  1291  func TestExtractIDFromCgroupPath(t *testing.T) {
  1292  	tests := []struct {
  1293  		cgroupPath string
  1294  		expected   string
  1295  	}{
  1296  		{
  1297  			cgroupPath: "/kubepods/burstable/pod2fc932ce-fdcc-454b-97bd-aadfdeb4c340/9be25294016e2dc0340dd605ce1f57b492039b267a6a618a7ad2a7a58a740f32",
  1298  			expected:   "9be25294016e2dc0340dd605ce1f57b492039b267a6a618a7ad2a7a58a740f32",
  1299  		},
  1300  		{
  1301  			cgroupPath: "/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod2fc932ce_fdcc_454b_97bd_aadfdeb4c340.slice/cri-containerd-aaefb9d8feed2d453b543f6d928cede7a4dbefa6a0ae7c9b990dd234c56e93b9.scope",
  1302  			expected:   "aaefb9d8feed2d453b543f6d928cede7a4dbefa6a0ae7c9b990dd234c56e93b9",
  1303  		},
  1304  		{
  1305  			cgroupPath: "/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod2fc932ce_fdcc_454b_97bd_aadfdeb4c340.slice/cri-o-aaefb9d8feed2d453b543f6d928cede7a4dbefa6a0ae7c9b990dd234c56e93b9.scope",
  1306  			expected:   "aaefb9d8feed2d453b543f6d928cede7a4dbefa6a0ae7c9b990dd234c56e93b9",
  1307  		},
  1308  	}
  1309  
  1310  	for _, test := range tests {
  1311  		id := extractIDFromCgroupPath(test.cgroupPath)
  1312  		assert.Equal(t, test.expected, id)
  1313  	}
  1314  }
  1315  
  1316  func getCRIContainerStatsStrictlyFromCRI(seed int, containerName string) statsapi.ContainerStats {
  1317  	result := statsapi.ContainerStats{
  1318  		Name:      containerName,
  1319  		StartTime: metav1.NewTime(timestamp),
  1320  		CPU:       &statsapi.CPUStats{},
  1321  		Memory:    &statsapi.MemoryStats{},
  1322  		// UserDefinedMetrics is not supported by CRI.
  1323  		Rootfs: &statsapi.FsStats{},
  1324  	}
  1325  
  1326  	result.CPU.Time = metav1.NewTime(timestamp)
  1327  	usageCoreNanoSeconds := uint64(seed + offsetCRI + offsetCPUUsageCoreSeconds)
  1328  	result.CPU.UsageCoreNanoSeconds = &usageCoreNanoSeconds
  1329  
  1330  	result.Memory.Time = metav1.NewTime(timestamp)
  1331  	workingSetBytes := uint64(seed + offsetCRI + offsetMemWorkingSetBytes)
  1332  	result.Memory.WorkingSetBytes = &workingSetBytes
  1333  
  1334  	result.Rootfs.Time = metav1.NewTime(timestamp)
  1335  	usedBytes := uint64(seed + offsetCRI + offsetFsUsage)
  1336  	result.Rootfs.UsedBytes = &usedBytes
  1337  
  1338  	inodesUsed := uint64(seed + offsetCRI + offsetFsInodeUsage)
  1339  	result.Rootfs.InodesUsed = &inodesUsed
  1340  
  1341  	return result
  1342  }