deployment_util_test.go 42 KB

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