cri_stats_provider_test.go 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935
  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. "math/rand"
  16. "os"
  17. "path/filepath"
  18. "runtime"
  19. "testing"
  20. "time"
  21. gomock "github.com/golang/mock/gomock"
  22. cadvisorfs "github.com/google/cadvisor/fs"
  23. cadvisorapiv2 "github.com/google/cadvisor/info/v2"
  24. "github.com/stretchr/testify/assert"
  25. "k8s.io/apimachinery/pkg/api/resource"
  26. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  27. "k8s.io/apimachinery/pkg/types"
  28. "k8s.io/apimachinery/pkg/util/uuid"
  29. runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
  30. critest "k8s.io/cri-api/pkg/apis/testing"
  31. statsapi "k8s.io/kubernetes/pkg/kubelet/apis/stats/v1alpha1"
  32. cadvisortest "k8s.io/kubernetes/pkg/kubelet/cadvisor/testing"
  33. "k8s.io/kubernetes/pkg/kubelet/cm"
  34. kubecontainertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
  35. "k8s.io/kubernetes/pkg/kubelet/kuberuntime"
  36. "k8s.io/kubernetes/pkg/kubelet/leaky"
  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. const (
  42. offsetInodeUsage = iota
  43. offsetUsage
  44. )
  45. const (
  46. seedRoot = 0
  47. seedKubelet = 200
  48. seedMisc = 300
  49. seedSandbox0 = 1000
  50. seedContainer0 = 2000
  51. seedSandbox1 = 3000
  52. seedContainer1 = 4000
  53. seedContainer2 = 5000
  54. seedSandbox2 = 6000
  55. seedContainer3 = 7000
  56. seedSandbox3 = 8000
  57. )
  58. const (
  59. pName0 = "pod0"
  60. pName1 = "pod1"
  61. pName2 = "pod2"
  62. )
  63. const (
  64. cName0 = "container0-name"
  65. cName1 = "container1-name"
  66. cName2 = "container2-name"
  67. cName3 = "container3-name"
  68. cName5 = "container5-name"
  69. cName6 = "container6-name"
  70. cName7 = "container7-name"
  71. cName8 = "container8-name"
  72. )
  73. func TestCRIListPodStats(t *testing.T) {
  74. var (
  75. imageFsMountpoint = "/test/mount/point"
  76. unknownMountpoint = "/unknown/mount/point"
  77. imageFsInfo = getTestFsInfo(2000)
  78. rootFsInfo = getTestFsInfo(1000)
  79. sandbox0 = makeFakePodSandbox("sandbox0-name", "sandbox0-uid", "sandbox0-ns", false)
  80. sandbox0Cgroup = "/" + cm.GetPodCgroupNameSuffix(types.UID(sandbox0.PodSandboxStatus.Metadata.Uid))
  81. container0 = makeFakeContainer(sandbox0, cName0, 0, false)
  82. containerStats0 = makeFakeContainerStats(container0, imageFsMountpoint)
  83. containerLogStats0 = makeFakeLogStats(1000)
  84. container1 = makeFakeContainer(sandbox0, cName1, 0, false)
  85. containerStats1 = makeFakeContainerStats(container1, unknownMountpoint)
  86. containerLogStats1 = makeFakeLogStats(2000)
  87. sandbox1 = makeFakePodSandbox("sandbox1-name", "sandbox1-uid", "sandbox1-ns", false)
  88. sandbox1Cgroup = "/" + cm.GetPodCgroupNameSuffix(types.UID(sandbox1.PodSandboxStatus.Metadata.Uid))
  89. container2 = makeFakeContainer(sandbox1, cName2, 0, false)
  90. containerStats2 = makeFakeContainerStats(container2, imageFsMountpoint)
  91. containerLogStats2 = makeFakeLogStats(3000)
  92. sandbox2 = makeFakePodSandbox("sandbox2-name", "sandbox2-uid", "sandbox2-ns", false)
  93. sandbox2Cgroup = "/" + cm.GetPodCgroupNameSuffix(types.UID(sandbox2.PodSandboxStatus.Metadata.Uid))
  94. container3 = makeFakeContainer(sandbox2, cName3, 0, true)
  95. containerStats3 = makeFakeContainerStats(container3, imageFsMountpoint)
  96. container4 = makeFakeContainer(sandbox2, cName3, 1, false)
  97. containerStats4 = makeFakeContainerStats(container4, imageFsMountpoint)
  98. containerLogStats4 = makeFakeLogStats(4000)
  99. // Running pod with a terminated container and a running container
  100. sandbox3 = makeFakePodSandbox("sandbox3-name", "sandbox3-uid", "sandbox3-ns", false)
  101. sandbox3Cgroup = "/" + cm.GetPodCgroupNameSuffix(types.UID(sandbox3.PodSandboxStatus.Metadata.Uid))
  102. container5 = makeFakeContainer(sandbox3, cName5, 0, true)
  103. containerStats5 = makeFakeContainerStats(container5, imageFsMountpoint)
  104. containerLogStats5 = makeFakeLogStats(5000)
  105. container8 = makeFakeContainer(sandbox3, cName8, 0, false)
  106. containerStats8 = makeFakeContainerStats(container8, imageFsMountpoint)
  107. containerLogStats8 = makeFakeLogStats(6000)
  108. // Terminated pod sandbox
  109. sandbox4 = makeFakePodSandbox("sandbox1-name", "sandbox1-uid", "sandbox1-ns", true)
  110. container6 = makeFakeContainer(sandbox4, cName6, 0, true)
  111. containerStats6 = makeFakeContainerStats(container6, imageFsMountpoint)
  112. // Terminated pod
  113. sandbox5 = makeFakePodSandbox("sandbox1-name", "sandbox5-uid", "sandbox1-ns", true)
  114. container7 = makeFakeContainer(sandbox5, cName7, 0, true)
  115. containerStats7 = makeFakeContainerStats(container7, imageFsMountpoint)
  116. podLogName0 = "pod-log-0"
  117. podLogName1 = "pod-log-1"
  118. podLogStats0 = makeFakeLogStats(5000)
  119. podLogStats1 = makeFakeLogStats(6000)
  120. )
  121. var (
  122. mockCadvisor = new(cadvisortest.Mock)
  123. mockRuntimeCache = new(kubecontainertest.MockRuntimeCache)
  124. mockPodManager = new(kubepodtest.MockManager)
  125. resourceAnalyzer = new(fakeResourceAnalyzer)
  126. fakeRuntimeService = critest.NewFakeRuntimeService()
  127. fakeImageService = critest.NewFakeImageService()
  128. )
  129. infos := map[string]cadvisorapiv2.ContainerInfo{
  130. "/": getTestContainerInfo(seedRoot, "", "", ""),
  131. "/kubelet": getTestContainerInfo(seedKubelet, "", "", ""),
  132. "/system": getTestContainerInfo(seedMisc, "", "", ""),
  133. sandbox0.PodSandboxStatus.Id: getTestContainerInfo(seedSandbox0, pName0, sandbox0.PodSandboxStatus.Metadata.Namespace, leaky.PodInfraContainerName),
  134. sandbox0Cgroup: getTestContainerInfo(seedSandbox0, "", "", ""),
  135. container0.ContainerStatus.Id: getTestContainerInfo(seedContainer0, pName0, sandbox0.PodSandboxStatus.Metadata.Namespace, cName0),
  136. container1.ContainerStatus.Id: getTestContainerInfo(seedContainer1, pName0, sandbox0.PodSandboxStatus.Metadata.Namespace, cName1),
  137. sandbox1.PodSandboxStatus.Id: getTestContainerInfo(seedSandbox1, pName1, sandbox1.PodSandboxStatus.Metadata.Namespace, leaky.PodInfraContainerName),
  138. sandbox1Cgroup: getTestContainerInfo(seedSandbox1, "", "", ""),
  139. container2.ContainerStatus.Id: getTestContainerInfo(seedContainer2, pName1, sandbox1.PodSandboxStatus.Metadata.Namespace, cName2),
  140. sandbox2.PodSandboxStatus.Id: getTestContainerInfo(seedSandbox2, pName2, sandbox2.PodSandboxStatus.Metadata.Namespace, leaky.PodInfraContainerName),
  141. sandbox2Cgroup: getTestContainerInfo(seedSandbox2, "", "", ""),
  142. container4.ContainerStatus.Id: getTestContainerInfo(seedContainer3, pName2, sandbox2.PodSandboxStatus.Metadata.Namespace, cName3),
  143. sandbox3Cgroup: getTestContainerInfo(seedSandbox3, "", "", ""),
  144. }
  145. options := cadvisorapiv2.RequestOptions{
  146. IdType: cadvisorapiv2.TypeName,
  147. Count: 2,
  148. Recursive: true,
  149. }
  150. mockCadvisor.
  151. On("ContainerInfoV2", "/", options).Return(infos, nil).
  152. On("RootFsInfo").Return(rootFsInfo, nil).
  153. On("GetDirFsInfo", imageFsMountpoint).Return(imageFsInfo, nil).
  154. On("GetDirFsInfo", unknownMountpoint).Return(cadvisorapiv2.FsInfo{}, cadvisorfs.ErrNoSuchDevice)
  155. fakeRuntimeService.SetFakeSandboxes([]*critest.FakePodSandbox{
  156. sandbox0, sandbox1, sandbox2, sandbox3, sandbox4, sandbox5,
  157. })
  158. fakeRuntimeService.SetFakeContainers([]*critest.FakeContainer{
  159. container0, container1, container2, container3, container4, container5, container6, container7, container8,
  160. })
  161. fakeRuntimeService.SetFakeContainerStats([]*runtimeapi.ContainerStats{
  162. containerStats0, containerStats1, containerStats2, containerStats3, containerStats4, containerStats5, containerStats6, containerStats7, containerStats8,
  163. })
  164. ephemeralVolumes := makeFakeVolumeStats([]string{"ephVolume1, ephVolumes2"})
  165. persistentVolumes := makeFakeVolumeStats([]string{"persisVolume1, persisVolumes2"})
  166. resourceAnalyzer.podVolumeStats = serverstats.PodVolumeStats{
  167. EphemeralVolumes: ephemeralVolumes,
  168. PersistentVolumes: persistentVolumes,
  169. }
  170. fakeLogStats := map[string]*volume.Metrics{
  171. kuberuntime.BuildContainerLogsDirectory("sandbox0-ns", "sandbox0-name", types.UID("sandbox0-uid"), cName0): containerLogStats0,
  172. kuberuntime.BuildContainerLogsDirectory("sandbox0-ns", "sandbox0-name", types.UID("sandbox0-uid"), cName1): containerLogStats1,
  173. kuberuntime.BuildContainerLogsDirectory("sandbox1-ns", "sandbox1-name", types.UID("sandbox1-uid"), cName2): containerLogStats2,
  174. kuberuntime.BuildContainerLogsDirectory("sandbox2-ns", "sandbox2-name", types.UID("sandbox2-uid"), cName3): containerLogStats4,
  175. kuberuntime.BuildContainerLogsDirectory("sandbox3-ns", "sandbox3-name", types.UID("sandbox3-uid"), cName5): containerLogStats5,
  176. kuberuntime.BuildContainerLogsDirectory("sandbox3-ns", "sandbox3-name", types.UID("sandbox3-uid"), cName8): containerLogStats8,
  177. filepath.Join(kuberuntime.BuildPodLogsDirectory("sandbox0-ns", "sandbox0-name", types.UID("sandbox0-uid")), podLogName0): podLogStats0,
  178. filepath.Join(kuberuntime.BuildPodLogsDirectory("sandbox1-ns", "sandbox1-name", types.UID("sandbox1-uid")), podLogName1): podLogStats1,
  179. }
  180. fakeLogStatsProvider := NewFakeLogMetricsService(fakeLogStats)
  181. ctrl := gomock.NewController(t)
  182. defer ctrl.Finish()
  183. fakeOS := &kubecontainertest.FakeOS{}
  184. fakeOS.ReadDirFn = func(path string) ([]os.FileInfo, error) {
  185. var fileInfos []os.FileInfo
  186. mockFI := kubecontainertest.NewMockFileInfo(ctrl)
  187. switch path {
  188. case kuberuntime.BuildPodLogsDirectory("sandbox0-ns", "sandbox0-name", types.UID("sandbox0-uid")):
  189. mockFI.EXPECT().Name().Return(podLogName0)
  190. case kuberuntime.BuildPodLogsDirectory("sandbox1-ns", "sandbox1-name", types.UID("sandbox1-uid")):
  191. mockFI.EXPECT().Name().Return(podLogName1)
  192. default:
  193. return nil, nil
  194. }
  195. mockFI.EXPECT().IsDir().Return(false)
  196. fileInfos = append(fileInfos, mockFI)
  197. return fileInfos, nil
  198. }
  199. provider := NewCRIStatsProvider(
  200. mockCadvisor,
  201. resourceAnalyzer,
  202. mockPodManager,
  203. mockRuntimeCache,
  204. fakeRuntimeService,
  205. fakeImageService,
  206. fakeLogStatsProvider,
  207. fakeOS,
  208. )
  209. stats, err := provider.ListPodStats()
  210. assert := assert.New(t)
  211. assert.NoError(err)
  212. assert.Equal(4, len(stats))
  213. podStatsMap := make(map[statsapi.PodReference]statsapi.PodStats)
  214. for _, s := range stats {
  215. podStatsMap[s.PodRef] = s
  216. }
  217. p0 := podStatsMap[statsapi.PodReference{Name: "sandbox0-name", UID: "sandbox0-uid", Namespace: "sandbox0-ns"}]
  218. assert.Equal(sandbox0.CreatedAt, p0.StartTime.UnixNano())
  219. assert.Equal(2, len(p0.Containers))
  220. checkEphemeralStorageStats(assert, p0, ephemeralVolumes, []*runtimeapi.ContainerStats{containerStats0, containerStats1},
  221. []*volume.Metrics{containerLogStats0, containerLogStats1}, podLogStats0)
  222. containerStatsMap := make(map[string]statsapi.ContainerStats)
  223. for _, s := range p0.Containers {
  224. containerStatsMap[s.Name] = s
  225. }
  226. c0 := containerStatsMap[cName0]
  227. assert.Equal(container0.CreatedAt, c0.StartTime.UnixNano())
  228. checkCRICPUAndMemoryStats(assert, c0, infos[container0.ContainerStatus.Id].Stats[0])
  229. checkCRIRootfsStats(assert, c0, containerStats0, &imageFsInfo)
  230. checkCRILogsStats(assert, c0, &rootFsInfo, containerLogStats0)
  231. c1 := containerStatsMap[cName1]
  232. assert.Equal(container1.CreatedAt, c1.StartTime.UnixNano())
  233. checkCRICPUAndMemoryStats(assert, c1, infos[container1.ContainerStatus.Id].Stats[0])
  234. checkCRIRootfsStats(assert, c1, containerStats1, nil)
  235. checkCRILogsStats(assert, c1, &rootFsInfo, containerLogStats1)
  236. checkCRINetworkStats(assert, p0.Network, infos[sandbox0.PodSandboxStatus.Id].Stats[0].Network)
  237. checkCRIPodCPUAndMemoryStats(assert, p0, infos[sandbox0Cgroup].Stats[0])
  238. p1 := podStatsMap[statsapi.PodReference{Name: "sandbox1-name", UID: "sandbox1-uid", Namespace: "sandbox1-ns"}]
  239. assert.Equal(sandbox1.CreatedAt, p1.StartTime.UnixNano())
  240. assert.Equal(1, len(p1.Containers))
  241. checkEphemeralStorageStats(assert, p1, ephemeralVolumes, []*runtimeapi.ContainerStats{containerStats2},
  242. []*volume.Metrics{containerLogStats2}, podLogStats1)
  243. c2 := p1.Containers[0]
  244. assert.Equal(cName2, c2.Name)
  245. assert.Equal(container2.CreatedAt, c2.StartTime.UnixNano())
  246. checkCRICPUAndMemoryStats(assert, c2, infos[container2.ContainerStatus.Id].Stats[0])
  247. checkCRIRootfsStats(assert, c2, containerStats2, &imageFsInfo)
  248. checkCRILogsStats(assert, c2, &rootFsInfo, containerLogStats2)
  249. checkCRINetworkStats(assert, p1.Network, infos[sandbox1.PodSandboxStatus.Id].Stats[0].Network)
  250. checkCRIPodCPUAndMemoryStats(assert, p1, infos[sandbox1Cgroup].Stats[0])
  251. p2 := podStatsMap[statsapi.PodReference{Name: "sandbox2-name", UID: "sandbox2-uid", Namespace: "sandbox2-ns"}]
  252. assert.Equal(sandbox2.CreatedAt, p2.StartTime.UnixNano())
  253. assert.Equal(1, len(p2.Containers))
  254. checkEphemeralStorageStats(assert, p2, ephemeralVolumes, []*runtimeapi.ContainerStats{containerStats4},
  255. []*volume.Metrics{containerLogStats4}, nil)
  256. c3 := p2.Containers[0]
  257. assert.Equal(cName3, c3.Name)
  258. assert.Equal(container4.CreatedAt, c3.StartTime.UnixNano())
  259. checkCRICPUAndMemoryStats(assert, c3, infos[container4.ContainerStatus.Id].Stats[0])
  260. checkCRIRootfsStats(assert, c3, containerStats4, &imageFsInfo)
  261. checkCRILogsStats(assert, c3, &rootFsInfo, containerLogStats4)
  262. checkCRINetworkStats(assert, p2.Network, infos[sandbox2.PodSandboxStatus.Id].Stats[0].Network)
  263. checkCRIPodCPUAndMemoryStats(assert, p2, infos[sandbox2Cgroup].Stats[0])
  264. p3 := podStatsMap[statsapi.PodReference{Name: "sandbox3-name", UID: "sandbox3-uid", Namespace: "sandbox3-ns"}]
  265. assert.Equal(sandbox3.CreatedAt, p3.StartTime.UnixNano())
  266. assert.Equal(1, len(p3.Containers))
  267. c8 := p3.Containers[0]
  268. assert.Equal(cName8, c8.Name)
  269. assert.Equal(container8.CreatedAt, c8.StartTime.UnixNano())
  270. assert.NotNil(c8.CPU.Time)
  271. assert.NotNil(c8.Memory.Time)
  272. checkCRIPodCPUAndMemoryStats(assert, p3, infos[sandbox3Cgroup].Stats[0])
  273. mockCadvisor.AssertExpectations(t)
  274. }
  275. func TestCRIListPodCPUAndMemoryStats(t *testing.T) {
  276. var (
  277. imageFsMountpoint = "/test/mount/point"
  278. unknownMountpoint = "/unknown/mount/point"
  279. sandbox0 = makeFakePodSandbox("sandbox0-name", "sandbox0-uid", "sandbox0-ns", false)
  280. sandbox0Cgroup = "/" + cm.GetPodCgroupNameSuffix(types.UID(sandbox0.PodSandboxStatus.Metadata.Uid))
  281. container0 = makeFakeContainer(sandbox0, cName0, 0, false)
  282. containerStats0 = makeFakeContainerStats(container0, imageFsMountpoint)
  283. container1 = makeFakeContainer(sandbox0, cName1, 0, false)
  284. containerStats1 = makeFakeContainerStats(container1, unknownMountpoint)
  285. sandbox1 = makeFakePodSandbox("sandbox1-name", "sandbox1-uid", "sandbox1-ns", false)
  286. sandbox1Cgroup = "/" + cm.GetPodCgroupNameSuffix(types.UID(sandbox1.PodSandboxStatus.Metadata.Uid))
  287. container2 = makeFakeContainer(sandbox1, cName2, 0, false)
  288. containerStats2 = makeFakeContainerStats(container2, imageFsMountpoint)
  289. sandbox2 = makeFakePodSandbox("sandbox2-name", "sandbox2-uid", "sandbox2-ns", false)
  290. sandbox2Cgroup = "/" + cm.GetPodCgroupNameSuffix(types.UID(sandbox2.PodSandboxStatus.Metadata.Uid))
  291. container3 = makeFakeContainer(sandbox2, cName3, 0, true)
  292. containerStats3 = makeFakeContainerStats(container3, imageFsMountpoint)
  293. container4 = makeFakeContainer(sandbox2, cName3, 1, false)
  294. containerStats4 = makeFakeContainerStats(container4, imageFsMountpoint)
  295. // Running pod with a terminated container and a running container
  296. sandbox3 = makeFakePodSandbox("sandbox3-name", "sandbox3-uid", "sandbox3-ns", false)
  297. sandbox3Cgroup = "/" + cm.GetPodCgroupNameSuffix(types.UID(sandbox3.PodSandboxStatus.Metadata.Uid))
  298. container5 = makeFakeContainer(sandbox3, cName5, 0, true)
  299. containerStats5 = makeFakeContainerStats(container5, imageFsMountpoint)
  300. container8 = makeFakeContainer(sandbox3, cName8, 0, false)
  301. containerStats8 = makeFakeContainerStats(container8, imageFsMountpoint)
  302. // Terminated pod sandbox
  303. sandbox4 = makeFakePodSandbox("sandbox1-name", "sandbox1-uid", "sandbox1-ns", true)
  304. container6 = makeFakeContainer(sandbox4, cName6, 0, true)
  305. containerStats6 = makeFakeContainerStats(container6, imageFsMountpoint)
  306. // Terminated pod
  307. sandbox5 = makeFakePodSandbox("sandbox1-name", "sandbox5-uid", "sandbox1-ns", true)
  308. container7 = makeFakeContainer(sandbox5, cName7, 0, true)
  309. containerStats7 = makeFakeContainerStats(container7, imageFsMountpoint)
  310. )
  311. var (
  312. mockCadvisor = new(cadvisortest.Mock)
  313. mockRuntimeCache = new(kubecontainertest.MockRuntimeCache)
  314. mockPodManager = new(kubepodtest.MockManager)
  315. resourceAnalyzer = new(fakeResourceAnalyzer)
  316. fakeRuntimeService = critest.NewFakeRuntimeService()
  317. )
  318. infos := map[string]cadvisorapiv2.ContainerInfo{
  319. "/": getTestContainerInfo(seedRoot, "", "", ""),
  320. "/kubelet": getTestContainerInfo(seedKubelet, "", "", ""),
  321. "/system": getTestContainerInfo(seedMisc, "", "", ""),
  322. sandbox0.PodSandboxStatus.Id: getTestContainerInfo(seedSandbox0, pName0, sandbox0.PodSandboxStatus.Metadata.Namespace, leaky.PodInfraContainerName),
  323. sandbox0Cgroup: getTestContainerInfo(seedSandbox0, "", "", ""),
  324. container0.ContainerStatus.Id: getTestContainerInfo(seedContainer0, pName0, sandbox0.PodSandboxStatus.Metadata.Namespace, cName0),
  325. container1.ContainerStatus.Id: getTestContainerInfo(seedContainer1, pName0, sandbox0.PodSandboxStatus.Metadata.Namespace, cName1),
  326. sandbox1.PodSandboxStatus.Id: getTestContainerInfo(seedSandbox1, pName1, sandbox1.PodSandboxStatus.Metadata.Namespace, leaky.PodInfraContainerName),
  327. sandbox1Cgroup: getTestContainerInfo(seedSandbox1, "", "", ""),
  328. container2.ContainerStatus.Id: getTestContainerInfo(seedContainer2, pName1, sandbox1.PodSandboxStatus.Metadata.Namespace, cName2),
  329. sandbox2.PodSandboxStatus.Id: getTestContainerInfo(seedSandbox2, pName2, sandbox2.PodSandboxStatus.Metadata.Namespace, leaky.PodInfraContainerName),
  330. sandbox2Cgroup: getTestContainerInfo(seedSandbox2, "", "", ""),
  331. container4.ContainerStatus.Id: getTestContainerInfo(seedContainer3, pName2, sandbox2.PodSandboxStatus.Metadata.Namespace, cName3),
  332. sandbox3Cgroup: getTestContainerInfo(seedSandbox3, "", "", ""),
  333. }
  334. options := cadvisorapiv2.RequestOptions{
  335. IdType: cadvisorapiv2.TypeName,
  336. Count: 2,
  337. Recursive: true,
  338. }
  339. mockCadvisor.
  340. On("ContainerInfoV2", "/", options).Return(infos, nil)
  341. fakeRuntimeService.SetFakeSandboxes([]*critest.FakePodSandbox{
  342. sandbox0, sandbox1, sandbox2, sandbox3, sandbox4, sandbox5,
  343. })
  344. fakeRuntimeService.SetFakeContainers([]*critest.FakeContainer{
  345. container0, container1, container2, container3, container4, container5, container6, container7, container8,
  346. })
  347. fakeRuntimeService.SetFakeContainerStats([]*runtimeapi.ContainerStats{
  348. containerStats0, containerStats1, containerStats2, containerStats3, containerStats4, containerStats5, containerStats6, containerStats7, containerStats8,
  349. })
  350. ephemeralVolumes := makeFakeVolumeStats([]string{"ephVolume1, ephVolumes2"})
  351. persistentVolumes := makeFakeVolumeStats([]string{"persisVolume1, persisVolumes2"})
  352. resourceAnalyzer.podVolumeStats = serverstats.PodVolumeStats{
  353. EphemeralVolumes: ephemeralVolumes,
  354. PersistentVolumes: persistentVolumes,
  355. }
  356. provider := NewCRIStatsProvider(
  357. mockCadvisor,
  358. resourceAnalyzer,
  359. mockPodManager,
  360. mockRuntimeCache,
  361. fakeRuntimeService,
  362. nil,
  363. nil,
  364. &kubecontainertest.FakeOS{},
  365. )
  366. stats, err := provider.ListPodCPUAndMemoryStats()
  367. assert := assert.New(t)
  368. assert.NoError(err)
  369. assert.Equal(4, len(stats))
  370. podStatsMap := make(map[statsapi.PodReference]statsapi.PodStats)
  371. for _, s := range stats {
  372. podStatsMap[s.PodRef] = s
  373. }
  374. p0 := podStatsMap[statsapi.PodReference{Name: "sandbox0-name", UID: "sandbox0-uid", Namespace: "sandbox0-ns"}]
  375. assert.Equal(sandbox0.CreatedAt, p0.StartTime.UnixNano())
  376. assert.Equal(2, len(p0.Containers))
  377. assert.Nil(p0.EphemeralStorage)
  378. assert.Nil(p0.VolumeStats)
  379. assert.Nil(p0.Network)
  380. checkCRIPodCPUAndMemoryStats(assert, p0, infos[sandbox0Cgroup].Stats[0])
  381. containerStatsMap := make(map[string]statsapi.ContainerStats)
  382. for _, s := range p0.Containers {
  383. containerStatsMap[s.Name] = s
  384. }
  385. c0 := containerStatsMap[cName0]
  386. assert.Equal(container0.CreatedAt, c0.StartTime.UnixNano())
  387. checkCRICPUAndMemoryStats(assert, c0, infos[container0.ContainerStatus.Id].Stats[0])
  388. assert.Nil(c0.Rootfs)
  389. assert.Nil(c0.Logs)
  390. assert.Nil(c0.Accelerators)
  391. assert.Nil(c0.UserDefinedMetrics)
  392. c1 := containerStatsMap[cName1]
  393. assert.Equal(container1.CreatedAt, c1.StartTime.UnixNano())
  394. checkCRICPUAndMemoryStats(assert, c1, infos[container1.ContainerStatus.Id].Stats[0])
  395. assert.Nil(c1.Rootfs)
  396. assert.Nil(c1.Logs)
  397. assert.Nil(c1.Accelerators)
  398. assert.Nil(c1.UserDefinedMetrics)
  399. p1 := podStatsMap[statsapi.PodReference{Name: "sandbox1-name", UID: "sandbox1-uid", Namespace: "sandbox1-ns"}]
  400. assert.Equal(sandbox1.CreatedAt, p1.StartTime.UnixNano())
  401. assert.Equal(1, len(p1.Containers))
  402. assert.Nil(p1.EphemeralStorage)
  403. assert.Nil(p1.VolumeStats)
  404. assert.Nil(p1.Network)
  405. checkCRIPodCPUAndMemoryStats(assert, p1, infos[sandbox1Cgroup].Stats[0])
  406. c2 := p1.Containers[0]
  407. assert.Equal(cName2, c2.Name)
  408. assert.Equal(container2.CreatedAt, c2.StartTime.UnixNano())
  409. checkCRICPUAndMemoryStats(assert, c2, infos[container2.ContainerStatus.Id].Stats[0])
  410. assert.Nil(c2.Rootfs)
  411. assert.Nil(c2.Logs)
  412. assert.Nil(c2.Accelerators)
  413. assert.Nil(c2.UserDefinedMetrics)
  414. p2 := podStatsMap[statsapi.PodReference{Name: "sandbox2-name", UID: "sandbox2-uid", Namespace: "sandbox2-ns"}]
  415. assert.Equal(sandbox2.CreatedAt, p2.StartTime.UnixNano())
  416. assert.Equal(1, len(p2.Containers))
  417. assert.Nil(p2.EphemeralStorage)
  418. assert.Nil(p2.VolumeStats)
  419. assert.Nil(p2.Network)
  420. checkCRIPodCPUAndMemoryStats(assert, p2, infos[sandbox2Cgroup].Stats[0])
  421. c3 := p2.Containers[0]
  422. assert.Equal(cName3, c3.Name)
  423. assert.Equal(container4.CreatedAt, c3.StartTime.UnixNano())
  424. checkCRICPUAndMemoryStats(assert, c3, infos[container4.ContainerStatus.Id].Stats[0])
  425. assert.Nil(c2.Rootfs)
  426. assert.Nil(c2.Logs)
  427. assert.Nil(c2.Accelerators)
  428. assert.Nil(c2.UserDefinedMetrics)
  429. p3 := podStatsMap[statsapi.PodReference{Name: "sandbox3-name", UID: "sandbox3-uid", Namespace: "sandbox3-ns"}]
  430. assert.Equal(sandbox3.CreatedAt, p3.StartTime.UnixNano())
  431. assert.Equal(1, len(p3.Containers))
  432. c8 := p3.Containers[0]
  433. assert.Equal(cName8, c8.Name)
  434. assert.Equal(container8.CreatedAt, c8.StartTime.UnixNano())
  435. assert.NotNil(c8.CPU.Time)
  436. assert.NotNil(c8.Memory.Time)
  437. checkCRIPodCPUAndMemoryStats(assert, p3, infos[sandbox3Cgroup].Stats[0])
  438. mockCadvisor.AssertExpectations(t)
  439. }
  440. func TestCRIImagesFsStats(t *testing.T) {
  441. var (
  442. imageFsMountpoint = "/test/mount/point"
  443. imageFsInfo = getTestFsInfo(2000)
  444. imageFsUsage = makeFakeImageFsUsage(imageFsMountpoint)
  445. )
  446. var (
  447. mockCadvisor = new(cadvisortest.Mock)
  448. mockRuntimeCache = new(kubecontainertest.MockRuntimeCache)
  449. mockPodManager = new(kubepodtest.MockManager)
  450. resourceAnalyzer = new(fakeResourceAnalyzer)
  451. fakeRuntimeService = critest.NewFakeRuntimeService()
  452. fakeImageService = critest.NewFakeImageService()
  453. fakeLogStatsProvider = NewFakeLogMetricsService(nil)
  454. )
  455. mockCadvisor.On("GetDirFsInfo", imageFsMountpoint).Return(imageFsInfo, nil)
  456. fakeImageService.SetFakeFilesystemUsage([]*runtimeapi.FilesystemUsage{
  457. imageFsUsage,
  458. })
  459. provider := NewCRIStatsProvider(
  460. mockCadvisor,
  461. resourceAnalyzer,
  462. mockPodManager,
  463. mockRuntimeCache,
  464. fakeRuntimeService,
  465. fakeImageService,
  466. fakeLogStatsProvider,
  467. &kubecontainertest.FakeOS{},
  468. )
  469. stats, err := provider.ImageFsStats()
  470. assert := assert.New(t)
  471. assert.NoError(err)
  472. assert.Equal(imageFsUsage.Timestamp, stats.Time.UnixNano())
  473. assert.Equal(imageFsInfo.Available, *stats.AvailableBytes)
  474. assert.Equal(imageFsInfo.Capacity, *stats.CapacityBytes)
  475. assert.Equal(imageFsInfo.InodesFree, stats.InodesFree)
  476. assert.Equal(imageFsInfo.Inodes, stats.Inodes)
  477. assert.Equal(imageFsUsage.UsedBytes.Value, *stats.UsedBytes)
  478. assert.Equal(imageFsUsage.InodesUsed.Value, *stats.InodesUsed)
  479. mockCadvisor.AssertExpectations(t)
  480. }
  481. func makeFakePodSandbox(name, uid, namespace string, terminated bool) *critest.FakePodSandbox {
  482. p := &critest.FakePodSandbox{
  483. PodSandboxStatus: runtimeapi.PodSandboxStatus{
  484. Metadata: &runtimeapi.PodSandboxMetadata{
  485. Name: name,
  486. Uid: uid,
  487. Namespace: namespace,
  488. },
  489. State: runtimeapi.PodSandboxState_SANDBOX_READY,
  490. CreatedAt: time.Now().UnixNano(),
  491. },
  492. }
  493. if terminated {
  494. p.PodSandboxStatus.State = runtimeapi.PodSandboxState_SANDBOX_NOTREADY
  495. }
  496. p.PodSandboxStatus.Id = string(uuid.NewUUID())
  497. return p
  498. }
  499. func makeFakeContainer(sandbox *critest.FakePodSandbox, name string, attempt uint32, terminated bool) *critest.FakeContainer {
  500. sandboxID := sandbox.PodSandboxStatus.Id
  501. c := &critest.FakeContainer{
  502. SandboxID: sandboxID,
  503. ContainerStatus: runtimeapi.ContainerStatus{
  504. Metadata: &runtimeapi.ContainerMetadata{Name: name, Attempt: attempt},
  505. Image: &runtimeapi.ImageSpec{},
  506. ImageRef: "fake-image-ref",
  507. CreatedAt: time.Now().UnixNano(),
  508. },
  509. }
  510. c.ContainerStatus.Labels = map[string]string{
  511. "io.kubernetes.pod.name": sandbox.Metadata.Name,
  512. "io.kubernetes.pod.uid": sandbox.Metadata.Uid,
  513. "io.kubernetes.pod.namespace": sandbox.Metadata.Namespace,
  514. "io.kubernetes.container.name": name,
  515. }
  516. if terminated {
  517. c.ContainerStatus.State = runtimeapi.ContainerState_CONTAINER_EXITED
  518. } else {
  519. c.ContainerStatus.State = runtimeapi.ContainerState_CONTAINER_RUNNING
  520. }
  521. c.ContainerStatus.Id = string(uuid.NewUUID())
  522. return c
  523. }
  524. func makeFakeContainerStats(container *critest.FakeContainer, imageFsMountpoint string) *runtimeapi.ContainerStats {
  525. containerStats := &runtimeapi.ContainerStats{
  526. Attributes: &runtimeapi.ContainerAttributes{
  527. Id: container.ContainerStatus.Id,
  528. Metadata: container.ContainerStatus.Metadata,
  529. },
  530. WritableLayer: &runtimeapi.FilesystemUsage{
  531. Timestamp: time.Now().UnixNano(),
  532. FsId: &runtimeapi.FilesystemIdentifier{Mountpoint: imageFsMountpoint},
  533. UsedBytes: &runtimeapi.UInt64Value{Value: rand.Uint64() / 100},
  534. InodesUsed: &runtimeapi.UInt64Value{Value: rand.Uint64() / 100},
  535. },
  536. }
  537. if container.State == runtimeapi.ContainerState_CONTAINER_EXITED {
  538. containerStats.Cpu = nil
  539. containerStats.Memory = nil
  540. } else {
  541. containerStats.Cpu = &runtimeapi.CpuUsage{
  542. Timestamp: time.Now().UnixNano(),
  543. UsageCoreNanoSeconds: &runtimeapi.UInt64Value{Value: rand.Uint64()},
  544. }
  545. containerStats.Memory = &runtimeapi.MemoryUsage{
  546. Timestamp: time.Now().UnixNano(),
  547. WorkingSetBytes: &runtimeapi.UInt64Value{Value: rand.Uint64()},
  548. }
  549. }
  550. return containerStats
  551. }
  552. func makeFakeImageFsUsage(fsMountpoint string) *runtimeapi.FilesystemUsage {
  553. return &runtimeapi.FilesystemUsage{
  554. Timestamp: time.Now().UnixNano(),
  555. FsId: &runtimeapi.FilesystemIdentifier{Mountpoint: fsMountpoint},
  556. UsedBytes: &runtimeapi.UInt64Value{Value: rand.Uint64()},
  557. InodesUsed: &runtimeapi.UInt64Value{Value: rand.Uint64()},
  558. }
  559. }
  560. func makeFakeVolumeStats(volumeNames []string) []statsapi.VolumeStats {
  561. volumes := make([]statsapi.VolumeStats, len(volumeNames))
  562. availableBytes := rand.Uint64()
  563. capacityBytes := rand.Uint64()
  564. usedBytes := rand.Uint64() / 100
  565. inodes := rand.Uint64()
  566. inodesFree := rand.Uint64()
  567. inodesUsed := rand.Uint64() / 100
  568. for i, name := range volumeNames {
  569. fsStats := statsapi.FsStats{
  570. Time: metav1.NewTime(time.Now()),
  571. AvailableBytes: &availableBytes,
  572. CapacityBytes: &capacityBytes,
  573. UsedBytes: &usedBytes,
  574. Inodes: &inodes,
  575. InodesFree: &inodesFree,
  576. InodesUsed: &inodesUsed,
  577. }
  578. volumes[i] = statsapi.VolumeStats{
  579. FsStats: fsStats,
  580. Name: name,
  581. }
  582. }
  583. return volumes
  584. }
  585. func checkCRICPUAndMemoryStats(assert *assert.Assertions, actual statsapi.ContainerStats, cs *cadvisorapiv2.ContainerStats) {
  586. assert.Equal(cs.Timestamp.UnixNano(), actual.CPU.Time.UnixNano())
  587. assert.Equal(cs.Cpu.Usage.Total, *actual.CPU.UsageCoreNanoSeconds)
  588. assert.Equal(cs.CpuInst.Usage.Total, *actual.CPU.UsageNanoCores)
  589. assert.Equal(cs.Memory.Usage, *actual.Memory.UsageBytes)
  590. assert.Equal(cs.Memory.WorkingSet, *actual.Memory.WorkingSetBytes)
  591. assert.Equal(cs.Memory.RSS, *actual.Memory.RSSBytes)
  592. assert.Equal(cs.Memory.ContainerData.Pgfault, *actual.Memory.PageFaults)
  593. assert.Equal(cs.Memory.ContainerData.Pgmajfault, *actual.Memory.MajorPageFaults)
  594. }
  595. func checkCRIRootfsStats(assert *assert.Assertions, actual statsapi.ContainerStats, cs *runtimeapi.ContainerStats, imageFsInfo *cadvisorapiv2.FsInfo) {
  596. assert.Equal(cs.WritableLayer.Timestamp, actual.Rootfs.Time.UnixNano())
  597. if imageFsInfo != nil {
  598. assert.Equal(imageFsInfo.Available, *actual.Rootfs.AvailableBytes)
  599. assert.Equal(imageFsInfo.Capacity, *actual.Rootfs.CapacityBytes)
  600. assert.Equal(*imageFsInfo.InodesFree, *actual.Rootfs.InodesFree)
  601. assert.Equal(*imageFsInfo.Inodes, *actual.Rootfs.Inodes)
  602. } else {
  603. assert.Nil(actual.Rootfs.AvailableBytes)
  604. assert.Nil(actual.Rootfs.CapacityBytes)
  605. assert.Nil(actual.Rootfs.InodesFree)
  606. assert.Nil(actual.Rootfs.Inodes)
  607. }
  608. assert.Equal(cs.WritableLayer.UsedBytes.Value, *actual.Rootfs.UsedBytes)
  609. assert.Equal(cs.WritableLayer.InodesUsed.Value, *actual.Rootfs.InodesUsed)
  610. }
  611. func checkCRILogsStats(assert *assert.Assertions, actual statsapi.ContainerStats, rootFsInfo *cadvisorapiv2.FsInfo, logStats *volume.Metrics) {
  612. assert.Equal(rootFsInfo.Timestamp, actual.Logs.Time.Time)
  613. assert.Equal(rootFsInfo.Available, *actual.Logs.AvailableBytes)
  614. assert.Equal(rootFsInfo.Capacity, *actual.Logs.CapacityBytes)
  615. assert.Equal(*rootFsInfo.InodesFree, *actual.Logs.InodesFree)
  616. assert.Equal(*rootFsInfo.Inodes, *actual.Logs.Inodes)
  617. assert.Equal(uint64(logStats.Used.Value()), *actual.Logs.UsedBytes)
  618. assert.Equal(uint64(logStats.InodesUsed.Value()), *actual.Logs.InodesUsed)
  619. }
  620. func checkEphemeralStorageStats(assert *assert.Assertions,
  621. actual statsapi.PodStats,
  622. volumes []statsapi.VolumeStats,
  623. containers []*runtimeapi.ContainerStats,
  624. containerLogStats []*volume.Metrics,
  625. podLogStats *volume.Metrics) {
  626. var totalUsed, inodesUsed uint64
  627. for _, container := range containers {
  628. totalUsed = totalUsed + container.WritableLayer.UsedBytes.Value
  629. inodesUsed = inodesUsed + container.WritableLayer.InodesUsed.Value
  630. }
  631. for _, volume := range volumes {
  632. totalUsed = totalUsed + *volume.FsStats.UsedBytes
  633. inodesUsed = inodesUsed + *volume.FsStats.InodesUsed
  634. }
  635. for _, logStats := range containerLogStats {
  636. totalUsed = totalUsed + uint64(logStats.Used.Value())
  637. inodesUsed = inodesUsed + uint64(logStats.InodesUsed.Value())
  638. }
  639. if podLogStats != nil {
  640. totalUsed = totalUsed + uint64(podLogStats.Used.Value())
  641. inodesUsed = inodesUsed + uint64(podLogStats.InodesUsed.Value())
  642. }
  643. assert.Equal(int(totalUsed), int(*actual.EphemeralStorage.UsedBytes))
  644. assert.Equal(int(inodesUsed), int(*actual.EphemeralStorage.InodesUsed))
  645. }
  646. func checkCRINetworkStats(assert *assert.Assertions, actual *statsapi.NetworkStats, expected *cadvisorapiv2.NetworkStats) {
  647. assert.Equal(expected.Interfaces[0].RxBytes, *actual.RxBytes)
  648. assert.Equal(expected.Interfaces[0].RxErrors, *actual.RxErrors)
  649. assert.Equal(expected.Interfaces[0].TxBytes, *actual.TxBytes)
  650. assert.Equal(expected.Interfaces[0].TxErrors, *actual.TxErrors)
  651. }
  652. func checkCRIPodCPUAndMemoryStats(assert *assert.Assertions, actual statsapi.PodStats, cs *cadvisorapiv2.ContainerStats) {
  653. if runtime.GOOS != "linux" {
  654. return
  655. }
  656. assert.Equal(cs.Timestamp.UnixNano(), actual.CPU.Time.UnixNano())
  657. assert.Equal(cs.Cpu.Usage.Total, *actual.CPU.UsageCoreNanoSeconds)
  658. assert.Equal(cs.CpuInst.Usage.Total, *actual.CPU.UsageNanoCores)
  659. assert.Equal(cs.Memory.Usage, *actual.Memory.UsageBytes)
  660. assert.Equal(cs.Memory.WorkingSet, *actual.Memory.WorkingSetBytes)
  661. assert.Equal(cs.Memory.RSS, *actual.Memory.RSSBytes)
  662. assert.Equal(cs.Memory.ContainerData.Pgfault, *actual.Memory.PageFaults)
  663. assert.Equal(cs.Memory.ContainerData.Pgmajfault, *actual.Memory.MajorPageFaults)
  664. }
  665. func makeFakeLogStats(seed int) *volume.Metrics {
  666. m := &volume.Metrics{}
  667. m.Used = resource.NewQuantity(int64(seed+offsetUsage), resource.BinarySI)
  668. m.InodesUsed = resource.NewQuantity(int64(seed+offsetInodeUsage), resource.BinarySI)
  669. return m
  670. }
  671. func TestGetContainerUsageNanoCores(t *testing.T) {
  672. var value0 uint64
  673. var value1 uint64 = 10000000000
  674. // Test with a large container of 100+ CPUs
  675. var value2 uint64 = 188427786383
  676. tests := []struct {
  677. desc string
  678. cpuUsageCache map[string]*cpuUsageRecord
  679. stats *runtimeapi.ContainerStats
  680. expected *uint64
  681. }{
  682. {
  683. desc: "should return nil if stats is nil",
  684. cpuUsageCache: map[string]*cpuUsageRecord{},
  685. },
  686. {
  687. desc: "should return nil if cpu stats is nil",
  688. cpuUsageCache: map[string]*cpuUsageRecord{},
  689. stats: &runtimeapi.ContainerStats{
  690. Attributes: &runtimeapi.ContainerAttributes{
  691. Id: "1",
  692. },
  693. Cpu: nil,
  694. },
  695. },
  696. {
  697. desc: "should return nil if usageCoreNanoSeconds is nil",
  698. cpuUsageCache: map[string]*cpuUsageRecord{},
  699. stats: &runtimeapi.ContainerStats{
  700. Attributes: &runtimeapi.ContainerAttributes{
  701. Id: "1",
  702. },
  703. Cpu: &runtimeapi.CpuUsage{
  704. Timestamp: 1,
  705. UsageCoreNanoSeconds: nil,
  706. },
  707. },
  708. },
  709. {
  710. desc: "should return nil if cpu stats is not cached yet",
  711. cpuUsageCache: map[string]*cpuUsageRecord{},
  712. stats: &runtimeapi.ContainerStats{
  713. Attributes: &runtimeapi.ContainerAttributes{
  714. Id: "1",
  715. },
  716. Cpu: &runtimeapi.CpuUsage{
  717. Timestamp: 1,
  718. UsageCoreNanoSeconds: &runtimeapi.UInt64Value{
  719. Value: 10000000000,
  720. },
  721. },
  722. },
  723. },
  724. {
  725. desc: "should return zero value if cached cpu stats is equal to current value",
  726. stats: &runtimeapi.ContainerStats{
  727. Attributes: &runtimeapi.ContainerAttributes{
  728. Id: "1",
  729. },
  730. Cpu: &runtimeapi.CpuUsage{
  731. Timestamp: 1,
  732. UsageCoreNanoSeconds: &runtimeapi.UInt64Value{
  733. Value: 10000000000,
  734. },
  735. },
  736. },
  737. cpuUsageCache: map[string]*cpuUsageRecord{
  738. "1": {
  739. stats: &runtimeapi.CpuUsage{
  740. Timestamp: 0,
  741. UsageCoreNanoSeconds: &runtimeapi.UInt64Value{
  742. Value: 10000000000,
  743. },
  744. },
  745. },
  746. },
  747. expected: &value0,
  748. },
  749. {
  750. desc: "should return correct value if cached cpu stats is not equal to current value",
  751. stats: &runtimeapi.ContainerStats{
  752. Attributes: &runtimeapi.ContainerAttributes{
  753. Id: "1",
  754. },
  755. Cpu: &runtimeapi.CpuUsage{
  756. Timestamp: int64(time.Second / time.Nanosecond),
  757. UsageCoreNanoSeconds: &runtimeapi.UInt64Value{
  758. Value: 20000000000,
  759. },
  760. },
  761. },
  762. cpuUsageCache: map[string]*cpuUsageRecord{
  763. "1": {
  764. stats: &runtimeapi.CpuUsage{
  765. Timestamp: 0,
  766. UsageCoreNanoSeconds: &runtimeapi.UInt64Value{
  767. Value: 10000000000,
  768. },
  769. },
  770. },
  771. },
  772. expected: &value1,
  773. },
  774. {
  775. desc: "should return correct value if elapsed UsageCoreNanoSeconds exceeds 18446744073",
  776. stats: &runtimeapi.ContainerStats{
  777. Attributes: &runtimeapi.ContainerAttributes{
  778. Id: "1",
  779. },
  780. Cpu: &runtimeapi.CpuUsage{
  781. Timestamp: int64(time.Second / time.Nanosecond),
  782. UsageCoreNanoSeconds: &runtimeapi.UInt64Value{
  783. Value: 68172016162105,
  784. },
  785. },
  786. },
  787. cpuUsageCache: map[string]*cpuUsageRecord{
  788. "1": {
  789. stats: &runtimeapi.CpuUsage{
  790. Timestamp: 0,
  791. UsageCoreNanoSeconds: &runtimeapi.UInt64Value{
  792. Value: 67983588375722,
  793. },
  794. },
  795. },
  796. },
  797. expected: &value2,
  798. },
  799. {
  800. desc: "should return nil if cpuacct is reset to 0 in a live container",
  801. stats: &runtimeapi.ContainerStats{
  802. Attributes: &runtimeapi.ContainerAttributes{
  803. Id: "1",
  804. },
  805. Cpu: &runtimeapi.CpuUsage{
  806. Timestamp: 2,
  807. UsageCoreNanoSeconds: &runtimeapi.UInt64Value{
  808. Value: 0,
  809. },
  810. },
  811. },
  812. cpuUsageCache: map[string]*cpuUsageRecord{
  813. "1": {
  814. stats: &runtimeapi.CpuUsage{
  815. Timestamp: 1,
  816. UsageCoreNanoSeconds: &runtimeapi.UInt64Value{
  817. Value: 10000000000,
  818. },
  819. },
  820. },
  821. },
  822. expected: nil,
  823. },
  824. }
  825. for _, test := range tests {
  826. provider := &criStatsProvider{cpuUsageCache: test.cpuUsageCache}
  827. // Before the update, the cached value should be nil
  828. cached := provider.getContainerUsageNanoCores(test.stats)
  829. assert.Nil(t, cached)
  830. // Update the cache and get the latest value.
  831. real := provider.getAndUpdateContainerUsageNanoCores(test.stats)
  832. assert.Equal(t, test.expected, real, test.desc)
  833. // After the update, the cached value should be up-to-date
  834. cached = provider.getContainerUsageNanoCores(test.stats)
  835. assert.Equal(t, test.expected, cached, test.desc)
  836. }
  837. }