rolling_updater_test.go 54 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852
  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 kubectl
  14. import (
  15. "bytes"
  16. "fmt"
  17. "io"
  18. "io/ioutil"
  19. "net/http"
  20. "reflect"
  21. "testing"
  22. "time"
  23. corev1 "k8s.io/api/core/v1"
  24. apiequality "k8s.io/apimachinery/pkg/api/equality"
  25. "k8s.io/apimachinery/pkg/api/errors"
  26. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  27. "k8s.io/apimachinery/pkg/runtime"
  28. "k8s.io/apimachinery/pkg/runtime/schema"
  29. "k8s.io/apimachinery/pkg/util/diff"
  30. "k8s.io/apimachinery/pkg/util/intstr"
  31. "k8s.io/apimachinery/pkg/util/sets"
  32. "k8s.io/client-go/kubernetes"
  33. "k8s.io/client-go/kubernetes/fake"
  34. restclient "k8s.io/client-go/rest"
  35. manualfake "k8s.io/client-go/rest/fake"
  36. testcore "k8s.io/client-go/testing"
  37. "k8s.io/kubernetes/pkg/kubectl/scheme"
  38. "k8s.io/kubernetes/pkg/kubectl/util"
  39. )
  40. func oldRc(replicas int, original int) *corev1.ReplicationController {
  41. t := replicas
  42. replicasCopy := int32(t)
  43. return &corev1.ReplicationController{
  44. ObjectMeta: metav1.ObjectMeta{
  45. Namespace: metav1.NamespaceDefault,
  46. Name: "foo-v1",
  47. UID: "7764ae47-9092-11e4-8393-42010af018ff",
  48. Annotations: map[string]string{
  49. originalReplicasAnnotation: fmt.Sprintf("%d", original),
  50. },
  51. },
  52. Spec: corev1.ReplicationControllerSpec{
  53. Replicas: &replicasCopy,
  54. Selector: map[string]string{"version": "v1"},
  55. Template: &corev1.PodTemplateSpec{
  56. ObjectMeta: metav1.ObjectMeta{
  57. Name: "foo-v1",
  58. Labels: map[string]string{"version": "v1"},
  59. },
  60. },
  61. },
  62. Status: corev1.ReplicationControllerStatus{
  63. Replicas: int32(replicas),
  64. },
  65. }
  66. }
  67. func newRc(replicas int, desired int) *corev1.ReplicationController {
  68. rc := oldRc(replicas, replicas)
  69. rc.Spec.Template = &corev1.PodTemplateSpec{
  70. ObjectMeta: metav1.ObjectMeta{
  71. Name: "foo-v2",
  72. Labels: map[string]string{"version": "v2"},
  73. },
  74. }
  75. rc.Spec.Selector = map[string]string{"version": "v2"}
  76. rc.ObjectMeta = metav1.ObjectMeta{
  77. Namespace: metav1.NamespaceDefault,
  78. Name: "foo-v2",
  79. Annotations: map[string]string{
  80. desiredReplicasAnnotation: fmt.Sprintf("%d", desired),
  81. sourceIDAnnotation: "foo-v1:7764ae47-9092-11e4-8393-42010af018ff",
  82. },
  83. }
  84. return rc
  85. }
  86. // TestUpdate performs complex scenario testing for rolling updates. It
  87. // provides fine grained control over the states for each update interval to
  88. // allow the expression of as many edge cases as possible.
  89. func TestUpdate(t *testing.T) {
  90. // up represents a simulated scale up event and expectation
  91. type up struct {
  92. // to is the expected replica count for a scale-up
  93. to int
  94. }
  95. // down represents a simulated scale down event and expectation
  96. type down struct {
  97. // oldReady is the number of oldRc replicas which will be seen
  98. // as ready during the scale down attempt
  99. oldReady int
  100. // newReady is the number of newRc replicas which will be seen
  101. // as ready during the scale up attempt
  102. newReady int
  103. // to is the expected replica count for the scale down
  104. to int
  105. // noop and to are mutually exclusive; if noop is true, that means for
  106. // this down event, no scaling attempt should be made (for example, if
  107. // by scaling down, the readiness minimum would be crossed.)
  108. noop bool
  109. }
  110. tests := []struct {
  111. name string
  112. // oldRc is the "from" deployment
  113. oldRc *corev1.ReplicationController
  114. // newRc is the "to" deployment
  115. newRc *corev1.ReplicationController
  116. // whether newRc existed (false means it was created)
  117. newRcExists bool
  118. maxUnavail intstr.IntOrString
  119. maxSurge intstr.IntOrString
  120. // expected is the sequence of up/down events that will be simulated and
  121. // verified
  122. expected []interface{}
  123. // output is the expected textual output written
  124. output string
  125. }{
  126. {
  127. name: "10->10 30/0 fast readiness",
  128. oldRc: oldRc(10, 10),
  129. newRc: newRc(0, 10),
  130. newRcExists: false,
  131. maxUnavail: intstr.FromString("30%"),
  132. maxSurge: intstr.FromString("0%"),
  133. expected: []interface{}{
  134. down{oldReady: 10, newReady: 0, to: 7},
  135. up{3},
  136. down{oldReady: 7, newReady: 3, to: 4},
  137. up{6},
  138. down{oldReady: 4, newReady: 6, to: 1},
  139. up{9},
  140. down{oldReady: 1, newReady: 9, to: 0},
  141. up{10},
  142. },
  143. output: `Created foo-v2
  144. Scaling up foo-v2 from 0 to 10, scaling down foo-v1 from 10 to 0 (keep 7 pods available, don't exceed 10 pods)
  145. Scaling foo-v1 down to 7
  146. Scaling foo-v2 up to 3
  147. Scaling foo-v1 down to 4
  148. Scaling foo-v2 up to 6
  149. Scaling foo-v1 down to 1
  150. Scaling foo-v2 up to 9
  151. Scaling foo-v1 down to 0
  152. Scaling foo-v2 up to 10
  153. `,
  154. },
  155. {
  156. name: "10->10 30/0 delayed readiness",
  157. oldRc: oldRc(10, 10),
  158. newRc: newRc(0, 10),
  159. newRcExists: false,
  160. maxUnavail: intstr.FromString("30%"),
  161. maxSurge: intstr.FromString("0%"),
  162. expected: []interface{}{
  163. down{oldReady: 10, newReady: 0, to: 7},
  164. up{3},
  165. down{oldReady: 7, newReady: 0, noop: true},
  166. down{oldReady: 7, newReady: 1, to: 6},
  167. up{4},
  168. down{oldReady: 6, newReady: 4, to: 3},
  169. up{7},
  170. down{oldReady: 3, newReady: 7, to: 0},
  171. up{10},
  172. },
  173. output: `Created foo-v2
  174. Scaling up foo-v2 from 0 to 10, scaling down foo-v1 from 10 to 0 (keep 7 pods available, don't exceed 10 pods)
  175. Scaling foo-v1 down to 7
  176. Scaling foo-v2 up to 3
  177. Scaling foo-v1 down to 6
  178. Scaling foo-v2 up to 4
  179. Scaling foo-v1 down to 3
  180. Scaling foo-v2 up to 7
  181. Scaling foo-v1 down to 0
  182. Scaling foo-v2 up to 10
  183. `,
  184. }, {
  185. name: "10->10 30/0 fast readiness, continuation",
  186. oldRc: oldRc(7, 10),
  187. newRc: newRc(3, 10),
  188. newRcExists: false,
  189. maxUnavail: intstr.FromString("30%"),
  190. maxSurge: intstr.FromString("0%"),
  191. expected: []interface{}{
  192. down{oldReady: 7, newReady: 3, to: 4},
  193. up{6},
  194. down{oldReady: 4, newReady: 6, to: 1},
  195. up{9},
  196. down{oldReady: 1, newReady: 9, to: 0},
  197. up{10},
  198. },
  199. output: `Created foo-v2
  200. Scaling up foo-v2 from 3 to 10, scaling down foo-v1 from 7 to 0 (keep 7 pods available, don't exceed 10 pods)
  201. Scaling foo-v1 down to 4
  202. Scaling foo-v2 up to 6
  203. Scaling foo-v1 down to 1
  204. Scaling foo-v2 up to 9
  205. Scaling foo-v1 down to 0
  206. Scaling foo-v2 up to 10
  207. `,
  208. }, {
  209. name: "10->10 30/0 fast readiness, continued after restart which prevented first scale-up",
  210. oldRc: oldRc(7, 10),
  211. newRc: newRc(0, 10),
  212. newRcExists: false,
  213. maxUnavail: intstr.FromString("30%"),
  214. maxSurge: intstr.FromString("0%"),
  215. expected: []interface{}{
  216. down{oldReady: 7, newReady: 0, noop: true},
  217. up{3},
  218. down{oldReady: 7, newReady: 3, to: 4},
  219. up{6},
  220. down{oldReady: 4, newReady: 6, to: 1},
  221. up{9},
  222. down{oldReady: 1, newReady: 9, to: 0},
  223. up{10},
  224. },
  225. output: `Created foo-v2
  226. Scaling up foo-v2 from 0 to 10, scaling down foo-v1 from 7 to 0 (keep 7 pods available, don't exceed 10 pods)
  227. Scaling foo-v2 up to 3
  228. Scaling foo-v1 down to 4
  229. Scaling foo-v2 up to 6
  230. Scaling foo-v1 down to 1
  231. Scaling foo-v2 up to 9
  232. Scaling foo-v1 down to 0
  233. Scaling foo-v2 up to 10
  234. `,
  235. }, {
  236. name: "10->10 0/30 fast readiness",
  237. oldRc: oldRc(10, 10),
  238. newRc: newRc(0, 10),
  239. newRcExists: false,
  240. maxUnavail: intstr.FromString("0%"),
  241. maxSurge: intstr.FromString("30%"),
  242. expected: []interface{}{
  243. up{3},
  244. down{oldReady: 10, newReady: 3, to: 7},
  245. up{6},
  246. down{oldReady: 7, newReady: 6, to: 4},
  247. up{9},
  248. down{oldReady: 4, newReady: 9, to: 1},
  249. up{10},
  250. down{oldReady: 1, newReady: 10, to: 0},
  251. },
  252. output: `Created foo-v2
  253. Scaling up foo-v2 from 0 to 10, scaling down foo-v1 from 10 to 0 (keep 10 pods available, don't exceed 13 pods)
  254. Scaling foo-v2 up to 3
  255. Scaling foo-v1 down to 7
  256. Scaling foo-v2 up to 6
  257. Scaling foo-v1 down to 4
  258. Scaling foo-v2 up to 9
  259. Scaling foo-v1 down to 1
  260. Scaling foo-v2 up to 10
  261. Scaling foo-v1 down to 0
  262. `,
  263. }, {
  264. name: "10->10 0/30 delayed readiness",
  265. oldRc: oldRc(10, 10),
  266. newRc: newRc(0, 10),
  267. newRcExists: false,
  268. maxUnavail: intstr.FromString("0%"),
  269. maxSurge: intstr.FromString("30%"),
  270. expected: []interface{}{
  271. up{3},
  272. down{oldReady: 10, newReady: 0, noop: true},
  273. down{oldReady: 10, newReady: 1, to: 9},
  274. up{4},
  275. down{oldReady: 9, newReady: 3, to: 7},
  276. up{6},
  277. down{oldReady: 7, newReady: 6, to: 4},
  278. up{9},
  279. down{oldReady: 4, newReady: 9, to: 1},
  280. up{10},
  281. down{oldReady: 1, newReady: 9, noop: true},
  282. down{oldReady: 1, newReady: 10, to: 0},
  283. },
  284. output: `Created foo-v2
  285. Scaling up foo-v2 from 0 to 10, scaling down foo-v1 from 10 to 0 (keep 10 pods available, don't exceed 13 pods)
  286. Scaling foo-v2 up to 3
  287. Scaling foo-v1 down to 9
  288. Scaling foo-v2 up to 4
  289. Scaling foo-v1 down to 7
  290. Scaling foo-v2 up to 6
  291. Scaling foo-v1 down to 4
  292. Scaling foo-v2 up to 9
  293. Scaling foo-v1 down to 1
  294. Scaling foo-v2 up to 10
  295. Scaling foo-v1 down to 0
  296. `,
  297. }, {
  298. name: "10->10 10/20 fast readiness",
  299. oldRc: oldRc(10, 10),
  300. newRc: newRc(0, 10),
  301. newRcExists: false,
  302. maxUnavail: intstr.FromString("10%"),
  303. maxSurge: intstr.FromString("20%"),
  304. expected: []interface{}{
  305. up{2},
  306. down{oldReady: 10, newReady: 2, to: 7},
  307. up{5},
  308. down{oldReady: 7, newReady: 5, to: 4},
  309. up{8},
  310. down{oldReady: 4, newReady: 8, to: 1},
  311. up{10},
  312. down{oldReady: 1, newReady: 10, to: 0},
  313. },
  314. output: `Created foo-v2
  315. Scaling up foo-v2 from 0 to 10, scaling down foo-v1 from 10 to 0 (keep 9 pods available, don't exceed 12 pods)
  316. Scaling foo-v2 up to 2
  317. Scaling foo-v1 down to 7
  318. Scaling foo-v2 up to 5
  319. Scaling foo-v1 down to 4
  320. Scaling foo-v2 up to 8
  321. Scaling foo-v1 down to 1
  322. Scaling foo-v2 up to 10
  323. Scaling foo-v1 down to 0
  324. `,
  325. }, {
  326. name: "10->10 10/20 delayed readiness",
  327. oldRc: oldRc(10, 10),
  328. newRc: newRc(0, 10),
  329. newRcExists: false,
  330. maxUnavail: intstr.FromString("10%"),
  331. maxSurge: intstr.FromString("20%"),
  332. expected: []interface{}{
  333. up{2},
  334. down{oldReady: 10, newReady: 2, to: 7},
  335. up{5},
  336. down{oldReady: 7, newReady: 4, to: 5},
  337. up{7},
  338. down{oldReady: 5, newReady: 4, noop: true},
  339. down{oldReady: 5, newReady: 7, to: 2},
  340. up{10},
  341. down{oldReady: 2, newReady: 9, to: 0},
  342. },
  343. output: `Created foo-v2
  344. Scaling up foo-v2 from 0 to 10, scaling down foo-v1 from 10 to 0 (keep 9 pods available, don't exceed 12 pods)
  345. Scaling foo-v2 up to 2
  346. Scaling foo-v1 down to 7
  347. Scaling foo-v2 up to 5
  348. Scaling foo-v1 down to 5
  349. Scaling foo-v2 up to 7
  350. Scaling foo-v1 down to 2
  351. Scaling foo-v2 up to 10
  352. Scaling foo-v1 down to 0
  353. `,
  354. }, {
  355. name: "10->10 10/20 fast readiness continued after restart which prevented first scale-down",
  356. oldRc: oldRc(10, 10),
  357. newRc: newRc(2, 10),
  358. newRcExists: false,
  359. maxUnavail: intstr.FromString("10%"),
  360. maxSurge: intstr.FromString("20%"),
  361. expected: []interface{}{
  362. down{oldReady: 10, newReady: 2, to: 7},
  363. up{5},
  364. down{oldReady: 7, newReady: 5, to: 4},
  365. up{8},
  366. down{oldReady: 4, newReady: 8, to: 1},
  367. up{10},
  368. down{oldReady: 1, newReady: 10, to: 0},
  369. },
  370. output: `Created foo-v2
  371. Scaling up foo-v2 from 2 to 10, scaling down foo-v1 from 10 to 0 (keep 9 pods available, don't exceed 12 pods)
  372. Scaling foo-v1 down to 7
  373. Scaling foo-v2 up to 5
  374. Scaling foo-v1 down to 4
  375. Scaling foo-v2 up to 8
  376. Scaling foo-v1 down to 1
  377. Scaling foo-v2 up to 10
  378. Scaling foo-v1 down to 0
  379. `,
  380. }, {
  381. name: "10->10 0/100 fast readiness",
  382. oldRc: oldRc(10, 10),
  383. newRc: newRc(0, 10),
  384. newRcExists: false,
  385. maxUnavail: intstr.FromString("0%"),
  386. maxSurge: intstr.FromString("100%"),
  387. expected: []interface{}{
  388. up{10},
  389. down{oldReady: 10, newReady: 10, to: 0},
  390. },
  391. output: `Created foo-v2
  392. Scaling up foo-v2 from 0 to 10, scaling down foo-v1 from 10 to 0 (keep 10 pods available, don't exceed 20 pods)
  393. Scaling foo-v2 up to 10
  394. Scaling foo-v1 down to 0
  395. `,
  396. }, {
  397. name: "10->10 0/100 delayed readiness",
  398. oldRc: oldRc(10, 10),
  399. newRc: newRc(0, 10),
  400. newRcExists: false,
  401. maxUnavail: intstr.FromString("0%"),
  402. maxSurge: intstr.FromString("100%"),
  403. expected: []interface{}{
  404. up{10},
  405. down{oldReady: 10, newReady: 0, noop: true},
  406. down{oldReady: 10, newReady: 2, to: 8},
  407. down{oldReady: 8, newReady: 7, to: 3},
  408. down{oldReady: 3, newReady: 10, to: 0},
  409. },
  410. output: `Created foo-v2
  411. Scaling up foo-v2 from 0 to 10, scaling down foo-v1 from 10 to 0 (keep 10 pods available, don't exceed 20 pods)
  412. Scaling foo-v2 up to 10
  413. Scaling foo-v1 down to 8
  414. Scaling foo-v1 down to 3
  415. Scaling foo-v1 down to 0
  416. `,
  417. }, {
  418. name: "10->10 100/0 fast readiness",
  419. oldRc: oldRc(10, 10),
  420. newRc: newRc(0, 10),
  421. newRcExists: false,
  422. maxUnavail: intstr.FromString("100%"),
  423. maxSurge: intstr.FromString("0%"),
  424. expected: []interface{}{
  425. down{oldReady: 10, newReady: 0, to: 0},
  426. up{10},
  427. },
  428. output: `Created foo-v2
  429. Scaling up foo-v2 from 0 to 10, scaling down foo-v1 from 10 to 0 (keep 0 pods available, don't exceed 10 pods)
  430. Scaling foo-v1 down to 0
  431. Scaling foo-v2 up to 10
  432. `,
  433. }, {
  434. name: "1->1 25/25 maintain minimum availability",
  435. oldRc: oldRc(1, 1),
  436. newRc: newRc(0, 1),
  437. newRcExists: false,
  438. maxUnavail: intstr.FromString("25%"),
  439. maxSurge: intstr.FromString("25%"),
  440. expected: []interface{}{
  441. up{1},
  442. down{oldReady: 1, newReady: 0, noop: true},
  443. down{oldReady: 1, newReady: 1, to: 0},
  444. },
  445. output: `Created foo-v2
  446. Scaling up foo-v2 from 0 to 1, scaling down foo-v1 from 1 to 0 (keep 1 pods available, don't exceed 2 pods)
  447. Scaling foo-v2 up to 1
  448. Scaling foo-v1 down to 0
  449. `,
  450. }, {
  451. name: "1->1 0/10 delayed readiness",
  452. oldRc: oldRc(1, 1),
  453. newRc: newRc(0, 1),
  454. newRcExists: false,
  455. maxUnavail: intstr.FromString("0%"),
  456. maxSurge: intstr.FromString("10%"),
  457. expected: []interface{}{
  458. up{1},
  459. down{oldReady: 1, newReady: 0, noop: true},
  460. down{oldReady: 1, newReady: 1, to: 0},
  461. },
  462. output: `Created foo-v2
  463. Scaling up foo-v2 from 0 to 1, scaling down foo-v1 from 1 to 0 (keep 1 pods available, don't exceed 2 pods)
  464. Scaling foo-v2 up to 1
  465. Scaling foo-v1 down to 0
  466. `,
  467. }, {
  468. name: "1->1 10/10 delayed readiness",
  469. oldRc: oldRc(1, 1),
  470. newRc: newRc(0, 1),
  471. newRcExists: false,
  472. maxUnavail: intstr.FromString("10%"),
  473. maxSurge: intstr.FromString("10%"),
  474. expected: []interface{}{
  475. up{1},
  476. down{oldReady: 1, newReady: 0, noop: true},
  477. down{oldReady: 1, newReady: 1, to: 0},
  478. },
  479. output: `Created foo-v2
  480. Scaling up foo-v2 from 0 to 1, scaling down foo-v1 from 1 to 0 (keep 1 pods available, don't exceed 2 pods)
  481. Scaling foo-v2 up to 1
  482. Scaling foo-v1 down to 0
  483. `,
  484. }, {
  485. name: "3->3 1/1 fast readiness (absolute values)",
  486. oldRc: oldRc(3, 3),
  487. newRc: newRc(0, 3),
  488. newRcExists: false,
  489. maxUnavail: intstr.FromInt(0),
  490. maxSurge: intstr.FromInt(1),
  491. expected: []interface{}{
  492. up{1},
  493. down{oldReady: 3, newReady: 1, to: 2},
  494. up{2},
  495. down{oldReady: 2, newReady: 2, to: 1},
  496. up{3},
  497. down{oldReady: 1, newReady: 3, to: 0},
  498. },
  499. output: `Created foo-v2
  500. Scaling up foo-v2 from 0 to 3, scaling down foo-v1 from 3 to 0 (keep 3 pods available, don't exceed 4 pods)
  501. Scaling foo-v2 up to 1
  502. Scaling foo-v1 down to 2
  503. Scaling foo-v2 up to 2
  504. Scaling foo-v1 down to 1
  505. Scaling foo-v2 up to 3
  506. Scaling foo-v1 down to 0
  507. `,
  508. }, {
  509. name: "10->10 0/20 fast readiness, continued after restart which resulted in partial first scale-up",
  510. oldRc: oldRc(6, 10),
  511. newRc: newRc(5, 10),
  512. newRcExists: false,
  513. maxUnavail: intstr.FromString("0%"),
  514. maxSurge: intstr.FromString("20%"),
  515. expected: []interface{}{
  516. up{6},
  517. down{oldReady: 6, newReady: 6, to: 4},
  518. up{8},
  519. down{oldReady: 4, newReady: 8, to: 2},
  520. up{10},
  521. down{oldReady: 1, newReady: 10, to: 0},
  522. },
  523. output: `Created foo-v2
  524. Scaling up foo-v2 from 5 to 10, scaling down foo-v1 from 6 to 0 (keep 10 pods available, don't exceed 12 pods)
  525. Scaling foo-v2 up to 6
  526. Scaling foo-v1 down to 4
  527. Scaling foo-v2 up to 8
  528. Scaling foo-v1 down to 2
  529. Scaling foo-v2 up to 10
  530. Scaling foo-v1 down to 0
  531. `,
  532. }, {
  533. name: "10->20 0/300 fast readiness",
  534. oldRc: oldRc(10, 10),
  535. newRc: newRc(0, 20),
  536. newRcExists: false,
  537. maxUnavail: intstr.FromString("0%"),
  538. maxSurge: intstr.FromString("300%"),
  539. expected: []interface{}{
  540. up{20},
  541. down{oldReady: 10, newReady: 20, to: 0},
  542. },
  543. output: `Created foo-v2
  544. Scaling up foo-v2 from 0 to 20, scaling down foo-v1 from 10 to 0 (keep 20 pods available, don't exceed 80 pods)
  545. Scaling foo-v2 up to 20
  546. Scaling foo-v1 down to 0
  547. `,
  548. }, {
  549. name: "1->1 0/1 scale down unavailable rc to a ready rc (rollback)",
  550. oldRc: oldRc(1, 1),
  551. newRc: newRc(1, 1),
  552. newRcExists: true,
  553. maxUnavail: intstr.FromInt(0),
  554. maxSurge: intstr.FromInt(1),
  555. expected: []interface{}{
  556. up{1},
  557. down{oldReady: 0, newReady: 1, to: 0},
  558. },
  559. output: `Continuing update with existing controller foo-v2.
  560. Scaling up foo-v2 from 1 to 1, scaling down foo-v1 from 1 to 0 (keep 1 pods available, don't exceed 2 pods)
  561. Scaling foo-v1 down to 0
  562. `,
  563. },
  564. {
  565. name: "3->0 1/1 desired 0 (absolute values)",
  566. oldRc: oldRc(3, 3),
  567. newRc: newRc(0, 0),
  568. newRcExists: true,
  569. maxUnavail: intstr.FromInt(1),
  570. maxSurge: intstr.FromInt(1),
  571. expected: []interface{}{
  572. down{oldReady: 3, newReady: 0, to: 0},
  573. },
  574. output: `Continuing update with existing controller foo-v2.
  575. Scaling up foo-v2 from 0 to 0, scaling down foo-v1 from 3 to 0 (keep 0 pods available, don't exceed 1 pods)
  576. Scaling foo-v1 down to 0
  577. `,
  578. },
  579. {
  580. name: "3->0 10/10 desired 0 (percentages)",
  581. oldRc: oldRc(3, 3),
  582. newRc: newRc(0, 0),
  583. newRcExists: true,
  584. maxUnavail: intstr.FromString("10%"),
  585. maxSurge: intstr.FromString("10%"),
  586. expected: []interface{}{
  587. down{oldReady: 3, newReady: 0, to: 0},
  588. },
  589. output: `Continuing update with existing controller foo-v2.
  590. Scaling up foo-v2 from 0 to 0, scaling down foo-v1 from 3 to 0 (keep 0 pods available, don't exceed 0 pods)
  591. Scaling foo-v1 down to 0
  592. `,
  593. },
  594. {
  595. name: "3->0 10/10 desired 0 (create new RC)",
  596. oldRc: oldRc(3, 3),
  597. newRc: newRc(0, 0),
  598. newRcExists: false,
  599. maxUnavail: intstr.FromString("10%"),
  600. maxSurge: intstr.FromString("10%"),
  601. expected: []interface{}{
  602. down{oldReady: 3, newReady: 0, to: 0},
  603. },
  604. output: `Created foo-v2
  605. Scaling up foo-v2 from 0 to 0, scaling down foo-v1 from 3 to 0 (keep 0 pods available, don't exceed 0 pods)
  606. Scaling foo-v1 down to 0
  607. `,
  608. },
  609. {
  610. name: "0->0 1/1 desired 0 (absolute values)",
  611. oldRc: oldRc(0, 0),
  612. newRc: newRc(0, 0),
  613. newRcExists: true,
  614. maxUnavail: intstr.FromInt(1),
  615. maxSurge: intstr.FromInt(1),
  616. expected: []interface{}{
  617. down{oldReady: 0, newReady: 0, to: 0},
  618. },
  619. output: `Continuing update with existing controller foo-v2.
  620. Scaling up foo-v2 from 0 to 0, scaling down foo-v1 from 0 to 0 (keep 0 pods available, don't exceed 1 pods)
  621. `,
  622. }, {
  623. name: "30->2 50%/0",
  624. oldRc: oldRc(30, 30),
  625. newRc: newRc(0, 2),
  626. newRcExists: false,
  627. maxUnavail: intstr.FromString("50%"),
  628. maxSurge: intstr.FromInt(0),
  629. expected: []interface{}{
  630. down{oldReady: 30, newReady: 0, to: 1},
  631. up{1},
  632. down{oldReady: 1, newReady: 2, to: 0},
  633. up{2},
  634. },
  635. output: `Created foo-v2
  636. Scaling up foo-v2 from 0 to 2, scaling down foo-v1 from 30 to 0 (keep 1 pods available, don't exceed 2 pods)
  637. Scaling foo-v1 down to 1
  638. Scaling foo-v2 up to 1
  639. Scaling foo-v1 down to 0
  640. Scaling foo-v2 up to 2
  641. `,
  642. },
  643. {
  644. name: "2->2 1/0 blocked oldRc",
  645. oldRc: oldRc(2, 2),
  646. newRc: newRc(0, 2),
  647. newRcExists: false,
  648. maxUnavail: intstr.FromInt(1),
  649. maxSurge: intstr.FromInt(0),
  650. expected: []interface{}{
  651. down{oldReady: 1, newReady: 0, to: 1},
  652. up{1},
  653. down{oldReady: 1, newReady: 1, to: 0},
  654. up{2},
  655. },
  656. output: `Created foo-v2
  657. Scaling up foo-v2 from 0 to 2, scaling down foo-v1 from 2 to 0 (keep 1 pods available, don't exceed 2 pods)
  658. Scaling foo-v1 down to 1
  659. Scaling foo-v2 up to 1
  660. Scaling foo-v1 down to 0
  661. Scaling foo-v2 up to 2
  662. `,
  663. },
  664. {
  665. name: "1->1 1/0 allow maxUnavailability",
  666. oldRc: oldRc(1, 1),
  667. newRc: newRc(0, 1),
  668. newRcExists: false,
  669. maxUnavail: intstr.FromString("1%"),
  670. maxSurge: intstr.FromInt(0),
  671. expected: []interface{}{
  672. down{oldReady: 1, newReady: 0, to: 0},
  673. up{1},
  674. },
  675. output: `Created foo-v2
  676. Scaling up foo-v2 from 0 to 1, scaling down foo-v1 from 1 to 0 (keep 0 pods available, don't exceed 1 pods)
  677. Scaling foo-v1 down to 0
  678. Scaling foo-v2 up to 1
  679. `,
  680. },
  681. {
  682. name: "1->2 25/25 complex asymmetric deployment",
  683. oldRc: oldRc(1, 1),
  684. newRc: newRc(0, 2),
  685. newRcExists: false,
  686. maxUnavail: intstr.FromString("25%"),
  687. maxSurge: intstr.FromString("25%"),
  688. expected: []interface{}{
  689. up{2},
  690. down{oldReady: 1, newReady: 2, to: 0},
  691. },
  692. output: `Created foo-v2
  693. Scaling up foo-v2 from 0 to 2, scaling down foo-v1 from 1 to 0 (keep 2 pods available, don't exceed 3 pods)
  694. Scaling foo-v2 up to 2
  695. Scaling foo-v1 down to 0
  696. `,
  697. },
  698. {
  699. name: "2->2 25/1 maxSurge trumps maxUnavailable",
  700. oldRc: oldRc(2, 2),
  701. newRc: newRc(0, 2),
  702. newRcExists: false,
  703. maxUnavail: intstr.FromString("25%"),
  704. maxSurge: intstr.FromString("1%"),
  705. expected: []interface{}{
  706. up{1},
  707. down{oldReady: 2, newReady: 1, to: 1},
  708. up{2},
  709. down{oldReady: 1, newReady: 2, to: 0},
  710. },
  711. output: `Created foo-v2
  712. Scaling up foo-v2 from 0 to 2, scaling down foo-v1 from 2 to 0 (keep 2 pods available, don't exceed 3 pods)
  713. Scaling foo-v2 up to 1
  714. Scaling foo-v1 down to 1
  715. Scaling foo-v2 up to 2
  716. Scaling foo-v1 down to 0
  717. `,
  718. },
  719. {
  720. name: "2->2 25/0 maxUnavailable resolves to zero, then one",
  721. oldRc: oldRc(2, 2),
  722. newRc: newRc(0, 2),
  723. newRcExists: false,
  724. maxUnavail: intstr.FromString("25%"),
  725. maxSurge: intstr.FromString("0%"),
  726. expected: []interface{}{
  727. down{oldReady: 2, newReady: 0, to: 1},
  728. up{1},
  729. down{oldReady: 1, newReady: 1, to: 0},
  730. up{2},
  731. },
  732. output: `Created foo-v2
  733. Scaling up foo-v2 from 0 to 2, scaling down foo-v1 from 2 to 0 (keep 1 pods available, don't exceed 2 pods)
  734. Scaling foo-v1 down to 1
  735. Scaling foo-v2 up to 1
  736. Scaling foo-v1 down to 0
  737. Scaling foo-v2 up to 2
  738. `,
  739. },
  740. }
  741. for i, tt := range tests {
  742. // Extract expectations into some makeshift FIFOs so they can be returned
  743. // in the correct order from the right places. This lets scale downs be
  744. // expressed a single event even though the data is used from multiple
  745. // interface calls.
  746. t.Run(tt.name, func(t *testing.T) {
  747. oldReady := []int{}
  748. newReady := []int{}
  749. upTo := []int{}
  750. downTo := []int{}
  751. for _, event := range tt.expected {
  752. switch e := event.(type) {
  753. case down:
  754. oldReady = append(oldReady, e.oldReady)
  755. newReady = append(newReady, e.newReady)
  756. if !e.noop {
  757. downTo = append(downTo, e.to)
  758. }
  759. case up:
  760. upTo = append(upTo, e.to)
  761. }
  762. }
  763. // Make a way to get the next item from our FIFOs. Returns -1 if the array
  764. // is empty.
  765. next := func(s *[]int) int {
  766. slice := *s
  767. v := -1
  768. if len(slice) > 0 {
  769. v = slice[0]
  770. if len(slice) > 1 {
  771. *s = slice[1:]
  772. } else {
  773. *s = []int{}
  774. }
  775. }
  776. return v
  777. }
  778. t.Logf("running test %d (%s) (up: %v, down: %v, oldReady: %v, newReady: %v)", i, tt.name, upTo, downTo, oldReady, newReady)
  779. updater := &RollingUpdater{
  780. ns: "default",
  781. scaleAndWait: func(rc *corev1.ReplicationController, retry *RetryParams, wait *RetryParams) (*corev1.ReplicationController, error) {
  782. // Return a scale up or scale down expectation depending on the rc,
  783. // and throw errors if there is no expectation expressed for this
  784. // call.
  785. expected := -1
  786. switch {
  787. case rc == tt.newRc:
  788. t.Logf("scaling up %s to %d", rc.Name, rc.Spec.Replicas)
  789. expected = next(&upTo)
  790. case rc == tt.oldRc:
  791. t.Logf("scaling down %s to %d", rc.Name, rc.Spec.Replicas)
  792. expected = next(&downTo)
  793. }
  794. if expected == -1 {
  795. t.Fatalf("unexpected scale of %s to %d", rc.Name, rc.Spec.Replicas)
  796. } else if e, a := expected, int(*rc.Spec.Replicas); e != a {
  797. t.Fatalf("expected scale of %s to %d, got %d", rc.Name, e, a)
  798. }
  799. // Simulate the scale.
  800. rc.Status.Replicas = *rc.Spec.Replicas
  801. return rc, nil
  802. },
  803. getOrCreateTargetController: func(controller *corev1.ReplicationController, sourceID string) (*corev1.ReplicationController, bool, error) {
  804. // Simulate a create vs. update of an existing controller.
  805. return tt.newRc, tt.newRcExists, nil
  806. },
  807. cleanup: func(oldRc, newRc *corev1.ReplicationController, config *RollingUpdaterConfig) error {
  808. return nil
  809. },
  810. }
  811. // Set up a mock readiness check which handles the test assertions.
  812. updater.getReadyPods = func(oldRc, newRc *corev1.ReplicationController, minReadySecondsDeadline int32) (int32, int32, error) {
  813. // Return simulated readiness, and throw an error if this call has no
  814. // expectations defined.
  815. oldReady := next(&oldReady)
  816. newReady := next(&newReady)
  817. if oldReady == -1 || newReady == -1 {
  818. t.Fatalf("unexpected getReadyPods call for:\noldRc: %#v\nnewRc: %#v", oldRc, newRc)
  819. }
  820. return int32(oldReady), int32(newReady), nil
  821. }
  822. var buffer bytes.Buffer
  823. config := &RollingUpdaterConfig{
  824. Out: &buffer,
  825. OldRc: tt.oldRc,
  826. NewRc: tt.newRc,
  827. UpdatePeriod: 0,
  828. Interval: time.Millisecond,
  829. Timeout: time.Millisecond,
  830. CleanupPolicy: DeleteRollingUpdateCleanupPolicy,
  831. MaxUnavailable: tt.maxUnavail,
  832. MaxSurge: tt.maxSurge,
  833. }
  834. err := updater.Update(config)
  835. if err != nil {
  836. t.Errorf("unexpected error: %v", err)
  837. }
  838. if buffer.String() != tt.output {
  839. t.Errorf("Bad output. expected:\n%s\ngot:\n%s", tt.output, buffer.String())
  840. }
  841. })
  842. }
  843. }
  844. // TestUpdate_progressTimeout ensures that an update which isn't making any
  845. // progress will eventually time out with a specified error.
  846. func TestUpdate_progressTimeout(t *testing.T) {
  847. oldRc := oldRc(2, 2)
  848. newRc := newRc(0, 2)
  849. updater := &RollingUpdater{
  850. ns: "default",
  851. scaleAndWait: func(rc *corev1.ReplicationController, retry *RetryParams, wait *RetryParams) (*corev1.ReplicationController, error) {
  852. // Do nothing.
  853. return rc, nil
  854. },
  855. getOrCreateTargetController: func(controller *corev1.ReplicationController, sourceID string) (*corev1.ReplicationController, bool, error) {
  856. return newRc, false, nil
  857. },
  858. cleanup: func(oldRc, newRc *corev1.ReplicationController, config *RollingUpdaterConfig) error {
  859. return nil
  860. },
  861. }
  862. updater.getReadyPods = func(oldRc, newRc *corev1.ReplicationController, minReadySeconds int32) (int32, int32, error) {
  863. // Coerce a timeout by pods never becoming ready.
  864. return 0, 0, nil
  865. }
  866. var buffer bytes.Buffer
  867. config := &RollingUpdaterConfig{
  868. Out: &buffer,
  869. OldRc: oldRc,
  870. NewRc: newRc,
  871. UpdatePeriod: 0,
  872. Interval: time.Millisecond,
  873. Timeout: time.Millisecond,
  874. CleanupPolicy: DeleteRollingUpdateCleanupPolicy,
  875. MaxUnavailable: intstr.FromInt(0),
  876. MaxSurge: intstr.FromInt(1),
  877. }
  878. err := updater.Update(config)
  879. if err == nil {
  880. t.Fatalf("expected an error")
  881. }
  882. if e, a := "timed out waiting for any update progress to be made", err.Error(); e != a {
  883. t.Fatalf("expected error message: %s, got: %s", e, a)
  884. }
  885. }
  886. func TestUpdate_assignOriginalAnnotation(t *testing.T) {
  887. oldRc := oldRc(1, 1)
  888. delete(oldRc.Annotations, originalReplicasAnnotation)
  889. newRc := newRc(1, 1)
  890. fake := fake.NewSimpleClientset(oldRc)
  891. updater := &RollingUpdater{
  892. rcClient: fake.CoreV1(),
  893. podClient: fake.CoreV1(),
  894. ns: "default",
  895. scaleAndWait: func(rc *corev1.ReplicationController, retry *RetryParams, wait *RetryParams) (*corev1.ReplicationController, error) {
  896. return rc, nil
  897. },
  898. getOrCreateTargetController: func(controller *corev1.ReplicationController, sourceID string) (*corev1.ReplicationController, bool, error) {
  899. return newRc, false, nil
  900. },
  901. cleanup: func(oldRc, newRc *corev1.ReplicationController, config *RollingUpdaterConfig) error {
  902. return nil
  903. },
  904. getReadyPods: func(oldRc, newRc *corev1.ReplicationController, minReadySeconds int32) (int32, int32, error) {
  905. return 1, 1, nil
  906. },
  907. }
  908. var buffer bytes.Buffer
  909. config := &RollingUpdaterConfig{
  910. Out: &buffer,
  911. OldRc: oldRc,
  912. NewRc: newRc,
  913. UpdatePeriod: 0,
  914. Interval: time.Millisecond,
  915. Timeout: time.Millisecond,
  916. CleanupPolicy: DeleteRollingUpdateCleanupPolicy,
  917. MaxUnavailable: intstr.FromString("100%"),
  918. }
  919. err := updater.Update(config)
  920. if err != nil {
  921. t.Fatalf("unexpected error: %v", err)
  922. }
  923. updateAction := fake.Actions()[1].(testcore.UpdateAction)
  924. if updateAction.GetResource().GroupResource() != corev1.Resource("replicationcontrollers") {
  925. t.Fatalf("expected rc to be updated: %#v", updateAction)
  926. }
  927. if e, a := "1", updateAction.GetObject().(*corev1.ReplicationController).Annotations[originalReplicasAnnotation]; e != a {
  928. t.Fatalf("expected annotation value %s, got %s", e, a)
  929. }
  930. }
  931. func TestRollingUpdater_multipleContainersInPod(t *testing.T) {
  932. tests := []struct {
  933. name string
  934. oldRc *corev1.ReplicationController
  935. newRc *corev1.ReplicationController
  936. container string
  937. image string
  938. deploymentKey string
  939. }{
  940. {
  941. name: "test1",
  942. oldRc: &corev1.ReplicationController{
  943. ObjectMeta: metav1.ObjectMeta{
  944. Namespace: metav1.NamespaceDefault,
  945. Name: "foo",
  946. },
  947. Spec: corev1.ReplicationControllerSpec{
  948. Selector: map[string]string{
  949. "dk": "old",
  950. },
  951. Template: &corev1.PodTemplateSpec{
  952. ObjectMeta: metav1.ObjectMeta{
  953. Labels: map[string]string{
  954. "dk": "old",
  955. },
  956. },
  957. Spec: corev1.PodSpec{
  958. Containers: []corev1.Container{
  959. {
  960. Name: "container1",
  961. Image: "image1",
  962. },
  963. {
  964. Name: "container2",
  965. Image: "image2",
  966. },
  967. },
  968. },
  969. },
  970. },
  971. },
  972. newRc: &corev1.ReplicationController{
  973. ObjectMeta: metav1.ObjectMeta{
  974. Namespace: metav1.NamespaceDefault,
  975. Name: "foo",
  976. },
  977. Spec: corev1.ReplicationControllerSpec{
  978. Selector: map[string]string{
  979. "dk": "old",
  980. },
  981. Template: &corev1.PodTemplateSpec{
  982. ObjectMeta: metav1.ObjectMeta{
  983. Labels: map[string]string{
  984. "dk": "old",
  985. },
  986. },
  987. Spec: corev1.PodSpec{
  988. Containers: []corev1.Container{
  989. {
  990. Name: "container1",
  991. Image: "newimage",
  992. },
  993. {
  994. Name: "container2",
  995. Image: "image2",
  996. },
  997. },
  998. },
  999. },
  1000. },
  1001. },
  1002. container: "container1",
  1003. image: "newimage",
  1004. deploymentKey: "dk",
  1005. },
  1006. {
  1007. name: "test2",
  1008. oldRc: &corev1.ReplicationController{
  1009. ObjectMeta: metav1.ObjectMeta{
  1010. Namespace: metav1.NamespaceDefault,
  1011. Name: "bar",
  1012. },
  1013. Spec: corev1.ReplicationControllerSpec{
  1014. Selector: map[string]string{
  1015. "dk": "old",
  1016. },
  1017. Template: &corev1.PodTemplateSpec{
  1018. ObjectMeta: metav1.ObjectMeta{
  1019. Labels: map[string]string{
  1020. "dk": "old",
  1021. },
  1022. },
  1023. Spec: corev1.PodSpec{
  1024. Containers: []corev1.Container{
  1025. {
  1026. Name: "container1",
  1027. Image: "image1",
  1028. },
  1029. },
  1030. },
  1031. },
  1032. },
  1033. },
  1034. newRc: &corev1.ReplicationController{
  1035. ObjectMeta: metav1.ObjectMeta{
  1036. Namespace: metav1.NamespaceDefault,
  1037. Name: "bar",
  1038. },
  1039. Spec: corev1.ReplicationControllerSpec{
  1040. Selector: map[string]string{
  1041. "dk": "old",
  1042. },
  1043. Template: &corev1.PodTemplateSpec{
  1044. ObjectMeta: metav1.ObjectMeta{
  1045. Labels: map[string]string{
  1046. "dk": "old",
  1047. },
  1048. },
  1049. Spec: corev1.PodSpec{
  1050. Containers: []corev1.Container{
  1051. {
  1052. Name: "container1",
  1053. Image: "newimage",
  1054. },
  1055. },
  1056. },
  1057. },
  1058. },
  1059. },
  1060. container: "container1",
  1061. image: "newimage",
  1062. deploymentKey: "dk",
  1063. },
  1064. }
  1065. for _, tt := range tests {
  1066. t.Run(tt.name, func(t *testing.T) {
  1067. fake := fake.NewSimpleClientset(tt.oldRc)
  1068. codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
  1069. deploymentHash, err := util.HashObject(tt.newRc, codec)
  1070. if err != nil {
  1071. t.Errorf("unexpected error: %v", err)
  1072. }
  1073. tt.newRc.Spec.Selector[tt.deploymentKey] = deploymentHash
  1074. tt.newRc.Spec.Template.Labels[tt.deploymentKey] = deploymentHash
  1075. tt.newRc.Name = fmt.Sprintf("%s-%s", tt.newRc.Name, deploymentHash)
  1076. config := &NewControllerConfig{
  1077. Namespace: metav1.NamespaceDefault,
  1078. OldName: tt.oldRc.ObjectMeta.Name,
  1079. NewName: tt.newRc.ObjectMeta.Name,
  1080. Image: tt.image,
  1081. Container: tt.container,
  1082. DeploymentKey: tt.deploymentKey,
  1083. }
  1084. updatedRc, err := CreateNewControllerFromCurrentController(fake.CoreV1(), codec, config)
  1085. if err != nil {
  1086. t.Errorf("unexpected error: %v", err)
  1087. }
  1088. if !reflect.DeepEqual(updatedRc, tt.newRc) {
  1089. t.Errorf("expected:\n%#v\ngot:\n%#v\n", tt.newRc, updatedRc)
  1090. }
  1091. })
  1092. }
  1093. }
  1094. // TestRollingUpdater_cleanupWithClients ensures that the cleanup policy is
  1095. // correctly implemented.
  1096. func TestRollingUpdater_cleanupWithClients(t *testing.T) {
  1097. rc := oldRc(2, 2)
  1098. rcExisting := newRc(1, 3)
  1099. tests := []struct {
  1100. name string
  1101. policy RollingUpdaterCleanupPolicy
  1102. responses []runtime.Object
  1103. expected []string
  1104. }{
  1105. {
  1106. name: "preserve",
  1107. policy: PreserveRollingUpdateCleanupPolicy,
  1108. responses: []runtime.Object{rcExisting},
  1109. expected: []string{
  1110. "get",
  1111. "update",
  1112. "get",
  1113. "get",
  1114. },
  1115. },
  1116. {
  1117. name: "delete",
  1118. policy: DeleteRollingUpdateCleanupPolicy,
  1119. responses: []runtime.Object{rcExisting},
  1120. expected: []string{
  1121. "get",
  1122. "update",
  1123. "get",
  1124. "get",
  1125. "delete",
  1126. },
  1127. },
  1128. //{
  1129. // This cases is separated to a standalone
  1130. // TestRollingUpdater_cleanupWithClients_Rename. We have to do this
  1131. // because the unversioned fake client is unable to delete objects.
  1132. // TODO: uncomment this case when the unversioned fake client uses
  1133. // pkg/client/testing/core.
  1134. // {
  1135. // name: "rename",
  1136. // policy: RenameRollingUpdateCleanupPolicy,
  1137. // responses: []runtime.Object{rcExisting},
  1138. // expected: []string{
  1139. // "get",
  1140. // "update",
  1141. // "get",
  1142. // "get",
  1143. // "delete",
  1144. // "create",
  1145. // "delete",
  1146. // },
  1147. // },
  1148. //},
  1149. }
  1150. for _, tt := range tests {
  1151. t.Run(tt.name, func(t *testing.T) {
  1152. objs := []runtime.Object{rc}
  1153. objs = append(objs, tt.responses...)
  1154. fake := fake.NewSimpleClientset(objs...)
  1155. updater := &RollingUpdater{
  1156. ns: "default",
  1157. rcClient: fake.CoreV1(),
  1158. podClient: fake.CoreV1(),
  1159. }
  1160. config := &RollingUpdaterConfig{
  1161. Out: ioutil.Discard,
  1162. OldRc: rc,
  1163. NewRc: rcExisting,
  1164. UpdatePeriod: 0,
  1165. Interval: time.Millisecond,
  1166. Timeout: time.Millisecond,
  1167. CleanupPolicy: tt.policy,
  1168. }
  1169. err := updater.cleanupWithClients(rc, rcExisting, config)
  1170. if err != nil {
  1171. t.Errorf("unexpected error: %v", err)
  1172. }
  1173. if len(fake.Actions()) != len(tt.expected) {
  1174. t.Fatalf("%s: unexpected actions: %v, expected %v", tt.name, fake.Actions(), tt.expected)
  1175. }
  1176. for j, action := range fake.Actions() {
  1177. if e, a := tt.expected[j], action.GetVerb(); e != a {
  1178. t.Errorf("%s: unexpected action: expected %s, got %s", tt.name, e, a)
  1179. }
  1180. }
  1181. })
  1182. }
  1183. }
  1184. // TestRollingUpdater_cleanupWithClients_Rename tests the rename cleanup policy. It's separated to
  1185. // a standalone test because the unversioned fake client is unable to delete
  1186. // objects.
  1187. // TODO: move this test back to TestRollingUpdater_cleanupWithClients
  1188. // when the fake client uses pkg/client/testing/core in the future.
  1189. func TestRollingUpdater_cleanupWithClients_Rename(t *testing.T) {
  1190. rc := oldRc(2, 2)
  1191. rcExisting := newRc(1, 3)
  1192. expectedActions := []string{"delete", "get", "create"}
  1193. fake := fake.NewSimpleClientset()
  1194. fake.AddReactor("*", "*", func(action testcore.Action) (handled bool, ret runtime.Object, err error) {
  1195. switch action.(type) {
  1196. case testcore.CreateAction:
  1197. return true, nil, nil
  1198. case testcore.GetAction:
  1199. return true, nil, errors.NewNotFound(schema.GroupResource{}, "")
  1200. case testcore.DeleteAction:
  1201. return true, nil, nil
  1202. }
  1203. return false, nil, nil
  1204. })
  1205. err := Rename(fake.CoreV1(), rcExisting, rc.Name)
  1206. if err != nil {
  1207. t.Fatal(err)
  1208. }
  1209. for j, action := range fake.Actions() {
  1210. if e, a := expectedActions[j], action.GetVerb(); e != a {
  1211. t.Errorf("unexpected action: expected %s, got %s", e, a)
  1212. }
  1213. }
  1214. }
  1215. func TestFindSourceController(t *testing.T) {
  1216. ctrl1 := corev1.ReplicationController{
  1217. ObjectMeta: metav1.ObjectMeta{
  1218. Namespace: metav1.NamespaceDefault,
  1219. Name: "foo",
  1220. Annotations: map[string]string{
  1221. sourceIDAnnotation: "bar:1234",
  1222. },
  1223. },
  1224. }
  1225. ctrl2 := corev1.ReplicationController{
  1226. ObjectMeta: metav1.ObjectMeta{
  1227. Namespace: metav1.NamespaceDefault,
  1228. Name: "bar",
  1229. Annotations: map[string]string{
  1230. sourceIDAnnotation: "foo:12345",
  1231. },
  1232. },
  1233. }
  1234. ctrl3 := corev1.ReplicationController{
  1235. ObjectMeta: metav1.ObjectMeta{
  1236. Namespace: metav1.NamespaceDefault,
  1237. Name: "baz",
  1238. Annotations: map[string]string{
  1239. sourceIDAnnotation: "baz:45667",
  1240. },
  1241. },
  1242. }
  1243. tests := []struct {
  1244. list *corev1.ReplicationControllerList
  1245. expectedController *corev1.ReplicationController
  1246. name string
  1247. expectError bool
  1248. }{
  1249. {
  1250. list: &corev1.ReplicationControllerList{},
  1251. expectError: true,
  1252. },
  1253. {
  1254. list: &corev1.ReplicationControllerList{
  1255. Items: []corev1.ReplicationController{ctrl1},
  1256. },
  1257. name: "foo",
  1258. expectError: true,
  1259. },
  1260. {
  1261. list: &corev1.ReplicationControllerList{
  1262. Items: []corev1.ReplicationController{ctrl1},
  1263. },
  1264. name: "bar",
  1265. expectedController: &ctrl1,
  1266. },
  1267. {
  1268. list: &corev1.ReplicationControllerList{
  1269. Items: []corev1.ReplicationController{ctrl1, ctrl2},
  1270. },
  1271. name: "bar",
  1272. expectedController: &ctrl1,
  1273. },
  1274. {
  1275. list: &corev1.ReplicationControllerList{
  1276. Items: []corev1.ReplicationController{ctrl1, ctrl2},
  1277. },
  1278. name: "foo",
  1279. expectedController: &ctrl2,
  1280. },
  1281. {
  1282. list: &corev1.ReplicationControllerList{
  1283. Items: []corev1.ReplicationController{ctrl1, ctrl2, ctrl3},
  1284. },
  1285. name: "baz",
  1286. expectedController: &ctrl3,
  1287. },
  1288. }
  1289. for _, tt := range tests {
  1290. t.Run(tt.name, func(t *testing.T) {
  1291. fakeClient := fake.NewSimpleClientset(tt.list)
  1292. ctrl, err := FindSourceController(fakeClient.CoreV1(), "default", tt.name)
  1293. if tt.expectError && err == nil {
  1294. t.Errorf("unexpected non-error")
  1295. }
  1296. if !tt.expectError && err != nil {
  1297. t.Errorf("unexpected error")
  1298. }
  1299. if !reflect.DeepEqual(ctrl, tt.expectedController) {
  1300. t.Errorf("expected:\n%v\ngot:\n%v\n", tt.expectedController, ctrl)
  1301. }
  1302. })
  1303. }
  1304. }
  1305. func TestUpdateExistingReplicationController(t *testing.T) {
  1306. tests := []struct {
  1307. rc *corev1.ReplicationController
  1308. name string
  1309. deploymentKey string
  1310. deploymentValue string
  1311. expectedRc *corev1.ReplicationController
  1312. expectErr bool
  1313. }{
  1314. {
  1315. rc: &corev1.ReplicationController{
  1316. ObjectMeta: metav1.ObjectMeta{
  1317. Namespace: metav1.NamespaceDefault,
  1318. Name: "foo",
  1319. },
  1320. Spec: corev1.ReplicationControllerSpec{
  1321. Template: &corev1.PodTemplateSpec{},
  1322. },
  1323. },
  1324. name: "foo",
  1325. deploymentKey: "dk",
  1326. deploymentValue: "some-hash",
  1327. expectedRc: &corev1.ReplicationController{
  1328. ObjectMeta: metav1.ObjectMeta{
  1329. Namespace: metav1.NamespaceDefault,
  1330. Name: "foo",
  1331. Annotations: map[string]string{
  1332. "kubectl.kubernetes.io/next-controller-id": "foo",
  1333. },
  1334. },
  1335. Spec: corev1.ReplicationControllerSpec{
  1336. Selector: map[string]string{
  1337. "dk": "some-hash",
  1338. },
  1339. Template: &corev1.PodTemplateSpec{
  1340. ObjectMeta: metav1.ObjectMeta{
  1341. Labels: map[string]string{
  1342. "dk": "some-hash",
  1343. },
  1344. },
  1345. },
  1346. },
  1347. },
  1348. },
  1349. {
  1350. rc: &corev1.ReplicationController{
  1351. ObjectMeta: metav1.ObjectMeta{
  1352. Namespace: metav1.NamespaceDefault,
  1353. Name: "foo",
  1354. },
  1355. Spec: corev1.ReplicationControllerSpec{
  1356. Template: &corev1.PodTemplateSpec{
  1357. ObjectMeta: metav1.ObjectMeta{
  1358. Labels: map[string]string{
  1359. "dk": "some-other-hash",
  1360. },
  1361. },
  1362. },
  1363. Selector: map[string]string{
  1364. "dk": "some-other-hash",
  1365. },
  1366. },
  1367. },
  1368. name: "foo",
  1369. deploymentKey: "dk",
  1370. deploymentValue: "some-hash",
  1371. expectedRc: &corev1.ReplicationController{
  1372. ObjectMeta: metav1.ObjectMeta{
  1373. Namespace: metav1.NamespaceDefault,
  1374. Name: "foo",
  1375. Annotations: map[string]string{
  1376. "kubectl.kubernetes.io/next-controller-id": "foo",
  1377. },
  1378. },
  1379. Spec: corev1.ReplicationControllerSpec{
  1380. Selector: map[string]string{
  1381. "dk": "some-other-hash",
  1382. },
  1383. Template: &corev1.PodTemplateSpec{
  1384. ObjectMeta: metav1.ObjectMeta{
  1385. Labels: map[string]string{
  1386. "dk": "some-other-hash",
  1387. },
  1388. },
  1389. },
  1390. },
  1391. },
  1392. },
  1393. }
  1394. for _, tt := range tests {
  1395. t.Run(tt.name, func(t *testing.T) {
  1396. buffer := &bytes.Buffer{}
  1397. fakeClient := fake.NewSimpleClientset(tt.expectedRc)
  1398. rc, err := UpdateExistingReplicationController(fakeClient.CoreV1(), fakeClient.CoreV1(), tt.rc, "default", tt.name, tt.deploymentKey, tt.deploymentValue, buffer)
  1399. if !reflect.DeepEqual(rc, tt.expectedRc) {
  1400. t.Errorf("expected:\n%#v\ngot:\n%#v\n", tt.expectedRc, rc)
  1401. }
  1402. if tt.expectErr && err == nil {
  1403. t.Errorf("unexpected non-error")
  1404. }
  1405. if !tt.expectErr && err != nil {
  1406. t.Errorf("unexpected error: %v", err)
  1407. }
  1408. })
  1409. }
  1410. }
  1411. func TestUpdateRcWithRetries(t *testing.T) {
  1412. codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
  1413. one := int32(1)
  1414. grace := int64(30)
  1415. enableServiceLinks := corev1.DefaultEnableServiceLinks
  1416. rc := &corev1.ReplicationController{
  1417. TypeMeta: metav1.TypeMeta{
  1418. APIVersion: "v1",
  1419. Kind: "ReplicationController",
  1420. },
  1421. ObjectMeta: metav1.ObjectMeta{Name: "rc",
  1422. Labels: map[string]string{
  1423. "foo": "bar",
  1424. },
  1425. },
  1426. Spec: corev1.ReplicationControllerSpec{
  1427. Replicas: &one,
  1428. Selector: map[string]string{
  1429. "foo": "bar",
  1430. },
  1431. Template: &corev1.PodTemplateSpec{
  1432. ObjectMeta: metav1.ObjectMeta{
  1433. Labels: map[string]string{
  1434. "foo": "bar",
  1435. },
  1436. },
  1437. Spec: corev1.PodSpec{
  1438. RestartPolicy: corev1.RestartPolicyAlways,
  1439. DNSPolicy: corev1.DNSClusterFirst,
  1440. TerminationGracePeriodSeconds: &grace,
  1441. SecurityContext: &corev1.PodSecurityContext{},
  1442. EnableServiceLinks: &enableServiceLinks,
  1443. },
  1444. },
  1445. },
  1446. }
  1447. rc.Spec.Template.Spec.SchedulerName = "default-scheduler"
  1448. // Test end to end updating of the rc with retries. Essentially make sure the update handler
  1449. // sees the right updates, failures in update/get are handled properly, and that the updated
  1450. // rc with new resource version is returned to the caller. Without any of these rollingupdate
  1451. // will fail cryptically.
  1452. newRc := *rc
  1453. newRc.ResourceVersion = "2"
  1454. newRc.Spec.Selector["baz"] = "foobar"
  1455. header := http.Header{}
  1456. header.Set("Content-Type", runtime.ContentTypeJSON)
  1457. updates := []*http.Response{
  1458. {StatusCode: 409, Header: header, Body: objBody(codec, &corev1.ReplicationController{})}, // conflict
  1459. {StatusCode: 409, Header: header, Body: objBody(codec, &corev1.ReplicationController{})}, // conflict
  1460. {StatusCode: 200, Header: header, Body: objBody(codec, &newRc)},
  1461. }
  1462. gets := []*http.Response{
  1463. {StatusCode: 500, Header: header, Body: objBody(codec, &corev1.ReplicationController{})},
  1464. {StatusCode: 200, Header: header, Body: objBody(codec, rc)},
  1465. }
  1466. fakeClient := &manualfake.RESTClient{
  1467. GroupVersion: corev1.SchemeGroupVersion,
  1468. NegotiatedSerializer: scheme.Codecs,
  1469. Client: manualfake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
  1470. switch p, m := req.URL.Path, req.Method; {
  1471. case p == "/api/v1/namespaces/default/replicationcontrollers/rc" && m == "PUT":
  1472. update := updates[0]
  1473. updates = updates[1:]
  1474. // We should always get an update with a valid rc even when the get fails. The rc should always
  1475. // contain the update.
  1476. if c, ok := readOrDie(t, req, codec).(*corev1.ReplicationController); !ok || !apiequality.Semantic.DeepEqual(rc, c) {
  1477. t.Errorf("Unexpected update body, got %+v expected %+v", c, rc)
  1478. t.Error(diff.ObjectDiff(rc, c))
  1479. } else if sel, ok := c.Spec.Selector["baz"]; !ok || sel != "foobar" {
  1480. t.Errorf("Expected selector label update, got %+v", c.Spec.Selector)
  1481. } else {
  1482. delete(c.Spec.Selector, "baz")
  1483. }
  1484. return update, nil
  1485. case p == "/api/v1/namespaces/default/replicationcontrollers/rc" && m == "GET":
  1486. get := gets[0]
  1487. gets = gets[1:]
  1488. return get, nil
  1489. default:
  1490. t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
  1491. return nil, nil
  1492. }
  1493. }),
  1494. }
  1495. clientConfig := &restclient.Config{
  1496. APIPath: "/api",
  1497. ContentConfig: restclient.ContentConfig{
  1498. NegotiatedSerializer: scheme.Codecs,
  1499. GroupVersion: &corev1.SchemeGroupVersion,
  1500. },
  1501. }
  1502. restClient, _ := restclient.RESTClientFor(clientConfig)
  1503. restClient.Client = fakeClient.Client
  1504. clientset := kubernetes.New(restClient)
  1505. if rc, err := updateRcWithRetries(
  1506. clientset.CoreV1(), "default", rc, func(c *corev1.ReplicationController) {
  1507. c.Spec.Selector["baz"] = "foobar"
  1508. }); err != nil {
  1509. t.Errorf("unexpected error: %v", err)
  1510. } else if sel, ok := rc.Spec.Selector["baz"]; !ok || sel != "foobar" || rc.ResourceVersion != "2" {
  1511. t.Errorf("Expected updated rc, got %+v", rc)
  1512. }
  1513. if len(updates) != 0 || len(gets) != 0 {
  1514. t.Errorf("Remaining updates %#v gets %#v", updates, gets)
  1515. }
  1516. }
  1517. func readOrDie(t *testing.T, req *http.Request, codec runtime.Codec) runtime.Object {
  1518. data, err := ioutil.ReadAll(req.Body)
  1519. if err != nil {
  1520. t.Errorf("Error reading: %v", err)
  1521. t.FailNow()
  1522. }
  1523. codec2 := scheme.Codecs.UniversalDecoder(scheme.Scheme.PrioritizedVersionsAllGroups()...)
  1524. obj, err := runtime.Decode(codec2, data)
  1525. if err != nil {
  1526. t.Log(string(data))
  1527. t.Errorf("error decoding: %v", err)
  1528. t.FailNow()
  1529. }
  1530. return obj
  1531. }
  1532. func objBody(codec runtime.Codec, obj runtime.Object) io.ReadCloser {
  1533. return ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(codec, obj))))
  1534. }
  1535. func TestAddDeploymentHash(t *testing.T) {
  1536. buf := &bytes.Buffer{}
  1537. codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
  1538. rc := &corev1.ReplicationController{
  1539. ObjectMeta: metav1.ObjectMeta{Name: "rc"},
  1540. Spec: corev1.ReplicationControllerSpec{
  1541. Selector: map[string]string{
  1542. "foo": "bar",
  1543. },
  1544. Template: &corev1.PodTemplateSpec{
  1545. ObjectMeta: metav1.ObjectMeta{
  1546. Labels: map[string]string{
  1547. "foo": "bar",
  1548. },
  1549. },
  1550. },
  1551. },
  1552. }
  1553. podList := &corev1.PodList{
  1554. Items: []corev1.Pod{
  1555. {ObjectMeta: metav1.ObjectMeta{Name: "foo"}},
  1556. {ObjectMeta: metav1.ObjectMeta{Name: "bar"}},
  1557. {ObjectMeta: metav1.ObjectMeta{Name: "baz"}},
  1558. },
  1559. }
  1560. seen := sets.String{}
  1561. updatedRc := false
  1562. fakeClient := &manualfake.RESTClient{
  1563. GroupVersion: corev1.SchemeGroupVersion,
  1564. NegotiatedSerializer: scheme.Codecs,
  1565. Client: manualfake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
  1566. header := http.Header{}
  1567. header.Set("Content-Type", runtime.ContentTypeJSON)
  1568. switch p, m := req.URL.Path, req.Method; {
  1569. case p == "/api/v1/namespaces/default/pods" && m == "GET":
  1570. if req.URL.RawQuery != "labelSelector=foo%3Dbar" {
  1571. t.Errorf("Unexpected query string: %s", req.URL.RawQuery)
  1572. }
  1573. return &http.Response{StatusCode: 200, Header: header, Body: objBody(codec, podList)}, nil
  1574. case p == "/api/v1/namespaces/default/pods/foo" && m == "PUT":
  1575. seen.Insert("foo")
  1576. obj := readOrDie(t, req, codec)
  1577. podList.Items[0] = *(obj.(*corev1.Pod))
  1578. return &http.Response{StatusCode: 200, Header: header, Body: objBody(codec, &podList.Items[0])}, nil
  1579. case p == "/api/v1/namespaces/default/pods/bar" && m == "PUT":
  1580. seen.Insert("bar")
  1581. obj := readOrDie(t, req, codec)
  1582. podList.Items[1] = *(obj.(*corev1.Pod))
  1583. return &http.Response{StatusCode: 200, Header: header, Body: objBody(codec, &podList.Items[1])}, nil
  1584. case p == "/api/v1/namespaces/default/pods/baz" && m == "PUT":
  1585. seen.Insert("baz")
  1586. obj := readOrDie(t, req, codec)
  1587. podList.Items[2] = *(obj.(*corev1.Pod))
  1588. return &http.Response{StatusCode: 200, Header: header, Body: objBody(codec, &podList.Items[2])}, nil
  1589. case p == "/api/v1/namespaces/default/replicationcontrollers/rc" && m == "PUT":
  1590. updatedRc = true
  1591. return &http.Response{StatusCode: 200, Header: header, Body: objBody(codec, rc)}, nil
  1592. default:
  1593. t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
  1594. return nil, nil
  1595. }
  1596. }),
  1597. }
  1598. clientConfig := &restclient.Config{
  1599. APIPath: "/api",
  1600. ContentConfig: restclient.ContentConfig{
  1601. NegotiatedSerializer: scheme.Codecs,
  1602. GroupVersion: &corev1.SchemeGroupVersion,
  1603. },
  1604. }
  1605. restClient, _ := restclient.RESTClientFor(clientConfig)
  1606. restClient.Client = fakeClient.Client
  1607. clientset := kubernetes.New(restClient)
  1608. if _, err := AddDeploymentKeyToReplicationController(rc, clientset.CoreV1(), clientset.CoreV1(), "dk", "hash", metav1.NamespaceDefault, buf); err != nil {
  1609. t.Errorf("unexpected error: %v", err)
  1610. }
  1611. for _, pod := range podList.Items {
  1612. if !seen.Has(pod.Name) {
  1613. t.Errorf("Missing update for pod: %s", pod.Name)
  1614. }
  1615. }
  1616. if !updatedRc {
  1617. t.Errorf("Failed to update replication controller with new labels")
  1618. }
  1619. }
  1620. func TestRollingUpdater_readyPods(t *testing.T) {
  1621. count := 0
  1622. now := metav1.Date(2016, time.April, 1, 1, 0, 0, 0, time.UTC)
  1623. mkpod := func(owner *corev1.ReplicationController, ready bool, readyTime metav1.Time) *corev1.Pod {
  1624. count = count + 1
  1625. labels := map[string]string{}
  1626. for k, v := range owner.Spec.Selector {
  1627. labels[k] = v
  1628. }
  1629. status := corev1.ConditionTrue
  1630. if !ready {
  1631. status = corev1.ConditionFalse
  1632. }
  1633. return &corev1.Pod{
  1634. ObjectMeta: metav1.ObjectMeta{
  1635. Namespace: metav1.NamespaceDefault,
  1636. Name: fmt.Sprintf("pod-%d", count),
  1637. Labels: labels,
  1638. },
  1639. Status: corev1.PodStatus{
  1640. Conditions: []corev1.PodCondition{
  1641. {
  1642. Type: corev1.PodReady,
  1643. Status: status,
  1644. LastTransitionTime: readyTime,
  1645. },
  1646. },
  1647. },
  1648. }
  1649. }
  1650. tests := []struct {
  1651. name string
  1652. oldRc *corev1.ReplicationController
  1653. newRc *corev1.ReplicationController
  1654. // expectated old/new ready counts
  1655. oldReady int32
  1656. newReady int32
  1657. // pods owned by the rcs; indicate whether they're ready
  1658. oldPods []bool
  1659. newPods []bool
  1660. // deletions - should be less then the size of the respective slice above
  1661. // e.g. len(oldPods) > oldPodDeletions && len(newPods) > newPodDeletions
  1662. oldPodDeletions int
  1663. newPodDeletions int
  1664. // specify additional time to wait for deployment to wait on top of the
  1665. // pod ready time
  1666. minReadySeconds int32
  1667. podReadyTimeFn func() metav1.Time
  1668. nowFn func() metav1.Time
  1669. }{
  1670. {
  1671. name: "test1",
  1672. oldRc: oldRc(4, 4),
  1673. newRc: newRc(4, 4),
  1674. oldReady: 4,
  1675. newReady: 2,
  1676. oldPods: []bool{
  1677. true,
  1678. true,
  1679. true,
  1680. true,
  1681. },
  1682. newPods: []bool{
  1683. true,
  1684. false,
  1685. true,
  1686. false,
  1687. },
  1688. },
  1689. {
  1690. name: "test2",
  1691. oldRc: oldRc(4, 4),
  1692. newRc: newRc(4, 4),
  1693. oldReady: 0,
  1694. newReady: 1,
  1695. oldPods: []bool{
  1696. false,
  1697. },
  1698. newPods: []bool{
  1699. true,
  1700. },
  1701. },
  1702. {
  1703. name: "test3",
  1704. oldRc: oldRc(4, 4),
  1705. newRc: newRc(4, 4),
  1706. oldReady: 1,
  1707. newReady: 0,
  1708. oldPods: []bool{
  1709. true,
  1710. },
  1711. newPods: []bool{
  1712. false,
  1713. },
  1714. },
  1715. {
  1716. name: "test4",
  1717. oldRc: oldRc(4, 4),
  1718. newRc: newRc(4, 4),
  1719. oldReady: 0,
  1720. newReady: 0,
  1721. oldPods: []bool{
  1722. true,
  1723. },
  1724. newPods: []bool{
  1725. true,
  1726. },
  1727. minReadySeconds: 5,
  1728. nowFn: func() metav1.Time { return now },
  1729. },
  1730. {
  1731. name: "test5",
  1732. oldRc: oldRc(4, 4),
  1733. newRc: newRc(4, 4),
  1734. oldReady: 1,
  1735. newReady: 1,
  1736. oldPods: []bool{
  1737. true,
  1738. },
  1739. newPods: []bool{
  1740. true,
  1741. },
  1742. minReadySeconds: 5,
  1743. nowFn: func() metav1.Time { return metav1.Time{Time: now.Add(time.Duration(6 * time.Second))} },
  1744. podReadyTimeFn: func() metav1.Time { return now },
  1745. },
  1746. {
  1747. name: "test6",
  1748. oldRc: oldRc(4, 4),
  1749. newRc: newRc(4, 4),
  1750. oldReady: 2,
  1751. newReady: 0,
  1752. oldPods: []bool{
  1753. // All old pods are ready
  1754. true, true, true, true,
  1755. },
  1756. // Two of them have been marked for deletion though
  1757. oldPodDeletions: 2,
  1758. },
  1759. }
  1760. for i, tt := range tests {
  1761. t.Run(tt.name, func(t *testing.T) {
  1762. t.Logf("evaluating test %d", i)
  1763. if tt.nowFn == nil {
  1764. tt.nowFn = func() metav1.Time { return now }
  1765. }
  1766. if tt.podReadyTimeFn == nil {
  1767. tt.podReadyTimeFn = tt.nowFn
  1768. }
  1769. // Populate the fake client with pods associated with their owners.
  1770. pods := []runtime.Object{}
  1771. for _, ready := range tt.oldPods {
  1772. pod := mkpod(tt.oldRc, ready, tt.podReadyTimeFn())
  1773. if tt.oldPodDeletions > 0 {
  1774. now := metav1.Now()
  1775. pod.DeletionTimestamp = &now
  1776. tt.oldPodDeletions--
  1777. }
  1778. pods = append(pods, pod)
  1779. }
  1780. for _, ready := range tt.newPods {
  1781. pod := mkpod(tt.newRc, ready, tt.podReadyTimeFn())
  1782. if tt.newPodDeletions > 0 {
  1783. now := metav1.Now()
  1784. pod.DeletionTimestamp = &now
  1785. tt.newPodDeletions--
  1786. }
  1787. pods = append(pods, pod)
  1788. }
  1789. client := fake.NewSimpleClientset(pods...)
  1790. updater := &RollingUpdater{
  1791. ns: "default",
  1792. rcClient: client.CoreV1(),
  1793. podClient: client.CoreV1(),
  1794. nowFn: tt.nowFn,
  1795. }
  1796. oldReady, newReady, err := updater.readyPods(tt.oldRc, tt.newRc, tt.minReadySeconds)
  1797. if err != nil {
  1798. t.Errorf("unexpected error: %v", err)
  1799. }
  1800. if e, a := tt.oldReady, oldReady; e != a {
  1801. t.Errorf("expected old ready %d, got %d", e, a)
  1802. }
  1803. if e, a := tt.newReady, newReady; e != a {
  1804. t.Errorf("expected new ready %d, got %d", e, a)
  1805. }
  1806. })
  1807. }
  1808. }