cadvisor_stats_provider_test.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. /*
  2. Copyright 2017 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package stats
  14. import (
  15. "testing"
  16. cadvisorapiv2 "github.com/google/cadvisor/info/v2"
  17. "github.com/stretchr/testify/assert"
  18. "k8s.io/api/core/v1"
  19. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  20. "k8s.io/apimachinery/pkg/types"
  21. statsapi "k8s.io/kubernetes/pkg/kubelet/apis/stats/v1alpha1"
  22. cadvisortest "k8s.io/kubernetes/pkg/kubelet/cadvisor/testing"
  23. kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
  24. containertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
  25. "k8s.io/kubernetes/pkg/kubelet/leaky"
  26. serverstats "k8s.io/kubernetes/pkg/kubelet/server/stats"
  27. statustest "k8s.io/kubernetes/pkg/kubelet/status/testing"
  28. )
  29. func TestRemoveTerminatedContainerInfo(t *testing.T) {
  30. const (
  31. seedPastPod0Infra = 1000
  32. seedPastPod0Container0 = 2000
  33. seedPod0Infra = 3000
  34. seedPod0Container0 = 4000
  35. )
  36. const (
  37. namespace = "test"
  38. pName0 = "pod0"
  39. cName00 = "c0"
  40. )
  41. infos := map[string]cadvisorapiv2.ContainerInfo{
  42. // ContainerInfo with past creation time and no CPU/memory usage for
  43. // simulating uncleaned cgroups of already terminated containers, which
  44. // should not be shown in the results.
  45. "/pod0-i-terminated-1": getTerminatedContainerInfo(seedPastPod0Infra, pName0, namespace, leaky.PodInfraContainerName),
  46. "/pod0-c0-terminated-1": getTerminatedContainerInfo(seedPastPod0Container0, pName0, namespace, cName00),
  47. // Same as above but uses the same creation time as the latest
  48. // containers. They are terminated containers, so they should not be in
  49. // the results.
  50. "/pod0-i-terminated-2": getTerminatedContainerInfo(seedPod0Infra, pName0, namespace, leaky.PodInfraContainerName),
  51. "/pod0-c0-terminated-2": getTerminatedContainerInfo(seedPod0Container0, pName0, namespace, cName00),
  52. // The latest containers, which should be in the results.
  53. "/pod0-i": getTestContainerInfo(seedPod0Infra, pName0, namespace, leaky.PodInfraContainerName),
  54. "/pod0-c0": getTestContainerInfo(seedPod0Container0, pName0, namespace, cName00),
  55. }
  56. output := removeTerminatedContainerInfo(infos)
  57. assert.Len(t, output, 2)
  58. for _, c := range []string{"/pod0-i", "/pod0-c0"} {
  59. if _, found := output[c]; !found {
  60. t.Errorf("%q is expected to be in the output\n", c)
  61. }
  62. }
  63. }
  64. func TestCadvisorListPodStats(t *testing.T) {
  65. const (
  66. namespace0 = "test0"
  67. namespace2 = "test2"
  68. )
  69. const (
  70. seedRoot = 0
  71. seedRuntime = 100
  72. seedKubelet = 200
  73. seedMisc = 300
  74. seedPod0Infra = 1000
  75. seedPod0Container0 = 2000
  76. seedPod0Container1 = 2001
  77. seedPod1Infra = 3000
  78. seedPod1Container = 4000
  79. seedPod2Infra = 5000
  80. seedPod2Container = 6000
  81. seedEphemeralVolume1 = 10000
  82. seedEphemeralVolume2 = 10001
  83. seedPersistentVolume1 = 20000
  84. seedPersistentVolume2 = 20001
  85. )
  86. const (
  87. pName0 = "pod0"
  88. pName1 = "pod1"
  89. pName2 = "pod0" // ensure pName2 conflicts with pName0, but is in a different namespace
  90. )
  91. const (
  92. cName00 = "c0"
  93. cName01 = "c1"
  94. cName10 = "c0" // ensure cName10 conflicts with cName02, but is in a different pod
  95. cName20 = "c1" // ensure cName20 conflicts with cName01, but is in a different pod + namespace
  96. )
  97. const (
  98. rootfsCapacity = uint64(10000000)
  99. rootfsAvailable = uint64(5000000)
  100. rootfsInodesFree = uint64(1000)
  101. rootfsInodes = uint64(2000)
  102. imagefsCapacity = uint64(20000000)
  103. imagefsAvailable = uint64(8000000)
  104. imagefsInodesFree = uint64(2000)
  105. imagefsInodes = uint64(4000)
  106. )
  107. prf0 := statsapi.PodReference{Name: pName0, Namespace: namespace0, UID: "UID" + pName0}
  108. prf1 := statsapi.PodReference{Name: pName1, Namespace: namespace0, UID: "UID" + pName1}
  109. prf2 := statsapi.PodReference{Name: pName2, Namespace: namespace2, UID: "UID" + pName2}
  110. infos := map[string]cadvisorapiv2.ContainerInfo{
  111. "/": getTestContainerInfo(seedRoot, "", "", ""),
  112. "/docker-daemon": getTestContainerInfo(seedRuntime, "", "", ""),
  113. "/kubelet": getTestContainerInfo(seedKubelet, "", "", ""),
  114. "/system": getTestContainerInfo(seedMisc, "", "", ""),
  115. // Pod0 - Namespace0
  116. "/pod0-i": getTestContainerInfo(seedPod0Infra, pName0, namespace0, leaky.PodInfraContainerName),
  117. "/pod0-c0": getTestContainerInfo(seedPod0Container0, pName0, namespace0, cName00),
  118. "/pod0-c1": getTestContainerInfo(seedPod0Container1, pName0, namespace0, cName01),
  119. // Pod1 - Namespace0
  120. "/pod1-i": getTestContainerInfo(seedPod1Infra, pName1, namespace0, leaky.PodInfraContainerName),
  121. "/pod1-c0": getTestContainerInfo(seedPod1Container, pName1, namespace0, cName10),
  122. // Pod2 - Namespace2
  123. "/pod2-i": getTestContainerInfo(seedPod2Infra, pName2, namespace2, leaky.PodInfraContainerName),
  124. "/pod2-c0": getTestContainerInfo(seedPod2Container, pName2, namespace2, cName20),
  125. "/kubepods/burstable/podUIDpod0": getTestContainerInfo(seedPod0Infra, pName0, namespace0, leaky.PodInfraContainerName),
  126. "/kubepods/podUIDpod1": getTestContainerInfo(seedPod1Infra, pName1, namespace0, leaky.PodInfraContainerName),
  127. }
  128. freeRootfsInodes := rootfsInodesFree
  129. totalRootfsInodes := rootfsInodes
  130. rootfs := cadvisorapiv2.FsInfo{
  131. Capacity: rootfsCapacity,
  132. Available: rootfsAvailable,
  133. InodesFree: &freeRootfsInodes,
  134. Inodes: &totalRootfsInodes,
  135. }
  136. freeImagefsInodes := imagefsInodesFree
  137. totalImagefsInodes := imagefsInodes
  138. imagefs := cadvisorapiv2.FsInfo{
  139. Capacity: imagefsCapacity,
  140. Available: imagefsAvailable,
  141. InodesFree: &freeImagefsInodes,
  142. Inodes: &totalImagefsInodes,
  143. }
  144. // memory limit overrides for each container (used to test available bytes if a memory limit is known)
  145. memoryLimitOverrides := map[string]uint64{
  146. "/": uint64(1 << 30),
  147. "/pod2-c0": uint64(1 << 15),
  148. }
  149. for name, memoryLimitOverride := range memoryLimitOverrides {
  150. info, found := infos[name]
  151. if !found {
  152. t.Errorf("No container defined with name %v", name)
  153. }
  154. info.Spec.Memory.Limit = memoryLimitOverride
  155. infos[name] = info
  156. }
  157. options := cadvisorapiv2.RequestOptions{
  158. IdType: cadvisorapiv2.TypeName,
  159. Count: 2,
  160. Recursive: true,
  161. }
  162. mockCadvisor := new(cadvisortest.Mock)
  163. mockCadvisor.
  164. On("ContainerInfoV2", "/", options).Return(infos, nil).
  165. On("RootFsInfo").Return(rootfs, nil).
  166. On("ImagesFsInfo").Return(imagefs, nil)
  167. mockRuntime := new(containertest.Mock)
  168. mockRuntime.
  169. On("ImageStats").Return(&kubecontainer.ImageStats{TotalStorageBytes: 123}, nil)
  170. ephemeralVolumes := []statsapi.VolumeStats{getPodVolumeStats(seedEphemeralVolume1, "ephemeralVolume1"),
  171. getPodVolumeStats(seedEphemeralVolume2, "ephemeralVolume2")}
  172. persistentVolumes := []statsapi.VolumeStats{getPodVolumeStats(seedPersistentVolume1, "persistentVolume1"),
  173. getPodVolumeStats(seedPersistentVolume2, "persistentVolume2")}
  174. volumeStats := serverstats.PodVolumeStats{
  175. EphemeralVolumes: ephemeralVolumes,
  176. PersistentVolumes: persistentVolumes,
  177. }
  178. p0Time := metav1.Now()
  179. p1Time := metav1.Now()
  180. p2Time := metav1.Now()
  181. mockStatus := new(statustest.MockStatusProvider)
  182. mockStatus.On("GetPodStatus", types.UID("UID"+pName0)).Return(v1.PodStatus{StartTime: &p0Time}, true)
  183. mockStatus.On("GetPodStatus", types.UID("UID"+pName1)).Return(v1.PodStatus{StartTime: &p1Time}, true)
  184. mockStatus.On("GetPodStatus", types.UID("UID"+pName2)).Return(v1.PodStatus{StartTime: &p2Time}, true)
  185. resourceAnalyzer := &fakeResourceAnalyzer{podVolumeStats: volumeStats}
  186. p := NewCadvisorStatsProvider(mockCadvisor, resourceAnalyzer, nil, nil, mockRuntime, mockStatus)
  187. pods, err := p.ListPodStats()
  188. assert.NoError(t, err)
  189. assert.Equal(t, 3, len(pods))
  190. indexPods := make(map[statsapi.PodReference]statsapi.PodStats, len(pods))
  191. for _, pod := range pods {
  192. indexPods[pod.PodRef] = pod
  193. }
  194. // Validate Pod0 Results
  195. ps, found := indexPods[prf0]
  196. assert.True(t, found)
  197. assert.Len(t, ps.Containers, 2)
  198. indexCon := make(map[string]statsapi.ContainerStats, len(ps.Containers))
  199. for _, con := range ps.Containers {
  200. indexCon[con.Name] = con
  201. }
  202. con := indexCon[cName00]
  203. assert.EqualValues(t, testTime(creationTime, seedPod0Container0).Unix(), con.StartTime.Time.Unix())
  204. checkCPUStats(t, "Pod0Container0", seedPod0Container0, con.CPU)
  205. checkMemoryStats(t, "Pod0Conainer0", seedPod0Container0, infos["/pod0-c0"], con.Memory)
  206. con = indexCon[cName01]
  207. assert.EqualValues(t, testTime(creationTime, seedPod0Container1).Unix(), con.StartTime.Time.Unix())
  208. checkCPUStats(t, "Pod0Container1", seedPod0Container1, con.CPU)
  209. checkMemoryStats(t, "Pod0Container1", seedPod0Container1, infos["/pod0-c1"], con.Memory)
  210. assert.EqualValues(t, p0Time.Unix(), ps.StartTime.Time.Unix())
  211. checkNetworkStats(t, "Pod0", seedPod0Infra, ps.Network)
  212. checkEphemeralStats(t, "Pod0", []int{seedPod0Container0, seedPod0Container1}, []int{seedEphemeralVolume1, seedEphemeralVolume2}, ps.EphemeralStorage)
  213. if ps.CPU != nil {
  214. checkCPUStats(t, "Pod0", seedPod0Infra, ps.CPU)
  215. }
  216. if ps.Memory != nil {
  217. checkMemoryStats(t, "Pod0", seedPod0Infra, infos["/pod0-i"], ps.Memory)
  218. }
  219. // Validate Pod1 Results
  220. ps, found = indexPods[prf1]
  221. assert.True(t, found)
  222. assert.Len(t, ps.Containers, 1)
  223. con = ps.Containers[0]
  224. assert.Equal(t, cName10, con.Name)
  225. checkCPUStats(t, "Pod1Container0", seedPod1Container, con.CPU)
  226. checkMemoryStats(t, "Pod1Container0", seedPod1Container, infos["/pod1-c0"], con.Memory)
  227. checkNetworkStats(t, "Pod1", seedPod1Infra, ps.Network)
  228. // Validate Pod2 Results
  229. ps, found = indexPods[prf2]
  230. assert.True(t, found)
  231. assert.Len(t, ps.Containers, 1)
  232. con = ps.Containers[0]
  233. assert.Equal(t, cName20, con.Name)
  234. checkCPUStats(t, "Pod2Container0", seedPod2Container, con.CPU)
  235. checkMemoryStats(t, "Pod2Container0", seedPod2Container, infos["/pod2-c0"], con.Memory)
  236. checkNetworkStats(t, "Pod2", seedPod2Infra, ps.Network)
  237. }
  238. func TestCadvisorListPodCPUAndMemoryStats(t *testing.T) {
  239. const (
  240. namespace0 = "test0"
  241. namespace2 = "test2"
  242. )
  243. const (
  244. seedRoot = 0
  245. seedRuntime = 100
  246. seedKubelet = 200
  247. seedMisc = 300
  248. seedPod0Infra = 1000
  249. seedPod0Container0 = 2000
  250. seedPod0Container1 = 2001
  251. seedPod1Infra = 3000
  252. seedPod1Container = 4000
  253. seedPod2Infra = 5000
  254. seedPod2Container = 6000
  255. seedEphemeralVolume1 = 10000
  256. seedEphemeralVolume2 = 10001
  257. seedPersistentVolume1 = 20000
  258. seedPersistentVolume2 = 20001
  259. )
  260. const (
  261. pName0 = "pod0"
  262. pName1 = "pod1"
  263. pName2 = "pod0" // ensure pName2 conflicts with pName0, but is in a different namespace
  264. )
  265. const (
  266. cName00 = "c0"
  267. cName01 = "c1"
  268. cName10 = "c0" // ensure cName10 conflicts with cName02, but is in a different pod
  269. cName20 = "c1" // ensure cName20 conflicts with cName01, but is in a different pod + namespace
  270. )
  271. prf0 := statsapi.PodReference{Name: pName0, Namespace: namespace0, UID: "UID" + pName0}
  272. prf1 := statsapi.PodReference{Name: pName1, Namespace: namespace0, UID: "UID" + pName1}
  273. prf2 := statsapi.PodReference{Name: pName2, Namespace: namespace2, UID: "UID" + pName2}
  274. infos := map[string]cadvisorapiv2.ContainerInfo{
  275. "/": getTestContainerInfo(seedRoot, "", "", ""),
  276. "/docker-daemon": getTestContainerInfo(seedRuntime, "", "", ""),
  277. "/kubelet": getTestContainerInfo(seedKubelet, "", "", ""),
  278. "/system": getTestContainerInfo(seedMisc, "", "", ""),
  279. // Pod0 - Namespace0
  280. "/pod0-i": getTestContainerInfo(seedPod0Infra, pName0, namespace0, leaky.PodInfraContainerName),
  281. "/pod0-c0": getTestContainerInfo(seedPod0Container0, pName0, namespace0, cName00),
  282. "/pod0-c1": getTestContainerInfo(seedPod0Container1, pName0, namespace0, cName01),
  283. // Pod1 - Namespace0
  284. "/pod1-i": getTestContainerInfo(seedPod1Infra, pName1, namespace0, leaky.PodInfraContainerName),
  285. "/pod1-c0": getTestContainerInfo(seedPod1Container, pName1, namespace0, cName10),
  286. // Pod2 - Namespace2
  287. "/pod2-i": getTestContainerInfo(seedPod2Infra, pName2, namespace2, leaky.PodInfraContainerName),
  288. "/pod2-c0": getTestContainerInfo(seedPod2Container, pName2, namespace2, cName20),
  289. "/kubepods/burstable/podUIDpod0": getTestContainerInfo(seedPod0Infra, pName0, namespace0, leaky.PodInfraContainerName),
  290. "/kubepods/podUIDpod1": getTestContainerInfo(seedPod1Infra, pName1, namespace0, leaky.PodInfraContainerName),
  291. }
  292. // memory limit overrides for each container (used to test available bytes if a memory limit is known)
  293. memoryLimitOverrides := map[string]uint64{
  294. "/": uint64(1 << 30),
  295. "/pod2-c0": uint64(1 << 15),
  296. }
  297. for name, memoryLimitOverride := range memoryLimitOverrides {
  298. info, found := infos[name]
  299. if !found {
  300. t.Errorf("No container defined with name %v", name)
  301. }
  302. info.Spec.Memory.Limit = memoryLimitOverride
  303. infos[name] = info
  304. }
  305. options := cadvisorapiv2.RequestOptions{
  306. IdType: cadvisorapiv2.TypeName,
  307. Count: 2,
  308. Recursive: true,
  309. }
  310. mockCadvisor := new(cadvisortest.Mock)
  311. mockCadvisor.
  312. On("ContainerInfoV2", "/", options).Return(infos, nil)
  313. ephemeralVolumes := []statsapi.VolumeStats{getPodVolumeStats(seedEphemeralVolume1, "ephemeralVolume1"),
  314. getPodVolumeStats(seedEphemeralVolume2, "ephemeralVolume2")}
  315. persistentVolumes := []statsapi.VolumeStats{getPodVolumeStats(seedPersistentVolume1, "persistentVolume1"),
  316. getPodVolumeStats(seedPersistentVolume2, "persistentVolume2")}
  317. volumeStats := serverstats.PodVolumeStats{
  318. EphemeralVolumes: ephemeralVolumes,
  319. PersistentVolumes: persistentVolumes,
  320. }
  321. resourceAnalyzer := &fakeResourceAnalyzer{podVolumeStats: volumeStats}
  322. p := NewCadvisorStatsProvider(mockCadvisor, resourceAnalyzer, nil, nil, nil, nil)
  323. pods, err := p.ListPodCPUAndMemoryStats()
  324. assert.NoError(t, err)
  325. assert.Equal(t, 3, len(pods))
  326. indexPods := make(map[statsapi.PodReference]statsapi.PodStats, len(pods))
  327. for _, pod := range pods {
  328. indexPods[pod.PodRef] = pod
  329. }
  330. // Validate Pod0 Results
  331. ps, found := indexPods[prf0]
  332. assert.True(t, found)
  333. assert.Len(t, ps.Containers, 2)
  334. indexCon := make(map[string]statsapi.ContainerStats, len(ps.Containers))
  335. for _, con := range ps.Containers {
  336. indexCon[con.Name] = con
  337. }
  338. con := indexCon[cName00]
  339. assert.EqualValues(t, testTime(creationTime, seedPod0Container0).Unix(), con.StartTime.Time.Unix())
  340. checkCPUStats(t, "Pod0Container0", seedPod0Container0, con.CPU)
  341. checkMemoryStats(t, "Pod0Conainer0", seedPod0Container0, infos["/pod0-c0"], con.Memory)
  342. assert.Nil(t, con.Rootfs)
  343. assert.Nil(t, con.Logs)
  344. assert.Nil(t, con.Accelerators)
  345. assert.Nil(t, con.UserDefinedMetrics)
  346. con = indexCon[cName01]
  347. assert.EqualValues(t, testTime(creationTime, seedPod0Container1).Unix(), con.StartTime.Time.Unix())
  348. checkCPUStats(t, "Pod0Container1", seedPod0Container1, con.CPU)
  349. checkMemoryStats(t, "Pod0Container1", seedPod0Container1, infos["/pod0-c1"], con.Memory)
  350. assert.Nil(t, con.Rootfs)
  351. assert.Nil(t, con.Logs)
  352. assert.Nil(t, con.Accelerators)
  353. assert.Nil(t, con.UserDefinedMetrics)
  354. assert.EqualValues(t, testTime(creationTime, seedPod0Infra).Unix(), ps.StartTime.Time.Unix())
  355. assert.Nil(t, ps.EphemeralStorage)
  356. assert.Nil(t, ps.VolumeStats)
  357. assert.Nil(t, ps.Network)
  358. if ps.CPU != nil {
  359. checkCPUStats(t, "Pod0", seedPod0Infra, ps.CPU)
  360. }
  361. if ps.Memory != nil {
  362. checkMemoryStats(t, "Pod0", seedPod0Infra, infos["/pod0-i"], ps.Memory)
  363. }
  364. // Validate Pod1 Results
  365. ps, found = indexPods[prf1]
  366. assert.True(t, found)
  367. assert.Len(t, ps.Containers, 1)
  368. con = ps.Containers[0]
  369. assert.Equal(t, cName10, con.Name)
  370. checkCPUStats(t, "Pod1Container0", seedPod1Container, con.CPU)
  371. checkMemoryStats(t, "Pod1Container0", seedPod1Container, infos["/pod1-c0"], con.Memory)
  372. assert.Nil(t, ps.EphemeralStorage)
  373. assert.Nil(t, ps.VolumeStats)
  374. assert.Nil(t, ps.Network)
  375. // Validate Pod2 Results
  376. ps, found = indexPods[prf2]
  377. assert.True(t, found)
  378. assert.Len(t, ps.Containers, 1)
  379. con = ps.Containers[0]
  380. assert.Equal(t, cName20, con.Name)
  381. checkCPUStats(t, "Pod2Container0", seedPod2Container, con.CPU)
  382. checkMemoryStats(t, "Pod2Container0", seedPod2Container, infos["/pod2-c0"], con.Memory)
  383. assert.Nil(t, ps.EphemeralStorage)
  384. assert.Nil(t, ps.VolumeStats)
  385. assert.Nil(t, ps.Network)
  386. }
  387. func TestCadvisorImagesFsStats(t *testing.T) {
  388. var (
  389. assert = assert.New(t)
  390. mockCadvisor = new(cadvisortest.Mock)
  391. mockRuntime = new(containertest.Mock)
  392. seed = 1000
  393. imageFsInfo = getTestFsInfo(seed)
  394. imageStats = &kubecontainer.ImageStats{TotalStorageBytes: 100}
  395. )
  396. mockCadvisor.On("ImagesFsInfo").Return(imageFsInfo, nil)
  397. mockRuntime.On("ImageStats").Return(imageStats, nil)
  398. provider := newCadvisorStatsProvider(mockCadvisor, &fakeResourceAnalyzer{}, mockRuntime, nil)
  399. stats, err := provider.ImageFsStats()
  400. assert.NoError(err)
  401. assert.Equal(imageFsInfo.Timestamp, stats.Time.Time)
  402. assert.Equal(imageFsInfo.Available, *stats.AvailableBytes)
  403. assert.Equal(imageFsInfo.Capacity, *stats.CapacityBytes)
  404. assert.Equal(imageStats.TotalStorageBytes, *stats.UsedBytes)
  405. assert.Equal(imageFsInfo.InodesFree, stats.InodesFree)
  406. assert.Equal(imageFsInfo.Inodes, stats.Inodes)
  407. assert.Equal(*imageFsInfo.Inodes-*imageFsInfo.InodesFree, *stats.InodesUsed)
  408. mockCadvisor.AssertExpectations(t)
  409. }