cadvisor_stats_provider_test.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  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. // Duplicated containers with non-zero CPU and memory usage. This case
  56. // shouldn't happen unless something goes wrong, but we want to test
  57. // that the metrics reporting logic works in this scenario.
  58. "/pod0-i-duplicated": getTestContainerInfo(seedPod0Infra, pName0, namespace, leaky.PodInfraContainerName),
  59. "/pod0-c0-duplicated": getTestContainerInfo(seedPod0Container0, pName0, namespace, cName00),
  60. }
  61. output := removeTerminatedContainerInfo(infos)
  62. assert.Len(t, output, 4)
  63. for _, c := range []string{"/pod0-i", "/pod0-c0", "/pod0-i-duplicated", "/pod0-c0-duplicated"} {
  64. if _, found := output[c]; !found {
  65. t.Errorf("%q is expected to be in the output\n", c)
  66. }
  67. }
  68. }
  69. func TestCadvisorListPodStats(t *testing.T) {
  70. const (
  71. namespace0 = "test0"
  72. namespace2 = "test2"
  73. )
  74. const (
  75. seedRoot = 0
  76. seedRuntime = 100
  77. seedKubelet = 200
  78. seedMisc = 300
  79. seedPod0Infra = 1000
  80. seedPod0Container0 = 2000
  81. seedPod0Container1 = 2001
  82. seedPod1Infra = 3000
  83. seedPod1Container = 4000
  84. seedPod2Infra = 5000
  85. seedPod2Container = 6000
  86. seedEphemeralVolume1 = 10000
  87. seedEphemeralVolume2 = 10001
  88. seedPersistentVolume1 = 20000
  89. seedPersistentVolume2 = 20001
  90. )
  91. const (
  92. pName0 = "pod0"
  93. pName1 = "pod1"
  94. pName2 = "pod0" // ensure pName2 conflicts with pName0, but is in a different namespace
  95. )
  96. const (
  97. cName00 = "c0"
  98. cName01 = "c1"
  99. cName10 = "c0" // ensure cName10 conflicts with cName02, but is in a different pod
  100. cName20 = "c1" // ensure cName20 conflicts with cName01, but is in a different pod + namespace
  101. )
  102. const (
  103. rootfsCapacity = uint64(10000000)
  104. rootfsAvailable = uint64(5000000)
  105. rootfsInodesFree = uint64(1000)
  106. rootfsInodes = uint64(2000)
  107. imagefsCapacity = uint64(20000000)
  108. imagefsAvailable = uint64(8000000)
  109. imagefsInodesFree = uint64(2000)
  110. imagefsInodes = uint64(4000)
  111. )
  112. prf0 := statsapi.PodReference{Name: pName0, Namespace: namespace0, UID: "UID" + pName0}
  113. prf1 := statsapi.PodReference{Name: pName1, Namespace: namespace0, UID: "UID" + pName1}
  114. prf2 := statsapi.PodReference{Name: pName2, Namespace: namespace2, UID: "UID" + pName2}
  115. infos := map[string]cadvisorapiv2.ContainerInfo{
  116. "/": getTestContainerInfo(seedRoot, "", "", ""),
  117. "/docker-daemon": getTestContainerInfo(seedRuntime, "", "", ""),
  118. "/kubelet": getTestContainerInfo(seedKubelet, "", "", ""),
  119. "/system": getTestContainerInfo(seedMisc, "", "", ""),
  120. // Pod0 - Namespace0
  121. "/pod0-i": getTestContainerInfo(seedPod0Infra, pName0, namespace0, leaky.PodInfraContainerName),
  122. "/pod0-c0": getTestContainerInfo(seedPod0Container0, pName0, namespace0, cName00),
  123. "/pod0-c1": getTestContainerInfo(seedPod0Container1, pName0, namespace0, cName01),
  124. // Pod1 - Namespace0
  125. "/pod1-i": getTestContainerInfo(seedPod1Infra, pName1, namespace0, leaky.PodInfraContainerName),
  126. "/pod1-c0": getTestContainerInfo(seedPod1Container, pName1, namespace0, cName10),
  127. // Pod2 - Namespace2
  128. "/pod2-i": getTestContainerInfo(seedPod2Infra, pName2, namespace2, leaky.PodInfraContainerName),
  129. "/pod2-c0": getTestContainerInfo(seedPod2Container, pName2, namespace2, cName20),
  130. "/kubepods/burstable/podUIDpod0": getTestContainerInfo(seedPod0Infra, pName0, namespace0, leaky.PodInfraContainerName),
  131. "/kubepods/podUIDpod1": getTestContainerInfo(seedPod1Infra, pName1, namespace0, leaky.PodInfraContainerName),
  132. }
  133. freeRootfsInodes := rootfsInodesFree
  134. totalRootfsInodes := rootfsInodes
  135. rootfs := cadvisorapiv2.FsInfo{
  136. Capacity: rootfsCapacity,
  137. Available: rootfsAvailable,
  138. InodesFree: &freeRootfsInodes,
  139. Inodes: &totalRootfsInodes,
  140. }
  141. freeImagefsInodes := imagefsInodesFree
  142. totalImagefsInodes := imagefsInodes
  143. imagefs := cadvisorapiv2.FsInfo{
  144. Capacity: imagefsCapacity,
  145. Available: imagefsAvailable,
  146. InodesFree: &freeImagefsInodes,
  147. Inodes: &totalImagefsInodes,
  148. }
  149. // memory limit overrides for each container (used to test available bytes if a memory limit is known)
  150. memoryLimitOverrides := map[string]uint64{
  151. "/": uint64(1 << 30),
  152. "/pod2-c0": uint64(1 << 15),
  153. }
  154. for name, memoryLimitOverride := range memoryLimitOverrides {
  155. info, found := infos[name]
  156. if !found {
  157. t.Errorf("No container defined with name %v", name)
  158. }
  159. info.Spec.Memory.Limit = memoryLimitOverride
  160. infos[name] = info
  161. }
  162. options := cadvisorapiv2.RequestOptions{
  163. IdType: cadvisorapiv2.TypeName,
  164. Count: 2,
  165. Recursive: true,
  166. }
  167. mockCadvisor := new(cadvisortest.Mock)
  168. mockCadvisor.
  169. On("ContainerInfoV2", "/", options).Return(infos, nil).
  170. On("RootFsInfo").Return(rootfs, nil).
  171. On("ImagesFsInfo").Return(imagefs, nil)
  172. mockRuntime := new(containertest.Mock)
  173. mockRuntime.
  174. On("ImageStats").Return(&kubecontainer.ImageStats{TotalStorageBytes: 123}, nil)
  175. ephemeralVolumes := []statsapi.VolumeStats{getPodVolumeStats(seedEphemeralVolume1, "ephemeralVolume1"),
  176. getPodVolumeStats(seedEphemeralVolume2, "ephemeralVolume2")}
  177. persistentVolumes := []statsapi.VolumeStats{getPodVolumeStats(seedPersistentVolume1, "persistentVolume1"),
  178. getPodVolumeStats(seedPersistentVolume2, "persistentVolume2")}
  179. volumeStats := serverstats.PodVolumeStats{
  180. EphemeralVolumes: ephemeralVolumes,
  181. PersistentVolumes: persistentVolumes,
  182. }
  183. p0Time := metav1.Now()
  184. p1Time := metav1.Now()
  185. p2Time := metav1.Now()
  186. mockStatus := new(statustest.MockStatusProvider)
  187. mockStatus.On("GetPodStatus", types.UID("UID"+pName0)).Return(v1.PodStatus{StartTime: &p0Time}, true)
  188. mockStatus.On("GetPodStatus", types.UID("UID"+pName1)).Return(v1.PodStatus{StartTime: &p1Time}, true)
  189. mockStatus.On("GetPodStatus", types.UID("UID"+pName2)).Return(v1.PodStatus{StartTime: &p2Time}, true)
  190. resourceAnalyzer := &fakeResourceAnalyzer{podVolumeStats: volumeStats}
  191. p := NewCadvisorStatsProvider(mockCadvisor, resourceAnalyzer, nil, nil, mockRuntime, mockStatus)
  192. pods, err := p.ListPodStats()
  193. assert.NoError(t, err)
  194. assert.Equal(t, 3, len(pods))
  195. indexPods := make(map[statsapi.PodReference]statsapi.PodStats, len(pods))
  196. for _, pod := range pods {
  197. indexPods[pod.PodRef] = pod
  198. }
  199. // Validate Pod0 Results
  200. ps, found := indexPods[prf0]
  201. assert.True(t, found)
  202. assert.Len(t, ps.Containers, 2)
  203. indexCon := make(map[string]statsapi.ContainerStats, len(ps.Containers))
  204. for _, con := range ps.Containers {
  205. indexCon[con.Name] = con
  206. }
  207. con := indexCon[cName00]
  208. assert.EqualValues(t, testTime(creationTime, seedPod0Container0).Unix(), con.StartTime.Time.Unix())
  209. checkCPUStats(t, "Pod0Container0", seedPod0Container0, con.CPU)
  210. checkMemoryStats(t, "Pod0Conainer0", seedPod0Container0, infos["/pod0-c0"], con.Memory)
  211. con = indexCon[cName01]
  212. assert.EqualValues(t, testTime(creationTime, seedPod0Container1).Unix(), con.StartTime.Time.Unix())
  213. checkCPUStats(t, "Pod0Container1", seedPod0Container1, con.CPU)
  214. checkMemoryStats(t, "Pod0Container1", seedPod0Container1, infos["/pod0-c1"], con.Memory)
  215. assert.EqualValues(t, p0Time.Unix(), ps.StartTime.Time.Unix())
  216. checkNetworkStats(t, "Pod0", seedPod0Infra, ps.Network)
  217. checkEphemeralStats(t, "Pod0", []int{seedPod0Container0, seedPod0Container1}, []int{seedEphemeralVolume1, seedEphemeralVolume2}, ps.EphemeralStorage)
  218. if ps.CPU != nil {
  219. checkCPUStats(t, "Pod0", seedPod0Infra, ps.CPU)
  220. }
  221. if ps.Memory != nil {
  222. checkMemoryStats(t, "Pod0", seedPod0Infra, infos["/pod0-i"], ps.Memory)
  223. }
  224. // Validate Pod1 Results
  225. ps, found = indexPods[prf1]
  226. assert.True(t, found)
  227. assert.Len(t, ps.Containers, 1)
  228. con = ps.Containers[0]
  229. assert.Equal(t, cName10, con.Name)
  230. checkCPUStats(t, "Pod1Container0", seedPod1Container, con.CPU)
  231. checkMemoryStats(t, "Pod1Container0", seedPod1Container, infos["/pod1-c0"], con.Memory)
  232. checkNetworkStats(t, "Pod1", seedPod1Infra, ps.Network)
  233. // Validate Pod2 Results
  234. ps, found = indexPods[prf2]
  235. assert.True(t, found)
  236. assert.Len(t, ps.Containers, 1)
  237. con = ps.Containers[0]
  238. assert.Equal(t, cName20, con.Name)
  239. checkCPUStats(t, "Pod2Container0", seedPod2Container, con.CPU)
  240. checkMemoryStats(t, "Pod2Container0", seedPod2Container, infos["/pod2-c0"], con.Memory)
  241. checkNetworkStats(t, "Pod2", seedPod2Infra, ps.Network)
  242. }
  243. func TestCadvisorListPodCPUAndMemoryStats(t *testing.T) {
  244. const (
  245. namespace0 = "test0"
  246. namespace2 = "test2"
  247. )
  248. const (
  249. seedRoot = 0
  250. seedRuntime = 100
  251. seedKubelet = 200
  252. seedMisc = 300
  253. seedPod0Infra = 1000
  254. seedPod0Container0 = 2000
  255. seedPod0Container1 = 2001
  256. seedPod1Infra = 3000
  257. seedPod1Container = 4000
  258. seedPod2Infra = 5000
  259. seedPod2Container = 6000
  260. seedEphemeralVolume1 = 10000
  261. seedEphemeralVolume2 = 10001
  262. seedPersistentVolume1 = 20000
  263. seedPersistentVolume2 = 20001
  264. )
  265. const (
  266. pName0 = "pod0"
  267. pName1 = "pod1"
  268. pName2 = "pod0" // ensure pName2 conflicts with pName0, but is in a different namespace
  269. )
  270. const (
  271. cName00 = "c0"
  272. cName01 = "c1"
  273. cName10 = "c0" // ensure cName10 conflicts with cName02, but is in a different pod
  274. cName20 = "c1" // ensure cName20 conflicts with cName01, but is in a different pod + namespace
  275. )
  276. prf0 := statsapi.PodReference{Name: pName0, Namespace: namespace0, UID: "UID" + pName0}
  277. prf1 := statsapi.PodReference{Name: pName1, Namespace: namespace0, UID: "UID" + pName1}
  278. prf2 := statsapi.PodReference{Name: pName2, Namespace: namespace2, UID: "UID" + pName2}
  279. infos := map[string]cadvisorapiv2.ContainerInfo{
  280. "/": getTestContainerInfo(seedRoot, "", "", ""),
  281. "/docker-daemon": getTestContainerInfo(seedRuntime, "", "", ""),
  282. "/kubelet": getTestContainerInfo(seedKubelet, "", "", ""),
  283. "/system": getTestContainerInfo(seedMisc, "", "", ""),
  284. // Pod0 - Namespace0
  285. "/pod0-i": getTestContainerInfo(seedPod0Infra, pName0, namespace0, leaky.PodInfraContainerName),
  286. "/pod0-c0": getTestContainerInfo(seedPod0Container0, pName0, namespace0, cName00),
  287. "/pod0-c1": getTestContainerInfo(seedPod0Container1, pName0, namespace0, cName01),
  288. // Pod1 - Namespace0
  289. "/pod1-i": getTestContainerInfo(seedPod1Infra, pName1, namespace0, leaky.PodInfraContainerName),
  290. "/pod1-c0": getTestContainerInfo(seedPod1Container, pName1, namespace0, cName10),
  291. // Pod2 - Namespace2
  292. "/pod2-i": getTestContainerInfo(seedPod2Infra, pName2, namespace2, leaky.PodInfraContainerName),
  293. "/pod2-c0": getTestContainerInfo(seedPod2Container, pName2, namespace2, cName20),
  294. "/kubepods/burstable/podUIDpod0": getTestContainerInfo(seedPod0Infra, pName0, namespace0, leaky.PodInfraContainerName),
  295. "/kubepods/podUIDpod1": getTestContainerInfo(seedPod1Infra, pName1, namespace0, leaky.PodInfraContainerName),
  296. }
  297. // memory limit overrides for each container (used to test available bytes if a memory limit is known)
  298. memoryLimitOverrides := map[string]uint64{
  299. "/": uint64(1 << 30),
  300. "/pod2-c0": uint64(1 << 15),
  301. }
  302. for name, memoryLimitOverride := range memoryLimitOverrides {
  303. info, found := infos[name]
  304. if !found {
  305. t.Errorf("No container defined with name %v", name)
  306. }
  307. info.Spec.Memory.Limit = memoryLimitOverride
  308. infos[name] = info
  309. }
  310. options := cadvisorapiv2.RequestOptions{
  311. IdType: cadvisorapiv2.TypeName,
  312. Count: 2,
  313. Recursive: true,
  314. }
  315. mockCadvisor := new(cadvisortest.Mock)
  316. mockCadvisor.
  317. On("ContainerInfoV2", "/", options).Return(infos, nil)
  318. ephemeralVolumes := []statsapi.VolumeStats{getPodVolumeStats(seedEphemeralVolume1, "ephemeralVolume1"),
  319. getPodVolumeStats(seedEphemeralVolume2, "ephemeralVolume2")}
  320. persistentVolumes := []statsapi.VolumeStats{getPodVolumeStats(seedPersistentVolume1, "persistentVolume1"),
  321. getPodVolumeStats(seedPersistentVolume2, "persistentVolume2")}
  322. volumeStats := serverstats.PodVolumeStats{
  323. EphemeralVolumes: ephemeralVolumes,
  324. PersistentVolumes: persistentVolumes,
  325. }
  326. resourceAnalyzer := &fakeResourceAnalyzer{podVolumeStats: volumeStats}
  327. p := NewCadvisorStatsProvider(mockCadvisor, resourceAnalyzer, nil, nil, nil, nil)
  328. pods, err := p.ListPodCPUAndMemoryStats()
  329. assert.NoError(t, err)
  330. assert.Equal(t, 3, len(pods))
  331. indexPods := make(map[statsapi.PodReference]statsapi.PodStats, len(pods))
  332. for _, pod := range pods {
  333. indexPods[pod.PodRef] = pod
  334. }
  335. // Validate Pod0 Results
  336. ps, found := indexPods[prf0]
  337. assert.True(t, found)
  338. assert.Len(t, ps.Containers, 2)
  339. indexCon := make(map[string]statsapi.ContainerStats, len(ps.Containers))
  340. for _, con := range ps.Containers {
  341. indexCon[con.Name] = con
  342. }
  343. con := indexCon[cName00]
  344. assert.EqualValues(t, testTime(creationTime, seedPod0Container0).Unix(), con.StartTime.Time.Unix())
  345. checkCPUStats(t, "Pod0Container0", seedPod0Container0, con.CPU)
  346. checkMemoryStats(t, "Pod0Conainer0", seedPod0Container0, infos["/pod0-c0"], con.Memory)
  347. assert.Nil(t, con.Rootfs)
  348. assert.Nil(t, con.Logs)
  349. assert.Nil(t, con.Accelerators)
  350. assert.Nil(t, con.UserDefinedMetrics)
  351. con = indexCon[cName01]
  352. assert.EqualValues(t, testTime(creationTime, seedPod0Container1).Unix(), con.StartTime.Time.Unix())
  353. checkCPUStats(t, "Pod0Container1", seedPod0Container1, con.CPU)
  354. checkMemoryStats(t, "Pod0Container1", seedPod0Container1, infos["/pod0-c1"], con.Memory)
  355. assert.Nil(t, con.Rootfs)
  356. assert.Nil(t, con.Logs)
  357. assert.Nil(t, con.Accelerators)
  358. assert.Nil(t, con.UserDefinedMetrics)
  359. assert.EqualValues(t, testTime(creationTime, seedPod0Infra).Unix(), ps.StartTime.Time.Unix())
  360. assert.Nil(t, ps.EphemeralStorage)
  361. assert.Nil(t, ps.VolumeStats)
  362. assert.Nil(t, ps.Network)
  363. if ps.CPU != nil {
  364. checkCPUStats(t, "Pod0", seedPod0Infra, ps.CPU)
  365. }
  366. if ps.Memory != nil {
  367. checkMemoryStats(t, "Pod0", seedPod0Infra, infos["/pod0-i"], ps.Memory)
  368. }
  369. // Validate Pod1 Results
  370. ps, found = indexPods[prf1]
  371. assert.True(t, found)
  372. assert.Len(t, ps.Containers, 1)
  373. con = ps.Containers[0]
  374. assert.Equal(t, cName10, con.Name)
  375. checkCPUStats(t, "Pod1Container0", seedPod1Container, con.CPU)
  376. checkMemoryStats(t, "Pod1Container0", seedPod1Container, infos["/pod1-c0"], con.Memory)
  377. assert.Nil(t, ps.EphemeralStorage)
  378. assert.Nil(t, ps.VolumeStats)
  379. assert.Nil(t, ps.Network)
  380. // Validate Pod2 Results
  381. ps, found = indexPods[prf2]
  382. assert.True(t, found)
  383. assert.Len(t, ps.Containers, 1)
  384. con = ps.Containers[0]
  385. assert.Equal(t, cName20, con.Name)
  386. checkCPUStats(t, "Pod2Container0", seedPod2Container, con.CPU)
  387. checkMemoryStats(t, "Pod2Container0", seedPod2Container, infos["/pod2-c0"], con.Memory)
  388. assert.Nil(t, ps.EphemeralStorage)
  389. assert.Nil(t, ps.VolumeStats)
  390. assert.Nil(t, ps.Network)
  391. }
  392. func TestCadvisorImagesFsStats(t *testing.T) {
  393. var (
  394. assert = assert.New(t)
  395. mockCadvisor = new(cadvisortest.Mock)
  396. mockRuntime = new(containertest.Mock)
  397. seed = 1000
  398. imageFsInfo = getTestFsInfo(seed)
  399. imageStats = &kubecontainer.ImageStats{TotalStorageBytes: 100}
  400. )
  401. mockCadvisor.On("ImagesFsInfo").Return(imageFsInfo, nil)
  402. mockRuntime.On("ImageStats").Return(imageStats, nil)
  403. provider := newCadvisorStatsProvider(mockCadvisor, &fakeResourceAnalyzer{}, mockRuntime, nil)
  404. stats, err := provider.ImageFsStats()
  405. assert.NoError(err)
  406. assert.Equal(imageFsInfo.Timestamp, stats.Time.Time)
  407. assert.Equal(imageFsInfo.Available, *stats.AvailableBytes)
  408. assert.Equal(imageFsInfo.Capacity, *stats.CapacityBytes)
  409. assert.Equal(imageStats.TotalStorageBytes, *stats.UsedBytes)
  410. assert.Equal(imageFsInfo.InodesFree, stats.InodesFree)
  411. assert.Equal(imageFsInfo.Inodes, stats.Inodes)
  412. assert.Equal(*imageFsInfo.Inodes-*imageFsInfo.InodesFree, *stats.InodesUsed)
  413. mockCadvisor.AssertExpectations(t)
  414. }