pod_workers_test.go 10.0 KB

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