deployment_util_test.go 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405
  1. /*
  2. Copyright 2015 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 util
  14. import (
  15. "fmt"
  16. "math"
  17. "math/rand"
  18. "reflect"
  19. "sort"
  20. "strconv"
  21. "testing"
  22. "time"
  23. apps "k8s.io/api/apps/v1"
  24. "k8s.io/api/core/v1"
  25. apiequality "k8s.io/apimachinery/pkg/api/equality"
  26. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  27. "k8s.io/apimachinery/pkg/runtime"
  28. "k8s.io/apimachinery/pkg/types"
  29. "k8s.io/apimachinery/pkg/util/intstr"
  30. "k8s.io/apiserver/pkg/storage/names"
  31. "k8s.io/client-go/kubernetes/fake"
  32. core "k8s.io/client-go/testing"
  33. "k8s.io/kubernetes/pkg/controller"
  34. )
  35. func addListRSReactor(fakeClient *fake.Clientset, obj runtime.Object) *fake.Clientset {
  36. fakeClient.AddReactor("list", "replicasets", func(action core.Action) (handled bool, ret runtime.Object, err error) {
  37. return true, obj, nil
  38. })
  39. return fakeClient
  40. }
  41. func addListPodsReactor(fakeClient *fake.Clientset, obj runtime.Object) *fake.Clientset {
  42. fakeClient.AddReactor("list", "pods", func(action core.Action) (handled bool, ret runtime.Object, err error) {
  43. return true, obj, nil
  44. })
  45. return fakeClient
  46. }
  47. func addGetRSReactor(fakeClient *fake.Clientset, obj runtime.Object) *fake.Clientset {
  48. rsList, ok := obj.(*apps.ReplicaSetList)
  49. fakeClient.AddReactor("get", "replicasets", func(action core.Action) (handled bool, ret runtime.Object, err error) {
  50. name := action.(core.GetAction).GetName()
  51. if ok {
  52. for _, rs := range rsList.Items {
  53. if rs.Name == name {
  54. return true, &rs, nil
  55. }
  56. }
  57. }
  58. return false, nil, fmt.Errorf("could not find the requested replica set: %s", name)
  59. })
  60. return fakeClient
  61. }
  62. func addUpdateRSReactor(fakeClient *fake.Clientset) *fake.Clientset {
  63. fakeClient.AddReactor("update", "replicasets", func(action core.Action) (handled bool, ret runtime.Object, err error) {
  64. obj := action.(core.UpdateAction).GetObject().(*apps.ReplicaSet)
  65. return true, obj, nil
  66. })
  67. return fakeClient
  68. }
  69. func addUpdatePodsReactor(fakeClient *fake.Clientset) *fake.Clientset {
  70. fakeClient.AddReactor("update", "pods", func(action core.Action) (handled bool, ret runtime.Object, err error) {
  71. obj := action.(core.UpdateAction).GetObject().(*v1.Pod)
  72. return true, obj, nil
  73. })
  74. return fakeClient
  75. }
  76. func generateRSWithLabel(labels map[string]string, image string) apps.ReplicaSet {
  77. return apps.ReplicaSet{
  78. ObjectMeta: metav1.ObjectMeta{
  79. Name: names.SimpleNameGenerator.GenerateName("replicaset"),
  80. Labels: labels,
  81. },
  82. Spec: apps.ReplicaSetSpec{
  83. Replicas: func(i int32) *int32 { return &i }(1),
  84. Selector: &metav1.LabelSelector{MatchLabels: labels},
  85. Template: v1.PodTemplateSpec{
  86. ObjectMeta: metav1.ObjectMeta{
  87. Labels: labels,
  88. },
  89. Spec: v1.PodSpec{
  90. Containers: []v1.Container{
  91. {
  92. Name: image,
  93. Image: image,
  94. ImagePullPolicy: v1.PullAlways,
  95. TerminationMessagePath: v1.TerminationMessagePathDefault,
  96. },
  97. },
  98. },
  99. },
  100. },
  101. }
  102. }
  103. func newDControllerRef(d *apps.Deployment) *metav1.OwnerReference {
  104. isController := true
  105. return &metav1.OwnerReference{
  106. APIVersion: "apps/v1",
  107. Kind: "Deployment",
  108. Name: d.GetName(),
  109. UID: d.GetUID(),
  110. Controller: &isController,
  111. }
  112. }
  113. // generateRS creates a replica set, with the input deployment's template as its template
  114. func generateRS(deployment apps.Deployment) apps.ReplicaSet {
  115. template := deployment.Spec.Template.DeepCopy()
  116. return apps.ReplicaSet{
  117. ObjectMeta: metav1.ObjectMeta{
  118. UID: randomUID(),
  119. Name: names.SimpleNameGenerator.GenerateName("replicaset"),
  120. Labels: template.Labels,
  121. OwnerReferences: []metav1.OwnerReference{*newDControllerRef(&deployment)},
  122. },
  123. Spec: apps.ReplicaSetSpec{
  124. Replicas: new(int32),
  125. Template: *template,
  126. Selector: &metav1.LabelSelector{MatchLabels: template.Labels},
  127. },
  128. }
  129. }
  130. func randomUID() types.UID {
  131. return types.UID(strconv.FormatInt(rand.Int63(), 10))
  132. }
  133. // generateDeployment creates a deployment, with the input image as its template
  134. func generateDeployment(image string) apps.Deployment {
  135. podLabels := map[string]string{"name": image}
  136. terminationSec := int64(30)
  137. enableServiceLinks := v1.DefaultEnableServiceLinks
  138. return apps.Deployment{
  139. ObjectMeta: metav1.ObjectMeta{
  140. Name: image,
  141. Annotations: make(map[string]string),
  142. },
  143. Spec: apps.DeploymentSpec{
  144. Replicas: func(i int32) *int32 { return &i }(1),
  145. Selector: &metav1.LabelSelector{MatchLabels: podLabels},
  146. Template: v1.PodTemplateSpec{
  147. ObjectMeta: metav1.ObjectMeta{
  148. Labels: podLabels,
  149. },
  150. Spec: v1.PodSpec{
  151. Containers: []v1.Container{
  152. {
  153. Name: image,
  154. Image: image,
  155. ImagePullPolicy: v1.PullAlways,
  156. TerminationMessagePath: v1.TerminationMessagePathDefault,
  157. },
  158. },
  159. DNSPolicy: v1.DNSClusterFirst,
  160. TerminationGracePeriodSeconds: &terminationSec,
  161. RestartPolicy: v1.RestartPolicyAlways,
  162. SecurityContext: &v1.PodSecurityContext{},
  163. EnableServiceLinks: &enableServiceLinks,
  164. },
  165. },
  166. },
  167. }
  168. }
  169. func TestGetNewRS(t *testing.T) {
  170. newDeployment := generateDeployment("nginx")
  171. newRC := generateRS(newDeployment)
  172. tests := []struct {
  173. Name string
  174. objs []runtime.Object
  175. expected *apps.ReplicaSet
  176. }{
  177. {
  178. "No new ReplicaSet",
  179. []runtime.Object{
  180. &v1.PodList{},
  181. &apps.ReplicaSetList{
  182. Items: []apps.ReplicaSet{
  183. generateRS(generateDeployment("foo")),
  184. generateRS(generateDeployment("bar")),
  185. },
  186. },
  187. },
  188. nil,
  189. },
  190. {
  191. "Has new ReplicaSet",
  192. []runtime.Object{
  193. &v1.PodList{},
  194. &apps.ReplicaSetList{
  195. Items: []apps.ReplicaSet{
  196. generateRS(generateDeployment("foo")),
  197. generateRS(generateDeployment("bar")),
  198. generateRS(generateDeployment("abc")),
  199. newRC,
  200. generateRS(generateDeployment("xyz")),
  201. },
  202. },
  203. },
  204. &newRC,
  205. },
  206. }
  207. for _, test := range tests {
  208. t.Run(test.Name, func(t *testing.T) {
  209. fakeClient := &fake.Clientset{}
  210. fakeClient = addListPodsReactor(fakeClient, test.objs[0])
  211. fakeClient = addListRSReactor(fakeClient, test.objs[1])
  212. fakeClient = addUpdatePodsReactor(fakeClient)
  213. fakeClient = addUpdateRSReactor(fakeClient)
  214. rs, err := GetNewReplicaSet(&newDeployment, fakeClient.AppsV1())
  215. if err != nil {
  216. t.Errorf("In test case %s, got unexpected error %v", test.Name, err)
  217. }
  218. if !apiequality.Semantic.DeepEqual(rs, test.expected) {
  219. t.Errorf("In test case %s, expected %#v, got %#v", test.Name, test.expected, rs)
  220. }
  221. })
  222. }
  223. }
  224. func TestGetOldRSs(t *testing.T) {
  225. newDeployment := generateDeployment("nginx")
  226. newRS := generateRS(newDeployment)
  227. newRS.Status.FullyLabeledReplicas = *(newRS.Spec.Replicas)
  228. // create 2 old deployments and related replica sets/pods, with the same labels but different template
  229. oldDeployment := generateDeployment("nginx")
  230. oldDeployment.Spec.Template.Spec.Containers[0].Name = "nginx-old-1"
  231. oldRS := generateRS(oldDeployment)
  232. oldRS.Status.FullyLabeledReplicas = *(oldRS.Spec.Replicas)
  233. oldDeployment2 := generateDeployment("nginx")
  234. oldDeployment2.Spec.Template.Spec.Containers[0].Name = "nginx-old-2"
  235. oldRS2 := generateRS(oldDeployment2)
  236. oldRS2.Status.FullyLabeledReplicas = *(oldRS2.Spec.Replicas)
  237. // create 1 ReplicaSet that existed before the deployment,
  238. // with the same labels as the deployment, but no ControllerRef.
  239. existedRS := generateRSWithLabel(newDeployment.Spec.Template.Labels, "foo")
  240. existedRS.Status.FullyLabeledReplicas = *(existedRS.Spec.Replicas)
  241. tests := []struct {
  242. Name string
  243. objs []runtime.Object
  244. expected []*apps.ReplicaSet
  245. }{
  246. {
  247. "No old ReplicaSets",
  248. []runtime.Object{
  249. &apps.ReplicaSetList{
  250. Items: []apps.ReplicaSet{
  251. generateRS(generateDeployment("foo")),
  252. newRS,
  253. generateRS(generateDeployment("bar")),
  254. },
  255. },
  256. },
  257. nil,
  258. },
  259. {
  260. "Has old ReplicaSet",
  261. []runtime.Object{
  262. &apps.ReplicaSetList{
  263. Items: []apps.ReplicaSet{
  264. oldRS2,
  265. oldRS,
  266. existedRS,
  267. newRS,
  268. generateRSWithLabel(map[string]string{"name": "xyz"}, "xyz"),
  269. generateRSWithLabel(map[string]string{"name": "bar"}, "bar"),
  270. },
  271. },
  272. },
  273. []*apps.ReplicaSet{&oldRS, &oldRS2},
  274. },
  275. }
  276. for _, test := range tests {
  277. t.Run(test.Name, func(t *testing.T) {
  278. fakeClient := &fake.Clientset{}
  279. fakeClient = addListRSReactor(fakeClient, test.objs[0])
  280. fakeClient = addGetRSReactor(fakeClient, test.objs[0])
  281. fakeClient = addUpdateRSReactor(fakeClient)
  282. _, rss, err := GetOldReplicaSets(&newDeployment, fakeClient.AppsV1())
  283. if err != nil {
  284. t.Errorf("In test case %s, got unexpected error %v", test.Name, err)
  285. }
  286. if !equal(rss, test.expected) {
  287. t.Errorf("In test case %q, expected:", test.Name)
  288. for _, rs := range test.expected {
  289. t.Errorf("rs = %#v", rs)
  290. }
  291. t.Errorf("In test case %q, got:", test.Name)
  292. for _, rs := range rss {
  293. t.Errorf("rs = %#v", rs)
  294. }
  295. }
  296. })
  297. }
  298. }
  299. func generatePodTemplateSpec(name, nodeName string, annotations, labels map[string]string) v1.PodTemplateSpec {
  300. return v1.PodTemplateSpec{
  301. ObjectMeta: metav1.ObjectMeta{
  302. Name: name,
  303. Annotations: annotations,
  304. Labels: labels,
  305. },
  306. Spec: v1.PodSpec{
  307. NodeName: nodeName,
  308. },
  309. }
  310. }
  311. func TestEqualIgnoreHash(t *testing.T) {
  312. tests := []struct {
  313. Name string
  314. former, latter v1.PodTemplateSpec
  315. expected bool
  316. }{
  317. {
  318. "Same spec, same labels",
  319. generatePodTemplateSpec("foo", "foo-node", map[string]string{}, map[string]string{apps.DefaultDeploymentUniqueLabelKey: "value-1", "something": "else"}),
  320. generatePodTemplateSpec("foo", "foo-node", map[string]string{}, map[string]string{apps.DefaultDeploymentUniqueLabelKey: "value-1", "something": "else"}),
  321. true,
  322. },
  323. {
  324. "Same spec, only pod-template-hash label value is different",
  325. generatePodTemplateSpec("foo", "foo-node", map[string]string{}, map[string]string{apps.DefaultDeploymentUniqueLabelKey: "value-1", "something": "else"}),
  326. generatePodTemplateSpec("foo", "foo-node", map[string]string{}, map[string]string{apps.DefaultDeploymentUniqueLabelKey: "value-2", "something": "else"}),
  327. true,
  328. },
  329. {
  330. "Same spec, the former doesn't have pod-template-hash label",
  331. generatePodTemplateSpec("foo", "foo-node", map[string]string{}, map[string]string{"something": "else"}),
  332. generatePodTemplateSpec("foo", "foo-node", map[string]string{}, map[string]string{apps.DefaultDeploymentUniqueLabelKey: "value-2", "something": "else"}),
  333. true,
  334. },
  335. {
  336. "Same spec, the label is different, the former doesn't have pod-template-hash label, same number of labels",
  337. generatePodTemplateSpec("foo", "foo-node", map[string]string{}, map[string]string{"something": "else"}),
  338. generatePodTemplateSpec("foo", "foo-node", map[string]string{}, map[string]string{apps.DefaultDeploymentUniqueLabelKey: "value-2"}),
  339. false,
  340. },
  341. {
  342. "Same spec, the label is different, the latter doesn't have pod-template-hash label, same number of labels",
  343. generatePodTemplateSpec("foo", "foo-node", map[string]string{}, map[string]string{apps.DefaultDeploymentUniqueLabelKey: "value-1"}),
  344. generatePodTemplateSpec("foo", "foo-node", map[string]string{}, map[string]string{"something": "else"}),
  345. false,
  346. },
  347. {
  348. "Same spec, the label is different, and the pod-template-hash label value is the same",
  349. generatePodTemplateSpec("foo", "foo-node", map[string]string{}, map[string]string{apps.DefaultDeploymentUniqueLabelKey: "value-1"}),
  350. generatePodTemplateSpec("foo", "foo-node", map[string]string{}, map[string]string{apps.DefaultDeploymentUniqueLabelKey: "value-1", "something": "else"}),
  351. false,
  352. },
  353. {
  354. "Different spec, same labels",
  355. generatePodTemplateSpec("foo", "foo-node", map[string]string{"former": "value"}, map[string]string{apps.DefaultDeploymentUniqueLabelKey: "value-1", "something": "else"}),
  356. generatePodTemplateSpec("foo", "foo-node", map[string]string{"latter": "value"}, map[string]string{apps.DefaultDeploymentUniqueLabelKey: "value-1", "something": "else"}),
  357. false,
  358. },
  359. {
  360. "Different spec, different pod-template-hash label value",
  361. generatePodTemplateSpec("foo-1", "foo-node", map[string]string{}, map[string]string{apps.DefaultDeploymentUniqueLabelKey: "value-1", "something": "else"}),
  362. generatePodTemplateSpec("foo-2", "foo-node", map[string]string{}, map[string]string{apps.DefaultDeploymentUniqueLabelKey: "value-2", "something": "else"}),
  363. false,
  364. },
  365. {
  366. "Different spec, the former doesn't have pod-template-hash label",
  367. generatePodTemplateSpec("foo-1", "foo-node-1", map[string]string{}, map[string]string{"something": "else"}),
  368. generatePodTemplateSpec("foo-2", "foo-node-2", map[string]string{}, map[string]string{apps.DefaultDeploymentUniqueLabelKey: "value-2", "something": "else"}),
  369. false,
  370. },
  371. {
  372. "Different spec, different labels",
  373. generatePodTemplateSpec("foo", "foo-node-1", map[string]string{}, map[string]string{"something": "else"}),
  374. generatePodTemplateSpec("foo", "foo-node-2", map[string]string{}, map[string]string{"nothing": "else"}),
  375. false,
  376. },
  377. }
  378. for _, test := range tests {
  379. t.Run(test.Name, func(t *testing.T) {
  380. runTest := func(t1, t2 *v1.PodTemplateSpec, reversed bool) {
  381. reverseString := ""
  382. if reversed {
  383. reverseString = " (reverse order)"
  384. }
  385. // Run
  386. equal := EqualIgnoreHash(t1, t2)
  387. if equal != test.expected {
  388. t.Errorf("%q%s: expected %v", test.Name, reverseString, test.expected)
  389. return
  390. }
  391. if t1.Labels == nil || t2.Labels == nil {
  392. t.Errorf("%q%s: unexpected labels becomes nil", test.Name, reverseString)
  393. }
  394. }
  395. runTest(&test.former, &test.latter, false)
  396. // Test the same case in reverse order
  397. runTest(&test.latter, &test.former, true)
  398. })
  399. }
  400. }
  401. func TestFindNewReplicaSet(t *testing.T) {
  402. now := metav1.Now()
  403. later := metav1.Time{Time: now.Add(time.Minute)}
  404. deployment := generateDeployment("nginx")
  405. newRS := generateRS(deployment)
  406. newRS.Labels[apps.DefaultDeploymentUniqueLabelKey] = "hash"
  407. newRS.CreationTimestamp = later
  408. newRSDup := generateRS(deployment)
  409. newRSDup.Labels[apps.DefaultDeploymentUniqueLabelKey] = "different-hash"
  410. newRSDup.CreationTimestamp = now
  411. oldDeployment := generateDeployment("nginx")
  412. oldDeployment.Spec.Template.Spec.Containers[0].Name = "nginx-old-1"
  413. oldRS := generateRS(oldDeployment)
  414. oldRS.Status.FullyLabeledReplicas = *(oldRS.Spec.Replicas)
  415. tests := []struct {
  416. Name string
  417. deployment apps.Deployment
  418. rsList []*apps.ReplicaSet
  419. expected *apps.ReplicaSet
  420. }{
  421. {
  422. Name: "Get new ReplicaSet with the same template as Deployment spec but different pod-template-hash value",
  423. deployment: deployment,
  424. rsList: []*apps.ReplicaSet{&newRS, &oldRS},
  425. expected: &newRS,
  426. },
  427. {
  428. Name: "Get the oldest new ReplicaSet when there are more than one ReplicaSet with the same template",
  429. deployment: deployment,
  430. rsList: []*apps.ReplicaSet{&newRS, &oldRS, &newRSDup},
  431. expected: &newRSDup,
  432. },
  433. {
  434. Name: "Get nil new ReplicaSet",
  435. deployment: deployment,
  436. rsList: []*apps.ReplicaSet{&oldRS},
  437. expected: nil,
  438. },
  439. }
  440. for _, test := range tests {
  441. t.Run(test.Name, func(t *testing.T) {
  442. if rs := FindNewReplicaSet(&test.deployment, test.rsList); !reflect.DeepEqual(rs, test.expected) {
  443. t.Errorf("In test case %q, expected %#v, got %#v", test.Name, test.expected, rs)
  444. }
  445. })
  446. }
  447. }
  448. func TestFindOldReplicaSets(t *testing.T) {
  449. now := metav1.Now()
  450. later := metav1.Time{Time: now.Add(time.Minute)}
  451. before := metav1.Time{Time: now.Add(-time.Minute)}
  452. deployment := generateDeployment("nginx")
  453. newRS := generateRS(deployment)
  454. *(newRS.Spec.Replicas) = 1
  455. newRS.Labels[apps.DefaultDeploymentUniqueLabelKey] = "hash"
  456. newRS.CreationTimestamp = later
  457. newRSDup := generateRS(deployment)
  458. newRSDup.Labels[apps.DefaultDeploymentUniqueLabelKey] = "different-hash"
  459. newRSDup.CreationTimestamp = now
  460. oldDeployment := generateDeployment("nginx")
  461. oldDeployment.Spec.Template.Spec.Containers[0].Name = "nginx-old-1"
  462. oldRS := generateRS(oldDeployment)
  463. oldRS.Status.FullyLabeledReplicas = *(oldRS.Spec.Replicas)
  464. oldRS.CreationTimestamp = before
  465. tests := []struct {
  466. Name string
  467. deployment apps.Deployment
  468. rsList []*apps.ReplicaSet
  469. expected []*apps.ReplicaSet
  470. expectedRequire []*apps.ReplicaSet
  471. }{
  472. {
  473. Name: "Get old ReplicaSets",
  474. deployment: deployment,
  475. rsList: []*apps.ReplicaSet{&newRS, &oldRS},
  476. expected: []*apps.ReplicaSet{&oldRS},
  477. expectedRequire: nil,
  478. },
  479. {
  480. Name: "Get old ReplicaSets with no new ReplicaSet",
  481. deployment: deployment,
  482. rsList: []*apps.ReplicaSet{&oldRS},
  483. expected: []*apps.ReplicaSet{&oldRS},
  484. expectedRequire: nil,
  485. },
  486. {
  487. Name: "Get old ReplicaSets with two new ReplicaSets, only the oldest new ReplicaSet is seen as new ReplicaSet",
  488. deployment: deployment,
  489. rsList: []*apps.ReplicaSet{&oldRS, &newRS, &newRSDup},
  490. expected: []*apps.ReplicaSet{&oldRS, &newRS},
  491. expectedRequire: []*apps.ReplicaSet{&newRS},
  492. },
  493. {
  494. Name: "Get empty old ReplicaSets",
  495. deployment: deployment,
  496. rsList: []*apps.ReplicaSet{&newRS},
  497. expected: nil,
  498. expectedRequire: nil,
  499. },
  500. }
  501. for _, test := range tests {
  502. t.Run(test.Name, func(t *testing.T) {
  503. requireRS, allRS := FindOldReplicaSets(&test.deployment, test.rsList)
  504. sort.Sort(controller.ReplicaSetsByCreationTimestamp(allRS))
  505. sort.Sort(controller.ReplicaSetsByCreationTimestamp(test.expected))
  506. if !reflect.DeepEqual(allRS, test.expected) {
  507. t.Errorf("In test case %q, expected %#v, got %#v", test.Name, test.expected, allRS)
  508. }
  509. // RSs are getting filtered correctly by rs.spec.replicas
  510. if !reflect.DeepEqual(requireRS, test.expectedRequire) {
  511. t.Errorf("In test case %q, expected %#v, got %#v", test.Name, test.expectedRequire, requireRS)
  512. }
  513. })
  514. }
  515. }
  516. // equal compares the equality of two ReplicaSet slices regardless of their ordering
  517. func equal(rss1, rss2 []*apps.ReplicaSet) bool {
  518. if reflect.DeepEqual(rss1, rss2) {
  519. return true
  520. }
  521. if rss1 == nil || rss2 == nil || len(rss1) != len(rss2) {
  522. return false
  523. }
  524. count := 0
  525. for _, rs1 := range rss1 {
  526. for _, rs2 := range rss2 {
  527. if reflect.DeepEqual(rs1, rs2) {
  528. count++
  529. break
  530. }
  531. }
  532. }
  533. return count == len(rss1)
  534. }
  535. func TestGetReplicaCountForReplicaSets(t *testing.T) {
  536. rs1 := generateRS(generateDeployment("foo"))
  537. *(rs1.Spec.Replicas) = 1
  538. rs1.Status.Replicas = 2
  539. rs2 := generateRS(generateDeployment("bar"))
  540. *(rs2.Spec.Replicas) = 2
  541. rs2.Status.Replicas = 3
  542. tests := []struct {
  543. Name string
  544. sets []*apps.ReplicaSet
  545. expectedCount int32
  546. expectedActual int32
  547. }{
  548. {
  549. "1:2 Replicas",
  550. []*apps.ReplicaSet{&rs1},
  551. 1,
  552. 2,
  553. },
  554. {
  555. "3:5 Replicas",
  556. []*apps.ReplicaSet{&rs1, &rs2},
  557. 3,
  558. 5,
  559. },
  560. }
  561. for _, test := range tests {
  562. t.Run(test.Name, func(t *testing.T) {
  563. rs := GetReplicaCountForReplicaSets(test.sets)
  564. if rs != test.expectedCount {
  565. t.Errorf("In test case %s, expectedCount %+v, got %+v", test.Name, test.expectedCount, rs)
  566. }
  567. rs = GetActualReplicaCountForReplicaSets(test.sets)
  568. if rs != test.expectedActual {
  569. t.Errorf("In test case %s, expectedActual %+v, got %+v", test.Name, test.expectedActual, rs)
  570. }
  571. })
  572. }
  573. }
  574. func TestResolveFenceposts(t *testing.T) {
  575. tests := []struct {
  576. maxSurge *string
  577. maxUnavailable *string
  578. desired int32
  579. expectSurge int32
  580. expectUnavailable int32
  581. expectError bool
  582. }{
  583. {
  584. maxSurge: newString("0%"),
  585. maxUnavailable: newString("0%"),
  586. desired: 0,
  587. expectSurge: 0,
  588. expectUnavailable: 1,
  589. expectError: false,
  590. },
  591. {
  592. maxSurge: newString("39%"),
  593. maxUnavailable: newString("39%"),
  594. desired: 10,
  595. expectSurge: 4,
  596. expectUnavailable: 3,
  597. expectError: false,
  598. },
  599. {
  600. maxSurge: newString("oops"),
  601. maxUnavailable: newString("39%"),
  602. desired: 10,
  603. expectSurge: 0,
  604. expectUnavailable: 0,
  605. expectError: true,
  606. },
  607. {
  608. maxSurge: newString("55%"),
  609. maxUnavailable: newString("urg"),
  610. desired: 10,
  611. expectSurge: 0,
  612. expectUnavailable: 0,
  613. expectError: true,
  614. },
  615. {
  616. maxSurge: nil,
  617. maxUnavailable: newString("39%"),
  618. desired: 10,
  619. expectSurge: 0,
  620. expectUnavailable: 3,
  621. expectError: false,
  622. },
  623. {
  624. maxSurge: newString("39%"),
  625. maxUnavailable: nil,
  626. desired: 10,
  627. expectSurge: 4,
  628. expectUnavailable: 0,
  629. expectError: false,
  630. },
  631. {
  632. maxSurge: nil,
  633. maxUnavailable: nil,
  634. desired: 10,
  635. expectSurge: 0,
  636. expectUnavailable: 1,
  637. expectError: false,
  638. },
  639. }
  640. for num, test := range tests {
  641. t.Run(fmt.Sprintf("%d", num), func(t *testing.T) {
  642. var maxSurge, maxUnavail *intstr.IntOrString
  643. if test.maxSurge != nil {
  644. surge := intstr.FromString(*test.maxSurge)
  645. maxSurge = &surge
  646. }
  647. if test.maxUnavailable != nil {
  648. unavail := intstr.FromString(*test.maxUnavailable)
  649. maxUnavail = &unavail
  650. }
  651. surge, unavail, err := ResolveFenceposts(maxSurge, maxUnavail, test.desired)
  652. if err != nil && !test.expectError {
  653. t.Errorf("unexpected error %v", err)
  654. }
  655. if err == nil && test.expectError {
  656. t.Error("expected error")
  657. }
  658. if surge != test.expectSurge || unavail != test.expectUnavailable {
  659. t.Errorf("#%v got %v:%v, want %v:%v", num, surge, unavail, test.expectSurge, test.expectUnavailable)
  660. }
  661. })
  662. }
  663. }
  664. func newString(s string) *string {
  665. return &s
  666. }
  667. func TestNewRSNewReplicas(t *testing.T) {
  668. tests := []struct {
  669. Name string
  670. strategyType apps.DeploymentStrategyType
  671. depReplicas int32
  672. newRSReplicas int32
  673. maxSurge int
  674. expected int32
  675. }{
  676. {
  677. "can not scale up - to newRSReplicas",
  678. apps.RollingUpdateDeploymentStrategyType,
  679. 1, 5, 1, 5,
  680. },
  681. {
  682. "scale up - to depReplicas",
  683. apps.RollingUpdateDeploymentStrategyType,
  684. 6, 2, 10, 6,
  685. },
  686. {
  687. "recreate - to depReplicas",
  688. apps.RecreateDeploymentStrategyType,
  689. 3, 1, 1, 3,
  690. },
  691. }
  692. newDeployment := generateDeployment("nginx")
  693. newRC := generateRS(newDeployment)
  694. rs5 := generateRS(newDeployment)
  695. *(rs5.Spec.Replicas) = 5
  696. for _, test := range tests {
  697. t.Run(test.Name, func(t *testing.T) {
  698. *(newDeployment.Spec.Replicas) = test.depReplicas
  699. newDeployment.Spec.Strategy = apps.DeploymentStrategy{Type: test.strategyType}
  700. newDeployment.Spec.Strategy.RollingUpdate = &apps.RollingUpdateDeployment{
  701. MaxUnavailable: func(i int) *intstr.IntOrString {
  702. x := intstr.FromInt(i)
  703. return &x
  704. }(1),
  705. MaxSurge: func(i int) *intstr.IntOrString {
  706. x := intstr.FromInt(i)
  707. return &x
  708. }(test.maxSurge),
  709. }
  710. *(newRC.Spec.Replicas) = test.newRSReplicas
  711. rs, err := NewRSNewReplicas(&newDeployment, []*apps.ReplicaSet{&rs5}, &newRC)
  712. if err != nil {
  713. t.Errorf("In test case %s, got unexpected error %v", test.Name, err)
  714. }
  715. if rs != test.expected {
  716. t.Errorf("In test case %s, expected %+v, got %+v", test.Name, test.expected, rs)
  717. }
  718. })
  719. }
  720. }
  721. var (
  722. condProgressing = func() apps.DeploymentCondition {
  723. return apps.DeploymentCondition{
  724. Type: apps.DeploymentProgressing,
  725. Status: v1.ConditionFalse,
  726. Reason: "ForSomeReason",
  727. }
  728. }
  729. condProgressing2 = func() apps.DeploymentCondition {
  730. return apps.DeploymentCondition{
  731. Type: apps.DeploymentProgressing,
  732. Status: v1.ConditionTrue,
  733. Reason: "BecauseItIs",
  734. }
  735. }
  736. condAvailable = func() apps.DeploymentCondition {
  737. return apps.DeploymentCondition{
  738. Type: apps.DeploymentAvailable,
  739. Status: v1.ConditionTrue,
  740. Reason: "AwesomeController",
  741. }
  742. }
  743. status = func() *apps.DeploymentStatus {
  744. return &apps.DeploymentStatus{
  745. Conditions: []apps.DeploymentCondition{condProgressing(), condAvailable()},
  746. }
  747. }
  748. )
  749. func TestGetCondition(t *testing.T) {
  750. exampleStatus := status()
  751. tests := []struct {
  752. name string
  753. status apps.DeploymentStatus
  754. condType apps.DeploymentConditionType
  755. expected bool
  756. }{
  757. {
  758. name: "condition exists",
  759. status: *exampleStatus,
  760. condType: apps.DeploymentAvailable,
  761. expected: true,
  762. },
  763. {
  764. name: "condition does not exist",
  765. status: *exampleStatus,
  766. condType: apps.DeploymentReplicaFailure,
  767. expected: false,
  768. },
  769. }
  770. for _, test := range tests {
  771. t.Run(test.name, func(t *testing.T) {
  772. cond := GetDeploymentCondition(test.status, test.condType)
  773. exists := cond != nil
  774. if exists != test.expected {
  775. t.Errorf("%s: expected condition to exist: %t, got: %t", test.name, test.expected, exists)
  776. }
  777. })
  778. }
  779. }
  780. func TestSetCondition(t *testing.T) {
  781. tests := []struct {
  782. name string
  783. status *apps.DeploymentStatus
  784. cond apps.DeploymentCondition
  785. expectedStatus *apps.DeploymentStatus
  786. }{
  787. {
  788. name: "set for the first time",
  789. status: &apps.DeploymentStatus{},
  790. cond: condAvailable(),
  791. expectedStatus: &apps.DeploymentStatus{Conditions: []apps.DeploymentCondition{condAvailable()}},
  792. },
  793. {
  794. name: "simple set",
  795. status: &apps.DeploymentStatus{Conditions: []apps.DeploymentCondition{condProgressing()}},
  796. cond: condAvailable(),
  797. expectedStatus: status(),
  798. },
  799. {
  800. name: "overwrite",
  801. status: &apps.DeploymentStatus{Conditions: []apps.DeploymentCondition{condProgressing()}},
  802. cond: condProgressing2(),
  803. expectedStatus: &apps.DeploymentStatus{Conditions: []apps.DeploymentCondition{condProgressing2()}},
  804. },
  805. }
  806. for _, test := range tests {
  807. t.Run(test.name, func(t *testing.T) {
  808. SetDeploymentCondition(test.status, test.cond)
  809. if !reflect.DeepEqual(test.status, test.expectedStatus) {
  810. t.Errorf("%s: expected status: %v, got: %v", test.name, test.expectedStatus, test.status)
  811. }
  812. })
  813. }
  814. }
  815. func TestRemoveCondition(t *testing.T) {
  816. tests := []struct {
  817. name string
  818. status *apps.DeploymentStatus
  819. condType apps.DeploymentConditionType
  820. expectedStatus *apps.DeploymentStatus
  821. }{
  822. {
  823. name: "remove from empty status",
  824. status: &apps.DeploymentStatus{},
  825. condType: apps.DeploymentProgressing,
  826. expectedStatus: &apps.DeploymentStatus{},
  827. },
  828. {
  829. name: "simple remove",
  830. status: &apps.DeploymentStatus{Conditions: []apps.DeploymentCondition{condProgressing()}},
  831. condType: apps.DeploymentProgressing,
  832. expectedStatus: &apps.DeploymentStatus{},
  833. },
  834. {
  835. name: "doesn't remove anything",
  836. status: status(),
  837. condType: apps.DeploymentReplicaFailure,
  838. expectedStatus: status(),
  839. },
  840. }
  841. for _, test := range tests {
  842. t.Run(test.name, func(t *testing.T) {
  843. RemoveDeploymentCondition(test.status, test.condType)
  844. if !reflect.DeepEqual(test.status, test.expectedStatus) {
  845. t.Errorf("%s: expected status: %v, got: %v", test.name, test.expectedStatus, test.status)
  846. }
  847. })
  848. }
  849. }
  850. func TestDeploymentComplete(t *testing.T) {
  851. deployment := func(desired, current, updated, available, maxUnavailable, maxSurge int32) *apps.Deployment {
  852. return &apps.Deployment{
  853. Spec: apps.DeploymentSpec{
  854. Replicas: &desired,
  855. Strategy: apps.DeploymentStrategy{
  856. RollingUpdate: &apps.RollingUpdateDeployment{
  857. MaxUnavailable: func(i int) *intstr.IntOrString { x := intstr.FromInt(i); return &x }(int(maxUnavailable)),
  858. MaxSurge: func(i int) *intstr.IntOrString { x := intstr.FromInt(i); return &x }(int(maxSurge)),
  859. },
  860. Type: apps.RollingUpdateDeploymentStrategyType,
  861. },
  862. },
  863. Status: apps.DeploymentStatus{
  864. Replicas: current,
  865. UpdatedReplicas: updated,
  866. AvailableReplicas: available,
  867. },
  868. }
  869. }
  870. tests := []struct {
  871. name string
  872. d *apps.Deployment
  873. expected bool
  874. }{
  875. {
  876. name: "not complete: min but not all pods become available",
  877. d: deployment(5, 5, 5, 4, 1, 0),
  878. expected: false,
  879. },
  880. {
  881. name: "not complete: min availability is not honored",
  882. d: deployment(5, 5, 5, 3, 1, 0),
  883. expected: false,
  884. },
  885. {
  886. name: "complete",
  887. d: deployment(5, 5, 5, 5, 0, 0),
  888. expected: true,
  889. },
  890. {
  891. name: "not complete: all pods are available but not updated",
  892. d: deployment(5, 5, 4, 5, 0, 0),
  893. expected: false,
  894. },
  895. {
  896. name: "not complete: still running old pods",
  897. // old replica set: spec.replicas=1, status.replicas=1, status.availableReplicas=1
  898. // new replica set: spec.replicas=1, status.replicas=1, status.availableReplicas=0
  899. d: deployment(1, 2, 1, 1, 0, 1),
  900. expected: false,
  901. },
  902. {
  903. name: "not complete: one replica deployment never comes up",
  904. d: deployment(1, 1, 1, 0, 1, 1),
  905. expected: false,
  906. },
  907. }
  908. for _, test := range tests {
  909. t.Run(test.name, func(t *testing.T) {
  910. if got, exp := DeploymentComplete(test.d, &test.d.Status), test.expected; got != exp {
  911. t.Errorf("expected complete: %t, got: %t", exp, got)
  912. }
  913. })
  914. }
  915. }
  916. func TestDeploymentProgressing(t *testing.T) {
  917. deployment := func(current, updated, ready, available int32) *apps.Deployment {
  918. return &apps.Deployment{
  919. Status: apps.DeploymentStatus{
  920. Replicas: current,
  921. UpdatedReplicas: updated,
  922. ReadyReplicas: ready,
  923. AvailableReplicas: available,
  924. },
  925. }
  926. }
  927. newStatus := func(current, updated, ready, available int32) apps.DeploymentStatus {
  928. return apps.DeploymentStatus{
  929. Replicas: current,
  930. UpdatedReplicas: updated,
  931. ReadyReplicas: ready,
  932. AvailableReplicas: available,
  933. }
  934. }
  935. tests := []struct {
  936. name string
  937. d *apps.Deployment
  938. newStatus apps.DeploymentStatus
  939. expected bool
  940. }{
  941. {
  942. name: "progressing: updated pods",
  943. d: deployment(10, 4, 4, 4),
  944. newStatus: newStatus(10, 6, 4, 4),
  945. expected: true,
  946. },
  947. {
  948. name: "not progressing",
  949. d: deployment(10, 4, 4, 4),
  950. newStatus: newStatus(10, 4, 4, 4),
  951. expected: false,
  952. },
  953. {
  954. name: "progressing: old pods removed",
  955. d: deployment(10, 4, 6, 6),
  956. newStatus: newStatus(8, 4, 6, 6),
  957. expected: true,
  958. },
  959. {
  960. name: "not progressing: less new pods",
  961. d: deployment(10, 7, 3, 3),
  962. newStatus: newStatus(10, 6, 3, 3),
  963. expected: false,
  964. },
  965. {
  966. name: "progressing: less overall but more new pods",
  967. d: deployment(10, 4, 7, 7),
  968. newStatus: newStatus(8, 8, 5, 5),
  969. expected: true,
  970. },
  971. {
  972. name: "progressing: more ready pods",
  973. d: deployment(10, 10, 9, 8),
  974. newStatus: newStatus(10, 10, 10, 8),
  975. expected: true,
  976. },
  977. {
  978. name: "progressing: more available pods",
  979. d: deployment(10, 10, 10, 9),
  980. newStatus: newStatus(10, 10, 10, 10),
  981. expected: true,
  982. },
  983. }
  984. for _, test := range tests {
  985. t.Run(test.name, func(t *testing.T) {
  986. if got, exp := DeploymentProgressing(test.d, &test.newStatus), test.expected; got != exp {
  987. t.Errorf("expected progressing: %t, got: %t", exp, got)
  988. }
  989. })
  990. }
  991. }
  992. func TestDeploymentTimedOut(t *testing.T) {
  993. var (
  994. null *int32
  995. ten = int32(10)
  996. infinite = int32(math.MaxInt32)
  997. )
  998. timeFn := func(min, sec int) time.Time {
  999. return time.Date(2016, 1, 1, 0, min, sec, 0, time.UTC)
  1000. }
  1001. deployment := func(condType apps.DeploymentConditionType, status v1.ConditionStatus, reason string, pds *int32, from time.Time) apps.Deployment {
  1002. return apps.Deployment{
  1003. Spec: apps.DeploymentSpec{
  1004. ProgressDeadlineSeconds: pds,
  1005. },
  1006. Status: apps.DeploymentStatus{
  1007. Conditions: []apps.DeploymentCondition{
  1008. {
  1009. Type: condType,
  1010. Status: status,
  1011. Reason: reason,
  1012. LastUpdateTime: metav1.Time{Time: from},
  1013. },
  1014. },
  1015. },
  1016. }
  1017. }
  1018. tests := []struct {
  1019. name string
  1020. d apps.Deployment
  1021. nowFn func() time.Time
  1022. expected bool
  1023. }{
  1024. {
  1025. name: "nil progressDeadlineSeconds specified - no timeout",
  1026. d: deployment(apps.DeploymentProgressing, v1.ConditionTrue, "", null, timeFn(1, 9)),
  1027. nowFn: func() time.Time { return timeFn(1, 20) },
  1028. expected: false,
  1029. },
  1030. {
  1031. name: "infinite progressDeadlineSeconds specified - no timeout",
  1032. d: deployment(apps.DeploymentProgressing, v1.ConditionTrue, "", &infinite, timeFn(1, 9)),
  1033. nowFn: func() time.Time { return timeFn(1, 20) },
  1034. expected: false,
  1035. },
  1036. {
  1037. name: "progressDeadlineSeconds: 10s, now - started => 00:01:20 - 00:01:09 => 11s",
  1038. d: deployment(apps.DeploymentProgressing, v1.ConditionTrue, "", &ten, timeFn(1, 9)),
  1039. nowFn: func() time.Time { return timeFn(1, 20) },
  1040. expected: true,
  1041. },
  1042. {
  1043. name: "progressDeadlineSeconds: 10s, now - started => 00:01:20 - 00:01:11 => 9s",
  1044. d: deployment(apps.DeploymentProgressing, v1.ConditionTrue, "", &ten, timeFn(1, 11)),
  1045. nowFn: func() time.Time { return timeFn(1, 20) },
  1046. expected: false,
  1047. },
  1048. {
  1049. name: "previous status was a complete deployment",
  1050. d: deployment(apps.DeploymentProgressing, v1.ConditionTrue, NewRSAvailableReason, nil, time.Time{}),
  1051. expected: false,
  1052. },
  1053. }
  1054. for _, test := range tests {
  1055. t.Run(test.name, func(t *testing.T) {
  1056. nowFn = test.nowFn
  1057. if got, exp := DeploymentTimedOut(&test.d, &test.d.Status), test.expected; got != exp {
  1058. t.Errorf("expected timeout: %t, got: %t", exp, got)
  1059. }
  1060. })
  1061. }
  1062. }
  1063. func TestMaxUnavailable(t *testing.T) {
  1064. deployment := func(replicas int32, maxUnavailable intstr.IntOrString) apps.Deployment {
  1065. return apps.Deployment{
  1066. Spec: apps.DeploymentSpec{
  1067. Replicas: func(i int32) *int32 { return &i }(replicas),
  1068. Strategy: apps.DeploymentStrategy{
  1069. RollingUpdate: &apps.RollingUpdateDeployment{
  1070. MaxSurge: func(i int) *intstr.IntOrString { x := intstr.FromInt(i); return &x }(int(1)),
  1071. MaxUnavailable: &maxUnavailable,
  1072. },
  1073. Type: apps.RollingUpdateDeploymentStrategyType,
  1074. },
  1075. },
  1076. }
  1077. }
  1078. tests := []struct {
  1079. name string
  1080. deployment apps.Deployment
  1081. expected int32
  1082. }{
  1083. {
  1084. name: "maxUnavailable less than replicas",
  1085. deployment: deployment(10, intstr.FromInt(5)),
  1086. expected: int32(5),
  1087. },
  1088. {
  1089. name: "maxUnavailable equal replicas",
  1090. deployment: deployment(10, intstr.FromInt(10)),
  1091. expected: int32(10),
  1092. },
  1093. {
  1094. name: "maxUnavailable greater than replicas",
  1095. deployment: deployment(5, intstr.FromInt(10)),
  1096. expected: int32(5),
  1097. },
  1098. {
  1099. name: "maxUnavailable with replicas is 0",
  1100. deployment: deployment(0, intstr.FromInt(10)),
  1101. expected: int32(0),
  1102. },
  1103. {
  1104. name: "maxUnavailable with Recreate deployment strategy",
  1105. deployment: apps.Deployment{
  1106. Spec: apps.DeploymentSpec{
  1107. Strategy: apps.DeploymentStrategy{
  1108. Type: apps.RecreateDeploymentStrategyType,
  1109. },
  1110. },
  1111. },
  1112. expected: int32(0),
  1113. },
  1114. {
  1115. name: "maxUnavailable less than replicas with percents",
  1116. deployment: deployment(10, intstr.FromString("50%")),
  1117. expected: int32(5),
  1118. },
  1119. {
  1120. name: "maxUnavailable equal replicas with percents",
  1121. deployment: deployment(10, intstr.FromString("100%")),
  1122. expected: int32(10),
  1123. },
  1124. {
  1125. name: "maxUnavailable greater than replicas with percents",
  1126. deployment: deployment(5, intstr.FromString("100%")),
  1127. expected: int32(5),
  1128. },
  1129. }
  1130. for _, test := range tests {
  1131. t.Log(test.name)
  1132. t.Run(test.name, func(t *testing.T) {
  1133. maxUnavailable := MaxUnavailable(test.deployment)
  1134. if test.expected != maxUnavailable {
  1135. t.Fatalf("expected:%v, got:%v", test.expected, maxUnavailable)
  1136. }
  1137. })
  1138. }
  1139. }
  1140. //Set of simple tests for annotation related util functions
  1141. func TestAnnotationUtils(t *testing.T) {
  1142. //Setup
  1143. tDeployment := generateDeployment("nginx")
  1144. tRS := generateRS(tDeployment)
  1145. tDeployment.Annotations[RevisionAnnotation] = "1"
  1146. //Test Case 1: Check if anotations are copied properly from deployment to RS
  1147. t.Run("SetNewReplicaSetAnnotations", func(t *testing.T) {
  1148. //Try to set the increment revision from 11 through 20
  1149. for i := 10; i < 20; i++ {
  1150. nextRevision := fmt.Sprintf("%d", i+1)
  1151. SetNewReplicaSetAnnotations(&tDeployment, &tRS, nextRevision, true, 5)
  1152. //Now the ReplicaSets Revision Annotation should be i+1
  1153. if i >= 12 {
  1154. expectedHistoryAnnotation := fmt.Sprintf("%d,%d", i-1, i)
  1155. if tRS.Annotations[RevisionHistoryAnnotation] != expectedHistoryAnnotation {
  1156. t.Errorf("Revision History Expected=%s Obtained=%s", expectedHistoryAnnotation, tRS.Annotations[RevisionHistoryAnnotation])
  1157. }
  1158. }
  1159. if tRS.Annotations[RevisionAnnotation] != nextRevision {
  1160. t.Errorf("Revision Expected=%s Obtained=%s", nextRevision, tRS.Annotations[RevisionAnnotation])
  1161. }
  1162. }
  1163. })
  1164. //Test Case 2: Check if annotations are set properly
  1165. t.Run("SetReplicasAnnotations", func(t *testing.T) {
  1166. updated := SetReplicasAnnotations(&tRS, 10, 11)
  1167. if !updated {
  1168. t.Errorf("SetReplicasAnnotations() failed")
  1169. }
  1170. value, ok := tRS.Annotations[DesiredReplicasAnnotation]
  1171. if !ok {
  1172. t.Errorf("SetReplicasAnnotations did not set DesiredReplicasAnnotation")
  1173. }
  1174. if value != "10" {
  1175. t.Errorf("SetReplicasAnnotations did not set DesiredReplicasAnnotation correctly value=%s", value)
  1176. }
  1177. if value, ok = tRS.Annotations[MaxReplicasAnnotation]; !ok {
  1178. t.Errorf("SetReplicasAnnotations did not set DesiredReplicasAnnotation")
  1179. }
  1180. if value != "11" {
  1181. t.Errorf("SetReplicasAnnotations did not set MaxReplicasAnnotation correctly value=%s", value)
  1182. }
  1183. })
  1184. //Test Case 3: Check if annotations reflect deployments state
  1185. tRS.Annotations[DesiredReplicasAnnotation] = "1"
  1186. tRS.Status.AvailableReplicas = 1
  1187. tRS.Spec.Replicas = new(int32)
  1188. *tRS.Spec.Replicas = 1
  1189. t.Run("IsSaturated", func(t *testing.T) {
  1190. saturated := IsSaturated(&tDeployment, &tRS)
  1191. if !saturated {
  1192. t.Errorf("SetReplicasAnnotations Expected=true Obtained=false")
  1193. }
  1194. })
  1195. //Tear Down
  1196. }
  1197. func TestReplicasAnnotationsNeedUpdate(t *testing.T) {
  1198. desiredReplicas := fmt.Sprintf("%d", int32(10))
  1199. maxReplicas := fmt.Sprintf("%d", int32(20))
  1200. tests := []struct {
  1201. name string
  1202. replicaSet *apps.ReplicaSet
  1203. expected bool
  1204. }{
  1205. {
  1206. name: "test Annotations nil",
  1207. replicaSet: &apps.ReplicaSet{
  1208. ObjectMeta: metav1.ObjectMeta{Name: "hello", Namespace: "test"},
  1209. Spec: apps.ReplicaSetSpec{
  1210. Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
  1211. },
  1212. },
  1213. expected: true,
  1214. },
  1215. {
  1216. name: "test desiredReplicas update",
  1217. replicaSet: &apps.ReplicaSet{
  1218. ObjectMeta: metav1.ObjectMeta{
  1219. Name: "hello",
  1220. Namespace: "test",
  1221. Annotations: map[string]string{DesiredReplicasAnnotation: "8", MaxReplicasAnnotation: maxReplicas},
  1222. },
  1223. Spec: apps.ReplicaSetSpec{
  1224. Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
  1225. },
  1226. },
  1227. expected: true,
  1228. },
  1229. {
  1230. name: "test maxReplicas update",
  1231. replicaSet: &apps.ReplicaSet{
  1232. ObjectMeta: metav1.ObjectMeta{
  1233. Name: "hello",
  1234. Namespace: "test",
  1235. Annotations: map[string]string{DesiredReplicasAnnotation: desiredReplicas, MaxReplicasAnnotation: "16"},
  1236. },
  1237. Spec: apps.ReplicaSetSpec{
  1238. Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
  1239. },
  1240. },
  1241. expected: true,
  1242. },
  1243. {
  1244. name: "test needn't update",
  1245. replicaSet: &apps.ReplicaSet{
  1246. ObjectMeta: metav1.ObjectMeta{
  1247. Name: "hello",
  1248. Namespace: "test",
  1249. Annotations: map[string]string{DesiredReplicasAnnotation: desiredReplicas, MaxReplicasAnnotation: maxReplicas},
  1250. },
  1251. Spec: apps.ReplicaSetSpec{
  1252. Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
  1253. },
  1254. },
  1255. expected: false,
  1256. },
  1257. }
  1258. for i, test := range tests {
  1259. t.Run(test.name, func(t *testing.T) {
  1260. result := ReplicasAnnotationsNeedUpdate(test.replicaSet, 10, 20)
  1261. if result != test.expected {
  1262. t.Errorf("case[%d]:%s Expected %v, Got: %v", i, test.name, test.expected, result)
  1263. }
  1264. })
  1265. }
  1266. }