...

Source file src/k8s.io/kubernetes/pkg/kubelet/stats/cadvisor_stats_provider.go

Documentation: k8s.io/kubernetes/pkg/kubelet/stats

     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  	"fmt"
    22  	"path/filepath"
    23  	"sort"
    24  	"strings"
    25  
    26  	cadvisorapiv2 "github.com/google/cadvisor/info/v2"
    27  	"k8s.io/klog/v2"
    28  
    29  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    30  	"k8s.io/apimachinery/pkg/types"
    31  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    32  	statsapi "k8s.io/kubelet/pkg/apis/stats/v1alpha1"
    33  	kubetypes "k8s.io/kubelet/pkg/types"
    34  	"k8s.io/kubernetes/pkg/features"
    35  	"k8s.io/kubernetes/pkg/kubelet/cadvisor"
    36  	"k8s.io/kubernetes/pkg/kubelet/cm"
    37  	kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
    38  	"k8s.io/kubernetes/pkg/kubelet/server/stats"
    39  	"k8s.io/kubernetes/pkg/kubelet/status"
    40  )
    41  
    42  // cadvisorStatsProvider implements the containerStatsProvider interface by
    43  // getting the container stats from cAdvisor. This is needed by
    44  // integrations which do not provide stats from CRI. See
    45  // `pkg/kubelet/cadvisor/util.go#UsingLegacyCadvisorStats` for the logic for
    46  // determining which integrations do not provide stats from CRI.
    47  type cadvisorStatsProvider struct {
    48  	// cadvisor is used to get the stats of the cgroup for the containers that
    49  	// are managed by pods.
    50  	cadvisor cadvisor.Interface
    51  	// resourceAnalyzer is used to get the volume stats of the pods.
    52  	resourceAnalyzer stats.ResourceAnalyzer
    53  	// imageService is used to get the stats of the image filesystem.
    54  	imageService kubecontainer.ImageService
    55  	// statusProvider is used to get pod metadata
    56  	statusProvider status.PodStatusProvider
    57  	// hostStatsProvider is used to get pod host stat usage.
    58  	hostStatsProvider HostStatsProvider
    59  }
    60  
    61  // newCadvisorStatsProvider returns a containerStatsProvider that provides
    62  // container stats from cAdvisor.
    63  func newCadvisorStatsProvider(
    64  	cadvisor cadvisor.Interface,
    65  	resourceAnalyzer stats.ResourceAnalyzer,
    66  	imageService kubecontainer.ImageService,
    67  	statusProvider status.PodStatusProvider,
    68  	hostStatsProvider HostStatsProvider,
    69  ) containerStatsProvider {
    70  	return &cadvisorStatsProvider{
    71  		cadvisor:          cadvisor,
    72  		resourceAnalyzer:  resourceAnalyzer,
    73  		imageService:      imageService,
    74  		statusProvider:    statusProvider,
    75  		hostStatsProvider: hostStatsProvider,
    76  	}
    77  }
    78  
    79  // ListPodStats returns the stats of all the pod-managed containers.
    80  func (p *cadvisorStatsProvider) ListPodStats(_ context.Context) ([]statsapi.PodStats, error) {
    81  	// Gets node root filesystem information and image filesystem stats, which
    82  	// will be used to populate the available and capacity bytes/inodes in
    83  	// container stats.
    84  	rootFsInfo, err := p.cadvisor.RootFsInfo()
    85  	if err != nil {
    86  		return nil, fmt.Errorf("failed to get rootFs info: %v", err)
    87  	}
    88  	imageFsInfo, err := p.cadvisor.ImagesFsInfo()
    89  	if err != nil {
    90  		return nil, fmt.Errorf("failed to get imageFs info: %v", err)
    91  	}
    92  	infos, err := getCadvisorContainerInfo(p.cadvisor)
    93  	if err != nil {
    94  		return nil, fmt.Errorf("failed to get container info from cadvisor: %v", err)
    95  	}
    96  
    97  	filteredInfos, allInfos := filterTerminatedContainerInfoAndAssembleByPodCgroupKey(infos)
    98  	// Map each container to a pod and update the PodStats with container data.
    99  	podToStats := map[statsapi.PodReference]*statsapi.PodStats{}
   100  	for key, cinfo := range filteredInfos {
   101  		// On systemd using devicemapper each mount into the container has an
   102  		// associated cgroup. We ignore them to ensure we do not get duplicate
   103  		// entries in our summary. For details on .mount units:
   104  		// http://man7.org/linux/man-pages/man5/systemd.mount.5.html
   105  		if strings.HasSuffix(key, ".mount") {
   106  			continue
   107  		}
   108  		// Build the Pod key if this container is managed by a Pod
   109  		if !isPodManagedContainer(&cinfo) {
   110  			continue
   111  		}
   112  		ref := buildPodRef(cinfo.Spec.Labels)
   113  
   114  		// Lookup the PodStats for the pod using the PodRef. If none exists,
   115  		// initialize a new entry.
   116  		podStats, found := podToStats[ref]
   117  		if !found {
   118  			podStats = &statsapi.PodStats{PodRef: ref}
   119  			podToStats[ref] = podStats
   120  		}
   121  
   122  		// Update the PodStats entry with the stats from the container by
   123  		// adding it to podStats.Containers.
   124  		containerName := kubetypes.GetContainerName(cinfo.Spec.Labels)
   125  		if containerName == kubetypes.PodInfraContainerName {
   126  			// Special case for infrastructure container which is hidden from
   127  			// the user and has network stats.
   128  			podStats.Network = cadvisorInfoToNetworkStats(&cinfo)
   129  		} else {
   130  			containerStat := cadvisorInfoToContainerStats(containerName, &cinfo, &rootFsInfo, &imageFsInfo)
   131  			// NOTE: This doesn't support the old pod log path, `/var/log/pods/UID`. For containers
   132  			// using old log path, they will be populated by cadvisorInfoToContainerStats.
   133  			podUID := types.UID(podStats.PodRef.UID)
   134  			logs, err := p.hostStatsProvider.getPodContainerLogStats(podStats.PodRef.Namespace, podStats.PodRef.Name, podUID, containerName, &rootFsInfo)
   135  			if err != nil {
   136  				klog.ErrorS(err, "Unable to fetch container log stats", "containerName", containerName)
   137  			} else {
   138  				containerStat.Logs = logs
   139  			}
   140  			podStats.Containers = append(podStats.Containers, *containerStat)
   141  		}
   142  	}
   143  
   144  	// Add each PodStats to the result.
   145  	result := make([]statsapi.PodStats, 0, len(podToStats))
   146  	for _, podStats := range podToStats {
   147  		makePodStorageStats(podStats, &rootFsInfo, p.resourceAnalyzer, p.hostStatsProvider, false)
   148  
   149  		podUID := types.UID(podStats.PodRef.UID)
   150  		// Lookup the pod-level cgroup's CPU and memory stats
   151  		podInfo := getCadvisorPodInfoFromPodUID(podUID, allInfos)
   152  		if podInfo != nil {
   153  			cpu, memory := cadvisorInfoToCPUandMemoryStats(podInfo)
   154  			podStats.CPU = cpu
   155  			podStats.Memory = memory
   156  			podStats.Swap = cadvisorInfoToSwapStats(podInfo)
   157  			podStats.ProcessStats = cadvisorInfoToProcessStats(podInfo)
   158  		}
   159  
   160  		status, found := p.statusProvider.GetPodStatus(podUID)
   161  		if found && status.StartTime != nil && !status.StartTime.IsZero() {
   162  			podStats.StartTime = *status.StartTime
   163  			// only append stats if we were able to get the start time of the pod
   164  			result = append(result, *podStats)
   165  		}
   166  	}
   167  
   168  	return result, nil
   169  }
   170  
   171  // ListPodStatsAndUpdateCPUNanoCoreUsage updates the cpu nano core usage for
   172  // the containers and returns the stats for all the pod-managed containers.
   173  // For cadvisor, cpu nano core usages are pre-computed and cached, so this
   174  // function simply calls ListPodStats.
   175  func (p *cadvisorStatsProvider) ListPodStatsAndUpdateCPUNanoCoreUsage(ctx context.Context) ([]statsapi.PodStats, error) {
   176  	return p.ListPodStats(ctx)
   177  }
   178  
   179  // ListPodCPUAndMemoryStats returns the cpu and memory stats of all the pod-managed containers.
   180  func (p *cadvisorStatsProvider) ListPodCPUAndMemoryStats(_ context.Context) ([]statsapi.PodStats, error) {
   181  	infos, err := getCadvisorContainerInfo(p.cadvisor)
   182  	if err != nil {
   183  		return nil, fmt.Errorf("failed to get container info from cadvisor: %v", err)
   184  	}
   185  	filteredInfos, allInfos := filterTerminatedContainerInfoAndAssembleByPodCgroupKey(infos)
   186  	// Map each container to a pod and update the PodStats with container data.
   187  	podToStats := map[statsapi.PodReference]*statsapi.PodStats{}
   188  	for key, cinfo := range filteredInfos {
   189  		// On systemd using devicemapper each mount into the container has an
   190  		// associated cgroup. We ignore them to ensure we do not get duplicate
   191  		// entries in our summary. For details on .mount units:
   192  		// http://man7.org/linux/man-pages/man5/systemd.mount.5.html
   193  		if strings.HasSuffix(key, ".mount") {
   194  			continue
   195  		}
   196  		// Build the Pod key if this container is managed by a Pod
   197  		if !isPodManagedContainer(&cinfo) {
   198  			continue
   199  		}
   200  		ref := buildPodRef(cinfo.Spec.Labels)
   201  
   202  		// Lookup the PodStats for the pod using the PodRef. If none exists,
   203  		// initialize a new entry.
   204  		podStats, found := podToStats[ref]
   205  		if !found {
   206  			podStats = &statsapi.PodStats{PodRef: ref}
   207  			podToStats[ref] = podStats
   208  		}
   209  
   210  		// Update the PodStats entry with the stats from the container by
   211  		// adding it to podStats.Containers.
   212  		containerName := kubetypes.GetContainerName(cinfo.Spec.Labels)
   213  		if containerName == kubetypes.PodInfraContainerName {
   214  			// Special case for infrastructure container which is hidden from
   215  			// the user and has network stats.
   216  			podStats.StartTime = metav1.NewTime(cinfo.Spec.CreationTime)
   217  		} else {
   218  			podStats.Containers = append(podStats.Containers, *cadvisorInfoToContainerCPUAndMemoryStats(containerName, &cinfo))
   219  		}
   220  	}
   221  
   222  	// Add each PodStats to the result.
   223  	result := make([]statsapi.PodStats, 0, len(podToStats))
   224  	for _, podStats := range podToStats {
   225  		podUID := types.UID(podStats.PodRef.UID)
   226  		// Lookup the pod-level cgroup's CPU and memory stats
   227  		podInfo := getCadvisorPodInfoFromPodUID(podUID, allInfos)
   228  		if podInfo != nil {
   229  			cpu, memory := cadvisorInfoToCPUandMemoryStats(podInfo)
   230  			podStats.CPU = cpu
   231  			podStats.Memory = memory
   232  			podStats.Swap = cadvisorInfoToSwapStats(podInfo)
   233  		}
   234  		result = append(result, *podStats)
   235  	}
   236  
   237  	return result, nil
   238  }
   239  
   240  // ImageFsStats returns the stats of the filesystem for storing images.
   241  func (p *cadvisorStatsProvider) ImageFsStats(ctx context.Context) (imageFsRet *statsapi.FsStats, containerFsRet *statsapi.FsStats, errCall error) {
   242  	imageFsInfo, err := p.cadvisor.ImagesFsInfo()
   243  	if err != nil {
   244  		return nil, nil, fmt.Errorf("failed to get imageFs info: %v", err)
   245  	}
   246  
   247  	if !utilfeature.DefaultFeatureGate.Enabled(features.KubeletSeparateDiskGC) {
   248  		imageStats, err := p.imageService.ImageStats(ctx)
   249  		if err != nil || imageStats == nil {
   250  			return nil, nil, fmt.Errorf("failed to get image stats: %v", err)
   251  		}
   252  
   253  		var imageFsInodesUsed *uint64
   254  		if imageFsInfo.Inodes != nil && imageFsInfo.InodesFree != nil {
   255  			imageFsIU := *imageFsInfo.Inodes - *imageFsInfo.InodesFree
   256  			imageFsInodesUsed = &imageFsIU
   257  		}
   258  
   259  		imageFs := &statsapi.FsStats{
   260  			Time:           metav1.NewTime(imageFsInfo.Timestamp),
   261  			AvailableBytes: &imageFsInfo.Available,
   262  			CapacityBytes:  &imageFsInfo.Capacity,
   263  			UsedBytes:      &imageStats.TotalStorageBytes,
   264  			InodesFree:     imageFsInfo.InodesFree,
   265  			Inodes:         imageFsInfo.Inodes,
   266  			InodesUsed:     imageFsInodesUsed,
   267  		}
   268  		return imageFs, imageFs, nil
   269  	}
   270  	containerFsInfo, err := p.cadvisor.ContainerFsInfo()
   271  	if err != nil {
   272  		return nil, nil, fmt.Errorf("failed to get container fs info: %v", err)
   273  	}
   274  	imageStats, err := p.imageService.ImageFsInfo(ctx)
   275  	if err != nil || imageStats == nil {
   276  		return nil, nil, fmt.Errorf("failed to get image stats: %v", err)
   277  	}
   278  	splitFileSystem := false
   279  	if imageStats.ImageFilesystems[0].FsId.Mountpoint != imageStats.ContainerFilesystems[0].FsId.Mountpoint {
   280  		klog.InfoS("Detect Split Filesystem", "ImageFilesystems", imageStats.ImageFilesystems[0], "ContainerFilesystems", imageStats.ContainerFilesystems[0])
   281  		splitFileSystem = true
   282  	}
   283  	imageFs := imageStats.ImageFilesystems[0]
   284  	var imageFsInodesUsed *uint64
   285  	if imageFsInfo.Inodes != nil && imageFsInfo.InodesFree != nil {
   286  		imageFsIU := *imageFsInfo.Inodes - *imageFsInfo.InodesFree
   287  		imageFsInodesUsed = &imageFsIU
   288  	}
   289  
   290  	fsStats := &statsapi.FsStats{
   291  		Time:           metav1.NewTime(imageFsInfo.Timestamp),
   292  		AvailableBytes: &imageFsInfo.Available,
   293  		CapacityBytes:  &imageFsInfo.Capacity,
   294  		UsedBytes:      &imageFs.UsedBytes.Value,
   295  		InodesFree:     imageFsInfo.InodesFree,
   296  		Inodes:         imageFsInfo.Inodes,
   297  		InodesUsed:     imageFsInodesUsed,
   298  	}
   299  	if !splitFileSystem {
   300  		return fsStats, fsStats, nil
   301  	}
   302  
   303  	containerFs := imageStats.ContainerFilesystems[0]
   304  	var containerFsInodesUsed *uint64
   305  	if containerFsInfo.Inodes != nil && containerFsInfo.InodesFree != nil {
   306  		containerFsIU := *containerFsInfo.Inodes - *containerFsInfo.InodesFree
   307  		containerFsInodesUsed = &containerFsIU
   308  	}
   309  
   310  	fsContainerStats := &statsapi.FsStats{
   311  		Time:           metav1.NewTime(containerFsInfo.Timestamp),
   312  		AvailableBytes: &containerFsInfo.Available,
   313  		CapacityBytes:  &containerFsInfo.Capacity,
   314  		UsedBytes:      &containerFs.UsedBytes.Value,
   315  		InodesFree:     containerFsInfo.InodesFree,
   316  		Inodes:         containerFsInfo.Inodes,
   317  		InodesUsed:     containerFsInodesUsed,
   318  	}
   319  
   320  	return fsStats, fsContainerStats, nil
   321  }
   322  
   323  // ImageFsDevice returns name of the device where the image filesystem locates,
   324  // e.g. /dev/sda1.
   325  func (p *cadvisorStatsProvider) ImageFsDevice(_ context.Context) (string, error) {
   326  	imageFsInfo, err := p.cadvisor.ImagesFsInfo()
   327  	if err != nil {
   328  		return "", err
   329  	}
   330  	return imageFsInfo.Device, nil
   331  }
   332  
   333  // buildPodRef returns a PodReference that identifies the Pod managing cinfo
   334  func buildPodRef(containerLabels map[string]string) statsapi.PodReference {
   335  	podName := kubetypes.GetPodName(containerLabels)
   336  	podNamespace := kubetypes.GetPodNamespace(containerLabels)
   337  	podUID := kubetypes.GetPodUID(containerLabels)
   338  	return statsapi.PodReference{Name: podName, Namespace: podNamespace, UID: podUID}
   339  }
   340  
   341  // isPodManagedContainer returns true if the cinfo container is managed by a Pod
   342  func isPodManagedContainer(cinfo *cadvisorapiv2.ContainerInfo) bool {
   343  	podName := kubetypes.GetPodName(cinfo.Spec.Labels)
   344  	podNamespace := kubetypes.GetPodNamespace(cinfo.Spec.Labels)
   345  	managed := podName != "" && podNamespace != ""
   346  	if !managed && podName != podNamespace {
   347  		klog.InfoS(
   348  			"Expect container to have either both podName and podNamespace labels, or neither",
   349  			"podNameLabel", podName, "podNamespaceLabel", podNamespace)
   350  	}
   351  	return managed
   352  }
   353  
   354  // getCadvisorPodInfoFromPodUID returns a pod cgroup information by matching the podUID with its CgroupName identifier base name
   355  func getCadvisorPodInfoFromPodUID(podUID types.UID, infos map[string]cadvisorapiv2.ContainerInfo) *cadvisorapiv2.ContainerInfo {
   356  	if info, found := infos[cm.GetPodCgroupNameSuffix(podUID)]; found {
   357  		return &info
   358  	}
   359  	return nil
   360  }
   361  
   362  // filterTerminatedContainerInfoAndAssembleByPodCgroupKey returns the specified containerInfo but with
   363  // the stats of the terminated containers removed and all containerInfos assembled by pod cgroup key.
   364  // the first return map is container cgroup name <-> ContainerInfo and
   365  // the second return map is pod cgroup key <-> ContainerInfo.
   366  // A ContainerInfo is considered to be of a terminated container if it has an
   367  // older CreationTime and zero CPU instantaneous and memory RSS usage.
   368  func filterTerminatedContainerInfoAndAssembleByPodCgroupKey(containerInfo map[string]cadvisorapiv2.ContainerInfo) (map[string]cadvisorapiv2.ContainerInfo, map[string]cadvisorapiv2.ContainerInfo) {
   369  	cinfoMap := make(map[containerID][]containerInfoWithCgroup)
   370  	cinfosByPodCgroupKey := make(map[string]cadvisorapiv2.ContainerInfo)
   371  	for key, cinfo := range containerInfo {
   372  		var podCgroupKey string
   373  		if cm.IsSystemdStyleName(key) {
   374  			// Convert to internal cgroup name and take the last component only.
   375  			internalCgroupName := cm.ParseSystemdToCgroupName(key)
   376  			podCgroupKey = internalCgroupName[len(internalCgroupName)-1]
   377  		} else {
   378  			// Take last component only.
   379  			podCgroupKey = filepath.Base(key)
   380  		}
   381  		cinfosByPodCgroupKey[podCgroupKey] = cinfo
   382  		if !isPodManagedContainer(&cinfo) {
   383  			continue
   384  		}
   385  		cinfoID := containerID{
   386  			podRef:        buildPodRef(cinfo.Spec.Labels),
   387  			containerName: kubetypes.GetContainerName(cinfo.Spec.Labels),
   388  		}
   389  		cinfoMap[cinfoID] = append(cinfoMap[cinfoID], containerInfoWithCgroup{
   390  			cinfo:  cinfo,
   391  			cgroup: key,
   392  		})
   393  	}
   394  	result := make(map[string]cadvisorapiv2.ContainerInfo)
   395  	for _, refs := range cinfoMap {
   396  		if len(refs) == 1 {
   397  			// ContainerInfo with no CPU/memory/network usage for uncleaned cgroups of
   398  			// already terminated containers, which should not be shown in the results.
   399  			if !isContainerTerminated(&refs[0].cinfo) {
   400  				result[refs[0].cgroup] = refs[0].cinfo
   401  			}
   402  			continue
   403  		}
   404  		sort.Sort(ByCreationTime(refs))
   405  		for i := len(refs) - 1; i >= 0; i-- {
   406  			if hasMemoryAndCPUInstUsage(&refs[i].cinfo) {
   407  				result[refs[i].cgroup] = refs[i].cinfo
   408  				break
   409  			}
   410  		}
   411  	}
   412  	return result, cinfosByPodCgroupKey
   413  }
   414  
   415  // ByCreationTime implements sort.Interface for []containerInfoWithCgroup based
   416  // on the cinfo.Spec.CreationTime field.
   417  type ByCreationTime []containerInfoWithCgroup
   418  
   419  func (a ByCreationTime) Len() int      { return len(a) }
   420  func (a ByCreationTime) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
   421  func (a ByCreationTime) Less(i, j int) bool {
   422  	if a[i].cinfo.Spec.CreationTime.Equal(a[j].cinfo.Spec.CreationTime) {
   423  		// There shouldn't be two containers with the same name and/or the same
   424  		// creation time. However, to make the logic here robust, we break the
   425  		// tie by moving the one without CPU instantaneous or memory RSS usage
   426  		// to the beginning.
   427  		return hasMemoryAndCPUInstUsage(&a[j].cinfo)
   428  	}
   429  	return a[i].cinfo.Spec.CreationTime.Before(a[j].cinfo.Spec.CreationTime)
   430  }
   431  
   432  // containerID is the identity of a container in a pod.
   433  type containerID struct {
   434  	podRef        statsapi.PodReference
   435  	containerName string
   436  }
   437  
   438  // containerInfoWithCgroup contains the ContainerInfo and its cgroup name.
   439  type containerInfoWithCgroup struct {
   440  	cinfo  cadvisorapiv2.ContainerInfo
   441  	cgroup string
   442  }
   443  
   444  // hasMemoryAndCPUInstUsage returns true if the specified container info has
   445  // both non-zero CPU instantaneous usage and non-zero memory RSS usage, and
   446  // false otherwise.
   447  func hasMemoryAndCPUInstUsage(info *cadvisorapiv2.ContainerInfo) bool {
   448  	if !info.Spec.HasCpu || !info.Spec.HasMemory {
   449  		return false
   450  	}
   451  	cstat, found := latestContainerStats(info)
   452  	if !found {
   453  		return false
   454  	}
   455  	if cstat.CpuInst == nil {
   456  		return false
   457  	}
   458  	return cstat.CpuInst.Usage.Total != 0 && cstat.Memory.RSS != 0
   459  }
   460  
   461  // isContainerTerminated returns true if the specified container meet one of the following conditions
   462  // 1. info.spec both cpu memory and network are false conditions
   463  // 2. info.Stats both network and cpu or memory are nil
   464  // 3. both zero CPU instantaneous usage zero memory RSS usage and zero network usage,
   465  // and false otherwise.
   466  func isContainerTerminated(info *cadvisorapiv2.ContainerInfo) bool {
   467  	if !info.Spec.HasCpu && !info.Spec.HasMemory && !info.Spec.HasNetwork {
   468  		return true
   469  	}
   470  	cstat, found := latestContainerStats(info)
   471  	if !found {
   472  		return true
   473  	}
   474  	if cstat.Network != nil {
   475  		iStats := cadvisorInfoToNetworkStats(info)
   476  		if iStats != nil {
   477  			for _, iStat := range iStats.Interfaces {
   478  				if *iStat.RxErrors != 0 || *iStat.TxErrors != 0 || *iStat.RxBytes != 0 || *iStat.TxBytes != 0 {
   479  					return false
   480  				}
   481  			}
   482  		}
   483  	}
   484  	if cstat.CpuInst == nil || cstat.Memory == nil {
   485  		return true
   486  	}
   487  	return cstat.CpuInst.Usage.Total == 0 && cstat.Memory.RSS == 0
   488  }
   489  
   490  func getCadvisorContainerInfo(ca cadvisor.Interface) (map[string]cadvisorapiv2.ContainerInfo, error) {
   491  	infos, err := ca.ContainerInfoV2("/", cadvisorapiv2.RequestOptions{
   492  		IdType:    cadvisorapiv2.TypeName,
   493  		Count:     2, // 2 samples are needed to compute "instantaneous" CPU
   494  		Recursive: true,
   495  	})
   496  	if err != nil {
   497  		if _, ok := infos["/"]; ok {
   498  			// If the failure is partial, log it and return a best-effort
   499  			// response.
   500  			klog.ErrorS(err, "Partial failure issuing cadvisor.ContainerInfoV2")
   501  		} else {
   502  			return nil, fmt.Errorf("failed to get root cgroup stats: %v", err)
   503  		}
   504  	}
   505  	return infos, nil
   506  }
   507  

View as plain text