pod_workers_test.go 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. /*
  2. Copyright 2014 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 kubelet
  14. import (
  15. "reflect"
  16. "sync"
  17. "testing"
  18. "time"
  19. "k8s.io/api/core/v1"
  20. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  21. "k8s.io/apimachinery/pkg/types"
  22. "k8s.io/apimachinery/pkg/util/clock"
  23. "k8s.io/client-go/tools/record"
  24. kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
  25. containertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
  26. kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
  27. "k8s.io/kubernetes/pkg/kubelet/util/queue"
  28. )
  29. // fakePodWorkers runs sync pod function in serial, so we can have
  30. // deterministic behaviour in testing.
  31. type fakePodWorkers struct {
  32. syncPodFn syncPodFnType
  33. cache kubecontainer.Cache
  34. t TestingInterface
  35. }
  36. func (f *fakePodWorkers) UpdatePod(options *UpdatePodOptions) {
  37. status, err := f.cache.Get(options.Pod.UID)
  38. if err != nil {
  39. f.t.Errorf("Unexpected error: %v", err)
  40. }
  41. if err := f.syncPodFn(syncPodOptions{
  42. mirrorPod: options.MirrorPod,
  43. pod: options.Pod,
  44. podStatus: status,
  45. updateType: options.UpdateType,
  46. killPodOptions: options.KillPodOptions,
  47. }); err != nil {
  48. f.t.Errorf("Unexpected error: %v", err)
  49. }
  50. }
  51. func (f *fakePodWorkers) ForgetNonExistingPodWorkers(desiredPods map[types.UID]empty) {}
  52. func (f *fakePodWorkers) ForgetWorker(uid types.UID) {}
  53. type TestingInterface interface {
  54. Errorf(format string, args ...interface{})
  55. }
  56. func newPod(uid, name string) *v1.Pod {
  57. return &v1.Pod{
  58. ObjectMeta: metav1.ObjectMeta{
  59. UID: types.UID(uid),
  60. Name: name,
  61. },
  62. }
  63. }
  64. // syncPodRecord is a record of a sync pod call
  65. type syncPodRecord struct {
  66. name string
  67. updateType kubetypes.SyncPodType
  68. }
  69. func createPodWorkers() (*podWorkers, map[types.UID][]syncPodRecord) {
  70. lock := sync.Mutex{}
  71. processed := make(map[types.UID][]syncPodRecord)
  72. fakeRecorder := &record.FakeRecorder{}
  73. fakeRuntime := &containertest.FakeRuntime{}
  74. fakeCache := containertest.NewFakeCache(fakeRuntime)
  75. podWorkers := newPodWorkers(
  76. func(options syncPodOptions) error {
  77. func() {
  78. lock.Lock()
  79. defer lock.Unlock()
  80. pod := options.pod
  81. processed[pod.UID] = append(processed[pod.UID], syncPodRecord{
  82. name: pod.Name,
  83. updateType: options.updateType,
  84. })
  85. }()
  86. return nil
  87. },
  88. fakeRecorder,
  89. queue.NewBasicWorkQueue(&clock.RealClock{}),
  90. time.Second,
  91. time.Second,
  92. fakeCache,
  93. )
  94. return podWorkers, processed
  95. }
  96. func drainWorkers(podWorkers *podWorkers, numPods int) {
  97. for {
  98. stillWorking := false
  99. podWorkers.podLock.Lock()
  100. for i := 0; i < numPods; i++ {
  101. if podWorkers.isWorking[types.UID(string(i))] {
  102. stillWorking = true
  103. }
  104. }
  105. podWorkers.podLock.Unlock()
  106. if !stillWorking {
  107. break
  108. }
  109. time.Sleep(50 * time.Millisecond)
  110. }
  111. }
  112. func TestUpdatePod(t *testing.T) {
  113. podWorkers, processed := createPodWorkers()
  114. numPods := 20
  115. for i := 0; i < numPods; i++ {
  116. for j := i; j < numPods; j++ {
  117. podWorkers.UpdatePod(&UpdatePodOptions{
  118. Pod: newPod(string(j), string(i)),
  119. UpdateType: kubetypes.SyncPodCreate,
  120. })
  121. }
  122. }
  123. drainWorkers(podWorkers, numPods)
  124. if len(processed) != numPods {
  125. t.Errorf("Not all pods processed: %v", len(processed))
  126. return
  127. }
  128. for i := 0; i < numPods; i++ {
  129. uid := types.UID(i)
  130. if len(processed[uid]) < 1 || len(processed[uid]) > i+1 {
  131. t.Errorf("Pod %v processed %v times", i, len(processed[uid]))
  132. continue
  133. }
  134. // PodWorker guarantees the first and the last event will be processed
  135. first := 0
  136. last := len(processed[uid]) - 1
  137. if processed[uid][first].name != string(0) {
  138. t.Errorf("Pod %v: incorrect order %v, %v", i, first, processed[uid][first])
  139. }
  140. if processed[uid][last].name != string(i) {
  141. t.Errorf("Pod %v: incorrect order %v, %v", i, last, processed[uid][last])
  142. }
  143. }
  144. }
  145. func TestUpdatePodDoesNotForgetSyncPodKill(t *testing.T) {
  146. podWorkers, processed := createPodWorkers()
  147. numPods := 20
  148. for i := 0; i < numPods; i++ {
  149. pod := newPod(string(i), string(i))
  150. podWorkers.UpdatePod(&UpdatePodOptions{
  151. Pod: pod,
  152. UpdateType: kubetypes.SyncPodCreate,
  153. })
  154. podWorkers.UpdatePod(&UpdatePodOptions{
  155. Pod: pod,
  156. UpdateType: kubetypes.SyncPodKill,
  157. })
  158. podWorkers.UpdatePod(&UpdatePodOptions{
  159. Pod: pod,
  160. UpdateType: kubetypes.SyncPodUpdate,
  161. })
  162. }
  163. drainWorkers(podWorkers, numPods)
  164. if len(processed) != numPods {
  165. t.Errorf("Not all pods processed: %v", len(processed))
  166. return
  167. }
  168. for i := 0; i < numPods; i++ {
  169. uid := types.UID(i)
  170. // each pod should be processed two times (create, kill, but not update)
  171. syncPodRecords := processed[uid]
  172. if len(syncPodRecords) < 2 {
  173. t.Errorf("Pod %v processed %v times, but expected at least 2", i, len(syncPodRecords))
  174. continue
  175. }
  176. if syncPodRecords[0].updateType != kubetypes.SyncPodCreate {
  177. t.Errorf("Pod %v event was %v, but expected %v", i, syncPodRecords[0].updateType, kubetypes.SyncPodCreate)
  178. }
  179. if syncPodRecords[1].updateType != kubetypes.SyncPodKill {
  180. t.Errorf("Pod %v event was %v, but expected %v", i, syncPodRecords[1].updateType, kubetypes.SyncPodKill)
  181. }
  182. }
  183. }
  184. func TestForgetNonExistingPodWorkers(t *testing.T) {
  185. podWorkers, _ := createPodWorkers()
  186. numPods := 20
  187. for i := 0; i < numPods; i++ {
  188. podWorkers.UpdatePod(&UpdatePodOptions{
  189. Pod: newPod(string(i), "name"),
  190. UpdateType: kubetypes.SyncPodUpdate,
  191. })
  192. }
  193. drainWorkers(podWorkers, numPods)
  194. if len(podWorkers.podUpdates) != numPods {
  195. t.Errorf("Incorrect number of open channels %v", len(podWorkers.podUpdates))
  196. }
  197. desiredPods := map[types.UID]empty{}
  198. desiredPods[types.UID(2)] = empty{}
  199. desiredPods[types.UID(14)] = empty{}
  200. podWorkers.ForgetNonExistingPodWorkers(desiredPods)
  201. if len(podWorkers.podUpdates) != 2 {
  202. t.Errorf("Incorrect number of open channels %v", len(podWorkers.podUpdates))
  203. }
  204. if _, exists := podWorkers.podUpdates[types.UID(2)]; !exists {
  205. t.Errorf("No updates channel for pod 2")
  206. }
  207. if _, exists := podWorkers.podUpdates[types.UID(14)]; !exists {
  208. t.Errorf("No updates channel for pod 14")
  209. }
  210. podWorkers.ForgetNonExistingPodWorkers(map[types.UID]empty{})
  211. if len(podWorkers.podUpdates) != 0 {
  212. t.Errorf("Incorrect number of open channels %v", len(podWorkers.podUpdates))
  213. }
  214. }
  215. type simpleFakeKubelet struct {
  216. pod *v1.Pod
  217. mirrorPod *v1.Pod
  218. podStatus *kubecontainer.PodStatus
  219. wg sync.WaitGroup
  220. }
  221. func (kl *simpleFakeKubelet) syncPod(options syncPodOptions) error {
  222. kl.pod, kl.mirrorPod, kl.podStatus = options.pod, options.mirrorPod, options.podStatus
  223. return nil
  224. }
  225. func (kl *simpleFakeKubelet) syncPodWithWaitGroup(options syncPodOptions) error {
  226. kl.pod, kl.mirrorPod, kl.podStatus = options.pod, options.mirrorPod, options.podStatus
  227. kl.wg.Done()
  228. return nil
  229. }
  230. // TestFakePodWorkers verifies that the fakePodWorkers behaves the same way as the real podWorkers
  231. // for their invocation of the syncPodFn.
  232. func TestFakePodWorkers(t *testing.T) {
  233. fakeRecorder := &record.FakeRecorder{}
  234. fakeRuntime := &containertest.FakeRuntime{}
  235. fakeCache := containertest.NewFakeCache(fakeRuntime)
  236. kubeletForRealWorkers := &simpleFakeKubelet{}
  237. kubeletForFakeWorkers := &simpleFakeKubelet{}
  238. realPodWorkers := newPodWorkers(kubeletForRealWorkers.syncPodWithWaitGroup, fakeRecorder, queue.NewBasicWorkQueue(&clock.RealClock{}), time.Second, time.Second, fakeCache)
  239. fakePodWorkers := &fakePodWorkers{kubeletForFakeWorkers.syncPod, fakeCache, t}
  240. tests := []struct {
  241. pod *v1.Pod
  242. mirrorPod *v1.Pod
  243. }{
  244. {
  245. &v1.Pod{},
  246. &v1.Pod{},
  247. },
  248. {
  249. podWithUIDNameNs("12345678", "foo", "new"),
  250. podWithUIDNameNs("12345678", "fooMirror", "new"),
  251. },
  252. {
  253. podWithUIDNameNs("98765", "bar", "new"),
  254. podWithUIDNameNs("98765", "barMirror", "new"),
  255. },
  256. }
  257. for i, tt := range tests {
  258. kubeletForRealWorkers.wg.Add(1)
  259. realPodWorkers.UpdatePod(&UpdatePodOptions{
  260. Pod: tt.pod,
  261. MirrorPod: tt.mirrorPod,
  262. UpdateType: kubetypes.SyncPodUpdate,
  263. })
  264. fakePodWorkers.UpdatePod(&UpdatePodOptions{
  265. Pod: tt.pod,
  266. MirrorPod: tt.mirrorPod,
  267. UpdateType: kubetypes.SyncPodUpdate,
  268. })
  269. kubeletForRealWorkers.wg.Wait()
  270. if !reflect.DeepEqual(kubeletForRealWorkers.pod, kubeletForFakeWorkers.pod) {
  271. t.Errorf("%d: Expected: %#v, Actual: %#v", i, kubeletForRealWorkers.pod, kubeletForFakeWorkers.pod)
  272. }
  273. if !reflect.DeepEqual(kubeletForRealWorkers.mirrorPod, kubeletForFakeWorkers.mirrorPod) {
  274. t.Errorf("%d: Expected: %#v, Actual: %#v", i, kubeletForRealWorkers.mirrorPod, kubeletForFakeWorkers.mirrorPod)
  275. }
  276. if !reflect.DeepEqual(kubeletForRealWorkers.podStatus, kubeletForFakeWorkers.podStatus) {
  277. t.Errorf("%d: Expected: %#v, Actual: %#v", i, kubeletForRealWorkers.podStatus, kubeletForFakeWorkers.podStatus)
  278. }
  279. }
  280. }
  281. // TestKillPodNowFunc tests the blocking kill pod function works with pod workers as expected.
  282. func TestKillPodNowFunc(t *testing.T) {
  283. fakeRecorder := &record.FakeRecorder{}
  284. podWorkers, processed := createPodWorkers()
  285. killPodFunc := killPodNow(podWorkers, fakeRecorder)
  286. pod := newPod("test", "test")
  287. gracePeriodOverride := int64(0)
  288. err := killPodFunc(pod, v1.PodStatus{Phase: v1.PodFailed, Reason: "reason", Message: "message"}, &gracePeriodOverride)
  289. if err != nil {
  290. t.Errorf("Unexpected error: %v", err)
  291. }
  292. if len(processed) != 1 {
  293. t.Errorf("len(processed) expected: %v, actual: %v", 1, len(processed))
  294. return
  295. }
  296. syncPodRecords := processed[pod.UID]
  297. if len(syncPodRecords) != 1 {
  298. t.Errorf("Pod processed %v times, but expected %v", len(syncPodRecords), 1)
  299. }
  300. if syncPodRecords[0].updateType != kubetypes.SyncPodKill {
  301. t.Errorf("Pod update type was %v, but expected %v", syncPodRecords[0].updateType, kubetypes.SyncPodKill)
  302. }
  303. }