validation_test.go 65 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182
  1. /*
  2. Copyright 2014 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package validation
  14. import (
  15. "fmt"
  16. "strings"
  17. "testing"
  18. "k8s.io/kubernetes/pkg/api"
  19. "k8s.io/kubernetes/pkg/api/unversioned"
  20. "k8s.io/kubernetes/pkg/apis/extensions"
  21. "k8s.io/kubernetes/pkg/security/apparmor"
  22. psputil "k8s.io/kubernetes/pkg/security/podsecuritypolicy/util"
  23. "k8s.io/kubernetes/pkg/util/intstr"
  24. "k8s.io/kubernetes/pkg/util/validation/field"
  25. )
  26. func TestValidateDaemonSetStatusUpdate(t *testing.T) {
  27. type dsUpdateTest struct {
  28. old extensions.DaemonSet
  29. update extensions.DaemonSet
  30. }
  31. successCases := []dsUpdateTest{
  32. {
  33. old: extensions.DaemonSet{
  34. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  35. Status: extensions.DaemonSetStatus{
  36. CurrentNumberScheduled: 1,
  37. NumberMisscheduled: 2,
  38. DesiredNumberScheduled: 3,
  39. },
  40. },
  41. update: extensions.DaemonSet{
  42. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  43. Status: extensions.DaemonSetStatus{
  44. CurrentNumberScheduled: 1,
  45. NumberMisscheduled: 1,
  46. DesiredNumberScheduled: 3,
  47. },
  48. },
  49. },
  50. }
  51. for _, successCase := range successCases {
  52. successCase.old.ObjectMeta.ResourceVersion = "1"
  53. successCase.update.ObjectMeta.ResourceVersion = "1"
  54. if errs := ValidateDaemonSetStatusUpdate(&successCase.update, &successCase.old); len(errs) != 0 {
  55. t.Errorf("expected success: %v", errs)
  56. }
  57. }
  58. errorCases := map[string]dsUpdateTest{
  59. "negative values": {
  60. old: extensions.DaemonSet{
  61. ObjectMeta: api.ObjectMeta{
  62. Name: "abc",
  63. Namespace: api.NamespaceDefault,
  64. ResourceVersion: "10",
  65. },
  66. Status: extensions.DaemonSetStatus{
  67. CurrentNumberScheduled: 1,
  68. NumberMisscheduled: 2,
  69. DesiredNumberScheduled: 3,
  70. },
  71. },
  72. update: extensions.DaemonSet{
  73. ObjectMeta: api.ObjectMeta{
  74. Name: "abc",
  75. Namespace: api.NamespaceDefault,
  76. ResourceVersion: "10",
  77. },
  78. Status: extensions.DaemonSetStatus{
  79. CurrentNumberScheduled: -1,
  80. NumberMisscheduled: -1,
  81. DesiredNumberScheduled: -3,
  82. },
  83. },
  84. },
  85. }
  86. for testName, errorCase := range errorCases {
  87. if errs := ValidateDaemonSetStatusUpdate(&errorCase.update, &errorCase.old); len(errs) == 0 {
  88. t.Errorf("expected failure: %s", testName)
  89. }
  90. }
  91. }
  92. func TestValidateDaemonSetUpdate(t *testing.T) {
  93. validSelector := map[string]string{"a": "b"}
  94. validSelector2 := map[string]string{"c": "d"}
  95. invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
  96. validPodSpecAbc := api.PodSpec{
  97. RestartPolicy: api.RestartPolicyAlways,
  98. DNSPolicy: api.DNSClusterFirst,
  99. Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
  100. }
  101. validPodSpecDef := api.PodSpec{
  102. RestartPolicy: api.RestartPolicyAlways,
  103. DNSPolicy: api.DNSClusterFirst,
  104. Containers: []api.Container{{Name: "def", Image: "image", ImagePullPolicy: "IfNotPresent"}},
  105. }
  106. validPodSpecNodeSelector := api.PodSpec{
  107. NodeSelector: validSelector,
  108. NodeName: "xyz",
  109. RestartPolicy: api.RestartPolicyAlways,
  110. DNSPolicy: api.DNSClusterFirst,
  111. Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
  112. }
  113. validPodSpecVolume := api.PodSpec{
  114. Volumes: []api.Volume{{Name: "gcepd", VolumeSource: api.VolumeSource{GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{PDName: "my-PD", FSType: "ext4", Partition: 1, ReadOnly: false}}}},
  115. RestartPolicy: api.RestartPolicyAlways,
  116. DNSPolicy: api.DNSClusterFirst,
  117. Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
  118. }
  119. validPodTemplateAbc := api.PodTemplate{
  120. Template: api.PodTemplateSpec{
  121. ObjectMeta: api.ObjectMeta{
  122. Labels: validSelector,
  123. },
  124. Spec: validPodSpecAbc,
  125. },
  126. }
  127. validPodTemplateNodeSelector := api.PodTemplate{
  128. Template: api.PodTemplateSpec{
  129. ObjectMeta: api.ObjectMeta{
  130. Labels: validSelector,
  131. },
  132. Spec: validPodSpecNodeSelector,
  133. },
  134. }
  135. validPodTemplateAbc2 := api.PodTemplate{
  136. Template: api.PodTemplateSpec{
  137. ObjectMeta: api.ObjectMeta{
  138. Labels: validSelector2,
  139. },
  140. Spec: validPodSpecAbc,
  141. },
  142. }
  143. validPodTemplateDef := api.PodTemplate{
  144. Template: api.PodTemplateSpec{
  145. ObjectMeta: api.ObjectMeta{
  146. Labels: validSelector2,
  147. },
  148. Spec: validPodSpecDef,
  149. },
  150. }
  151. invalidPodTemplate := api.PodTemplate{
  152. Template: api.PodTemplateSpec{
  153. Spec: api.PodSpec{
  154. RestartPolicy: api.RestartPolicyAlways,
  155. DNSPolicy: api.DNSClusterFirst,
  156. },
  157. ObjectMeta: api.ObjectMeta{
  158. Labels: invalidSelector,
  159. },
  160. },
  161. }
  162. readWriteVolumePodTemplate := api.PodTemplate{
  163. Template: api.PodTemplateSpec{
  164. ObjectMeta: api.ObjectMeta{
  165. Labels: validSelector,
  166. },
  167. Spec: validPodSpecVolume,
  168. },
  169. }
  170. type dsUpdateTest struct {
  171. old extensions.DaemonSet
  172. update extensions.DaemonSet
  173. }
  174. successCases := []dsUpdateTest{
  175. {
  176. old: extensions.DaemonSet{
  177. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  178. Spec: extensions.DaemonSetSpec{
  179. Selector: &unversioned.LabelSelector{MatchLabels: validSelector},
  180. Template: validPodTemplateAbc.Template,
  181. },
  182. },
  183. update: extensions.DaemonSet{
  184. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  185. Spec: extensions.DaemonSetSpec{
  186. Selector: &unversioned.LabelSelector{MatchLabels: validSelector},
  187. Template: validPodTemplateAbc.Template,
  188. },
  189. },
  190. },
  191. {
  192. old: extensions.DaemonSet{
  193. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  194. Spec: extensions.DaemonSetSpec{
  195. Selector: &unversioned.LabelSelector{MatchLabels: validSelector},
  196. Template: validPodTemplateAbc.Template,
  197. },
  198. },
  199. update: extensions.DaemonSet{
  200. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  201. Spec: extensions.DaemonSetSpec{
  202. Selector: &unversioned.LabelSelector{MatchLabels: validSelector2},
  203. Template: validPodTemplateAbc2.Template,
  204. },
  205. },
  206. },
  207. {
  208. old: extensions.DaemonSet{
  209. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  210. Spec: extensions.DaemonSetSpec{
  211. Selector: &unversioned.LabelSelector{MatchLabels: validSelector},
  212. Template: validPodTemplateAbc.Template,
  213. },
  214. },
  215. update: extensions.DaemonSet{
  216. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  217. Spec: extensions.DaemonSetSpec{
  218. Selector: &unversioned.LabelSelector{MatchLabels: validSelector},
  219. Template: validPodTemplateNodeSelector.Template,
  220. },
  221. },
  222. },
  223. }
  224. for _, successCase := range successCases {
  225. successCase.old.ObjectMeta.ResourceVersion = "1"
  226. successCase.update.ObjectMeta.ResourceVersion = "1"
  227. if errs := ValidateDaemonSetUpdate(&successCase.update, &successCase.old); len(errs) != 0 {
  228. t.Errorf("expected success: %v", errs)
  229. }
  230. }
  231. errorCases := map[string]dsUpdateTest{
  232. "change daemon name": {
  233. old: extensions.DaemonSet{
  234. ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault},
  235. Spec: extensions.DaemonSetSpec{
  236. Selector: &unversioned.LabelSelector{MatchLabels: validSelector},
  237. Template: validPodTemplateAbc.Template,
  238. },
  239. },
  240. update: extensions.DaemonSet{
  241. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  242. Spec: extensions.DaemonSetSpec{
  243. Selector: &unversioned.LabelSelector{MatchLabels: validSelector},
  244. Template: validPodTemplateAbc.Template,
  245. },
  246. },
  247. },
  248. "invalid selector": {
  249. old: extensions.DaemonSet{
  250. ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault},
  251. Spec: extensions.DaemonSetSpec{
  252. Selector: &unversioned.LabelSelector{MatchLabels: validSelector},
  253. Template: validPodTemplateAbc.Template,
  254. },
  255. },
  256. update: extensions.DaemonSet{
  257. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  258. Spec: extensions.DaemonSetSpec{
  259. Selector: &unversioned.LabelSelector{MatchLabels: invalidSelector},
  260. Template: validPodTemplateAbc.Template,
  261. },
  262. },
  263. },
  264. "invalid pod": {
  265. old: extensions.DaemonSet{
  266. ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault},
  267. Spec: extensions.DaemonSetSpec{
  268. Selector: &unversioned.LabelSelector{MatchLabels: validSelector},
  269. Template: validPodTemplateAbc.Template,
  270. },
  271. },
  272. update: extensions.DaemonSet{
  273. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  274. Spec: extensions.DaemonSetSpec{
  275. Selector: &unversioned.LabelSelector{MatchLabels: validSelector},
  276. Template: invalidPodTemplate.Template,
  277. },
  278. },
  279. },
  280. "change container image": {
  281. old: extensions.DaemonSet{
  282. ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault},
  283. Spec: extensions.DaemonSetSpec{
  284. Selector: &unversioned.LabelSelector{MatchLabels: validSelector},
  285. Template: validPodTemplateAbc.Template,
  286. },
  287. },
  288. update: extensions.DaemonSet{
  289. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  290. Spec: extensions.DaemonSetSpec{
  291. Selector: &unversioned.LabelSelector{MatchLabels: validSelector},
  292. Template: validPodTemplateDef.Template,
  293. },
  294. },
  295. },
  296. "read-write volume": {
  297. old: extensions.DaemonSet{
  298. ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault},
  299. Spec: extensions.DaemonSetSpec{
  300. Selector: &unversioned.LabelSelector{MatchLabels: validSelector},
  301. Template: validPodTemplateAbc.Template,
  302. },
  303. },
  304. update: extensions.DaemonSet{
  305. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  306. Spec: extensions.DaemonSetSpec{
  307. Selector: &unversioned.LabelSelector{MatchLabels: validSelector},
  308. Template: readWriteVolumePodTemplate.Template,
  309. },
  310. },
  311. },
  312. "invalid update strategy": {
  313. old: extensions.DaemonSet{
  314. ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault},
  315. Spec: extensions.DaemonSetSpec{
  316. Selector: &unversioned.LabelSelector{MatchLabels: validSelector},
  317. Template: validPodTemplateAbc.Template,
  318. },
  319. },
  320. update: extensions.DaemonSet{
  321. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  322. Spec: extensions.DaemonSetSpec{
  323. Selector: &unversioned.LabelSelector{MatchLabels: invalidSelector},
  324. Template: validPodTemplateAbc.Template,
  325. },
  326. },
  327. },
  328. }
  329. for testName, errorCase := range errorCases {
  330. if errs := ValidateDaemonSetUpdate(&errorCase.update, &errorCase.old); len(errs) == 0 {
  331. t.Errorf("expected failure: %s", testName)
  332. }
  333. }
  334. }
  335. func TestValidateDaemonSet(t *testing.T) {
  336. validSelector := map[string]string{"a": "b"}
  337. validPodTemplate := api.PodTemplate{
  338. Template: api.PodTemplateSpec{
  339. ObjectMeta: api.ObjectMeta{
  340. Labels: validSelector,
  341. },
  342. Spec: api.PodSpec{
  343. RestartPolicy: api.RestartPolicyAlways,
  344. DNSPolicy: api.DNSClusterFirst,
  345. Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
  346. },
  347. },
  348. }
  349. invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
  350. invalidPodTemplate := api.PodTemplate{
  351. Template: api.PodTemplateSpec{
  352. Spec: api.PodSpec{
  353. RestartPolicy: api.RestartPolicyAlways,
  354. DNSPolicy: api.DNSClusterFirst,
  355. },
  356. ObjectMeta: api.ObjectMeta{
  357. Labels: invalidSelector,
  358. },
  359. },
  360. }
  361. successCases := []extensions.DaemonSet{
  362. {
  363. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  364. Spec: extensions.DaemonSetSpec{
  365. Selector: &unversioned.LabelSelector{MatchLabels: validSelector},
  366. Template: validPodTemplate.Template,
  367. },
  368. },
  369. {
  370. ObjectMeta: api.ObjectMeta{Name: "abc-123", Namespace: api.NamespaceDefault},
  371. Spec: extensions.DaemonSetSpec{
  372. Selector: &unversioned.LabelSelector{MatchLabels: validSelector},
  373. Template: validPodTemplate.Template,
  374. },
  375. },
  376. }
  377. for _, successCase := range successCases {
  378. if errs := ValidateDaemonSet(&successCase); len(errs) != 0 {
  379. t.Errorf("expected success: %v", errs)
  380. }
  381. }
  382. errorCases := map[string]extensions.DaemonSet{
  383. "zero-length ID": {
  384. ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault},
  385. Spec: extensions.DaemonSetSpec{
  386. Selector: &unversioned.LabelSelector{MatchLabels: validSelector},
  387. Template: validPodTemplate.Template,
  388. },
  389. },
  390. "missing-namespace": {
  391. ObjectMeta: api.ObjectMeta{Name: "abc-123"},
  392. Spec: extensions.DaemonSetSpec{
  393. Selector: &unversioned.LabelSelector{MatchLabels: validSelector},
  394. Template: validPodTemplate.Template,
  395. },
  396. },
  397. "nil selector": {
  398. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  399. Spec: extensions.DaemonSetSpec{
  400. Template: validPodTemplate.Template,
  401. },
  402. },
  403. "empty selector": {
  404. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  405. Spec: extensions.DaemonSetSpec{
  406. Selector: &unversioned.LabelSelector{},
  407. Template: validPodTemplate.Template,
  408. },
  409. },
  410. "selector_doesnt_match": {
  411. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  412. Spec: extensions.DaemonSetSpec{
  413. Selector: &unversioned.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
  414. Template: validPodTemplate.Template,
  415. },
  416. },
  417. "invalid template": {
  418. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  419. Spec: extensions.DaemonSetSpec{
  420. Selector: &unversioned.LabelSelector{MatchLabels: validSelector},
  421. },
  422. },
  423. "invalid_label": {
  424. ObjectMeta: api.ObjectMeta{
  425. Name: "abc-123",
  426. Namespace: api.NamespaceDefault,
  427. Labels: map[string]string{
  428. "NoUppercaseOrSpecialCharsLike=Equals": "bar",
  429. },
  430. },
  431. Spec: extensions.DaemonSetSpec{
  432. Selector: &unversioned.LabelSelector{MatchLabels: validSelector},
  433. Template: validPodTemplate.Template,
  434. },
  435. },
  436. "invalid_label 2": {
  437. ObjectMeta: api.ObjectMeta{
  438. Name: "abc-123",
  439. Namespace: api.NamespaceDefault,
  440. Labels: map[string]string{
  441. "NoUppercaseOrSpecialCharsLike=Equals": "bar",
  442. },
  443. },
  444. Spec: extensions.DaemonSetSpec{
  445. Template: invalidPodTemplate.Template,
  446. },
  447. },
  448. "invalid_annotation": {
  449. ObjectMeta: api.ObjectMeta{
  450. Name: "abc-123",
  451. Namespace: api.NamespaceDefault,
  452. Annotations: map[string]string{
  453. "NoUppercaseOrSpecialCharsLike=Equals": "bar",
  454. },
  455. },
  456. Spec: extensions.DaemonSetSpec{
  457. Selector: &unversioned.LabelSelector{MatchLabels: validSelector},
  458. Template: validPodTemplate.Template,
  459. },
  460. },
  461. "invalid restart policy 1": {
  462. ObjectMeta: api.ObjectMeta{
  463. Name: "abc-123",
  464. Namespace: api.NamespaceDefault,
  465. },
  466. Spec: extensions.DaemonSetSpec{
  467. Selector: &unversioned.LabelSelector{MatchLabels: validSelector},
  468. Template: api.PodTemplateSpec{
  469. Spec: api.PodSpec{
  470. RestartPolicy: api.RestartPolicyOnFailure,
  471. DNSPolicy: api.DNSClusterFirst,
  472. Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
  473. },
  474. ObjectMeta: api.ObjectMeta{
  475. Labels: validSelector,
  476. },
  477. },
  478. },
  479. },
  480. "invalid restart policy 2": {
  481. ObjectMeta: api.ObjectMeta{
  482. Name: "abc-123",
  483. Namespace: api.NamespaceDefault,
  484. },
  485. Spec: extensions.DaemonSetSpec{
  486. Selector: &unversioned.LabelSelector{MatchLabels: validSelector},
  487. Template: api.PodTemplateSpec{
  488. Spec: api.PodSpec{
  489. RestartPolicy: api.RestartPolicyNever,
  490. DNSPolicy: api.DNSClusterFirst,
  491. Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
  492. },
  493. ObjectMeta: api.ObjectMeta{
  494. Labels: validSelector,
  495. },
  496. },
  497. },
  498. },
  499. }
  500. for k, v := range errorCases {
  501. errs := ValidateDaemonSet(&v)
  502. if len(errs) == 0 {
  503. t.Errorf("expected failure for %s", k)
  504. }
  505. for i := range errs {
  506. field := errs[i].Field
  507. if !strings.HasPrefix(field, "spec.template.") &&
  508. !strings.HasPrefix(field, "spec.updateStrategy") &&
  509. field != "metadata.name" &&
  510. field != "metadata.namespace" &&
  511. field != "spec.selector" &&
  512. field != "spec.template" &&
  513. field != "GCEPersistentDisk.ReadOnly" &&
  514. field != "spec.template.labels" &&
  515. field != "metadata.annotations" &&
  516. field != "metadata.labels" {
  517. t.Errorf("%s: missing prefix for: %v", k, errs[i])
  518. }
  519. }
  520. }
  521. }
  522. func validDeployment() *extensions.Deployment {
  523. return &extensions.Deployment{
  524. ObjectMeta: api.ObjectMeta{
  525. Name: "abc",
  526. Namespace: api.NamespaceDefault,
  527. },
  528. Spec: extensions.DeploymentSpec{
  529. Selector: &unversioned.LabelSelector{
  530. MatchLabels: map[string]string{
  531. "name": "abc",
  532. },
  533. },
  534. Strategy: extensions.DeploymentStrategy{
  535. Type: extensions.RollingUpdateDeploymentStrategyType,
  536. RollingUpdate: &extensions.RollingUpdateDeployment{
  537. MaxSurge: intstr.FromInt(1),
  538. MaxUnavailable: intstr.FromInt(1),
  539. },
  540. },
  541. Template: api.PodTemplateSpec{
  542. ObjectMeta: api.ObjectMeta{
  543. Name: "abc",
  544. Namespace: api.NamespaceDefault,
  545. Labels: map[string]string{
  546. "name": "abc",
  547. },
  548. },
  549. Spec: api.PodSpec{
  550. RestartPolicy: api.RestartPolicyAlways,
  551. DNSPolicy: api.DNSDefault,
  552. Containers: []api.Container{
  553. {
  554. Name: "nginx",
  555. Image: "image",
  556. ImagePullPolicy: api.PullNever,
  557. },
  558. },
  559. },
  560. },
  561. RollbackTo: &extensions.RollbackConfig{
  562. Revision: 1,
  563. },
  564. },
  565. }
  566. }
  567. func TestValidateDeployment(t *testing.T) {
  568. successCases := []*extensions.Deployment{
  569. validDeployment(),
  570. }
  571. for _, successCase := range successCases {
  572. if errs := ValidateDeployment(successCase); len(errs) != 0 {
  573. t.Errorf("expected success: %v", errs)
  574. }
  575. }
  576. errorCases := map[string]*extensions.Deployment{}
  577. errorCases["metadata.name: Required value"] = &extensions.Deployment{
  578. ObjectMeta: api.ObjectMeta{
  579. Namespace: api.NamespaceDefault,
  580. },
  581. }
  582. // selector should match the labels in pod template.
  583. invalidSelectorDeployment := validDeployment()
  584. invalidSelectorDeployment.Spec.Selector = &unversioned.LabelSelector{
  585. MatchLabels: map[string]string{
  586. "name": "def",
  587. },
  588. }
  589. errorCases["`selector` does not match template `labels`"] = invalidSelectorDeployment
  590. // RestartPolicy should be always.
  591. invalidRestartPolicyDeployment := validDeployment()
  592. invalidRestartPolicyDeployment.Spec.Template.Spec.RestartPolicy = api.RestartPolicyNever
  593. errorCases["Unsupported value: \"Never\""] = invalidRestartPolicyDeployment
  594. // must have valid strategy type
  595. invalidStrategyDeployment := validDeployment()
  596. invalidStrategyDeployment.Spec.Strategy.Type = extensions.DeploymentStrategyType("randomType")
  597. errorCases["supported values: Recreate, RollingUpdate"] = invalidStrategyDeployment
  598. // rollingUpdate should be nil for recreate.
  599. invalidRecreateDeployment := validDeployment()
  600. invalidRecreateDeployment.Spec.Strategy = extensions.DeploymentStrategy{
  601. Type: extensions.RecreateDeploymentStrategyType,
  602. RollingUpdate: &extensions.RollingUpdateDeployment{},
  603. }
  604. errorCases["may not be specified when strategy `type` is 'Recreate'"] = invalidRecreateDeployment
  605. // MaxSurge should be in the form of 20%.
  606. invalidMaxSurgeDeployment := validDeployment()
  607. invalidMaxSurgeDeployment.Spec.Strategy = extensions.DeploymentStrategy{
  608. Type: extensions.RollingUpdateDeploymentStrategyType,
  609. RollingUpdate: &extensions.RollingUpdateDeployment{
  610. MaxSurge: intstr.FromString("20Percent"),
  611. },
  612. }
  613. errorCases["must match the regex"] = invalidMaxSurgeDeployment
  614. // MaxSurge and MaxUnavailable cannot both be zero.
  615. invalidRollingUpdateDeployment := validDeployment()
  616. invalidRollingUpdateDeployment.Spec.Strategy = extensions.DeploymentStrategy{
  617. Type: extensions.RollingUpdateDeploymentStrategyType,
  618. RollingUpdate: &extensions.RollingUpdateDeployment{
  619. MaxSurge: intstr.FromString("0%"),
  620. MaxUnavailable: intstr.FromInt(0),
  621. },
  622. }
  623. errorCases["may not be 0 when `maxSurge` is 0"] = invalidRollingUpdateDeployment
  624. // MaxUnavailable should not be more than 100%.
  625. invalidMaxUnavailableDeployment := validDeployment()
  626. invalidMaxUnavailableDeployment.Spec.Strategy = extensions.DeploymentStrategy{
  627. Type: extensions.RollingUpdateDeploymentStrategyType,
  628. RollingUpdate: &extensions.RollingUpdateDeployment{
  629. MaxUnavailable: intstr.FromString("110%"),
  630. },
  631. }
  632. errorCases["must not be greater than 100%"] = invalidMaxUnavailableDeployment
  633. // Rollback.Revision must be non-negative
  634. invalidRollbackRevisionDeployment := validDeployment()
  635. invalidRollbackRevisionDeployment.Spec.RollbackTo.Revision = -3
  636. errorCases["must be greater than or equal to 0"] = invalidRollbackRevisionDeployment
  637. for k, v := range errorCases {
  638. errs := ValidateDeployment(v)
  639. if len(errs) == 0 {
  640. t.Errorf("[%s] expected failure", k)
  641. } else if !strings.Contains(errs[0].Error(), k) {
  642. t.Errorf("unexpected error: %q, expected: %q", errs[0].Error(), k)
  643. }
  644. }
  645. }
  646. func validDeploymentRollback() *extensions.DeploymentRollback {
  647. return &extensions.DeploymentRollback{
  648. Name: "abc",
  649. UpdatedAnnotations: map[string]string{
  650. "created-by": "abc",
  651. },
  652. RollbackTo: extensions.RollbackConfig{
  653. Revision: 1,
  654. },
  655. }
  656. }
  657. func TestValidateDeploymentRollback(t *testing.T) {
  658. noAnnotation := validDeploymentRollback()
  659. noAnnotation.UpdatedAnnotations = nil
  660. successCases := []*extensions.DeploymentRollback{
  661. validDeploymentRollback(),
  662. noAnnotation,
  663. }
  664. for _, successCase := range successCases {
  665. if errs := ValidateDeploymentRollback(successCase); len(errs) != 0 {
  666. t.Errorf("expected success: %v", errs)
  667. }
  668. }
  669. errorCases := map[string]*extensions.DeploymentRollback{}
  670. invalidNoName := validDeploymentRollback()
  671. invalidNoName.Name = ""
  672. errorCases["name: Required value"] = invalidNoName
  673. for k, v := range errorCases {
  674. errs := ValidateDeploymentRollback(v)
  675. if len(errs) == 0 {
  676. t.Errorf("[%s] expected failure", k)
  677. } else if !strings.Contains(errs[0].Error(), k) {
  678. t.Errorf("unexpected error: %q, expected: %q", errs[0].Error(), k)
  679. }
  680. }
  681. }
  682. type ingressRules map[string]string
  683. func TestValidateIngress(t *testing.T) {
  684. defaultBackend := extensions.IngressBackend{
  685. ServiceName: "default-backend",
  686. ServicePort: intstr.FromInt(80),
  687. }
  688. newValid := func() extensions.Ingress {
  689. return extensions.Ingress{
  690. ObjectMeta: api.ObjectMeta{
  691. Name: "foo",
  692. Namespace: api.NamespaceDefault,
  693. },
  694. Spec: extensions.IngressSpec{
  695. Backend: &extensions.IngressBackend{
  696. ServiceName: "default-backend",
  697. ServicePort: intstr.FromInt(80),
  698. },
  699. Rules: []extensions.IngressRule{
  700. {
  701. Host: "foo.bar.com",
  702. IngressRuleValue: extensions.IngressRuleValue{
  703. HTTP: &extensions.HTTPIngressRuleValue{
  704. Paths: []extensions.HTTPIngressPath{
  705. {
  706. Path: "/foo",
  707. Backend: defaultBackend,
  708. },
  709. },
  710. },
  711. },
  712. },
  713. },
  714. },
  715. Status: extensions.IngressStatus{
  716. LoadBalancer: api.LoadBalancerStatus{
  717. Ingress: []api.LoadBalancerIngress{
  718. {IP: "127.0.0.1"},
  719. },
  720. },
  721. },
  722. }
  723. }
  724. servicelessBackend := newValid()
  725. servicelessBackend.Spec.Backend.ServiceName = ""
  726. invalidNameBackend := newValid()
  727. invalidNameBackend.Spec.Backend.ServiceName = "defaultBackend"
  728. noPortBackend := newValid()
  729. noPortBackend.Spec.Backend = &extensions.IngressBackend{ServiceName: defaultBackend.ServiceName}
  730. noForwardSlashPath := newValid()
  731. noForwardSlashPath.Spec.Rules[0].IngressRuleValue.HTTP.Paths = []extensions.HTTPIngressPath{
  732. {
  733. Path: "invalid",
  734. Backend: defaultBackend,
  735. },
  736. }
  737. noPaths := newValid()
  738. noPaths.Spec.Rules[0].IngressRuleValue.HTTP.Paths = []extensions.HTTPIngressPath{}
  739. badHost := newValid()
  740. badHost.Spec.Rules[0].Host = "foobar:80"
  741. badRegexPath := newValid()
  742. badPathExpr := "/invalid["
  743. badRegexPath.Spec.Rules[0].IngressRuleValue.HTTP.Paths = []extensions.HTTPIngressPath{
  744. {
  745. Path: badPathExpr,
  746. Backend: defaultBackend,
  747. },
  748. }
  749. badPathErr := fmt.Sprintf("spec.rules[0].http.paths[0].path: Invalid value: '%v'", badPathExpr)
  750. hostIP := "127.0.0.1"
  751. badHostIP := newValid()
  752. badHostIP.Spec.Rules[0].Host = hostIP
  753. badHostIPErr := fmt.Sprintf("spec.rules[0].host: Invalid value: '%v'", hostIP)
  754. errorCases := map[string]extensions.Ingress{
  755. "spec.backend.serviceName: Required value": servicelessBackend,
  756. "spec.backend.serviceName: Invalid value": invalidNameBackend,
  757. "spec.backend.servicePort: Invalid value": noPortBackend,
  758. "spec.rules[0].host: Invalid value": badHost,
  759. "spec.rules[0].http.paths: Required value": noPaths,
  760. "spec.rules[0].http.paths[0].path: Invalid value": noForwardSlashPath,
  761. }
  762. errorCases[badPathErr] = badRegexPath
  763. errorCases[badHostIPErr] = badHostIP
  764. wildcardHost := "foo.*.bar.com"
  765. badWildcard := newValid()
  766. badWildcard.Spec.Rules[0].Host = wildcardHost
  767. badWildcardErr := fmt.Sprintf("spec.rules[0].host: Invalid value: '%v'", wildcardHost)
  768. errorCases[badWildcardErr] = badWildcard
  769. for k, v := range errorCases {
  770. errs := ValidateIngress(&v)
  771. if len(errs) == 0 {
  772. t.Errorf("expected failure for %q", k)
  773. } else {
  774. s := strings.Split(k, ":")
  775. err := errs[0]
  776. if err.Field != s[0] || !strings.Contains(err.Error(), s[1]) {
  777. t.Errorf("unexpected error: %q, expected: %q", err, k)
  778. }
  779. }
  780. }
  781. }
  782. func TestValidateIngressTLS(t *testing.T) {
  783. defaultBackend := extensions.IngressBackend{
  784. ServiceName: "default-backend",
  785. ServicePort: intstr.FromInt(80),
  786. }
  787. newValid := func() extensions.Ingress {
  788. return extensions.Ingress{
  789. ObjectMeta: api.ObjectMeta{
  790. Name: "foo",
  791. Namespace: api.NamespaceDefault,
  792. },
  793. Spec: extensions.IngressSpec{
  794. Backend: &extensions.IngressBackend{
  795. ServiceName: "default-backend",
  796. ServicePort: intstr.FromInt(80),
  797. },
  798. Rules: []extensions.IngressRule{
  799. {
  800. Host: "foo.bar.com",
  801. IngressRuleValue: extensions.IngressRuleValue{
  802. HTTP: &extensions.HTTPIngressRuleValue{
  803. Paths: []extensions.HTTPIngressPath{
  804. {
  805. Path: "/foo",
  806. Backend: defaultBackend,
  807. },
  808. },
  809. },
  810. },
  811. },
  812. },
  813. },
  814. Status: extensions.IngressStatus{
  815. LoadBalancer: api.LoadBalancerStatus{
  816. Ingress: []api.LoadBalancerIngress{
  817. {IP: "127.0.0.1"},
  818. },
  819. },
  820. },
  821. }
  822. }
  823. errorCases := map[string]extensions.Ingress{}
  824. wildcardHost := "foo.*.bar.com"
  825. badWildcardTLS := newValid()
  826. badWildcardTLS.Spec.Rules[0].Host = "*.foo.bar.com"
  827. badWildcardTLS.Spec.TLS = []extensions.IngressTLS{
  828. {
  829. Hosts: []string{wildcardHost},
  830. },
  831. }
  832. badWildcardTLSErr := fmt.Sprintf("spec.tls[0].hosts: Invalid value: '%v'", wildcardHost)
  833. errorCases[badWildcardTLSErr] = badWildcardTLS
  834. for k, v := range errorCases {
  835. errs := ValidateIngress(&v)
  836. if len(errs) == 0 {
  837. t.Errorf("expected failure for %q", k)
  838. } else {
  839. s := strings.Split(k, ":")
  840. err := errs[0]
  841. if err.Field != s[0] || !strings.Contains(err.Error(), s[1]) {
  842. t.Errorf("unexpected error: %q, expected: %q", err, k)
  843. }
  844. }
  845. }
  846. }
  847. func TestValidateIngressStatusUpdate(t *testing.T) {
  848. defaultBackend := extensions.IngressBackend{
  849. ServiceName: "default-backend",
  850. ServicePort: intstr.FromInt(80),
  851. }
  852. newValid := func() extensions.Ingress {
  853. return extensions.Ingress{
  854. ObjectMeta: api.ObjectMeta{
  855. Name: "foo",
  856. Namespace: api.NamespaceDefault,
  857. ResourceVersion: "9",
  858. },
  859. Spec: extensions.IngressSpec{
  860. Backend: &extensions.IngressBackend{
  861. ServiceName: "default-backend",
  862. ServicePort: intstr.FromInt(80),
  863. },
  864. Rules: []extensions.IngressRule{
  865. {
  866. Host: "foo.bar.com",
  867. IngressRuleValue: extensions.IngressRuleValue{
  868. HTTP: &extensions.HTTPIngressRuleValue{
  869. Paths: []extensions.HTTPIngressPath{
  870. {
  871. Path: "/foo",
  872. Backend: defaultBackend,
  873. },
  874. },
  875. },
  876. },
  877. },
  878. },
  879. },
  880. Status: extensions.IngressStatus{
  881. LoadBalancer: api.LoadBalancerStatus{
  882. Ingress: []api.LoadBalancerIngress{
  883. {IP: "127.0.0.1", Hostname: "foo.bar.com"},
  884. },
  885. },
  886. },
  887. }
  888. }
  889. oldValue := newValid()
  890. newValue := newValid()
  891. newValue.Status = extensions.IngressStatus{
  892. LoadBalancer: api.LoadBalancerStatus{
  893. Ingress: []api.LoadBalancerIngress{
  894. {IP: "127.0.0.2", Hostname: "foo.com"},
  895. },
  896. },
  897. }
  898. invalidIP := newValid()
  899. invalidIP.Status = extensions.IngressStatus{
  900. LoadBalancer: api.LoadBalancerStatus{
  901. Ingress: []api.LoadBalancerIngress{
  902. {IP: "abcd", Hostname: "foo.com"},
  903. },
  904. },
  905. }
  906. invalidHostname := newValid()
  907. invalidHostname.Status = extensions.IngressStatus{
  908. LoadBalancer: api.LoadBalancerStatus{
  909. Ingress: []api.LoadBalancerIngress{
  910. {IP: "127.0.0.1", Hostname: "127.0.0.1"},
  911. },
  912. },
  913. }
  914. errs := ValidateIngressStatusUpdate(&newValue, &oldValue)
  915. if len(errs) != 0 {
  916. t.Errorf("Unexpected error %v", errs)
  917. }
  918. errorCases := map[string]extensions.Ingress{
  919. "status.loadBalancer.ingress[0].ip: Invalid value": invalidIP,
  920. "status.loadBalancer.ingress[0].hostname: Invalid value": invalidHostname,
  921. }
  922. for k, v := range errorCases {
  923. errs := ValidateIngressStatusUpdate(&v, &oldValue)
  924. if len(errs) == 0 {
  925. t.Errorf("expected failure for %s", k)
  926. } else {
  927. s := strings.Split(k, ":")
  928. err := errs[0]
  929. if err.Field != s[0] || !strings.Contains(err.Error(), s[1]) {
  930. t.Errorf("unexpected error: %q, expected: %q", err, k)
  931. }
  932. }
  933. }
  934. }
  935. func TestValidateScale(t *testing.T) {
  936. successCases := []extensions.Scale{
  937. {
  938. ObjectMeta: api.ObjectMeta{
  939. Name: "frontend",
  940. Namespace: api.NamespaceDefault,
  941. },
  942. Spec: extensions.ScaleSpec{
  943. Replicas: 1,
  944. },
  945. },
  946. {
  947. ObjectMeta: api.ObjectMeta{
  948. Name: "frontend",
  949. Namespace: api.NamespaceDefault,
  950. },
  951. Spec: extensions.ScaleSpec{
  952. Replicas: 10,
  953. },
  954. },
  955. {
  956. ObjectMeta: api.ObjectMeta{
  957. Name: "frontend",
  958. Namespace: api.NamespaceDefault,
  959. },
  960. Spec: extensions.ScaleSpec{
  961. Replicas: 0,
  962. },
  963. },
  964. }
  965. for _, successCase := range successCases {
  966. if errs := ValidateScale(&successCase); len(errs) != 0 {
  967. t.Errorf("expected success: %v", errs)
  968. }
  969. }
  970. errorCases := []struct {
  971. scale extensions.Scale
  972. msg string
  973. }{
  974. {
  975. scale: extensions.Scale{
  976. ObjectMeta: api.ObjectMeta{
  977. Name: "frontend",
  978. Namespace: api.NamespaceDefault,
  979. },
  980. Spec: extensions.ScaleSpec{
  981. Replicas: -1,
  982. },
  983. },
  984. msg: "must be greater than or equal to 0",
  985. },
  986. }
  987. for _, c := range errorCases {
  988. if errs := ValidateScale(&c.scale); len(errs) == 0 {
  989. t.Errorf("expected failure for %s", c.msg)
  990. } else if !strings.Contains(errs[0].Error(), c.msg) {
  991. t.Errorf("unexpected error: %v, expected: %s", errs[0], c.msg)
  992. }
  993. }
  994. }
  995. func TestValidateReplicaSetStatusUpdate(t *testing.T) {
  996. validLabels := map[string]string{"a": "b"}
  997. validPodTemplate := api.PodTemplate{
  998. Template: api.PodTemplateSpec{
  999. ObjectMeta: api.ObjectMeta{
  1000. Labels: validLabels,
  1001. },
  1002. Spec: api.PodSpec{
  1003. RestartPolicy: api.RestartPolicyAlways,
  1004. DNSPolicy: api.DNSClusterFirst,
  1005. Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
  1006. },
  1007. },
  1008. }
  1009. type rcUpdateTest struct {
  1010. old extensions.ReplicaSet
  1011. update extensions.ReplicaSet
  1012. }
  1013. successCases := []rcUpdateTest{
  1014. {
  1015. old: extensions.ReplicaSet{
  1016. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  1017. Spec: extensions.ReplicaSetSpec{
  1018. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  1019. Template: validPodTemplate.Template,
  1020. },
  1021. Status: extensions.ReplicaSetStatus{
  1022. Replicas: 2,
  1023. },
  1024. },
  1025. update: extensions.ReplicaSet{
  1026. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  1027. Spec: extensions.ReplicaSetSpec{
  1028. Replicas: 3,
  1029. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  1030. Template: validPodTemplate.Template,
  1031. },
  1032. Status: extensions.ReplicaSetStatus{
  1033. Replicas: 4,
  1034. },
  1035. },
  1036. },
  1037. }
  1038. for _, successCase := range successCases {
  1039. successCase.old.ObjectMeta.ResourceVersion = "1"
  1040. successCase.update.ObjectMeta.ResourceVersion = "1"
  1041. if errs := ValidateReplicaSetStatusUpdate(&successCase.update, &successCase.old); len(errs) != 0 {
  1042. t.Errorf("expected success: %v", errs)
  1043. }
  1044. }
  1045. errorCases := map[string]rcUpdateTest{
  1046. "negative replicas": {
  1047. old: extensions.ReplicaSet{
  1048. ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault},
  1049. Spec: extensions.ReplicaSetSpec{
  1050. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  1051. Template: validPodTemplate.Template,
  1052. },
  1053. Status: extensions.ReplicaSetStatus{
  1054. Replicas: 3,
  1055. },
  1056. },
  1057. update: extensions.ReplicaSet{
  1058. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  1059. Spec: extensions.ReplicaSetSpec{
  1060. Replicas: 2,
  1061. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  1062. Template: validPodTemplate.Template,
  1063. },
  1064. Status: extensions.ReplicaSetStatus{
  1065. Replicas: -3,
  1066. },
  1067. },
  1068. },
  1069. }
  1070. for testName, errorCase := range errorCases {
  1071. if errs := ValidateReplicaSetStatusUpdate(&errorCase.update, &errorCase.old); len(errs) == 0 {
  1072. t.Errorf("expected failure: %s", testName)
  1073. }
  1074. }
  1075. }
  1076. func TestValidateReplicaSetUpdate(t *testing.T) {
  1077. validLabels := map[string]string{"a": "b"}
  1078. validPodTemplate := api.PodTemplate{
  1079. Template: api.PodTemplateSpec{
  1080. ObjectMeta: api.ObjectMeta{
  1081. Labels: validLabels,
  1082. },
  1083. Spec: api.PodSpec{
  1084. RestartPolicy: api.RestartPolicyAlways,
  1085. DNSPolicy: api.DNSClusterFirst,
  1086. Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
  1087. },
  1088. },
  1089. }
  1090. readWriteVolumePodTemplate := api.PodTemplate{
  1091. Template: api.PodTemplateSpec{
  1092. ObjectMeta: api.ObjectMeta{
  1093. Labels: validLabels,
  1094. },
  1095. Spec: api.PodSpec{
  1096. RestartPolicy: api.RestartPolicyAlways,
  1097. DNSPolicy: api.DNSClusterFirst,
  1098. Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
  1099. Volumes: []api.Volume{{Name: "gcepd", VolumeSource: api.VolumeSource{GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{PDName: "my-PD", FSType: "ext4", Partition: 1, ReadOnly: false}}}},
  1100. },
  1101. },
  1102. }
  1103. invalidLabels := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
  1104. invalidPodTemplate := api.PodTemplate{
  1105. Template: api.PodTemplateSpec{
  1106. Spec: api.PodSpec{
  1107. RestartPolicy: api.RestartPolicyAlways,
  1108. DNSPolicy: api.DNSClusterFirst,
  1109. },
  1110. ObjectMeta: api.ObjectMeta{
  1111. Labels: invalidLabels,
  1112. },
  1113. },
  1114. }
  1115. type rcUpdateTest struct {
  1116. old extensions.ReplicaSet
  1117. update extensions.ReplicaSet
  1118. }
  1119. successCases := []rcUpdateTest{
  1120. {
  1121. old: extensions.ReplicaSet{
  1122. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  1123. Spec: extensions.ReplicaSetSpec{
  1124. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  1125. Template: validPodTemplate.Template,
  1126. },
  1127. },
  1128. update: extensions.ReplicaSet{
  1129. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  1130. Spec: extensions.ReplicaSetSpec{
  1131. Replicas: 3,
  1132. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  1133. Template: validPodTemplate.Template,
  1134. },
  1135. },
  1136. },
  1137. {
  1138. old: extensions.ReplicaSet{
  1139. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  1140. Spec: extensions.ReplicaSetSpec{
  1141. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  1142. Template: validPodTemplate.Template,
  1143. },
  1144. },
  1145. update: extensions.ReplicaSet{
  1146. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  1147. Spec: extensions.ReplicaSetSpec{
  1148. Replicas: 1,
  1149. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  1150. Template: readWriteVolumePodTemplate.Template,
  1151. },
  1152. },
  1153. },
  1154. }
  1155. for _, successCase := range successCases {
  1156. successCase.old.ObjectMeta.ResourceVersion = "1"
  1157. successCase.update.ObjectMeta.ResourceVersion = "1"
  1158. if errs := ValidateReplicaSetUpdate(&successCase.update, &successCase.old); len(errs) != 0 {
  1159. t.Errorf("expected success: %v", errs)
  1160. }
  1161. }
  1162. errorCases := map[string]rcUpdateTest{
  1163. "more than one read/write": {
  1164. old: extensions.ReplicaSet{
  1165. ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault},
  1166. Spec: extensions.ReplicaSetSpec{
  1167. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  1168. Template: validPodTemplate.Template,
  1169. },
  1170. },
  1171. update: extensions.ReplicaSet{
  1172. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  1173. Spec: extensions.ReplicaSetSpec{
  1174. Replicas: 2,
  1175. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  1176. Template: readWriteVolumePodTemplate.Template,
  1177. },
  1178. },
  1179. },
  1180. "invalid selector": {
  1181. old: extensions.ReplicaSet{
  1182. ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault},
  1183. Spec: extensions.ReplicaSetSpec{
  1184. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  1185. Template: validPodTemplate.Template,
  1186. },
  1187. },
  1188. update: extensions.ReplicaSet{
  1189. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  1190. Spec: extensions.ReplicaSetSpec{
  1191. Replicas: 2,
  1192. Selector: &unversioned.LabelSelector{MatchLabels: invalidLabels},
  1193. Template: validPodTemplate.Template,
  1194. },
  1195. },
  1196. },
  1197. "invalid pod": {
  1198. old: extensions.ReplicaSet{
  1199. ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault},
  1200. Spec: extensions.ReplicaSetSpec{
  1201. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  1202. Template: validPodTemplate.Template,
  1203. },
  1204. },
  1205. update: extensions.ReplicaSet{
  1206. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  1207. Spec: extensions.ReplicaSetSpec{
  1208. Replicas: 2,
  1209. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  1210. Template: invalidPodTemplate.Template,
  1211. },
  1212. },
  1213. },
  1214. "negative replicas": {
  1215. old: extensions.ReplicaSet{
  1216. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  1217. Spec: extensions.ReplicaSetSpec{
  1218. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  1219. Template: validPodTemplate.Template,
  1220. },
  1221. },
  1222. update: extensions.ReplicaSet{
  1223. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  1224. Spec: extensions.ReplicaSetSpec{
  1225. Replicas: -1,
  1226. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  1227. Template: validPodTemplate.Template,
  1228. },
  1229. },
  1230. },
  1231. }
  1232. for testName, errorCase := range errorCases {
  1233. if errs := ValidateReplicaSetUpdate(&errorCase.update, &errorCase.old); len(errs) == 0 {
  1234. t.Errorf("expected failure: %s", testName)
  1235. }
  1236. }
  1237. }
  1238. func TestValidateReplicaSet(t *testing.T) {
  1239. validLabels := map[string]string{"a": "b"}
  1240. validPodTemplate := api.PodTemplate{
  1241. Template: api.PodTemplateSpec{
  1242. ObjectMeta: api.ObjectMeta{
  1243. Labels: validLabels,
  1244. },
  1245. Spec: api.PodSpec{
  1246. RestartPolicy: api.RestartPolicyAlways,
  1247. DNSPolicy: api.DNSClusterFirst,
  1248. Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
  1249. },
  1250. },
  1251. }
  1252. readWriteVolumePodTemplate := api.PodTemplate{
  1253. Template: api.PodTemplateSpec{
  1254. ObjectMeta: api.ObjectMeta{
  1255. Labels: validLabels,
  1256. },
  1257. Spec: api.PodSpec{
  1258. Volumes: []api.Volume{{Name: "gcepd", VolumeSource: api.VolumeSource{GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{PDName: "my-PD", FSType: "ext4", Partition: 1, ReadOnly: false}}}},
  1259. RestartPolicy: api.RestartPolicyAlways,
  1260. DNSPolicy: api.DNSClusterFirst,
  1261. Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
  1262. },
  1263. },
  1264. }
  1265. invalidLabels := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
  1266. invalidPodTemplate := api.PodTemplate{
  1267. Template: api.PodTemplateSpec{
  1268. Spec: api.PodSpec{
  1269. RestartPolicy: api.RestartPolicyAlways,
  1270. DNSPolicy: api.DNSClusterFirst,
  1271. },
  1272. ObjectMeta: api.ObjectMeta{
  1273. Labels: invalidLabels,
  1274. },
  1275. },
  1276. }
  1277. successCases := []extensions.ReplicaSet{
  1278. {
  1279. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  1280. Spec: extensions.ReplicaSetSpec{
  1281. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  1282. Template: validPodTemplate.Template,
  1283. },
  1284. },
  1285. {
  1286. ObjectMeta: api.ObjectMeta{Name: "abc-123", Namespace: api.NamespaceDefault},
  1287. Spec: extensions.ReplicaSetSpec{
  1288. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  1289. Template: validPodTemplate.Template,
  1290. },
  1291. },
  1292. {
  1293. ObjectMeta: api.ObjectMeta{Name: "abc-123", Namespace: api.NamespaceDefault},
  1294. Spec: extensions.ReplicaSetSpec{
  1295. Replicas: 1,
  1296. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  1297. Template: readWriteVolumePodTemplate.Template,
  1298. },
  1299. },
  1300. }
  1301. for _, successCase := range successCases {
  1302. if errs := ValidateReplicaSet(&successCase); len(errs) != 0 {
  1303. t.Errorf("expected success: %v", errs)
  1304. }
  1305. }
  1306. errorCases := map[string]extensions.ReplicaSet{
  1307. "zero-length ID": {
  1308. ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault},
  1309. Spec: extensions.ReplicaSetSpec{
  1310. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  1311. Template: validPodTemplate.Template,
  1312. },
  1313. },
  1314. "missing-namespace": {
  1315. ObjectMeta: api.ObjectMeta{Name: "abc-123"},
  1316. Spec: extensions.ReplicaSetSpec{
  1317. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  1318. Template: validPodTemplate.Template,
  1319. },
  1320. },
  1321. "empty selector": {
  1322. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  1323. Spec: extensions.ReplicaSetSpec{
  1324. Template: validPodTemplate.Template,
  1325. },
  1326. },
  1327. "selector_doesnt_match": {
  1328. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  1329. Spec: extensions.ReplicaSetSpec{
  1330. Selector: &unversioned.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
  1331. Template: validPodTemplate.Template,
  1332. },
  1333. },
  1334. "invalid manifest": {
  1335. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  1336. Spec: extensions.ReplicaSetSpec{
  1337. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  1338. },
  1339. },
  1340. "read-write persistent disk with > 1 pod": {
  1341. ObjectMeta: api.ObjectMeta{Name: "abc"},
  1342. Spec: extensions.ReplicaSetSpec{
  1343. Replicas: 2,
  1344. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  1345. Template: readWriteVolumePodTemplate.Template,
  1346. },
  1347. },
  1348. "negative_replicas": {
  1349. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  1350. Spec: extensions.ReplicaSetSpec{
  1351. Replicas: -1,
  1352. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  1353. },
  1354. },
  1355. "invalid_label": {
  1356. ObjectMeta: api.ObjectMeta{
  1357. Name: "abc-123",
  1358. Namespace: api.NamespaceDefault,
  1359. Labels: map[string]string{
  1360. "NoUppercaseOrSpecialCharsLike=Equals": "bar",
  1361. },
  1362. },
  1363. Spec: extensions.ReplicaSetSpec{
  1364. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  1365. Template: validPodTemplate.Template,
  1366. },
  1367. },
  1368. "invalid_label 2": {
  1369. ObjectMeta: api.ObjectMeta{
  1370. Name: "abc-123",
  1371. Namespace: api.NamespaceDefault,
  1372. Labels: map[string]string{
  1373. "NoUppercaseOrSpecialCharsLike=Equals": "bar",
  1374. },
  1375. },
  1376. Spec: extensions.ReplicaSetSpec{
  1377. Template: invalidPodTemplate.Template,
  1378. },
  1379. },
  1380. "invalid_annotation": {
  1381. ObjectMeta: api.ObjectMeta{
  1382. Name: "abc-123",
  1383. Namespace: api.NamespaceDefault,
  1384. Annotations: map[string]string{
  1385. "NoUppercaseOrSpecialCharsLike=Equals": "bar",
  1386. },
  1387. },
  1388. Spec: extensions.ReplicaSetSpec{
  1389. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  1390. Template: validPodTemplate.Template,
  1391. },
  1392. },
  1393. "invalid restart policy 1": {
  1394. ObjectMeta: api.ObjectMeta{
  1395. Name: "abc-123",
  1396. Namespace: api.NamespaceDefault,
  1397. },
  1398. Spec: extensions.ReplicaSetSpec{
  1399. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  1400. Template: api.PodTemplateSpec{
  1401. Spec: api.PodSpec{
  1402. RestartPolicy: api.RestartPolicyOnFailure,
  1403. DNSPolicy: api.DNSClusterFirst,
  1404. Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
  1405. },
  1406. ObjectMeta: api.ObjectMeta{
  1407. Labels: validLabels,
  1408. },
  1409. },
  1410. },
  1411. },
  1412. "invalid restart policy 2": {
  1413. ObjectMeta: api.ObjectMeta{
  1414. Name: "abc-123",
  1415. Namespace: api.NamespaceDefault,
  1416. },
  1417. Spec: extensions.ReplicaSetSpec{
  1418. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  1419. Template: api.PodTemplateSpec{
  1420. Spec: api.PodSpec{
  1421. RestartPolicy: api.RestartPolicyNever,
  1422. DNSPolicy: api.DNSClusterFirst,
  1423. Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
  1424. },
  1425. ObjectMeta: api.ObjectMeta{
  1426. Labels: validLabels,
  1427. },
  1428. },
  1429. },
  1430. },
  1431. }
  1432. for k, v := range errorCases {
  1433. errs := ValidateReplicaSet(&v)
  1434. if len(errs) == 0 {
  1435. t.Errorf("expected failure for %s", k)
  1436. }
  1437. for i := range errs {
  1438. field := errs[i].Field
  1439. if !strings.HasPrefix(field, "spec.template.") &&
  1440. field != "metadata.name" &&
  1441. field != "metadata.namespace" &&
  1442. field != "spec.selector" &&
  1443. field != "spec.template" &&
  1444. field != "GCEPersistentDisk.ReadOnly" &&
  1445. field != "spec.replicas" &&
  1446. field != "spec.template.labels" &&
  1447. field != "metadata.annotations" &&
  1448. field != "metadata.labels" &&
  1449. field != "status.replicas" {
  1450. t.Errorf("%s: missing prefix for: %v", k, errs[i])
  1451. }
  1452. }
  1453. }
  1454. }
  1455. func TestValidatePodSecurityPolicy(t *testing.T) {
  1456. validPSP := func() *extensions.PodSecurityPolicy {
  1457. return &extensions.PodSecurityPolicy{
  1458. ObjectMeta: api.ObjectMeta{
  1459. Name: "foo",
  1460. Annotations: map[string]string{},
  1461. },
  1462. Spec: extensions.PodSecurityPolicySpec{
  1463. SELinux: extensions.SELinuxStrategyOptions{
  1464. Rule: extensions.SELinuxStrategyRunAsAny,
  1465. },
  1466. RunAsUser: extensions.RunAsUserStrategyOptions{
  1467. Rule: extensions.RunAsUserStrategyRunAsAny,
  1468. },
  1469. FSGroup: extensions.FSGroupStrategyOptions{
  1470. Rule: extensions.FSGroupStrategyRunAsAny,
  1471. },
  1472. SupplementalGroups: extensions.SupplementalGroupsStrategyOptions{
  1473. Rule: extensions.SupplementalGroupsStrategyRunAsAny,
  1474. },
  1475. },
  1476. }
  1477. }
  1478. noUserOptions := validPSP()
  1479. noUserOptions.Spec.RunAsUser.Rule = ""
  1480. noSELinuxOptions := validPSP()
  1481. noSELinuxOptions.Spec.SELinux.Rule = ""
  1482. invalidUserStratType := validPSP()
  1483. invalidUserStratType.Spec.RunAsUser.Rule = "invalid"
  1484. invalidSELinuxStratType := validPSP()
  1485. invalidSELinuxStratType.Spec.SELinux.Rule = "invalid"
  1486. invalidUIDPSP := validPSP()
  1487. invalidUIDPSP.Spec.RunAsUser.Rule = extensions.RunAsUserStrategyMustRunAs
  1488. invalidUIDPSP.Spec.RunAsUser.Ranges = []extensions.IDRange{
  1489. {Min: -1, Max: 1},
  1490. }
  1491. missingObjectMetaName := validPSP()
  1492. missingObjectMetaName.ObjectMeta.Name = ""
  1493. noFSGroupOptions := validPSP()
  1494. noFSGroupOptions.Spec.FSGroup.Rule = ""
  1495. invalidFSGroupStratType := validPSP()
  1496. invalidFSGroupStratType.Spec.FSGroup.Rule = "invalid"
  1497. noSupplementalGroupsOptions := validPSP()
  1498. noSupplementalGroupsOptions.Spec.SupplementalGroups.Rule = ""
  1499. invalidSupGroupStratType := validPSP()
  1500. invalidSupGroupStratType.Spec.SupplementalGroups.Rule = "invalid"
  1501. invalidRangeMinGreaterThanMax := validPSP()
  1502. invalidRangeMinGreaterThanMax.Spec.FSGroup.Ranges = []extensions.IDRange{
  1503. {Min: 2, Max: 1},
  1504. }
  1505. invalidRangeNegativeMin := validPSP()
  1506. invalidRangeNegativeMin.Spec.FSGroup.Ranges = []extensions.IDRange{
  1507. {Min: -1, Max: 10},
  1508. }
  1509. invalidRangeNegativeMax := validPSP()
  1510. invalidRangeNegativeMax.Spec.FSGroup.Ranges = []extensions.IDRange{
  1511. {Min: 1, Max: -10},
  1512. }
  1513. requiredCapAddAndDrop := validPSP()
  1514. requiredCapAddAndDrop.Spec.DefaultAddCapabilities = []api.Capability{"foo"}
  1515. requiredCapAddAndDrop.Spec.RequiredDropCapabilities = []api.Capability{"foo"}
  1516. allowedCapListedInRequiredDrop := validPSP()
  1517. allowedCapListedInRequiredDrop.Spec.RequiredDropCapabilities = []api.Capability{"foo"}
  1518. allowedCapListedInRequiredDrop.Spec.AllowedCapabilities = []api.Capability{"foo"}
  1519. invalidAppArmorDefault := validPSP()
  1520. invalidAppArmorDefault.Annotations = map[string]string{
  1521. apparmor.DefaultProfileAnnotationKey: "not-good",
  1522. }
  1523. invalidAppArmorAllowed := validPSP()
  1524. invalidAppArmorAllowed.Annotations = map[string]string{
  1525. apparmor.AllowedProfilesAnnotationKey: apparmor.ProfileRuntimeDefault + ",not-good",
  1526. }
  1527. invalidSysctlPattern := validPSP()
  1528. invalidSysctlPattern.Annotations[extensions.SysctlsPodSecurityPolicyAnnotationKey] = "a.*.b"
  1529. errorCases := map[string]struct {
  1530. psp *extensions.PodSecurityPolicy
  1531. errorType field.ErrorType
  1532. errorDetail string
  1533. }{
  1534. "no user options": {
  1535. psp: noUserOptions,
  1536. errorType: field.ErrorTypeNotSupported,
  1537. errorDetail: "supported values: MustRunAs, MustRunAsNonRoot, RunAsAny",
  1538. },
  1539. "no selinux options": {
  1540. psp: noSELinuxOptions,
  1541. errorType: field.ErrorTypeNotSupported,
  1542. errorDetail: "supported values: MustRunAs, RunAsAny",
  1543. },
  1544. "no fsgroup options": {
  1545. psp: noFSGroupOptions,
  1546. errorType: field.ErrorTypeNotSupported,
  1547. errorDetail: "supported values: MustRunAs, RunAsAny",
  1548. },
  1549. "no sup group options": {
  1550. psp: noSupplementalGroupsOptions,
  1551. errorType: field.ErrorTypeNotSupported,
  1552. errorDetail: "supported values: MustRunAs, RunAsAny",
  1553. },
  1554. "invalid user strategy type": {
  1555. psp: invalidUserStratType,
  1556. errorType: field.ErrorTypeNotSupported,
  1557. errorDetail: "supported values: MustRunAs, MustRunAsNonRoot, RunAsAny",
  1558. },
  1559. "invalid selinux strategy type": {
  1560. psp: invalidSELinuxStratType,
  1561. errorType: field.ErrorTypeNotSupported,
  1562. errorDetail: "supported values: MustRunAs, RunAsAny",
  1563. },
  1564. "invalid sup group strategy type": {
  1565. psp: invalidSupGroupStratType,
  1566. errorType: field.ErrorTypeNotSupported,
  1567. errorDetail: "supported values: MustRunAs, RunAsAny",
  1568. },
  1569. "invalid fs group strategy type": {
  1570. psp: invalidFSGroupStratType,
  1571. errorType: field.ErrorTypeNotSupported,
  1572. errorDetail: "supported values: MustRunAs, RunAsAny",
  1573. },
  1574. "invalid uid": {
  1575. psp: invalidUIDPSP,
  1576. errorType: field.ErrorTypeInvalid,
  1577. errorDetail: "min cannot be negative",
  1578. },
  1579. "missing object meta name": {
  1580. psp: missingObjectMetaName,
  1581. errorType: field.ErrorTypeRequired,
  1582. errorDetail: "name or generateName is required",
  1583. },
  1584. "invalid range min greater than max": {
  1585. psp: invalidRangeMinGreaterThanMax,
  1586. errorType: field.ErrorTypeInvalid,
  1587. errorDetail: "min cannot be greater than max",
  1588. },
  1589. "invalid range negative min": {
  1590. psp: invalidRangeNegativeMin,
  1591. errorType: field.ErrorTypeInvalid,
  1592. errorDetail: "min cannot be negative",
  1593. },
  1594. "invalid range negative max": {
  1595. psp: invalidRangeNegativeMax,
  1596. errorType: field.ErrorTypeInvalid,
  1597. errorDetail: "max cannot be negative",
  1598. },
  1599. "invalid required caps": {
  1600. psp: requiredCapAddAndDrop,
  1601. errorType: field.ErrorTypeInvalid,
  1602. errorDetail: "capability is listed in defaultAddCapabilities and requiredDropCapabilities",
  1603. },
  1604. "allowed cap listed in required drops": {
  1605. psp: allowedCapListedInRequiredDrop,
  1606. errorType: field.ErrorTypeInvalid,
  1607. errorDetail: "capability is listed in allowedCapabilities and requiredDropCapabilities",
  1608. },
  1609. "invalid AppArmor default profile": {
  1610. psp: invalidAppArmorDefault,
  1611. errorType: field.ErrorTypeInvalid,
  1612. errorDetail: "invalid AppArmor profile name: \"not-good\"",
  1613. },
  1614. "invalid AppArmor allowed profile": {
  1615. psp: invalidAppArmorAllowed,
  1616. errorType: field.ErrorTypeInvalid,
  1617. errorDetail: "invalid AppArmor profile name: \"not-good\"",
  1618. },
  1619. "invalid sysctl pattern": {
  1620. psp: invalidSysctlPattern,
  1621. errorType: field.ErrorTypeInvalid,
  1622. errorDetail: fmt.Sprintf("must have at most 253 characters and match regex %s", SysctlPatternFmt),
  1623. },
  1624. }
  1625. for k, v := range errorCases {
  1626. errs := ValidatePodSecurityPolicy(v.psp)
  1627. if len(errs) == 0 {
  1628. t.Errorf("%s expected errors but got none", k)
  1629. continue
  1630. }
  1631. if errs[0].Type != v.errorType {
  1632. t.Errorf("%s received an unexpected error type. Expected: %v got: %v", k, v.errorType, errs[0].Type)
  1633. }
  1634. if errs[0].Detail != v.errorDetail {
  1635. t.Errorf("%s received an unexpected error detail. Expected %v got: %v", k, v.errorDetail, errs[0].Detail)
  1636. }
  1637. }
  1638. mustRunAs := validPSP()
  1639. mustRunAs.Spec.FSGroup.Rule = extensions.FSGroupStrategyMustRunAs
  1640. mustRunAs.Spec.SupplementalGroups.Rule = extensions.SupplementalGroupsStrategyMustRunAs
  1641. mustRunAs.Spec.RunAsUser.Rule = extensions.RunAsUserStrategyMustRunAs
  1642. mustRunAs.Spec.RunAsUser.Ranges = []extensions.IDRange{
  1643. {Min: 1, Max: 1},
  1644. }
  1645. mustRunAs.Spec.SELinux.Rule = extensions.SELinuxStrategyMustRunAs
  1646. runAsNonRoot := validPSP()
  1647. runAsNonRoot.Spec.RunAsUser.Rule = extensions.RunAsUserStrategyMustRunAsNonRoot
  1648. caseInsensitiveAddDrop := validPSP()
  1649. caseInsensitiveAddDrop.Spec.DefaultAddCapabilities = []api.Capability{"foo"}
  1650. caseInsensitiveAddDrop.Spec.RequiredDropCapabilities = []api.Capability{"FOO"}
  1651. caseInsensitiveAllowedDrop := validPSP()
  1652. caseInsensitiveAllowedDrop.Spec.RequiredDropCapabilities = []api.Capability{"FOO"}
  1653. caseInsensitiveAllowedDrop.Spec.AllowedCapabilities = []api.Capability{"foo"}
  1654. validAppArmor := validPSP()
  1655. validAppArmor.Annotations = map[string]string{
  1656. apparmor.DefaultProfileAnnotationKey: apparmor.ProfileRuntimeDefault,
  1657. apparmor.AllowedProfilesAnnotationKey: apparmor.ProfileRuntimeDefault + "," + apparmor.ProfileNamePrefix + "foo",
  1658. }
  1659. withSysctl := validPSP()
  1660. withSysctl.Annotations[extensions.SysctlsPodSecurityPolicyAnnotationKey] = "net.*"
  1661. successCases := map[string]struct {
  1662. psp *extensions.PodSecurityPolicy
  1663. }{
  1664. "must run as": {
  1665. psp: mustRunAs,
  1666. },
  1667. "run as any": {
  1668. psp: validPSP(),
  1669. },
  1670. "run as non-root (user only)": {
  1671. psp: runAsNonRoot,
  1672. },
  1673. "comparison for add -> drop is case sensitive": {
  1674. psp: caseInsensitiveAddDrop,
  1675. },
  1676. "comparison for allowed -> drop is case sensitive": {
  1677. psp: caseInsensitiveAllowedDrop,
  1678. },
  1679. "valid AppArmor annotations": {
  1680. psp: validAppArmor,
  1681. },
  1682. "with network sysctls": {
  1683. psp: withSysctl,
  1684. },
  1685. }
  1686. for k, v := range successCases {
  1687. if errs := ValidatePodSecurityPolicy(v.psp); len(errs) != 0 {
  1688. t.Errorf("Expected success for %s, got %v", k, errs)
  1689. }
  1690. }
  1691. }
  1692. func TestValidatePSPVolumes(t *testing.T) {
  1693. validPSP := func() *extensions.PodSecurityPolicy {
  1694. return &extensions.PodSecurityPolicy{
  1695. ObjectMeta: api.ObjectMeta{Name: "foo"},
  1696. Spec: extensions.PodSecurityPolicySpec{
  1697. SELinux: extensions.SELinuxStrategyOptions{
  1698. Rule: extensions.SELinuxStrategyRunAsAny,
  1699. },
  1700. RunAsUser: extensions.RunAsUserStrategyOptions{
  1701. Rule: extensions.RunAsUserStrategyRunAsAny,
  1702. },
  1703. FSGroup: extensions.FSGroupStrategyOptions{
  1704. Rule: extensions.FSGroupStrategyRunAsAny,
  1705. },
  1706. SupplementalGroups: extensions.SupplementalGroupsStrategyOptions{
  1707. Rule: extensions.SupplementalGroupsStrategyRunAsAny,
  1708. },
  1709. },
  1710. }
  1711. }
  1712. volumes := psputil.GetAllFSTypesAsSet()
  1713. // add in the * value since that is a pseudo type that is not included by default
  1714. volumes.Insert(string(extensions.All))
  1715. for _, strVolume := range volumes.List() {
  1716. psp := validPSP()
  1717. psp.Spec.Volumes = []extensions.FSType{extensions.FSType(strVolume)}
  1718. errs := ValidatePodSecurityPolicy(psp)
  1719. if len(errs) != 0 {
  1720. t.Errorf("%s validation expected no errors but received %v", strVolume, errs)
  1721. }
  1722. }
  1723. }
  1724. func TestValidateNetworkPolicy(t *testing.T) {
  1725. successCases := []extensions.NetworkPolicy{
  1726. {
  1727. ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "bar"},
  1728. Spec: extensions.NetworkPolicySpec{
  1729. PodSelector: unversioned.LabelSelector{
  1730. MatchLabels: map[string]string{"a": "b"},
  1731. },
  1732. Ingress: []extensions.NetworkPolicyIngressRule{},
  1733. },
  1734. },
  1735. {
  1736. ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "bar"},
  1737. Spec: extensions.NetworkPolicySpec{
  1738. PodSelector: unversioned.LabelSelector{
  1739. MatchLabels: map[string]string{"a": "b"},
  1740. },
  1741. Ingress: []extensions.NetworkPolicyIngressRule{
  1742. {
  1743. From: []extensions.NetworkPolicyPeer{},
  1744. Ports: []extensions.NetworkPolicyPort{},
  1745. },
  1746. },
  1747. },
  1748. },
  1749. {
  1750. ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "bar"},
  1751. Spec: extensions.NetworkPolicySpec{
  1752. PodSelector: unversioned.LabelSelector{
  1753. MatchLabels: map[string]string{"a": "b"},
  1754. },
  1755. Ingress: []extensions.NetworkPolicyIngressRule{
  1756. {
  1757. From: []extensions.NetworkPolicyPeer{
  1758. {
  1759. PodSelector: &unversioned.LabelSelector{
  1760. MatchLabels: map[string]string{"c": "d"},
  1761. },
  1762. },
  1763. },
  1764. },
  1765. },
  1766. },
  1767. },
  1768. {
  1769. ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "bar"},
  1770. Spec: extensions.NetworkPolicySpec{
  1771. PodSelector: unversioned.LabelSelector{
  1772. MatchLabels: map[string]string{"a": "b"},
  1773. },
  1774. Ingress: []extensions.NetworkPolicyIngressRule{
  1775. {
  1776. From: []extensions.NetworkPolicyPeer{
  1777. {
  1778. NamespaceSelector: &unversioned.LabelSelector{
  1779. MatchLabels: map[string]string{"c": "d"},
  1780. },
  1781. },
  1782. },
  1783. },
  1784. },
  1785. },
  1786. },
  1787. }
  1788. // Success cases are expected to pass validation.
  1789. for k, v := range successCases {
  1790. if errs := ValidateNetworkPolicy(&v); len(errs) != 0 {
  1791. t.Errorf("Expected success for %d, got %v", k, errs)
  1792. }
  1793. }
  1794. invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
  1795. errorCases := map[string]extensions.NetworkPolicy{
  1796. "namespaceSelector and podSelector": {
  1797. ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "bar"},
  1798. Spec: extensions.NetworkPolicySpec{
  1799. PodSelector: unversioned.LabelSelector{
  1800. MatchLabels: map[string]string{"a": "b"},
  1801. },
  1802. Ingress: []extensions.NetworkPolicyIngressRule{
  1803. {
  1804. From: []extensions.NetworkPolicyPeer{
  1805. {
  1806. PodSelector: &unversioned.LabelSelector{
  1807. MatchLabels: map[string]string{"c": "d"},
  1808. },
  1809. NamespaceSelector: &unversioned.LabelSelector{
  1810. MatchLabels: map[string]string{"c": "d"},
  1811. },
  1812. },
  1813. },
  1814. },
  1815. },
  1816. },
  1817. },
  1818. "invalid spec.podSelector": {
  1819. ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "bar"},
  1820. Spec: extensions.NetworkPolicySpec{
  1821. PodSelector: unversioned.LabelSelector{
  1822. MatchLabels: invalidSelector,
  1823. },
  1824. Ingress: []extensions.NetworkPolicyIngressRule{
  1825. {
  1826. From: []extensions.NetworkPolicyPeer{
  1827. {
  1828. NamespaceSelector: &unversioned.LabelSelector{
  1829. MatchLabels: map[string]string{"c": "d"},
  1830. },
  1831. },
  1832. },
  1833. },
  1834. },
  1835. },
  1836. },
  1837. "invalid ingress.from.podSelector": {
  1838. ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "bar"},
  1839. Spec: extensions.NetworkPolicySpec{
  1840. PodSelector: unversioned.LabelSelector{},
  1841. Ingress: []extensions.NetworkPolicyIngressRule{
  1842. {
  1843. From: []extensions.NetworkPolicyPeer{
  1844. {
  1845. PodSelector: &unversioned.LabelSelector{
  1846. MatchLabels: invalidSelector,
  1847. },
  1848. },
  1849. },
  1850. },
  1851. },
  1852. },
  1853. },
  1854. "invalid ingress.from.namespaceSelector": {
  1855. ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "bar"},
  1856. Spec: extensions.NetworkPolicySpec{
  1857. PodSelector: unversioned.LabelSelector{},
  1858. Ingress: []extensions.NetworkPolicyIngressRule{
  1859. {
  1860. From: []extensions.NetworkPolicyPeer{
  1861. {
  1862. NamespaceSelector: &unversioned.LabelSelector{
  1863. MatchLabels: invalidSelector,
  1864. },
  1865. },
  1866. },
  1867. },
  1868. },
  1869. },
  1870. },
  1871. }
  1872. // Error cases are not expected to pass validation.
  1873. for testName, networkPolicy := range errorCases {
  1874. if errs := ValidateNetworkPolicy(&networkPolicy); len(errs) == 0 {
  1875. t.Errorf("Expected failure for test: %s", testName)
  1876. }
  1877. }
  1878. }
  1879. func TestValidateNetworkPolicyUpdate(t *testing.T) {
  1880. type npUpdateTest struct {
  1881. old extensions.NetworkPolicy
  1882. update extensions.NetworkPolicy
  1883. }
  1884. successCases := []npUpdateTest{
  1885. {
  1886. old: extensions.NetworkPolicy{
  1887. ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "bar"},
  1888. Spec: extensions.NetworkPolicySpec{
  1889. PodSelector: unversioned.LabelSelector{
  1890. MatchLabels: map[string]string{"a": "b"},
  1891. },
  1892. Ingress: []extensions.NetworkPolicyIngressRule{},
  1893. },
  1894. },
  1895. update: extensions.NetworkPolicy{
  1896. ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "bar"},
  1897. Spec: extensions.NetworkPolicySpec{
  1898. PodSelector: unversioned.LabelSelector{
  1899. MatchLabels: map[string]string{"a": "b"},
  1900. },
  1901. Ingress: []extensions.NetworkPolicyIngressRule{},
  1902. },
  1903. },
  1904. },
  1905. }
  1906. for _, successCase := range successCases {
  1907. successCase.old.ObjectMeta.ResourceVersion = "1"
  1908. successCase.update.ObjectMeta.ResourceVersion = "1"
  1909. if errs := ValidateNetworkPolicyUpdate(&successCase.update, &successCase.old); len(errs) != 0 {
  1910. t.Errorf("expected success: %v", errs)
  1911. }
  1912. }
  1913. errorCases := map[string]npUpdateTest{
  1914. "change name": {
  1915. old: extensions.NetworkPolicy{
  1916. ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "bar"},
  1917. Spec: extensions.NetworkPolicySpec{
  1918. PodSelector: unversioned.LabelSelector{},
  1919. Ingress: []extensions.NetworkPolicyIngressRule{},
  1920. },
  1921. },
  1922. update: extensions.NetworkPolicy{
  1923. ObjectMeta: api.ObjectMeta{Name: "baz", Namespace: "bar"},
  1924. Spec: extensions.NetworkPolicySpec{
  1925. PodSelector: unversioned.LabelSelector{},
  1926. Ingress: []extensions.NetworkPolicyIngressRule{},
  1927. },
  1928. },
  1929. },
  1930. "change spec": {
  1931. old: extensions.NetworkPolicy{
  1932. ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "bar"},
  1933. Spec: extensions.NetworkPolicySpec{
  1934. PodSelector: unversioned.LabelSelector{},
  1935. Ingress: []extensions.NetworkPolicyIngressRule{},
  1936. },
  1937. },
  1938. update: extensions.NetworkPolicy{
  1939. ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "bar"},
  1940. Spec: extensions.NetworkPolicySpec{
  1941. PodSelector: unversioned.LabelSelector{
  1942. MatchLabels: map[string]string{"a": "b"},
  1943. },
  1944. Ingress: []extensions.NetworkPolicyIngressRule{},
  1945. },
  1946. },
  1947. },
  1948. }
  1949. for testName, errorCase := range errorCases {
  1950. if errs := ValidateNetworkPolicyUpdate(&errorCase.update, &errorCase.old); len(errs) == 0 {
  1951. t.Errorf("expected failure: %s", testName)
  1952. }
  1953. }
  1954. }
  1955. func TestIsValidSysctlPattern(t *testing.T) {
  1956. valid := []string{
  1957. "a.b.c.d",
  1958. "a",
  1959. "a_b",
  1960. "a-b",
  1961. "abc",
  1962. "abc.def",
  1963. "*",
  1964. "a.*",
  1965. "*",
  1966. "abc*",
  1967. "a.abc*",
  1968. "a.b.*",
  1969. }
  1970. invalid := []string{
  1971. "",
  1972. "ä",
  1973. "a_",
  1974. "_",
  1975. "_a",
  1976. "_a._b",
  1977. "__",
  1978. "-",
  1979. ".",
  1980. "a.",
  1981. ".a",
  1982. "a.b.",
  1983. "a*.b",
  1984. "a*b",
  1985. "*a",
  1986. "Abc",
  1987. func(n int) string {
  1988. x := make([]byte, n)
  1989. for i := range x {
  1990. x[i] = byte('a')
  1991. }
  1992. return string(x)
  1993. }(256),
  1994. }
  1995. for _, s := range valid {
  1996. if !IsValidSysctlPattern(s) {
  1997. t.Errorf("%q expected to be a valid sysctl pattern", s)
  1998. }
  1999. }
  2000. for _, s := range invalid {
  2001. if IsValidSysctlPattern(s) {
  2002. t.Errorf("%q expected to be an invalid sysctl pattern", s)
  2003. }
  2004. }
  2005. }
  2006. func newBool(val bool) *bool {
  2007. p := new(bool)
  2008. *p = val
  2009. return p
  2010. }
  2011. func TestValidateStorageClass(t *testing.T) {
  2012. successCases := []extensions.StorageClass{
  2013. {
  2014. // empty parameters
  2015. ObjectMeta: api.ObjectMeta{Name: "foo"},
  2016. Provisioner: "kubernetes.io/foo-provisioner",
  2017. Parameters: map[string]string{},
  2018. },
  2019. {
  2020. // nil parameters
  2021. ObjectMeta: api.ObjectMeta{Name: "foo"},
  2022. Provisioner: "kubernetes.io/foo-provisioner",
  2023. },
  2024. {
  2025. // some parameters
  2026. ObjectMeta: api.ObjectMeta{Name: "foo"},
  2027. Provisioner: "kubernetes.io/foo-provisioner",
  2028. Parameters: map[string]string{
  2029. "kubernetes.io/foo-parameter": "free/form/string",
  2030. "foo-parameter": "free-form-string",
  2031. "foo-parameter2": "{\"embedded\": \"json\", \"with\": {\"structures\":\"inside\"}}",
  2032. },
  2033. },
  2034. }
  2035. // Success cases are expected to pass validation.
  2036. for k, v := range successCases {
  2037. if errs := ValidateStorageClass(&v); len(errs) != 0 {
  2038. t.Errorf("Expected success for %d, got %v", k, errs)
  2039. }
  2040. }
  2041. // generate a map longer than maxProvisionerParameterSize
  2042. longParameters := make(map[string]string)
  2043. totalSize := 0
  2044. for totalSize < maxProvisionerParameterSize {
  2045. k := fmt.Sprintf("param/%d", totalSize)
  2046. v := fmt.Sprintf("value-%d", totalSize)
  2047. longParameters[k] = v
  2048. totalSize = totalSize + len(k) + len(v)
  2049. }
  2050. errorCases := map[string]extensions.StorageClass{
  2051. "namespace is present": {
  2052. ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "bar"},
  2053. Provisioner: "kubernetes.io/foo-provisioner",
  2054. },
  2055. "invalid provisioner": {
  2056. ObjectMeta: api.ObjectMeta{Name: "foo"},
  2057. Provisioner: "kubernetes.io/invalid/provisioner",
  2058. },
  2059. "invalid empty parameter name": {
  2060. ObjectMeta: api.ObjectMeta{Name: "foo"},
  2061. Provisioner: "kubernetes.io/foo",
  2062. Parameters: map[string]string{
  2063. "": "value",
  2064. },
  2065. },
  2066. "provisioner: Required value": {
  2067. ObjectMeta: api.ObjectMeta{Name: "foo"},
  2068. Provisioner: "",
  2069. },
  2070. "too long parameters": {
  2071. ObjectMeta: api.ObjectMeta{Name: "foo"},
  2072. Provisioner: "kubernetes.io/foo",
  2073. Parameters: longParameters,
  2074. },
  2075. }
  2076. // Error cases are not expected to pass validation.
  2077. for testName, storageClass := range errorCases {
  2078. if errs := ValidateStorageClass(&storageClass); len(errs) == 0 {
  2079. t.Errorf("Expected failure for test: %s", testName)
  2080. }
  2081. }
  2082. }