validation_test.go 94 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935
  1. /*
  2. Copyright 2016 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package validation
  14. import (
  15. "strconv"
  16. "strings"
  17. "testing"
  18. "github.com/davecgh/go-spew/spew"
  19. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  20. "k8s.io/apimachinery/pkg/runtime"
  21. "k8s.io/apimachinery/pkg/util/intstr"
  22. "k8s.io/apimachinery/pkg/util/validation/field"
  23. "k8s.io/kubernetes/pkg/apis/apps"
  24. api "k8s.io/kubernetes/pkg/apis/core"
  25. )
  26. func TestValidateStatefulSet(t *testing.T) {
  27. validLabels := map[string]string{"a": "b"}
  28. validPodTemplate := api.PodTemplate{
  29. Template: api.PodTemplateSpec{
  30. ObjectMeta: metav1.ObjectMeta{
  31. Labels: validLabels,
  32. },
  33. Spec: api.PodSpec{
  34. RestartPolicy: api.RestartPolicyAlways,
  35. DNSPolicy: api.DNSClusterFirst,
  36. Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
  37. },
  38. },
  39. }
  40. invalidLabels := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
  41. invalidPodTemplate := api.PodTemplate{
  42. Template: api.PodTemplateSpec{
  43. Spec: api.PodSpec{
  44. RestartPolicy: api.RestartPolicyAlways,
  45. DNSPolicy: api.DNSClusterFirst,
  46. },
  47. ObjectMeta: metav1.ObjectMeta{
  48. Labels: invalidLabels,
  49. },
  50. },
  51. }
  52. invalidTime := int64(60)
  53. invalidPodTemplate2 := api.PodTemplate{
  54. Template: api.PodTemplateSpec{
  55. ObjectMeta: metav1.ObjectMeta{
  56. Labels: map[string]string{"foo": "bar"},
  57. },
  58. Spec: api.PodSpec{
  59. RestartPolicy: api.RestartPolicyOnFailure,
  60. DNSPolicy: api.DNSClusterFirst,
  61. ActiveDeadlineSeconds: &invalidTime,
  62. },
  63. },
  64. }
  65. successCases := []apps.StatefulSet{
  66. {
  67. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  68. Spec: apps.StatefulSetSpec{
  69. PodManagementPolicy: apps.OrderedReadyPodManagement,
  70. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  71. Template: validPodTemplate.Template,
  72. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
  73. },
  74. },
  75. {
  76. ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault},
  77. Spec: apps.StatefulSetSpec{
  78. PodManagementPolicy: apps.OrderedReadyPodManagement,
  79. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  80. Template: validPodTemplate.Template,
  81. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
  82. },
  83. },
  84. {
  85. ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault},
  86. Spec: apps.StatefulSetSpec{
  87. PodManagementPolicy: apps.ParallelPodManagement,
  88. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  89. Template: validPodTemplate.Template,
  90. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
  91. },
  92. },
  93. {
  94. ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault},
  95. Spec: apps.StatefulSetSpec{
  96. PodManagementPolicy: apps.OrderedReadyPodManagement,
  97. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  98. Template: validPodTemplate.Template,
  99. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.OnDeleteStatefulSetStrategyType},
  100. },
  101. },
  102. {
  103. ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault},
  104. Spec: apps.StatefulSetSpec{
  105. PodManagementPolicy: apps.OrderedReadyPodManagement,
  106. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  107. Template: validPodTemplate.Template,
  108. Replicas: 3,
  109. UpdateStrategy: apps.StatefulSetUpdateStrategy{
  110. Type: apps.RollingUpdateStatefulSetStrategyType,
  111. RollingUpdate: func() *apps.RollingUpdateStatefulSetStrategy {
  112. return &apps.RollingUpdateStatefulSetStrategy{Partition: 2}
  113. }()},
  114. },
  115. },
  116. }
  117. for i, successCase := range successCases {
  118. t.Run("success case "+strconv.Itoa(i), func(t *testing.T) {
  119. if errs := ValidateStatefulSet(&successCase); len(errs) != 0 {
  120. t.Errorf("expected success: %v", errs)
  121. }
  122. })
  123. }
  124. errorCases := map[string]apps.StatefulSet{
  125. "zero-length ID": {
  126. ObjectMeta: metav1.ObjectMeta{Name: "", Namespace: metav1.NamespaceDefault},
  127. Spec: apps.StatefulSetSpec{
  128. PodManagementPolicy: apps.OrderedReadyPodManagement,
  129. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  130. Template: validPodTemplate.Template,
  131. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
  132. },
  133. },
  134. "missing-namespace": {
  135. ObjectMeta: metav1.ObjectMeta{Name: "abc-123"},
  136. Spec: apps.StatefulSetSpec{
  137. PodManagementPolicy: apps.OrderedReadyPodManagement,
  138. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  139. Template: validPodTemplate.Template,
  140. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
  141. },
  142. },
  143. "empty selector": {
  144. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  145. Spec: apps.StatefulSetSpec{
  146. PodManagementPolicy: apps.OrderedReadyPodManagement,
  147. Template: validPodTemplate.Template,
  148. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
  149. },
  150. },
  151. "selector_doesnt_match": {
  152. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  153. Spec: apps.StatefulSetSpec{
  154. PodManagementPolicy: apps.OrderedReadyPodManagement,
  155. Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
  156. Template: validPodTemplate.Template,
  157. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
  158. },
  159. },
  160. "invalid manifest": {
  161. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  162. Spec: apps.StatefulSetSpec{
  163. PodManagementPolicy: apps.OrderedReadyPodManagement,
  164. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  165. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
  166. },
  167. },
  168. "negative_replicas": {
  169. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  170. Spec: apps.StatefulSetSpec{
  171. PodManagementPolicy: apps.OrderedReadyPodManagement,
  172. Replicas: -1,
  173. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  174. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
  175. },
  176. },
  177. "invalid_label": {
  178. ObjectMeta: metav1.ObjectMeta{
  179. Name: "abc-123",
  180. Namespace: metav1.NamespaceDefault,
  181. Labels: map[string]string{
  182. "NoUppercaseOrSpecialCharsLike=Equals": "bar",
  183. },
  184. },
  185. Spec: apps.StatefulSetSpec{
  186. PodManagementPolicy: apps.OrderedReadyPodManagement,
  187. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  188. Template: validPodTemplate.Template,
  189. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
  190. },
  191. },
  192. "invalid_label 2": {
  193. ObjectMeta: metav1.ObjectMeta{
  194. Name: "abc-123",
  195. Namespace: metav1.NamespaceDefault,
  196. Labels: map[string]string{
  197. "NoUppercaseOrSpecialCharsLike=Equals": "bar",
  198. },
  199. },
  200. Spec: apps.StatefulSetSpec{
  201. PodManagementPolicy: apps.OrderedReadyPodManagement,
  202. Template: invalidPodTemplate.Template,
  203. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
  204. },
  205. },
  206. "invalid_annotation": {
  207. ObjectMeta: metav1.ObjectMeta{
  208. Name: "abc-123",
  209. Namespace: metav1.NamespaceDefault,
  210. Annotations: map[string]string{
  211. "NoUppercaseOrSpecialCharsLike=Equals": "bar",
  212. },
  213. },
  214. Spec: apps.StatefulSetSpec{
  215. PodManagementPolicy: apps.OrderedReadyPodManagement,
  216. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  217. Template: validPodTemplate.Template,
  218. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
  219. },
  220. },
  221. "invalid restart policy 1": {
  222. ObjectMeta: metav1.ObjectMeta{
  223. Name: "abc-123",
  224. Namespace: metav1.NamespaceDefault,
  225. },
  226. Spec: apps.StatefulSetSpec{
  227. PodManagementPolicy: apps.OrderedReadyPodManagement,
  228. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  229. Template: api.PodTemplateSpec{
  230. Spec: api.PodSpec{
  231. RestartPolicy: api.RestartPolicyOnFailure,
  232. DNSPolicy: api.DNSClusterFirst,
  233. Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
  234. },
  235. ObjectMeta: metav1.ObjectMeta{
  236. Labels: validLabels,
  237. },
  238. },
  239. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
  240. },
  241. },
  242. "invalid restart policy 2": {
  243. ObjectMeta: metav1.ObjectMeta{
  244. Name: "abc-123",
  245. Namespace: metav1.NamespaceDefault,
  246. },
  247. Spec: apps.StatefulSetSpec{
  248. PodManagementPolicy: apps.OrderedReadyPodManagement,
  249. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  250. Template: api.PodTemplateSpec{
  251. Spec: api.PodSpec{
  252. RestartPolicy: api.RestartPolicyNever,
  253. DNSPolicy: api.DNSClusterFirst,
  254. Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
  255. },
  256. ObjectMeta: metav1.ObjectMeta{
  257. Labels: validLabels,
  258. },
  259. },
  260. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
  261. },
  262. },
  263. "invalid update strategy": {
  264. ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault},
  265. Spec: apps.StatefulSetSpec{
  266. PodManagementPolicy: apps.OrderedReadyPodManagement,
  267. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  268. Template: validPodTemplate.Template,
  269. Replicas: 3,
  270. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: "foo"},
  271. },
  272. },
  273. "empty update strategy": {
  274. ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault},
  275. Spec: apps.StatefulSetSpec{
  276. PodManagementPolicy: apps.OrderedReadyPodManagement,
  277. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  278. Template: validPodTemplate.Template,
  279. Replicas: 3,
  280. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: ""},
  281. },
  282. },
  283. "invalid rolling update": {
  284. ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault},
  285. Spec: apps.StatefulSetSpec{
  286. PodManagementPolicy: apps.OrderedReadyPodManagement,
  287. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  288. Template: validPodTemplate.Template,
  289. Replicas: 3,
  290. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.OnDeleteStatefulSetStrategyType,
  291. RollingUpdate: func() *apps.RollingUpdateStatefulSetStrategy {
  292. return &apps.RollingUpdateStatefulSetStrategy{Partition: 1}
  293. }()},
  294. },
  295. },
  296. "negative parition": {
  297. ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault},
  298. Spec: apps.StatefulSetSpec{
  299. PodManagementPolicy: apps.OrderedReadyPodManagement,
  300. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  301. Template: validPodTemplate.Template,
  302. Replicas: 3,
  303. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType,
  304. RollingUpdate: func() *apps.RollingUpdateStatefulSetStrategy {
  305. return &apps.RollingUpdateStatefulSetStrategy{Partition: -1}
  306. }()},
  307. },
  308. },
  309. "empty pod management policy": {
  310. ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault},
  311. Spec: apps.StatefulSetSpec{
  312. PodManagementPolicy: "",
  313. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  314. Template: validPodTemplate.Template,
  315. Replicas: 3,
  316. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
  317. },
  318. },
  319. "invalid pod management policy": {
  320. ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault},
  321. Spec: apps.StatefulSetSpec{
  322. PodManagementPolicy: "foo",
  323. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  324. Template: validPodTemplate.Template,
  325. Replicas: 3,
  326. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
  327. },
  328. },
  329. "set active deadline seconds": {
  330. ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault},
  331. Spec: apps.StatefulSetSpec{
  332. PodManagementPolicy: "foo",
  333. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  334. Template: invalidPodTemplate2.Template,
  335. Replicas: 3,
  336. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
  337. },
  338. },
  339. }
  340. for k, v := range errorCases {
  341. t.Run(k, func(t *testing.T) {
  342. errs := ValidateStatefulSet(&v)
  343. if len(errs) == 0 {
  344. t.Errorf("expected failure for %s", k)
  345. }
  346. for i := range errs {
  347. field := errs[i].Field
  348. if !strings.HasPrefix(field, "spec.template.") &&
  349. field != "metadata.name" &&
  350. field != "metadata.namespace" &&
  351. field != "spec.selector" &&
  352. field != "spec.template" &&
  353. field != "GCEPersistentDisk.ReadOnly" &&
  354. field != "spec.replicas" &&
  355. field != "spec.template.labels" &&
  356. field != "metadata.annotations" &&
  357. field != "metadata.labels" &&
  358. field != "status.replicas" &&
  359. field != "spec.updateStrategy" &&
  360. field != "spec.updateStrategy.rollingUpdate" &&
  361. field != "spec.updateStrategy.rollingUpdate.partition" &&
  362. field != "spec.podManagementPolicy" &&
  363. field != "spec.template.spec.activeDeadlineSeconds" {
  364. t.Errorf("%s: missing prefix for: %v", k, errs[i])
  365. }
  366. }
  367. })
  368. }
  369. }
  370. func TestValidateStatefulSetStatus(t *testing.T) {
  371. observedGenerationMinusOne := int64(-1)
  372. collisionCountMinusOne := int32(-1)
  373. tests := []struct {
  374. name string
  375. replicas int32
  376. readyReplicas int32
  377. currentReplicas int32
  378. updatedReplicas int32
  379. observedGeneration *int64
  380. collisionCount *int32
  381. expectedErr bool
  382. }{
  383. {
  384. name: "valid status",
  385. replicas: 3,
  386. readyReplicas: 3,
  387. currentReplicas: 2,
  388. updatedReplicas: 1,
  389. expectedErr: false,
  390. },
  391. {
  392. name: "invalid replicas",
  393. replicas: -1,
  394. readyReplicas: 3,
  395. currentReplicas: 2,
  396. updatedReplicas: 1,
  397. expectedErr: true,
  398. },
  399. {
  400. name: "invalid readyReplicas",
  401. replicas: 3,
  402. readyReplicas: -1,
  403. currentReplicas: 2,
  404. updatedReplicas: 1,
  405. expectedErr: true,
  406. },
  407. {
  408. name: "invalid currentReplicas",
  409. replicas: 3,
  410. readyReplicas: 3,
  411. currentReplicas: -1,
  412. updatedReplicas: 1,
  413. expectedErr: true,
  414. },
  415. {
  416. name: "invalid updatedReplicas",
  417. replicas: 3,
  418. readyReplicas: 3,
  419. currentReplicas: 2,
  420. updatedReplicas: -1,
  421. expectedErr: true,
  422. },
  423. {
  424. name: "invalid observedGeneration",
  425. replicas: 3,
  426. readyReplicas: 3,
  427. currentReplicas: 2,
  428. updatedReplicas: 1,
  429. observedGeneration: &observedGenerationMinusOne,
  430. expectedErr: true,
  431. },
  432. {
  433. name: "invalid collisionCount",
  434. replicas: 3,
  435. readyReplicas: 3,
  436. currentReplicas: 2,
  437. updatedReplicas: 1,
  438. collisionCount: &collisionCountMinusOne,
  439. expectedErr: true,
  440. },
  441. {
  442. name: "readyReplicas greater than replicas",
  443. replicas: 3,
  444. readyReplicas: 4,
  445. currentReplicas: 2,
  446. updatedReplicas: 1,
  447. expectedErr: true,
  448. },
  449. {
  450. name: "currentReplicas greater than replicas",
  451. replicas: 3,
  452. readyReplicas: 3,
  453. currentReplicas: 4,
  454. updatedReplicas: 1,
  455. expectedErr: true,
  456. },
  457. {
  458. name: "updatedReplicas greater than replicas",
  459. replicas: 3,
  460. readyReplicas: 3,
  461. currentReplicas: 2,
  462. updatedReplicas: 4,
  463. expectedErr: true,
  464. },
  465. }
  466. for _, test := range tests {
  467. t.Run(test.name, func(t *testing.T) {
  468. status := apps.StatefulSetStatus{
  469. Replicas: test.replicas,
  470. ReadyReplicas: test.readyReplicas,
  471. CurrentReplicas: test.currentReplicas,
  472. UpdatedReplicas: test.updatedReplicas,
  473. ObservedGeneration: test.observedGeneration,
  474. CollisionCount: test.collisionCount,
  475. }
  476. errs := ValidateStatefulSetStatus(&status, field.NewPath("status"))
  477. if hasErr := len(errs) > 0; hasErr != test.expectedErr {
  478. t.Errorf("%s: expected error: %t, got error: %t\nerrors: %s", test.name, test.expectedErr, hasErr, errs.ToAggregate().Error())
  479. }
  480. })
  481. }
  482. }
  483. func TestValidateStatefulSetUpdate(t *testing.T) {
  484. validLabels := map[string]string{"a": "b"}
  485. validPodTemplate := api.PodTemplate{
  486. Template: api.PodTemplateSpec{
  487. ObjectMeta: metav1.ObjectMeta{
  488. Labels: validLabels,
  489. },
  490. Spec: api.PodSpec{
  491. RestartPolicy: api.RestartPolicyAlways,
  492. DNSPolicy: api.DNSClusterFirst,
  493. Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
  494. },
  495. },
  496. }
  497. addContainersValidTemplate := validPodTemplate.DeepCopy()
  498. addContainersValidTemplate.Template.Spec.Containers = append(addContainersValidTemplate.Template.Spec.Containers,
  499. api.Container{Name: "def", Image: "image2", ImagePullPolicy: "IfNotPresent"})
  500. if len(addContainersValidTemplate.Template.Spec.Containers) != len(validPodTemplate.Template.Spec.Containers)+1 {
  501. t.Errorf("failure during test setup: template %v should have more containers than template %v", addContainersValidTemplate, validPodTemplate)
  502. }
  503. readWriteVolumePodTemplate := api.PodTemplate{
  504. Template: api.PodTemplateSpec{
  505. ObjectMeta: metav1.ObjectMeta{
  506. Labels: validLabels,
  507. },
  508. Spec: api.PodSpec{
  509. RestartPolicy: api.RestartPolicyAlways,
  510. DNSPolicy: api.DNSClusterFirst,
  511. Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
  512. Volumes: []api.Volume{{Name: "gcepd", VolumeSource: api.VolumeSource{GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{PDName: "my-PD", FSType: "ext4", Partition: 1, ReadOnly: false}}}},
  513. },
  514. },
  515. }
  516. invalidLabels := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
  517. invalidPodTemplate := api.PodTemplate{
  518. Template: api.PodTemplateSpec{
  519. Spec: api.PodSpec{
  520. RestartPolicy: api.RestartPolicyAlways,
  521. DNSPolicy: api.DNSClusterFirst,
  522. },
  523. ObjectMeta: metav1.ObjectMeta{
  524. Labels: invalidLabels,
  525. },
  526. },
  527. }
  528. type psUpdateTest struct {
  529. old apps.StatefulSet
  530. update apps.StatefulSet
  531. }
  532. successCases := []psUpdateTest{
  533. {
  534. old: apps.StatefulSet{
  535. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  536. Spec: apps.StatefulSetSpec{
  537. PodManagementPolicy: apps.OrderedReadyPodManagement,
  538. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  539. Template: validPodTemplate.Template,
  540. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
  541. },
  542. },
  543. update: apps.StatefulSet{
  544. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  545. Spec: apps.StatefulSetSpec{
  546. PodManagementPolicy: apps.OrderedReadyPodManagement,
  547. Replicas: 3,
  548. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  549. Template: validPodTemplate.Template,
  550. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
  551. },
  552. },
  553. },
  554. {
  555. old: apps.StatefulSet{
  556. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  557. Spec: apps.StatefulSetSpec{
  558. PodManagementPolicy: apps.OrderedReadyPodManagement,
  559. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  560. Template: validPodTemplate.Template,
  561. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
  562. },
  563. },
  564. update: apps.StatefulSet{
  565. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  566. Spec: apps.StatefulSetSpec{
  567. PodManagementPolicy: apps.OrderedReadyPodManagement,
  568. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  569. Template: addContainersValidTemplate.Template,
  570. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
  571. },
  572. },
  573. },
  574. {
  575. old: apps.StatefulSet{
  576. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  577. Spec: apps.StatefulSetSpec{
  578. PodManagementPolicy: apps.OrderedReadyPodManagement,
  579. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  580. Template: addContainersValidTemplate.Template,
  581. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
  582. },
  583. },
  584. update: apps.StatefulSet{
  585. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  586. Spec: apps.StatefulSetSpec{
  587. PodManagementPolicy: apps.OrderedReadyPodManagement,
  588. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  589. Template: validPodTemplate.Template,
  590. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
  591. },
  592. },
  593. },
  594. }
  595. for i, successCase := range successCases {
  596. t.Run("success case "+strconv.Itoa(i), func(t *testing.T) {
  597. successCase.old.ObjectMeta.ResourceVersion = "1"
  598. successCase.update.ObjectMeta.ResourceVersion = "1"
  599. if errs := ValidateStatefulSetUpdate(&successCase.update, &successCase.old); len(errs) != 0 {
  600. t.Errorf("expected success: %v", errs)
  601. }
  602. })
  603. }
  604. errorCases := map[string]psUpdateTest{
  605. "more than one read/write": {
  606. old: apps.StatefulSet{
  607. ObjectMeta: metav1.ObjectMeta{Name: "", Namespace: metav1.NamespaceDefault},
  608. Spec: apps.StatefulSetSpec{
  609. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  610. Template: validPodTemplate.Template,
  611. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
  612. },
  613. },
  614. update: apps.StatefulSet{
  615. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  616. Spec: apps.StatefulSetSpec{
  617. PodManagementPolicy: apps.OrderedReadyPodManagement,
  618. Replicas: 2,
  619. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  620. Template: readWriteVolumePodTemplate.Template,
  621. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
  622. },
  623. },
  624. },
  625. "empty pod creation policy": {
  626. old: apps.StatefulSet{
  627. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  628. Spec: apps.StatefulSetSpec{
  629. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  630. Template: validPodTemplate.Template,
  631. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
  632. },
  633. },
  634. update: apps.StatefulSet{
  635. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  636. Spec: apps.StatefulSetSpec{
  637. Replicas: 3,
  638. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  639. Template: validPodTemplate.Template,
  640. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
  641. },
  642. },
  643. },
  644. "invalid pod creation policy": {
  645. old: apps.StatefulSet{
  646. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  647. Spec: apps.StatefulSetSpec{
  648. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  649. Template: validPodTemplate.Template,
  650. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
  651. },
  652. },
  653. update: apps.StatefulSet{
  654. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  655. Spec: apps.StatefulSetSpec{
  656. PodManagementPolicy: apps.PodManagementPolicyType("Other"),
  657. Replicas: 3,
  658. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  659. Template: validPodTemplate.Template,
  660. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
  661. },
  662. },
  663. },
  664. "invalid selector": {
  665. old: apps.StatefulSet{
  666. ObjectMeta: metav1.ObjectMeta{Name: "", Namespace: metav1.NamespaceDefault},
  667. Spec: apps.StatefulSetSpec{
  668. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  669. Template: validPodTemplate.Template,
  670. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
  671. },
  672. },
  673. update: apps.StatefulSet{
  674. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  675. Spec: apps.StatefulSetSpec{
  676. PodManagementPolicy: apps.OrderedReadyPodManagement,
  677. Replicas: 2,
  678. Selector: &metav1.LabelSelector{MatchLabels: invalidLabels},
  679. Template: validPodTemplate.Template,
  680. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
  681. },
  682. },
  683. },
  684. "invalid pod": {
  685. old: apps.StatefulSet{
  686. ObjectMeta: metav1.ObjectMeta{Name: "", Namespace: metav1.NamespaceDefault},
  687. Spec: apps.StatefulSetSpec{
  688. PodManagementPolicy: apps.OrderedReadyPodManagement,
  689. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  690. Template: validPodTemplate.Template,
  691. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
  692. },
  693. },
  694. update: apps.StatefulSet{
  695. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  696. Spec: apps.StatefulSetSpec{
  697. Replicas: 2,
  698. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  699. Template: invalidPodTemplate.Template,
  700. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
  701. },
  702. },
  703. },
  704. "negative replicas": {
  705. old: apps.StatefulSet{
  706. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  707. Spec: apps.StatefulSetSpec{
  708. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  709. Template: validPodTemplate.Template,
  710. },
  711. },
  712. update: apps.StatefulSet{
  713. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  714. Spec: apps.StatefulSetSpec{
  715. PodManagementPolicy: apps.OrderedReadyPodManagement,
  716. Replicas: -1,
  717. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  718. Template: validPodTemplate.Template,
  719. UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
  720. },
  721. },
  722. },
  723. }
  724. for testName, errorCase := range errorCases {
  725. t.Run(testName, func(t *testing.T) {
  726. if errs := ValidateStatefulSetUpdate(&errorCase.update, &errorCase.old); len(errs) == 0 {
  727. t.Errorf("expected failure: %s", testName)
  728. }
  729. })
  730. }
  731. }
  732. func TestValidateControllerRevision(t *testing.T) {
  733. newControllerRevision := func(name, namespace string, data runtime.Object, revision int64) apps.ControllerRevision {
  734. return apps.ControllerRevision{
  735. ObjectMeta: metav1.ObjectMeta{
  736. Name: name,
  737. Namespace: namespace,
  738. },
  739. Data: data,
  740. Revision: revision,
  741. }
  742. }
  743. ss := apps.StatefulSet{
  744. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  745. Spec: apps.StatefulSetSpec{
  746. Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
  747. Template: api.PodTemplateSpec{
  748. Spec: api.PodSpec{
  749. RestartPolicy: api.RestartPolicyAlways,
  750. DNSPolicy: api.DNSClusterFirst,
  751. },
  752. ObjectMeta: metav1.ObjectMeta{
  753. Labels: map[string]string{"foo": "bar"},
  754. },
  755. },
  756. },
  757. }
  758. var (
  759. valid = newControllerRevision("validname", "validns", &ss, 0)
  760. badRevision = newControllerRevision("validname", "validns", &ss, -1)
  761. emptyName = newControllerRevision("", "validns", &ss, 0)
  762. invalidName = newControllerRevision("NoUppercaseOrSpecialCharsLike=Equals", "validns", &ss, 0)
  763. emptyNs = newControllerRevision("validname", "", &ss, 100)
  764. invalidNs = newControllerRevision("validname", "NoUppercaseOrSpecialCharsLike=Equals", &ss, 100)
  765. nilData = newControllerRevision("validname", "NoUppercaseOrSpecialCharsLike=Equals", nil, 100)
  766. )
  767. tests := map[string]struct {
  768. history apps.ControllerRevision
  769. isValid bool
  770. }{
  771. "valid": {valid, true},
  772. "negative revision": {badRevision, false},
  773. "empty name": {emptyName, false},
  774. "invalid name": {invalidName, false},
  775. "empty namespace": {emptyNs, false},
  776. "invalid namespace": {invalidNs, false},
  777. "nil data": {nilData, false},
  778. }
  779. for name, tc := range tests {
  780. t.Run(name, func(t *testing.T) {
  781. errs := ValidateControllerRevision(&tc.history)
  782. if tc.isValid && len(errs) > 0 {
  783. t.Errorf("%v: unexpected error: %v", name, errs)
  784. }
  785. if !tc.isValid && len(errs) == 0 {
  786. t.Errorf("%v: unexpected non-error", name)
  787. }
  788. })
  789. }
  790. }
  791. func TestValidateControllerRevisionUpdate(t *testing.T) {
  792. newControllerRevision := func(version, name, namespace string, data runtime.Object, revision int64) apps.ControllerRevision {
  793. return apps.ControllerRevision{
  794. ObjectMeta: metav1.ObjectMeta{
  795. Name: name,
  796. Namespace: namespace,
  797. ResourceVersion: version,
  798. },
  799. Data: data,
  800. Revision: revision,
  801. }
  802. }
  803. ss := apps.StatefulSet{
  804. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  805. Spec: apps.StatefulSetSpec{
  806. Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
  807. Template: api.PodTemplateSpec{
  808. Spec: api.PodSpec{
  809. RestartPolicy: api.RestartPolicyAlways,
  810. DNSPolicy: api.DNSClusterFirst,
  811. },
  812. ObjectMeta: metav1.ObjectMeta{
  813. Labels: map[string]string{"foo": "bar"},
  814. },
  815. },
  816. },
  817. }
  818. modifiedss := apps.StatefulSet{
  819. ObjectMeta: metav1.ObjectMeta{Name: "cdf", Namespace: metav1.NamespaceDefault},
  820. Spec: apps.StatefulSetSpec{
  821. Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
  822. Template: api.PodTemplateSpec{
  823. Spec: api.PodSpec{
  824. RestartPolicy: api.RestartPolicyAlways,
  825. DNSPolicy: api.DNSClusterFirst,
  826. },
  827. ObjectMeta: metav1.ObjectMeta{
  828. Labels: map[string]string{"foo": "bar"},
  829. },
  830. },
  831. },
  832. }
  833. var (
  834. valid = newControllerRevision("1", "validname", "validns", &ss, 0)
  835. noVersion = newControllerRevision("", "validname", "validns", &ss, 0)
  836. changedData = newControllerRevision("1", "validname", "validns", &modifiedss, 0)
  837. changedRevision = newControllerRevision("1", "validname", "validns", &ss, 1)
  838. )
  839. cases := []struct {
  840. name string
  841. newHistory apps.ControllerRevision
  842. oldHistory apps.ControllerRevision
  843. isValid bool
  844. }{
  845. {
  846. name: "valid",
  847. newHistory: valid,
  848. oldHistory: valid,
  849. isValid: true,
  850. },
  851. {
  852. name: "invalid",
  853. newHistory: noVersion,
  854. oldHistory: valid,
  855. isValid: false,
  856. },
  857. {
  858. name: "changed data",
  859. newHistory: changedData,
  860. oldHistory: valid,
  861. isValid: false,
  862. },
  863. {
  864. name: "changed revision",
  865. newHistory: changedRevision,
  866. oldHistory: valid,
  867. isValid: true,
  868. },
  869. }
  870. for _, tc := range cases {
  871. t.Run(tc.name, func(t *testing.T) {
  872. errs := ValidateControllerRevisionUpdate(&tc.newHistory, &tc.oldHistory)
  873. if tc.isValid && len(errs) > 0 {
  874. t.Errorf("%v: unexpected error: %v", tc.name, errs)
  875. }
  876. if !tc.isValid && len(errs) == 0 {
  877. t.Errorf("%v: unexpected non-error", tc.name)
  878. }
  879. })
  880. }
  881. }
  882. func TestValidateDaemonSetStatusUpdate(t *testing.T) {
  883. type dsUpdateTest struct {
  884. old apps.DaemonSet
  885. update apps.DaemonSet
  886. }
  887. successCases := []dsUpdateTest{
  888. {
  889. old: apps.DaemonSet{
  890. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  891. Status: apps.DaemonSetStatus{
  892. CurrentNumberScheduled: 1,
  893. NumberMisscheduled: 2,
  894. DesiredNumberScheduled: 3,
  895. NumberReady: 1,
  896. UpdatedNumberScheduled: 1,
  897. NumberAvailable: 1,
  898. NumberUnavailable: 2,
  899. },
  900. },
  901. update: apps.DaemonSet{
  902. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  903. Status: apps.DaemonSetStatus{
  904. CurrentNumberScheduled: 1,
  905. NumberMisscheduled: 1,
  906. DesiredNumberScheduled: 3,
  907. NumberReady: 1,
  908. UpdatedNumberScheduled: 1,
  909. NumberAvailable: 1,
  910. NumberUnavailable: 2,
  911. },
  912. },
  913. },
  914. }
  915. for _, successCase := range successCases {
  916. successCase.old.ObjectMeta.ResourceVersion = "1"
  917. successCase.update.ObjectMeta.ResourceVersion = "1"
  918. if errs := ValidateDaemonSetStatusUpdate(&successCase.update, &successCase.old); len(errs) != 0 {
  919. t.Errorf("expected success: %v", errs)
  920. }
  921. }
  922. errorCases := map[string]dsUpdateTest{
  923. "negative values": {
  924. old: apps.DaemonSet{
  925. ObjectMeta: metav1.ObjectMeta{
  926. Name: "abc",
  927. Namespace: metav1.NamespaceDefault,
  928. ResourceVersion: "10",
  929. },
  930. Status: apps.DaemonSetStatus{
  931. CurrentNumberScheduled: 1,
  932. NumberMisscheduled: 2,
  933. DesiredNumberScheduled: 3,
  934. NumberReady: 1,
  935. ObservedGeneration: 3,
  936. UpdatedNumberScheduled: 1,
  937. NumberAvailable: 1,
  938. NumberUnavailable: 2,
  939. },
  940. },
  941. update: apps.DaemonSet{
  942. ObjectMeta: metav1.ObjectMeta{
  943. Name: "abc",
  944. Namespace: metav1.NamespaceDefault,
  945. ResourceVersion: "10",
  946. },
  947. Status: apps.DaemonSetStatus{
  948. CurrentNumberScheduled: -1,
  949. NumberMisscheduled: -1,
  950. DesiredNumberScheduled: -3,
  951. NumberReady: -1,
  952. ObservedGeneration: -3,
  953. UpdatedNumberScheduled: -1,
  954. NumberAvailable: -1,
  955. NumberUnavailable: -2,
  956. },
  957. },
  958. },
  959. "negative CurrentNumberScheduled": {
  960. old: apps.DaemonSet{
  961. ObjectMeta: metav1.ObjectMeta{
  962. Name: "abc",
  963. Namespace: metav1.NamespaceDefault,
  964. ResourceVersion: "10",
  965. },
  966. Status: apps.DaemonSetStatus{
  967. CurrentNumberScheduled: 1,
  968. NumberMisscheduled: 2,
  969. DesiredNumberScheduled: 3,
  970. NumberReady: 1,
  971. ObservedGeneration: 3,
  972. UpdatedNumberScheduled: 1,
  973. NumberAvailable: 1,
  974. NumberUnavailable: 2,
  975. },
  976. },
  977. update: apps.DaemonSet{
  978. ObjectMeta: metav1.ObjectMeta{
  979. Name: "abc",
  980. Namespace: metav1.NamespaceDefault,
  981. ResourceVersion: "10",
  982. },
  983. Status: apps.DaemonSetStatus{
  984. CurrentNumberScheduled: -1,
  985. NumberMisscheduled: 1,
  986. DesiredNumberScheduled: 3,
  987. NumberReady: 1,
  988. ObservedGeneration: 3,
  989. UpdatedNumberScheduled: 1,
  990. NumberAvailable: 1,
  991. NumberUnavailable: 2,
  992. },
  993. },
  994. },
  995. "negative NumberMisscheduled": {
  996. old: apps.DaemonSet{
  997. ObjectMeta: metav1.ObjectMeta{
  998. Name: "abc",
  999. Namespace: metav1.NamespaceDefault,
  1000. ResourceVersion: "10",
  1001. },
  1002. Status: apps.DaemonSetStatus{
  1003. CurrentNumberScheduled: 1,
  1004. NumberMisscheduled: 2,
  1005. DesiredNumberScheduled: 3,
  1006. NumberReady: 1,
  1007. ObservedGeneration: 3,
  1008. UpdatedNumberScheduled: 1,
  1009. NumberAvailable: 1,
  1010. NumberUnavailable: 2,
  1011. },
  1012. },
  1013. update: apps.DaemonSet{
  1014. ObjectMeta: metav1.ObjectMeta{
  1015. Name: "abc",
  1016. Namespace: metav1.NamespaceDefault,
  1017. ResourceVersion: "10",
  1018. },
  1019. Status: apps.DaemonSetStatus{
  1020. CurrentNumberScheduled: 1,
  1021. NumberMisscheduled: -1,
  1022. DesiredNumberScheduled: 3,
  1023. NumberReady: 1,
  1024. ObservedGeneration: 3,
  1025. UpdatedNumberScheduled: 1,
  1026. NumberAvailable: 1,
  1027. NumberUnavailable: 2,
  1028. },
  1029. },
  1030. },
  1031. "negative DesiredNumberScheduled": {
  1032. old: apps.DaemonSet{
  1033. ObjectMeta: metav1.ObjectMeta{
  1034. Name: "abc",
  1035. Namespace: metav1.NamespaceDefault,
  1036. ResourceVersion: "10",
  1037. },
  1038. Status: apps.DaemonSetStatus{
  1039. CurrentNumberScheduled: 1,
  1040. NumberMisscheduled: 2,
  1041. DesiredNumberScheduled: 3,
  1042. NumberReady: 1,
  1043. ObservedGeneration: 3,
  1044. UpdatedNumberScheduled: 1,
  1045. NumberAvailable: 1,
  1046. NumberUnavailable: 2,
  1047. },
  1048. },
  1049. update: apps.DaemonSet{
  1050. ObjectMeta: metav1.ObjectMeta{
  1051. Name: "abc",
  1052. Namespace: metav1.NamespaceDefault,
  1053. ResourceVersion: "10",
  1054. },
  1055. Status: apps.DaemonSetStatus{
  1056. CurrentNumberScheduled: 1,
  1057. NumberMisscheduled: 1,
  1058. DesiredNumberScheduled: -3,
  1059. NumberReady: 1,
  1060. ObservedGeneration: 3,
  1061. UpdatedNumberScheduled: 1,
  1062. NumberAvailable: 1,
  1063. NumberUnavailable: 2,
  1064. },
  1065. },
  1066. },
  1067. "negative NumberReady": {
  1068. old: apps.DaemonSet{
  1069. ObjectMeta: metav1.ObjectMeta{
  1070. Name: "abc",
  1071. Namespace: metav1.NamespaceDefault,
  1072. ResourceVersion: "10",
  1073. },
  1074. Status: apps.DaemonSetStatus{
  1075. CurrentNumberScheduled: 1,
  1076. NumberMisscheduled: 2,
  1077. DesiredNumberScheduled: 3,
  1078. NumberReady: 1,
  1079. ObservedGeneration: 3,
  1080. UpdatedNumberScheduled: 1,
  1081. NumberAvailable: 1,
  1082. NumberUnavailable: 2,
  1083. },
  1084. },
  1085. update: apps.DaemonSet{
  1086. ObjectMeta: metav1.ObjectMeta{
  1087. Name: "abc",
  1088. Namespace: metav1.NamespaceDefault,
  1089. ResourceVersion: "10",
  1090. },
  1091. Status: apps.DaemonSetStatus{
  1092. CurrentNumberScheduled: 1,
  1093. NumberMisscheduled: 1,
  1094. DesiredNumberScheduled: 3,
  1095. NumberReady: -1,
  1096. ObservedGeneration: 3,
  1097. UpdatedNumberScheduled: 1,
  1098. NumberAvailable: 1,
  1099. NumberUnavailable: 2,
  1100. },
  1101. },
  1102. },
  1103. "negative ObservedGeneration": {
  1104. old: apps.DaemonSet{
  1105. ObjectMeta: metav1.ObjectMeta{
  1106. Name: "abc",
  1107. Namespace: metav1.NamespaceDefault,
  1108. ResourceVersion: "10",
  1109. },
  1110. Status: apps.DaemonSetStatus{
  1111. CurrentNumberScheduled: 1,
  1112. NumberMisscheduled: 2,
  1113. DesiredNumberScheduled: 3,
  1114. NumberReady: 1,
  1115. ObservedGeneration: 3,
  1116. UpdatedNumberScheduled: 1,
  1117. NumberAvailable: 1,
  1118. NumberUnavailable: 2,
  1119. },
  1120. },
  1121. update: apps.DaemonSet{
  1122. ObjectMeta: metav1.ObjectMeta{
  1123. Name: "abc",
  1124. Namespace: metav1.NamespaceDefault,
  1125. ResourceVersion: "10",
  1126. },
  1127. Status: apps.DaemonSetStatus{
  1128. CurrentNumberScheduled: 1,
  1129. NumberMisscheduled: 1,
  1130. DesiredNumberScheduled: 3,
  1131. NumberReady: 1,
  1132. ObservedGeneration: -3,
  1133. UpdatedNumberScheduled: 1,
  1134. NumberAvailable: 1,
  1135. NumberUnavailable: 2,
  1136. },
  1137. },
  1138. },
  1139. "negative UpdatedNumberScheduled": {
  1140. old: apps.DaemonSet{
  1141. ObjectMeta: metav1.ObjectMeta{
  1142. Name: "abc",
  1143. Namespace: metav1.NamespaceDefault,
  1144. ResourceVersion: "10",
  1145. },
  1146. Status: apps.DaemonSetStatus{
  1147. CurrentNumberScheduled: 1,
  1148. NumberMisscheduled: 2,
  1149. DesiredNumberScheduled: 3,
  1150. NumberReady: 1,
  1151. ObservedGeneration: 3,
  1152. UpdatedNumberScheduled: 1,
  1153. NumberAvailable: 1,
  1154. NumberUnavailable: 2,
  1155. },
  1156. },
  1157. update: apps.DaemonSet{
  1158. ObjectMeta: metav1.ObjectMeta{
  1159. Name: "abc",
  1160. Namespace: metav1.NamespaceDefault,
  1161. ResourceVersion: "10",
  1162. },
  1163. Status: apps.DaemonSetStatus{
  1164. CurrentNumberScheduled: 1,
  1165. NumberMisscheduled: 1,
  1166. DesiredNumberScheduled: 3,
  1167. NumberReady: 1,
  1168. ObservedGeneration: 3,
  1169. UpdatedNumberScheduled: -1,
  1170. NumberAvailable: 1,
  1171. NumberUnavailable: 2,
  1172. },
  1173. },
  1174. },
  1175. "negative NumberAvailable": {
  1176. old: apps.DaemonSet{
  1177. ObjectMeta: metav1.ObjectMeta{
  1178. Name: "abc",
  1179. Namespace: metav1.NamespaceDefault,
  1180. ResourceVersion: "10",
  1181. },
  1182. Status: apps.DaemonSetStatus{
  1183. CurrentNumberScheduled: 1,
  1184. NumberMisscheduled: 2,
  1185. DesiredNumberScheduled: 3,
  1186. NumberReady: 1,
  1187. ObservedGeneration: 3,
  1188. UpdatedNumberScheduled: 1,
  1189. NumberAvailable: 1,
  1190. NumberUnavailable: 2,
  1191. },
  1192. },
  1193. update: apps.DaemonSet{
  1194. ObjectMeta: metav1.ObjectMeta{
  1195. Name: "abc",
  1196. Namespace: metav1.NamespaceDefault,
  1197. ResourceVersion: "10",
  1198. },
  1199. Status: apps.DaemonSetStatus{
  1200. CurrentNumberScheduled: 1,
  1201. NumberMisscheduled: 1,
  1202. DesiredNumberScheduled: 3,
  1203. NumberReady: 1,
  1204. ObservedGeneration: 3,
  1205. UpdatedNumberScheduled: 1,
  1206. NumberAvailable: -1,
  1207. NumberUnavailable: 2,
  1208. },
  1209. },
  1210. },
  1211. "negative NumberUnavailable": {
  1212. old: apps.DaemonSet{
  1213. ObjectMeta: metav1.ObjectMeta{
  1214. Name: "abc",
  1215. Namespace: metav1.NamespaceDefault,
  1216. ResourceVersion: "10",
  1217. },
  1218. Status: apps.DaemonSetStatus{
  1219. CurrentNumberScheduled: 1,
  1220. NumberMisscheduled: 2,
  1221. DesiredNumberScheduled: 3,
  1222. NumberReady: 1,
  1223. ObservedGeneration: 3,
  1224. UpdatedNumberScheduled: 1,
  1225. NumberAvailable: 1,
  1226. NumberUnavailable: 2,
  1227. },
  1228. },
  1229. update: apps.DaemonSet{
  1230. ObjectMeta: metav1.ObjectMeta{
  1231. Name: "abc",
  1232. Namespace: metav1.NamespaceDefault,
  1233. ResourceVersion: "10",
  1234. },
  1235. Status: apps.DaemonSetStatus{
  1236. CurrentNumberScheduled: 1,
  1237. NumberMisscheduled: 1,
  1238. DesiredNumberScheduled: 3,
  1239. NumberReady: 1,
  1240. ObservedGeneration: 3,
  1241. UpdatedNumberScheduled: 1,
  1242. NumberAvailable: 1,
  1243. NumberUnavailable: -2,
  1244. },
  1245. },
  1246. },
  1247. }
  1248. for testName, errorCase := range errorCases {
  1249. if errs := ValidateDaemonSetStatusUpdate(&errorCase.update, &errorCase.old); len(errs) == 0 {
  1250. t.Errorf("expected failure: %s", testName)
  1251. }
  1252. }
  1253. }
  1254. func TestValidateDaemonSetUpdate(t *testing.T) {
  1255. validSelector := map[string]string{"a": "b"}
  1256. validSelector2 := map[string]string{"c": "d"}
  1257. invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
  1258. validPodSpecAbc := api.PodSpec{
  1259. RestartPolicy: api.RestartPolicyAlways,
  1260. DNSPolicy: api.DNSClusterFirst,
  1261. Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}},
  1262. }
  1263. validPodSpecDef := api.PodSpec{
  1264. RestartPolicy: api.RestartPolicyAlways,
  1265. DNSPolicy: api.DNSClusterFirst,
  1266. Containers: []api.Container{{Name: "def", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}},
  1267. }
  1268. validPodSpecNodeSelector := api.PodSpec{
  1269. NodeSelector: validSelector,
  1270. NodeName: "xyz",
  1271. RestartPolicy: api.RestartPolicyAlways,
  1272. DNSPolicy: api.DNSClusterFirst,
  1273. Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}},
  1274. }
  1275. validPodSpecVolume := api.PodSpec{
  1276. Volumes: []api.Volume{{Name: "gcepd", VolumeSource: api.VolumeSource{GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{PDName: "my-PD", FSType: "ext4", Partition: 1, ReadOnly: false}}}},
  1277. RestartPolicy: api.RestartPolicyAlways,
  1278. DNSPolicy: api.DNSClusterFirst,
  1279. Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}},
  1280. }
  1281. validPodTemplateAbc := api.PodTemplate{
  1282. Template: api.PodTemplateSpec{
  1283. ObjectMeta: metav1.ObjectMeta{
  1284. Labels: validSelector,
  1285. },
  1286. Spec: validPodSpecAbc,
  1287. },
  1288. }
  1289. validPodTemplateAbcSemanticallyEqual := api.PodTemplate{
  1290. Template: api.PodTemplateSpec{
  1291. ObjectMeta: metav1.ObjectMeta{
  1292. Labels: validSelector,
  1293. },
  1294. Spec: validPodSpecAbc,
  1295. },
  1296. }
  1297. validPodTemplateAbcSemanticallyEqual.Template.Spec.ImagePullSecrets = []api.LocalObjectReference{}
  1298. validPodTemplateNodeSelector := api.PodTemplate{
  1299. Template: api.PodTemplateSpec{
  1300. ObjectMeta: metav1.ObjectMeta{
  1301. Labels: validSelector,
  1302. },
  1303. Spec: validPodSpecNodeSelector,
  1304. },
  1305. }
  1306. validPodTemplateAbc2 := api.PodTemplate{
  1307. Template: api.PodTemplateSpec{
  1308. ObjectMeta: metav1.ObjectMeta{
  1309. Labels: validSelector2,
  1310. },
  1311. Spec: validPodSpecAbc,
  1312. },
  1313. }
  1314. validPodTemplateDef := api.PodTemplate{
  1315. Template: api.PodTemplateSpec{
  1316. ObjectMeta: metav1.ObjectMeta{
  1317. Labels: validSelector2,
  1318. },
  1319. Spec: validPodSpecDef,
  1320. },
  1321. }
  1322. invalidPodTemplate := api.PodTemplate{
  1323. Template: api.PodTemplateSpec{
  1324. Spec: api.PodSpec{
  1325. // no containers specified
  1326. RestartPolicy: api.RestartPolicyAlways,
  1327. DNSPolicy: api.DNSClusterFirst,
  1328. },
  1329. ObjectMeta: metav1.ObjectMeta{
  1330. Labels: validSelector,
  1331. },
  1332. },
  1333. }
  1334. readWriteVolumePodTemplate := api.PodTemplate{
  1335. Template: api.PodTemplateSpec{
  1336. ObjectMeta: metav1.ObjectMeta{
  1337. Labels: validSelector,
  1338. },
  1339. Spec: validPodSpecVolume,
  1340. },
  1341. }
  1342. type dsUpdateTest struct {
  1343. old apps.DaemonSet
  1344. update apps.DaemonSet
  1345. expectedErrNum int
  1346. }
  1347. successCases := map[string]dsUpdateTest{
  1348. "no change": {
  1349. old: apps.DaemonSet{
  1350. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  1351. Spec: apps.DaemonSetSpec{
  1352. Selector: &metav1.LabelSelector{MatchLabels: validSelector},
  1353. TemplateGeneration: 1,
  1354. Template: validPodTemplateAbc.Template,
  1355. UpdateStrategy: apps.DaemonSetUpdateStrategy{
  1356. Type: apps.OnDeleteDaemonSetStrategyType,
  1357. },
  1358. },
  1359. },
  1360. update: apps.DaemonSet{
  1361. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  1362. Spec: apps.DaemonSetSpec{
  1363. Selector: &metav1.LabelSelector{MatchLabels: validSelector},
  1364. TemplateGeneration: 1,
  1365. Template: validPodTemplateAbc.Template,
  1366. UpdateStrategy: apps.DaemonSetUpdateStrategy{
  1367. Type: apps.OnDeleteDaemonSetStrategyType,
  1368. },
  1369. },
  1370. },
  1371. },
  1372. "change template and selector": {
  1373. old: apps.DaemonSet{
  1374. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  1375. Spec: apps.DaemonSetSpec{
  1376. Selector: &metav1.LabelSelector{MatchLabels: validSelector},
  1377. TemplateGeneration: 2,
  1378. Template: validPodTemplateAbc.Template,
  1379. UpdateStrategy: apps.DaemonSetUpdateStrategy{
  1380. Type: apps.OnDeleteDaemonSetStrategyType,
  1381. },
  1382. },
  1383. },
  1384. update: apps.DaemonSet{
  1385. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  1386. Spec: apps.DaemonSetSpec{
  1387. Selector: &metav1.LabelSelector{MatchLabels: validSelector2},
  1388. TemplateGeneration: 3,
  1389. Template: validPodTemplateAbc2.Template,
  1390. UpdateStrategy: apps.DaemonSetUpdateStrategy{
  1391. Type: apps.OnDeleteDaemonSetStrategyType,
  1392. },
  1393. },
  1394. },
  1395. },
  1396. "change template": {
  1397. old: apps.DaemonSet{
  1398. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  1399. Spec: apps.DaemonSetSpec{
  1400. Selector: &metav1.LabelSelector{MatchLabels: validSelector},
  1401. TemplateGeneration: 3,
  1402. Template: validPodTemplateAbc.Template,
  1403. UpdateStrategy: apps.DaemonSetUpdateStrategy{
  1404. Type: apps.OnDeleteDaemonSetStrategyType,
  1405. },
  1406. },
  1407. },
  1408. update: apps.DaemonSet{
  1409. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  1410. Spec: apps.DaemonSetSpec{
  1411. Selector: &metav1.LabelSelector{MatchLabels: validSelector},
  1412. TemplateGeneration: 4,
  1413. Template: validPodTemplateNodeSelector.Template,
  1414. UpdateStrategy: apps.DaemonSetUpdateStrategy{
  1415. Type: apps.OnDeleteDaemonSetStrategyType,
  1416. },
  1417. },
  1418. },
  1419. },
  1420. "change container image name": {
  1421. old: apps.DaemonSet{
  1422. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  1423. Spec: apps.DaemonSetSpec{
  1424. Selector: &metav1.LabelSelector{MatchLabels: validSelector},
  1425. TemplateGeneration: 1,
  1426. Template: validPodTemplateAbc.Template,
  1427. UpdateStrategy: apps.DaemonSetUpdateStrategy{
  1428. Type: apps.OnDeleteDaemonSetStrategyType,
  1429. },
  1430. },
  1431. },
  1432. update: apps.DaemonSet{
  1433. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  1434. Spec: apps.DaemonSetSpec{
  1435. Selector: &metav1.LabelSelector{MatchLabels: validSelector2},
  1436. TemplateGeneration: 2,
  1437. Template: validPodTemplateDef.Template,
  1438. UpdateStrategy: apps.DaemonSetUpdateStrategy{
  1439. Type: apps.OnDeleteDaemonSetStrategyType,
  1440. },
  1441. },
  1442. },
  1443. },
  1444. "change update strategy": {
  1445. old: apps.DaemonSet{
  1446. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  1447. Spec: apps.DaemonSetSpec{
  1448. Selector: &metav1.LabelSelector{MatchLabels: validSelector},
  1449. TemplateGeneration: 4,
  1450. Template: validPodTemplateAbc.Template,
  1451. UpdateStrategy: apps.DaemonSetUpdateStrategy{
  1452. Type: apps.OnDeleteDaemonSetStrategyType,
  1453. },
  1454. },
  1455. },
  1456. update: apps.DaemonSet{
  1457. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  1458. Spec: apps.DaemonSetSpec{
  1459. Selector: &metav1.LabelSelector{MatchLabels: validSelector},
  1460. TemplateGeneration: 4,
  1461. Template: validPodTemplateAbc.Template,
  1462. UpdateStrategy: apps.DaemonSetUpdateStrategy{
  1463. Type: apps.RollingUpdateDaemonSetStrategyType,
  1464. RollingUpdate: &apps.RollingUpdateDaemonSet{
  1465. MaxUnavailable: intstr.FromInt(1),
  1466. },
  1467. },
  1468. },
  1469. },
  1470. },
  1471. "unchanged templateGeneration upon semantically equal template update": {
  1472. old: apps.DaemonSet{
  1473. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  1474. Spec: apps.DaemonSetSpec{
  1475. Selector: &metav1.LabelSelector{MatchLabels: validSelector},
  1476. TemplateGeneration: 4,
  1477. Template: validPodTemplateAbc.Template,
  1478. UpdateStrategy: apps.DaemonSetUpdateStrategy{
  1479. Type: apps.OnDeleteDaemonSetStrategyType,
  1480. },
  1481. },
  1482. },
  1483. update: apps.DaemonSet{
  1484. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  1485. Spec: apps.DaemonSetSpec{
  1486. Selector: &metav1.LabelSelector{MatchLabels: validSelector},
  1487. TemplateGeneration: 4,
  1488. Template: validPodTemplateAbcSemanticallyEqual.Template,
  1489. UpdateStrategy: apps.DaemonSetUpdateStrategy{
  1490. Type: apps.RollingUpdateDaemonSetStrategyType,
  1491. RollingUpdate: &apps.RollingUpdateDaemonSet{
  1492. MaxUnavailable: intstr.FromInt(1),
  1493. },
  1494. },
  1495. },
  1496. },
  1497. },
  1498. }
  1499. for testName, successCase := range successCases {
  1500. // ResourceVersion is required for updates.
  1501. successCase.old.ObjectMeta.ResourceVersion = "1"
  1502. successCase.update.ObjectMeta.ResourceVersion = "2"
  1503. // Check test setup
  1504. if successCase.expectedErrNum > 0 {
  1505. t.Errorf("%q has incorrect test setup with expectedErrNum %d, expected no error", testName, successCase.expectedErrNum)
  1506. }
  1507. if len(successCase.old.ObjectMeta.ResourceVersion) == 0 || len(successCase.update.ObjectMeta.ResourceVersion) == 0 {
  1508. t.Errorf("%q has incorrect test setup with no resource version set", testName)
  1509. }
  1510. if errs := ValidateDaemonSetUpdate(&successCase.update, &successCase.old); len(errs) != 0 {
  1511. t.Errorf("%q expected no error, but got: %v", testName, errs)
  1512. }
  1513. }
  1514. errorCases := map[string]dsUpdateTest{
  1515. "change daemon name": {
  1516. old: apps.DaemonSet{
  1517. ObjectMeta: metav1.ObjectMeta{Name: "", Namespace: metav1.NamespaceDefault},
  1518. Spec: apps.DaemonSetSpec{
  1519. Selector: &metav1.LabelSelector{MatchLabels: validSelector},
  1520. TemplateGeneration: 1,
  1521. Template: validPodTemplateAbc.Template,
  1522. UpdateStrategy: apps.DaemonSetUpdateStrategy{
  1523. Type: apps.OnDeleteDaemonSetStrategyType,
  1524. },
  1525. },
  1526. },
  1527. update: apps.DaemonSet{
  1528. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  1529. Spec: apps.DaemonSetSpec{
  1530. Selector: &metav1.LabelSelector{MatchLabels: validSelector},
  1531. TemplateGeneration: 1,
  1532. Template: validPodTemplateAbc.Template,
  1533. UpdateStrategy: apps.DaemonSetUpdateStrategy{
  1534. Type: apps.OnDeleteDaemonSetStrategyType,
  1535. },
  1536. },
  1537. },
  1538. expectedErrNum: 1,
  1539. },
  1540. "invalid selector": {
  1541. old: apps.DaemonSet{
  1542. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  1543. Spec: apps.DaemonSetSpec{
  1544. Selector: &metav1.LabelSelector{MatchLabels: validSelector},
  1545. TemplateGeneration: 1,
  1546. Template: validPodTemplateAbc.Template,
  1547. UpdateStrategy: apps.DaemonSetUpdateStrategy{
  1548. Type: apps.OnDeleteDaemonSetStrategyType,
  1549. },
  1550. },
  1551. },
  1552. update: apps.DaemonSet{
  1553. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  1554. Spec: apps.DaemonSetSpec{
  1555. Selector: &metav1.LabelSelector{MatchLabels: invalidSelector},
  1556. TemplateGeneration: 1,
  1557. Template: validPodTemplateAbc.Template,
  1558. UpdateStrategy: apps.DaemonSetUpdateStrategy{
  1559. Type: apps.OnDeleteDaemonSetStrategyType,
  1560. },
  1561. },
  1562. },
  1563. expectedErrNum: 1,
  1564. },
  1565. "invalid pod": {
  1566. old: apps.DaemonSet{
  1567. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  1568. Spec: apps.DaemonSetSpec{
  1569. Selector: &metav1.LabelSelector{MatchLabels: validSelector},
  1570. TemplateGeneration: 1,
  1571. Template: validPodTemplateAbc.Template,
  1572. UpdateStrategy: apps.DaemonSetUpdateStrategy{
  1573. Type: apps.OnDeleteDaemonSetStrategyType,
  1574. },
  1575. },
  1576. },
  1577. update: apps.DaemonSet{
  1578. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  1579. Spec: apps.DaemonSetSpec{
  1580. Selector: &metav1.LabelSelector{MatchLabels: validSelector},
  1581. TemplateGeneration: 2,
  1582. Template: invalidPodTemplate.Template,
  1583. UpdateStrategy: apps.DaemonSetUpdateStrategy{
  1584. Type: apps.OnDeleteDaemonSetStrategyType,
  1585. },
  1586. },
  1587. },
  1588. expectedErrNum: 1,
  1589. },
  1590. "invalid read-write volume": {
  1591. old: apps.DaemonSet{
  1592. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  1593. Spec: apps.DaemonSetSpec{
  1594. Selector: &metav1.LabelSelector{MatchLabels: validSelector},
  1595. TemplateGeneration: 1,
  1596. Template: validPodTemplateAbc.Template,
  1597. UpdateStrategy: apps.DaemonSetUpdateStrategy{
  1598. Type: apps.OnDeleteDaemonSetStrategyType,
  1599. },
  1600. },
  1601. },
  1602. update: apps.DaemonSet{
  1603. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  1604. Spec: apps.DaemonSetSpec{
  1605. Selector: &metav1.LabelSelector{MatchLabels: validSelector},
  1606. TemplateGeneration: 2,
  1607. Template: readWriteVolumePodTemplate.Template,
  1608. UpdateStrategy: apps.DaemonSetUpdateStrategy{
  1609. Type: apps.OnDeleteDaemonSetStrategyType,
  1610. },
  1611. },
  1612. },
  1613. expectedErrNum: 1,
  1614. },
  1615. "invalid update strategy": {
  1616. old: apps.DaemonSet{
  1617. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  1618. Spec: apps.DaemonSetSpec{
  1619. Selector: &metav1.LabelSelector{MatchLabels: validSelector},
  1620. TemplateGeneration: 1,
  1621. Template: validPodTemplateAbc.Template,
  1622. UpdateStrategy: apps.DaemonSetUpdateStrategy{
  1623. Type: apps.OnDeleteDaemonSetStrategyType,
  1624. },
  1625. },
  1626. },
  1627. update: apps.DaemonSet{
  1628. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  1629. Spec: apps.DaemonSetSpec{
  1630. Selector: &metav1.LabelSelector{MatchLabels: validSelector},
  1631. TemplateGeneration: 1,
  1632. Template: validPodTemplateAbc.Template,
  1633. UpdateStrategy: apps.DaemonSetUpdateStrategy{
  1634. Type: "Random",
  1635. },
  1636. },
  1637. },
  1638. expectedErrNum: 1,
  1639. },
  1640. "negative templateGeneration": {
  1641. old: apps.DaemonSet{
  1642. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  1643. Spec: apps.DaemonSetSpec{
  1644. Selector: &metav1.LabelSelector{MatchLabels: validSelector},
  1645. TemplateGeneration: -1,
  1646. Template: validPodTemplateAbc.Template,
  1647. UpdateStrategy: apps.DaemonSetUpdateStrategy{
  1648. Type: apps.OnDeleteDaemonSetStrategyType,
  1649. },
  1650. },
  1651. },
  1652. update: apps.DaemonSet{
  1653. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  1654. Spec: apps.DaemonSetSpec{
  1655. Selector: &metav1.LabelSelector{MatchLabels: validSelector},
  1656. TemplateGeneration: -1,
  1657. Template: validPodTemplateAbc.Template,
  1658. UpdateStrategy: apps.DaemonSetUpdateStrategy{
  1659. Type: apps.OnDeleteDaemonSetStrategyType,
  1660. },
  1661. },
  1662. },
  1663. expectedErrNum: 1,
  1664. },
  1665. "decreased templateGeneration": {
  1666. old: apps.DaemonSet{
  1667. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  1668. Spec: apps.DaemonSetSpec{
  1669. TemplateGeneration: 2,
  1670. Selector: &metav1.LabelSelector{MatchLabels: validSelector},
  1671. Template: validPodTemplateAbc.Template,
  1672. UpdateStrategy: apps.DaemonSetUpdateStrategy{
  1673. Type: apps.OnDeleteDaemonSetStrategyType,
  1674. },
  1675. },
  1676. },
  1677. update: apps.DaemonSet{
  1678. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  1679. Spec: apps.DaemonSetSpec{
  1680. TemplateGeneration: 1,
  1681. Selector: &metav1.LabelSelector{MatchLabels: validSelector},
  1682. Template: validPodTemplateAbc.Template,
  1683. UpdateStrategy: apps.DaemonSetUpdateStrategy{
  1684. Type: apps.OnDeleteDaemonSetStrategyType,
  1685. },
  1686. },
  1687. },
  1688. expectedErrNum: 1,
  1689. },
  1690. "unchanged templateGeneration upon template update": {
  1691. old: apps.DaemonSet{
  1692. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  1693. Spec: apps.DaemonSetSpec{
  1694. TemplateGeneration: 2,
  1695. Selector: &metav1.LabelSelector{MatchLabels: validSelector},
  1696. Template: validPodTemplateAbc.Template,
  1697. UpdateStrategy: apps.DaemonSetUpdateStrategy{
  1698. Type: apps.OnDeleteDaemonSetStrategyType,
  1699. },
  1700. },
  1701. },
  1702. update: apps.DaemonSet{
  1703. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  1704. Spec: apps.DaemonSetSpec{
  1705. TemplateGeneration: 2,
  1706. Selector: &metav1.LabelSelector{MatchLabels: validSelector2},
  1707. Template: validPodTemplateAbc2.Template,
  1708. UpdateStrategy: apps.DaemonSetUpdateStrategy{
  1709. Type: apps.OnDeleteDaemonSetStrategyType,
  1710. },
  1711. },
  1712. },
  1713. expectedErrNum: 1,
  1714. },
  1715. }
  1716. for testName, errorCase := range errorCases {
  1717. // ResourceVersion is required for updates.
  1718. errorCase.old.ObjectMeta.ResourceVersion = "1"
  1719. errorCase.update.ObjectMeta.ResourceVersion = "2"
  1720. // Check test setup
  1721. if errorCase.expectedErrNum <= 0 {
  1722. t.Errorf("%q has incorrect test setup with expectedErrNum %d, expected at least one error", testName, errorCase.expectedErrNum)
  1723. }
  1724. if len(errorCase.old.ObjectMeta.ResourceVersion) == 0 || len(errorCase.update.ObjectMeta.ResourceVersion) == 0 {
  1725. t.Errorf("%q has incorrect test setup with no resource version set", testName)
  1726. }
  1727. // Run the tests
  1728. if errs := ValidateDaemonSetUpdate(&errorCase.update, &errorCase.old); len(errs) != errorCase.expectedErrNum {
  1729. t.Errorf("%q expected %d errors, but got %d error: %v", testName, errorCase.expectedErrNum, len(errs), errs)
  1730. } else {
  1731. t.Logf("(PASS) %q got errors %v", testName, errs)
  1732. }
  1733. }
  1734. }
  1735. func TestValidateDaemonSet(t *testing.T) {
  1736. validSelector := map[string]string{"a": "b"}
  1737. validPodTemplate := api.PodTemplate{
  1738. Template: api.PodTemplateSpec{
  1739. ObjectMeta: metav1.ObjectMeta{
  1740. Labels: validSelector,
  1741. },
  1742. Spec: api.PodSpec{
  1743. RestartPolicy: api.RestartPolicyAlways,
  1744. DNSPolicy: api.DNSClusterFirst,
  1745. Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}},
  1746. },
  1747. },
  1748. }
  1749. invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
  1750. invalidPodTemplate := api.PodTemplate{
  1751. Template: api.PodTemplateSpec{
  1752. Spec: api.PodSpec{
  1753. RestartPolicy: api.RestartPolicyAlways,
  1754. DNSPolicy: api.DNSClusterFirst,
  1755. },
  1756. ObjectMeta: metav1.ObjectMeta{
  1757. Labels: invalidSelector,
  1758. },
  1759. },
  1760. }
  1761. successCases := []apps.DaemonSet{
  1762. {
  1763. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  1764. Spec: apps.DaemonSetSpec{
  1765. Selector: &metav1.LabelSelector{MatchLabels: validSelector},
  1766. Template: validPodTemplate.Template,
  1767. UpdateStrategy: apps.DaemonSetUpdateStrategy{
  1768. Type: apps.OnDeleteDaemonSetStrategyType,
  1769. },
  1770. },
  1771. },
  1772. {
  1773. ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault},
  1774. Spec: apps.DaemonSetSpec{
  1775. Selector: &metav1.LabelSelector{MatchLabels: validSelector},
  1776. Template: validPodTemplate.Template,
  1777. UpdateStrategy: apps.DaemonSetUpdateStrategy{
  1778. Type: apps.OnDeleteDaemonSetStrategyType,
  1779. },
  1780. },
  1781. },
  1782. }
  1783. for _, successCase := range successCases {
  1784. if errs := ValidateDaemonSet(&successCase); len(errs) != 0 {
  1785. t.Errorf("expected success: %v", errs)
  1786. }
  1787. }
  1788. errorCases := map[string]apps.DaemonSet{
  1789. "zero-length ID": {
  1790. ObjectMeta: metav1.ObjectMeta{Name: "", Namespace: metav1.NamespaceDefault},
  1791. Spec: apps.DaemonSetSpec{
  1792. Selector: &metav1.LabelSelector{MatchLabels: validSelector},
  1793. Template: validPodTemplate.Template,
  1794. },
  1795. },
  1796. "missing-namespace": {
  1797. ObjectMeta: metav1.ObjectMeta{Name: "abc-123"},
  1798. Spec: apps.DaemonSetSpec{
  1799. Selector: &metav1.LabelSelector{MatchLabels: validSelector},
  1800. Template: validPodTemplate.Template,
  1801. },
  1802. },
  1803. "nil selector": {
  1804. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  1805. Spec: apps.DaemonSetSpec{
  1806. Template: validPodTemplate.Template,
  1807. },
  1808. },
  1809. "empty selector": {
  1810. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  1811. Spec: apps.DaemonSetSpec{
  1812. Selector: &metav1.LabelSelector{},
  1813. Template: validPodTemplate.Template,
  1814. },
  1815. },
  1816. "selector_doesnt_match": {
  1817. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  1818. Spec: apps.DaemonSetSpec{
  1819. Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
  1820. Template: validPodTemplate.Template,
  1821. },
  1822. },
  1823. "invalid template": {
  1824. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  1825. Spec: apps.DaemonSetSpec{
  1826. Selector: &metav1.LabelSelector{MatchLabels: validSelector},
  1827. },
  1828. },
  1829. "invalid_label": {
  1830. ObjectMeta: metav1.ObjectMeta{
  1831. Name: "abc-123",
  1832. Namespace: metav1.NamespaceDefault,
  1833. Labels: map[string]string{
  1834. "NoUppercaseOrSpecialCharsLike=Equals": "bar",
  1835. },
  1836. },
  1837. Spec: apps.DaemonSetSpec{
  1838. Selector: &metav1.LabelSelector{MatchLabels: validSelector},
  1839. Template: validPodTemplate.Template,
  1840. },
  1841. },
  1842. "invalid_label 2": {
  1843. ObjectMeta: metav1.ObjectMeta{
  1844. Name: "abc-123",
  1845. Namespace: metav1.NamespaceDefault,
  1846. Labels: map[string]string{
  1847. "NoUppercaseOrSpecialCharsLike=Equals": "bar",
  1848. },
  1849. },
  1850. Spec: apps.DaemonSetSpec{
  1851. Template: invalidPodTemplate.Template,
  1852. },
  1853. },
  1854. "invalid_annotation": {
  1855. ObjectMeta: metav1.ObjectMeta{
  1856. Name: "abc-123",
  1857. Namespace: metav1.NamespaceDefault,
  1858. Annotations: map[string]string{
  1859. "NoUppercaseOrSpecialCharsLike=Equals": "bar",
  1860. },
  1861. },
  1862. Spec: apps.DaemonSetSpec{
  1863. Selector: &metav1.LabelSelector{MatchLabels: validSelector},
  1864. Template: validPodTemplate.Template,
  1865. },
  1866. },
  1867. "invalid restart policy 1": {
  1868. ObjectMeta: metav1.ObjectMeta{
  1869. Name: "abc-123",
  1870. Namespace: metav1.NamespaceDefault,
  1871. },
  1872. Spec: apps.DaemonSetSpec{
  1873. Selector: &metav1.LabelSelector{MatchLabels: validSelector},
  1874. Template: api.PodTemplateSpec{
  1875. Spec: api.PodSpec{
  1876. RestartPolicy: api.RestartPolicyOnFailure,
  1877. DNSPolicy: api.DNSClusterFirst,
  1878. Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}},
  1879. },
  1880. ObjectMeta: metav1.ObjectMeta{
  1881. Labels: validSelector,
  1882. },
  1883. },
  1884. },
  1885. },
  1886. "invalid restart policy 2": {
  1887. ObjectMeta: metav1.ObjectMeta{
  1888. Name: "abc-123",
  1889. Namespace: metav1.NamespaceDefault,
  1890. },
  1891. Spec: apps.DaemonSetSpec{
  1892. Selector: &metav1.LabelSelector{MatchLabels: validSelector},
  1893. Template: api.PodTemplateSpec{
  1894. Spec: api.PodSpec{
  1895. RestartPolicy: api.RestartPolicyNever,
  1896. DNSPolicy: api.DNSClusterFirst,
  1897. Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}},
  1898. },
  1899. ObjectMeta: metav1.ObjectMeta{
  1900. Labels: validSelector,
  1901. },
  1902. },
  1903. },
  1904. },
  1905. }
  1906. for k, v := range errorCases {
  1907. errs := ValidateDaemonSet(&v)
  1908. if len(errs) == 0 {
  1909. t.Errorf("expected failure for %s", k)
  1910. }
  1911. for i := range errs {
  1912. field := errs[i].Field
  1913. if !strings.HasPrefix(field, "spec.template.") &&
  1914. !strings.HasPrefix(field, "spec.updateStrategy") &&
  1915. field != "metadata.name" &&
  1916. field != "metadata.namespace" &&
  1917. field != "spec.selector" &&
  1918. field != "spec.template" &&
  1919. field != "GCEPersistentDisk.ReadOnly" &&
  1920. field != "spec.template.labels" &&
  1921. field != "metadata.annotations" &&
  1922. field != "metadata.labels" {
  1923. t.Errorf("%s: missing prefix for: %v", k, errs[i])
  1924. }
  1925. }
  1926. }
  1927. }
  1928. func validDeployment() *apps.Deployment {
  1929. return &apps.Deployment{
  1930. ObjectMeta: metav1.ObjectMeta{
  1931. Name: "abc",
  1932. Namespace: metav1.NamespaceDefault,
  1933. },
  1934. Spec: apps.DeploymentSpec{
  1935. Selector: &metav1.LabelSelector{
  1936. MatchLabels: map[string]string{
  1937. "name": "abc",
  1938. },
  1939. },
  1940. Strategy: apps.DeploymentStrategy{
  1941. Type: apps.RollingUpdateDeploymentStrategyType,
  1942. RollingUpdate: &apps.RollingUpdateDeployment{
  1943. MaxSurge: intstr.FromInt(1),
  1944. MaxUnavailable: intstr.FromInt(1),
  1945. },
  1946. },
  1947. Template: api.PodTemplateSpec{
  1948. ObjectMeta: metav1.ObjectMeta{
  1949. Name: "abc",
  1950. Namespace: metav1.NamespaceDefault,
  1951. Labels: map[string]string{
  1952. "name": "abc",
  1953. },
  1954. },
  1955. Spec: api.PodSpec{
  1956. RestartPolicy: api.RestartPolicyAlways,
  1957. DNSPolicy: api.DNSDefault,
  1958. Containers: []api.Container{
  1959. {
  1960. Name: "nginx",
  1961. Image: "image",
  1962. ImagePullPolicy: api.PullNever,
  1963. TerminationMessagePolicy: api.TerminationMessageReadFile,
  1964. },
  1965. },
  1966. },
  1967. },
  1968. RollbackTo: &apps.RollbackConfig{
  1969. Revision: 1,
  1970. },
  1971. },
  1972. }
  1973. }
  1974. func TestValidateDeployment(t *testing.T) {
  1975. successCases := []*apps.Deployment{
  1976. validDeployment(),
  1977. }
  1978. for _, successCase := range successCases {
  1979. if errs := ValidateDeployment(successCase); len(errs) != 0 {
  1980. t.Errorf("expected success: %v", errs)
  1981. }
  1982. }
  1983. errorCases := map[string]*apps.Deployment{}
  1984. errorCases["metadata.name: Required value"] = &apps.Deployment{
  1985. ObjectMeta: metav1.ObjectMeta{
  1986. Namespace: metav1.NamespaceDefault,
  1987. },
  1988. }
  1989. // selector should match the labels in pod template.
  1990. invalidSelectorDeployment := validDeployment()
  1991. invalidSelectorDeployment.Spec.Selector = &metav1.LabelSelector{
  1992. MatchLabels: map[string]string{
  1993. "name": "def",
  1994. },
  1995. }
  1996. errorCases["`selector` does not match template `labels`"] = invalidSelectorDeployment
  1997. // RestartPolicy should be always.
  1998. invalidRestartPolicyDeployment := validDeployment()
  1999. invalidRestartPolicyDeployment.Spec.Template.Spec.RestartPolicy = api.RestartPolicyNever
  2000. errorCases["Unsupported value: \"Never\""] = invalidRestartPolicyDeployment
  2001. // must have valid strategy type
  2002. invalidStrategyDeployment := validDeployment()
  2003. invalidStrategyDeployment.Spec.Strategy.Type = apps.DeploymentStrategyType("randomType")
  2004. errorCases[`supported values: "Recreate", "RollingUpdate"`] = invalidStrategyDeployment
  2005. // rollingUpdate should be nil for recreate.
  2006. invalidRecreateDeployment := validDeployment()
  2007. invalidRecreateDeployment.Spec.Strategy = apps.DeploymentStrategy{
  2008. Type: apps.RecreateDeploymentStrategyType,
  2009. RollingUpdate: &apps.RollingUpdateDeployment{},
  2010. }
  2011. errorCases["may not be specified when strategy `type` is 'Recreate'"] = invalidRecreateDeployment
  2012. // MaxSurge should be in the form of 20%.
  2013. invalidMaxSurgeDeployment := validDeployment()
  2014. invalidMaxSurgeDeployment.Spec.Strategy = apps.DeploymentStrategy{
  2015. Type: apps.RollingUpdateDeploymentStrategyType,
  2016. RollingUpdate: &apps.RollingUpdateDeployment{
  2017. MaxSurge: intstr.FromString("20Percent"),
  2018. },
  2019. }
  2020. errorCases["a valid percent string must be"] = invalidMaxSurgeDeployment
  2021. // MaxSurge and MaxUnavailable cannot both be zero.
  2022. invalidRollingUpdateDeployment := validDeployment()
  2023. invalidRollingUpdateDeployment.Spec.Strategy = apps.DeploymentStrategy{
  2024. Type: apps.RollingUpdateDeploymentStrategyType,
  2025. RollingUpdate: &apps.RollingUpdateDeployment{
  2026. MaxSurge: intstr.FromString("0%"),
  2027. MaxUnavailable: intstr.FromInt(0),
  2028. },
  2029. }
  2030. errorCases["may not be 0 when `maxSurge` is 0"] = invalidRollingUpdateDeployment
  2031. // MaxUnavailable should not be more than 100%.
  2032. invalidMaxUnavailableDeployment := validDeployment()
  2033. invalidMaxUnavailableDeployment.Spec.Strategy = apps.DeploymentStrategy{
  2034. Type: apps.RollingUpdateDeploymentStrategyType,
  2035. RollingUpdate: &apps.RollingUpdateDeployment{
  2036. MaxUnavailable: intstr.FromString("110%"),
  2037. },
  2038. }
  2039. errorCases["must not be greater than 100%"] = invalidMaxUnavailableDeployment
  2040. // Rollback.Revision must be non-negative
  2041. invalidRollbackRevisionDeployment := validDeployment()
  2042. invalidRollbackRevisionDeployment.Spec.RollbackTo.Revision = -3
  2043. errorCases["must be greater than or equal to 0"] = invalidRollbackRevisionDeployment
  2044. // ProgressDeadlineSeconds should be greater than MinReadySeconds
  2045. invalidProgressDeadlineDeployment := validDeployment()
  2046. seconds := int32(600)
  2047. invalidProgressDeadlineDeployment.Spec.ProgressDeadlineSeconds = &seconds
  2048. invalidProgressDeadlineDeployment.Spec.MinReadySeconds = seconds
  2049. errorCases["must be greater than minReadySeconds"] = invalidProgressDeadlineDeployment
  2050. for k, v := range errorCases {
  2051. errs := ValidateDeployment(v)
  2052. if len(errs) == 0 {
  2053. t.Errorf("[%s] expected failure", k)
  2054. } else if !strings.Contains(errs[0].Error(), k) {
  2055. t.Errorf("unexpected error: %q, expected: %q", errs[0].Error(), k)
  2056. }
  2057. }
  2058. }
  2059. func TestValidateDeploymentStatus(t *testing.T) {
  2060. collisionCount := int32(-3)
  2061. tests := []struct {
  2062. name string
  2063. replicas int32
  2064. updatedReplicas int32
  2065. readyReplicas int32
  2066. availableReplicas int32
  2067. observedGeneration int64
  2068. collisionCount *int32
  2069. expectedErr bool
  2070. }{
  2071. {
  2072. name: "valid status",
  2073. replicas: 3,
  2074. updatedReplicas: 3,
  2075. readyReplicas: 2,
  2076. availableReplicas: 1,
  2077. observedGeneration: 2,
  2078. expectedErr: false,
  2079. },
  2080. {
  2081. name: "invalid replicas",
  2082. replicas: -1,
  2083. updatedReplicas: 2,
  2084. readyReplicas: 2,
  2085. availableReplicas: 1,
  2086. observedGeneration: 2,
  2087. expectedErr: true,
  2088. },
  2089. {
  2090. name: "invalid updatedReplicas",
  2091. replicas: 2,
  2092. updatedReplicas: -1,
  2093. readyReplicas: 2,
  2094. availableReplicas: 1,
  2095. observedGeneration: 2,
  2096. expectedErr: true,
  2097. },
  2098. {
  2099. name: "invalid readyReplicas",
  2100. replicas: 3,
  2101. readyReplicas: -1,
  2102. availableReplicas: 1,
  2103. observedGeneration: 2,
  2104. expectedErr: true,
  2105. },
  2106. {
  2107. name: "invalid availableReplicas",
  2108. replicas: 3,
  2109. readyReplicas: 3,
  2110. availableReplicas: -1,
  2111. observedGeneration: 2,
  2112. expectedErr: true,
  2113. },
  2114. {
  2115. name: "invalid observedGeneration",
  2116. replicas: 3,
  2117. readyReplicas: 3,
  2118. availableReplicas: 3,
  2119. observedGeneration: -1,
  2120. expectedErr: true,
  2121. },
  2122. {
  2123. name: "updatedReplicas greater than replicas",
  2124. replicas: 3,
  2125. updatedReplicas: 4,
  2126. readyReplicas: 3,
  2127. availableReplicas: 3,
  2128. observedGeneration: 1,
  2129. expectedErr: true,
  2130. },
  2131. {
  2132. name: "readyReplicas greater than replicas",
  2133. replicas: 3,
  2134. readyReplicas: 4,
  2135. availableReplicas: 3,
  2136. observedGeneration: 1,
  2137. expectedErr: true,
  2138. },
  2139. {
  2140. name: "availableReplicas greater than replicas",
  2141. replicas: 3,
  2142. readyReplicas: 3,
  2143. availableReplicas: 4,
  2144. observedGeneration: 1,
  2145. expectedErr: true,
  2146. },
  2147. {
  2148. name: "availableReplicas greater than readyReplicas",
  2149. replicas: 3,
  2150. readyReplicas: 2,
  2151. availableReplicas: 3,
  2152. observedGeneration: 1,
  2153. expectedErr: true,
  2154. },
  2155. {
  2156. name: "invalid collisionCount",
  2157. replicas: 3,
  2158. observedGeneration: 1,
  2159. collisionCount: &collisionCount,
  2160. expectedErr: true,
  2161. },
  2162. }
  2163. for _, test := range tests {
  2164. status := apps.DeploymentStatus{
  2165. Replicas: test.replicas,
  2166. UpdatedReplicas: test.updatedReplicas,
  2167. ReadyReplicas: test.readyReplicas,
  2168. AvailableReplicas: test.availableReplicas,
  2169. ObservedGeneration: test.observedGeneration,
  2170. CollisionCount: test.collisionCount,
  2171. }
  2172. errs := ValidateDeploymentStatus(&status, field.NewPath("status"))
  2173. if hasErr := len(errs) > 0; hasErr != test.expectedErr {
  2174. errString := spew.Sprintf("%#v", errs)
  2175. t.Errorf("%s: expected error: %t, got error: %t\nerrors: %s", test.name, test.expectedErr, hasErr, errString)
  2176. }
  2177. }
  2178. }
  2179. func TestValidateDeploymentStatusUpdate(t *testing.T) {
  2180. collisionCount := int32(1)
  2181. otherCollisionCount := int32(2)
  2182. tests := []struct {
  2183. name string
  2184. from, to apps.DeploymentStatus
  2185. expectedErr bool
  2186. }{
  2187. {
  2188. name: "increase: valid update",
  2189. from: apps.DeploymentStatus{
  2190. CollisionCount: nil,
  2191. },
  2192. to: apps.DeploymentStatus{
  2193. CollisionCount: &collisionCount,
  2194. },
  2195. expectedErr: false,
  2196. },
  2197. {
  2198. name: "stable: valid update",
  2199. from: apps.DeploymentStatus{
  2200. CollisionCount: &collisionCount,
  2201. },
  2202. to: apps.DeploymentStatus{
  2203. CollisionCount: &collisionCount,
  2204. },
  2205. expectedErr: false,
  2206. },
  2207. {
  2208. name: "unset: invalid update",
  2209. from: apps.DeploymentStatus{
  2210. CollisionCount: &collisionCount,
  2211. },
  2212. to: apps.DeploymentStatus{
  2213. CollisionCount: nil,
  2214. },
  2215. expectedErr: true,
  2216. },
  2217. {
  2218. name: "decrease: invalid update",
  2219. from: apps.DeploymentStatus{
  2220. CollisionCount: &otherCollisionCount,
  2221. },
  2222. to: apps.DeploymentStatus{
  2223. CollisionCount: &collisionCount,
  2224. },
  2225. expectedErr: true,
  2226. },
  2227. }
  2228. for _, test := range tests {
  2229. meta := metav1.ObjectMeta{Name: "foo", Namespace: metav1.NamespaceDefault, ResourceVersion: "1"}
  2230. from := &apps.Deployment{
  2231. ObjectMeta: meta,
  2232. Status: test.from,
  2233. }
  2234. to := &apps.Deployment{
  2235. ObjectMeta: meta,
  2236. Status: test.to,
  2237. }
  2238. errs := ValidateDeploymentStatusUpdate(to, from)
  2239. if hasErr := len(errs) > 0; hasErr != test.expectedErr {
  2240. errString := spew.Sprintf("%#v", errs)
  2241. t.Errorf("%s: expected error: %t, got error: %t\nerrors: %s", test.name, test.expectedErr, hasErr, errString)
  2242. }
  2243. }
  2244. }
  2245. func validDeploymentRollback() *apps.DeploymentRollback {
  2246. return &apps.DeploymentRollback{
  2247. Name: "abc",
  2248. UpdatedAnnotations: map[string]string{
  2249. "created-by": "abc",
  2250. },
  2251. RollbackTo: apps.RollbackConfig{
  2252. Revision: 1,
  2253. },
  2254. }
  2255. }
  2256. func TestValidateDeploymentRollback(t *testing.T) {
  2257. noAnnotation := validDeploymentRollback()
  2258. noAnnotation.UpdatedAnnotations = nil
  2259. successCases := []*apps.DeploymentRollback{
  2260. validDeploymentRollback(),
  2261. noAnnotation,
  2262. }
  2263. for _, successCase := range successCases {
  2264. if errs := ValidateDeploymentRollback(successCase); len(errs) != 0 {
  2265. t.Errorf("expected success: %v", errs)
  2266. }
  2267. }
  2268. errorCases := map[string]*apps.DeploymentRollback{}
  2269. invalidNoName := validDeploymentRollback()
  2270. invalidNoName.Name = ""
  2271. errorCases["name: Required value"] = invalidNoName
  2272. for k, v := range errorCases {
  2273. errs := ValidateDeploymentRollback(v)
  2274. if len(errs) == 0 {
  2275. t.Errorf("[%s] expected failure", k)
  2276. } else if !strings.Contains(errs[0].Error(), k) {
  2277. t.Errorf("unexpected error: %q, expected: %q", errs[0].Error(), k)
  2278. }
  2279. }
  2280. }
  2281. func TestValidateReplicaSetStatus(t *testing.T) {
  2282. tests := []struct {
  2283. name string
  2284. replicas int32
  2285. fullyLabeledReplicas int32
  2286. readyReplicas int32
  2287. availableReplicas int32
  2288. observedGeneration int64
  2289. expectedErr bool
  2290. }{
  2291. {
  2292. name: "valid status",
  2293. replicas: 3,
  2294. fullyLabeledReplicas: 3,
  2295. readyReplicas: 2,
  2296. availableReplicas: 1,
  2297. observedGeneration: 2,
  2298. expectedErr: false,
  2299. },
  2300. {
  2301. name: "invalid replicas",
  2302. replicas: -1,
  2303. fullyLabeledReplicas: 3,
  2304. readyReplicas: 2,
  2305. availableReplicas: 1,
  2306. observedGeneration: 2,
  2307. expectedErr: true,
  2308. },
  2309. {
  2310. name: "invalid fullyLabeledReplicas",
  2311. replicas: 3,
  2312. fullyLabeledReplicas: -1,
  2313. readyReplicas: 2,
  2314. availableReplicas: 1,
  2315. observedGeneration: 2,
  2316. expectedErr: true,
  2317. },
  2318. {
  2319. name: "invalid readyReplicas",
  2320. replicas: 3,
  2321. fullyLabeledReplicas: 3,
  2322. readyReplicas: -1,
  2323. availableReplicas: 1,
  2324. observedGeneration: 2,
  2325. expectedErr: true,
  2326. },
  2327. {
  2328. name: "invalid availableReplicas",
  2329. replicas: 3,
  2330. fullyLabeledReplicas: 3,
  2331. readyReplicas: 3,
  2332. availableReplicas: -1,
  2333. observedGeneration: 2,
  2334. expectedErr: true,
  2335. },
  2336. {
  2337. name: "invalid observedGeneration",
  2338. replicas: 3,
  2339. fullyLabeledReplicas: 3,
  2340. readyReplicas: 3,
  2341. availableReplicas: 3,
  2342. observedGeneration: -1,
  2343. expectedErr: true,
  2344. },
  2345. {
  2346. name: "fullyLabeledReplicas greater than replicas",
  2347. replicas: 3,
  2348. fullyLabeledReplicas: 4,
  2349. readyReplicas: 3,
  2350. availableReplicas: 3,
  2351. observedGeneration: 1,
  2352. expectedErr: true,
  2353. },
  2354. {
  2355. name: "readyReplicas greater than replicas",
  2356. replicas: 3,
  2357. fullyLabeledReplicas: 3,
  2358. readyReplicas: 4,
  2359. availableReplicas: 3,
  2360. observedGeneration: 1,
  2361. expectedErr: true,
  2362. },
  2363. {
  2364. name: "availableReplicas greater than replicas",
  2365. replicas: 3,
  2366. fullyLabeledReplicas: 3,
  2367. readyReplicas: 3,
  2368. availableReplicas: 4,
  2369. observedGeneration: 1,
  2370. expectedErr: true,
  2371. },
  2372. {
  2373. name: "availableReplicas greater than readyReplicas",
  2374. replicas: 3,
  2375. fullyLabeledReplicas: 3,
  2376. readyReplicas: 2,
  2377. availableReplicas: 3,
  2378. observedGeneration: 1,
  2379. expectedErr: true,
  2380. },
  2381. }
  2382. for _, test := range tests {
  2383. status := apps.ReplicaSetStatus{
  2384. Replicas: test.replicas,
  2385. FullyLabeledReplicas: test.fullyLabeledReplicas,
  2386. ReadyReplicas: test.readyReplicas,
  2387. AvailableReplicas: test.availableReplicas,
  2388. ObservedGeneration: test.observedGeneration,
  2389. }
  2390. if hasErr := len(ValidateReplicaSetStatus(status, field.NewPath("status"))) > 0; hasErr != test.expectedErr {
  2391. t.Errorf("%s: expected error: %t, got error: %t", test.name, test.expectedErr, hasErr)
  2392. }
  2393. }
  2394. }
  2395. func TestValidateReplicaSetStatusUpdate(t *testing.T) {
  2396. validLabels := map[string]string{"a": "b"}
  2397. validPodTemplate := api.PodTemplate{
  2398. Template: api.PodTemplateSpec{
  2399. ObjectMeta: metav1.ObjectMeta{
  2400. Labels: validLabels,
  2401. },
  2402. Spec: api.PodSpec{
  2403. RestartPolicy: api.RestartPolicyAlways,
  2404. DNSPolicy: api.DNSClusterFirst,
  2405. Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}},
  2406. },
  2407. },
  2408. }
  2409. type rcUpdateTest struct {
  2410. old apps.ReplicaSet
  2411. update apps.ReplicaSet
  2412. }
  2413. successCases := []rcUpdateTest{
  2414. {
  2415. old: apps.ReplicaSet{
  2416. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  2417. Spec: apps.ReplicaSetSpec{
  2418. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  2419. Template: validPodTemplate.Template,
  2420. },
  2421. Status: apps.ReplicaSetStatus{
  2422. Replicas: 2,
  2423. },
  2424. },
  2425. update: apps.ReplicaSet{
  2426. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  2427. Spec: apps.ReplicaSetSpec{
  2428. Replicas: 3,
  2429. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  2430. Template: validPodTemplate.Template,
  2431. },
  2432. Status: apps.ReplicaSetStatus{
  2433. Replicas: 4,
  2434. },
  2435. },
  2436. },
  2437. }
  2438. for _, successCase := range successCases {
  2439. successCase.old.ObjectMeta.ResourceVersion = "1"
  2440. successCase.update.ObjectMeta.ResourceVersion = "1"
  2441. if errs := ValidateReplicaSetStatusUpdate(&successCase.update, &successCase.old); len(errs) != 0 {
  2442. t.Errorf("expected success: %v", errs)
  2443. }
  2444. }
  2445. errorCases := map[string]rcUpdateTest{
  2446. "negative replicas": {
  2447. old: apps.ReplicaSet{
  2448. ObjectMeta: metav1.ObjectMeta{Name: "", Namespace: metav1.NamespaceDefault},
  2449. Spec: apps.ReplicaSetSpec{
  2450. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  2451. Template: validPodTemplate.Template,
  2452. },
  2453. Status: apps.ReplicaSetStatus{
  2454. Replicas: 3,
  2455. },
  2456. },
  2457. update: apps.ReplicaSet{
  2458. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  2459. Spec: apps.ReplicaSetSpec{
  2460. Replicas: 2,
  2461. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  2462. Template: validPodTemplate.Template,
  2463. },
  2464. Status: apps.ReplicaSetStatus{
  2465. Replicas: -3,
  2466. },
  2467. },
  2468. },
  2469. }
  2470. for testName, errorCase := range errorCases {
  2471. if errs := ValidateReplicaSetStatusUpdate(&errorCase.update, &errorCase.old); len(errs) == 0 {
  2472. t.Errorf("expected failure: %s", testName)
  2473. }
  2474. }
  2475. }
  2476. func TestValidateReplicaSetUpdate(t *testing.T) {
  2477. validLabels := map[string]string{"a": "b"}
  2478. validPodTemplate := api.PodTemplate{
  2479. Template: api.PodTemplateSpec{
  2480. ObjectMeta: metav1.ObjectMeta{
  2481. Labels: validLabels,
  2482. },
  2483. Spec: api.PodSpec{
  2484. RestartPolicy: api.RestartPolicyAlways,
  2485. DNSPolicy: api.DNSClusterFirst,
  2486. Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}},
  2487. },
  2488. },
  2489. }
  2490. readWriteVolumePodTemplate := api.PodTemplate{
  2491. Template: api.PodTemplateSpec{
  2492. ObjectMeta: metav1.ObjectMeta{
  2493. Labels: validLabels,
  2494. },
  2495. Spec: api.PodSpec{
  2496. RestartPolicy: api.RestartPolicyAlways,
  2497. DNSPolicy: api.DNSClusterFirst,
  2498. Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}},
  2499. Volumes: []api.Volume{{Name: "gcepd", VolumeSource: api.VolumeSource{GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{PDName: "my-PD", FSType: "ext4", Partition: 1, ReadOnly: false}}}},
  2500. },
  2501. },
  2502. }
  2503. invalidLabels := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
  2504. invalidPodTemplate := api.PodTemplate{
  2505. Template: api.PodTemplateSpec{
  2506. Spec: api.PodSpec{
  2507. RestartPolicy: api.RestartPolicyAlways,
  2508. DNSPolicy: api.DNSClusterFirst,
  2509. },
  2510. ObjectMeta: metav1.ObjectMeta{
  2511. Labels: invalidLabels,
  2512. },
  2513. },
  2514. }
  2515. type rcUpdateTest struct {
  2516. old apps.ReplicaSet
  2517. update apps.ReplicaSet
  2518. }
  2519. successCases := []rcUpdateTest{
  2520. {
  2521. old: apps.ReplicaSet{
  2522. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  2523. Spec: apps.ReplicaSetSpec{
  2524. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  2525. Template: validPodTemplate.Template,
  2526. },
  2527. },
  2528. update: apps.ReplicaSet{
  2529. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  2530. Spec: apps.ReplicaSetSpec{
  2531. Replicas: 3,
  2532. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  2533. Template: validPodTemplate.Template,
  2534. },
  2535. },
  2536. },
  2537. {
  2538. old: apps.ReplicaSet{
  2539. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  2540. Spec: apps.ReplicaSetSpec{
  2541. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  2542. Template: validPodTemplate.Template,
  2543. },
  2544. },
  2545. update: apps.ReplicaSet{
  2546. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  2547. Spec: apps.ReplicaSetSpec{
  2548. Replicas: 1,
  2549. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  2550. Template: readWriteVolumePodTemplate.Template,
  2551. },
  2552. },
  2553. },
  2554. }
  2555. for _, successCase := range successCases {
  2556. successCase.old.ObjectMeta.ResourceVersion = "1"
  2557. successCase.update.ObjectMeta.ResourceVersion = "1"
  2558. if errs := ValidateReplicaSetUpdate(&successCase.update, &successCase.old); len(errs) != 0 {
  2559. t.Errorf("expected success: %v", errs)
  2560. }
  2561. }
  2562. errorCases := map[string]rcUpdateTest{
  2563. "more than one read/write": {
  2564. old: apps.ReplicaSet{
  2565. ObjectMeta: metav1.ObjectMeta{Name: "", Namespace: metav1.NamespaceDefault},
  2566. Spec: apps.ReplicaSetSpec{
  2567. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  2568. Template: validPodTemplate.Template,
  2569. },
  2570. },
  2571. update: apps.ReplicaSet{
  2572. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  2573. Spec: apps.ReplicaSetSpec{
  2574. Replicas: 2,
  2575. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  2576. Template: readWriteVolumePodTemplate.Template,
  2577. },
  2578. },
  2579. },
  2580. "invalid selector": {
  2581. old: apps.ReplicaSet{
  2582. ObjectMeta: metav1.ObjectMeta{Name: "", Namespace: metav1.NamespaceDefault},
  2583. Spec: apps.ReplicaSetSpec{
  2584. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  2585. Template: validPodTemplate.Template,
  2586. },
  2587. },
  2588. update: apps.ReplicaSet{
  2589. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  2590. Spec: apps.ReplicaSetSpec{
  2591. Replicas: 2,
  2592. Selector: &metav1.LabelSelector{MatchLabels: invalidLabels},
  2593. Template: validPodTemplate.Template,
  2594. },
  2595. },
  2596. },
  2597. "invalid pod": {
  2598. old: apps.ReplicaSet{
  2599. ObjectMeta: metav1.ObjectMeta{Name: "", Namespace: metav1.NamespaceDefault},
  2600. Spec: apps.ReplicaSetSpec{
  2601. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  2602. Template: validPodTemplate.Template,
  2603. },
  2604. },
  2605. update: apps.ReplicaSet{
  2606. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  2607. Spec: apps.ReplicaSetSpec{
  2608. Replicas: 2,
  2609. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  2610. Template: invalidPodTemplate.Template,
  2611. },
  2612. },
  2613. },
  2614. "negative replicas": {
  2615. old: apps.ReplicaSet{
  2616. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  2617. Spec: apps.ReplicaSetSpec{
  2618. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  2619. Template: validPodTemplate.Template,
  2620. },
  2621. },
  2622. update: apps.ReplicaSet{
  2623. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  2624. Spec: apps.ReplicaSetSpec{
  2625. Replicas: -1,
  2626. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  2627. Template: validPodTemplate.Template,
  2628. },
  2629. },
  2630. },
  2631. }
  2632. for testName, errorCase := range errorCases {
  2633. if errs := ValidateReplicaSetUpdate(&errorCase.update, &errorCase.old); len(errs) == 0 {
  2634. t.Errorf("expected failure: %s", testName)
  2635. }
  2636. }
  2637. }
  2638. func TestValidateReplicaSet(t *testing.T) {
  2639. validLabels := map[string]string{"a": "b"}
  2640. validPodTemplate := api.PodTemplate{
  2641. Template: api.PodTemplateSpec{
  2642. ObjectMeta: metav1.ObjectMeta{
  2643. Labels: validLabels,
  2644. },
  2645. Spec: api.PodSpec{
  2646. RestartPolicy: api.RestartPolicyAlways,
  2647. DNSPolicy: api.DNSClusterFirst,
  2648. Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}},
  2649. },
  2650. },
  2651. }
  2652. readWriteVolumePodTemplate := api.PodTemplate{
  2653. Template: api.PodTemplateSpec{
  2654. ObjectMeta: metav1.ObjectMeta{
  2655. Labels: validLabels,
  2656. },
  2657. Spec: api.PodSpec{
  2658. Volumes: []api.Volume{{Name: "gcepd", VolumeSource: api.VolumeSource{GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{PDName: "my-PD", FSType: "ext4", Partition: 1, ReadOnly: false}}}},
  2659. RestartPolicy: api.RestartPolicyAlways,
  2660. DNSPolicy: api.DNSClusterFirst,
  2661. Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}},
  2662. },
  2663. },
  2664. }
  2665. invalidLabels := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
  2666. invalidPodTemplate := api.PodTemplate{
  2667. Template: api.PodTemplateSpec{
  2668. Spec: api.PodSpec{
  2669. RestartPolicy: api.RestartPolicyAlways,
  2670. DNSPolicy: api.DNSClusterFirst,
  2671. },
  2672. ObjectMeta: metav1.ObjectMeta{
  2673. Labels: invalidLabels,
  2674. },
  2675. },
  2676. }
  2677. successCases := []apps.ReplicaSet{
  2678. {
  2679. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  2680. Spec: apps.ReplicaSetSpec{
  2681. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  2682. Template: validPodTemplate.Template,
  2683. },
  2684. },
  2685. {
  2686. ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault},
  2687. Spec: apps.ReplicaSetSpec{
  2688. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  2689. Template: validPodTemplate.Template,
  2690. },
  2691. },
  2692. {
  2693. ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault},
  2694. Spec: apps.ReplicaSetSpec{
  2695. Replicas: 1,
  2696. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  2697. Template: readWriteVolumePodTemplate.Template,
  2698. },
  2699. },
  2700. }
  2701. for _, successCase := range successCases {
  2702. if errs := ValidateReplicaSet(&successCase); len(errs) != 0 {
  2703. t.Errorf("expected success: %v", errs)
  2704. }
  2705. }
  2706. errorCases := map[string]apps.ReplicaSet{
  2707. "zero-length ID": {
  2708. ObjectMeta: metav1.ObjectMeta{Name: "", Namespace: metav1.NamespaceDefault},
  2709. Spec: apps.ReplicaSetSpec{
  2710. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  2711. Template: validPodTemplate.Template,
  2712. },
  2713. },
  2714. "missing-namespace": {
  2715. ObjectMeta: metav1.ObjectMeta{Name: "abc-123"},
  2716. Spec: apps.ReplicaSetSpec{
  2717. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  2718. Template: validPodTemplate.Template,
  2719. },
  2720. },
  2721. "empty selector": {
  2722. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  2723. Spec: apps.ReplicaSetSpec{
  2724. Template: validPodTemplate.Template,
  2725. },
  2726. },
  2727. "selector_doesnt_match": {
  2728. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  2729. Spec: apps.ReplicaSetSpec{
  2730. Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
  2731. Template: validPodTemplate.Template,
  2732. },
  2733. },
  2734. "invalid manifest": {
  2735. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  2736. Spec: apps.ReplicaSetSpec{
  2737. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  2738. },
  2739. },
  2740. "read-write persistent disk with > 1 pod": {
  2741. ObjectMeta: metav1.ObjectMeta{Name: "abc"},
  2742. Spec: apps.ReplicaSetSpec{
  2743. Replicas: 2,
  2744. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  2745. Template: readWriteVolumePodTemplate.Template,
  2746. },
  2747. },
  2748. "negative_replicas": {
  2749. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  2750. Spec: apps.ReplicaSetSpec{
  2751. Replicas: -1,
  2752. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  2753. },
  2754. },
  2755. "invalid_label": {
  2756. ObjectMeta: metav1.ObjectMeta{
  2757. Name: "abc-123",
  2758. Namespace: metav1.NamespaceDefault,
  2759. Labels: map[string]string{
  2760. "NoUppercaseOrSpecialCharsLike=Equals": "bar",
  2761. },
  2762. },
  2763. Spec: apps.ReplicaSetSpec{
  2764. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  2765. Template: validPodTemplate.Template,
  2766. },
  2767. },
  2768. "invalid_label 2": {
  2769. ObjectMeta: metav1.ObjectMeta{
  2770. Name: "abc-123",
  2771. Namespace: metav1.NamespaceDefault,
  2772. Labels: map[string]string{
  2773. "NoUppercaseOrSpecialCharsLike=Equals": "bar",
  2774. },
  2775. },
  2776. Spec: apps.ReplicaSetSpec{
  2777. Template: invalidPodTemplate.Template,
  2778. },
  2779. },
  2780. "invalid_annotation": {
  2781. ObjectMeta: metav1.ObjectMeta{
  2782. Name: "abc-123",
  2783. Namespace: metav1.NamespaceDefault,
  2784. Annotations: map[string]string{
  2785. "NoUppercaseOrSpecialCharsLike=Equals": "bar",
  2786. },
  2787. },
  2788. Spec: apps.ReplicaSetSpec{
  2789. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  2790. Template: validPodTemplate.Template,
  2791. },
  2792. },
  2793. "invalid restart policy 1": {
  2794. ObjectMeta: metav1.ObjectMeta{
  2795. Name: "abc-123",
  2796. Namespace: metav1.NamespaceDefault,
  2797. },
  2798. Spec: apps.ReplicaSetSpec{
  2799. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  2800. Template: api.PodTemplateSpec{
  2801. Spec: api.PodSpec{
  2802. RestartPolicy: api.RestartPolicyOnFailure,
  2803. DNSPolicy: api.DNSClusterFirst,
  2804. Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}},
  2805. },
  2806. ObjectMeta: metav1.ObjectMeta{
  2807. Labels: validLabels,
  2808. },
  2809. },
  2810. },
  2811. },
  2812. "invalid restart policy 2": {
  2813. ObjectMeta: metav1.ObjectMeta{
  2814. Name: "abc-123",
  2815. Namespace: metav1.NamespaceDefault,
  2816. },
  2817. Spec: apps.ReplicaSetSpec{
  2818. Selector: &metav1.LabelSelector{MatchLabels: validLabels},
  2819. Template: api.PodTemplateSpec{
  2820. Spec: api.PodSpec{
  2821. RestartPolicy: api.RestartPolicyNever,
  2822. DNSPolicy: api.DNSClusterFirst,
  2823. Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}},
  2824. },
  2825. ObjectMeta: metav1.ObjectMeta{
  2826. Labels: validLabels,
  2827. },
  2828. },
  2829. },
  2830. },
  2831. }
  2832. for k, v := range errorCases {
  2833. errs := ValidateReplicaSet(&v)
  2834. if len(errs) == 0 {
  2835. t.Errorf("expected failure for %s", k)
  2836. }
  2837. for i := range errs {
  2838. field := errs[i].Field
  2839. if !strings.HasPrefix(field, "spec.template.") &&
  2840. field != "metadata.name" &&
  2841. field != "metadata.namespace" &&
  2842. field != "spec.selector" &&
  2843. field != "spec.template" &&
  2844. field != "GCEPersistentDisk.ReadOnly" &&
  2845. field != "spec.replicas" &&
  2846. field != "spec.template.labels" &&
  2847. field != "metadata.annotations" &&
  2848. field != "metadata.labels" &&
  2849. field != "status.replicas" {
  2850. t.Errorf("%s: missing prefix for: %v", k, errs[i])
  2851. }
  2852. }
  2853. }
  2854. }