validation_test.go 96 KB

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