update_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  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 daemon
  14. import (
  15. "testing"
  16. apps "k8s.io/api/apps/v1"
  17. "k8s.io/api/core/v1"
  18. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  19. "k8s.io/apimachinery/pkg/labels"
  20. "k8s.io/apimachinery/pkg/util/intstr"
  21. )
  22. func TestDaemonSetUpdatesPods(t *testing.T) {
  23. ds := newDaemonSet("foo")
  24. manager, podControl, _, err := newTestController(ds)
  25. if err != nil {
  26. t.Fatalf("error creating DaemonSets controller: %v", err)
  27. }
  28. maxUnavailable := 2
  29. addNodes(manager.nodeStore, 0, 5, nil)
  30. manager.dsStore.Add(ds)
  31. syncAndValidateDaemonSets(t, manager, ds, podControl, 5, 0, 0)
  32. markPodsReady(podControl.podStore)
  33. ds.Spec.Template.Spec.Containers[0].Image = "foo2/bar2"
  34. ds.Spec.UpdateStrategy.Type = apps.RollingUpdateDaemonSetStrategyType
  35. intStr := intstr.FromInt(maxUnavailable)
  36. ds.Spec.UpdateStrategy.RollingUpdate = &apps.RollingUpdateDaemonSet{MaxUnavailable: &intStr}
  37. manager.dsStore.Update(ds)
  38. clearExpectations(t, manager, ds, podControl)
  39. syncAndValidateDaemonSets(t, manager, ds, podControl, 0, maxUnavailable, 0)
  40. clearExpectations(t, manager, ds, podControl)
  41. syncAndValidateDaemonSets(t, manager, ds, podControl, maxUnavailable, 0, 0)
  42. markPodsReady(podControl.podStore)
  43. clearExpectations(t, manager, ds, podControl)
  44. syncAndValidateDaemonSets(t, manager, ds, podControl, 0, maxUnavailable, 0)
  45. clearExpectations(t, manager, ds, podControl)
  46. syncAndValidateDaemonSets(t, manager, ds, podControl, maxUnavailable, 0, 0)
  47. markPodsReady(podControl.podStore)
  48. clearExpectations(t, manager, ds, podControl)
  49. syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 1, 0)
  50. clearExpectations(t, manager, ds, podControl)
  51. syncAndValidateDaemonSets(t, manager, ds, podControl, 1, 0, 0)
  52. markPodsReady(podControl.podStore)
  53. clearExpectations(t, manager, ds, podControl)
  54. syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0, 0)
  55. clearExpectations(t, manager, ds, podControl)
  56. }
  57. func TestDaemonSetUpdatesWhenNewPosIsNotReady(t *testing.T) {
  58. ds := newDaemonSet("foo")
  59. manager, podControl, _, err := newTestController(ds)
  60. if err != nil {
  61. t.Fatalf("error creating DaemonSets controller: %v", err)
  62. }
  63. maxUnavailable := 3
  64. addNodes(manager.nodeStore, 0, 5, nil)
  65. manager.dsStore.Add(ds)
  66. syncAndValidateDaemonSets(t, manager, ds, podControl, 5, 0, 0)
  67. markPodsReady(podControl.podStore)
  68. ds.Spec.Template.Spec.Containers[0].Image = "foo2/bar2"
  69. ds.Spec.UpdateStrategy.Type = apps.RollingUpdateDaemonSetStrategyType
  70. intStr := intstr.FromInt(maxUnavailable)
  71. ds.Spec.UpdateStrategy.RollingUpdate = &apps.RollingUpdateDaemonSet{MaxUnavailable: &intStr}
  72. manager.dsStore.Update(ds)
  73. // new pods are not ready numUnavailable == maxUnavailable
  74. clearExpectations(t, manager, ds, podControl)
  75. syncAndValidateDaemonSets(t, manager, ds, podControl, 0, maxUnavailable, 0)
  76. clearExpectations(t, manager, ds, podControl)
  77. syncAndValidateDaemonSets(t, manager, ds, podControl, maxUnavailable, 0, 0)
  78. clearExpectations(t, manager, ds, podControl)
  79. syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0, 0)
  80. clearExpectations(t, manager, ds, podControl)
  81. }
  82. func TestDaemonSetUpdatesAllOldPodsNotReady(t *testing.T) {
  83. ds := newDaemonSet("foo")
  84. manager, podControl, _, err := newTestController(ds)
  85. if err != nil {
  86. t.Fatalf("error creating DaemonSets controller: %v", err)
  87. }
  88. maxUnavailable := 3
  89. addNodes(manager.nodeStore, 0, 5, nil)
  90. manager.dsStore.Add(ds)
  91. syncAndValidateDaemonSets(t, manager, ds, podControl, 5, 0, 0)
  92. ds.Spec.Template.Spec.Containers[0].Image = "foo2/bar2"
  93. ds.Spec.UpdateStrategy.Type = apps.RollingUpdateDaemonSetStrategyType
  94. intStr := intstr.FromInt(maxUnavailable)
  95. ds.Spec.UpdateStrategy.RollingUpdate = &apps.RollingUpdateDaemonSet{MaxUnavailable: &intStr}
  96. manager.dsStore.Update(ds)
  97. // all old pods are unavailable so should be removed
  98. clearExpectations(t, manager, ds, podControl)
  99. syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 5, 0)
  100. clearExpectations(t, manager, ds, podControl)
  101. syncAndValidateDaemonSets(t, manager, ds, podControl, 5, 0, 0)
  102. clearExpectations(t, manager, ds, podControl)
  103. syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0, 0)
  104. clearExpectations(t, manager, ds, podControl)
  105. }
  106. func TestDaemonSetUpdatesNoTemplateChanged(t *testing.T) {
  107. ds := newDaemonSet("foo")
  108. manager, podControl, _, err := newTestController(ds)
  109. if err != nil {
  110. t.Fatalf("error creating DaemonSets controller: %v", err)
  111. }
  112. maxUnavailable := 3
  113. addNodes(manager.nodeStore, 0, 5, nil)
  114. manager.dsStore.Add(ds)
  115. syncAndValidateDaemonSets(t, manager, ds, podControl, 5, 0, 0)
  116. ds.Spec.UpdateStrategy.Type = apps.RollingUpdateDaemonSetStrategyType
  117. intStr := intstr.FromInt(maxUnavailable)
  118. ds.Spec.UpdateStrategy.RollingUpdate = &apps.RollingUpdateDaemonSet{MaxUnavailable: &intStr}
  119. manager.dsStore.Update(ds)
  120. // template is not changed no pod should be removed
  121. clearExpectations(t, manager, ds, podControl)
  122. syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0, 0)
  123. clearExpectations(t, manager, ds, podControl)
  124. }
  125. func TestGetUnavailableNumbers(t *testing.T) {
  126. cases := []struct {
  127. name string
  128. Manager *daemonSetsController
  129. ds *apps.DaemonSet
  130. nodeToPods map[string][]*v1.Pod
  131. maxUnavailable int
  132. numUnavailable int
  133. Err error
  134. }{
  135. {
  136. name: "No nodes",
  137. Manager: func() *daemonSetsController {
  138. manager, _, _, err := newTestController()
  139. if err != nil {
  140. t.Fatalf("error creating DaemonSets controller: %v", err)
  141. }
  142. return manager
  143. }(),
  144. ds: func() *apps.DaemonSet {
  145. ds := newDaemonSet("x")
  146. intStr := intstr.FromInt(0)
  147. ds.Spec.UpdateStrategy.RollingUpdate = &apps.RollingUpdateDaemonSet{MaxUnavailable: &intStr}
  148. return ds
  149. }(),
  150. nodeToPods: make(map[string][]*v1.Pod),
  151. maxUnavailable: 0,
  152. numUnavailable: 0,
  153. },
  154. {
  155. name: "Two nodes with ready pods",
  156. Manager: func() *daemonSetsController {
  157. manager, _, _, err := newTestController()
  158. if err != nil {
  159. t.Fatalf("error creating DaemonSets controller: %v", err)
  160. }
  161. addNodes(manager.nodeStore, 0, 2, nil)
  162. return manager
  163. }(),
  164. ds: func() *apps.DaemonSet {
  165. ds := newDaemonSet("x")
  166. intStr := intstr.FromInt(1)
  167. ds.Spec.UpdateStrategy.RollingUpdate = &apps.RollingUpdateDaemonSet{MaxUnavailable: &intStr}
  168. return ds
  169. }(),
  170. nodeToPods: func() map[string][]*v1.Pod {
  171. mapping := make(map[string][]*v1.Pod)
  172. pod0 := newPod("pod-0", "node-0", simpleDaemonSetLabel, nil)
  173. pod1 := newPod("pod-1", "node-1", simpleDaemonSetLabel, nil)
  174. markPodReady(pod0)
  175. markPodReady(pod1)
  176. mapping["node-0"] = []*v1.Pod{pod0}
  177. mapping["node-1"] = []*v1.Pod{pod1}
  178. return mapping
  179. }(),
  180. maxUnavailable: 1,
  181. numUnavailable: 0,
  182. },
  183. {
  184. name: "Two nodes, one node without pods",
  185. Manager: func() *daemonSetsController {
  186. manager, _, _, err := newTestController()
  187. if err != nil {
  188. t.Fatalf("error creating DaemonSets controller: %v", err)
  189. }
  190. addNodes(manager.nodeStore, 0, 2, nil)
  191. return manager
  192. }(),
  193. ds: func() *apps.DaemonSet {
  194. ds := newDaemonSet("x")
  195. intStr := intstr.FromInt(0)
  196. ds.Spec.UpdateStrategy.RollingUpdate = &apps.RollingUpdateDaemonSet{MaxUnavailable: &intStr}
  197. return ds
  198. }(),
  199. nodeToPods: func() map[string][]*v1.Pod {
  200. mapping := make(map[string][]*v1.Pod)
  201. pod0 := newPod("pod-0", "node-0", simpleDaemonSetLabel, nil)
  202. markPodReady(pod0)
  203. mapping["node-0"] = []*v1.Pod{pod0}
  204. return mapping
  205. }(),
  206. maxUnavailable: 0,
  207. numUnavailable: 1,
  208. },
  209. {
  210. name: "Two nodes with pods, MaxUnavailable in percents",
  211. Manager: func() *daemonSetsController {
  212. manager, _, _, err := newTestController()
  213. if err != nil {
  214. t.Fatalf("error creating DaemonSets controller: %v", err)
  215. }
  216. addNodes(manager.nodeStore, 0, 2, nil)
  217. return manager
  218. }(),
  219. ds: func() *apps.DaemonSet {
  220. ds := newDaemonSet("x")
  221. intStr := intstr.FromString("50%")
  222. ds.Spec.UpdateStrategy.RollingUpdate = &apps.RollingUpdateDaemonSet{MaxUnavailable: &intStr}
  223. return ds
  224. }(),
  225. nodeToPods: func() map[string][]*v1.Pod {
  226. mapping := make(map[string][]*v1.Pod)
  227. pod0 := newPod("pod-0", "node-0", simpleDaemonSetLabel, nil)
  228. pod1 := newPod("pod-1", "node-1", simpleDaemonSetLabel, nil)
  229. markPodReady(pod0)
  230. markPodReady(pod1)
  231. mapping["node-0"] = []*v1.Pod{pod0}
  232. mapping["node-1"] = []*v1.Pod{pod1}
  233. return mapping
  234. }(),
  235. maxUnavailable: 1,
  236. numUnavailable: 0,
  237. },
  238. {
  239. name: "Two nodes with pods, MaxUnavailable in percents, pod terminating",
  240. Manager: func() *daemonSetsController {
  241. manager, _, _, err := newTestController()
  242. if err != nil {
  243. t.Fatalf("error creating DaemonSets controller: %v", err)
  244. }
  245. addNodes(manager.nodeStore, 0, 2, nil)
  246. return manager
  247. }(),
  248. ds: func() *apps.DaemonSet {
  249. ds := newDaemonSet("x")
  250. intStr := intstr.FromString("50%")
  251. ds.Spec.UpdateStrategy.RollingUpdate = &apps.RollingUpdateDaemonSet{MaxUnavailable: &intStr}
  252. return ds
  253. }(),
  254. nodeToPods: func() map[string][]*v1.Pod {
  255. mapping := make(map[string][]*v1.Pod)
  256. pod0 := newPod("pod-0", "node-0", simpleDaemonSetLabel, nil)
  257. pod1 := newPod("pod-1", "node-1", simpleDaemonSetLabel, nil)
  258. now := metav1.Now()
  259. markPodReady(pod0)
  260. markPodReady(pod1)
  261. pod1.DeletionTimestamp = &now
  262. mapping["node-0"] = []*v1.Pod{pod0}
  263. mapping["node-1"] = []*v1.Pod{pod1}
  264. return mapping
  265. }(),
  266. maxUnavailable: 1,
  267. numUnavailable: 1,
  268. },
  269. }
  270. for _, c := range cases {
  271. c.Manager.dsStore.Add(c.ds)
  272. nodeList, err := c.Manager.nodeLister.List(labels.Everything())
  273. if err != nil {
  274. t.Fatalf("error listing nodes: %v", err)
  275. }
  276. maxUnavailable, numUnavailable, err := c.Manager.getUnavailableNumbers(c.ds, nodeList, c.nodeToPods)
  277. if err != nil && c.Err != nil {
  278. if c.Err != err {
  279. t.Errorf("Test case: %s. Expected error: %v but got: %v", c.name, c.Err, err)
  280. }
  281. } else if err != nil {
  282. t.Errorf("Test case: %s. Unexpected error: %v", c.name, err)
  283. } else if maxUnavailable != c.maxUnavailable || numUnavailable != c.numUnavailable {
  284. t.Errorf("Test case: %s. Wrong values. maxUnavailable: %d, expected: %d, numUnavailable: %d. expected: %d", c.name, maxUnavailable, c.maxUnavailable, numUnavailable, c.numUnavailable)
  285. }
  286. }
  287. }