rolling_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. /*
  2. Copyright 2016 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 deployment
  14. import (
  15. "testing"
  16. apps "k8s.io/api/apps/v1"
  17. "k8s.io/apimachinery/pkg/util/intstr"
  18. "k8s.io/client-go/kubernetes/fake"
  19. core "k8s.io/client-go/testing"
  20. "k8s.io/client-go/tools/record"
  21. )
  22. func TestDeploymentController_reconcileNewReplicaSet(t *testing.T) {
  23. tests := []struct {
  24. deploymentReplicas int
  25. maxSurge intstr.IntOrString
  26. oldReplicas int
  27. newReplicas int
  28. scaleExpected bool
  29. expectedNewReplicas int
  30. }{
  31. {
  32. // Should not scale up.
  33. deploymentReplicas: 10,
  34. maxSurge: intstr.FromInt(0),
  35. oldReplicas: 10,
  36. newReplicas: 0,
  37. scaleExpected: false,
  38. },
  39. {
  40. deploymentReplicas: 10,
  41. maxSurge: intstr.FromInt(2),
  42. oldReplicas: 10,
  43. newReplicas: 0,
  44. scaleExpected: true,
  45. expectedNewReplicas: 2,
  46. },
  47. {
  48. deploymentReplicas: 10,
  49. maxSurge: intstr.FromInt(2),
  50. oldReplicas: 5,
  51. newReplicas: 0,
  52. scaleExpected: true,
  53. expectedNewReplicas: 7,
  54. },
  55. {
  56. deploymentReplicas: 10,
  57. maxSurge: intstr.FromInt(2),
  58. oldReplicas: 10,
  59. newReplicas: 2,
  60. scaleExpected: false,
  61. },
  62. {
  63. // Should scale down.
  64. deploymentReplicas: 10,
  65. maxSurge: intstr.FromInt(2),
  66. oldReplicas: 2,
  67. newReplicas: 11,
  68. scaleExpected: true,
  69. expectedNewReplicas: 10,
  70. },
  71. }
  72. for i := range tests {
  73. test := tests[i]
  74. t.Logf("executing scenario %d", i)
  75. newRS := rs("foo-v2", test.newReplicas, nil, noTimestamp)
  76. oldRS := rs("foo-v2", test.oldReplicas, nil, noTimestamp)
  77. allRSs := []*apps.ReplicaSet{newRS, oldRS}
  78. maxUnavailable := intstr.FromInt(0)
  79. deployment := newDeployment("foo", test.deploymentReplicas, nil, &test.maxSurge, &maxUnavailable, map[string]string{"foo": "bar"})
  80. fake := fake.Clientset{}
  81. controller := &DeploymentController{
  82. client: &fake,
  83. eventRecorder: &record.FakeRecorder{},
  84. }
  85. scaled, err := controller.reconcileNewReplicaSet(allRSs, newRS, deployment)
  86. if err != nil {
  87. t.Errorf("unexpected error: %v", err)
  88. continue
  89. }
  90. if !test.scaleExpected {
  91. if scaled || len(fake.Actions()) > 0 {
  92. t.Errorf("unexpected scaling: %v", fake.Actions())
  93. }
  94. continue
  95. }
  96. if test.scaleExpected && !scaled {
  97. t.Errorf("expected scaling to occur")
  98. continue
  99. }
  100. if len(fake.Actions()) != 1 {
  101. t.Errorf("expected 1 action during scale, got: %v", fake.Actions())
  102. continue
  103. }
  104. updated := fake.Actions()[0].(core.UpdateAction).GetObject().(*apps.ReplicaSet)
  105. if e, a := test.expectedNewReplicas, int(*(updated.Spec.Replicas)); e != a {
  106. t.Errorf("expected update to %d replicas, got %d", e, a)
  107. }
  108. }
  109. }
  110. func TestDeploymentController_reconcileOldReplicaSets(t *testing.T) {
  111. tests := []struct {
  112. deploymentReplicas int
  113. maxUnavailable intstr.IntOrString
  114. oldReplicas int
  115. newReplicas int
  116. readyPodsFromOldRS int
  117. readyPodsFromNewRS int
  118. scaleExpected bool
  119. expectedOldReplicas int
  120. }{
  121. {
  122. deploymentReplicas: 10,
  123. maxUnavailable: intstr.FromInt(0),
  124. oldReplicas: 10,
  125. newReplicas: 0,
  126. readyPodsFromOldRS: 10,
  127. readyPodsFromNewRS: 0,
  128. scaleExpected: true,
  129. expectedOldReplicas: 9,
  130. },
  131. {
  132. deploymentReplicas: 10,
  133. maxUnavailable: intstr.FromInt(2),
  134. oldReplicas: 10,
  135. newReplicas: 0,
  136. readyPodsFromOldRS: 10,
  137. readyPodsFromNewRS: 0,
  138. scaleExpected: true,
  139. expectedOldReplicas: 8,
  140. },
  141. { // expect unhealthy replicas from old replica sets been cleaned up
  142. deploymentReplicas: 10,
  143. maxUnavailable: intstr.FromInt(2),
  144. oldReplicas: 10,
  145. newReplicas: 0,
  146. readyPodsFromOldRS: 8,
  147. readyPodsFromNewRS: 0,
  148. scaleExpected: true,
  149. expectedOldReplicas: 8,
  150. },
  151. { // expect 1 unhealthy replica from old replica sets been cleaned up, and 1 ready pod been scaled down
  152. deploymentReplicas: 10,
  153. maxUnavailable: intstr.FromInt(2),
  154. oldReplicas: 10,
  155. newReplicas: 0,
  156. readyPodsFromOldRS: 9,
  157. readyPodsFromNewRS: 0,
  158. scaleExpected: true,
  159. expectedOldReplicas: 8,
  160. },
  161. { // the unavailable pods from the newRS would not make us scale down old RSs in a further step
  162. deploymentReplicas: 10,
  163. maxUnavailable: intstr.FromInt(2),
  164. oldReplicas: 8,
  165. newReplicas: 2,
  166. readyPodsFromOldRS: 8,
  167. readyPodsFromNewRS: 0,
  168. scaleExpected: false,
  169. },
  170. }
  171. for i := range tests {
  172. test := tests[i]
  173. t.Logf("executing scenario %d", i)
  174. newSelector := map[string]string{"foo": "new"}
  175. oldSelector := map[string]string{"foo": "old"}
  176. newRS := rs("foo-new", test.newReplicas, newSelector, noTimestamp)
  177. newRS.Status.AvailableReplicas = int32(test.readyPodsFromNewRS)
  178. oldRS := rs("foo-old", test.oldReplicas, oldSelector, noTimestamp)
  179. oldRS.Status.AvailableReplicas = int32(test.readyPodsFromOldRS)
  180. oldRSs := []*apps.ReplicaSet{oldRS}
  181. allRSs := []*apps.ReplicaSet{oldRS, newRS}
  182. maxSurge := intstr.FromInt(0)
  183. deployment := newDeployment("foo", test.deploymentReplicas, nil, &maxSurge, &test.maxUnavailable, newSelector)
  184. fakeClientset := fake.Clientset{}
  185. controller := &DeploymentController{
  186. client: &fakeClientset,
  187. eventRecorder: &record.FakeRecorder{},
  188. }
  189. scaled, err := controller.reconcileOldReplicaSets(allRSs, oldRSs, newRS, deployment)
  190. if err != nil {
  191. t.Errorf("unexpected error: %v", err)
  192. continue
  193. }
  194. if !test.scaleExpected && scaled {
  195. t.Errorf("unexpected scaling: %v", fakeClientset.Actions())
  196. }
  197. if test.scaleExpected && !scaled {
  198. t.Errorf("expected scaling to occur")
  199. continue
  200. }
  201. continue
  202. }
  203. }
  204. func TestDeploymentController_cleanupUnhealthyReplicas(t *testing.T) {
  205. tests := []struct {
  206. oldReplicas int
  207. readyPods int
  208. unHealthyPods int
  209. maxCleanupCount int
  210. cleanupCountExpected int
  211. }{
  212. {
  213. oldReplicas: 10,
  214. readyPods: 8,
  215. unHealthyPods: 2,
  216. maxCleanupCount: 1,
  217. cleanupCountExpected: 1,
  218. },
  219. {
  220. oldReplicas: 10,
  221. readyPods: 8,
  222. unHealthyPods: 2,
  223. maxCleanupCount: 3,
  224. cleanupCountExpected: 2,
  225. },
  226. {
  227. oldReplicas: 10,
  228. readyPods: 8,
  229. unHealthyPods: 2,
  230. maxCleanupCount: 0,
  231. cleanupCountExpected: 0,
  232. },
  233. {
  234. oldReplicas: 10,
  235. readyPods: 10,
  236. unHealthyPods: 0,
  237. maxCleanupCount: 3,
  238. cleanupCountExpected: 0,
  239. },
  240. }
  241. for i, test := range tests {
  242. t.Logf("executing scenario %d", i)
  243. oldRS := rs("foo-v2", test.oldReplicas, nil, noTimestamp)
  244. oldRS.Status.AvailableReplicas = int32(test.readyPods)
  245. oldRSs := []*apps.ReplicaSet{oldRS}
  246. maxSurge := intstr.FromInt(2)
  247. maxUnavailable := intstr.FromInt(2)
  248. deployment := newDeployment("foo", 10, nil, &maxSurge, &maxUnavailable, nil)
  249. fakeClientset := fake.Clientset{}
  250. controller := &DeploymentController{
  251. client: &fakeClientset,
  252. eventRecorder: &record.FakeRecorder{},
  253. }
  254. _, cleanupCount, err := controller.cleanupUnhealthyReplicas(oldRSs, deployment, int32(test.maxCleanupCount))
  255. if err != nil {
  256. t.Errorf("unexpected error: %v", err)
  257. continue
  258. }
  259. if int(cleanupCount) != test.cleanupCountExpected {
  260. t.Errorf("expected %v unhealthy replicas been cleaned up, got %v", test.cleanupCountExpected, cleanupCount)
  261. continue
  262. }
  263. }
  264. }
  265. func TestDeploymentController_scaleDownOldReplicaSetsForRollingUpdate(t *testing.T) {
  266. tests := []struct {
  267. deploymentReplicas int
  268. maxUnavailable intstr.IntOrString
  269. readyPods int
  270. oldReplicas int
  271. scaleExpected bool
  272. expectedOldReplicas int
  273. }{
  274. {
  275. deploymentReplicas: 10,
  276. maxUnavailable: intstr.FromInt(0),
  277. readyPods: 10,
  278. oldReplicas: 10,
  279. scaleExpected: true,
  280. expectedOldReplicas: 9,
  281. },
  282. {
  283. deploymentReplicas: 10,
  284. maxUnavailable: intstr.FromInt(2),
  285. readyPods: 10,
  286. oldReplicas: 10,
  287. scaleExpected: true,
  288. expectedOldReplicas: 8,
  289. },
  290. {
  291. deploymentReplicas: 10,
  292. maxUnavailable: intstr.FromInt(2),
  293. readyPods: 8,
  294. oldReplicas: 10,
  295. scaleExpected: false,
  296. },
  297. {
  298. deploymentReplicas: 10,
  299. maxUnavailable: intstr.FromInt(2),
  300. readyPods: 10,
  301. oldReplicas: 0,
  302. scaleExpected: false,
  303. },
  304. {
  305. deploymentReplicas: 10,
  306. maxUnavailable: intstr.FromInt(2),
  307. readyPods: 1,
  308. oldReplicas: 10,
  309. scaleExpected: false,
  310. },
  311. }
  312. for i := range tests {
  313. test := tests[i]
  314. t.Logf("executing scenario %d", i)
  315. oldRS := rs("foo-v2", test.oldReplicas, nil, noTimestamp)
  316. oldRS.Status.AvailableReplicas = int32(test.readyPods)
  317. allRSs := []*apps.ReplicaSet{oldRS}
  318. oldRSs := []*apps.ReplicaSet{oldRS}
  319. maxSurge := intstr.FromInt(0)
  320. deployment := newDeployment("foo", test.deploymentReplicas, nil, &maxSurge, &test.maxUnavailable, map[string]string{"foo": "bar"})
  321. fakeClientset := fake.Clientset{}
  322. controller := &DeploymentController{
  323. client: &fakeClientset,
  324. eventRecorder: &record.FakeRecorder{},
  325. }
  326. scaled, err := controller.scaleDownOldReplicaSetsForRollingUpdate(allRSs, oldRSs, deployment)
  327. if err != nil {
  328. t.Errorf("unexpected error: %v", err)
  329. continue
  330. }
  331. if !test.scaleExpected {
  332. if scaled != 0 {
  333. t.Errorf("unexpected scaling: %v", fakeClientset.Actions())
  334. }
  335. continue
  336. }
  337. if test.scaleExpected && scaled == 0 {
  338. t.Errorf("expected scaling to occur; actions: %v", fakeClientset.Actions())
  339. continue
  340. }
  341. // There are both list and update actions logged, so extract the update
  342. // action for verification.
  343. var updateAction core.UpdateAction
  344. for _, action := range fakeClientset.Actions() {
  345. switch a := action.(type) {
  346. case core.UpdateAction:
  347. if updateAction != nil {
  348. t.Errorf("expected only 1 update action; had %v and found %v", updateAction, a)
  349. } else {
  350. updateAction = a
  351. }
  352. }
  353. }
  354. if updateAction == nil {
  355. t.Errorf("expected an update action")
  356. continue
  357. }
  358. updated := updateAction.GetObject().(*apps.ReplicaSet)
  359. if e, a := test.expectedOldReplicas, int(*(updated.Spec.Replicas)); e != a {
  360. t.Errorf("expected update to %d replicas, got %d", e, a)
  361. }
  362. }
  363. }