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