generic_test.go 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713
  1. /*
  2. Copyright 2015 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 pleg
  14. import (
  15. "errors"
  16. "fmt"
  17. "reflect"
  18. "sort"
  19. "strings"
  20. "testing"
  21. "time"
  22. "github.com/stretchr/testify/assert"
  23. "k8s.io/apimachinery/pkg/types"
  24. "k8s.io/apimachinery/pkg/util/clock"
  25. "k8s.io/apimachinery/pkg/util/diff"
  26. "k8s.io/component-base/metrics/testutil"
  27. kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
  28. containertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
  29. "k8s.io/kubernetes/pkg/kubelet/metrics"
  30. )
  31. const (
  32. testContainerRuntimeType = "fooRuntime"
  33. // largeChannelCap is a large enough capacity to hold all events in a single test.
  34. largeChannelCap = 100
  35. )
  36. type TestGenericPLEG struct {
  37. pleg *GenericPLEG
  38. runtime *containertest.FakeRuntime
  39. clock *clock.FakeClock
  40. }
  41. func newTestGenericPLEG() *TestGenericPLEG {
  42. return newTestGenericPLEGWithChannelSize(largeChannelCap)
  43. }
  44. func newTestGenericPLEGWithChannelSize(eventChannelCap int) *TestGenericPLEG {
  45. fakeRuntime := &containertest.FakeRuntime{}
  46. clock := clock.NewFakeClock(time.Time{})
  47. // The channel capacity should be large enough to hold all events in a
  48. // single test.
  49. pleg := &GenericPLEG{
  50. relistPeriod: time.Hour,
  51. runtime: fakeRuntime,
  52. eventChannel: make(chan *PodLifecycleEvent, eventChannelCap),
  53. podRecords: make(podRecords),
  54. clock: clock,
  55. }
  56. return &TestGenericPLEG{pleg: pleg, runtime: fakeRuntime, clock: clock}
  57. }
  58. func getEventsFromChannel(ch <-chan *PodLifecycleEvent) []*PodLifecycleEvent {
  59. events := []*PodLifecycleEvent{}
  60. for len(ch) > 0 {
  61. e := <-ch
  62. events = append(events, e)
  63. }
  64. return events
  65. }
  66. func createTestContainer(ID string, state kubecontainer.ContainerState) *kubecontainer.Container {
  67. return &kubecontainer.Container{
  68. ID: kubecontainer.ContainerID{Type: testContainerRuntimeType, ID: ID},
  69. State: state,
  70. }
  71. }
  72. type sortableEvents []*PodLifecycleEvent
  73. func (a sortableEvents) Len() int { return len(a) }
  74. func (a sortableEvents) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
  75. func (a sortableEvents) Less(i, j int) bool {
  76. if a[i].ID != a[j].ID {
  77. return a[i].ID < a[j].ID
  78. }
  79. return a[i].Data.(string) < a[j].Data.(string)
  80. }
  81. func verifyEvents(t *testing.T, expected, actual []*PodLifecycleEvent) {
  82. sort.Sort(sortableEvents(expected))
  83. sort.Sort(sortableEvents(actual))
  84. if !reflect.DeepEqual(expected, actual) {
  85. t.Errorf("Actual events differ from the expected; diff:\n %v", diff.ObjectDiff(expected, actual))
  86. }
  87. }
  88. func TestRelisting(t *testing.T) {
  89. testPleg := newTestGenericPLEG()
  90. pleg, runtime := testPleg.pleg, testPleg.runtime
  91. ch := pleg.Watch()
  92. // The first relist should send a PodSync event to each pod.
  93. runtime.AllPodList = []*containertest.FakePod{
  94. {Pod: &kubecontainer.Pod{
  95. ID: "1234",
  96. Containers: []*kubecontainer.Container{
  97. createTestContainer("c1", kubecontainer.ContainerStateExited),
  98. createTestContainer("c2", kubecontainer.ContainerStateRunning),
  99. createTestContainer("c3", kubecontainer.ContainerStateUnknown),
  100. },
  101. }},
  102. {Pod: &kubecontainer.Pod{
  103. ID: "4567",
  104. Containers: []*kubecontainer.Container{
  105. createTestContainer("c1", kubecontainer.ContainerStateExited),
  106. },
  107. }},
  108. }
  109. pleg.relist()
  110. // Report every running/exited container if we see them for the first time.
  111. expected := []*PodLifecycleEvent{
  112. {ID: "1234", Type: ContainerStarted, Data: "c2"},
  113. {ID: "4567", Type: ContainerDied, Data: "c1"},
  114. {ID: "1234", Type: ContainerDied, Data: "c1"},
  115. }
  116. actual := getEventsFromChannel(ch)
  117. verifyEvents(t, expected, actual)
  118. // The second relist should not send out any event because no container has
  119. // changed.
  120. pleg.relist()
  121. verifyEvents(t, expected, actual)
  122. runtime.AllPodList = []*containertest.FakePod{
  123. {Pod: &kubecontainer.Pod{
  124. ID: "1234",
  125. Containers: []*kubecontainer.Container{
  126. createTestContainer("c2", kubecontainer.ContainerStateExited),
  127. createTestContainer("c3", kubecontainer.ContainerStateRunning),
  128. },
  129. }},
  130. {Pod: &kubecontainer.Pod{
  131. ID: "4567",
  132. Containers: []*kubecontainer.Container{
  133. createTestContainer("c4", kubecontainer.ContainerStateRunning),
  134. },
  135. }},
  136. }
  137. pleg.relist()
  138. // Only report containers that transitioned to running or exited status.
  139. expected = []*PodLifecycleEvent{
  140. {ID: "1234", Type: ContainerRemoved, Data: "c1"},
  141. {ID: "1234", Type: ContainerDied, Data: "c2"},
  142. {ID: "1234", Type: ContainerStarted, Data: "c3"},
  143. {ID: "4567", Type: ContainerRemoved, Data: "c1"},
  144. {ID: "4567", Type: ContainerStarted, Data: "c4"},
  145. }
  146. actual = getEventsFromChannel(ch)
  147. verifyEvents(t, expected, actual)
  148. }
  149. // TestEventChannelFull test when channel is full, the events will be discard.
  150. func TestEventChannelFull(t *testing.T) {
  151. testPleg := newTestGenericPLEGWithChannelSize(4)
  152. pleg, runtime := testPleg.pleg, testPleg.runtime
  153. ch := pleg.Watch()
  154. // The first relist should send a PodSync event to each pod.
  155. runtime.AllPodList = []*containertest.FakePod{
  156. {Pod: &kubecontainer.Pod{
  157. ID: "1234",
  158. Containers: []*kubecontainer.Container{
  159. createTestContainer("c1", kubecontainer.ContainerStateExited),
  160. createTestContainer("c2", kubecontainer.ContainerStateRunning),
  161. createTestContainer("c3", kubecontainer.ContainerStateUnknown),
  162. },
  163. }},
  164. {Pod: &kubecontainer.Pod{
  165. ID: "4567",
  166. Containers: []*kubecontainer.Container{
  167. createTestContainer("c1", kubecontainer.ContainerStateExited),
  168. },
  169. }},
  170. }
  171. pleg.relist()
  172. // Report every running/exited container if we see them for the first time.
  173. expected := []*PodLifecycleEvent{
  174. {ID: "1234", Type: ContainerStarted, Data: "c2"},
  175. {ID: "4567", Type: ContainerDied, Data: "c1"},
  176. {ID: "1234", Type: ContainerDied, Data: "c1"},
  177. }
  178. actual := getEventsFromChannel(ch)
  179. verifyEvents(t, expected, actual)
  180. runtime.AllPodList = []*containertest.FakePod{
  181. {Pod: &kubecontainer.Pod{
  182. ID: "1234",
  183. Containers: []*kubecontainer.Container{
  184. createTestContainer("c2", kubecontainer.ContainerStateExited),
  185. createTestContainer("c3", kubecontainer.ContainerStateRunning),
  186. },
  187. }},
  188. {Pod: &kubecontainer.Pod{
  189. ID: "4567",
  190. Containers: []*kubecontainer.Container{
  191. createTestContainer("c4", kubecontainer.ContainerStateRunning),
  192. },
  193. }},
  194. }
  195. pleg.relist()
  196. allEvents := []*PodLifecycleEvent{
  197. {ID: "1234", Type: ContainerRemoved, Data: "c1"},
  198. {ID: "1234", Type: ContainerDied, Data: "c2"},
  199. {ID: "1234", Type: ContainerStarted, Data: "c3"},
  200. {ID: "4567", Type: ContainerRemoved, Data: "c1"},
  201. {ID: "4567", Type: ContainerStarted, Data: "c4"},
  202. }
  203. // event channel is full, discard events
  204. actual = getEventsFromChannel(ch)
  205. assert.True(t, len(actual) == 4, "channel length should be 4")
  206. assert.Subsetf(t, allEvents, actual, "actual events should in all events")
  207. }
  208. func TestDetectingContainerDeaths(t *testing.T) {
  209. // Vary the number of relists after the container started and before the
  210. // container died to account for the changes in pleg's internal states.
  211. testReportMissingContainers(t, 1)
  212. testReportMissingPods(t, 1)
  213. testReportMissingContainers(t, 3)
  214. testReportMissingPods(t, 3)
  215. }
  216. func testReportMissingContainers(t *testing.T, numRelists int) {
  217. testPleg := newTestGenericPLEG()
  218. pleg, runtime := testPleg.pleg, testPleg.runtime
  219. ch := pleg.Watch()
  220. runtime.AllPodList = []*containertest.FakePod{
  221. {Pod: &kubecontainer.Pod{
  222. ID: "1234",
  223. Containers: []*kubecontainer.Container{
  224. createTestContainer("c1", kubecontainer.ContainerStateRunning),
  225. createTestContainer("c2", kubecontainer.ContainerStateRunning),
  226. createTestContainer("c3", kubecontainer.ContainerStateExited),
  227. },
  228. }},
  229. }
  230. // Relist and drain the events from the channel.
  231. for i := 0; i < numRelists; i++ {
  232. pleg.relist()
  233. getEventsFromChannel(ch)
  234. }
  235. // Container c2 was stopped and removed between relists. We should report
  236. // the event. The exited container c3 was garbage collected (i.e., removed)
  237. // between relists. We should ignore that event.
  238. runtime.AllPodList = []*containertest.FakePod{
  239. {Pod: &kubecontainer.Pod{
  240. ID: "1234",
  241. Containers: []*kubecontainer.Container{
  242. createTestContainer("c1", kubecontainer.ContainerStateRunning),
  243. },
  244. }},
  245. }
  246. pleg.relist()
  247. expected := []*PodLifecycleEvent{
  248. {ID: "1234", Type: ContainerDied, Data: "c2"},
  249. {ID: "1234", Type: ContainerRemoved, Data: "c2"},
  250. {ID: "1234", Type: ContainerRemoved, Data: "c3"},
  251. }
  252. actual := getEventsFromChannel(ch)
  253. verifyEvents(t, expected, actual)
  254. }
  255. func testReportMissingPods(t *testing.T, numRelists int) {
  256. testPleg := newTestGenericPLEG()
  257. pleg, runtime := testPleg.pleg, testPleg.runtime
  258. ch := pleg.Watch()
  259. runtime.AllPodList = []*containertest.FakePod{
  260. {Pod: &kubecontainer.Pod{
  261. ID: "1234",
  262. Containers: []*kubecontainer.Container{
  263. createTestContainer("c2", kubecontainer.ContainerStateRunning),
  264. },
  265. }},
  266. }
  267. // Relist and drain the events from the channel.
  268. for i := 0; i < numRelists; i++ {
  269. pleg.relist()
  270. getEventsFromChannel(ch)
  271. }
  272. // Container c2 was stopped and removed between relists. We should report
  273. // the event.
  274. runtime.AllPodList = []*containertest.FakePod{}
  275. pleg.relist()
  276. expected := []*PodLifecycleEvent{
  277. {ID: "1234", Type: ContainerDied, Data: "c2"},
  278. {ID: "1234", Type: ContainerRemoved, Data: "c2"},
  279. }
  280. actual := getEventsFromChannel(ch)
  281. verifyEvents(t, expected, actual)
  282. }
  283. func newTestGenericPLEGWithRuntimeMock() (*GenericPLEG, *containertest.Mock) {
  284. runtimeMock := &containertest.Mock{}
  285. pleg := &GenericPLEG{
  286. relistPeriod: time.Hour,
  287. runtime: runtimeMock,
  288. eventChannel: make(chan *PodLifecycleEvent, 100),
  289. podRecords: make(podRecords),
  290. cache: kubecontainer.NewCache(),
  291. clock: clock.RealClock{},
  292. }
  293. return pleg, runtimeMock
  294. }
  295. func createTestPodsStatusesAndEvents(num int) ([]*kubecontainer.Pod, []*kubecontainer.PodStatus, []*PodLifecycleEvent) {
  296. var pods []*kubecontainer.Pod
  297. var statuses []*kubecontainer.PodStatus
  298. var events []*PodLifecycleEvent
  299. for i := 0; i < num; i++ {
  300. id := types.UID(fmt.Sprintf("test-pod-%d", i))
  301. cState := kubecontainer.ContainerStateRunning
  302. container := createTestContainer(fmt.Sprintf("c%d", i), cState)
  303. pod := &kubecontainer.Pod{
  304. ID: id,
  305. Containers: []*kubecontainer.Container{container},
  306. }
  307. status := &kubecontainer.PodStatus{
  308. ID: id,
  309. ContainerStatuses: []*kubecontainer.ContainerStatus{{ID: container.ID, State: cState}},
  310. }
  311. event := &PodLifecycleEvent{ID: pod.ID, Type: ContainerStarted, Data: container.ID.ID}
  312. pods = append(pods, pod)
  313. statuses = append(statuses, status)
  314. events = append(events, event)
  315. }
  316. return pods, statuses, events
  317. }
  318. func TestRelistWithCache(t *testing.T) {
  319. pleg, runtimeMock := newTestGenericPLEGWithRuntimeMock()
  320. ch := pleg.Watch()
  321. pods, statuses, events := createTestPodsStatusesAndEvents(2)
  322. runtimeMock.On("GetPods", true).Return(pods, nil)
  323. runtimeMock.On("GetPodStatus", pods[0].ID, "", "").Return(statuses[0], nil).Once()
  324. // Inject an error when querying runtime for the pod status for pods[1].
  325. statusErr := fmt.Errorf("unable to get status")
  326. runtimeMock.On("GetPodStatus", pods[1].ID, "", "").Return(&kubecontainer.PodStatus{}, statusErr).Once()
  327. pleg.relist()
  328. actualEvents := getEventsFromChannel(ch)
  329. cases := []struct {
  330. pod *kubecontainer.Pod
  331. status *kubecontainer.PodStatus
  332. error error
  333. }{
  334. {pod: pods[0], status: statuses[0], error: nil},
  335. {pod: pods[1], status: &kubecontainer.PodStatus{}, error: statusErr},
  336. }
  337. for i, c := range cases {
  338. testStr := fmt.Sprintf("test[%d]", i)
  339. actualStatus, actualErr := pleg.cache.Get(c.pod.ID)
  340. assert.Equal(t, c.status, actualStatus, testStr)
  341. assert.Equal(t, c.error, actualErr, testStr)
  342. }
  343. // pleg should not generate any event for pods[1] because of the error.
  344. assert.Exactly(t, []*PodLifecycleEvent{events[0]}, actualEvents)
  345. // Return normal status for pods[1].
  346. runtimeMock.On("GetPodStatus", pods[1].ID, "", "").Return(statuses[1], nil).Once()
  347. pleg.relist()
  348. actualEvents = getEventsFromChannel(ch)
  349. cases = []struct {
  350. pod *kubecontainer.Pod
  351. status *kubecontainer.PodStatus
  352. error error
  353. }{
  354. {pod: pods[0], status: statuses[0], error: nil},
  355. {pod: pods[1], status: statuses[1], error: nil},
  356. }
  357. for i, c := range cases {
  358. testStr := fmt.Sprintf("test[%d]", i)
  359. actualStatus, actualErr := pleg.cache.Get(c.pod.ID)
  360. assert.Equal(t, c.status, actualStatus, testStr)
  361. assert.Equal(t, c.error, actualErr, testStr)
  362. }
  363. // Now that we are able to query status for pods[1], pleg should generate an event.
  364. assert.Exactly(t, []*PodLifecycleEvent{events[1]}, actualEvents)
  365. }
  366. func TestRemoveCacheEntry(t *testing.T) {
  367. pleg, runtimeMock := newTestGenericPLEGWithRuntimeMock()
  368. pods, statuses, _ := createTestPodsStatusesAndEvents(1)
  369. runtimeMock.On("GetPods", true).Return(pods, nil).Once()
  370. runtimeMock.On("GetPodStatus", pods[0].ID, "", "").Return(statuses[0], nil).Once()
  371. // Does a relist to populate the cache.
  372. pleg.relist()
  373. // Delete the pod from runtime. Verify that the cache entry has been
  374. // removed after relisting.
  375. runtimeMock.On("GetPods", true).Return([]*kubecontainer.Pod{}, nil).Once()
  376. pleg.relist()
  377. actualStatus, actualErr := pleg.cache.Get(pods[0].ID)
  378. assert.Equal(t, &kubecontainer.PodStatus{ID: pods[0].ID}, actualStatus)
  379. assert.Equal(t, nil, actualErr)
  380. }
  381. func TestHealthy(t *testing.T) {
  382. testPleg := newTestGenericPLEG()
  383. // pleg should initially be unhealthy
  384. pleg, _, clock := testPleg.pleg, testPleg.runtime, testPleg.clock
  385. ok, _ := pleg.Healthy()
  386. assert.False(t, ok, "pleg should be unhealthy")
  387. // Advance the clock without any relisting.
  388. clock.Step(time.Minute * 10)
  389. ok, _ = pleg.Healthy()
  390. assert.False(t, ok, "pleg should be unhealthy")
  391. // Relist and than advance the time by 1 minute. pleg should be healthy
  392. // because this is within the allowed limit.
  393. pleg.relist()
  394. clock.Step(time.Minute * 1)
  395. ok, _ = pleg.Healthy()
  396. assert.True(t, ok, "pleg should be healthy")
  397. // Advance by relistThreshold without any relisting. pleg should be unhealthy
  398. // because it has been longer than relistThreshold since a relist occurred.
  399. clock.Step(relistThreshold)
  400. ok, _ = pleg.Healthy()
  401. assert.False(t, ok, "pleg should be unhealthy")
  402. }
  403. func TestRelistWithReinspection(t *testing.T) {
  404. pleg, runtimeMock := newTestGenericPLEGWithRuntimeMock()
  405. ch := pleg.Watch()
  406. infraContainer := createTestContainer("infra", kubecontainer.ContainerStateRunning)
  407. podID := types.UID("test-pod")
  408. pods := []*kubecontainer.Pod{{
  409. ID: podID,
  410. Containers: []*kubecontainer.Container{infraContainer},
  411. }}
  412. runtimeMock.On("GetPods", true).Return(pods, nil).Once()
  413. goodStatus := &kubecontainer.PodStatus{
  414. ID: podID,
  415. ContainerStatuses: []*kubecontainer.ContainerStatus{{ID: infraContainer.ID, State: infraContainer.State}},
  416. }
  417. runtimeMock.On("GetPodStatus", podID, "", "").Return(goodStatus, nil).Once()
  418. goodEvent := &PodLifecycleEvent{ID: podID, Type: ContainerStarted, Data: infraContainer.ID.ID}
  419. // listing 1 - everything ok, infra container set up for pod
  420. pleg.relist()
  421. actualEvents := getEventsFromChannel(ch)
  422. actualStatus, actualErr := pleg.cache.Get(podID)
  423. assert.Equal(t, goodStatus, actualStatus)
  424. assert.Equal(t, nil, actualErr)
  425. assert.Exactly(t, []*PodLifecycleEvent{goodEvent}, actualEvents)
  426. // listing 2 - pretend runtime was in the middle of creating the non-infra container for the pod
  427. // and return an error during inspection
  428. transientContainer := createTestContainer("transient", kubecontainer.ContainerStateUnknown)
  429. podsWithTransientContainer := []*kubecontainer.Pod{{
  430. ID: podID,
  431. Containers: []*kubecontainer.Container{infraContainer, transientContainer},
  432. }}
  433. runtimeMock.On("GetPods", true).Return(podsWithTransientContainer, nil).Once()
  434. badStatus := &kubecontainer.PodStatus{
  435. ID: podID,
  436. ContainerStatuses: []*kubecontainer.ContainerStatus{},
  437. }
  438. runtimeMock.On("GetPodStatus", podID, "", "").Return(badStatus, errors.New("inspection error")).Once()
  439. pleg.relist()
  440. actualEvents = getEventsFromChannel(ch)
  441. actualStatus, actualErr = pleg.cache.Get(podID)
  442. assert.Equal(t, badStatus, actualStatus)
  443. assert.Equal(t, errors.New("inspection error"), actualErr)
  444. assert.Exactly(t, []*PodLifecycleEvent{}, actualEvents)
  445. // listing 3 - pretend the transient container has now disappeared, leaving just the infra
  446. // container. Make sure the pod is reinspected for its status and the cache is updated.
  447. runtimeMock.On("GetPods", true).Return(pods, nil).Once()
  448. runtimeMock.On("GetPodStatus", podID, "", "").Return(goodStatus, nil).Once()
  449. pleg.relist()
  450. actualEvents = getEventsFromChannel(ch)
  451. actualStatus, actualErr = pleg.cache.Get(podID)
  452. assert.Equal(t, goodStatus, actualStatus)
  453. assert.Equal(t, nil, actualErr)
  454. // no events are expected because relist #1 set the old pod record which has the infra container
  455. // running. relist #2 had the inspection error and therefore didn't modify either old or new.
  456. // relist #3 forced the reinspection of the pod to retrieve its status, but because the list of
  457. // containers was the same as relist #1, nothing "changed", so there are no new events.
  458. assert.Exactly(t, []*PodLifecycleEvent{}, actualEvents)
  459. }
  460. // Test detecting sandbox state changes.
  461. func TestRelistingWithSandboxes(t *testing.T) {
  462. testPleg := newTestGenericPLEG()
  463. pleg, runtime := testPleg.pleg, testPleg.runtime
  464. ch := pleg.Watch()
  465. // The first relist should send a PodSync event to each pod.
  466. runtime.AllPodList = []*containertest.FakePod{
  467. {Pod: &kubecontainer.Pod{
  468. ID: "1234",
  469. Sandboxes: []*kubecontainer.Container{
  470. createTestContainer("c1", kubecontainer.ContainerStateExited),
  471. createTestContainer("c2", kubecontainer.ContainerStateRunning),
  472. createTestContainer("c3", kubecontainer.ContainerStateUnknown),
  473. },
  474. }},
  475. {Pod: &kubecontainer.Pod{
  476. ID: "4567",
  477. Sandboxes: []*kubecontainer.Container{
  478. createTestContainer("c1", kubecontainer.ContainerStateExited),
  479. },
  480. }},
  481. }
  482. pleg.relist()
  483. // Report every running/exited container if we see them for the first time.
  484. expected := []*PodLifecycleEvent{
  485. {ID: "1234", Type: ContainerStarted, Data: "c2"},
  486. {ID: "4567", Type: ContainerDied, Data: "c1"},
  487. {ID: "1234", Type: ContainerDied, Data: "c1"},
  488. }
  489. actual := getEventsFromChannel(ch)
  490. verifyEvents(t, expected, actual)
  491. // The second relist should not send out any event because no container has
  492. // changed.
  493. pleg.relist()
  494. verifyEvents(t, expected, actual)
  495. runtime.AllPodList = []*containertest.FakePod{
  496. {Pod: &kubecontainer.Pod{
  497. ID: "1234",
  498. Sandboxes: []*kubecontainer.Container{
  499. createTestContainer("c2", kubecontainer.ContainerStateExited),
  500. createTestContainer("c3", kubecontainer.ContainerStateRunning),
  501. },
  502. }},
  503. {Pod: &kubecontainer.Pod{
  504. ID: "4567",
  505. Sandboxes: []*kubecontainer.Container{
  506. createTestContainer("c4", kubecontainer.ContainerStateRunning),
  507. },
  508. }},
  509. }
  510. pleg.relist()
  511. // Only report containers that transitioned to running or exited status.
  512. expected = []*PodLifecycleEvent{
  513. {ID: "1234", Type: ContainerRemoved, Data: "c1"},
  514. {ID: "1234", Type: ContainerDied, Data: "c2"},
  515. {ID: "1234", Type: ContainerStarted, Data: "c3"},
  516. {ID: "4567", Type: ContainerRemoved, Data: "c1"},
  517. {ID: "4567", Type: ContainerStarted, Data: "c4"},
  518. }
  519. actual = getEventsFromChannel(ch)
  520. verifyEvents(t, expected, actual)
  521. }
  522. func TestRelistIPChange(t *testing.T) {
  523. testCases := []struct {
  524. name string
  525. podID string
  526. podIPs []string
  527. }{
  528. {
  529. name: "test-0",
  530. podID: "test-pod-0",
  531. podIPs: []string{"192.168.1.5"},
  532. },
  533. {
  534. name: "tets-1",
  535. podID: "test-pod-1",
  536. podIPs: []string{"192.168.1.5/24", "2000::"},
  537. },
  538. }
  539. for _, tc := range testCases {
  540. pleg, runtimeMock := newTestGenericPLEGWithRuntimeMock()
  541. ch := pleg.Watch()
  542. id := types.UID(tc.podID)
  543. cState := kubecontainer.ContainerStateRunning
  544. container := createTestContainer("c0", cState)
  545. pod := &kubecontainer.Pod{
  546. ID: id,
  547. Containers: []*kubecontainer.Container{container},
  548. }
  549. status := &kubecontainer.PodStatus{
  550. ID: id,
  551. IPs: tc.podIPs,
  552. ContainerStatuses: []*kubecontainer.ContainerStatus{{ID: container.ID, State: cState}},
  553. }
  554. event := &PodLifecycleEvent{ID: pod.ID, Type: ContainerStarted, Data: container.ID.ID}
  555. runtimeMock.On("GetPods", true).Return([]*kubecontainer.Pod{pod}, nil).Once()
  556. runtimeMock.On("GetPodStatus", pod.ID, "", "").Return(status, nil).Once()
  557. pleg.relist()
  558. actualEvents := getEventsFromChannel(ch)
  559. actualStatus, actualErr := pleg.cache.Get(pod.ID)
  560. assert.Equal(t, status, actualStatus, tc.name)
  561. assert.Nil(t, actualErr, tc.name)
  562. assert.Exactly(t, []*PodLifecycleEvent{event}, actualEvents)
  563. // Clear the IP address and mark the container terminated
  564. container = createTestContainer("c0", kubecontainer.ContainerStateExited)
  565. pod = &kubecontainer.Pod{
  566. ID: id,
  567. Containers: []*kubecontainer.Container{container},
  568. }
  569. status = &kubecontainer.PodStatus{
  570. ID: id,
  571. ContainerStatuses: []*kubecontainer.ContainerStatus{{ID: container.ID, State: kubecontainer.ContainerStateExited}},
  572. }
  573. event = &PodLifecycleEvent{ID: pod.ID, Type: ContainerDied, Data: container.ID.ID}
  574. runtimeMock.On("GetPods", true).Return([]*kubecontainer.Pod{pod}, nil).Once()
  575. runtimeMock.On("GetPodStatus", pod.ID, "", "").Return(status, nil).Once()
  576. pleg.relist()
  577. actualEvents = getEventsFromChannel(ch)
  578. actualStatus, actualErr = pleg.cache.Get(pod.ID)
  579. // Must copy status to compare since its pointer gets passed through all
  580. // the way to the event
  581. statusCopy := *status
  582. statusCopy.IPs = tc.podIPs
  583. assert.Equal(t, &statusCopy, actualStatus, tc.name)
  584. assert.Nil(t, actualErr, tc.name)
  585. assert.Exactly(t, []*PodLifecycleEvent{event}, actualEvents)
  586. }
  587. }
  588. func TestRunningPodAndContainerCount(t *testing.T) {
  589. fakeRuntime := &containertest.FakeRuntime{}
  590. runtimeCache, _ := kubecontainer.NewRuntimeCache(fakeRuntime)
  591. metrics.Register(runtimeCache)
  592. testPleg := newTestGenericPLEG()
  593. pleg, runtime := testPleg.pleg, testPleg.runtime
  594. runtime.AllPodList = []*containertest.FakePod{
  595. {Pod: &kubecontainer.Pod{
  596. ID: "1234",
  597. Containers: []*kubecontainer.Container{
  598. createTestContainer("c1", kubecontainer.ContainerStateRunning),
  599. createTestContainer("c2", kubecontainer.ContainerStateUnknown),
  600. createTestContainer("c3", kubecontainer.ContainerStateUnknown),
  601. },
  602. }},
  603. {Pod: &kubecontainer.Pod{
  604. ID: "4567",
  605. Containers: []*kubecontainer.Container{
  606. createTestContainer("c1", kubecontainer.ContainerStateExited),
  607. },
  608. }},
  609. }
  610. pleg.relist()
  611. tests := []struct {
  612. name string
  613. metricsName string
  614. wants string
  615. }{
  616. {
  617. name: "test container count",
  618. metricsName: "kubelet_running_container_count",
  619. wants: `
  620. # HELP kubelet_running_container_count [ALPHA] Number of containers currently running
  621. # TYPE kubelet_running_container_count gauge
  622. kubelet_running_container_count{container_state="exited"} 1
  623. kubelet_running_container_count{container_state="running"} 1
  624. kubelet_running_container_count{container_state="unknown"} 2
  625. `,
  626. },
  627. {
  628. name: "test pod count",
  629. metricsName: "kubelet_running_pod_count",
  630. wants: `
  631. # HELP kubelet_running_pod_count [ALPHA] Number of pods currently running
  632. # TYPE kubelet_running_pod_count gauge
  633. kubelet_running_pod_count 2
  634. `,
  635. },
  636. }
  637. for _, test := range tests {
  638. tc := test
  639. t.Run(tc.name, func(t *testing.T) {
  640. if err := testutil.GatherAndCompare(metrics.GetGather(), strings.NewReader(tc.wants), tc.metricsName); err != nil {
  641. t.Fatal(err)
  642. }
  643. })
  644. }
  645. }