...

Source file src/k8s.io/kubernetes/pkg/kubelet/stats/provider_test.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  	"testing"
    23  	"time"
    24  
    25  	"github.com/golang/mock/gomock"
    26  	cadvisorapiv1 "github.com/google/cadvisor/info/v1"
    27  	cadvisorapiv2 "github.com/google/cadvisor/info/v2"
    28  	fuzz "github.com/google/gofuzz"
    29  	"github.com/stretchr/testify/assert"
    30  	"github.com/stretchr/testify/require"
    31  
    32  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    33  	"k8s.io/apimachinery/pkg/types"
    34  	statsapi "k8s.io/kubelet/pkg/apis/stats/v1alpha1"
    35  	cadvisortest "k8s.io/kubernetes/pkg/kubelet/cadvisor/testing"
    36  	kubecontainertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
    37  	kubepodtest "k8s.io/kubernetes/pkg/kubelet/pod/testing"
    38  	serverstats "k8s.io/kubernetes/pkg/kubelet/server/stats"
    39  	"k8s.io/kubernetes/pkg/volume"
    40  )
    41  
    42  const (
    43  	// Offsets from seed value in generated container stats.
    44  	offsetCPUUsageCores = iota
    45  	offsetCPUUsageCoreSeconds
    46  	offsetMemPageFaults
    47  	offsetMemMajorPageFaults
    48  	offsetMemUsageBytes
    49  	offsetMemRSSBytes
    50  	offsetMemWorkingSetBytes
    51  	offsetNetRxBytes
    52  	offsetNetRxErrors
    53  	offsetNetTxBytes
    54  	offsetNetTxErrors
    55  	offsetFsCapacity
    56  	offsetFsAvailable
    57  	offsetFsUsage
    58  	offsetFsInodes
    59  	offsetFsInodesFree
    60  	offsetFsTotalUsageBytes
    61  	offsetFsBaseUsageBytes
    62  	offsetFsInodeUsage
    63  	offsetAcceleratorDutyCycle
    64  	offsetMemSwapUsageBytes
    65  )
    66  
    67  var (
    68  	timestamp    = time.Now()
    69  	creationTime = timestamp.Add(-5 * time.Minute)
    70  )
    71  
    72  func TestGetCgroupStats(t *testing.T) {
    73  	const (
    74  		cgroupName        = "test-cgroup-name"
    75  		containerInfoSeed = 1000
    76  		updateStats       = false
    77  	)
    78  
    79  	mockCtrl := gomock.NewController(t)
    80  	defer mockCtrl.Finish()
    81  
    82  	var (
    83  		mockCadvisor     = cadvisortest.NewMockInterface(mockCtrl)
    84  		mockPodManager   = new(kubepodtest.MockManager)
    85  		mockRuntimeCache = new(kubecontainertest.MockRuntimeCache)
    86  
    87  		assert  = assert.New(t)
    88  		options = cadvisorapiv2.RequestOptions{IdType: cadvisorapiv2.TypeName, Count: 2, Recursive: false}
    89  
    90  		containerInfo    = getTestContainerInfo(containerInfoSeed, "test-pod", "test-ns", "test-container")
    91  		containerInfoMap = map[string]cadvisorapiv2.ContainerInfo{cgroupName: containerInfo}
    92  	)
    93  
    94  	mockCadvisor.EXPECT().ContainerInfoV2(cgroupName, options).Return(containerInfoMap, nil)
    95  
    96  	provider := newStatsProvider(mockCadvisor, mockPodManager, mockRuntimeCache, fakeContainerStatsProvider{})
    97  	cs, ns, err := provider.GetCgroupStats(cgroupName, updateStats)
    98  	assert.NoError(err)
    99  
   100  	checkCPUStats(t, "", containerInfoSeed, cs.CPU)
   101  	checkMemoryStats(t, "", containerInfoSeed, containerInfo, cs.Memory)
   102  	checkNetworkStats(t, "", containerInfoSeed, ns)
   103  	checkSwapStats(t, "", containerInfoSeed, containerInfo, cs.Swap)
   104  
   105  	assert.Equal(cgroupName, cs.Name)
   106  	assert.Equal(metav1.NewTime(containerInfo.Spec.CreationTime), cs.StartTime)
   107  }
   108  
   109  func TestGetCgroupCPUAndMemoryStats(t *testing.T) {
   110  	const (
   111  		cgroupName        = "test-cgroup-name"
   112  		containerInfoSeed = 1000
   113  		updateStats       = false
   114  	)
   115  
   116  	mockCtrl := gomock.NewController(t)
   117  	defer mockCtrl.Finish()
   118  
   119  	var (
   120  		mockCadvisor     = cadvisortest.NewMockInterface(mockCtrl)
   121  		mockPodManager   = new(kubepodtest.MockManager)
   122  		mockRuntimeCache = new(kubecontainertest.MockRuntimeCache)
   123  
   124  		assert  = assert.New(t)
   125  		options = cadvisorapiv2.RequestOptions{IdType: cadvisorapiv2.TypeName, Count: 2, Recursive: false}
   126  
   127  		containerInfo    = getTestContainerInfo(containerInfoSeed, "test-pod", "test-ns", "test-container")
   128  		containerInfoMap = map[string]cadvisorapiv2.ContainerInfo{cgroupName: containerInfo}
   129  	)
   130  
   131  	mockCadvisor.EXPECT().ContainerInfoV2(cgroupName, options).Return(containerInfoMap, nil)
   132  
   133  	provider := newStatsProvider(mockCadvisor, mockPodManager, mockRuntimeCache, fakeContainerStatsProvider{})
   134  	cs, err := provider.GetCgroupCPUAndMemoryStats(cgroupName, updateStats)
   135  	assert.NoError(err)
   136  
   137  	checkCPUStats(t, "", containerInfoSeed, cs.CPU)
   138  	checkMemoryStats(t, "", containerInfoSeed, containerInfo, cs.Memory)
   139  
   140  	assert.Equal(cgroupName, cs.Name)
   141  	assert.Equal(metav1.NewTime(containerInfo.Spec.CreationTime), cs.StartTime)
   142  }
   143  
   144  func TestRootFsStats(t *testing.T) {
   145  	const (
   146  		rootFsInfoSeed    = 1000
   147  		containerInfoSeed = 2000
   148  	)
   149  
   150  	mockCtrl := gomock.NewController(t)
   151  	defer mockCtrl.Finish()
   152  
   153  	var (
   154  		mockCadvisor     = cadvisortest.NewMockInterface(mockCtrl)
   155  		mockPodManager   = new(kubepodtest.MockManager)
   156  		mockRuntimeCache = new(kubecontainertest.MockRuntimeCache)
   157  
   158  		assert  = assert.New(t)
   159  		options = cadvisorapiv2.RequestOptions{IdType: cadvisorapiv2.TypeName, Count: 2, Recursive: false}
   160  
   161  		rootFsInfo       = getTestFsInfo(rootFsInfoSeed)
   162  		containerInfo    = getTestContainerInfo(containerInfoSeed, "test-pod", "test-ns", "test-container")
   163  		containerInfoMap = map[string]cadvisorapiv2.ContainerInfo{"/": containerInfo}
   164  	)
   165  
   166  	mockCadvisor.EXPECT().RootFsInfo().Return(rootFsInfo, nil)
   167  	mockCadvisor.EXPECT().ContainerInfoV2("/", options).Return(containerInfoMap, nil)
   168  
   169  	provider := newStatsProvider(mockCadvisor, mockPodManager, mockRuntimeCache, fakeContainerStatsProvider{})
   170  	stats, err := provider.RootFsStats()
   171  	assert.NoError(err)
   172  
   173  	checkFsStats(t, "", rootFsInfoSeed, stats)
   174  
   175  	assert.Equal(metav1.NewTime(containerInfo.Stats[0].Timestamp), stats.Time)
   176  	assert.Equal(rootFsInfo.Usage, *stats.UsedBytes)
   177  	assert.Equal(*rootFsInfo.Inodes-*rootFsInfo.InodesFree, *stats.InodesUsed)
   178  }
   179  
   180  func TestHasDedicatedImageFs(t *testing.T) {
   181  	ctx := context.Background()
   182  	mockCtrl := gomock.NewController(t)
   183  	defer mockCtrl.Finish()
   184  	imageStatsExpected := &statsapi.FsStats{AvailableBytes: uint64Ptr(1)}
   185  
   186  	for desc, test := range map[string]struct {
   187  		rootfsDevice     string
   188  		imagefsDevice    string
   189  		dedicated        bool
   190  		imageFsStats     *statsapi.FsStats
   191  		containerFsStats *statsapi.FsStats
   192  	}{
   193  		"dedicated device for image filesystem": {
   194  			rootfsDevice:  "root/device",
   195  			imagefsDevice: "image/device",
   196  			dedicated:     true,
   197  			imageFsStats:  imageStatsExpected,
   198  		},
   199  		"shared device for image filesystem": {
   200  			rootfsDevice:     "share/device",
   201  			imagefsDevice:    "share/device",
   202  			dedicated:        false,
   203  			imageFsStats:     imageStatsExpected,
   204  			containerFsStats: imageStatsExpected,
   205  		},
   206  		"split filesystem for images": {
   207  			rootfsDevice:     "root/device",
   208  			imagefsDevice:    "root/device",
   209  			dedicated:        true,
   210  			imageFsStats:     &statsapi.FsStats{AvailableBytes: uint64Ptr(1)},
   211  			containerFsStats: &statsapi.FsStats{AvailableBytes: uint64Ptr(2)},
   212  		},
   213  	} {
   214  		t.Logf("TestCase %q", desc)
   215  		var (
   216  			mockCadvisor     = cadvisortest.NewMockInterface(mockCtrl)
   217  			mockPodManager   = new(kubepodtest.MockManager)
   218  			mockRuntimeCache = new(kubecontainertest.MockRuntimeCache)
   219  		)
   220  		mockCadvisor.EXPECT().RootFsInfo().Return(cadvisorapiv2.FsInfo{Device: test.rootfsDevice}, nil)
   221  		provider := newStatsProvider(mockCadvisor, mockPodManager, mockRuntimeCache, fakeContainerStatsProvider{
   222  			device:      test.imagefsDevice,
   223  			imageFs:     test.imageFsStats,
   224  			containerFs: test.containerFsStats,
   225  		})
   226  
   227  		dedicated, err := provider.HasDedicatedImageFs(ctx)
   228  		assert.NoError(t, err)
   229  		assert.Equal(t, test.dedicated, dedicated)
   230  	}
   231  }
   232  
   233  func getTerminatedContainerInfo(seed int, podName string, podNamespace string, containerName string) cadvisorapiv2.ContainerInfo {
   234  	cinfo := getTestContainerInfo(seed, podName, podNamespace, containerName)
   235  	cinfo.Stats[0].Memory.RSS = 0
   236  	cinfo.Stats[0].CpuInst.Usage.Total = 0
   237  	cinfo.Stats[0].Network = &cadvisorapiv2.NetworkStats{
   238  		Interfaces: []cadvisorapiv1.InterfaceStats{{
   239  			Name:     "eth0",
   240  			RxBytes:  0,
   241  			RxErrors: 0,
   242  			TxBytes:  0,
   243  			TxErrors: 0,
   244  		}, {
   245  			Name:     "cbr0",
   246  			RxBytes:  0,
   247  			RxErrors: 0,
   248  			TxBytes:  0,
   249  			TxErrors: 0,
   250  		}},
   251  	}
   252  	return cinfo
   253  }
   254  
   255  func getContainerInfoWithZeroCpuMem(seed int, podName string, podNamespace string, containerName string) cadvisorapiv2.ContainerInfo {
   256  	cinfo := getTestContainerInfo(seed, podName, podNamespace, containerName)
   257  	cinfo.Stats[0].Memory.RSS = 0
   258  	cinfo.Stats[0].CpuInst.Usage.Total = 0
   259  	return cinfo
   260  }
   261  
   262  func getTestContainerInfo(seed int, podName string, podNamespace string, containerName string) cadvisorapiv2.ContainerInfo {
   263  	labels := map[string]string{}
   264  	if podName != "" {
   265  		labels = map[string]string{
   266  			"io.kubernetes.pod.name":       podName,
   267  			"io.kubernetes.pod.uid":        "UID" + podName,
   268  			"io.kubernetes.pod.namespace":  podNamespace,
   269  			"io.kubernetes.container.name": containerName,
   270  		}
   271  	}
   272  	// by default, kernel will set memory.limit_in_bytes to 1 << 63 if not bounded
   273  	unlimitedMemory := uint64(1 << 63)
   274  	spec := cadvisorapiv2.ContainerSpec{
   275  		CreationTime: testTime(creationTime, seed),
   276  		HasCpu:       true,
   277  		HasMemory:    true,
   278  		HasNetwork:   true,
   279  		Labels:       labels,
   280  		Memory: cadvisorapiv2.MemorySpec{
   281  			Limit:     unlimitedMemory,
   282  			SwapLimit: unlimitedMemory,
   283  		},
   284  		CustomMetrics: generateCustomMetricSpec(),
   285  	}
   286  
   287  	totalUsageBytes := uint64(seed + offsetFsTotalUsageBytes)
   288  	baseUsageBytes := uint64(seed + offsetFsBaseUsageBytes)
   289  	inodeUsage := uint64(seed + offsetFsInodeUsage)
   290  
   291  	stats := cadvisorapiv2.ContainerStats{
   292  		Timestamp: testTime(timestamp, seed),
   293  		Cpu:       &cadvisorapiv1.CpuStats{},
   294  		CpuInst:   &cadvisorapiv2.CpuInstStats{},
   295  		Memory: &cadvisorapiv1.MemoryStats{
   296  			Usage:      uint64(seed + offsetMemUsageBytes),
   297  			WorkingSet: uint64(seed + offsetMemWorkingSetBytes),
   298  			RSS:        uint64(seed + offsetMemRSSBytes),
   299  			ContainerData: cadvisorapiv1.MemoryStatsMemoryData{
   300  				Pgfault:    uint64(seed + offsetMemPageFaults),
   301  				Pgmajfault: uint64(seed + offsetMemMajorPageFaults),
   302  			},
   303  			Swap: uint64(seed + offsetMemSwapUsageBytes),
   304  		},
   305  		Network: &cadvisorapiv2.NetworkStats{
   306  			Interfaces: []cadvisorapiv1.InterfaceStats{{
   307  				Name:     "eth0",
   308  				RxBytes:  uint64(seed + offsetNetRxBytes),
   309  				RxErrors: uint64(seed + offsetNetRxErrors),
   310  				TxBytes:  uint64(seed + offsetNetTxBytes),
   311  				TxErrors: uint64(seed + offsetNetTxErrors),
   312  			}, {
   313  				Name:     "cbr0",
   314  				RxBytes:  100,
   315  				RxErrors: 100,
   316  				TxBytes:  100,
   317  				TxErrors: 100,
   318  			}},
   319  		},
   320  		CustomMetrics: generateCustomMetrics(spec.CustomMetrics),
   321  		Filesystem: &cadvisorapiv2.FilesystemStats{
   322  			TotalUsageBytes: &totalUsageBytes,
   323  			BaseUsageBytes:  &baseUsageBytes,
   324  			InodeUsage:      &inodeUsage,
   325  		},
   326  		Accelerators: []cadvisorapiv1.AcceleratorStats{
   327  			{
   328  				Make:        "nvidia",
   329  				Model:       "Tesla K80",
   330  				ID:          "foobar",
   331  				MemoryTotal: uint64(seed + offsetMemUsageBytes),
   332  				MemoryUsed:  uint64(seed + offsetMemUsageBytes),
   333  				DutyCycle:   uint64(seed + offsetAcceleratorDutyCycle),
   334  			},
   335  		},
   336  	}
   337  	stats.Cpu.Usage.Total = uint64(seed + offsetCPUUsageCoreSeconds)
   338  	stats.CpuInst.Usage.Total = uint64(seed + offsetCPUUsageCores)
   339  	return cadvisorapiv2.ContainerInfo{
   340  		Spec:  spec,
   341  		Stats: []*cadvisorapiv2.ContainerStats{&stats},
   342  	}
   343  }
   344  
   345  func getTestFsInfo(seed int) cadvisorapiv2.FsInfo {
   346  	var (
   347  		inodes     = uint64(seed + offsetFsInodes)
   348  		inodesFree = uint64(seed + offsetFsInodesFree)
   349  	)
   350  	return cadvisorapiv2.FsInfo{
   351  		Timestamp:  time.Now(),
   352  		Device:     "test-device",
   353  		Mountpoint: "test-mount-point",
   354  		Capacity:   uint64(seed + offsetFsCapacity),
   355  		Available:  uint64(seed + offsetFsAvailable),
   356  		Usage:      uint64(seed + offsetFsUsage),
   357  		Inodes:     &inodes,
   358  		InodesFree: &inodesFree,
   359  	}
   360  }
   361  
   362  func getPodVolumeStats(seed int, volumeName string) statsapi.VolumeStats {
   363  	availableBytes := uint64(seed + offsetFsAvailable)
   364  	capacityBytes := uint64(seed + offsetFsCapacity)
   365  	usedBytes := uint64(seed + offsetFsUsage)
   366  	inodes := uint64(seed + offsetFsInodes)
   367  	inodesFree := uint64(seed + offsetFsInodesFree)
   368  	inodesUsed := uint64(seed + offsetFsInodeUsage)
   369  	fsStats := statsapi.FsStats{
   370  		Time:           metav1.NewTime(time.Now()),
   371  		AvailableBytes: &availableBytes,
   372  		CapacityBytes:  &capacityBytes,
   373  		UsedBytes:      &usedBytes,
   374  		Inodes:         &inodes,
   375  		InodesFree:     &inodesFree,
   376  		InodesUsed:     &inodesUsed,
   377  	}
   378  	return statsapi.VolumeStats{
   379  		FsStats: fsStats,
   380  		Name:    volumeName,
   381  	}
   382  }
   383  
   384  func generateCustomMetricSpec() []cadvisorapiv1.MetricSpec {
   385  	f := fuzz.New().NilChance(0).Funcs(
   386  		func(e *cadvisorapiv1.MetricSpec, c fuzz.Continue) {
   387  			c.Fuzz(&e.Name)
   388  			switch c.Intn(3) {
   389  			case 0:
   390  				e.Type = cadvisorapiv1.MetricGauge
   391  			case 1:
   392  				e.Type = cadvisorapiv1.MetricCumulative
   393  			case 2:
   394  				e.Type = cadvisorapiv1.MetricType("delta")
   395  			}
   396  			switch c.Intn(2) {
   397  			case 0:
   398  				e.Format = cadvisorapiv1.IntType
   399  			case 1:
   400  				e.Format = cadvisorapiv1.FloatType
   401  			}
   402  			c.Fuzz(&e.Units)
   403  		})
   404  	var ret []cadvisorapiv1.MetricSpec
   405  	f.Fuzz(&ret)
   406  	return ret
   407  }
   408  
   409  func generateCustomMetrics(spec []cadvisorapiv1.MetricSpec) map[string][]cadvisorapiv1.MetricVal {
   410  	ret := map[string][]cadvisorapiv1.MetricVal{}
   411  	for _, metricSpec := range spec {
   412  		f := fuzz.New().NilChance(0).Funcs(
   413  			func(e *cadvisorapiv1.MetricVal, c fuzz.Continue) {
   414  				switch metricSpec.Format {
   415  				case cadvisorapiv1.IntType:
   416  					c.Fuzz(&e.IntValue)
   417  				case cadvisorapiv1.FloatType:
   418  					c.Fuzz(&e.FloatValue)
   419  				}
   420  			})
   421  
   422  		var metrics []cadvisorapiv1.MetricVal
   423  		f.Fuzz(&metrics)
   424  		ret[metricSpec.Name] = metrics
   425  	}
   426  	return ret
   427  }
   428  
   429  func testTime(base time.Time, seed int) time.Time {
   430  	return base.Add(time.Duration(seed) * time.Second)
   431  }
   432  
   433  func checkNetworkStats(t *testing.T, label string, seed int, stats *statsapi.NetworkStats) {
   434  	assert.NotNil(t, stats)
   435  	assert.EqualValues(t, testTime(timestamp, seed).Unix(), stats.Time.Time.Unix(), label+".Net.Time")
   436  	assert.EqualValues(t, "eth0", stats.Name, "default interface name is not eth0")
   437  	assert.EqualValues(t, seed+offsetNetRxBytes, *stats.RxBytes, label+".Net.RxBytes")
   438  	assert.EqualValues(t, seed+offsetNetRxErrors, *stats.RxErrors, label+".Net.RxErrors")
   439  	assert.EqualValues(t, seed+offsetNetTxBytes, *stats.TxBytes, label+".Net.TxBytes")
   440  	assert.EqualValues(t, seed+offsetNetTxErrors, *stats.TxErrors, label+".Net.TxErrors")
   441  
   442  	assert.EqualValues(t, 2, len(stats.Interfaces), "network interfaces should contain 2 elements")
   443  
   444  	assert.EqualValues(t, "eth0", stats.Interfaces[0].Name, "default interface name is not eth0")
   445  	assert.EqualValues(t, seed+offsetNetRxBytes, *stats.Interfaces[0].RxBytes, label+".Net.TxErrors")
   446  	assert.EqualValues(t, seed+offsetNetRxErrors, *stats.Interfaces[0].RxErrors, label+".Net.TxErrors")
   447  	assert.EqualValues(t, seed+offsetNetTxBytes, *stats.Interfaces[0].TxBytes, label+".Net.TxErrors")
   448  	assert.EqualValues(t, seed+offsetNetTxErrors, *stats.Interfaces[0].TxErrors, label+".Net.TxErrors")
   449  
   450  	assert.EqualValues(t, "cbr0", stats.Interfaces[1].Name, "cbr0 interface name is not cbr0")
   451  	assert.EqualValues(t, 100, *stats.Interfaces[1].RxBytes, label+".Net.TxErrors")
   452  	assert.EqualValues(t, 100, *stats.Interfaces[1].RxErrors, label+".Net.TxErrors")
   453  	assert.EqualValues(t, 100, *stats.Interfaces[1].TxBytes, label+".Net.TxErrors")
   454  	assert.EqualValues(t, 100, *stats.Interfaces[1].TxErrors, label+".Net.TxErrors")
   455  
   456  }
   457  
   458  func checkCPUStats(t *testing.T, label string, seed int, stats *statsapi.CPUStats) {
   459  	require.NotNil(t, stats.Time, label+".CPU.Time")
   460  	require.NotNil(t, stats.UsageNanoCores, label+".CPU.UsageNanoCores")
   461  	require.NotNil(t, stats.UsageNanoCores, label+".CPU.UsageCoreSeconds")
   462  	assert.EqualValues(t, testTime(timestamp, seed).Unix(), stats.Time.Time.Unix(), label+".CPU.Time")
   463  	assert.EqualValues(t, seed+offsetCPUUsageCores, *stats.UsageNanoCores, label+".CPU.UsageCores")
   464  	assert.EqualValues(t, seed+offsetCPUUsageCoreSeconds, *stats.UsageCoreNanoSeconds, label+".CPU.UsageCoreSeconds")
   465  }
   466  
   467  func checkMemoryStats(t *testing.T, label string, seed int, info cadvisorapiv2.ContainerInfo, stats *statsapi.MemoryStats) {
   468  	assert.EqualValues(t, testTime(timestamp, seed).Unix(), stats.Time.Time.Unix(), label+".Mem.Time")
   469  	assert.EqualValues(t, seed+offsetMemUsageBytes, *stats.UsageBytes, label+".Mem.UsageBytes")
   470  	assert.EqualValues(t, seed+offsetMemWorkingSetBytes, *stats.WorkingSetBytes, label+".Mem.WorkingSetBytes")
   471  	assert.EqualValues(t, seed+offsetMemRSSBytes, *stats.RSSBytes, label+".Mem.RSSBytes")
   472  	assert.EqualValues(t, seed+offsetMemPageFaults, *stats.PageFaults, label+".Mem.PageFaults")
   473  	assert.EqualValues(t, seed+offsetMemMajorPageFaults, *stats.MajorPageFaults, label+".Mem.MajorPageFaults")
   474  	if !info.Spec.HasMemory || isMemoryUnlimited(info.Spec.Memory.Limit) {
   475  		assert.Nil(t, stats.AvailableBytes, label+".Mem.AvailableBytes")
   476  	} else {
   477  		expected := info.Spec.Memory.Limit - *stats.WorkingSetBytes
   478  		assert.EqualValues(t, expected, *stats.AvailableBytes, label+".Mem.AvailableBytes")
   479  	}
   480  }
   481  
   482  func checkSwapStats(t *testing.T, label string, seed int, info cadvisorapiv2.ContainerInfo, stats *statsapi.SwapStats) {
   483  	label += ".Swap"
   484  
   485  	assert.EqualValues(t, testTime(timestamp, seed).Unix(), stats.Time.Time.Unix(), label+".Time")
   486  	assert.EqualValues(t, seed+offsetMemSwapUsageBytes, *stats.SwapUsageBytes, label+".SwapUsageBytes")
   487  
   488  	if !info.Spec.HasMemory || isMemoryUnlimited(info.Spec.Memory.SwapLimit) {
   489  		assert.Nil(t, stats.SwapAvailableBytes, label+".SwapAvailableBytes")
   490  	} else {
   491  		expected := info.Spec.Memory.Limit - *stats.SwapUsageBytes
   492  		assert.EqualValues(t, expected, *stats.SwapAvailableBytes, label+".AvailableBytes")
   493  	}
   494  }
   495  
   496  func checkFsStats(t *testing.T, label string, seed int, stats *statsapi.FsStats) {
   497  	assert.EqualValues(t, seed+offsetFsCapacity, *stats.CapacityBytes, label+".CapacityBytes")
   498  	assert.EqualValues(t, seed+offsetFsAvailable, *stats.AvailableBytes, label+".AvailableBytes")
   499  	assert.EqualValues(t, seed+offsetFsInodes, *stats.Inodes, label+".Inodes")
   500  	assert.EqualValues(t, seed+offsetFsInodesFree, *stats.InodesFree, label+".InodesFree")
   501  }
   502  
   503  func checkEphemeralStats(t *testing.T, label string, containerSeeds []int, volumeSeeds []int, containerLogStats []*volume.Metrics, stats *statsapi.FsStats) {
   504  	var usedBytes, inodeUsage int
   505  	for _, cseed := range containerSeeds {
   506  		usedBytes += cseed + offsetFsBaseUsageBytes
   507  		inodeUsage += cseed + offsetFsInodeUsage
   508  		// If containerLogStats is nil, then the log stats calculated from cAdvisor
   509  		// information is used. Since it's Total - Base, and these values are
   510  		// set to the offset, we can use the calculated difference in the offset
   511  		// to account for this.
   512  		if containerLogStats == nil {
   513  			usedBytes += offsetFsTotalUsageBytes - offsetFsBaseUsageBytes
   514  		}
   515  	}
   516  	for _, vseed := range volumeSeeds {
   517  		usedBytes += vseed + offsetFsUsage
   518  		inodeUsage += vseed + offsetFsInodeUsage
   519  	}
   520  	for _, logStats := range containerLogStats {
   521  		usedBytes += int(logStats.Used.Value())
   522  		inodeUsage += int(logStats.InodesUsed.Value())
   523  	}
   524  	assert.EqualValues(t, usedBytes, int(*stats.UsedBytes), label+".UsedBytes")
   525  	assert.EqualValues(t, inodeUsage, int(*stats.InodesUsed), label+".InodesUsed")
   526  }
   527  
   528  type fakeResourceAnalyzer struct {
   529  	podVolumeStats serverstats.PodVolumeStats
   530  }
   531  
   532  func (o *fakeResourceAnalyzer) Start()                                               {}
   533  func (o *fakeResourceAnalyzer) Get(context.Context, bool) (*statsapi.Summary, error) { return nil, nil }
   534  func (o *fakeResourceAnalyzer) GetCPUAndMemoryStats(context.Context) (*statsapi.Summary, error) {
   535  	return nil, nil
   536  }
   537  func (o *fakeResourceAnalyzer) GetPodVolumeStats(uid types.UID) (serverstats.PodVolumeStats, bool) {
   538  	return o.podVolumeStats, true
   539  }
   540  
   541  type fakeContainerStatsProvider struct {
   542  	device      string
   543  	imageFs     *statsapi.FsStats
   544  	containerFs *statsapi.FsStats
   545  }
   546  
   547  func (p fakeContainerStatsProvider) ListPodStats(context.Context) ([]statsapi.PodStats, error) {
   548  	return nil, fmt.Errorf("not implemented")
   549  }
   550  
   551  func (p fakeContainerStatsProvider) ListPodStatsAndUpdateCPUNanoCoreUsage(context.Context) ([]statsapi.PodStats, error) {
   552  	return nil, fmt.Errorf("not implemented")
   553  }
   554  
   555  func (p fakeContainerStatsProvider) ListPodCPUAndMemoryStats(context.Context) ([]statsapi.PodStats, error) {
   556  	return nil, fmt.Errorf("not implemented")
   557  }
   558  
   559  func (p fakeContainerStatsProvider) ImageFsStats(context.Context) (*statsapi.FsStats, *statsapi.FsStats, error) {
   560  	return p.imageFs, p.containerFs, nil
   561  }
   562  
   563  func (p fakeContainerStatsProvider) ImageFsDevice(context.Context) (string, error) {
   564  	return p.device, nil
   565  }
   566  

View as plain text