validation.go 225 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398
  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. "encoding/json"
  16. "fmt"
  17. "math"
  18. "net"
  19. "path"
  20. "path/filepath"
  21. "reflect"
  22. "regexp"
  23. "strings"
  24. "k8s.io/klog"
  25. "k8s.io/api/core/v1"
  26. apiequality "k8s.io/apimachinery/pkg/api/equality"
  27. "k8s.io/apimachinery/pkg/api/resource"
  28. apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
  29. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  30. unversionedvalidation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
  31. "k8s.io/apimachinery/pkg/labels"
  32. "k8s.io/apimachinery/pkg/runtime/schema"
  33. "k8s.io/apimachinery/pkg/util/diff"
  34. "k8s.io/apimachinery/pkg/util/intstr"
  35. "k8s.io/apimachinery/pkg/util/sets"
  36. "k8s.io/apimachinery/pkg/util/validation"
  37. "k8s.io/apimachinery/pkg/util/validation/field"
  38. utilfeature "k8s.io/apiserver/pkg/util/feature"
  39. apiservice "k8s.io/kubernetes/pkg/api/service"
  40. "k8s.io/kubernetes/pkg/apis/core"
  41. "k8s.io/kubernetes/pkg/apis/core/helper"
  42. podshelper "k8s.io/kubernetes/pkg/apis/core/pods"
  43. corev1 "k8s.io/kubernetes/pkg/apis/core/v1"
  44. v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
  45. "k8s.io/kubernetes/pkg/capabilities"
  46. "k8s.io/kubernetes/pkg/features"
  47. "k8s.io/kubernetes/pkg/fieldpath"
  48. "k8s.io/kubernetes/pkg/master/ports"
  49. "k8s.io/kubernetes/pkg/security/apparmor"
  50. )
  51. const isNegativeErrorMsg string = apimachineryvalidation.IsNegativeErrorMsg
  52. const isInvalidQuotaResource string = `must be a standard resource for quota`
  53. const fieldImmutableErrorMsg string = apimachineryvalidation.FieldImmutableErrorMsg
  54. const isNotIntegerErrorMsg string = `must be an integer`
  55. const isNotPositiveErrorMsg string = `must be greater than zero`
  56. var pdPartitionErrorMsg string = validation.InclusiveRangeError(1, 255)
  57. var fileModeErrorMsg = "must be a number between 0 and 0777 (octal), both inclusive"
  58. // BannedOwners is a black list of object that are not allowed to be owners.
  59. var BannedOwners = apimachineryvalidation.BannedOwners
  60. var iscsiInitiatorIqnRegex = regexp.MustCompile(`iqn\.\d{4}-\d{2}\.([[:alnum:]-.]+)(:[^,;*&$|\s]+)$`)
  61. var iscsiInitiatorEuiRegex = regexp.MustCompile(`^eui.[[:alnum:]]{16}$`)
  62. var iscsiInitiatorNaaRegex = regexp.MustCompile(`^naa.[[:alnum:]]{32}$`)
  63. // ValidateHasLabel requires that metav1.ObjectMeta has a Label with key and expectedValue
  64. func ValidateHasLabel(meta metav1.ObjectMeta, fldPath *field.Path, key, expectedValue string) field.ErrorList {
  65. allErrs := field.ErrorList{}
  66. actualValue, found := meta.Labels[key]
  67. if !found {
  68. allErrs = append(allErrs, field.Required(fldPath.Child("labels").Key(key),
  69. fmt.Sprintf("must be '%s'", expectedValue)))
  70. return allErrs
  71. }
  72. if actualValue != expectedValue {
  73. allErrs = append(allErrs, field.Invalid(fldPath.Child("labels").Key(key), meta.Labels,
  74. fmt.Sprintf("must be '%s'", expectedValue)))
  75. }
  76. return allErrs
  77. }
  78. // ValidateAnnotations validates that a set of annotations are correctly defined.
  79. func ValidateAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList {
  80. return apimachineryvalidation.ValidateAnnotations(annotations, fldPath)
  81. }
  82. func ValidateDNS1123Label(value string, fldPath *field.Path) field.ErrorList {
  83. allErrs := field.ErrorList{}
  84. for _, msg := range validation.IsDNS1123Label(value) {
  85. allErrs = append(allErrs, field.Invalid(fldPath, value, msg))
  86. }
  87. return allErrs
  88. }
  89. // ValidateDNS1123Subdomain validates that a name is a proper DNS subdomain.
  90. func ValidateDNS1123Subdomain(value string, fldPath *field.Path) field.ErrorList {
  91. allErrs := field.ErrorList{}
  92. for _, msg := range validation.IsDNS1123Subdomain(value) {
  93. allErrs = append(allErrs, field.Invalid(fldPath, value, msg))
  94. }
  95. return allErrs
  96. }
  97. func ValidatePodSpecificAnnotations(annotations map[string]string, spec *core.PodSpec, fldPath *field.Path) field.ErrorList {
  98. allErrs := field.ErrorList{}
  99. if value, isMirror := annotations[core.MirrorPodAnnotationKey]; isMirror {
  100. if len(spec.NodeName) == 0 {
  101. allErrs = append(allErrs, field.Invalid(fldPath.Key(core.MirrorPodAnnotationKey), value, "must set spec.nodeName if mirror pod annotation is set"))
  102. }
  103. }
  104. if annotations[core.TolerationsAnnotationKey] != "" {
  105. allErrs = append(allErrs, ValidateTolerationsInPodAnnotations(annotations, fldPath)...)
  106. }
  107. allErrs = append(allErrs, ValidateSeccompPodAnnotations(annotations, fldPath)...)
  108. allErrs = append(allErrs, ValidateAppArmorPodAnnotations(annotations, spec, fldPath)...)
  109. return allErrs
  110. }
  111. // ValidateTolerationsInPodAnnotations tests that the serialized tolerations in Pod.Annotations has valid data
  112. func ValidateTolerationsInPodAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList {
  113. allErrs := field.ErrorList{}
  114. tolerations, err := helper.GetTolerationsFromPodAnnotations(annotations)
  115. if err != nil {
  116. allErrs = append(allErrs, field.Invalid(fldPath, core.TolerationsAnnotationKey, err.Error()))
  117. return allErrs
  118. }
  119. if len(tolerations) > 0 {
  120. allErrs = append(allErrs, ValidateTolerations(tolerations, fldPath.Child(core.TolerationsAnnotationKey))...)
  121. }
  122. return allErrs
  123. }
  124. func ValidatePodSpecificAnnotationUpdates(newPod, oldPod *core.Pod, fldPath *field.Path) field.ErrorList {
  125. allErrs := field.ErrorList{}
  126. newAnnotations := newPod.Annotations
  127. oldAnnotations := oldPod.Annotations
  128. for k, oldVal := range oldAnnotations {
  129. if newVal, exists := newAnnotations[k]; exists && newVal == oldVal {
  130. continue // No change.
  131. }
  132. if strings.HasPrefix(k, apparmor.ContainerAnnotationKeyPrefix) {
  133. allErrs = append(allErrs, field.Forbidden(fldPath.Key(k), "may not remove or update AppArmor annotations"))
  134. }
  135. if k == core.MirrorPodAnnotationKey {
  136. allErrs = append(allErrs, field.Forbidden(fldPath.Key(k), "may not remove or update mirror pod annotation"))
  137. }
  138. }
  139. // Check for additions
  140. for k := range newAnnotations {
  141. if _, ok := oldAnnotations[k]; ok {
  142. continue // No change.
  143. }
  144. if strings.HasPrefix(k, apparmor.ContainerAnnotationKeyPrefix) {
  145. allErrs = append(allErrs, field.Forbidden(fldPath.Key(k), "may not add AppArmor annotations"))
  146. }
  147. if k == core.MirrorPodAnnotationKey {
  148. allErrs = append(allErrs, field.Forbidden(fldPath.Key(k), "may not add mirror pod annotation"))
  149. }
  150. }
  151. allErrs = append(allErrs, ValidatePodSpecificAnnotations(newAnnotations, &newPod.Spec, fldPath)...)
  152. return allErrs
  153. }
  154. func ValidateEndpointsSpecificAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList {
  155. allErrs := field.ErrorList{}
  156. return allErrs
  157. }
  158. // ValidateNameFunc validates that the provided name is valid for a given resource type.
  159. // Not all resources have the same validation rules for names. Prefix is true
  160. // if the name will have a value appended to it. If the name is not valid,
  161. // this returns a list of descriptions of individual characteristics of the
  162. // value that were not valid. Otherwise this returns an empty list or nil.
  163. type ValidateNameFunc apimachineryvalidation.ValidateNameFunc
  164. // ValidatePodName can be used to check whether the given pod name is valid.
  165. // Prefix indicates this name will be used as part of generation, in which case
  166. // trailing dashes are allowed.
  167. var ValidatePodName = apimachineryvalidation.NameIsDNSSubdomain
  168. // ValidateReplicationControllerName can be used to check whether the given replication
  169. // controller name is valid.
  170. // Prefix indicates this name will be used as part of generation, in which case
  171. // trailing dashes are allowed.
  172. var ValidateReplicationControllerName = apimachineryvalidation.NameIsDNSSubdomain
  173. // ValidateServiceName can be used to check whether the given service name is valid.
  174. // Prefix indicates this name will be used as part of generation, in which case
  175. // trailing dashes are allowed.
  176. var ValidateServiceName = apimachineryvalidation.NameIsDNS1035Label
  177. // ValidateNodeName can be used to check whether the given node name is valid.
  178. // Prefix indicates this name will be used as part of generation, in which case
  179. // trailing dashes are allowed.
  180. var ValidateNodeName = apimachineryvalidation.NameIsDNSSubdomain
  181. // ValidateNamespaceName can be used to check whether the given namespace name is valid.
  182. // Prefix indicates this name will be used as part of generation, in which case
  183. // trailing dashes are allowed.
  184. var ValidateNamespaceName = apimachineryvalidation.ValidateNamespaceName
  185. // ValidateLimitRangeName can be used to check whether the given limit range name is valid.
  186. // Prefix indicates this name will be used as part of generation, in which case
  187. // trailing dashes are allowed.
  188. var ValidateLimitRangeName = apimachineryvalidation.NameIsDNSSubdomain
  189. // ValidateResourceQuotaName can be used to check whether the given
  190. // resource quota name is valid.
  191. // Prefix indicates this name will be used as part of generation, in which case
  192. // trailing dashes are allowed.
  193. var ValidateResourceQuotaName = apimachineryvalidation.NameIsDNSSubdomain
  194. // ValidateSecretName can be used to check whether the given secret name is valid.
  195. // Prefix indicates this name will be used as part of generation, in which case
  196. // trailing dashes are allowed.
  197. var ValidateSecretName = apimachineryvalidation.NameIsDNSSubdomain
  198. // ValidateServiceAccountName can be used to check whether the given service account name is valid.
  199. // Prefix indicates this name will be used as part of generation, in which case
  200. // trailing dashes are allowed.
  201. var ValidateServiceAccountName = apimachineryvalidation.ValidateServiceAccountName
  202. // ValidateEndpointsName can be used to check whether the given endpoints name is valid.
  203. // Prefix indicates this name will be used as part of generation, in which case
  204. // trailing dashes are allowed.
  205. var ValidateEndpointsName = apimachineryvalidation.NameIsDNSSubdomain
  206. // ValidateClusterName can be used to check whether the given cluster name is valid.
  207. var ValidateClusterName = apimachineryvalidation.ValidateClusterName
  208. // ValidateClassName can be used to check whether the given class name is valid.
  209. // It is defined here to avoid import cycle between pkg/apis/storage/validation
  210. // (where it should be) and this file.
  211. var ValidateClassName = apimachineryvalidation.NameIsDNSSubdomain
  212. // ValidatePiorityClassName can be used to check whether the given priority
  213. // class name is valid.
  214. var ValidatePriorityClassName = apimachineryvalidation.NameIsDNSSubdomain
  215. // ValidateRuntimeClassName can be used to check whether the given RuntimeClass name is valid.
  216. // Prefix indicates this name will be used as part of generation, in which case
  217. // trailing dashes are allowed.
  218. func ValidateRuntimeClassName(name string, fldPath *field.Path) field.ErrorList {
  219. var allErrs field.ErrorList
  220. for _, msg := range apimachineryvalidation.NameIsDNSSubdomain(name, false) {
  221. allErrs = append(allErrs, field.Invalid(fldPath, name, msg))
  222. }
  223. return allErrs
  224. }
  225. // Validates that given value is not negative.
  226. func ValidateNonnegativeField(value int64, fldPath *field.Path) field.ErrorList {
  227. return apimachineryvalidation.ValidateNonnegativeField(value, fldPath)
  228. }
  229. // Validates that a Quantity is not negative
  230. func ValidateNonnegativeQuantity(value resource.Quantity, fldPath *field.Path) field.ErrorList {
  231. allErrs := field.ErrorList{}
  232. if value.Cmp(resource.Quantity{}) < 0 {
  233. allErrs = append(allErrs, field.Invalid(fldPath, value.String(), isNegativeErrorMsg))
  234. }
  235. return allErrs
  236. }
  237. // Validates that a Quantity is positive
  238. func ValidatePositiveQuantityValue(value resource.Quantity, fldPath *field.Path) field.ErrorList {
  239. allErrs := field.ErrorList{}
  240. if value.Cmp(resource.Quantity{}) <= 0 {
  241. allErrs = append(allErrs, field.Invalid(fldPath, value.String(), isNotPositiveErrorMsg))
  242. }
  243. return allErrs
  244. }
  245. func ValidateImmutableField(newVal, oldVal interface{}, fldPath *field.Path) field.ErrorList {
  246. return apimachineryvalidation.ValidateImmutableField(newVal, oldVal, fldPath)
  247. }
  248. func ValidateImmutableAnnotation(newVal string, oldVal string, annotation string, fldPath *field.Path) field.ErrorList {
  249. allErrs := field.ErrorList{}
  250. if oldVal != newVal {
  251. allErrs = append(allErrs, field.Invalid(fldPath.Child("annotations", annotation), newVal, fieldImmutableErrorMsg))
  252. }
  253. return allErrs
  254. }
  255. // ValidateObjectMeta validates an object's metadata on creation. It expects that name generation has already
  256. // been performed.
  257. // It doesn't return an error for rootscoped resources with namespace, because namespace should already be cleared before.
  258. // TODO: Remove calls to this method scattered in validations of specific resources, e.g., ValidatePodUpdate.
  259. func ValidateObjectMeta(meta *metav1.ObjectMeta, requiresNamespace bool, nameFn ValidateNameFunc, fldPath *field.Path) field.ErrorList {
  260. allErrs := apimachineryvalidation.ValidateObjectMeta(meta, requiresNamespace, apimachineryvalidation.ValidateNameFunc(nameFn), fldPath)
  261. // run additional checks for the finalizer name
  262. for i := range meta.Finalizers {
  263. allErrs = append(allErrs, validateKubeFinalizerName(string(meta.Finalizers[i]), fldPath.Child("finalizers").Index(i))...)
  264. }
  265. return allErrs
  266. }
  267. // ValidateObjectMetaUpdate validates an object's metadata when updated
  268. func ValidateObjectMetaUpdate(newMeta, oldMeta *metav1.ObjectMeta, fldPath *field.Path) field.ErrorList {
  269. allErrs := apimachineryvalidation.ValidateObjectMetaUpdate(newMeta, oldMeta, fldPath)
  270. // run additional checks for the finalizer name
  271. for i := range newMeta.Finalizers {
  272. allErrs = append(allErrs, validateKubeFinalizerName(string(newMeta.Finalizers[i]), fldPath.Child("finalizers").Index(i))...)
  273. }
  274. return allErrs
  275. }
  276. func ValidateVolumes(volumes []core.Volume, fldPath *field.Path) (map[string]core.VolumeSource, field.ErrorList) {
  277. allErrs := field.ErrorList{}
  278. allNames := sets.String{}
  279. vols := make(map[string]core.VolumeSource)
  280. for i, vol := range volumes {
  281. idxPath := fldPath.Index(i)
  282. namePath := idxPath.Child("name")
  283. el := validateVolumeSource(&vol.VolumeSource, idxPath, vol.Name)
  284. if len(vol.Name) == 0 {
  285. el = append(el, field.Required(namePath, ""))
  286. } else {
  287. el = append(el, ValidateDNS1123Label(vol.Name, namePath)...)
  288. }
  289. if allNames.Has(vol.Name) {
  290. el = append(el, field.Duplicate(namePath, vol.Name))
  291. }
  292. if len(el) == 0 {
  293. allNames.Insert(vol.Name)
  294. vols[vol.Name] = vol.VolumeSource
  295. } else {
  296. allErrs = append(allErrs, el...)
  297. }
  298. }
  299. return vols, allErrs
  300. }
  301. func IsMatchedVolume(name string, volumes map[string]core.VolumeSource) bool {
  302. if _, ok := volumes[name]; ok {
  303. return true
  304. }
  305. return false
  306. }
  307. func isMatchedDevice(name string, volumes map[string]core.VolumeSource) (bool, bool) {
  308. if source, ok := volumes[name]; ok {
  309. if source.PersistentVolumeClaim != nil {
  310. return true, true
  311. }
  312. return true, false
  313. }
  314. return false, false
  315. }
  316. func mountNameAlreadyExists(name string, devices map[string]string) bool {
  317. if _, ok := devices[name]; ok {
  318. return true
  319. }
  320. return false
  321. }
  322. func mountPathAlreadyExists(mountPath string, devices map[string]string) bool {
  323. for _, devPath := range devices {
  324. if mountPath == devPath {
  325. return true
  326. }
  327. }
  328. return false
  329. }
  330. func deviceNameAlreadyExists(name string, mounts map[string]string) bool {
  331. if _, ok := mounts[name]; ok {
  332. return true
  333. }
  334. return false
  335. }
  336. func devicePathAlreadyExists(devicePath string, mounts map[string]string) bool {
  337. for _, mountPath := range mounts {
  338. if mountPath == devicePath {
  339. return true
  340. }
  341. }
  342. return false
  343. }
  344. func validateVolumeSource(source *core.VolumeSource, fldPath *field.Path, volName string) field.ErrorList {
  345. numVolumes := 0
  346. allErrs := field.ErrorList{}
  347. if source.EmptyDir != nil {
  348. numVolumes++
  349. if source.EmptyDir.SizeLimit != nil && source.EmptyDir.SizeLimit.Cmp(resource.Quantity{}) < 0 {
  350. allErrs = append(allErrs, field.Forbidden(fldPath.Child("emptyDir").Child("sizeLimit"), "SizeLimit field must be a valid resource quantity"))
  351. }
  352. }
  353. if source.HostPath != nil {
  354. if numVolumes > 0 {
  355. allErrs = append(allErrs, field.Forbidden(fldPath.Child("hostPath"), "may not specify more than 1 volume type"))
  356. } else {
  357. numVolumes++
  358. allErrs = append(allErrs, validateHostPathVolumeSource(source.HostPath, fldPath.Child("hostPath"))...)
  359. }
  360. }
  361. if source.GitRepo != nil {
  362. if numVolumes > 0 {
  363. allErrs = append(allErrs, field.Forbidden(fldPath.Child("gitRepo"), "may not specify more than 1 volume type"))
  364. } else {
  365. numVolumes++
  366. allErrs = append(allErrs, validateGitRepoVolumeSource(source.GitRepo, fldPath.Child("gitRepo"))...)
  367. }
  368. }
  369. if source.GCEPersistentDisk != nil {
  370. if numVolumes > 0 {
  371. allErrs = append(allErrs, field.Forbidden(fldPath.Child("gcePersistentDisk"), "may not specify more than 1 volume type"))
  372. } else {
  373. numVolumes++
  374. allErrs = append(allErrs, validateGCEPersistentDiskVolumeSource(source.GCEPersistentDisk, fldPath.Child("persistentDisk"))...)
  375. }
  376. }
  377. if source.AWSElasticBlockStore != nil {
  378. if numVolumes > 0 {
  379. allErrs = append(allErrs, field.Forbidden(fldPath.Child("awsElasticBlockStore"), "may not specify more than 1 volume type"))
  380. } else {
  381. numVolumes++
  382. allErrs = append(allErrs, validateAWSElasticBlockStoreVolumeSource(source.AWSElasticBlockStore, fldPath.Child("awsElasticBlockStore"))...)
  383. }
  384. }
  385. if source.Secret != nil {
  386. if numVolumes > 0 {
  387. allErrs = append(allErrs, field.Forbidden(fldPath.Child("secret"), "may not specify more than 1 volume type"))
  388. } else {
  389. numVolumes++
  390. allErrs = append(allErrs, validateSecretVolumeSource(source.Secret, fldPath.Child("secret"))...)
  391. }
  392. }
  393. if source.NFS != nil {
  394. if numVolumes > 0 {
  395. allErrs = append(allErrs, field.Forbidden(fldPath.Child("nfs"), "may not specify more than 1 volume type"))
  396. } else {
  397. numVolumes++
  398. allErrs = append(allErrs, validateNFSVolumeSource(source.NFS, fldPath.Child("nfs"))...)
  399. }
  400. }
  401. if source.ISCSI != nil {
  402. if numVolumes > 0 {
  403. allErrs = append(allErrs, field.Forbidden(fldPath.Child("iscsi"), "may not specify more than 1 volume type"))
  404. } else {
  405. numVolumes++
  406. allErrs = append(allErrs, validateISCSIVolumeSource(source.ISCSI, fldPath.Child("iscsi"))...)
  407. }
  408. if source.ISCSI.InitiatorName != nil && len(volName+":"+source.ISCSI.TargetPortal) > 64 {
  409. tooLongErr := "Total length of <volume name>:<iscsi.targetPortal> must be under 64 characters if iscsi.initiatorName is specified."
  410. allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), volName, tooLongErr))
  411. }
  412. }
  413. if source.Glusterfs != nil {
  414. if numVolumes > 0 {
  415. allErrs = append(allErrs, field.Forbidden(fldPath.Child("glusterfs"), "may not specify more than 1 volume type"))
  416. } else {
  417. numVolumes++
  418. allErrs = append(allErrs, validateGlusterfsVolumeSource(source.Glusterfs, fldPath.Child("glusterfs"))...)
  419. }
  420. }
  421. if source.Flocker != nil {
  422. if numVolumes > 0 {
  423. allErrs = append(allErrs, field.Forbidden(fldPath.Child("flocker"), "may not specify more than 1 volume type"))
  424. } else {
  425. numVolumes++
  426. allErrs = append(allErrs, validateFlockerVolumeSource(source.Flocker, fldPath.Child("flocker"))...)
  427. }
  428. }
  429. if source.PersistentVolumeClaim != nil {
  430. if numVolumes > 0 {
  431. allErrs = append(allErrs, field.Forbidden(fldPath.Child("persistentVolumeClaim"), "may not specify more than 1 volume type"))
  432. } else {
  433. numVolumes++
  434. allErrs = append(allErrs, validatePersistentClaimVolumeSource(source.PersistentVolumeClaim, fldPath.Child("persistentVolumeClaim"))...)
  435. }
  436. }
  437. if source.RBD != nil {
  438. if numVolumes > 0 {
  439. allErrs = append(allErrs, field.Forbidden(fldPath.Child("rbd"), "may not specify more than 1 volume type"))
  440. } else {
  441. numVolumes++
  442. allErrs = append(allErrs, validateRBDVolumeSource(source.RBD, fldPath.Child("rbd"))...)
  443. }
  444. }
  445. if source.Cinder != nil {
  446. if numVolumes > 0 {
  447. allErrs = append(allErrs, field.Forbidden(fldPath.Child("cinder"), "may not specify more than 1 volume type"))
  448. } else {
  449. numVolumes++
  450. allErrs = append(allErrs, validateCinderVolumeSource(source.Cinder, fldPath.Child("cinder"))...)
  451. }
  452. }
  453. if source.CephFS != nil {
  454. if numVolumes > 0 {
  455. allErrs = append(allErrs, field.Forbidden(fldPath.Child("cephFS"), "may not specify more than 1 volume type"))
  456. } else {
  457. numVolumes++
  458. allErrs = append(allErrs, validateCephFSVolumeSource(source.CephFS, fldPath.Child("cephfs"))...)
  459. }
  460. }
  461. if source.Quobyte != nil {
  462. if numVolumes > 0 {
  463. allErrs = append(allErrs, field.Forbidden(fldPath.Child("quobyte"), "may not specify more than 1 volume type"))
  464. } else {
  465. numVolumes++
  466. allErrs = append(allErrs, validateQuobyteVolumeSource(source.Quobyte, fldPath.Child("quobyte"))...)
  467. }
  468. }
  469. if source.DownwardAPI != nil {
  470. if numVolumes > 0 {
  471. allErrs = append(allErrs, field.Forbidden(fldPath.Child("downwarAPI"), "may not specify more than 1 volume type"))
  472. } else {
  473. numVolumes++
  474. allErrs = append(allErrs, validateDownwardAPIVolumeSource(source.DownwardAPI, fldPath.Child("downwardAPI"))...)
  475. }
  476. }
  477. if source.FC != nil {
  478. if numVolumes > 0 {
  479. allErrs = append(allErrs, field.Forbidden(fldPath.Child("fc"), "may not specify more than 1 volume type"))
  480. } else {
  481. numVolumes++
  482. allErrs = append(allErrs, validateFCVolumeSource(source.FC, fldPath.Child("fc"))...)
  483. }
  484. }
  485. if source.FlexVolume != nil {
  486. if numVolumes > 0 {
  487. allErrs = append(allErrs, field.Forbidden(fldPath.Child("flexVolume"), "may not specify more than 1 volume type"))
  488. } else {
  489. numVolumes++
  490. allErrs = append(allErrs, validateFlexVolumeSource(source.FlexVolume, fldPath.Child("flexVolume"))...)
  491. }
  492. }
  493. if source.ConfigMap != nil {
  494. if numVolumes > 0 {
  495. allErrs = append(allErrs, field.Forbidden(fldPath.Child("configMap"), "may not specify more than 1 volume type"))
  496. } else {
  497. numVolumes++
  498. allErrs = append(allErrs, validateConfigMapVolumeSource(source.ConfigMap, fldPath.Child("configMap"))...)
  499. }
  500. }
  501. if source.AzureFile != nil {
  502. if numVolumes > 0 {
  503. allErrs = append(allErrs, field.Forbidden(fldPath.Child("azureFile"), "may not specify more than 1 volume type"))
  504. } else {
  505. numVolumes++
  506. allErrs = append(allErrs, validateAzureFile(source.AzureFile, fldPath.Child("azureFile"))...)
  507. }
  508. }
  509. if source.VsphereVolume != nil {
  510. if numVolumes > 0 {
  511. allErrs = append(allErrs, field.Forbidden(fldPath.Child("vsphereVolume"), "may not specify more than 1 volume type"))
  512. } else {
  513. numVolumes++
  514. allErrs = append(allErrs, validateVsphereVolumeSource(source.VsphereVolume, fldPath.Child("vsphereVolume"))...)
  515. }
  516. }
  517. if source.PhotonPersistentDisk != nil {
  518. if numVolumes > 0 {
  519. allErrs = append(allErrs, field.Forbidden(fldPath.Child("photonPersistentDisk"), "may not specify more than 1 volume type"))
  520. } else {
  521. numVolumes++
  522. allErrs = append(allErrs, validatePhotonPersistentDiskVolumeSource(source.PhotonPersistentDisk, fldPath.Child("photonPersistentDisk"))...)
  523. }
  524. }
  525. if source.PortworxVolume != nil {
  526. if numVolumes > 0 {
  527. allErrs = append(allErrs, field.Forbidden(fldPath.Child("portworxVolume"), "may not specify more than 1 volume type"))
  528. } else {
  529. numVolumes++
  530. allErrs = append(allErrs, validatePortworxVolumeSource(source.PortworxVolume, fldPath.Child("portworxVolume"))...)
  531. }
  532. }
  533. if source.AzureDisk != nil {
  534. if numVolumes > 0 {
  535. allErrs = append(allErrs, field.Forbidden(fldPath.Child("azureDisk"), "may not specify more than 1 volume type"))
  536. } else {
  537. numVolumes++
  538. allErrs = append(allErrs, validateAzureDisk(source.AzureDisk, fldPath.Child("azureDisk"))...)
  539. }
  540. }
  541. if source.StorageOS != nil {
  542. if numVolumes > 0 {
  543. allErrs = append(allErrs, field.Forbidden(fldPath.Child("storageos"), "may not specify more than 1 volume type"))
  544. } else {
  545. numVolumes++
  546. allErrs = append(allErrs, validateStorageOSVolumeSource(source.StorageOS, fldPath.Child("storageos"))...)
  547. }
  548. }
  549. if source.Projected != nil {
  550. if numVolumes > 0 {
  551. allErrs = append(allErrs, field.Forbidden(fldPath.Child("projected"), "may not specify more than 1 volume type"))
  552. } else {
  553. numVolumes++
  554. allErrs = append(allErrs, validateProjectedVolumeSource(source.Projected, fldPath.Child("projected"))...)
  555. }
  556. }
  557. if source.ScaleIO != nil {
  558. if numVolumes > 0 {
  559. allErrs = append(allErrs, field.Forbidden(fldPath.Child("scaleIO"), "may not specify more than 1 volume type"))
  560. } else {
  561. numVolumes++
  562. allErrs = append(allErrs, validateScaleIOVolumeSource(source.ScaleIO, fldPath.Child("scaleIO"))...)
  563. }
  564. }
  565. if source.CSI != nil {
  566. if numVolumes > 0 {
  567. allErrs = append(allErrs, field.Forbidden(fldPath.Child("csi"), "may not specify more than 1 volume type"))
  568. } else {
  569. numVolumes++
  570. allErrs = append(allErrs, validateCSIVolumeSource(source.CSI, fldPath.Child("csi"))...)
  571. }
  572. }
  573. if numVolumes == 0 {
  574. allErrs = append(allErrs, field.Required(fldPath, "must specify a volume type"))
  575. }
  576. return allErrs
  577. }
  578. func validateHostPathVolumeSource(hostPath *core.HostPathVolumeSource, fldPath *field.Path) field.ErrorList {
  579. allErrs := field.ErrorList{}
  580. if len(hostPath.Path) == 0 {
  581. allErrs = append(allErrs, field.Required(fldPath.Child("path"), ""))
  582. return allErrs
  583. }
  584. allErrs = append(allErrs, validatePathNoBacksteps(hostPath.Path, fldPath.Child("path"))...)
  585. allErrs = append(allErrs, validateHostPathType(hostPath.Type, fldPath.Child("type"))...)
  586. return allErrs
  587. }
  588. func validateGitRepoVolumeSource(gitRepo *core.GitRepoVolumeSource, fldPath *field.Path) field.ErrorList {
  589. allErrs := field.ErrorList{}
  590. if len(gitRepo.Repository) == 0 {
  591. allErrs = append(allErrs, field.Required(fldPath.Child("repository"), ""))
  592. }
  593. pathErrs := validateLocalDescendingPath(gitRepo.Directory, fldPath.Child("directory"))
  594. allErrs = append(allErrs, pathErrs...)
  595. return allErrs
  596. }
  597. func validateISCSIVolumeSource(iscsi *core.ISCSIVolumeSource, fldPath *field.Path) field.ErrorList {
  598. allErrs := field.ErrorList{}
  599. if len(iscsi.TargetPortal) == 0 {
  600. allErrs = append(allErrs, field.Required(fldPath.Child("targetPortal"), ""))
  601. }
  602. if len(iscsi.IQN) == 0 {
  603. allErrs = append(allErrs, field.Required(fldPath.Child("iqn"), ""))
  604. } else {
  605. if !strings.HasPrefix(iscsi.IQN, "iqn") && !strings.HasPrefix(iscsi.IQN, "eui") && !strings.HasPrefix(iscsi.IQN, "naa") {
  606. allErrs = append(allErrs, field.Invalid(fldPath.Child("iqn"), iscsi.IQN, "must be valid format starting with iqn, eui, or naa"))
  607. } else if strings.HasPrefix(iscsi.IQN, "iqn") && !iscsiInitiatorIqnRegex.MatchString(iscsi.IQN) {
  608. allErrs = append(allErrs, field.Invalid(fldPath.Child("iqn"), iscsi.IQN, "must be valid format"))
  609. } else if strings.HasPrefix(iscsi.IQN, "eui") && !iscsiInitiatorEuiRegex.MatchString(iscsi.IQN) {
  610. allErrs = append(allErrs, field.Invalid(fldPath.Child("iqn"), iscsi.IQN, "must be valid format"))
  611. } else if strings.HasPrefix(iscsi.IQN, "naa") && !iscsiInitiatorNaaRegex.MatchString(iscsi.IQN) {
  612. allErrs = append(allErrs, field.Invalid(fldPath.Child("iqn"), iscsi.IQN, "must be valid format"))
  613. }
  614. }
  615. if iscsi.Lun < 0 || iscsi.Lun > 255 {
  616. allErrs = append(allErrs, field.Invalid(fldPath.Child("lun"), iscsi.Lun, validation.InclusiveRangeError(0, 255)))
  617. }
  618. if (iscsi.DiscoveryCHAPAuth || iscsi.SessionCHAPAuth) && iscsi.SecretRef == nil {
  619. allErrs = append(allErrs, field.Required(fldPath.Child("secretRef"), ""))
  620. }
  621. if iscsi.InitiatorName != nil {
  622. initiator := *iscsi.InitiatorName
  623. if !strings.HasPrefix(initiator, "iqn") && !strings.HasPrefix(initiator, "eui") && !strings.HasPrefix(initiator, "naa") {
  624. allErrs = append(allErrs, field.Invalid(fldPath.Child("initiatorname"), initiator, "must be valid format starting with iqn, eui, or naa"))
  625. }
  626. if strings.HasPrefix(initiator, "iqn") && !iscsiInitiatorIqnRegex.MatchString(initiator) {
  627. allErrs = append(allErrs, field.Invalid(fldPath.Child("initiatorname"), initiator, "must be valid format"))
  628. } else if strings.HasPrefix(initiator, "eui") && !iscsiInitiatorEuiRegex.MatchString(initiator) {
  629. allErrs = append(allErrs, field.Invalid(fldPath.Child("initiatorname"), initiator, "must be valid format"))
  630. } else if strings.HasPrefix(initiator, "naa") && !iscsiInitiatorNaaRegex.MatchString(initiator) {
  631. allErrs = append(allErrs, field.Invalid(fldPath.Child("initiatorname"), initiator, "must be valid format"))
  632. }
  633. }
  634. return allErrs
  635. }
  636. func validateISCSIPersistentVolumeSource(iscsi *core.ISCSIPersistentVolumeSource, pvName string, fldPath *field.Path) field.ErrorList {
  637. allErrs := field.ErrorList{}
  638. if len(iscsi.TargetPortal) == 0 {
  639. allErrs = append(allErrs, field.Required(fldPath.Child("targetPortal"), ""))
  640. }
  641. if iscsi.InitiatorName != nil && len(pvName+":"+iscsi.TargetPortal) > 64 {
  642. tooLongErr := "Total length of <volume name>:<iscsi.targetPortal> must be under 64 characters if iscsi.initiatorName is specified."
  643. allErrs = append(allErrs, field.Invalid(fldPath.Child("targetportal"), iscsi.TargetPortal, tooLongErr))
  644. }
  645. if len(iscsi.IQN) == 0 {
  646. allErrs = append(allErrs, field.Required(fldPath.Child("iqn"), ""))
  647. } else {
  648. if !strings.HasPrefix(iscsi.IQN, "iqn") && !strings.HasPrefix(iscsi.IQN, "eui") && !strings.HasPrefix(iscsi.IQN, "naa") {
  649. allErrs = append(allErrs, field.Invalid(fldPath.Child("iqn"), iscsi.IQN, "must be valid format"))
  650. } else if strings.HasPrefix(iscsi.IQN, "iqn") && !iscsiInitiatorIqnRegex.MatchString(iscsi.IQN) {
  651. allErrs = append(allErrs, field.Invalid(fldPath.Child("iqn"), iscsi.IQN, "must be valid format"))
  652. } else if strings.HasPrefix(iscsi.IQN, "eui") && !iscsiInitiatorEuiRegex.MatchString(iscsi.IQN) {
  653. allErrs = append(allErrs, field.Invalid(fldPath.Child("iqn"), iscsi.IQN, "must be valid format"))
  654. } else if strings.HasPrefix(iscsi.IQN, "naa") && !iscsiInitiatorNaaRegex.MatchString(iscsi.IQN) {
  655. allErrs = append(allErrs, field.Invalid(fldPath.Child("iqn"), iscsi.IQN, "must be valid format"))
  656. }
  657. }
  658. if iscsi.Lun < 0 || iscsi.Lun > 255 {
  659. allErrs = append(allErrs, field.Invalid(fldPath.Child("lun"), iscsi.Lun, validation.InclusiveRangeError(0, 255)))
  660. }
  661. if (iscsi.DiscoveryCHAPAuth || iscsi.SessionCHAPAuth) && iscsi.SecretRef == nil {
  662. allErrs = append(allErrs, field.Required(fldPath.Child("secretRef"), ""))
  663. }
  664. if iscsi.SecretRef != nil {
  665. if len(iscsi.SecretRef.Name) == 0 {
  666. allErrs = append(allErrs, field.Required(fldPath.Child("secretRef", "name"), ""))
  667. }
  668. }
  669. if iscsi.InitiatorName != nil {
  670. initiator := *iscsi.InitiatorName
  671. if !strings.HasPrefix(initiator, "iqn") && !strings.HasPrefix(initiator, "eui") && !strings.HasPrefix(initiator, "naa") {
  672. allErrs = append(allErrs, field.Invalid(fldPath.Child("initiatorname"), initiator, "must be valid format"))
  673. }
  674. if strings.HasPrefix(initiator, "iqn") && !iscsiInitiatorIqnRegex.MatchString(initiator) {
  675. allErrs = append(allErrs, field.Invalid(fldPath.Child("initiatorname"), initiator, "must be valid format"))
  676. } else if strings.HasPrefix(initiator, "eui") && !iscsiInitiatorEuiRegex.MatchString(initiator) {
  677. allErrs = append(allErrs, field.Invalid(fldPath.Child("initiatorname"), initiator, "must be valid format"))
  678. } else if strings.HasPrefix(initiator, "naa") && !iscsiInitiatorNaaRegex.MatchString(initiator) {
  679. allErrs = append(allErrs, field.Invalid(fldPath.Child("initiatorname"), initiator, "must be valid format"))
  680. }
  681. }
  682. return allErrs
  683. }
  684. func validateFCVolumeSource(fc *core.FCVolumeSource, fldPath *field.Path) field.ErrorList {
  685. allErrs := field.ErrorList{}
  686. if len(fc.TargetWWNs) < 1 && len(fc.WWIDs) < 1 {
  687. allErrs = append(allErrs, field.Required(fldPath.Child("targetWWNs"), "must specify either targetWWNs or wwids, but not both"))
  688. }
  689. if len(fc.TargetWWNs) != 0 && len(fc.WWIDs) != 0 {
  690. allErrs = append(allErrs, field.Invalid(fldPath.Child("targetWWNs"), fc.TargetWWNs, "targetWWNs and wwids can not be specified simultaneously"))
  691. }
  692. if len(fc.TargetWWNs) != 0 {
  693. if fc.Lun == nil {
  694. allErrs = append(allErrs, field.Required(fldPath.Child("lun"), "lun is required if targetWWNs is specified"))
  695. } else {
  696. if *fc.Lun < 0 || *fc.Lun > 255 {
  697. allErrs = append(allErrs, field.Invalid(fldPath.Child("lun"), fc.Lun, validation.InclusiveRangeError(0, 255)))
  698. }
  699. }
  700. }
  701. return allErrs
  702. }
  703. func validateGCEPersistentDiskVolumeSource(pd *core.GCEPersistentDiskVolumeSource, fldPath *field.Path) field.ErrorList {
  704. allErrs := field.ErrorList{}
  705. if len(pd.PDName) == 0 {
  706. allErrs = append(allErrs, field.Required(fldPath.Child("pdName"), ""))
  707. }
  708. if pd.Partition < 0 || pd.Partition > 255 {
  709. allErrs = append(allErrs, field.Invalid(fldPath.Child("partition"), pd.Partition, pdPartitionErrorMsg))
  710. }
  711. return allErrs
  712. }
  713. func validateAWSElasticBlockStoreVolumeSource(PD *core.AWSElasticBlockStoreVolumeSource, fldPath *field.Path) field.ErrorList {
  714. allErrs := field.ErrorList{}
  715. if len(PD.VolumeID) == 0 {
  716. allErrs = append(allErrs, field.Required(fldPath.Child("volumeID"), ""))
  717. }
  718. if PD.Partition < 0 || PD.Partition > 255 {
  719. allErrs = append(allErrs, field.Invalid(fldPath.Child("partition"), PD.Partition, pdPartitionErrorMsg))
  720. }
  721. return allErrs
  722. }
  723. func validateSecretVolumeSource(secretSource *core.SecretVolumeSource, fldPath *field.Path) field.ErrorList {
  724. allErrs := field.ErrorList{}
  725. if len(secretSource.SecretName) == 0 {
  726. allErrs = append(allErrs, field.Required(fldPath.Child("secretName"), ""))
  727. }
  728. secretMode := secretSource.DefaultMode
  729. if secretMode != nil && (*secretMode > 0777 || *secretMode < 0) {
  730. allErrs = append(allErrs, field.Invalid(fldPath.Child("defaultMode"), *secretMode, fileModeErrorMsg))
  731. }
  732. itemsPath := fldPath.Child("items")
  733. for i, kp := range secretSource.Items {
  734. itemPath := itemsPath.Index(i)
  735. allErrs = append(allErrs, validateKeyToPath(&kp, itemPath)...)
  736. }
  737. return allErrs
  738. }
  739. func validateConfigMapVolumeSource(configMapSource *core.ConfigMapVolumeSource, fldPath *field.Path) field.ErrorList {
  740. allErrs := field.ErrorList{}
  741. if len(configMapSource.Name) == 0 {
  742. allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
  743. }
  744. configMapMode := configMapSource.DefaultMode
  745. if configMapMode != nil && (*configMapMode > 0777 || *configMapMode < 0) {
  746. allErrs = append(allErrs, field.Invalid(fldPath.Child("defaultMode"), *configMapMode, fileModeErrorMsg))
  747. }
  748. itemsPath := fldPath.Child("items")
  749. for i, kp := range configMapSource.Items {
  750. itemPath := itemsPath.Index(i)
  751. allErrs = append(allErrs, validateKeyToPath(&kp, itemPath)...)
  752. }
  753. return allErrs
  754. }
  755. func validateKeyToPath(kp *core.KeyToPath, fldPath *field.Path) field.ErrorList {
  756. allErrs := field.ErrorList{}
  757. if len(kp.Key) == 0 {
  758. allErrs = append(allErrs, field.Required(fldPath.Child("key"), ""))
  759. }
  760. if len(kp.Path) == 0 {
  761. allErrs = append(allErrs, field.Required(fldPath.Child("path"), ""))
  762. }
  763. allErrs = append(allErrs, validateLocalNonReservedPath(kp.Path, fldPath.Child("path"))...)
  764. if kp.Mode != nil && (*kp.Mode > 0777 || *kp.Mode < 0) {
  765. allErrs = append(allErrs, field.Invalid(fldPath.Child("mode"), *kp.Mode, fileModeErrorMsg))
  766. }
  767. return allErrs
  768. }
  769. func validatePersistentClaimVolumeSource(claim *core.PersistentVolumeClaimVolumeSource, fldPath *field.Path) field.ErrorList {
  770. allErrs := field.ErrorList{}
  771. if len(claim.ClaimName) == 0 {
  772. allErrs = append(allErrs, field.Required(fldPath.Child("claimName"), ""))
  773. }
  774. return allErrs
  775. }
  776. func validateNFSVolumeSource(nfs *core.NFSVolumeSource, fldPath *field.Path) field.ErrorList {
  777. allErrs := field.ErrorList{}
  778. if len(nfs.Server) == 0 {
  779. allErrs = append(allErrs, field.Required(fldPath.Child("server"), ""))
  780. }
  781. if len(nfs.Path) == 0 {
  782. allErrs = append(allErrs, field.Required(fldPath.Child("path"), ""))
  783. }
  784. if !path.IsAbs(nfs.Path) {
  785. allErrs = append(allErrs, field.Invalid(fldPath.Child("path"), nfs.Path, "must be an absolute path"))
  786. }
  787. return allErrs
  788. }
  789. func validateQuobyteVolumeSource(quobyte *core.QuobyteVolumeSource, fldPath *field.Path) field.ErrorList {
  790. allErrs := field.ErrorList{}
  791. if len(quobyte.Registry) == 0 {
  792. allErrs = append(allErrs, field.Required(fldPath.Child("registry"), "must be a host:port pair or multiple pairs separated by commas"))
  793. } else if len(quobyte.Tenant) >= 65 {
  794. allErrs = append(allErrs, field.Required(fldPath.Child("tenant"), "must be a UUID and may not exceed a length of 64 characters"))
  795. } else {
  796. for _, hostPortPair := range strings.Split(quobyte.Registry, ",") {
  797. if _, _, err := net.SplitHostPort(hostPortPair); err != nil {
  798. allErrs = append(allErrs, field.Invalid(fldPath.Child("registry"), quobyte.Registry, "must be a host:port pair or multiple pairs separated by commas"))
  799. }
  800. }
  801. }
  802. if len(quobyte.Volume) == 0 {
  803. allErrs = append(allErrs, field.Required(fldPath.Child("volume"), ""))
  804. }
  805. return allErrs
  806. }
  807. func validateGlusterfsVolumeSource(glusterfs *core.GlusterfsVolumeSource, fldPath *field.Path) field.ErrorList {
  808. allErrs := field.ErrorList{}
  809. if len(glusterfs.EndpointsName) == 0 {
  810. allErrs = append(allErrs, field.Required(fldPath.Child("endpoints"), ""))
  811. }
  812. if len(glusterfs.Path) == 0 {
  813. allErrs = append(allErrs, field.Required(fldPath.Child("path"), ""))
  814. }
  815. return allErrs
  816. }
  817. func validateGlusterfsPersistentVolumeSource(glusterfs *core.GlusterfsPersistentVolumeSource, fldPath *field.Path) field.ErrorList {
  818. allErrs := field.ErrorList{}
  819. if len(glusterfs.EndpointsName) == 0 {
  820. allErrs = append(allErrs, field.Required(fldPath.Child("endpoints"), ""))
  821. }
  822. if len(glusterfs.Path) == 0 {
  823. allErrs = append(allErrs, field.Required(fldPath.Child("path"), ""))
  824. }
  825. if glusterfs.EndpointsNamespace != nil {
  826. endpointNs := glusterfs.EndpointsNamespace
  827. if *endpointNs == "" {
  828. allErrs = append(allErrs, field.Invalid(fldPath.Child("endpointsNamespace"), *endpointNs, "if the endpointnamespace is set, it must be a valid namespace name"))
  829. } else {
  830. for _, msg := range ValidateNamespaceName(*endpointNs, false) {
  831. allErrs = append(allErrs, field.Invalid(fldPath.Child("endpointsNamespace"), *endpointNs, msg))
  832. }
  833. }
  834. }
  835. return allErrs
  836. }
  837. func validateFlockerVolumeSource(flocker *core.FlockerVolumeSource, fldPath *field.Path) field.ErrorList {
  838. allErrs := field.ErrorList{}
  839. if len(flocker.DatasetName) == 0 && len(flocker.DatasetUUID) == 0 {
  840. //TODO: consider adding a RequiredOneOf() error for this and similar cases
  841. allErrs = append(allErrs, field.Required(fldPath, "one of datasetName and datasetUUID is required"))
  842. }
  843. if len(flocker.DatasetName) != 0 && len(flocker.DatasetUUID) != 0 {
  844. allErrs = append(allErrs, field.Invalid(fldPath, "resource", "datasetName and datasetUUID can not be specified simultaneously"))
  845. }
  846. if strings.Contains(flocker.DatasetName, "/") {
  847. allErrs = append(allErrs, field.Invalid(fldPath.Child("datasetName"), flocker.DatasetName, "must not contain '/'"))
  848. }
  849. return allErrs
  850. }
  851. var validVolumeDownwardAPIFieldPathExpressions = sets.NewString(
  852. "metadata.name",
  853. "metadata.namespace",
  854. "metadata.labels",
  855. "metadata.annotations",
  856. "metadata.uid")
  857. func validateDownwardAPIVolumeFile(file *core.DownwardAPIVolumeFile, fldPath *field.Path) field.ErrorList {
  858. allErrs := field.ErrorList{}
  859. if len(file.Path) == 0 {
  860. allErrs = append(allErrs, field.Required(fldPath.Child("path"), ""))
  861. }
  862. allErrs = append(allErrs, validateLocalNonReservedPath(file.Path, fldPath.Child("path"))...)
  863. if file.FieldRef != nil {
  864. allErrs = append(allErrs, validateObjectFieldSelector(file.FieldRef, &validVolumeDownwardAPIFieldPathExpressions, fldPath.Child("fieldRef"))...)
  865. if file.ResourceFieldRef != nil {
  866. allErrs = append(allErrs, field.Invalid(fldPath, "resource", "fieldRef and resourceFieldRef can not be specified simultaneously"))
  867. }
  868. } else if file.ResourceFieldRef != nil {
  869. allErrs = append(allErrs, validateContainerResourceFieldSelector(file.ResourceFieldRef, &validContainerResourceFieldPathExpressions, fldPath.Child("resourceFieldRef"), true)...)
  870. } else {
  871. allErrs = append(allErrs, field.Required(fldPath, "one of fieldRef and resourceFieldRef is required"))
  872. }
  873. if file.Mode != nil && (*file.Mode > 0777 || *file.Mode < 0) {
  874. allErrs = append(allErrs, field.Invalid(fldPath.Child("mode"), *file.Mode, fileModeErrorMsg))
  875. }
  876. return allErrs
  877. }
  878. func validateDownwardAPIVolumeSource(downwardAPIVolume *core.DownwardAPIVolumeSource, fldPath *field.Path) field.ErrorList {
  879. allErrs := field.ErrorList{}
  880. downwardAPIMode := downwardAPIVolume.DefaultMode
  881. if downwardAPIMode != nil && (*downwardAPIMode > 0777 || *downwardAPIMode < 0) {
  882. allErrs = append(allErrs, field.Invalid(fldPath.Child("defaultMode"), *downwardAPIMode, fileModeErrorMsg))
  883. }
  884. for _, file := range downwardAPIVolume.Items {
  885. allErrs = append(allErrs, validateDownwardAPIVolumeFile(&file, fldPath)...)
  886. }
  887. return allErrs
  888. }
  889. func validateProjectionSources(projection *core.ProjectedVolumeSource, projectionMode *int32, fldPath *field.Path) field.ErrorList {
  890. allErrs := field.ErrorList{}
  891. allPaths := sets.String{}
  892. for i, source := range projection.Sources {
  893. numSources := 0
  894. srcPath := fldPath.Child("sources").Index(i)
  895. if projPath := srcPath.Child("secret"); source.Secret != nil {
  896. numSources++
  897. if len(source.Secret.Name) == 0 {
  898. allErrs = append(allErrs, field.Required(projPath.Child("name"), ""))
  899. }
  900. itemsPath := projPath.Child("items")
  901. for i, kp := range source.Secret.Items {
  902. itemPath := itemsPath.Index(i)
  903. allErrs = append(allErrs, validateKeyToPath(&kp, itemPath)...)
  904. if len(kp.Path) > 0 {
  905. curPath := kp.Path
  906. if !allPaths.Has(curPath) {
  907. allPaths.Insert(curPath)
  908. } else {
  909. allErrs = append(allErrs, field.Invalid(fldPath, source.Secret.Name, "conflicting duplicate paths"))
  910. }
  911. }
  912. }
  913. }
  914. if projPath := srcPath.Child("configMap"); source.ConfigMap != nil {
  915. numSources++
  916. if len(source.ConfigMap.Name) == 0 {
  917. allErrs = append(allErrs, field.Required(projPath.Child("name"), ""))
  918. }
  919. itemsPath := projPath.Child("items")
  920. for i, kp := range source.ConfigMap.Items {
  921. itemPath := itemsPath.Index(i)
  922. allErrs = append(allErrs, validateKeyToPath(&kp, itemPath)...)
  923. if len(kp.Path) > 0 {
  924. curPath := kp.Path
  925. if !allPaths.Has(curPath) {
  926. allPaths.Insert(curPath)
  927. } else {
  928. allErrs = append(allErrs, field.Invalid(fldPath, source.ConfigMap.Name, "conflicting duplicate paths"))
  929. }
  930. }
  931. }
  932. }
  933. if projPath := srcPath.Child("downwardAPI"); source.DownwardAPI != nil {
  934. numSources++
  935. for _, file := range source.DownwardAPI.Items {
  936. allErrs = append(allErrs, validateDownwardAPIVolumeFile(&file, projPath)...)
  937. if len(file.Path) > 0 {
  938. curPath := file.Path
  939. if !allPaths.Has(curPath) {
  940. allPaths.Insert(curPath)
  941. } else {
  942. allErrs = append(allErrs, field.Invalid(fldPath, curPath, "conflicting duplicate paths"))
  943. }
  944. }
  945. }
  946. }
  947. if projPath := srcPath.Child("serviceAccountToken"); source.ServiceAccountToken != nil {
  948. numSources++
  949. if source.ServiceAccountToken.ExpirationSeconds < 10*60 {
  950. allErrs = append(allErrs, field.Invalid(projPath.Child("expirationSeconds"), source.ServiceAccountToken.ExpirationSeconds, "may not specify a duration less than 10 minutes"))
  951. }
  952. if source.ServiceAccountToken.ExpirationSeconds > 1<<32 {
  953. allErrs = append(allErrs, field.Invalid(projPath.Child("expirationSeconds"), source.ServiceAccountToken.ExpirationSeconds, "may not specify a duration larger than 2^32 seconds"))
  954. }
  955. if source.ServiceAccountToken.Path == "" {
  956. allErrs = append(allErrs, field.Required(fldPath.Child("path"), ""))
  957. }
  958. }
  959. if numSources > 1 {
  960. allErrs = append(allErrs, field.Forbidden(srcPath, "may not specify more than 1 volume type"))
  961. }
  962. }
  963. return allErrs
  964. }
  965. func validateProjectedVolumeSource(projection *core.ProjectedVolumeSource, fldPath *field.Path) field.ErrorList {
  966. allErrs := field.ErrorList{}
  967. projectionMode := projection.DefaultMode
  968. if projectionMode != nil && (*projectionMode > 0777 || *projectionMode < 0) {
  969. allErrs = append(allErrs, field.Invalid(fldPath.Child("defaultMode"), *projectionMode, fileModeErrorMsg))
  970. }
  971. allErrs = append(allErrs, validateProjectionSources(projection, projectionMode, fldPath)...)
  972. return allErrs
  973. }
  974. var supportedHostPathTypes = sets.NewString(
  975. string(core.HostPathUnset),
  976. string(core.HostPathDirectoryOrCreate),
  977. string(core.HostPathDirectory),
  978. string(core.HostPathFileOrCreate),
  979. string(core.HostPathFile),
  980. string(core.HostPathSocket),
  981. string(core.HostPathCharDev),
  982. string(core.HostPathBlockDev))
  983. func validateHostPathType(hostPathType *core.HostPathType, fldPath *field.Path) field.ErrorList {
  984. allErrs := field.ErrorList{}
  985. if hostPathType != nil && !supportedHostPathTypes.Has(string(*hostPathType)) {
  986. allErrs = append(allErrs, field.NotSupported(fldPath, hostPathType, supportedHostPathTypes.List()))
  987. }
  988. return allErrs
  989. }
  990. // This validate will make sure targetPath:
  991. // 1. is not abs path
  992. // 2. does not have any element which is ".."
  993. func validateLocalDescendingPath(targetPath string, fldPath *field.Path) field.ErrorList {
  994. allErrs := field.ErrorList{}
  995. if path.IsAbs(targetPath) {
  996. allErrs = append(allErrs, field.Invalid(fldPath, targetPath, "must be a relative path"))
  997. }
  998. allErrs = append(allErrs, validatePathNoBacksteps(targetPath, fldPath)...)
  999. return allErrs
  1000. }
  1001. // validatePathNoBacksteps makes sure the targetPath does not have any `..` path elements when split
  1002. //
  1003. // This assumes the OS of the apiserver and the nodes are the same. The same check should be done
  1004. // on the node to ensure there are no backsteps.
  1005. func validatePathNoBacksteps(targetPath string, fldPath *field.Path) field.ErrorList {
  1006. allErrs := field.ErrorList{}
  1007. parts := strings.Split(filepath.ToSlash(targetPath), "/")
  1008. for _, item := range parts {
  1009. if item == ".." {
  1010. allErrs = append(allErrs, field.Invalid(fldPath, targetPath, "must not contain '..'"))
  1011. break // even for `../../..`, one error is sufficient to make the point
  1012. }
  1013. }
  1014. return allErrs
  1015. }
  1016. // validateMountPropagation verifies that MountPropagation field is valid and
  1017. // allowed for given container.
  1018. func validateMountPropagation(mountPropagation *core.MountPropagationMode, container *core.Container, fldPath *field.Path) field.ErrorList {
  1019. allErrs := field.ErrorList{}
  1020. if mountPropagation == nil {
  1021. return allErrs
  1022. }
  1023. supportedMountPropagations := sets.NewString(string(core.MountPropagationBidirectional), string(core.MountPropagationHostToContainer), string(core.MountPropagationNone))
  1024. if !supportedMountPropagations.Has(string(*mountPropagation)) {
  1025. allErrs = append(allErrs, field.NotSupported(fldPath, *mountPropagation, supportedMountPropagations.List()))
  1026. }
  1027. if container == nil {
  1028. // The container is not available yet, e.g. during validation of
  1029. // PodPreset. Stop validation now, Pod validation will refuse final
  1030. // Pods with Bidirectional propagation in non-privileged containers.
  1031. return allErrs
  1032. }
  1033. privileged := container.SecurityContext != nil && container.SecurityContext.Privileged != nil && *container.SecurityContext.Privileged
  1034. if *mountPropagation == core.MountPropagationBidirectional && !privileged {
  1035. allErrs = append(allErrs, field.Forbidden(fldPath, "Bidirectional mount propagation is available only to privileged containers"))
  1036. }
  1037. return allErrs
  1038. }
  1039. // This validate will make sure targetPath:
  1040. // 1. is not abs path
  1041. // 2. does not contain any '..' elements
  1042. // 3. does not start with '..'
  1043. func validateLocalNonReservedPath(targetPath string, fldPath *field.Path) field.ErrorList {
  1044. allErrs := field.ErrorList{}
  1045. allErrs = append(allErrs, validateLocalDescendingPath(targetPath, fldPath)...)
  1046. // Don't report this error if the check for .. elements already caught it.
  1047. if strings.HasPrefix(targetPath, "..") && !strings.HasPrefix(targetPath, "../") {
  1048. allErrs = append(allErrs, field.Invalid(fldPath, targetPath, "must not start with '..'"))
  1049. }
  1050. return allErrs
  1051. }
  1052. func validateRBDVolumeSource(rbd *core.RBDVolumeSource, fldPath *field.Path) field.ErrorList {
  1053. allErrs := field.ErrorList{}
  1054. if len(rbd.CephMonitors) == 0 {
  1055. allErrs = append(allErrs, field.Required(fldPath.Child("monitors"), ""))
  1056. }
  1057. if len(rbd.RBDImage) == 0 {
  1058. allErrs = append(allErrs, field.Required(fldPath.Child("image"), ""))
  1059. }
  1060. return allErrs
  1061. }
  1062. func validateRBDPersistentVolumeSource(rbd *core.RBDPersistentVolumeSource, fldPath *field.Path) field.ErrorList {
  1063. allErrs := field.ErrorList{}
  1064. if len(rbd.CephMonitors) == 0 {
  1065. allErrs = append(allErrs, field.Required(fldPath.Child("monitors"), ""))
  1066. }
  1067. if len(rbd.RBDImage) == 0 {
  1068. allErrs = append(allErrs, field.Required(fldPath.Child("image"), ""))
  1069. }
  1070. return allErrs
  1071. }
  1072. func validateCinderVolumeSource(cd *core.CinderVolumeSource, fldPath *field.Path) field.ErrorList {
  1073. allErrs := field.ErrorList{}
  1074. if len(cd.VolumeID) == 0 {
  1075. allErrs = append(allErrs, field.Required(fldPath.Child("volumeID"), ""))
  1076. }
  1077. if cd.SecretRef != nil {
  1078. if len(cd.SecretRef.Name) == 0 {
  1079. allErrs = append(allErrs, field.Required(fldPath.Child("secretRef", "name"), ""))
  1080. }
  1081. }
  1082. return allErrs
  1083. }
  1084. func validateCinderPersistentVolumeSource(cd *core.CinderPersistentVolumeSource, fldPath *field.Path) field.ErrorList {
  1085. allErrs := field.ErrorList{}
  1086. if len(cd.VolumeID) == 0 {
  1087. allErrs = append(allErrs, field.Required(fldPath.Child("volumeID"), ""))
  1088. }
  1089. if cd.SecretRef != nil {
  1090. if len(cd.SecretRef.Name) == 0 {
  1091. allErrs = append(allErrs, field.Required(fldPath.Child("secretRef", "name"), ""))
  1092. }
  1093. if len(cd.SecretRef.Namespace) == 0 {
  1094. allErrs = append(allErrs, field.Required(fldPath.Child("secretRef", "namespace"), ""))
  1095. }
  1096. }
  1097. return allErrs
  1098. }
  1099. func validateCephFSVolumeSource(cephfs *core.CephFSVolumeSource, fldPath *field.Path) field.ErrorList {
  1100. allErrs := field.ErrorList{}
  1101. if len(cephfs.Monitors) == 0 {
  1102. allErrs = append(allErrs, field.Required(fldPath.Child("monitors"), ""))
  1103. }
  1104. return allErrs
  1105. }
  1106. func validateCephFSPersistentVolumeSource(cephfs *core.CephFSPersistentVolumeSource, fldPath *field.Path) field.ErrorList {
  1107. allErrs := field.ErrorList{}
  1108. if len(cephfs.Monitors) == 0 {
  1109. allErrs = append(allErrs, field.Required(fldPath.Child("monitors"), ""))
  1110. }
  1111. return allErrs
  1112. }
  1113. func validateFlexVolumeSource(fv *core.FlexVolumeSource, fldPath *field.Path) field.ErrorList {
  1114. allErrs := field.ErrorList{}
  1115. if len(fv.Driver) == 0 {
  1116. allErrs = append(allErrs, field.Required(fldPath.Child("driver"), ""))
  1117. }
  1118. // Make sure user-specified options don't use kubernetes namespaces
  1119. for k := range fv.Options {
  1120. namespace := k
  1121. if parts := strings.SplitN(k, "/", 2); len(parts) == 2 {
  1122. namespace = parts[0]
  1123. }
  1124. normalized := "." + strings.ToLower(namespace)
  1125. if strings.HasSuffix(normalized, ".kubernetes.io") || strings.HasSuffix(normalized, ".k8s.io") {
  1126. allErrs = append(allErrs, field.Invalid(fldPath.Child("options").Key(k), k, "kubernetes.io and k8s.io namespaces are reserved"))
  1127. }
  1128. }
  1129. return allErrs
  1130. }
  1131. func validateFlexPersistentVolumeSource(fv *core.FlexPersistentVolumeSource, fldPath *field.Path) field.ErrorList {
  1132. allErrs := field.ErrorList{}
  1133. if len(fv.Driver) == 0 {
  1134. allErrs = append(allErrs, field.Required(fldPath.Child("driver"), ""))
  1135. }
  1136. // Make sure user-specified options don't use kubernetes namespaces
  1137. for k := range fv.Options {
  1138. namespace := k
  1139. if parts := strings.SplitN(k, "/", 2); len(parts) == 2 {
  1140. namespace = parts[0]
  1141. }
  1142. normalized := "." + strings.ToLower(namespace)
  1143. if strings.HasSuffix(normalized, ".kubernetes.io") || strings.HasSuffix(normalized, ".k8s.io") {
  1144. allErrs = append(allErrs, field.Invalid(fldPath.Child("options").Key(k), k, "kubernetes.io and k8s.io namespaces are reserved"))
  1145. }
  1146. }
  1147. return allErrs
  1148. }
  1149. func validateAzureFile(azure *core.AzureFileVolumeSource, fldPath *field.Path) field.ErrorList {
  1150. allErrs := field.ErrorList{}
  1151. if azure.SecretName == "" {
  1152. allErrs = append(allErrs, field.Required(fldPath.Child("secretName"), ""))
  1153. }
  1154. if azure.ShareName == "" {
  1155. allErrs = append(allErrs, field.Required(fldPath.Child("shareName"), ""))
  1156. }
  1157. return allErrs
  1158. }
  1159. func validateAzureFilePV(azure *core.AzureFilePersistentVolumeSource, fldPath *field.Path) field.ErrorList {
  1160. allErrs := field.ErrorList{}
  1161. if azure.SecretName == "" {
  1162. allErrs = append(allErrs, field.Required(fldPath.Child("secretName"), ""))
  1163. }
  1164. if azure.ShareName == "" {
  1165. allErrs = append(allErrs, field.Required(fldPath.Child("shareName"), ""))
  1166. }
  1167. if azure.SecretNamespace != nil {
  1168. if len(*azure.SecretNamespace) == 0 {
  1169. allErrs = append(allErrs, field.Required(fldPath.Child("secretNamespace"), ""))
  1170. }
  1171. }
  1172. return allErrs
  1173. }
  1174. func validateAzureDisk(azure *core.AzureDiskVolumeSource, fldPath *field.Path) field.ErrorList {
  1175. var supportedCachingModes = sets.NewString(string(core.AzureDataDiskCachingNone), string(core.AzureDataDiskCachingReadOnly), string(core.AzureDataDiskCachingReadWrite))
  1176. var supportedDiskKinds = sets.NewString(string(core.AzureSharedBlobDisk), string(core.AzureDedicatedBlobDisk), string(core.AzureManagedDisk))
  1177. diskURISupportedManaged := []string{"/subscriptions/{sub-id}/resourcegroups/{group-name}/providers/microsoft.compute/disks/{disk-id}"}
  1178. diskURISupportedblob := []string{"https://{account-name}.blob.core.windows.net/{container-name}/{disk-name}.vhd"}
  1179. allErrs := field.ErrorList{}
  1180. if azure.DiskName == "" {
  1181. allErrs = append(allErrs, field.Required(fldPath.Child("diskName"), ""))
  1182. }
  1183. if azure.DataDiskURI == "" {
  1184. allErrs = append(allErrs, field.Required(fldPath.Child("diskURI"), ""))
  1185. }
  1186. if azure.CachingMode != nil && !supportedCachingModes.Has(string(*azure.CachingMode)) {
  1187. allErrs = append(allErrs, field.NotSupported(fldPath.Child("cachingMode"), *azure.CachingMode, supportedCachingModes.List()))
  1188. }
  1189. if azure.Kind != nil && !supportedDiskKinds.Has(string(*azure.Kind)) {
  1190. allErrs = append(allErrs, field.NotSupported(fldPath.Child("kind"), *azure.Kind, supportedDiskKinds.List()))
  1191. }
  1192. // validate that DiskUri is the correct format
  1193. if azure.Kind != nil && *azure.Kind == core.AzureManagedDisk && strings.Index(azure.DataDiskURI, "/subscriptions/") != 0 {
  1194. allErrs = append(allErrs, field.NotSupported(fldPath.Child("diskURI"), azure.DataDiskURI, diskURISupportedManaged))
  1195. }
  1196. if azure.Kind != nil && *azure.Kind != core.AzureManagedDisk && strings.Index(azure.DataDiskURI, "https://") != 0 {
  1197. allErrs = append(allErrs, field.NotSupported(fldPath.Child("diskURI"), azure.DataDiskURI, diskURISupportedblob))
  1198. }
  1199. return allErrs
  1200. }
  1201. func validateVsphereVolumeSource(cd *core.VsphereVirtualDiskVolumeSource, fldPath *field.Path) field.ErrorList {
  1202. allErrs := field.ErrorList{}
  1203. if len(cd.VolumePath) == 0 {
  1204. allErrs = append(allErrs, field.Required(fldPath.Child("volumePath"), ""))
  1205. }
  1206. return allErrs
  1207. }
  1208. func validatePhotonPersistentDiskVolumeSource(cd *core.PhotonPersistentDiskVolumeSource, fldPath *field.Path) field.ErrorList {
  1209. allErrs := field.ErrorList{}
  1210. if len(cd.PdID) == 0 {
  1211. allErrs = append(allErrs, field.Required(fldPath.Child("pdID"), ""))
  1212. }
  1213. return allErrs
  1214. }
  1215. func validatePortworxVolumeSource(pwx *core.PortworxVolumeSource, fldPath *field.Path) field.ErrorList {
  1216. allErrs := field.ErrorList{}
  1217. if len(pwx.VolumeID) == 0 {
  1218. allErrs = append(allErrs, field.Required(fldPath.Child("volumeID"), ""))
  1219. }
  1220. return allErrs
  1221. }
  1222. func validateScaleIOVolumeSource(sio *core.ScaleIOVolumeSource, fldPath *field.Path) field.ErrorList {
  1223. allErrs := field.ErrorList{}
  1224. if sio.Gateway == "" {
  1225. allErrs = append(allErrs, field.Required(fldPath.Child("gateway"), ""))
  1226. }
  1227. if sio.System == "" {
  1228. allErrs = append(allErrs, field.Required(fldPath.Child("system"), ""))
  1229. }
  1230. if sio.VolumeName == "" {
  1231. allErrs = append(allErrs, field.Required(fldPath.Child("volumeName"), ""))
  1232. }
  1233. return allErrs
  1234. }
  1235. func validateScaleIOPersistentVolumeSource(sio *core.ScaleIOPersistentVolumeSource, fldPath *field.Path) field.ErrorList {
  1236. allErrs := field.ErrorList{}
  1237. if sio.Gateway == "" {
  1238. allErrs = append(allErrs, field.Required(fldPath.Child("gateway"), ""))
  1239. }
  1240. if sio.System == "" {
  1241. allErrs = append(allErrs, field.Required(fldPath.Child("system"), ""))
  1242. }
  1243. if sio.VolumeName == "" {
  1244. allErrs = append(allErrs, field.Required(fldPath.Child("volumeName"), ""))
  1245. }
  1246. return allErrs
  1247. }
  1248. func validateLocalVolumeSource(ls *core.LocalVolumeSource, fldPath *field.Path) field.ErrorList {
  1249. allErrs := field.ErrorList{}
  1250. if ls.Path == "" {
  1251. allErrs = append(allErrs, field.Required(fldPath.Child("path"), ""))
  1252. return allErrs
  1253. }
  1254. allErrs = append(allErrs, validatePathNoBacksteps(ls.Path, fldPath.Child("path"))...)
  1255. return allErrs
  1256. }
  1257. func validateStorageOSVolumeSource(storageos *core.StorageOSVolumeSource, fldPath *field.Path) field.ErrorList {
  1258. allErrs := field.ErrorList{}
  1259. if len(storageos.VolumeName) == 0 {
  1260. allErrs = append(allErrs, field.Required(fldPath.Child("volumeName"), ""))
  1261. } else {
  1262. allErrs = append(allErrs, ValidateDNS1123Label(storageos.VolumeName, fldPath.Child("volumeName"))...)
  1263. }
  1264. if len(storageos.VolumeNamespace) > 0 {
  1265. allErrs = append(allErrs, ValidateDNS1123Label(storageos.VolumeNamespace, fldPath.Child("volumeNamespace"))...)
  1266. }
  1267. if storageos.SecretRef != nil {
  1268. if len(storageos.SecretRef.Name) == 0 {
  1269. allErrs = append(allErrs, field.Required(fldPath.Child("secretRef", "name"), ""))
  1270. }
  1271. }
  1272. return allErrs
  1273. }
  1274. func validateStorageOSPersistentVolumeSource(storageos *core.StorageOSPersistentVolumeSource, fldPath *field.Path) field.ErrorList {
  1275. allErrs := field.ErrorList{}
  1276. if len(storageos.VolumeName) == 0 {
  1277. allErrs = append(allErrs, field.Required(fldPath.Child("volumeName"), ""))
  1278. } else {
  1279. allErrs = append(allErrs, ValidateDNS1123Label(storageos.VolumeName, fldPath.Child("volumeName"))...)
  1280. }
  1281. if len(storageos.VolumeNamespace) > 0 {
  1282. allErrs = append(allErrs, ValidateDNS1123Label(storageos.VolumeNamespace, fldPath.Child("volumeNamespace"))...)
  1283. }
  1284. if storageos.SecretRef != nil {
  1285. if len(storageos.SecretRef.Name) == 0 {
  1286. allErrs = append(allErrs, field.Required(fldPath.Child("secretRef", "name"), ""))
  1287. }
  1288. if len(storageos.SecretRef.Namespace) == 0 {
  1289. allErrs = append(allErrs, field.Required(fldPath.Child("secretRef", "namespace"), ""))
  1290. }
  1291. }
  1292. return allErrs
  1293. }
  1294. func ValidateCSIDriverName(driverName string, fldPath *field.Path) field.ErrorList {
  1295. allErrs := field.ErrorList{}
  1296. if len(driverName) == 0 {
  1297. allErrs = append(allErrs, field.Required(fldPath, ""))
  1298. }
  1299. if len(driverName) > 63 {
  1300. allErrs = append(allErrs, field.TooLong(fldPath, driverName, 63))
  1301. }
  1302. for _, msg := range validation.IsDNS1123Subdomain(strings.ToLower(driverName)) {
  1303. allErrs = append(allErrs, field.Invalid(fldPath, driverName, msg))
  1304. }
  1305. return allErrs
  1306. }
  1307. func validateCSIPersistentVolumeSource(csi *core.CSIPersistentVolumeSource, fldPath *field.Path) field.ErrorList {
  1308. allErrs := field.ErrorList{}
  1309. allErrs = append(allErrs, ValidateCSIDriverName(csi.Driver, fldPath.Child("driver"))...)
  1310. if len(csi.VolumeHandle) == 0 {
  1311. allErrs = append(allErrs, field.Required(fldPath.Child("volumeHandle"), ""))
  1312. }
  1313. if csi.ControllerPublishSecretRef != nil {
  1314. if len(csi.ControllerPublishSecretRef.Name) == 0 {
  1315. allErrs = append(allErrs, field.Required(fldPath.Child("controllerPublishSecretRef", "name"), ""))
  1316. } else {
  1317. allErrs = append(allErrs, ValidateDNS1123Label(csi.ControllerPublishSecretRef.Name, fldPath.Child("name"))...)
  1318. }
  1319. if len(csi.ControllerPublishSecretRef.Namespace) == 0 {
  1320. allErrs = append(allErrs, field.Required(fldPath.Child("controllerPublishSecretRef", "namespace"), ""))
  1321. } else {
  1322. allErrs = append(allErrs, ValidateDNS1123Label(csi.ControllerPublishSecretRef.Namespace, fldPath.Child("namespace"))...)
  1323. }
  1324. }
  1325. if csi.ControllerExpandSecretRef != nil {
  1326. if len(csi.ControllerExpandSecretRef.Name) == 0 {
  1327. allErrs = append(allErrs, field.Required(fldPath.Child("controllerExpandSecretRef", "name"), ""))
  1328. } else {
  1329. allErrs = append(allErrs, ValidateDNS1123Label(csi.ControllerExpandSecretRef.Name, fldPath.Child("name"))...)
  1330. }
  1331. if len(csi.ControllerExpandSecretRef.Namespace) == 0 {
  1332. allErrs = append(allErrs, field.Required(fldPath.Child("controllerExpandSecretRef", "namespace"), ""))
  1333. } else {
  1334. allErrs = append(allErrs, ValidateDNS1123Label(csi.ControllerExpandSecretRef.Namespace, fldPath.Child("namespace"))...)
  1335. }
  1336. }
  1337. if csi.NodePublishSecretRef != nil {
  1338. if len(csi.NodePublishSecretRef.Name) == 0 {
  1339. allErrs = append(allErrs, field.Required(fldPath.Child("nodePublishSecretRef ", "name"), ""))
  1340. } else {
  1341. allErrs = append(allErrs, ValidateDNS1123Label(csi.NodePublishSecretRef.Name, fldPath.Child("name"))...)
  1342. }
  1343. if len(csi.NodePublishSecretRef.Namespace) == 0 {
  1344. allErrs = append(allErrs, field.Required(fldPath.Child("nodePublishSecretRef ", "namespace"), ""))
  1345. } else {
  1346. allErrs = append(allErrs, ValidateDNS1123Label(csi.NodePublishSecretRef.Namespace, fldPath.Child("namespace"))...)
  1347. }
  1348. }
  1349. return allErrs
  1350. }
  1351. func validateCSIVolumeSource(csi *core.CSIVolumeSource, fldPath *field.Path) field.ErrorList {
  1352. allErrs := field.ErrorList{}
  1353. allErrs = append(allErrs, ValidateCSIDriverName(csi.Driver, fldPath.Child("driver"))...)
  1354. if csi.NodePublishSecretRef != nil {
  1355. if len(csi.NodePublishSecretRef.Name) == 0 {
  1356. allErrs = append(allErrs, field.Required(fldPath.Child("nodePublishSecretRef ", "name"), ""))
  1357. } else {
  1358. for _, msg := range ValidateSecretName(csi.NodePublishSecretRef.Name, false) {
  1359. allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), csi.NodePublishSecretRef.Name, msg))
  1360. }
  1361. }
  1362. }
  1363. return allErrs
  1364. }
  1365. // ValidatePersistentVolumeName checks that a name is appropriate for a
  1366. // PersistentVolumeName object.
  1367. var ValidatePersistentVolumeName = apimachineryvalidation.NameIsDNSSubdomain
  1368. var supportedAccessModes = sets.NewString(string(core.ReadWriteOnce), string(core.ReadOnlyMany), string(core.ReadWriteMany))
  1369. var supportedReclaimPolicy = sets.NewString(string(core.PersistentVolumeReclaimDelete), string(core.PersistentVolumeReclaimRecycle), string(core.PersistentVolumeReclaimRetain))
  1370. var supportedVolumeModes = sets.NewString(string(core.PersistentVolumeBlock), string(core.PersistentVolumeFilesystem))
  1371. var supportedDataSourceAPIGroupKinds = map[schema.GroupKind]bool{
  1372. {Group: "snapshot.storage.k8s.io", Kind: "VolumeSnapshot"}: true,
  1373. {Group: "", Kind: "PersistentVolumeClaim"}: true,
  1374. }
  1375. func ValidatePersistentVolumeSpec(pvSpec *core.PersistentVolumeSpec, pvName string, validateInlinePersistentVolumeSpec bool, fldPath *field.Path) field.ErrorList {
  1376. allErrs := field.ErrorList{}
  1377. if validateInlinePersistentVolumeSpec {
  1378. if pvSpec.ClaimRef != nil {
  1379. allErrs = append(allErrs, field.Forbidden(fldPath.Child("claimRef"), "may not be specified in the context of inline volumes"))
  1380. }
  1381. if len(pvSpec.Capacity) != 0 {
  1382. allErrs = append(allErrs, field.Forbidden(fldPath.Child("capacity"), "may not be specified in the context of inline volumes"))
  1383. }
  1384. if pvSpec.CSI == nil {
  1385. allErrs = append(allErrs, field.Required(fldPath.Child("csi"), "has to be specified in the context of inline volumes"))
  1386. }
  1387. }
  1388. if len(pvSpec.AccessModes) == 0 {
  1389. allErrs = append(allErrs, field.Required(fldPath.Child("accessModes"), ""))
  1390. }
  1391. for _, mode := range pvSpec.AccessModes {
  1392. if !supportedAccessModes.Has(string(mode)) {
  1393. allErrs = append(allErrs, field.NotSupported(fldPath.Child("accessModes"), mode, supportedAccessModes.List()))
  1394. }
  1395. }
  1396. if !validateInlinePersistentVolumeSpec {
  1397. if len(pvSpec.Capacity) == 0 {
  1398. allErrs = append(allErrs, field.Required(fldPath.Child("capacity"), ""))
  1399. }
  1400. if _, ok := pvSpec.Capacity[core.ResourceStorage]; !ok || len(pvSpec.Capacity) > 1 {
  1401. allErrs = append(allErrs, field.NotSupported(fldPath.Child("capacity"), pvSpec.Capacity, []string{string(core.ResourceStorage)}))
  1402. }
  1403. capPath := fldPath.Child("capacity")
  1404. for r, qty := range pvSpec.Capacity {
  1405. allErrs = append(allErrs, validateBasicResource(qty, capPath.Key(string(r)))...)
  1406. allErrs = append(allErrs, ValidatePositiveQuantityValue(qty, capPath.Key(string(r)))...)
  1407. }
  1408. }
  1409. if len(string(pvSpec.PersistentVolumeReclaimPolicy)) > 0 {
  1410. if validateInlinePersistentVolumeSpec {
  1411. if pvSpec.PersistentVolumeReclaimPolicy != core.PersistentVolumeReclaimRetain {
  1412. allErrs = append(allErrs, field.Forbidden(fldPath.Child("persistentVolumeReclaimPolicy"), "may only be "+string(core.PersistentVolumeReclaimRetain)+" in the context of inline volumes"))
  1413. }
  1414. } else {
  1415. if !supportedReclaimPolicy.Has(string(pvSpec.PersistentVolumeReclaimPolicy)) {
  1416. allErrs = append(allErrs, field.NotSupported(fldPath.Child("persistentVolumeReclaimPolicy"), pvSpec.PersistentVolumeReclaimPolicy, supportedReclaimPolicy.List()))
  1417. }
  1418. }
  1419. }
  1420. var nodeAffinitySpecified bool
  1421. var errs field.ErrorList
  1422. if pvSpec.NodeAffinity != nil {
  1423. if validateInlinePersistentVolumeSpec {
  1424. allErrs = append(allErrs, field.Forbidden(fldPath.Child("nodeAffinity"), "may not be specified in the context of inline volumes"))
  1425. } else {
  1426. nodeAffinitySpecified, errs = validateVolumeNodeAffinity(pvSpec.NodeAffinity, fldPath.Child("nodeAffinity"))
  1427. allErrs = append(allErrs, errs...)
  1428. }
  1429. }
  1430. numVolumes := 0
  1431. if pvSpec.HostPath != nil {
  1432. if numVolumes > 0 {
  1433. allErrs = append(allErrs, field.Forbidden(fldPath.Child("hostPath"), "may not specify more than 1 volume type"))
  1434. } else {
  1435. numVolumes++
  1436. allErrs = append(allErrs, validateHostPathVolumeSource(pvSpec.HostPath, fldPath.Child("hostPath"))...)
  1437. }
  1438. }
  1439. if pvSpec.GCEPersistentDisk != nil {
  1440. if numVolumes > 0 {
  1441. allErrs = append(allErrs, field.Forbidden(fldPath.Child("gcePersistentDisk"), "may not specify more than 1 volume type"))
  1442. } else {
  1443. numVolumes++
  1444. allErrs = append(allErrs, validateGCEPersistentDiskVolumeSource(pvSpec.GCEPersistentDisk, fldPath.Child("persistentDisk"))...)
  1445. }
  1446. }
  1447. if pvSpec.AWSElasticBlockStore != nil {
  1448. if numVolumes > 0 {
  1449. allErrs = append(allErrs, field.Forbidden(fldPath.Child("awsElasticBlockStore"), "may not specify more than 1 volume type"))
  1450. } else {
  1451. numVolumes++
  1452. allErrs = append(allErrs, validateAWSElasticBlockStoreVolumeSource(pvSpec.AWSElasticBlockStore, fldPath.Child("awsElasticBlockStore"))...)
  1453. }
  1454. }
  1455. if pvSpec.Glusterfs != nil {
  1456. if numVolumes > 0 {
  1457. allErrs = append(allErrs, field.Forbidden(fldPath.Child("glusterfs"), "may not specify more than 1 volume type"))
  1458. } else {
  1459. numVolumes++
  1460. allErrs = append(allErrs, validateGlusterfsPersistentVolumeSource(pvSpec.Glusterfs, fldPath.Child("glusterfs"))...)
  1461. }
  1462. }
  1463. if pvSpec.Flocker != nil {
  1464. if numVolumes > 0 {
  1465. allErrs = append(allErrs, field.Forbidden(fldPath.Child("flocker"), "may not specify more than 1 volume type"))
  1466. } else {
  1467. numVolumes++
  1468. allErrs = append(allErrs, validateFlockerVolumeSource(pvSpec.Flocker, fldPath.Child("flocker"))...)
  1469. }
  1470. }
  1471. if pvSpec.NFS != nil {
  1472. if numVolumes > 0 {
  1473. allErrs = append(allErrs, field.Forbidden(fldPath.Child("nfs"), "may not specify more than 1 volume type"))
  1474. } else {
  1475. numVolumes++
  1476. allErrs = append(allErrs, validateNFSVolumeSource(pvSpec.NFS, fldPath.Child("nfs"))...)
  1477. }
  1478. }
  1479. if pvSpec.RBD != nil {
  1480. if numVolumes > 0 {
  1481. allErrs = append(allErrs, field.Forbidden(fldPath.Child("rbd"), "may not specify more than 1 volume type"))
  1482. } else {
  1483. numVolumes++
  1484. allErrs = append(allErrs, validateRBDPersistentVolumeSource(pvSpec.RBD, fldPath.Child("rbd"))...)
  1485. }
  1486. }
  1487. if pvSpec.Quobyte != nil {
  1488. if numVolumes > 0 {
  1489. allErrs = append(allErrs, field.Forbidden(fldPath.Child("quobyte"), "may not specify more than 1 volume type"))
  1490. } else {
  1491. numVolumes++
  1492. allErrs = append(allErrs, validateQuobyteVolumeSource(pvSpec.Quobyte, fldPath.Child("quobyte"))...)
  1493. }
  1494. }
  1495. if pvSpec.CephFS != nil {
  1496. if numVolumes > 0 {
  1497. allErrs = append(allErrs, field.Forbidden(fldPath.Child("cephFS"), "may not specify more than 1 volume type"))
  1498. } else {
  1499. numVolumes++
  1500. allErrs = append(allErrs, validateCephFSPersistentVolumeSource(pvSpec.CephFS, fldPath.Child("cephfs"))...)
  1501. }
  1502. }
  1503. if pvSpec.ISCSI != nil {
  1504. if numVolumes > 0 {
  1505. allErrs = append(allErrs, field.Forbidden(fldPath.Child("iscsi"), "may not specify more than 1 volume type"))
  1506. } else {
  1507. numVolumes++
  1508. allErrs = append(allErrs, validateISCSIPersistentVolumeSource(pvSpec.ISCSI, pvName, fldPath.Child("iscsi"))...)
  1509. }
  1510. }
  1511. if pvSpec.Cinder != nil {
  1512. if numVolumes > 0 {
  1513. allErrs = append(allErrs, field.Forbidden(fldPath.Child("cinder"), "may not specify more than 1 volume type"))
  1514. } else {
  1515. numVolumes++
  1516. allErrs = append(allErrs, validateCinderPersistentVolumeSource(pvSpec.Cinder, fldPath.Child("cinder"))...)
  1517. }
  1518. }
  1519. if pvSpec.FC != nil {
  1520. if numVolumes > 0 {
  1521. allErrs = append(allErrs, field.Forbidden(fldPath.Child("fc"), "may not specify more than 1 volume type"))
  1522. } else {
  1523. numVolumes++
  1524. allErrs = append(allErrs, validateFCVolumeSource(pvSpec.FC, fldPath.Child("fc"))...)
  1525. }
  1526. }
  1527. if pvSpec.FlexVolume != nil {
  1528. numVolumes++
  1529. allErrs = append(allErrs, validateFlexPersistentVolumeSource(pvSpec.FlexVolume, fldPath.Child("flexVolume"))...)
  1530. }
  1531. if pvSpec.AzureFile != nil {
  1532. if numVolumes > 0 {
  1533. allErrs = append(allErrs, field.Forbidden(fldPath.Child("azureFile"), "may not specify more than 1 volume type"))
  1534. } else {
  1535. numVolumes++
  1536. allErrs = append(allErrs, validateAzureFilePV(pvSpec.AzureFile, fldPath.Child("azureFile"))...)
  1537. }
  1538. }
  1539. if pvSpec.VsphereVolume != nil {
  1540. if numVolumes > 0 {
  1541. allErrs = append(allErrs, field.Forbidden(fldPath.Child("vsphereVolume"), "may not specify more than 1 volume type"))
  1542. } else {
  1543. numVolumes++
  1544. allErrs = append(allErrs, validateVsphereVolumeSource(pvSpec.VsphereVolume, fldPath.Child("vsphereVolume"))...)
  1545. }
  1546. }
  1547. if pvSpec.PhotonPersistentDisk != nil {
  1548. if numVolumes > 0 {
  1549. allErrs = append(allErrs, field.Forbidden(fldPath.Child("photonPersistentDisk"), "may not specify more than 1 volume type"))
  1550. } else {
  1551. numVolumes++
  1552. allErrs = append(allErrs, validatePhotonPersistentDiskVolumeSource(pvSpec.PhotonPersistentDisk, fldPath.Child("photonPersistentDisk"))...)
  1553. }
  1554. }
  1555. if pvSpec.PortworxVolume != nil {
  1556. if numVolumes > 0 {
  1557. allErrs = append(allErrs, field.Forbidden(fldPath.Child("portworxVolume"), "may not specify more than 1 volume type"))
  1558. } else {
  1559. numVolumes++
  1560. allErrs = append(allErrs, validatePortworxVolumeSource(pvSpec.PortworxVolume, fldPath.Child("portworxVolume"))...)
  1561. }
  1562. }
  1563. if pvSpec.AzureDisk != nil {
  1564. if numVolumes > 0 {
  1565. allErrs = append(allErrs, field.Forbidden(fldPath.Child("azureDisk"), "may not specify more than 1 volume type"))
  1566. } else {
  1567. numVolumes++
  1568. allErrs = append(allErrs, validateAzureDisk(pvSpec.AzureDisk, fldPath.Child("azureDisk"))...)
  1569. }
  1570. }
  1571. if pvSpec.ScaleIO != nil {
  1572. if numVolumes > 0 {
  1573. allErrs = append(allErrs, field.Forbidden(fldPath.Child("scaleIO"), "may not specify more than 1 volume type"))
  1574. } else {
  1575. numVolumes++
  1576. allErrs = append(allErrs, validateScaleIOPersistentVolumeSource(pvSpec.ScaleIO, fldPath.Child("scaleIO"))...)
  1577. }
  1578. }
  1579. if pvSpec.Local != nil {
  1580. if numVolumes > 0 {
  1581. allErrs = append(allErrs, field.Forbidden(fldPath.Child("local"), "may not specify more than 1 volume type"))
  1582. } else {
  1583. numVolumes++
  1584. allErrs = append(allErrs, validateLocalVolumeSource(pvSpec.Local, fldPath.Child("local"))...)
  1585. // NodeAffinity is required
  1586. if !nodeAffinitySpecified {
  1587. allErrs = append(allErrs, field.Required(fldPath.Child("nodeAffinity"), "Local volume requires node affinity"))
  1588. }
  1589. }
  1590. }
  1591. if pvSpec.StorageOS != nil {
  1592. if numVolumes > 0 {
  1593. allErrs = append(allErrs, field.Forbidden(fldPath.Child("storageos"), "may not specify more than 1 volume type"))
  1594. } else {
  1595. numVolumes++
  1596. allErrs = append(allErrs, validateStorageOSPersistentVolumeSource(pvSpec.StorageOS, fldPath.Child("storageos"))...)
  1597. }
  1598. }
  1599. if pvSpec.CSI != nil {
  1600. if numVolumes > 0 {
  1601. allErrs = append(allErrs, field.Forbidden(fldPath.Child("csi"), "may not specify more than 1 volume type"))
  1602. } else {
  1603. numVolumes++
  1604. allErrs = append(allErrs, validateCSIPersistentVolumeSource(pvSpec.CSI, fldPath.Child("csi"))...)
  1605. }
  1606. }
  1607. if numVolumes == 0 {
  1608. allErrs = append(allErrs, field.Required(fldPath, "must specify a volume type"))
  1609. }
  1610. // do not allow hostPath mounts of '/' to have a 'recycle' reclaim policy
  1611. if pvSpec.HostPath != nil && path.Clean(pvSpec.HostPath.Path) == "/" && pvSpec.PersistentVolumeReclaimPolicy == core.PersistentVolumeReclaimRecycle {
  1612. allErrs = append(allErrs, field.Forbidden(fldPath.Child("persistentVolumeReclaimPolicy"), "may not be 'recycle' for a hostPath mount of '/'"))
  1613. }
  1614. if len(pvSpec.StorageClassName) > 0 {
  1615. if validateInlinePersistentVolumeSpec {
  1616. allErrs = append(allErrs, field.Forbidden(fldPath.Child("storageClassName"), "may not be specified in the context of inline volumes"))
  1617. } else {
  1618. for _, msg := range ValidateClassName(pvSpec.StorageClassName, false) {
  1619. allErrs = append(allErrs, field.Invalid(fldPath.Child("storageClassName"), pvSpec.StorageClassName, msg))
  1620. }
  1621. }
  1622. }
  1623. if pvSpec.VolumeMode != nil {
  1624. if validateInlinePersistentVolumeSpec {
  1625. if *pvSpec.VolumeMode != core.PersistentVolumeFilesystem {
  1626. allErrs = append(allErrs, field.Forbidden(fldPath.Child("volumeMode"), "may not specify volumeMode other than "+string(core.PersistentVolumeFilesystem)+" in the context of inline volumes"))
  1627. }
  1628. } else {
  1629. if !supportedVolumeModes.Has(string(*pvSpec.VolumeMode)) {
  1630. allErrs = append(allErrs, field.NotSupported(fldPath.Child("volumeMode"), *pvSpec.VolumeMode, supportedVolumeModes.List()))
  1631. }
  1632. }
  1633. }
  1634. return allErrs
  1635. }
  1636. func ValidatePersistentVolume(pv *core.PersistentVolume) field.ErrorList {
  1637. metaPath := field.NewPath("metadata")
  1638. allErrs := ValidateObjectMeta(&pv.ObjectMeta, false, ValidatePersistentVolumeName, metaPath)
  1639. allErrs = append(allErrs, ValidatePersistentVolumeSpec(&pv.Spec, pv.ObjectMeta.Name, false, field.NewPath("spec"))...)
  1640. return allErrs
  1641. }
  1642. // ValidatePersistentVolumeUpdate tests to see if the update is legal for an end user to make.
  1643. // newPv is updated with fields that cannot be changed.
  1644. func ValidatePersistentVolumeUpdate(newPv, oldPv *core.PersistentVolume) field.ErrorList {
  1645. allErrs := ValidatePersistentVolume(newPv)
  1646. // if oldPV does not have ControllerExpandSecretRef then allow it to be set
  1647. if (oldPv.Spec.CSI != nil && oldPv.Spec.CSI.ControllerExpandSecretRef == nil) &&
  1648. (newPv.Spec.CSI != nil && newPv.Spec.CSI.ControllerExpandSecretRef != nil) {
  1649. newPv = newPv.DeepCopy()
  1650. newPv.Spec.CSI.ControllerExpandSecretRef = nil
  1651. }
  1652. // PersistentVolumeSource should be immutable after creation.
  1653. if !apiequality.Semantic.DeepEqual(newPv.Spec.PersistentVolumeSource, oldPv.Spec.PersistentVolumeSource) {
  1654. allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "persistentvolumesource"), "is immutable after creation"))
  1655. }
  1656. allErrs = append(allErrs, ValidateImmutableField(newPv.Spec.VolumeMode, oldPv.Spec.VolumeMode, field.NewPath("volumeMode"))...)
  1657. // Allow setting NodeAffinity if oldPv NodeAffinity was not set
  1658. if oldPv.Spec.NodeAffinity != nil {
  1659. allErrs = append(allErrs, ValidateImmutableField(newPv.Spec.NodeAffinity, oldPv.Spec.NodeAffinity, field.NewPath("nodeAffinity"))...)
  1660. }
  1661. return allErrs
  1662. }
  1663. // ValidatePersistentVolumeStatusUpdate tests to see if the status update is legal for an end user to make.
  1664. // newPv is updated with fields that cannot be changed.
  1665. func ValidatePersistentVolumeStatusUpdate(newPv, oldPv *core.PersistentVolume) field.ErrorList {
  1666. allErrs := ValidateObjectMetaUpdate(&newPv.ObjectMeta, &oldPv.ObjectMeta, field.NewPath("metadata"))
  1667. if len(newPv.ResourceVersion) == 0 {
  1668. allErrs = append(allErrs, field.Required(field.NewPath("resourceVersion"), ""))
  1669. }
  1670. newPv.Spec = oldPv.Spec
  1671. return allErrs
  1672. }
  1673. // ValidatePersistentVolumeClaim validates a PersistentVolumeClaim
  1674. func ValidatePersistentVolumeClaim(pvc *core.PersistentVolumeClaim) field.ErrorList {
  1675. allErrs := ValidateObjectMeta(&pvc.ObjectMeta, true, ValidatePersistentVolumeName, field.NewPath("metadata"))
  1676. allErrs = append(allErrs, ValidatePersistentVolumeClaimSpec(&pvc.Spec, field.NewPath("spec"))...)
  1677. return allErrs
  1678. }
  1679. // ValidatePersistentVolumeClaimSpec validates a PersistentVolumeClaimSpec
  1680. func ValidatePersistentVolumeClaimSpec(spec *core.PersistentVolumeClaimSpec, fldPath *field.Path) field.ErrorList {
  1681. allErrs := field.ErrorList{}
  1682. if len(spec.AccessModes) == 0 {
  1683. allErrs = append(allErrs, field.Required(fldPath.Child("accessModes"), "at least 1 access mode is required"))
  1684. }
  1685. if spec.Selector != nil {
  1686. allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(spec.Selector, fldPath.Child("selector"))...)
  1687. }
  1688. for _, mode := range spec.AccessModes {
  1689. if mode != core.ReadWriteOnce && mode != core.ReadOnlyMany && mode != core.ReadWriteMany {
  1690. allErrs = append(allErrs, field.NotSupported(fldPath.Child("accessModes"), mode, supportedAccessModes.List()))
  1691. }
  1692. }
  1693. storageValue, ok := spec.Resources.Requests[core.ResourceStorage]
  1694. if !ok {
  1695. allErrs = append(allErrs, field.Required(fldPath.Child("resources").Key(string(core.ResourceStorage)), ""))
  1696. } else {
  1697. allErrs = append(allErrs, ValidateResourceQuantityValue(string(core.ResourceStorage), storageValue, fldPath.Child("resources").Key(string(core.ResourceStorage)))...)
  1698. allErrs = append(allErrs, ValidatePositiveQuantityValue(storageValue, fldPath.Child("resources").Key(string(core.ResourceStorage)))...)
  1699. }
  1700. if spec.StorageClassName != nil && len(*spec.StorageClassName) > 0 {
  1701. for _, msg := range ValidateClassName(*spec.StorageClassName, false) {
  1702. allErrs = append(allErrs, field.Invalid(fldPath.Child("storageClassName"), *spec.StorageClassName, msg))
  1703. }
  1704. }
  1705. if spec.VolumeMode != nil && !supportedVolumeModes.Has(string(*spec.VolumeMode)) {
  1706. allErrs = append(allErrs, field.NotSupported(fldPath.Child("volumeMode"), *spec.VolumeMode, supportedVolumeModes.List()))
  1707. }
  1708. if spec.DataSource != nil {
  1709. if len(spec.DataSource.Name) == 0 {
  1710. allErrs = append(allErrs, field.Required(fldPath.Child("dataSource", "name"), ""))
  1711. }
  1712. groupKind := schema.GroupKind{Group: "", Kind: spec.DataSource.Kind}
  1713. if spec.DataSource.APIGroup != nil {
  1714. groupKind.Group = string(*spec.DataSource.APIGroup)
  1715. }
  1716. groupKindList := make([]string, 0, len(supportedDataSourceAPIGroupKinds))
  1717. for grp := range supportedDataSourceAPIGroupKinds {
  1718. groupKindList = append(groupKindList, grp.String())
  1719. }
  1720. if !supportedDataSourceAPIGroupKinds[groupKind] {
  1721. allErrs = append(allErrs, field.NotSupported(fldPath.Child("dataSource"), groupKind.String(), groupKindList))
  1722. }
  1723. }
  1724. return allErrs
  1725. }
  1726. // ValidatePersistentVolumeClaimUpdate validates an update to a PersistentVolumeClaim
  1727. func ValidatePersistentVolumeClaimUpdate(newPvc, oldPvc *core.PersistentVolumeClaim) field.ErrorList {
  1728. allErrs := ValidateObjectMetaUpdate(&newPvc.ObjectMeta, &oldPvc.ObjectMeta, field.NewPath("metadata"))
  1729. allErrs = append(allErrs, ValidatePersistentVolumeClaim(newPvc)...)
  1730. newPvcClone := newPvc.DeepCopy()
  1731. oldPvcClone := oldPvc.DeepCopy()
  1732. // PVController needs to update PVC.Spec w/ VolumeName.
  1733. // Claims are immutable in order to enforce quota, range limits, etc. without gaming the system.
  1734. if len(oldPvc.Spec.VolumeName) == 0 {
  1735. // volumeName changes are allowed once.
  1736. oldPvcClone.Spec.VolumeName = newPvcClone.Spec.VolumeName
  1737. }
  1738. if validateStorageClassUpgrade(oldPvcClone.Annotations, newPvcClone.Annotations,
  1739. oldPvcClone.Spec.StorageClassName, newPvcClone.Spec.StorageClassName) {
  1740. newPvcClone.Spec.StorageClassName = nil
  1741. metav1.SetMetaDataAnnotation(&newPvcClone.ObjectMeta, core.BetaStorageClassAnnotation, oldPvcClone.Annotations[core.BetaStorageClassAnnotation])
  1742. } else {
  1743. // storageclass annotation should be immutable after creation
  1744. // TODO: remove Beta when no longer needed
  1745. allErrs = append(allErrs, ValidateImmutableAnnotation(newPvc.ObjectMeta.Annotations[v1.BetaStorageClassAnnotation], oldPvc.ObjectMeta.Annotations[v1.BetaStorageClassAnnotation], v1.BetaStorageClassAnnotation, field.NewPath("metadata"))...)
  1746. }
  1747. if utilfeature.DefaultFeatureGate.Enabled(features.ExpandPersistentVolumes) {
  1748. // lets make sure storage values are same.
  1749. if newPvc.Status.Phase == core.ClaimBound && newPvcClone.Spec.Resources.Requests != nil {
  1750. newPvcClone.Spec.Resources.Requests["storage"] = oldPvc.Spec.Resources.Requests["storage"]
  1751. }
  1752. oldSize := oldPvc.Spec.Resources.Requests["storage"]
  1753. newSize := newPvc.Spec.Resources.Requests["storage"]
  1754. if !apiequality.Semantic.DeepEqual(newPvcClone.Spec, oldPvcClone.Spec) {
  1755. allErrs = append(allErrs, field.Forbidden(field.NewPath("spec"), "is immutable after creation except resources.requests for bound claims"))
  1756. }
  1757. if newSize.Cmp(oldSize) < 0 {
  1758. allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "resources", "requests", "storage"), "field can not be less than previous value"))
  1759. }
  1760. } else {
  1761. // changes to Spec are not allowed, but updates to label/and some annotations are OK.
  1762. // no-op updates pass validation.
  1763. if !apiequality.Semantic.DeepEqual(newPvcClone.Spec, oldPvcClone.Spec) {
  1764. allErrs = append(allErrs, field.Forbidden(field.NewPath("spec"), "field is immutable after creation"))
  1765. }
  1766. }
  1767. allErrs = append(allErrs, ValidateImmutableField(newPvc.Spec.VolumeMode, oldPvc.Spec.VolumeMode, field.NewPath("volumeMode"))...)
  1768. return allErrs
  1769. }
  1770. // Provide an upgrade path from PVC with storage class specified in beta
  1771. // annotation to storage class specified in attribute. We allow update of
  1772. // StorageClassName only if following four conditions are met at the same time:
  1773. // 1. The old pvc's StorageClassAnnotation is set
  1774. // 2. The old pvc's StorageClassName is not set
  1775. // 3. The new pvc's StorageClassName is set and equal to the old value in annotation
  1776. // 4. If the new pvc's StorageClassAnnotation is set,it must be equal to the old pv/pvc's StorageClassAnnotation
  1777. func validateStorageClassUpgrade(oldAnnotations, newAnnotations map[string]string, oldScName, newScName *string) bool {
  1778. oldSc, oldAnnotationExist := oldAnnotations[core.BetaStorageClassAnnotation]
  1779. newScInAnnotation, newAnnotationExist := newAnnotations[core.BetaStorageClassAnnotation]
  1780. return oldAnnotationExist /* condition 1 */ &&
  1781. oldScName == nil /* condition 2*/ &&
  1782. (newScName != nil && *newScName == oldSc) /* condition 3 */ &&
  1783. (!newAnnotationExist || newScInAnnotation == oldSc) /* condition 4 */
  1784. }
  1785. // ValidatePersistentVolumeClaimStatusUpdate validates an update to status of a PersistentVolumeClaim
  1786. func ValidatePersistentVolumeClaimStatusUpdate(newPvc, oldPvc *core.PersistentVolumeClaim) field.ErrorList {
  1787. allErrs := ValidateObjectMetaUpdate(&newPvc.ObjectMeta, &oldPvc.ObjectMeta, field.NewPath("metadata"))
  1788. if len(newPvc.ResourceVersion) == 0 {
  1789. allErrs = append(allErrs, field.Required(field.NewPath("resourceVersion"), ""))
  1790. }
  1791. if len(newPvc.Spec.AccessModes) == 0 {
  1792. allErrs = append(allErrs, field.Required(field.NewPath("Spec", "accessModes"), ""))
  1793. }
  1794. capPath := field.NewPath("status", "capacity")
  1795. for r, qty := range newPvc.Status.Capacity {
  1796. allErrs = append(allErrs, validateBasicResource(qty, capPath.Key(string(r)))...)
  1797. }
  1798. newPvc.Spec = oldPvc.Spec
  1799. return allErrs
  1800. }
  1801. var supportedPortProtocols = sets.NewString(string(core.ProtocolTCP), string(core.ProtocolUDP), string(core.ProtocolSCTP))
  1802. func validateContainerPorts(ports []core.ContainerPort, fldPath *field.Path) field.ErrorList {
  1803. allErrs := field.ErrorList{}
  1804. allNames := sets.String{}
  1805. for i, port := range ports {
  1806. idxPath := fldPath.Index(i)
  1807. if len(port.Name) > 0 {
  1808. if msgs := validation.IsValidPortName(port.Name); len(msgs) != 0 {
  1809. for i = range msgs {
  1810. allErrs = append(allErrs, field.Invalid(idxPath.Child("name"), port.Name, msgs[i]))
  1811. }
  1812. } else if allNames.Has(port.Name) {
  1813. allErrs = append(allErrs, field.Duplicate(idxPath.Child("name"), port.Name))
  1814. } else {
  1815. allNames.Insert(port.Name)
  1816. }
  1817. }
  1818. if port.ContainerPort == 0 {
  1819. allErrs = append(allErrs, field.Required(idxPath.Child("containerPort"), ""))
  1820. } else {
  1821. for _, msg := range validation.IsValidPortNum(int(port.ContainerPort)) {
  1822. allErrs = append(allErrs, field.Invalid(idxPath.Child("containerPort"), port.ContainerPort, msg))
  1823. }
  1824. }
  1825. if port.HostPort != 0 {
  1826. for _, msg := range validation.IsValidPortNum(int(port.HostPort)) {
  1827. allErrs = append(allErrs, field.Invalid(idxPath.Child("hostPort"), port.HostPort, msg))
  1828. }
  1829. }
  1830. if len(port.Protocol) == 0 {
  1831. allErrs = append(allErrs, field.Required(idxPath.Child("protocol"), ""))
  1832. } else if !supportedPortProtocols.Has(string(port.Protocol)) {
  1833. allErrs = append(allErrs, field.NotSupported(idxPath.Child("protocol"), port.Protocol, supportedPortProtocols.List()))
  1834. }
  1835. }
  1836. return allErrs
  1837. }
  1838. // ValidateEnv validates env vars
  1839. func ValidateEnv(vars []core.EnvVar, fldPath *field.Path) field.ErrorList {
  1840. allErrs := field.ErrorList{}
  1841. for i, ev := range vars {
  1842. idxPath := fldPath.Index(i)
  1843. if len(ev.Name) == 0 {
  1844. allErrs = append(allErrs, field.Required(idxPath.Child("name"), ""))
  1845. } else {
  1846. for _, msg := range validation.IsEnvVarName(ev.Name) {
  1847. allErrs = append(allErrs, field.Invalid(idxPath.Child("name"), ev.Name, msg))
  1848. }
  1849. }
  1850. allErrs = append(allErrs, validateEnvVarValueFrom(ev, idxPath.Child("valueFrom"))...)
  1851. }
  1852. return allErrs
  1853. }
  1854. var validEnvDownwardAPIFieldPathExpressions = sets.NewString(
  1855. "metadata.name",
  1856. "metadata.namespace",
  1857. "metadata.uid",
  1858. "spec.nodeName",
  1859. "spec.serviceAccountName",
  1860. "status.hostIP",
  1861. "status.podIP")
  1862. var validContainerResourceFieldPathExpressions = sets.NewString("limits.cpu", "limits.memory", "limits.ephemeral-storage", "requests.cpu", "requests.memory", "requests.ephemeral-storage")
  1863. func validateEnvVarValueFrom(ev core.EnvVar, fldPath *field.Path) field.ErrorList {
  1864. allErrs := field.ErrorList{}
  1865. if ev.ValueFrom == nil {
  1866. return allErrs
  1867. }
  1868. numSources := 0
  1869. if ev.ValueFrom.FieldRef != nil {
  1870. numSources++
  1871. allErrs = append(allErrs, validateObjectFieldSelector(ev.ValueFrom.FieldRef, &validEnvDownwardAPIFieldPathExpressions, fldPath.Child("fieldRef"))...)
  1872. }
  1873. if ev.ValueFrom.ResourceFieldRef != nil {
  1874. numSources++
  1875. allErrs = append(allErrs, validateContainerResourceFieldSelector(ev.ValueFrom.ResourceFieldRef, &validContainerResourceFieldPathExpressions, fldPath.Child("resourceFieldRef"), false)...)
  1876. }
  1877. if ev.ValueFrom.ConfigMapKeyRef != nil {
  1878. numSources++
  1879. allErrs = append(allErrs, validateConfigMapKeySelector(ev.ValueFrom.ConfigMapKeyRef, fldPath.Child("configMapKeyRef"))...)
  1880. }
  1881. if ev.ValueFrom.SecretKeyRef != nil {
  1882. numSources++
  1883. allErrs = append(allErrs, validateSecretKeySelector(ev.ValueFrom.SecretKeyRef, fldPath.Child("secretKeyRef"))...)
  1884. }
  1885. if numSources == 0 {
  1886. allErrs = append(allErrs, field.Invalid(fldPath, "", "must specify one of: `fieldRef`, `resourceFieldRef`, `configMapKeyRef` or `secretKeyRef`"))
  1887. } else if len(ev.Value) != 0 {
  1888. if numSources != 0 {
  1889. allErrs = append(allErrs, field.Invalid(fldPath, "", "may not be specified when `value` is not empty"))
  1890. }
  1891. } else if numSources > 1 {
  1892. allErrs = append(allErrs, field.Invalid(fldPath, "", "may not have more than one field specified at a time"))
  1893. }
  1894. return allErrs
  1895. }
  1896. func validateObjectFieldSelector(fs *core.ObjectFieldSelector, expressions *sets.String, fldPath *field.Path) field.ErrorList {
  1897. allErrs := field.ErrorList{}
  1898. if len(fs.APIVersion) == 0 {
  1899. allErrs = append(allErrs, field.Required(fldPath.Child("apiVersion"), ""))
  1900. return allErrs
  1901. }
  1902. if len(fs.FieldPath) == 0 {
  1903. allErrs = append(allErrs, field.Required(fldPath.Child("fieldPath"), ""))
  1904. return allErrs
  1905. }
  1906. internalFieldPath, _, err := podshelper.ConvertDownwardAPIFieldLabel(fs.APIVersion, fs.FieldPath, "")
  1907. if err != nil {
  1908. allErrs = append(allErrs, field.Invalid(fldPath.Child("fieldPath"), fs.FieldPath, fmt.Sprintf("error converting fieldPath: %v", err)))
  1909. return allErrs
  1910. }
  1911. if path, subscript, ok := fieldpath.SplitMaybeSubscriptedPath(internalFieldPath); ok {
  1912. switch path {
  1913. case "metadata.annotations":
  1914. for _, msg := range validation.IsQualifiedName(strings.ToLower(subscript)) {
  1915. allErrs = append(allErrs, field.Invalid(fldPath, subscript, msg))
  1916. }
  1917. case "metadata.labels":
  1918. for _, msg := range validation.IsQualifiedName(subscript) {
  1919. allErrs = append(allErrs, field.Invalid(fldPath, subscript, msg))
  1920. }
  1921. default:
  1922. allErrs = append(allErrs, field.Invalid(fldPath, path, "does not support subscript"))
  1923. }
  1924. } else if !expressions.Has(path) {
  1925. allErrs = append(allErrs, field.NotSupported(fldPath.Child("fieldPath"), path, expressions.List()))
  1926. return allErrs
  1927. }
  1928. return allErrs
  1929. }
  1930. func validateContainerResourceFieldSelector(fs *core.ResourceFieldSelector, expressions *sets.String, fldPath *field.Path, volume bool) field.ErrorList {
  1931. allErrs := field.ErrorList{}
  1932. if volume && len(fs.ContainerName) == 0 {
  1933. allErrs = append(allErrs, field.Required(fldPath.Child("containerName"), ""))
  1934. } else if len(fs.Resource) == 0 {
  1935. allErrs = append(allErrs, field.Required(fldPath.Child("resource"), ""))
  1936. } else if !expressions.Has(fs.Resource) {
  1937. allErrs = append(allErrs, field.NotSupported(fldPath.Child("resource"), fs.Resource, expressions.List()))
  1938. }
  1939. allErrs = append(allErrs, validateContainerResourceDivisor(fs.Resource, fs.Divisor, fldPath)...)
  1940. return allErrs
  1941. }
  1942. func ValidateEnvFrom(vars []core.EnvFromSource, fldPath *field.Path) field.ErrorList {
  1943. allErrs := field.ErrorList{}
  1944. for i, ev := range vars {
  1945. idxPath := fldPath.Index(i)
  1946. if len(ev.Prefix) > 0 {
  1947. for _, msg := range validation.IsEnvVarName(ev.Prefix) {
  1948. allErrs = append(allErrs, field.Invalid(idxPath.Child("prefix"), ev.Prefix, msg))
  1949. }
  1950. }
  1951. numSources := 0
  1952. if ev.ConfigMapRef != nil {
  1953. numSources++
  1954. allErrs = append(allErrs, validateConfigMapEnvSource(ev.ConfigMapRef, idxPath.Child("configMapRef"))...)
  1955. }
  1956. if ev.SecretRef != nil {
  1957. numSources++
  1958. allErrs = append(allErrs, validateSecretEnvSource(ev.SecretRef, idxPath.Child("secretRef"))...)
  1959. }
  1960. if numSources == 0 {
  1961. allErrs = append(allErrs, field.Invalid(fldPath, "", "must specify one of: `configMapRef` or `secretRef`"))
  1962. } else if numSources > 1 {
  1963. allErrs = append(allErrs, field.Invalid(fldPath, "", "may not have more than one field specified at a time"))
  1964. }
  1965. }
  1966. return allErrs
  1967. }
  1968. func validateConfigMapEnvSource(configMapSource *core.ConfigMapEnvSource, fldPath *field.Path) field.ErrorList {
  1969. allErrs := field.ErrorList{}
  1970. if len(configMapSource.Name) == 0 {
  1971. allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
  1972. } else {
  1973. for _, msg := range ValidateConfigMapName(configMapSource.Name, true) {
  1974. allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), configMapSource.Name, msg))
  1975. }
  1976. }
  1977. return allErrs
  1978. }
  1979. func validateSecretEnvSource(secretSource *core.SecretEnvSource, fldPath *field.Path) field.ErrorList {
  1980. allErrs := field.ErrorList{}
  1981. if len(secretSource.Name) == 0 {
  1982. allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
  1983. } else {
  1984. for _, msg := range ValidateSecretName(secretSource.Name, true) {
  1985. allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), secretSource.Name, msg))
  1986. }
  1987. }
  1988. return allErrs
  1989. }
  1990. var validContainerResourceDivisorForCPU = sets.NewString("1m", "1")
  1991. var validContainerResourceDivisorForMemory = sets.NewString("1", "1k", "1M", "1G", "1T", "1P", "1E", "1Ki", "1Mi", "1Gi", "1Ti", "1Pi", "1Ei")
  1992. var validContainerResourceDivisorForEphemeralStorage = sets.NewString("1", "1k", "1M", "1G", "1T", "1P", "1E", "1Ki", "1Mi", "1Gi", "1Ti", "1Pi", "1Ei")
  1993. func validateContainerResourceDivisor(rName string, divisor resource.Quantity, fldPath *field.Path) field.ErrorList {
  1994. allErrs := field.ErrorList{}
  1995. unsetDivisor := resource.Quantity{}
  1996. if unsetDivisor.Cmp(divisor) == 0 {
  1997. return allErrs
  1998. }
  1999. switch rName {
  2000. case "limits.cpu", "requests.cpu":
  2001. if !validContainerResourceDivisorForCPU.Has(divisor.String()) {
  2002. allErrs = append(allErrs, field.Invalid(fldPath.Child("divisor"), rName, "only divisor's values 1m and 1 are supported with the cpu resource"))
  2003. }
  2004. case "limits.memory", "requests.memory":
  2005. if !validContainerResourceDivisorForMemory.Has(divisor.String()) {
  2006. allErrs = append(allErrs, field.Invalid(fldPath.Child("divisor"), rName, "only divisor's values 1, 1k, 1M, 1G, 1T, 1P, 1E, 1Ki, 1Mi, 1Gi, 1Ti, 1Pi, 1Ei are supported with the memory resource"))
  2007. }
  2008. case "limits.ephemeral-storage", "requests.ephemeral-storage":
  2009. if !validContainerResourceDivisorForEphemeralStorage.Has(divisor.String()) {
  2010. allErrs = append(allErrs, field.Invalid(fldPath.Child("divisor"), rName, "only divisor's values 1, 1k, 1M, 1G, 1T, 1P, 1E, 1Ki, 1Mi, 1Gi, 1Ti, 1Pi, 1Ei are supported with the local ephemeral storage resource"))
  2011. }
  2012. }
  2013. return allErrs
  2014. }
  2015. func validateConfigMapKeySelector(s *core.ConfigMapKeySelector, fldPath *field.Path) field.ErrorList {
  2016. allErrs := field.ErrorList{}
  2017. nameFn := ValidateNameFunc(ValidateSecretName)
  2018. for _, msg := range nameFn(s.Name, false) {
  2019. allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), s.Name, msg))
  2020. }
  2021. if len(s.Key) == 0 {
  2022. allErrs = append(allErrs, field.Required(fldPath.Child("key"), ""))
  2023. } else {
  2024. for _, msg := range validation.IsConfigMapKey(s.Key) {
  2025. allErrs = append(allErrs, field.Invalid(fldPath.Child("key"), s.Key, msg))
  2026. }
  2027. }
  2028. return allErrs
  2029. }
  2030. func validateSecretKeySelector(s *core.SecretKeySelector, fldPath *field.Path) field.ErrorList {
  2031. allErrs := field.ErrorList{}
  2032. nameFn := ValidateNameFunc(ValidateSecretName)
  2033. for _, msg := range nameFn(s.Name, false) {
  2034. allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), s.Name, msg))
  2035. }
  2036. if len(s.Key) == 0 {
  2037. allErrs = append(allErrs, field.Required(fldPath.Child("key"), ""))
  2038. } else {
  2039. for _, msg := range validation.IsConfigMapKey(s.Key) {
  2040. allErrs = append(allErrs, field.Invalid(fldPath.Child("key"), s.Key, msg))
  2041. }
  2042. }
  2043. return allErrs
  2044. }
  2045. func GetVolumeMountMap(mounts []core.VolumeMount) map[string]string {
  2046. volmounts := make(map[string]string)
  2047. for _, mnt := range mounts {
  2048. volmounts[mnt.Name] = mnt.MountPath
  2049. }
  2050. return volmounts
  2051. }
  2052. func GetVolumeDeviceMap(devices []core.VolumeDevice) map[string]string {
  2053. voldevices := make(map[string]string)
  2054. for _, dev := range devices {
  2055. voldevices[dev.Name] = dev.DevicePath
  2056. }
  2057. return voldevices
  2058. }
  2059. func ValidateVolumeMounts(mounts []core.VolumeMount, voldevices map[string]string, volumes map[string]core.VolumeSource, container *core.Container, fldPath *field.Path) field.ErrorList {
  2060. allErrs := field.ErrorList{}
  2061. mountpoints := sets.NewString()
  2062. for i, mnt := range mounts {
  2063. idxPath := fldPath.Index(i)
  2064. if len(mnt.Name) == 0 {
  2065. allErrs = append(allErrs, field.Required(idxPath.Child("name"), ""))
  2066. }
  2067. if !IsMatchedVolume(mnt.Name, volumes) {
  2068. allErrs = append(allErrs, field.NotFound(idxPath.Child("name"), mnt.Name))
  2069. }
  2070. if len(mnt.MountPath) == 0 {
  2071. allErrs = append(allErrs, field.Required(idxPath.Child("mountPath"), ""))
  2072. }
  2073. if mountpoints.Has(mnt.MountPath) {
  2074. allErrs = append(allErrs, field.Invalid(idxPath.Child("mountPath"), mnt.MountPath, "must be unique"))
  2075. }
  2076. mountpoints.Insert(mnt.MountPath)
  2077. // check for overlap with VolumeDevice
  2078. if mountNameAlreadyExists(mnt.Name, voldevices) {
  2079. allErrs = append(allErrs, field.Invalid(idxPath.Child("name"), mnt.Name, "must not already exist in volumeDevices"))
  2080. }
  2081. if mountPathAlreadyExists(mnt.MountPath, voldevices) {
  2082. allErrs = append(allErrs, field.Invalid(idxPath.Child("mountPath"), mnt.MountPath, "must not already exist as a path in volumeDevices"))
  2083. }
  2084. if len(mnt.SubPath) > 0 {
  2085. allErrs = append(allErrs, validateLocalDescendingPath(mnt.SubPath, fldPath.Child("subPath"))...)
  2086. }
  2087. if len(mnt.SubPathExpr) > 0 {
  2088. if len(mnt.SubPath) > 0 {
  2089. allErrs = append(allErrs, field.Invalid(idxPath.Child("subPathExpr"), mnt.SubPathExpr, "subPathExpr and subPath are mutually exclusive"))
  2090. }
  2091. allErrs = append(allErrs, validateLocalDescendingPath(mnt.SubPathExpr, fldPath.Child("subPathExpr"))...)
  2092. }
  2093. if mnt.MountPropagation != nil {
  2094. allErrs = append(allErrs, validateMountPropagation(mnt.MountPropagation, container, fldPath.Child("mountPropagation"))...)
  2095. }
  2096. }
  2097. return allErrs
  2098. }
  2099. func ValidateVolumeDevices(devices []core.VolumeDevice, volmounts map[string]string, volumes map[string]core.VolumeSource, fldPath *field.Path) field.ErrorList {
  2100. allErrs := field.ErrorList{}
  2101. devicepath := sets.NewString()
  2102. devicename := sets.NewString()
  2103. for i, dev := range devices {
  2104. idxPath := fldPath.Index(i)
  2105. devName := dev.Name
  2106. devPath := dev.DevicePath
  2107. didMatch, isPVC := isMatchedDevice(devName, volumes)
  2108. if len(devName) == 0 {
  2109. allErrs = append(allErrs, field.Required(idxPath.Child("name"), ""))
  2110. }
  2111. if devicename.Has(devName) {
  2112. allErrs = append(allErrs, field.Invalid(idxPath.Child("name"), devName, "must be unique"))
  2113. }
  2114. // Must be PersistentVolumeClaim volume source
  2115. if didMatch && !isPVC {
  2116. allErrs = append(allErrs, field.Invalid(idxPath.Child("name"), devName, "can only use volume source type of PersistentVolumeClaim for block mode"))
  2117. }
  2118. if !didMatch {
  2119. allErrs = append(allErrs, field.NotFound(idxPath.Child("name"), devName))
  2120. }
  2121. if len(devPath) == 0 {
  2122. allErrs = append(allErrs, field.Required(idxPath.Child("devicePath"), ""))
  2123. }
  2124. if devicepath.Has(devPath) {
  2125. allErrs = append(allErrs, field.Invalid(idxPath.Child("devicePath"), devPath, "must be unique"))
  2126. }
  2127. if len(devPath) > 0 && len(validatePathNoBacksteps(devPath, fldPath.Child("devicePath"))) > 0 {
  2128. allErrs = append(allErrs, field.Invalid(idxPath.Child("devicePath"), devPath, "can not contain backsteps ('..')"))
  2129. } else {
  2130. devicepath.Insert(devPath)
  2131. }
  2132. // check for overlap with VolumeMount
  2133. if deviceNameAlreadyExists(devName, volmounts) {
  2134. allErrs = append(allErrs, field.Invalid(idxPath.Child("name"), devName, "must not already exist in volumeMounts"))
  2135. }
  2136. if devicePathAlreadyExists(devPath, volmounts) {
  2137. allErrs = append(allErrs, field.Invalid(idxPath.Child("devicePath"), devPath, "must not already exist as a path in volumeMounts"))
  2138. }
  2139. if len(devName) > 0 {
  2140. devicename.Insert(devName)
  2141. }
  2142. }
  2143. return allErrs
  2144. }
  2145. func validateProbe(probe *core.Probe, fldPath *field.Path) field.ErrorList {
  2146. allErrs := field.ErrorList{}
  2147. if probe == nil {
  2148. return allErrs
  2149. }
  2150. allErrs = append(allErrs, validateHandler(&probe.Handler, fldPath)...)
  2151. allErrs = append(allErrs, ValidateNonnegativeField(int64(probe.InitialDelaySeconds), fldPath.Child("initialDelaySeconds"))...)
  2152. allErrs = append(allErrs, ValidateNonnegativeField(int64(probe.TimeoutSeconds), fldPath.Child("timeoutSeconds"))...)
  2153. allErrs = append(allErrs, ValidateNonnegativeField(int64(probe.PeriodSeconds), fldPath.Child("periodSeconds"))...)
  2154. allErrs = append(allErrs, ValidateNonnegativeField(int64(probe.SuccessThreshold), fldPath.Child("successThreshold"))...)
  2155. allErrs = append(allErrs, ValidateNonnegativeField(int64(probe.FailureThreshold), fldPath.Child("failureThreshold"))...)
  2156. return allErrs
  2157. }
  2158. func validateClientIPAffinityConfig(config *core.SessionAffinityConfig, fldPath *field.Path) field.ErrorList {
  2159. allErrs := field.ErrorList{}
  2160. if config == nil {
  2161. allErrs = append(allErrs, field.Required(fldPath, fmt.Sprintf("when session affinity type is %s", core.ServiceAffinityClientIP)))
  2162. return allErrs
  2163. }
  2164. if config.ClientIP == nil {
  2165. allErrs = append(allErrs, field.Required(fldPath.Child("clientIP"), fmt.Sprintf("when session affinity type is %s", core.ServiceAffinityClientIP)))
  2166. return allErrs
  2167. }
  2168. if config.ClientIP.TimeoutSeconds == nil {
  2169. allErrs = append(allErrs, field.Required(fldPath.Child("clientIP").Child("timeoutSeconds"), fmt.Sprintf("when session affinity type is %s", core.ServiceAffinityClientIP)))
  2170. return allErrs
  2171. }
  2172. allErrs = append(allErrs, validateAffinityTimeout(config.ClientIP.TimeoutSeconds, fldPath.Child("clientIP").Child("timeoutSeconds"))...)
  2173. return allErrs
  2174. }
  2175. func validateAffinityTimeout(timeout *int32, fldPath *field.Path) field.ErrorList {
  2176. allErrs := field.ErrorList{}
  2177. if *timeout <= 0 || *timeout > core.MaxClientIPServiceAffinitySeconds {
  2178. allErrs = append(allErrs, field.Invalid(fldPath, timeout, fmt.Sprintf("must be greater than 0 and less than %d", core.MaxClientIPServiceAffinitySeconds)))
  2179. }
  2180. return allErrs
  2181. }
  2182. // AccumulateUniqueHostPorts extracts each HostPort of each Container,
  2183. // accumulating the results and returning an error if any ports conflict.
  2184. func AccumulateUniqueHostPorts(containers []core.Container, accumulator *sets.String, fldPath *field.Path) field.ErrorList {
  2185. allErrs := field.ErrorList{}
  2186. for ci, ctr := range containers {
  2187. idxPath := fldPath.Index(ci)
  2188. portsPath := idxPath.Child("ports")
  2189. for pi := range ctr.Ports {
  2190. idxPath := portsPath.Index(pi)
  2191. port := ctr.Ports[pi].HostPort
  2192. if port == 0 {
  2193. continue
  2194. }
  2195. str := fmt.Sprintf("%s/%s/%d", ctr.Ports[pi].Protocol, ctr.Ports[pi].HostIP, port)
  2196. if accumulator.Has(str) {
  2197. allErrs = append(allErrs, field.Duplicate(idxPath.Child("hostPort"), str))
  2198. } else {
  2199. accumulator.Insert(str)
  2200. }
  2201. }
  2202. }
  2203. return allErrs
  2204. }
  2205. // checkHostPortConflicts checks for colliding Port.HostPort values across
  2206. // a slice of containers.
  2207. func checkHostPortConflicts(containers []core.Container, fldPath *field.Path) field.ErrorList {
  2208. allPorts := sets.String{}
  2209. return AccumulateUniqueHostPorts(containers, &allPorts, fldPath)
  2210. }
  2211. func validateExecAction(exec *core.ExecAction, fldPath *field.Path) field.ErrorList {
  2212. allErrors := field.ErrorList{}
  2213. if len(exec.Command) == 0 {
  2214. allErrors = append(allErrors, field.Required(fldPath.Child("command"), ""))
  2215. }
  2216. return allErrors
  2217. }
  2218. var supportedHTTPSchemes = sets.NewString(string(core.URISchemeHTTP), string(core.URISchemeHTTPS))
  2219. func validateHTTPGetAction(http *core.HTTPGetAction, fldPath *field.Path) field.ErrorList {
  2220. allErrors := field.ErrorList{}
  2221. if len(http.Path) == 0 {
  2222. allErrors = append(allErrors, field.Required(fldPath.Child("path"), ""))
  2223. }
  2224. allErrors = append(allErrors, ValidatePortNumOrName(http.Port, fldPath.Child("port"))...)
  2225. if !supportedHTTPSchemes.Has(string(http.Scheme)) {
  2226. allErrors = append(allErrors, field.NotSupported(fldPath.Child("scheme"), http.Scheme, supportedHTTPSchemes.List()))
  2227. }
  2228. for _, header := range http.HTTPHeaders {
  2229. for _, msg := range validation.IsHTTPHeaderName(header.Name) {
  2230. allErrors = append(allErrors, field.Invalid(fldPath.Child("httpHeaders"), header.Name, msg))
  2231. }
  2232. }
  2233. return allErrors
  2234. }
  2235. func ValidatePortNumOrName(port intstr.IntOrString, fldPath *field.Path) field.ErrorList {
  2236. allErrs := field.ErrorList{}
  2237. if port.Type == intstr.Int {
  2238. for _, msg := range validation.IsValidPortNum(port.IntValue()) {
  2239. allErrs = append(allErrs, field.Invalid(fldPath, port.IntValue(), msg))
  2240. }
  2241. } else if port.Type == intstr.String {
  2242. for _, msg := range validation.IsValidPortName(port.StrVal) {
  2243. allErrs = append(allErrs, field.Invalid(fldPath, port.StrVal, msg))
  2244. }
  2245. } else {
  2246. allErrs = append(allErrs, field.InternalError(fldPath, fmt.Errorf("unknown type: %v", port.Type)))
  2247. }
  2248. return allErrs
  2249. }
  2250. func validateTCPSocketAction(tcp *core.TCPSocketAction, fldPath *field.Path) field.ErrorList {
  2251. return ValidatePortNumOrName(tcp.Port, fldPath.Child("port"))
  2252. }
  2253. func validateHandler(handler *core.Handler, fldPath *field.Path) field.ErrorList {
  2254. numHandlers := 0
  2255. allErrors := field.ErrorList{}
  2256. if handler.Exec != nil {
  2257. if numHandlers > 0 {
  2258. allErrors = append(allErrors, field.Forbidden(fldPath.Child("exec"), "may not specify more than 1 handler type"))
  2259. } else {
  2260. numHandlers++
  2261. allErrors = append(allErrors, validateExecAction(handler.Exec, fldPath.Child("exec"))...)
  2262. }
  2263. }
  2264. if handler.HTTPGet != nil {
  2265. if numHandlers > 0 {
  2266. allErrors = append(allErrors, field.Forbidden(fldPath.Child("httpGet"), "may not specify more than 1 handler type"))
  2267. } else {
  2268. numHandlers++
  2269. allErrors = append(allErrors, validateHTTPGetAction(handler.HTTPGet, fldPath.Child("httpGet"))...)
  2270. }
  2271. }
  2272. if handler.TCPSocket != nil {
  2273. if numHandlers > 0 {
  2274. allErrors = append(allErrors, field.Forbidden(fldPath.Child("tcpSocket"), "may not specify more than 1 handler type"))
  2275. } else {
  2276. numHandlers++
  2277. allErrors = append(allErrors, validateTCPSocketAction(handler.TCPSocket, fldPath.Child("tcpSocket"))...)
  2278. }
  2279. }
  2280. if numHandlers == 0 {
  2281. allErrors = append(allErrors, field.Required(fldPath, "must specify a handler type"))
  2282. }
  2283. return allErrors
  2284. }
  2285. func validateLifecycle(lifecycle *core.Lifecycle, fldPath *field.Path) field.ErrorList {
  2286. allErrs := field.ErrorList{}
  2287. if lifecycle.PostStart != nil {
  2288. allErrs = append(allErrs, validateHandler(lifecycle.PostStart, fldPath.Child("postStart"))...)
  2289. }
  2290. if lifecycle.PreStop != nil {
  2291. allErrs = append(allErrs, validateHandler(lifecycle.PreStop, fldPath.Child("preStop"))...)
  2292. }
  2293. return allErrs
  2294. }
  2295. var supportedPullPolicies = sets.NewString(string(core.PullAlways), string(core.PullIfNotPresent), string(core.PullNever))
  2296. func validatePullPolicy(policy core.PullPolicy, fldPath *field.Path) field.ErrorList {
  2297. allErrors := field.ErrorList{}
  2298. switch policy {
  2299. case core.PullAlways, core.PullIfNotPresent, core.PullNever:
  2300. break
  2301. case "":
  2302. allErrors = append(allErrors, field.Required(fldPath, ""))
  2303. default:
  2304. allErrors = append(allErrors, field.NotSupported(fldPath, policy, supportedPullPolicies.List()))
  2305. }
  2306. return allErrors
  2307. }
  2308. func validateInitContainers(containers, otherContainers []core.Container, deviceVolumes map[string]core.VolumeSource, fldPath *field.Path) field.ErrorList {
  2309. var allErrs field.ErrorList
  2310. if len(containers) > 0 {
  2311. allErrs = append(allErrs, validateContainers(containers, true, deviceVolumes, fldPath)...)
  2312. }
  2313. allNames := sets.String{}
  2314. for _, ctr := range otherContainers {
  2315. allNames.Insert(ctr.Name)
  2316. }
  2317. for i, ctr := range containers {
  2318. idxPath := fldPath.Index(i)
  2319. if allNames.Has(ctr.Name) {
  2320. allErrs = append(allErrs, field.Duplicate(idxPath.Child("name"), ctr.Name))
  2321. }
  2322. if len(ctr.Name) > 0 {
  2323. allNames.Insert(ctr.Name)
  2324. }
  2325. if ctr.Lifecycle != nil {
  2326. allErrs = append(allErrs, field.Invalid(idxPath.Child("lifecycle"), ctr.Lifecycle, "must not be set for init containers"))
  2327. }
  2328. if ctr.LivenessProbe != nil {
  2329. allErrs = append(allErrs, field.Invalid(idxPath.Child("livenessProbe"), ctr.LivenessProbe, "must not be set for init containers"))
  2330. }
  2331. if ctr.ReadinessProbe != nil {
  2332. allErrs = append(allErrs, field.Invalid(idxPath.Child("readinessProbe"), ctr.ReadinessProbe, "must not be set for init containers"))
  2333. }
  2334. }
  2335. return allErrs
  2336. }
  2337. func validateContainers(containers []core.Container, isInitContainers bool, volumes map[string]core.VolumeSource, fldPath *field.Path) field.ErrorList {
  2338. allErrs := field.ErrorList{}
  2339. if len(containers) == 0 {
  2340. return append(allErrs, field.Required(fldPath, ""))
  2341. }
  2342. allNames := sets.String{}
  2343. for i, ctr := range containers {
  2344. idxPath := fldPath.Index(i)
  2345. namePath := idxPath.Child("name")
  2346. volMounts := GetVolumeMountMap(ctr.VolumeMounts)
  2347. volDevices := GetVolumeDeviceMap(ctr.VolumeDevices)
  2348. if len(ctr.Name) == 0 {
  2349. allErrs = append(allErrs, field.Required(namePath, ""))
  2350. } else {
  2351. allErrs = append(allErrs, ValidateDNS1123Label(ctr.Name, namePath)...)
  2352. }
  2353. if allNames.Has(ctr.Name) {
  2354. allErrs = append(allErrs, field.Duplicate(namePath, ctr.Name))
  2355. } else {
  2356. allNames.Insert(ctr.Name)
  2357. }
  2358. // TODO: do not validate leading and trailing whitespace to preserve backward compatibility.
  2359. // for example: https://github.com/openshift/origin/issues/14659 image = " " is special token in pod template
  2360. // others may have done similar
  2361. if len(ctr.Image) == 0 {
  2362. allErrs = append(allErrs, field.Required(idxPath.Child("image"), ""))
  2363. }
  2364. if ctr.Lifecycle != nil {
  2365. allErrs = append(allErrs, validateLifecycle(ctr.Lifecycle, idxPath.Child("lifecycle"))...)
  2366. }
  2367. allErrs = append(allErrs, validateProbe(ctr.LivenessProbe, idxPath.Child("livenessProbe"))...)
  2368. // Liveness-specific validation
  2369. if ctr.LivenessProbe != nil && ctr.LivenessProbe.SuccessThreshold != 1 {
  2370. allErrs = append(allErrs, field.Invalid(idxPath.Child("livenessProbe", "successThreshold"), ctr.LivenessProbe.SuccessThreshold, "must be 1"))
  2371. }
  2372. switch ctr.TerminationMessagePolicy {
  2373. case core.TerminationMessageReadFile, core.TerminationMessageFallbackToLogsOnError:
  2374. case "":
  2375. allErrs = append(allErrs, field.Required(idxPath.Child("terminationMessagePolicy"), "must be 'File' or 'FallbackToLogsOnError'"))
  2376. default:
  2377. allErrs = append(allErrs, field.Invalid(idxPath.Child("terminationMessagePolicy"), ctr.TerminationMessagePolicy, "must be 'File' or 'FallbackToLogsOnError'"))
  2378. }
  2379. allErrs = append(allErrs, validateProbe(ctr.ReadinessProbe, idxPath.Child("readinessProbe"))...)
  2380. allErrs = append(allErrs, validateContainerPorts(ctr.Ports, idxPath.Child("ports"))...)
  2381. allErrs = append(allErrs, ValidateEnv(ctr.Env, idxPath.Child("env"))...)
  2382. allErrs = append(allErrs, ValidateEnvFrom(ctr.EnvFrom, idxPath.Child("envFrom"))...)
  2383. allErrs = append(allErrs, ValidateVolumeMounts(ctr.VolumeMounts, volDevices, volumes, &ctr, idxPath.Child("volumeMounts"))...)
  2384. allErrs = append(allErrs, ValidateVolumeDevices(ctr.VolumeDevices, volMounts, volumes, idxPath.Child("volumeDevices"))...)
  2385. allErrs = append(allErrs, validatePullPolicy(ctr.ImagePullPolicy, idxPath.Child("imagePullPolicy"))...)
  2386. allErrs = append(allErrs, ValidateResourceRequirements(&ctr.Resources, idxPath.Child("resources"))...)
  2387. allErrs = append(allErrs, ValidateSecurityContext(ctr.SecurityContext, idxPath.Child("securityContext"))...)
  2388. }
  2389. if isInitContainers {
  2390. // check initContainers one by one since they are running in sequential order.
  2391. for _, initContainer := range containers {
  2392. allErrs = append(allErrs, checkHostPortConflicts([]core.Container{initContainer}, fldPath)...)
  2393. }
  2394. } else {
  2395. // Check for colliding ports across all containers.
  2396. allErrs = append(allErrs, checkHostPortConflicts(containers, fldPath)...)
  2397. }
  2398. return allErrs
  2399. }
  2400. func validateRestartPolicy(restartPolicy *core.RestartPolicy, fldPath *field.Path) field.ErrorList {
  2401. allErrors := field.ErrorList{}
  2402. switch *restartPolicy {
  2403. case core.RestartPolicyAlways, core.RestartPolicyOnFailure, core.RestartPolicyNever:
  2404. break
  2405. case "":
  2406. allErrors = append(allErrors, field.Required(fldPath, ""))
  2407. default:
  2408. validValues := []string{string(core.RestartPolicyAlways), string(core.RestartPolicyOnFailure), string(core.RestartPolicyNever)}
  2409. allErrors = append(allErrors, field.NotSupported(fldPath, *restartPolicy, validValues))
  2410. }
  2411. return allErrors
  2412. }
  2413. func ValidatePreemptionPolicy(preemptionPolicy *core.PreemptionPolicy, fldPath *field.Path) field.ErrorList {
  2414. allErrors := field.ErrorList{}
  2415. switch *preemptionPolicy {
  2416. case core.PreemptLowerPriority, core.PreemptNever:
  2417. case "":
  2418. allErrors = append(allErrors, field.Required(fldPath, ""))
  2419. default:
  2420. validValues := []string{string(core.PreemptLowerPriority), string(core.PreemptNever)}
  2421. allErrors = append(allErrors, field.NotSupported(fldPath, preemptionPolicy, validValues))
  2422. }
  2423. return allErrors
  2424. }
  2425. func validateDNSPolicy(dnsPolicy *core.DNSPolicy, fldPath *field.Path) field.ErrorList {
  2426. allErrors := field.ErrorList{}
  2427. switch *dnsPolicy {
  2428. case core.DNSClusterFirstWithHostNet, core.DNSClusterFirst, core.DNSDefault, core.DNSNone:
  2429. case "":
  2430. allErrors = append(allErrors, field.Required(fldPath, ""))
  2431. default:
  2432. validValues := []string{string(core.DNSClusterFirstWithHostNet), string(core.DNSClusterFirst), string(core.DNSDefault), string(core.DNSNone)}
  2433. allErrors = append(allErrors, field.NotSupported(fldPath, dnsPolicy, validValues))
  2434. }
  2435. return allErrors
  2436. }
  2437. const (
  2438. // Limits on various DNS parameters. These are derived from
  2439. // restrictions in Linux libc name resolution handling.
  2440. // Max number of DNS name servers.
  2441. MaxDNSNameservers = 3
  2442. // Max number of domains in search path.
  2443. MaxDNSSearchPaths = 6
  2444. // Max number of characters in search path.
  2445. MaxDNSSearchListChars = 256
  2446. )
  2447. func validateReadinessGates(readinessGates []core.PodReadinessGate, fldPath *field.Path) field.ErrorList {
  2448. allErrs := field.ErrorList{}
  2449. for i, value := range readinessGates {
  2450. for _, msg := range validation.IsQualifiedName(string(value.ConditionType)) {
  2451. allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("conditionType"), string(value.ConditionType), msg))
  2452. }
  2453. }
  2454. return allErrs
  2455. }
  2456. func validatePodDNSConfig(dnsConfig *core.PodDNSConfig, dnsPolicy *core.DNSPolicy, fldPath *field.Path) field.ErrorList {
  2457. allErrs := field.ErrorList{}
  2458. // Validate DNSNone case. Must provide at least one DNS name server.
  2459. if dnsPolicy != nil && *dnsPolicy == core.DNSNone {
  2460. if dnsConfig == nil {
  2461. return append(allErrs, field.Required(fldPath, fmt.Sprintf("must provide `dnsConfig` when `dnsPolicy` is %s", core.DNSNone)))
  2462. }
  2463. if len(dnsConfig.Nameservers) == 0 {
  2464. return append(allErrs, field.Required(fldPath.Child("nameservers"), fmt.Sprintf("must provide at least one DNS nameserver when `dnsPolicy` is %s", core.DNSNone)))
  2465. }
  2466. }
  2467. if dnsConfig != nil {
  2468. // Validate nameservers.
  2469. if len(dnsConfig.Nameservers) > MaxDNSNameservers {
  2470. allErrs = append(allErrs, field.Invalid(fldPath.Child("nameservers"), dnsConfig.Nameservers, fmt.Sprintf("must not have more than %v nameservers", MaxDNSNameservers)))
  2471. }
  2472. for i, ns := range dnsConfig.Nameservers {
  2473. if ip := net.ParseIP(ns); ip == nil {
  2474. allErrs = append(allErrs, field.Invalid(fldPath.Child("nameservers").Index(i), ns, "must be valid IP address"))
  2475. }
  2476. }
  2477. // Validate searches.
  2478. if len(dnsConfig.Searches) > MaxDNSSearchPaths {
  2479. allErrs = append(allErrs, field.Invalid(fldPath.Child("searches"), dnsConfig.Searches, fmt.Sprintf("must not have more than %v search paths", MaxDNSSearchPaths)))
  2480. }
  2481. // Include the space between search paths.
  2482. if len(strings.Join(dnsConfig.Searches, " ")) > MaxDNSSearchListChars {
  2483. allErrs = append(allErrs, field.Invalid(fldPath.Child("searches"), dnsConfig.Searches, "must not have more than 256 characters (including spaces) in the search list"))
  2484. }
  2485. for i, search := range dnsConfig.Searches {
  2486. // it is fine to have a trailing dot
  2487. if strings.HasSuffix(search, ".") {
  2488. search = search[0 : len(search)-1]
  2489. }
  2490. allErrs = append(allErrs, ValidateDNS1123Subdomain(search, fldPath.Child("searches").Index(i))...)
  2491. }
  2492. // Validate options.
  2493. for i, option := range dnsConfig.Options {
  2494. if len(option.Name) == 0 {
  2495. allErrs = append(allErrs, field.Required(fldPath.Child("options").Index(i), "must not be empty"))
  2496. }
  2497. }
  2498. }
  2499. return allErrs
  2500. }
  2501. func validateHostNetwork(hostNetwork bool, containers []core.Container, fldPath *field.Path) field.ErrorList {
  2502. allErrors := field.ErrorList{}
  2503. if hostNetwork {
  2504. for i, container := range containers {
  2505. portsPath := fldPath.Index(i).Child("ports")
  2506. for i, port := range container.Ports {
  2507. idxPath := portsPath.Index(i)
  2508. if port.HostPort != port.ContainerPort {
  2509. allErrors = append(allErrors, field.Invalid(idxPath.Child("containerPort"), port.ContainerPort, "must match `hostPort` when `hostNetwork` is true"))
  2510. }
  2511. }
  2512. }
  2513. }
  2514. return allErrors
  2515. }
  2516. // validateImagePullSecrets checks to make sure the pull secrets are well
  2517. // formed. Right now, we only expect name to be set (it's the only field). If
  2518. // this ever changes and someone decides to set those fields, we'd like to
  2519. // know.
  2520. func validateImagePullSecrets(imagePullSecrets []core.LocalObjectReference, fldPath *field.Path) field.ErrorList {
  2521. allErrors := field.ErrorList{}
  2522. for i, currPullSecret := range imagePullSecrets {
  2523. idxPath := fldPath.Index(i)
  2524. strippedRef := core.LocalObjectReference{Name: currPullSecret.Name}
  2525. if !reflect.DeepEqual(strippedRef, currPullSecret) {
  2526. allErrors = append(allErrors, field.Invalid(idxPath, currPullSecret, "only name may be set"))
  2527. }
  2528. }
  2529. return allErrors
  2530. }
  2531. // validateAffinity checks if given affinities are valid
  2532. func validateAffinity(affinity *core.Affinity, fldPath *field.Path) field.ErrorList {
  2533. allErrs := field.ErrorList{}
  2534. if affinity != nil {
  2535. if affinity.NodeAffinity != nil {
  2536. allErrs = append(allErrs, validateNodeAffinity(affinity.NodeAffinity, fldPath.Child("nodeAffinity"))...)
  2537. }
  2538. if affinity.PodAffinity != nil {
  2539. allErrs = append(allErrs, validatePodAffinity(affinity.PodAffinity, fldPath.Child("podAffinity"))...)
  2540. }
  2541. if affinity.PodAntiAffinity != nil {
  2542. allErrs = append(allErrs, validatePodAntiAffinity(affinity.PodAntiAffinity, fldPath.Child("podAntiAffinity"))...)
  2543. }
  2544. }
  2545. return allErrs
  2546. }
  2547. func validateTaintEffect(effect *core.TaintEffect, allowEmpty bool, fldPath *field.Path) field.ErrorList {
  2548. if !allowEmpty && len(*effect) == 0 {
  2549. return field.ErrorList{field.Required(fldPath, "")}
  2550. }
  2551. allErrors := field.ErrorList{}
  2552. switch *effect {
  2553. // TODO: Replace next line with subsequent commented-out line when implement TaintEffectNoScheduleNoAdmit.
  2554. case core.TaintEffectNoSchedule, core.TaintEffectPreferNoSchedule, core.TaintEffectNoExecute:
  2555. // case core.TaintEffectNoSchedule, core.TaintEffectPreferNoSchedule, core.TaintEffectNoScheduleNoAdmit, core.TaintEffectNoExecute:
  2556. default:
  2557. validValues := []string{
  2558. string(core.TaintEffectNoSchedule),
  2559. string(core.TaintEffectPreferNoSchedule),
  2560. string(core.TaintEffectNoExecute),
  2561. // TODO: Uncomment this block when implement TaintEffectNoScheduleNoAdmit.
  2562. // string(core.TaintEffectNoScheduleNoAdmit),
  2563. }
  2564. allErrors = append(allErrors, field.NotSupported(fldPath, *effect, validValues))
  2565. }
  2566. return allErrors
  2567. }
  2568. // validateOnlyAddedTolerations validates updated pod tolerations.
  2569. func validateOnlyAddedTolerations(newTolerations []core.Toleration, oldTolerations []core.Toleration, fldPath *field.Path) field.ErrorList {
  2570. allErrs := field.ErrorList{}
  2571. for _, old := range oldTolerations {
  2572. found := false
  2573. old.TolerationSeconds = nil
  2574. for _, new := range newTolerations {
  2575. new.TolerationSeconds = nil
  2576. if reflect.DeepEqual(old, new) {
  2577. found = true
  2578. break
  2579. }
  2580. }
  2581. if !found {
  2582. allErrs = append(allErrs, field.Forbidden(fldPath, "existing toleration can not be modified except its tolerationSeconds"))
  2583. return allErrs
  2584. }
  2585. }
  2586. allErrs = append(allErrs, ValidateTolerations(newTolerations, fldPath)...)
  2587. return allErrs
  2588. }
  2589. func ValidateHostAliases(hostAliases []core.HostAlias, fldPath *field.Path) field.ErrorList {
  2590. allErrs := field.ErrorList{}
  2591. for _, hostAlias := range hostAliases {
  2592. if ip := net.ParseIP(hostAlias.IP); ip == nil {
  2593. allErrs = append(allErrs, field.Invalid(fldPath.Child("ip"), hostAlias.IP, "must be valid IP address"))
  2594. }
  2595. for _, hostname := range hostAlias.Hostnames {
  2596. allErrs = append(allErrs, ValidateDNS1123Subdomain(hostname, fldPath.Child("hostnames"))...)
  2597. }
  2598. }
  2599. return allErrs
  2600. }
  2601. // ValidateTolerations tests if given tolerations have valid data.
  2602. func ValidateTolerations(tolerations []core.Toleration, fldPath *field.Path) field.ErrorList {
  2603. allErrors := field.ErrorList{}
  2604. for i, toleration := range tolerations {
  2605. idxPath := fldPath.Index(i)
  2606. // validate the toleration key
  2607. if len(toleration.Key) > 0 {
  2608. allErrors = append(allErrors, unversionedvalidation.ValidateLabelName(toleration.Key, idxPath.Child("key"))...)
  2609. }
  2610. // empty toleration key with Exists operator and empty value means match all taints
  2611. if len(toleration.Key) == 0 && toleration.Operator != core.TolerationOpExists {
  2612. allErrors = append(allErrors, field.Invalid(idxPath.Child("operator"), toleration.Operator,
  2613. "operator must be Exists when `key` is empty, which means \"match all values and all keys\""))
  2614. }
  2615. if toleration.TolerationSeconds != nil && toleration.Effect != core.TaintEffectNoExecute {
  2616. allErrors = append(allErrors, field.Invalid(idxPath.Child("effect"), toleration.Effect,
  2617. "effect must be 'NoExecute' when `tolerationSeconds` is set"))
  2618. }
  2619. // validate toleration operator and value
  2620. switch toleration.Operator {
  2621. // empty operator means Equal
  2622. case core.TolerationOpEqual, "":
  2623. if errs := validation.IsValidLabelValue(toleration.Value); len(errs) != 0 {
  2624. allErrors = append(allErrors, field.Invalid(idxPath.Child("operator"), toleration.Value, strings.Join(errs, ";")))
  2625. }
  2626. case core.TolerationOpExists:
  2627. if len(toleration.Value) > 0 {
  2628. allErrors = append(allErrors, field.Invalid(idxPath.Child("operator"), toleration, "value must be empty when `operator` is 'Exists'"))
  2629. }
  2630. default:
  2631. validValues := []string{string(core.TolerationOpEqual), string(core.TolerationOpExists)}
  2632. allErrors = append(allErrors, field.NotSupported(idxPath.Child("operator"), toleration.Operator, validValues))
  2633. }
  2634. // validate toleration effect, empty toleration effect means match all taint effects
  2635. if len(toleration.Effect) > 0 {
  2636. allErrors = append(allErrors, validateTaintEffect(&toleration.Effect, true, idxPath.Child("effect"))...)
  2637. }
  2638. }
  2639. return allErrors
  2640. }
  2641. func toResourceNames(resources core.ResourceList) []core.ResourceName {
  2642. result := []core.ResourceName{}
  2643. for resourceName := range resources {
  2644. result = append(result, resourceName)
  2645. }
  2646. return result
  2647. }
  2648. func toSet(resourceNames []core.ResourceName) sets.String {
  2649. result := sets.NewString()
  2650. for _, resourceName := range resourceNames {
  2651. result.Insert(string(resourceName))
  2652. }
  2653. return result
  2654. }
  2655. func toContainerResourcesSet(ctr *core.Container) sets.String {
  2656. resourceNames := toResourceNames(ctr.Resources.Requests)
  2657. resourceNames = append(resourceNames, toResourceNames(ctr.Resources.Limits)...)
  2658. return toSet(resourceNames)
  2659. }
  2660. // validateContainersOnlyForPod does additional validation for containers on a pod versus a pod template
  2661. // it only does additive validation of fields not covered in validateContainers
  2662. func validateContainersOnlyForPod(containers []core.Container, fldPath *field.Path) field.ErrorList {
  2663. allErrs := field.ErrorList{}
  2664. for i, ctr := range containers {
  2665. idxPath := fldPath.Index(i)
  2666. if len(ctr.Image) != len(strings.TrimSpace(ctr.Image)) {
  2667. allErrs = append(allErrs, field.Invalid(idxPath.Child("image"), ctr.Image, "must not have leading or trailing whitespace"))
  2668. }
  2669. }
  2670. return allErrs
  2671. }
  2672. // ValidatePod tests if required fields in the pod are set.
  2673. func ValidatePod(pod *core.Pod) field.ErrorList {
  2674. fldPath := field.NewPath("metadata")
  2675. allErrs := ValidateObjectMeta(&pod.ObjectMeta, true, ValidatePodName, fldPath)
  2676. allErrs = append(allErrs, ValidatePodSpecificAnnotations(pod.ObjectMeta.Annotations, &pod.Spec, fldPath.Child("annotations"))...)
  2677. allErrs = append(allErrs, ValidatePodSpec(&pod.Spec, field.NewPath("spec"))...)
  2678. // we do additional validation only pertinent for pods and not pod templates
  2679. // this was done to preserve backwards compatibility
  2680. specPath := field.NewPath("spec")
  2681. if pod.Spec.ServiceAccountName == "" {
  2682. for vi, volume := range pod.Spec.Volumes {
  2683. path := specPath.Child("volumes").Index(vi).Child("projected")
  2684. if volume.Projected != nil {
  2685. for si, source := range volume.Projected.Sources {
  2686. saPath := path.Child("sources").Index(si).Child("serviceAccountToken")
  2687. if source.ServiceAccountToken != nil {
  2688. allErrs = append(allErrs, field.Forbidden(saPath, "must not be specified when serviceAccountName is not set"))
  2689. }
  2690. }
  2691. }
  2692. }
  2693. }
  2694. allErrs = append(allErrs, validateContainersOnlyForPod(pod.Spec.Containers, specPath.Child("containers"))...)
  2695. allErrs = append(allErrs, validateContainersOnlyForPod(pod.Spec.InitContainers, specPath.Child("initContainers"))...)
  2696. hugePageResources := sets.NewString()
  2697. for i := range pod.Spec.Containers {
  2698. resourceSet := toContainerResourcesSet(&pod.Spec.Containers[i])
  2699. for resourceStr := range resourceSet {
  2700. if v1helper.IsHugePageResourceName(v1.ResourceName(resourceStr)) {
  2701. hugePageResources.Insert(resourceStr)
  2702. }
  2703. }
  2704. }
  2705. if len(hugePageResources) > 1 {
  2706. allErrs = append(allErrs, field.Invalid(specPath, hugePageResources, "must use a single hugepage size in a pod spec"))
  2707. }
  2708. return allErrs
  2709. }
  2710. // ValidatePodSpec tests that the specified PodSpec has valid data.
  2711. // This includes checking formatting and uniqueness. It also canonicalizes the
  2712. // structure by setting default values and implementing any backwards-compatibility
  2713. // tricks.
  2714. func ValidatePodSpec(spec *core.PodSpec, fldPath *field.Path) field.ErrorList {
  2715. allErrs := field.ErrorList{}
  2716. vols, vErrs := ValidateVolumes(spec.Volumes, fldPath.Child("volumes"))
  2717. allErrs = append(allErrs, vErrs...)
  2718. allErrs = append(allErrs, validateContainers(spec.Containers, false, vols, fldPath.Child("containers"))...)
  2719. allErrs = append(allErrs, validateInitContainers(spec.InitContainers, spec.Containers, vols, fldPath.Child("initContainers"))...)
  2720. allErrs = append(allErrs, validateRestartPolicy(&spec.RestartPolicy, fldPath.Child("restartPolicy"))...)
  2721. allErrs = append(allErrs, validateDNSPolicy(&spec.DNSPolicy, fldPath.Child("dnsPolicy"))...)
  2722. allErrs = append(allErrs, unversionedvalidation.ValidateLabels(spec.NodeSelector, fldPath.Child("nodeSelector"))...)
  2723. allErrs = append(allErrs, ValidatePodSecurityContext(spec.SecurityContext, spec, fldPath, fldPath.Child("securityContext"))...)
  2724. allErrs = append(allErrs, validateImagePullSecrets(spec.ImagePullSecrets, fldPath.Child("imagePullSecrets"))...)
  2725. allErrs = append(allErrs, validateAffinity(spec.Affinity, fldPath.Child("affinity"))...)
  2726. allErrs = append(allErrs, validatePodDNSConfig(spec.DNSConfig, &spec.DNSPolicy, fldPath.Child("dnsConfig"))...)
  2727. allErrs = append(allErrs, validateReadinessGates(spec.ReadinessGates, fldPath.Child("readinessGates"))...)
  2728. if len(spec.ServiceAccountName) > 0 {
  2729. for _, msg := range ValidateServiceAccountName(spec.ServiceAccountName, false) {
  2730. allErrs = append(allErrs, field.Invalid(fldPath.Child("serviceAccountName"), spec.ServiceAccountName, msg))
  2731. }
  2732. }
  2733. if len(spec.NodeName) > 0 {
  2734. for _, msg := range ValidateNodeName(spec.NodeName, false) {
  2735. allErrs = append(allErrs, field.Invalid(fldPath.Child("nodeName"), spec.NodeName, msg))
  2736. }
  2737. }
  2738. if spec.ActiveDeadlineSeconds != nil {
  2739. value := *spec.ActiveDeadlineSeconds
  2740. if value < 1 || value > math.MaxInt32 {
  2741. allErrs = append(allErrs, field.Invalid(fldPath.Child("activeDeadlineSeconds"), value, validation.InclusiveRangeError(1, math.MaxInt32)))
  2742. }
  2743. }
  2744. if len(spec.Hostname) > 0 {
  2745. allErrs = append(allErrs, ValidateDNS1123Label(spec.Hostname, fldPath.Child("hostname"))...)
  2746. }
  2747. if len(spec.Subdomain) > 0 {
  2748. allErrs = append(allErrs, ValidateDNS1123Label(spec.Subdomain, fldPath.Child("subdomain"))...)
  2749. }
  2750. if len(spec.Tolerations) > 0 {
  2751. allErrs = append(allErrs, ValidateTolerations(spec.Tolerations, fldPath.Child("tolerations"))...)
  2752. }
  2753. if len(spec.HostAliases) > 0 {
  2754. allErrs = append(allErrs, ValidateHostAliases(spec.HostAliases, fldPath.Child("hostAliases"))...)
  2755. }
  2756. if len(spec.PriorityClassName) > 0 {
  2757. for _, msg := range ValidatePriorityClassName(spec.PriorityClassName, false) {
  2758. allErrs = append(allErrs, field.Invalid(fldPath.Child("priorityClassName"), spec.PriorityClassName, msg))
  2759. }
  2760. }
  2761. if spec.RuntimeClassName != nil {
  2762. allErrs = append(allErrs, ValidateRuntimeClassName(*spec.RuntimeClassName, fldPath.Child("runtimeClassName"))...)
  2763. }
  2764. if spec.PreemptionPolicy != nil {
  2765. allErrs = append(allErrs, ValidatePreemptionPolicy(spec.PreemptionPolicy, fldPath.Child("preemptionPolicy"))...)
  2766. }
  2767. return allErrs
  2768. }
  2769. // ValidateNodeSelectorRequirement tests that the specified NodeSelectorRequirement fields has valid data
  2770. func ValidateNodeSelectorRequirement(rq core.NodeSelectorRequirement, fldPath *field.Path) field.ErrorList {
  2771. allErrs := field.ErrorList{}
  2772. switch rq.Operator {
  2773. case core.NodeSelectorOpIn, core.NodeSelectorOpNotIn:
  2774. if len(rq.Values) == 0 {
  2775. allErrs = append(allErrs, field.Required(fldPath.Child("values"), "must be specified when `operator` is 'In' or 'NotIn'"))
  2776. }
  2777. case core.NodeSelectorOpExists, core.NodeSelectorOpDoesNotExist:
  2778. if len(rq.Values) > 0 {
  2779. allErrs = append(allErrs, field.Forbidden(fldPath.Child("values"), "may not be specified when `operator` is 'Exists' or 'DoesNotExist'"))
  2780. }
  2781. case core.NodeSelectorOpGt, core.NodeSelectorOpLt:
  2782. if len(rq.Values) != 1 {
  2783. allErrs = append(allErrs, field.Required(fldPath.Child("values"), "must be specified single value when `operator` is 'Lt' or 'Gt'"))
  2784. }
  2785. default:
  2786. allErrs = append(allErrs, field.Invalid(fldPath.Child("operator"), rq.Operator, "not a valid selector operator"))
  2787. }
  2788. allErrs = append(allErrs, unversionedvalidation.ValidateLabelName(rq.Key, fldPath.Child("key"))...)
  2789. return allErrs
  2790. }
  2791. var nodeFieldSelectorValidators = map[string]func(string, bool) []string{
  2792. core.ObjectNameField: ValidateNodeName,
  2793. }
  2794. // ValidateNodeFieldSelectorRequirement tests that the specified NodeSelectorRequirement fields has valid data
  2795. func ValidateNodeFieldSelectorRequirement(req core.NodeSelectorRequirement, fldPath *field.Path) field.ErrorList {
  2796. allErrs := field.ErrorList{}
  2797. switch req.Operator {
  2798. case core.NodeSelectorOpIn, core.NodeSelectorOpNotIn:
  2799. if len(req.Values) != 1 {
  2800. allErrs = append(allErrs, field.Required(fldPath.Child("values"),
  2801. "must be only one value when `operator` is 'In' or 'NotIn' for node field selector"))
  2802. }
  2803. default:
  2804. allErrs = append(allErrs, field.Invalid(fldPath.Child("operator"), req.Operator, "not a valid selector operator"))
  2805. }
  2806. if vf, found := nodeFieldSelectorValidators[req.Key]; !found {
  2807. allErrs = append(allErrs, field.Invalid(fldPath.Child("key"), req.Key, "not a valid field selector key"))
  2808. } else {
  2809. for i, v := range req.Values {
  2810. for _, msg := range vf(v, false) {
  2811. allErrs = append(allErrs, field.Invalid(fldPath.Child("values").Index(i), v, msg))
  2812. }
  2813. }
  2814. }
  2815. return allErrs
  2816. }
  2817. // ValidateNodeSelectorTerm tests that the specified node selector term has valid data
  2818. func ValidateNodeSelectorTerm(term core.NodeSelectorTerm, fldPath *field.Path) field.ErrorList {
  2819. allErrs := field.ErrorList{}
  2820. for j, req := range term.MatchExpressions {
  2821. allErrs = append(allErrs, ValidateNodeSelectorRequirement(req, fldPath.Child("matchExpressions").Index(j))...)
  2822. }
  2823. for j, req := range term.MatchFields {
  2824. allErrs = append(allErrs, ValidateNodeFieldSelectorRequirement(req, fldPath.Child("matchFields").Index(j))...)
  2825. }
  2826. return allErrs
  2827. }
  2828. // ValidateNodeSelector tests that the specified nodeSelector fields has valid data
  2829. func ValidateNodeSelector(nodeSelector *core.NodeSelector, fldPath *field.Path) field.ErrorList {
  2830. allErrs := field.ErrorList{}
  2831. termFldPath := fldPath.Child("nodeSelectorTerms")
  2832. if len(nodeSelector.NodeSelectorTerms) == 0 {
  2833. return append(allErrs, field.Required(termFldPath, "must have at least one node selector term"))
  2834. }
  2835. for i, term := range nodeSelector.NodeSelectorTerms {
  2836. allErrs = append(allErrs, ValidateNodeSelectorTerm(term, termFldPath.Index(i))...)
  2837. }
  2838. return allErrs
  2839. }
  2840. // validateTopologySelectorLabelRequirement tests that the specified TopologySelectorLabelRequirement fields has valid data,
  2841. // and constructs a set containing all of its Values.
  2842. func validateTopologySelectorLabelRequirement(rq core.TopologySelectorLabelRequirement, fldPath *field.Path) (sets.String, field.ErrorList) {
  2843. allErrs := field.ErrorList{}
  2844. valueSet := make(sets.String)
  2845. valuesPath := fldPath.Child("values")
  2846. if len(rq.Values) == 0 {
  2847. allErrs = append(allErrs, field.Required(valuesPath, ""))
  2848. }
  2849. // Validate set property of Values field
  2850. for i, value := range rq.Values {
  2851. if valueSet.Has(value) {
  2852. allErrs = append(allErrs, field.Duplicate(valuesPath.Index(i), value))
  2853. }
  2854. valueSet.Insert(value)
  2855. }
  2856. allErrs = append(allErrs, unversionedvalidation.ValidateLabelName(rq.Key, fldPath.Child("key"))...)
  2857. return valueSet, allErrs
  2858. }
  2859. // ValidateTopologySelectorTerm tests that the specified topology selector term has valid data,
  2860. // and constructs a map representing the term in raw form.
  2861. func ValidateTopologySelectorTerm(term core.TopologySelectorTerm, fldPath *field.Path) (map[string]sets.String, field.ErrorList) {
  2862. allErrs := field.ErrorList{}
  2863. exprMap := make(map[string]sets.String)
  2864. exprPath := fldPath.Child("matchLabelExpressions")
  2865. // Allow empty MatchLabelExpressions, in case this field becomes optional in the future.
  2866. for i, req := range term.MatchLabelExpressions {
  2867. idxPath := exprPath.Index(i)
  2868. valueSet, exprErrs := validateTopologySelectorLabelRequirement(req, idxPath)
  2869. allErrs = append(allErrs, exprErrs...)
  2870. // Validate no duplicate keys exist.
  2871. if _, exists := exprMap[req.Key]; exists {
  2872. allErrs = append(allErrs, field.Duplicate(idxPath.Child("key"), req.Key))
  2873. }
  2874. exprMap[req.Key] = valueSet
  2875. }
  2876. return exprMap, allErrs
  2877. }
  2878. // ValidateAvoidPodsInNodeAnnotations tests that the serialized AvoidPods in Node.Annotations has valid data
  2879. func ValidateAvoidPodsInNodeAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList {
  2880. allErrs := field.ErrorList{}
  2881. v1Avoids, err := v1helper.GetAvoidPodsFromNodeAnnotations(annotations)
  2882. if err != nil {
  2883. allErrs = append(allErrs, field.Invalid(fldPath.Child("AvoidPods"), core.PreferAvoidPodsAnnotationKey, err.Error()))
  2884. return allErrs
  2885. }
  2886. var avoids core.AvoidPods
  2887. if err := corev1.Convert_v1_AvoidPods_To_core_AvoidPods(&v1Avoids, &avoids, nil); err != nil {
  2888. allErrs = append(allErrs, field.Invalid(fldPath.Child("AvoidPods"), core.PreferAvoidPodsAnnotationKey, err.Error()))
  2889. return allErrs
  2890. }
  2891. if len(avoids.PreferAvoidPods) != 0 {
  2892. for i, pa := range avoids.PreferAvoidPods {
  2893. idxPath := fldPath.Child(core.PreferAvoidPodsAnnotationKey).Index(i)
  2894. allErrs = append(allErrs, validatePreferAvoidPodsEntry(pa, idxPath)...)
  2895. }
  2896. }
  2897. return allErrs
  2898. }
  2899. // validatePreferAvoidPodsEntry tests if given PreferAvoidPodsEntry has valid data.
  2900. func validatePreferAvoidPodsEntry(avoidPodEntry core.PreferAvoidPodsEntry, fldPath *field.Path) field.ErrorList {
  2901. allErrors := field.ErrorList{}
  2902. if avoidPodEntry.PodSignature.PodController == nil {
  2903. allErrors = append(allErrors, field.Required(fldPath.Child("PodSignature"), ""))
  2904. } else {
  2905. if !*(avoidPodEntry.PodSignature.PodController.Controller) {
  2906. allErrors = append(allErrors,
  2907. field.Invalid(fldPath.Child("PodSignature").Child("PodController").Child("Controller"),
  2908. *(avoidPodEntry.PodSignature.PodController.Controller), "must point to a controller"))
  2909. }
  2910. }
  2911. return allErrors
  2912. }
  2913. // ValidatePreferredSchedulingTerms tests that the specified SoftNodeAffinity fields has valid data
  2914. func ValidatePreferredSchedulingTerms(terms []core.PreferredSchedulingTerm, fldPath *field.Path) field.ErrorList {
  2915. allErrs := field.ErrorList{}
  2916. for i, term := range terms {
  2917. if term.Weight <= 0 || term.Weight > 100 {
  2918. allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("weight"), term.Weight, "must be in the range 1-100"))
  2919. }
  2920. allErrs = append(allErrs, ValidateNodeSelectorTerm(term.Preference, fldPath.Index(i).Child("preference"))...)
  2921. }
  2922. return allErrs
  2923. }
  2924. // validatePodAffinityTerm tests that the specified podAffinityTerm fields have valid data
  2925. func validatePodAffinityTerm(podAffinityTerm core.PodAffinityTerm, fldPath *field.Path) field.ErrorList {
  2926. allErrs := field.ErrorList{}
  2927. allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(podAffinityTerm.LabelSelector, fldPath.Child("matchExpressions"))...)
  2928. for _, name := range podAffinityTerm.Namespaces {
  2929. for _, msg := range ValidateNamespaceName(name, false) {
  2930. allErrs = append(allErrs, field.Invalid(fldPath.Child("namespace"), name, msg))
  2931. }
  2932. }
  2933. if len(podAffinityTerm.TopologyKey) == 0 {
  2934. allErrs = append(allErrs, field.Required(fldPath.Child("topologyKey"), "can not be empty"))
  2935. }
  2936. return append(allErrs, unversionedvalidation.ValidateLabelName(podAffinityTerm.TopologyKey, fldPath.Child("topologyKey"))...)
  2937. }
  2938. // validatePodAffinityTerms tests that the specified podAffinityTerms fields have valid data
  2939. func validatePodAffinityTerms(podAffinityTerms []core.PodAffinityTerm, fldPath *field.Path) field.ErrorList {
  2940. allErrs := field.ErrorList{}
  2941. for i, podAffinityTerm := range podAffinityTerms {
  2942. allErrs = append(allErrs, validatePodAffinityTerm(podAffinityTerm, fldPath.Index(i))...)
  2943. }
  2944. return allErrs
  2945. }
  2946. // validateWeightedPodAffinityTerms tests that the specified weightedPodAffinityTerms fields have valid data
  2947. func validateWeightedPodAffinityTerms(weightedPodAffinityTerms []core.WeightedPodAffinityTerm, fldPath *field.Path) field.ErrorList {
  2948. allErrs := field.ErrorList{}
  2949. for j, weightedTerm := range weightedPodAffinityTerms {
  2950. if weightedTerm.Weight <= 0 || weightedTerm.Weight > 100 {
  2951. allErrs = append(allErrs, field.Invalid(fldPath.Index(j).Child("weight"), weightedTerm.Weight, "must be in the range 1-100"))
  2952. }
  2953. allErrs = append(allErrs, validatePodAffinityTerm(weightedTerm.PodAffinityTerm, fldPath.Index(j).Child("podAffinityTerm"))...)
  2954. }
  2955. return allErrs
  2956. }
  2957. // validatePodAntiAffinity tests that the specified podAntiAffinity fields have valid data
  2958. func validatePodAntiAffinity(podAntiAffinity *core.PodAntiAffinity, fldPath *field.Path) field.ErrorList {
  2959. allErrs := field.ErrorList{}
  2960. // TODO:Uncomment below code once RequiredDuringSchedulingRequiredDuringExecution is implemented.
  2961. // if podAntiAffinity.RequiredDuringSchedulingRequiredDuringExecution != nil {
  2962. // allErrs = append(allErrs, validatePodAffinityTerms(podAntiAffinity.RequiredDuringSchedulingRequiredDuringExecution, false,
  2963. // fldPath.Child("requiredDuringSchedulingRequiredDuringExecution"))...)
  2964. //}
  2965. if podAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution != nil {
  2966. allErrs = append(allErrs, validatePodAffinityTerms(podAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution,
  2967. fldPath.Child("requiredDuringSchedulingIgnoredDuringExecution"))...)
  2968. }
  2969. if podAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution != nil {
  2970. allErrs = append(allErrs, validateWeightedPodAffinityTerms(podAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution,
  2971. fldPath.Child("preferredDuringSchedulingIgnoredDuringExecution"))...)
  2972. }
  2973. return allErrs
  2974. }
  2975. // validateNodeAffinity tests that the specified nodeAffinity fields have valid data
  2976. func validateNodeAffinity(na *core.NodeAffinity, fldPath *field.Path) field.ErrorList {
  2977. allErrs := field.ErrorList{}
  2978. // TODO: Uncomment the next three lines once RequiredDuringSchedulingRequiredDuringExecution is implemented.
  2979. // if na.RequiredDuringSchedulingRequiredDuringExecution != nil {
  2980. // allErrs = append(allErrs, ValidateNodeSelector(na.RequiredDuringSchedulingRequiredDuringExecution, fldPath.Child("requiredDuringSchedulingRequiredDuringExecution"))...)
  2981. // }
  2982. if na.RequiredDuringSchedulingIgnoredDuringExecution != nil {
  2983. allErrs = append(allErrs, ValidateNodeSelector(na.RequiredDuringSchedulingIgnoredDuringExecution, fldPath.Child("requiredDuringSchedulingIgnoredDuringExecution"))...)
  2984. }
  2985. if len(na.PreferredDuringSchedulingIgnoredDuringExecution) > 0 {
  2986. allErrs = append(allErrs, ValidatePreferredSchedulingTerms(na.PreferredDuringSchedulingIgnoredDuringExecution, fldPath.Child("preferredDuringSchedulingIgnoredDuringExecution"))...)
  2987. }
  2988. return allErrs
  2989. }
  2990. // validatePodAffinity tests that the specified podAffinity fields have valid data
  2991. func validatePodAffinity(podAffinity *core.PodAffinity, fldPath *field.Path) field.ErrorList {
  2992. allErrs := field.ErrorList{}
  2993. // TODO:Uncomment below code once RequiredDuringSchedulingRequiredDuringExecution is implemented.
  2994. // if podAffinity.RequiredDuringSchedulingRequiredDuringExecution != nil {
  2995. // allErrs = append(allErrs, validatePodAffinityTerms(podAffinity.RequiredDuringSchedulingRequiredDuringExecution, false,
  2996. // fldPath.Child("requiredDuringSchedulingRequiredDuringExecution"))...)
  2997. //}
  2998. if podAffinity.RequiredDuringSchedulingIgnoredDuringExecution != nil {
  2999. allErrs = append(allErrs, validatePodAffinityTerms(podAffinity.RequiredDuringSchedulingIgnoredDuringExecution,
  3000. fldPath.Child("requiredDuringSchedulingIgnoredDuringExecution"))...)
  3001. }
  3002. if podAffinity.PreferredDuringSchedulingIgnoredDuringExecution != nil {
  3003. allErrs = append(allErrs, validateWeightedPodAffinityTerms(podAffinity.PreferredDuringSchedulingIgnoredDuringExecution,
  3004. fldPath.Child("preferredDuringSchedulingIgnoredDuringExecution"))...)
  3005. }
  3006. return allErrs
  3007. }
  3008. func ValidateSeccompProfile(p string, fldPath *field.Path) field.ErrorList {
  3009. if p == core.SeccompProfileRuntimeDefault || p == core.DeprecatedSeccompProfileDockerDefault {
  3010. return nil
  3011. }
  3012. if p == "unconfined" {
  3013. return nil
  3014. }
  3015. if strings.HasPrefix(p, "localhost/") {
  3016. return validateLocalDescendingPath(strings.TrimPrefix(p, "localhost/"), fldPath)
  3017. }
  3018. return field.ErrorList{field.Invalid(fldPath, p, "must be a valid seccomp profile")}
  3019. }
  3020. func ValidateSeccompPodAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList {
  3021. allErrs := field.ErrorList{}
  3022. if p, exists := annotations[core.SeccompPodAnnotationKey]; exists {
  3023. allErrs = append(allErrs, ValidateSeccompProfile(p, fldPath.Child(core.SeccompPodAnnotationKey))...)
  3024. }
  3025. for k, p := range annotations {
  3026. if strings.HasPrefix(k, core.SeccompContainerAnnotationKeyPrefix) {
  3027. allErrs = append(allErrs, ValidateSeccompProfile(p, fldPath.Child(k))...)
  3028. }
  3029. }
  3030. return allErrs
  3031. }
  3032. func ValidateAppArmorPodAnnotations(annotations map[string]string, spec *core.PodSpec, fldPath *field.Path) field.ErrorList {
  3033. allErrs := field.ErrorList{}
  3034. for k, p := range annotations {
  3035. if !strings.HasPrefix(k, apparmor.ContainerAnnotationKeyPrefix) {
  3036. continue
  3037. }
  3038. containerName := strings.TrimPrefix(k, apparmor.ContainerAnnotationKeyPrefix)
  3039. if !podSpecHasContainer(spec, containerName) {
  3040. allErrs = append(allErrs, field.Invalid(fldPath.Key(k), containerName, "container not found"))
  3041. }
  3042. if err := apparmor.ValidateProfileFormat(p); err != nil {
  3043. allErrs = append(allErrs, field.Invalid(fldPath.Key(k), p, err.Error()))
  3044. }
  3045. }
  3046. return allErrs
  3047. }
  3048. func podSpecHasContainer(spec *core.PodSpec, containerName string) bool {
  3049. for _, c := range spec.InitContainers {
  3050. if c.Name == containerName {
  3051. return true
  3052. }
  3053. }
  3054. for _, c := range spec.Containers {
  3055. if c.Name == containerName {
  3056. return true
  3057. }
  3058. }
  3059. return false
  3060. }
  3061. const (
  3062. // a sysctl segment regex, concatenated with dots to form a sysctl name
  3063. SysctlSegmentFmt string = "[a-z0-9]([-_a-z0-9]*[a-z0-9])?"
  3064. // a sysctl name regex
  3065. SysctlFmt string = "(" + SysctlSegmentFmt + "\\.)*" + SysctlSegmentFmt
  3066. // the maximal length of a sysctl name
  3067. SysctlMaxLength int = 253
  3068. )
  3069. var sysctlRegexp = regexp.MustCompile("^" + SysctlFmt + "$")
  3070. // IsValidSysctlName checks that the given string is a valid sysctl name,
  3071. // i.e. matches SysctlFmt.
  3072. func IsValidSysctlName(name string) bool {
  3073. if len(name) > SysctlMaxLength {
  3074. return false
  3075. }
  3076. return sysctlRegexp.MatchString(name)
  3077. }
  3078. func validateSysctls(sysctls []core.Sysctl, fldPath *field.Path) field.ErrorList {
  3079. allErrs := field.ErrorList{}
  3080. names := make(map[string]struct{})
  3081. for i, s := range sysctls {
  3082. if len(s.Name) == 0 {
  3083. allErrs = append(allErrs, field.Required(fldPath.Index(i).Child("name"), ""))
  3084. } else if !IsValidSysctlName(s.Name) {
  3085. allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("name"), s.Name, fmt.Sprintf("must have at most %d characters and match regex %s", SysctlMaxLength, SysctlFmt)))
  3086. } else if _, ok := names[s.Name]; ok {
  3087. allErrs = append(allErrs, field.Duplicate(fldPath.Index(i).Child("name"), s.Name))
  3088. }
  3089. names[s.Name] = struct{}{}
  3090. }
  3091. return allErrs
  3092. }
  3093. // ValidatePodSecurityContext test that the specified PodSecurityContext has valid data.
  3094. func ValidatePodSecurityContext(securityContext *core.PodSecurityContext, spec *core.PodSpec, specPath, fldPath *field.Path) field.ErrorList {
  3095. allErrs := field.ErrorList{}
  3096. if securityContext != nil {
  3097. allErrs = append(allErrs, validateHostNetwork(securityContext.HostNetwork, spec.Containers, specPath.Child("containers"))...)
  3098. if securityContext.FSGroup != nil {
  3099. for _, msg := range validation.IsValidGroupID(*securityContext.FSGroup) {
  3100. allErrs = append(allErrs, field.Invalid(fldPath.Child("fsGroup"), *(securityContext.FSGroup), msg))
  3101. }
  3102. }
  3103. if securityContext.RunAsUser != nil {
  3104. for _, msg := range validation.IsValidUserID(*securityContext.RunAsUser) {
  3105. allErrs = append(allErrs, field.Invalid(fldPath.Child("runAsUser"), *(securityContext.RunAsUser), msg))
  3106. }
  3107. }
  3108. if securityContext.RunAsGroup != nil {
  3109. for _, msg := range validation.IsValidGroupID(*securityContext.RunAsGroup) {
  3110. allErrs = append(allErrs, field.Invalid(fldPath.Child("runAsGroup"), *(securityContext.RunAsGroup), msg))
  3111. }
  3112. }
  3113. for g, gid := range securityContext.SupplementalGroups {
  3114. for _, msg := range validation.IsValidGroupID(gid) {
  3115. allErrs = append(allErrs, field.Invalid(fldPath.Child("supplementalGroups").Index(g), gid, msg))
  3116. }
  3117. }
  3118. if securityContext.ShareProcessNamespace != nil && securityContext.HostPID && *securityContext.ShareProcessNamespace {
  3119. allErrs = append(allErrs, field.Invalid(fldPath.Child("shareProcessNamespace"), *securityContext.ShareProcessNamespace, "ShareProcessNamespace and HostPID cannot both be enabled"))
  3120. }
  3121. if len(securityContext.Sysctls) != 0 {
  3122. allErrs = append(allErrs, validateSysctls(securityContext.Sysctls, fldPath.Child("sysctls"))...)
  3123. }
  3124. allErrs = append(allErrs, validateWindowsSecurityContextOptions(securityContext.WindowsOptions, fldPath.Child("windowsOptions"))...)
  3125. }
  3126. return allErrs
  3127. }
  3128. func ValidateContainerUpdates(newContainers, oldContainers []core.Container, fldPath *field.Path) (allErrs field.ErrorList, stop bool) {
  3129. allErrs = field.ErrorList{}
  3130. if len(newContainers) != len(oldContainers) {
  3131. //TODO: Pinpoint the specific container that causes the invalid error after we have strategic merge diff
  3132. allErrs = append(allErrs, field.Forbidden(fldPath, "pod updates may not add or remove containers"))
  3133. return allErrs, true
  3134. }
  3135. // validate updated container images
  3136. for i, ctr := range newContainers {
  3137. if len(ctr.Image) == 0 {
  3138. allErrs = append(allErrs, field.Required(fldPath.Index(i).Child("image"), ""))
  3139. }
  3140. // this is only called from ValidatePodUpdate so its safe to check leading/trailing whitespace.
  3141. if len(strings.TrimSpace(ctr.Image)) != len(ctr.Image) {
  3142. allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("image"), ctr.Image, "must not have leading or trailing whitespace"))
  3143. }
  3144. }
  3145. return allErrs, false
  3146. }
  3147. // ValidatePodUpdate tests to see if the update is legal for an end user to make. newPod is updated with fields
  3148. // that cannot be changed.
  3149. func ValidatePodUpdate(newPod, oldPod *core.Pod) field.ErrorList {
  3150. fldPath := field.NewPath("metadata")
  3151. allErrs := ValidateObjectMetaUpdate(&newPod.ObjectMeta, &oldPod.ObjectMeta, fldPath)
  3152. allErrs = append(allErrs, ValidatePodSpecificAnnotationUpdates(newPod, oldPod, fldPath.Child("annotations"))...)
  3153. specPath := field.NewPath("spec")
  3154. // validate updateable fields:
  3155. // 1. spec.containers[*].image
  3156. // 2. spec.initContainers[*].image
  3157. // 3. spec.activeDeadlineSeconds
  3158. containerErrs, stop := ValidateContainerUpdates(newPod.Spec.Containers, oldPod.Spec.Containers, specPath.Child("containers"))
  3159. allErrs = append(allErrs, containerErrs...)
  3160. if stop {
  3161. return allErrs
  3162. }
  3163. containerErrs, stop = ValidateContainerUpdates(newPod.Spec.InitContainers, oldPod.Spec.InitContainers, specPath.Child("initContainers"))
  3164. allErrs = append(allErrs, containerErrs...)
  3165. if stop {
  3166. return allErrs
  3167. }
  3168. // validate updated spec.activeDeadlineSeconds. two types of updates are allowed:
  3169. // 1. from nil to a positive value
  3170. // 2. from a positive value to a lesser, non-negative value
  3171. if newPod.Spec.ActiveDeadlineSeconds != nil {
  3172. newActiveDeadlineSeconds := *newPod.Spec.ActiveDeadlineSeconds
  3173. if newActiveDeadlineSeconds < 0 || newActiveDeadlineSeconds > math.MaxInt32 {
  3174. allErrs = append(allErrs, field.Invalid(specPath.Child("activeDeadlineSeconds"), newActiveDeadlineSeconds, validation.InclusiveRangeError(0, math.MaxInt32)))
  3175. return allErrs
  3176. }
  3177. if oldPod.Spec.ActiveDeadlineSeconds != nil {
  3178. oldActiveDeadlineSeconds := *oldPod.Spec.ActiveDeadlineSeconds
  3179. if oldActiveDeadlineSeconds < newActiveDeadlineSeconds {
  3180. allErrs = append(allErrs, field.Invalid(specPath.Child("activeDeadlineSeconds"), newActiveDeadlineSeconds, "must be less than or equal to previous value"))
  3181. return allErrs
  3182. }
  3183. }
  3184. } else if oldPod.Spec.ActiveDeadlineSeconds != nil {
  3185. allErrs = append(allErrs, field.Invalid(specPath.Child("activeDeadlineSeconds"), newPod.Spec.ActiveDeadlineSeconds, "must not update from a positive integer to nil value"))
  3186. }
  3187. // handle updateable fields by munging those fields prior to deep equal comparison.
  3188. mungedPod := *newPod
  3189. // munge spec.containers[*].image
  3190. var newContainers []core.Container
  3191. for ix, container := range mungedPod.Spec.Containers {
  3192. container.Image = oldPod.Spec.Containers[ix].Image
  3193. newContainers = append(newContainers, container)
  3194. }
  3195. mungedPod.Spec.Containers = newContainers
  3196. // munge spec.initContainers[*].image
  3197. var newInitContainers []core.Container
  3198. for ix, container := range mungedPod.Spec.InitContainers {
  3199. container.Image = oldPod.Spec.InitContainers[ix].Image
  3200. newInitContainers = append(newInitContainers, container)
  3201. }
  3202. mungedPod.Spec.InitContainers = newInitContainers
  3203. // munge spec.activeDeadlineSeconds
  3204. mungedPod.Spec.ActiveDeadlineSeconds = nil
  3205. if oldPod.Spec.ActiveDeadlineSeconds != nil {
  3206. activeDeadlineSeconds := *oldPod.Spec.ActiveDeadlineSeconds
  3207. mungedPod.Spec.ActiveDeadlineSeconds = &activeDeadlineSeconds
  3208. }
  3209. // Allow only additions to tolerations updates.
  3210. mungedPod.Spec.Tolerations = oldPod.Spec.Tolerations
  3211. allErrs = append(allErrs, validateOnlyAddedTolerations(newPod.Spec.Tolerations, oldPod.Spec.Tolerations, specPath.Child("tolerations"))...)
  3212. if !apiequality.Semantic.DeepEqual(mungedPod.Spec, oldPod.Spec) {
  3213. // This diff isn't perfect, but it's a helluva lot better an "I'm not going to tell you what the difference is".
  3214. //TODO: Pinpoint the specific field that causes the invalid error after we have strategic merge diff
  3215. specDiff := diff.ObjectDiff(mungedPod.Spec, oldPod.Spec)
  3216. allErrs = append(allErrs, field.Forbidden(specPath, fmt.Sprintf("pod updates may not change fields other than `spec.containers[*].image`, `spec.initContainers[*].image`, `spec.activeDeadlineSeconds` or `spec.tolerations` (only additions to existing tolerations)\n%v", specDiff)))
  3217. }
  3218. return allErrs
  3219. }
  3220. // ValidateContainerStateTransition test to if any illegal container state transitions are being attempted
  3221. func ValidateContainerStateTransition(newStatuses, oldStatuses []core.ContainerStatus, fldpath *field.Path, restartPolicy core.RestartPolicy) field.ErrorList {
  3222. allErrs := field.ErrorList{}
  3223. // If we should always restart, containers are allowed to leave the terminated state
  3224. if restartPolicy == core.RestartPolicyAlways {
  3225. return allErrs
  3226. }
  3227. for i, oldStatus := range oldStatuses {
  3228. // Skip any container that is not terminated
  3229. if oldStatus.State.Terminated == nil {
  3230. continue
  3231. }
  3232. // Skip any container that failed but is allowed to restart
  3233. if oldStatus.State.Terminated.ExitCode != 0 && restartPolicy == core.RestartPolicyOnFailure {
  3234. continue
  3235. }
  3236. for _, newStatus := range newStatuses {
  3237. if oldStatus.Name == newStatus.Name && newStatus.State.Terminated == nil {
  3238. allErrs = append(allErrs, field.Forbidden(fldpath.Index(i).Child("state"), "may not be transitioned to non-terminated state"))
  3239. }
  3240. }
  3241. }
  3242. return allErrs
  3243. }
  3244. // ValidatePodStatusUpdate tests to see if the update is legal for an end user to make. newPod is updated with fields
  3245. // that cannot be changed.
  3246. func ValidatePodStatusUpdate(newPod, oldPod *core.Pod) field.ErrorList {
  3247. fldPath := field.NewPath("metadata")
  3248. allErrs := ValidateObjectMetaUpdate(&newPod.ObjectMeta, &oldPod.ObjectMeta, fldPath)
  3249. allErrs = append(allErrs, ValidatePodSpecificAnnotationUpdates(newPod, oldPod, fldPath.Child("annotations"))...)
  3250. allErrs = append(allErrs, validatePodConditions(newPod.Status.Conditions, fldPath.Child("conditions"))...)
  3251. fldPath = field.NewPath("status")
  3252. if newPod.Spec.NodeName != oldPod.Spec.NodeName {
  3253. allErrs = append(allErrs, field.Forbidden(fldPath.Child("nodeName"), "may not be changed directly"))
  3254. }
  3255. if newPod.Status.NominatedNodeName != oldPod.Status.NominatedNodeName && len(newPod.Status.NominatedNodeName) > 0 {
  3256. for _, msg := range ValidateNodeName(newPod.Status.NominatedNodeName, false) {
  3257. allErrs = append(allErrs, field.Invalid(fldPath.Child("nominatedNodeName"), newPod.Status.NominatedNodeName, msg))
  3258. }
  3259. }
  3260. // If pod should not restart, make sure the status update does not transition
  3261. // any terminated containers to a non-terminated state.
  3262. allErrs = append(allErrs, ValidateContainerStateTransition(newPod.Status.ContainerStatuses, oldPod.Status.ContainerStatuses, fldPath.Child("containerStatuses"), oldPod.Spec.RestartPolicy)...)
  3263. allErrs = append(allErrs, ValidateContainerStateTransition(newPod.Status.InitContainerStatuses, oldPod.Status.InitContainerStatuses, fldPath.Child("initContainerStatuses"), oldPod.Spec.RestartPolicy)...)
  3264. // For status update we ignore changes to pod spec.
  3265. newPod.Spec = oldPod.Spec
  3266. return allErrs
  3267. }
  3268. // validatePodConditions tests if the custom pod conditions are valid.
  3269. func validatePodConditions(conditions []core.PodCondition, fldPath *field.Path) field.ErrorList {
  3270. allErrs := field.ErrorList{}
  3271. systemConditions := sets.NewString(string(core.PodScheduled), string(core.PodReady), string(core.PodInitialized))
  3272. for i, condition := range conditions {
  3273. if systemConditions.Has(string(condition.Type)) {
  3274. continue
  3275. }
  3276. for _, msg := range validation.IsQualifiedName(string(condition.Type)) {
  3277. allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("Type"), string(condition.Type), msg))
  3278. }
  3279. }
  3280. return allErrs
  3281. }
  3282. // ValidatePodBinding tests if required fields in the pod binding are legal.
  3283. func ValidatePodBinding(binding *core.Binding) field.ErrorList {
  3284. allErrs := field.ErrorList{}
  3285. if len(binding.Target.Kind) != 0 && binding.Target.Kind != "Node" {
  3286. // TODO: When validation becomes versioned, this gets more complicated.
  3287. allErrs = append(allErrs, field.NotSupported(field.NewPath("target", "kind"), binding.Target.Kind, []string{"Node", "<empty>"}))
  3288. }
  3289. if len(binding.Target.Name) == 0 {
  3290. // TODO: When validation becomes versioned, this gets more complicated.
  3291. allErrs = append(allErrs, field.Required(field.NewPath("target", "name"), ""))
  3292. }
  3293. return allErrs
  3294. }
  3295. // ValidatePodTemplate tests if required fields in the pod template are set.
  3296. func ValidatePodTemplate(pod *core.PodTemplate) field.ErrorList {
  3297. allErrs := ValidateObjectMeta(&pod.ObjectMeta, true, ValidatePodName, field.NewPath("metadata"))
  3298. allErrs = append(allErrs, ValidatePodTemplateSpec(&pod.Template, field.NewPath("template"))...)
  3299. return allErrs
  3300. }
  3301. // ValidatePodTemplateUpdate tests to see if the update is legal for an end user to make. newPod is updated with fields
  3302. // that cannot be changed.
  3303. func ValidatePodTemplateUpdate(newPod, oldPod *core.PodTemplate) field.ErrorList {
  3304. allErrs := ValidateObjectMetaUpdate(&newPod.ObjectMeta, &oldPod.ObjectMeta, field.NewPath("metadata"))
  3305. allErrs = append(allErrs, ValidatePodTemplateSpec(&newPod.Template, field.NewPath("template"))...)
  3306. return allErrs
  3307. }
  3308. var supportedSessionAffinityType = sets.NewString(string(core.ServiceAffinityClientIP), string(core.ServiceAffinityNone))
  3309. var supportedServiceType = sets.NewString(string(core.ServiceTypeClusterIP), string(core.ServiceTypeNodePort),
  3310. string(core.ServiceTypeLoadBalancer), string(core.ServiceTypeExternalName))
  3311. // ValidateService tests if required fields/annotations of a Service are valid.
  3312. func ValidateService(service *core.Service) field.ErrorList {
  3313. allErrs := ValidateObjectMeta(&service.ObjectMeta, true, ValidateServiceName, field.NewPath("metadata"))
  3314. specPath := field.NewPath("spec")
  3315. isHeadlessService := service.Spec.ClusterIP == core.ClusterIPNone
  3316. if len(service.Spec.Ports) == 0 && !isHeadlessService && service.Spec.Type != core.ServiceTypeExternalName {
  3317. allErrs = append(allErrs, field.Required(specPath.Child("ports"), ""))
  3318. }
  3319. switch service.Spec.Type {
  3320. case core.ServiceTypeLoadBalancer:
  3321. for ix := range service.Spec.Ports {
  3322. port := &service.Spec.Ports[ix]
  3323. // This is a workaround for broken cloud environments that
  3324. // over-open firewalls. Hopefully it can go away when more clouds
  3325. // understand containers better.
  3326. if port.Port == ports.KubeletPort {
  3327. portPath := specPath.Child("ports").Index(ix)
  3328. allErrs = append(allErrs, field.Invalid(portPath, port.Port, fmt.Sprintf("may not expose port %v externally since it is used by kubelet", ports.KubeletPort)))
  3329. }
  3330. }
  3331. if service.Spec.ClusterIP == "None" {
  3332. allErrs = append(allErrs, field.Invalid(specPath.Child("clusterIP"), service.Spec.ClusterIP, "may not be set to 'None' for LoadBalancer services"))
  3333. }
  3334. case core.ServiceTypeNodePort:
  3335. if service.Spec.ClusterIP == "None" {
  3336. allErrs = append(allErrs, field.Invalid(specPath.Child("clusterIP"), service.Spec.ClusterIP, "may not be set to 'None' for NodePort services"))
  3337. }
  3338. case core.ServiceTypeExternalName:
  3339. if service.Spec.ClusterIP != "" {
  3340. allErrs = append(allErrs, field.Forbidden(specPath.Child("clusterIP"), "must be empty for ExternalName services"))
  3341. }
  3342. // The value (a CNAME) may have a trailing dot to denote it as fully qualified
  3343. cname := strings.TrimSuffix(service.Spec.ExternalName, ".")
  3344. if len(cname) > 0 {
  3345. allErrs = append(allErrs, ValidateDNS1123Subdomain(cname, specPath.Child("externalName"))...)
  3346. } else {
  3347. allErrs = append(allErrs, field.Required(specPath.Child("externalName"), ""))
  3348. }
  3349. }
  3350. allPortNames := sets.String{}
  3351. portsPath := specPath.Child("ports")
  3352. for i := range service.Spec.Ports {
  3353. portPath := portsPath.Index(i)
  3354. allErrs = append(allErrs, validateServicePort(&service.Spec.Ports[i], len(service.Spec.Ports) > 1, isHeadlessService, &allPortNames, portPath)...)
  3355. }
  3356. if service.Spec.Selector != nil {
  3357. allErrs = append(allErrs, unversionedvalidation.ValidateLabels(service.Spec.Selector, specPath.Child("selector"))...)
  3358. }
  3359. if len(service.Spec.SessionAffinity) == 0 {
  3360. allErrs = append(allErrs, field.Required(specPath.Child("sessionAffinity"), ""))
  3361. } else if !supportedSessionAffinityType.Has(string(service.Spec.SessionAffinity)) {
  3362. allErrs = append(allErrs, field.NotSupported(specPath.Child("sessionAffinity"), service.Spec.SessionAffinity, supportedSessionAffinityType.List()))
  3363. }
  3364. if service.Spec.SessionAffinity == core.ServiceAffinityClientIP {
  3365. allErrs = append(allErrs, validateClientIPAffinityConfig(service.Spec.SessionAffinityConfig, specPath.Child("sessionAffinityConfig"))...)
  3366. } else if service.Spec.SessionAffinity == core.ServiceAffinityNone {
  3367. if service.Spec.SessionAffinityConfig != nil {
  3368. allErrs = append(allErrs, field.Forbidden(specPath.Child("sessionAffinityConfig"), fmt.Sprintf("must not be set when session affinity is %s", string(core.ServiceAffinityNone))))
  3369. }
  3370. }
  3371. if helper.IsServiceIPSet(service) {
  3372. if ip := net.ParseIP(service.Spec.ClusterIP); ip == nil {
  3373. allErrs = append(allErrs, field.Invalid(specPath.Child("clusterIP"), service.Spec.ClusterIP, "must be empty, 'None', or a valid IP address"))
  3374. }
  3375. }
  3376. ipPath := specPath.Child("externalIPs")
  3377. for i, ip := range service.Spec.ExternalIPs {
  3378. idxPath := ipPath.Index(i)
  3379. if msgs := validation.IsValidIP(ip); len(msgs) != 0 {
  3380. for i := range msgs {
  3381. allErrs = append(allErrs, field.Invalid(idxPath, ip, msgs[i]))
  3382. }
  3383. } else {
  3384. allErrs = append(allErrs, validateNonSpecialIP(ip, idxPath)...)
  3385. }
  3386. }
  3387. if len(service.Spec.Type) == 0 {
  3388. allErrs = append(allErrs, field.Required(specPath.Child("type"), ""))
  3389. } else if !supportedServiceType.Has(string(service.Spec.Type)) {
  3390. allErrs = append(allErrs, field.NotSupported(specPath.Child("type"), service.Spec.Type, supportedServiceType.List()))
  3391. }
  3392. if service.Spec.Type == core.ServiceTypeLoadBalancer {
  3393. portsPath := specPath.Child("ports")
  3394. includeProtocols := sets.NewString()
  3395. for i := range service.Spec.Ports {
  3396. portPath := portsPath.Index(i)
  3397. if !supportedPortProtocols.Has(string(service.Spec.Ports[i].Protocol)) {
  3398. allErrs = append(allErrs, field.Invalid(portPath.Child("protocol"), service.Spec.Ports[i].Protocol, "cannot create an external load balancer with non-TCP/UDP/SCTP ports"))
  3399. } else {
  3400. includeProtocols.Insert(string(service.Spec.Ports[i].Protocol))
  3401. }
  3402. }
  3403. if includeProtocols.Len() > 1 {
  3404. allErrs = append(allErrs, field.Invalid(portsPath, service.Spec.Ports, "cannot create an external load balancer with mix protocols"))
  3405. }
  3406. }
  3407. if service.Spec.Type == core.ServiceTypeClusterIP {
  3408. portsPath := specPath.Child("ports")
  3409. for i := range service.Spec.Ports {
  3410. portPath := portsPath.Index(i)
  3411. if service.Spec.Ports[i].NodePort != 0 {
  3412. allErrs = append(allErrs, field.Forbidden(portPath.Child("nodePort"), "may not be used when `type` is 'ClusterIP'"))
  3413. }
  3414. }
  3415. }
  3416. // Check for duplicate NodePorts, considering (protocol,port) pairs
  3417. portsPath = specPath.Child("ports")
  3418. nodePorts := make(map[core.ServicePort]bool)
  3419. for i := range service.Spec.Ports {
  3420. port := &service.Spec.Ports[i]
  3421. if port.NodePort == 0 {
  3422. continue
  3423. }
  3424. portPath := portsPath.Index(i)
  3425. var key core.ServicePort
  3426. key.Protocol = port.Protocol
  3427. key.NodePort = port.NodePort
  3428. _, found := nodePorts[key]
  3429. if found {
  3430. allErrs = append(allErrs, field.Duplicate(portPath.Child("nodePort"), port.NodePort))
  3431. }
  3432. nodePorts[key] = true
  3433. }
  3434. // Check for duplicate Ports, considering (protocol,port) pairs
  3435. portsPath = specPath.Child("ports")
  3436. ports := make(map[core.ServicePort]bool)
  3437. for i, port := range service.Spec.Ports {
  3438. portPath := portsPath.Index(i)
  3439. key := core.ServicePort{Protocol: port.Protocol, Port: port.Port}
  3440. _, found := ports[key]
  3441. if found {
  3442. allErrs = append(allErrs, field.Duplicate(portPath, key))
  3443. }
  3444. ports[key] = true
  3445. }
  3446. // Validate SourceRange field and annotation
  3447. _, ok := service.Annotations[core.AnnotationLoadBalancerSourceRangesKey]
  3448. if len(service.Spec.LoadBalancerSourceRanges) > 0 || ok {
  3449. var fieldPath *field.Path
  3450. var val string
  3451. if len(service.Spec.LoadBalancerSourceRanges) > 0 {
  3452. fieldPath = specPath.Child("LoadBalancerSourceRanges")
  3453. val = fmt.Sprintf("%v", service.Spec.LoadBalancerSourceRanges)
  3454. } else {
  3455. fieldPath = field.NewPath("metadata", "annotations").Key(core.AnnotationLoadBalancerSourceRangesKey)
  3456. val = service.Annotations[core.AnnotationLoadBalancerSourceRangesKey]
  3457. }
  3458. if service.Spec.Type != core.ServiceTypeLoadBalancer {
  3459. allErrs = append(allErrs, field.Forbidden(fieldPath, "may only be used when `type` is 'LoadBalancer'"))
  3460. }
  3461. _, err := apiservice.GetLoadBalancerSourceRanges(service)
  3462. if err != nil {
  3463. allErrs = append(allErrs, field.Invalid(fieldPath, val, "must be a list of IP ranges. For example, 10.240.0.0/24,10.250.0.0/24 "))
  3464. }
  3465. }
  3466. allErrs = append(allErrs, validateServiceExternalTrafficFieldsValue(service)...)
  3467. return allErrs
  3468. }
  3469. func validateServicePort(sp *core.ServicePort, requireName, isHeadlessService bool, allNames *sets.String, fldPath *field.Path) field.ErrorList {
  3470. allErrs := field.ErrorList{}
  3471. if requireName && len(sp.Name) == 0 {
  3472. allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
  3473. } else if len(sp.Name) != 0 {
  3474. allErrs = append(allErrs, ValidateDNS1123Label(sp.Name, fldPath.Child("name"))...)
  3475. if allNames.Has(sp.Name) {
  3476. allErrs = append(allErrs, field.Duplicate(fldPath.Child("name"), sp.Name))
  3477. } else {
  3478. allNames.Insert(sp.Name)
  3479. }
  3480. }
  3481. for _, msg := range validation.IsValidPortNum(int(sp.Port)) {
  3482. allErrs = append(allErrs, field.Invalid(fldPath.Child("port"), sp.Port, msg))
  3483. }
  3484. if len(sp.Protocol) == 0 {
  3485. allErrs = append(allErrs, field.Required(fldPath.Child("protocol"), ""))
  3486. } else if !supportedPortProtocols.Has(string(sp.Protocol)) {
  3487. allErrs = append(allErrs, field.NotSupported(fldPath.Child("protocol"), sp.Protocol, supportedPortProtocols.List()))
  3488. }
  3489. allErrs = append(allErrs, ValidatePortNumOrName(sp.TargetPort, fldPath.Child("targetPort"))...)
  3490. // in the v1 API, targetPorts on headless services were tolerated.
  3491. // once we have version-specific validation, we can reject this on newer API versions, but until then, we have to tolerate it for compatibility.
  3492. //
  3493. // if isHeadlessService {
  3494. // if sp.TargetPort.Type == intstr.String || (sp.TargetPort.Type == intstr.Int && sp.Port != sp.TargetPort.IntValue()) {
  3495. // allErrs = append(allErrs, field.Invalid(fldPath.Child("targetPort"), sp.TargetPort, "must be equal to the value of 'port' when clusterIP = None"))
  3496. // }
  3497. // }
  3498. return allErrs
  3499. }
  3500. // validateServiceExternalTrafficFieldsValue validates ExternalTraffic related annotations
  3501. // have legal value.
  3502. func validateServiceExternalTrafficFieldsValue(service *core.Service) field.ErrorList {
  3503. allErrs := field.ErrorList{}
  3504. // Check first class fields.
  3505. if service.Spec.ExternalTrafficPolicy != "" &&
  3506. service.Spec.ExternalTrafficPolicy != core.ServiceExternalTrafficPolicyTypeCluster &&
  3507. service.Spec.ExternalTrafficPolicy != core.ServiceExternalTrafficPolicyTypeLocal {
  3508. allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("externalTrafficPolicy"), service.Spec.ExternalTrafficPolicy,
  3509. fmt.Sprintf("ExternalTrafficPolicy must be empty, %v or %v", core.ServiceExternalTrafficPolicyTypeCluster, core.ServiceExternalTrafficPolicyTypeLocal)))
  3510. }
  3511. if service.Spec.HealthCheckNodePort < 0 {
  3512. allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("healthCheckNodePort"), service.Spec.HealthCheckNodePort,
  3513. "HealthCheckNodePort must be not less than 0"))
  3514. }
  3515. return allErrs
  3516. }
  3517. // ValidateServiceExternalTrafficFieldsCombination validates if ExternalTrafficPolicy,
  3518. // HealthCheckNodePort and Type combination are legal. For update, it should be called
  3519. // after clearing externalTraffic related fields for the ease of transitioning between
  3520. // different service types.
  3521. func ValidateServiceExternalTrafficFieldsCombination(service *core.Service) field.ErrorList {
  3522. allErrs := field.ErrorList{}
  3523. if service.Spec.Type != core.ServiceTypeLoadBalancer &&
  3524. service.Spec.Type != core.ServiceTypeNodePort &&
  3525. service.Spec.ExternalTrafficPolicy != "" {
  3526. allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "externalTrafficPolicy"), service.Spec.ExternalTrafficPolicy,
  3527. "ExternalTrafficPolicy can only be set on NodePort and LoadBalancer service"))
  3528. }
  3529. if !apiservice.NeedsHealthCheck(service) &&
  3530. service.Spec.HealthCheckNodePort != 0 {
  3531. allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "healthCheckNodePort"), service.Spec.HealthCheckNodePort,
  3532. "HealthCheckNodePort can only be set on LoadBalancer service with ExternalTrafficPolicy=Local"))
  3533. }
  3534. return allErrs
  3535. }
  3536. // ValidateServiceUpdate tests if required fields in the service are set during an update
  3537. func ValidateServiceUpdate(service, oldService *core.Service) field.ErrorList {
  3538. allErrs := ValidateObjectMetaUpdate(&service.ObjectMeta, &oldService.ObjectMeta, field.NewPath("metadata"))
  3539. // ClusterIP should be immutable for services using it (every type other than ExternalName)
  3540. // which do not have ClusterIP assigned yet (empty string value)
  3541. if service.Spec.Type != core.ServiceTypeExternalName {
  3542. if oldService.Spec.Type != core.ServiceTypeExternalName && oldService.Spec.ClusterIP != "" {
  3543. allErrs = append(allErrs, ValidateImmutableField(service.Spec.ClusterIP, oldService.Spec.ClusterIP, field.NewPath("spec", "clusterIP"))...)
  3544. }
  3545. }
  3546. allErrs = append(allErrs, ValidateService(service)...)
  3547. return allErrs
  3548. }
  3549. // ValidateServiceStatusUpdate tests if required fields in the Service are set when updating status.
  3550. func ValidateServiceStatusUpdate(service, oldService *core.Service) field.ErrorList {
  3551. allErrs := ValidateObjectMetaUpdate(&service.ObjectMeta, &oldService.ObjectMeta, field.NewPath("metadata"))
  3552. allErrs = append(allErrs, ValidateLoadBalancerStatus(&service.Status.LoadBalancer, field.NewPath("status", "loadBalancer"))...)
  3553. return allErrs
  3554. }
  3555. // ValidateReplicationController tests if required fields in the replication controller are set.
  3556. func ValidateReplicationController(controller *core.ReplicationController) field.ErrorList {
  3557. allErrs := ValidateObjectMeta(&controller.ObjectMeta, true, ValidateReplicationControllerName, field.NewPath("metadata"))
  3558. allErrs = append(allErrs, ValidateReplicationControllerSpec(&controller.Spec, field.NewPath("spec"))...)
  3559. return allErrs
  3560. }
  3561. // ValidateReplicationControllerUpdate tests if required fields in the replication controller are set.
  3562. func ValidateReplicationControllerUpdate(controller, oldController *core.ReplicationController) field.ErrorList {
  3563. allErrs := ValidateObjectMetaUpdate(&controller.ObjectMeta, &oldController.ObjectMeta, field.NewPath("metadata"))
  3564. allErrs = append(allErrs, ValidateReplicationControllerSpec(&controller.Spec, field.NewPath("spec"))...)
  3565. return allErrs
  3566. }
  3567. // ValidateReplicationControllerStatusUpdate tests if required fields in the replication controller are set.
  3568. func ValidateReplicationControllerStatusUpdate(controller, oldController *core.ReplicationController) field.ErrorList {
  3569. allErrs := ValidateObjectMetaUpdate(&controller.ObjectMeta, &oldController.ObjectMeta, field.NewPath("metadata"))
  3570. allErrs = append(allErrs, ValidateReplicationControllerStatus(controller.Status, field.NewPath("status"))...)
  3571. return allErrs
  3572. }
  3573. func ValidateReplicationControllerStatus(status core.ReplicationControllerStatus, statusPath *field.Path) field.ErrorList {
  3574. allErrs := field.ErrorList{}
  3575. allErrs = append(allErrs, ValidateNonnegativeField(int64(status.Replicas), statusPath.Child("replicas"))...)
  3576. allErrs = append(allErrs, ValidateNonnegativeField(int64(status.FullyLabeledReplicas), statusPath.Child("fullyLabeledReplicas"))...)
  3577. allErrs = append(allErrs, ValidateNonnegativeField(int64(status.ReadyReplicas), statusPath.Child("readyReplicas"))...)
  3578. allErrs = append(allErrs, ValidateNonnegativeField(int64(status.AvailableReplicas), statusPath.Child("availableReplicas"))...)
  3579. allErrs = append(allErrs, ValidateNonnegativeField(int64(status.ObservedGeneration), statusPath.Child("observedGeneration"))...)
  3580. msg := "cannot be greater than status.replicas"
  3581. if status.FullyLabeledReplicas > status.Replicas {
  3582. allErrs = append(allErrs, field.Invalid(statusPath.Child("fullyLabeledReplicas"), status.FullyLabeledReplicas, msg))
  3583. }
  3584. if status.ReadyReplicas > status.Replicas {
  3585. allErrs = append(allErrs, field.Invalid(statusPath.Child("readyReplicas"), status.ReadyReplicas, msg))
  3586. }
  3587. if status.AvailableReplicas > status.Replicas {
  3588. allErrs = append(allErrs, field.Invalid(statusPath.Child("availableReplicas"), status.AvailableReplicas, msg))
  3589. }
  3590. if status.AvailableReplicas > status.ReadyReplicas {
  3591. allErrs = append(allErrs, field.Invalid(statusPath.Child("availableReplicas"), status.AvailableReplicas, "cannot be greater than readyReplicas"))
  3592. }
  3593. return allErrs
  3594. }
  3595. // Validates that the given selector is non-empty.
  3596. func ValidateNonEmptySelector(selectorMap map[string]string, fldPath *field.Path) field.ErrorList {
  3597. allErrs := field.ErrorList{}
  3598. selector := labels.Set(selectorMap).AsSelector()
  3599. if selector.Empty() {
  3600. allErrs = append(allErrs, field.Required(fldPath, ""))
  3601. }
  3602. return allErrs
  3603. }
  3604. // Validates the given template and ensures that it is in accordance with the desired selector and replicas.
  3605. func ValidatePodTemplateSpecForRC(template *core.PodTemplateSpec, selectorMap map[string]string, replicas int32, fldPath *field.Path) field.ErrorList {
  3606. allErrs := field.ErrorList{}
  3607. if template == nil {
  3608. allErrs = append(allErrs, field.Required(fldPath, ""))
  3609. } else {
  3610. selector := labels.Set(selectorMap).AsSelector()
  3611. if !selector.Empty() {
  3612. // Verify that the RC selector matches the labels in template.
  3613. labels := labels.Set(template.Labels)
  3614. if !selector.Matches(labels) {
  3615. allErrs = append(allErrs, field.Invalid(fldPath.Child("metadata", "labels"), template.Labels, "`selector` does not match template `labels`"))
  3616. }
  3617. }
  3618. allErrs = append(allErrs, ValidatePodTemplateSpec(template, fldPath)...)
  3619. if replicas > 1 {
  3620. allErrs = append(allErrs, ValidateReadOnlyPersistentDisks(template.Spec.Volumes, fldPath.Child("spec", "volumes"))...)
  3621. }
  3622. // RestartPolicy has already been first-order validated as per ValidatePodTemplateSpec().
  3623. if template.Spec.RestartPolicy != core.RestartPolicyAlways {
  3624. allErrs = append(allErrs, field.NotSupported(fldPath.Child("spec", "restartPolicy"), template.Spec.RestartPolicy, []string{string(core.RestartPolicyAlways)}))
  3625. }
  3626. if template.Spec.ActiveDeadlineSeconds != nil {
  3627. allErrs = append(allErrs, field.Forbidden(fldPath.Child("spec", "activeDeadlineSeconds"), "activeDeadlineSeconds in ReplicationController is not Supported"))
  3628. }
  3629. }
  3630. return allErrs
  3631. }
  3632. // ValidateReplicationControllerSpec tests if required fields in the replication controller spec are set.
  3633. func ValidateReplicationControllerSpec(spec *core.ReplicationControllerSpec, fldPath *field.Path) field.ErrorList {
  3634. allErrs := field.ErrorList{}
  3635. allErrs = append(allErrs, ValidateNonnegativeField(int64(spec.MinReadySeconds), fldPath.Child("minReadySeconds"))...)
  3636. allErrs = append(allErrs, ValidateNonEmptySelector(spec.Selector, fldPath.Child("selector"))...)
  3637. allErrs = append(allErrs, ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...)
  3638. allErrs = append(allErrs, ValidatePodTemplateSpecForRC(spec.Template, spec.Selector, spec.Replicas, fldPath.Child("template"))...)
  3639. return allErrs
  3640. }
  3641. // ValidatePodTemplateSpec validates the spec of a pod template
  3642. func ValidatePodTemplateSpec(spec *core.PodTemplateSpec, fldPath *field.Path) field.ErrorList {
  3643. allErrs := field.ErrorList{}
  3644. allErrs = append(allErrs, unversionedvalidation.ValidateLabels(spec.Labels, fldPath.Child("labels"))...)
  3645. allErrs = append(allErrs, ValidateAnnotations(spec.Annotations, fldPath.Child("annotations"))...)
  3646. allErrs = append(allErrs, ValidatePodSpecificAnnotations(spec.Annotations, &spec.Spec, fldPath.Child("annotations"))...)
  3647. allErrs = append(allErrs, ValidatePodSpec(&spec.Spec, fldPath.Child("spec"))...)
  3648. return allErrs
  3649. }
  3650. func ValidateReadOnlyPersistentDisks(volumes []core.Volume, fldPath *field.Path) field.ErrorList {
  3651. allErrs := field.ErrorList{}
  3652. for i := range volumes {
  3653. vol := &volumes[i]
  3654. idxPath := fldPath.Index(i)
  3655. if vol.GCEPersistentDisk != nil {
  3656. if !vol.GCEPersistentDisk.ReadOnly {
  3657. allErrs = append(allErrs, field.Invalid(idxPath.Child("gcePersistentDisk", "readOnly"), false, "must be true for replicated pods > 1; GCE PD can only be mounted on multiple machines if it is read-only"))
  3658. }
  3659. }
  3660. // TODO: What to do for AWS? It doesn't support replicas
  3661. }
  3662. return allErrs
  3663. }
  3664. // ValidateTaintsInNodeAnnotations tests that the serialized taints in Node.Annotations has valid data
  3665. func ValidateTaintsInNodeAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList {
  3666. allErrs := field.ErrorList{}
  3667. taints, err := helper.GetTaintsFromNodeAnnotations(annotations)
  3668. if err != nil {
  3669. allErrs = append(allErrs, field.Invalid(fldPath, core.TaintsAnnotationKey, err.Error()))
  3670. return allErrs
  3671. }
  3672. if len(taints) > 0 {
  3673. allErrs = append(allErrs, validateNodeTaints(taints, fldPath.Child(core.TaintsAnnotationKey))...)
  3674. }
  3675. return allErrs
  3676. }
  3677. // validateNodeTaints tests if given taints have valid data.
  3678. func validateNodeTaints(taints []core.Taint, fldPath *field.Path) field.ErrorList {
  3679. allErrors := field.ErrorList{}
  3680. uniqueTaints := map[core.TaintEffect]sets.String{}
  3681. for i, currTaint := range taints {
  3682. idxPath := fldPath.Index(i)
  3683. // validate the taint key
  3684. allErrors = append(allErrors, unversionedvalidation.ValidateLabelName(currTaint.Key, idxPath.Child("key"))...)
  3685. // validate the taint value
  3686. if errs := validation.IsValidLabelValue(currTaint.Value); len(errs) != 0 {
  3687. allErrors = append(allErrors, field.Invalid(idxPath.Child("value"), currTaint.Value, strings.Join(errs, ";")))
  3688. }
  3689. // validate the taint effect
  3690. allErrors = append(allErrors, validateTaintEffect(&currTaint.Effect, false, idxPath.Child("effect"))...)
  3691. // validate if taint is unique by <key, effect>
  3692. if len(uniqueTaints[currTaint.Effect]) > 0 && uniqueTaints[currTaint.Effect].Has(currTaint.Key) {
  3693. duplicatedError := field.Duplicate(idxPath, currTaint)
  3694. duplicatedError.Detail = "taints must be unique by key and effect pair"
  3695. allErrors = append(allErrors, duplicatedError)
  3696. continue
  3697. }
  3698. // add taint to existingTaints for uniqueness check
  3699. if len(uniqueTaints[currTaint.Effect]) == 0 {
  3700. uniqueTaints[currTaint.Effect] = sets.String{}
  3701. }
  3702. uniqueTaints[currTaint.Effect].Insert(currTaint.Key)
  3703. }
  3704. return allErrors
  3705. }
  3706. func ValidateNodeSpecificAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList {
  3707. allErrs := field.ErrorList{}
  3708. if annotations[core.TaintsAnnotationKey] != "" {
  3709. allErrs = append(allErrs, ValidateTaintsInNodeAnnotations(annotations, fldPath)...)
  3710. }
  3711. if annotations[core.PreferAvoidPodsAnnotationKey] != "" {
  3712. allErrs = append(allErrs, ValidateAvoidPodsInNodeAnnotations(annotations, fldPath)...)
  3713. }
  3714. return allErrs
  3715. }
  3716. // ValidateNode tests if required fields in the node are set.
  3717. func ValidateNode(node *core.Node) field.ErrorList {
  3718. fldPath := field.NewPath("metadata")
  3719. allErrs := ValidateObjectMeta(&node.ObjectMeta, false, ValidateNodeName, fldPath)
  3720. allErrs = append(allErrs, ValidateNodeSpecificAnnotations(node.ObjectMeta.Annotations, fldPath.Child("annotations"))...)
  3721. if len(node.Spec.Taints) > 0 {
  3722. allErrs = append(allErrs, validateNodeTaints(node.Spec.Taints, fldPath.Child("taints"))...)
  3723. }
  3724. // Only validate spec.
  3725. // All status fields are optional and can be updated later.
  3726. // That said, if specified, we need to ensure they are valid.
  3727. allErrs = append(allErrs, ValidateNodeResources(node)...)
  3728. if len(node.Spec.PodCIDR) != 0 {
  3729. _, err := ValidateCIDR(node.Spec.PodCIDR)
  3730. if err != nil {
  3731. allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "podCIDR"), node.Spec.PodCIDR, "not a valid CIDR"))
  3732. }
  3733. }
  3734. return allErrs
  3735. }
  3736. // ValidateNodeResources is used to make sure a node has valid capacity and allocatable values.
  3737. func ValidateNodeResources(node *core.Node) field.ErrorList {
  3738. allErrs := field.ErrorList{}
  3739. // Validate resource quantities in capacity.
  3740. hugePageSizes := sets.NewString()
  3741. for k, v := range node.Status.Capacity {
  3742. resPath := field.NewPath("status", "capacity", string(k))
  3743. allErrs = append(allErrs, ValidateResourceQuantityValue(string(k), v, resPath)...)
  3744. // track any huge page size that has a positive value
  3745. if helper.IsHugePageResourceName(k) && v.Value() > int64(0) {
  3746. hugePageSizes.Insert(string(k))
  3747. }
  3748. if len(hugePageSizes) > 1 {
  3749. allErrs = append(allErrs, field.Invalid(resPath, v, "may not have pre-allocated hugepages for multiple page sizes"))
  3750. }
  3751. }
  3752. // Validate resource quantities in allocatable.
  3753. hugePageSizes = sets.NewString()
  3754. for k, v := range node.Status.Allocatable {
  3755. resPath := field.NewPath("status", "allocatable", string(k))
  3756. allErrs = append(allErrs, ValidateResourceQuantityValue(string(k), v, resPath)...)
  3757. // track any huge page size that has a positive value
  3758. if helper.IsHugePageResourceName(k) && v.Value() > int64(0) {
  3759. hugePageSizes.Insert(string(k))
  3760. }
  3761. if len(hugePageSizes) > 1 {
  3762. allErrs = append(allErrs, field.Invalid(resPath, v, "may not have pre-allocated hugepages for multiple page sizes"))
  3763. }
  3764. }
  3765. return allErrs
  3766. }
  3767. // ValidateNodeUpdate tests to make sure a node update can be applied. Modifies oldNode.
  3768. func ValidateNodeUpdate(node, oldNode *core.Node) field.ErrorList {
  3769. fldPath := field.NewPath("metadata")
  3770. allErrs := ValidateObjectMetaUpdate(&node.ObjectMeta, &oldNode.ObjectMeta, fldPath)
  3771. allErrs = append(allErrs, ValidateNodeSpecificAnnotations(node.ObjectMeta.Annotations, fldPath.Child("annotations"))...)
  3772. // TODO: Enable the code once we have better core object.status update model. Currently,
  3773. // anyone can update node status.
  3774. // if !apiequality.Semantic.DeepEqual(node.Status, core.NodeStatus{}) {
  3775. // allErrs = append(allErrs, field.Invalid("status", node.Status, "must be empty"))
  3776. // }
  3777. allErrs = append(allErrs, ValidateNodeResources(node)...)
  3778. // Validate no duplicate addresses in node status.
  3779. addresses := make(map[core.NodeAddress]bool)
  3780. for i, address := range node.Status.Addresses {
  3781. if _, ok := addresses[address]; ok {
  3782. allErrs = append(allErrs, field.Duplicate(field.NewPath("status", "addresses").Index(i), address))
  3783. }
  3784. addresses[address] = true
  3785. }
  3786. if len(oldNode.Spec.PodCIDR) == 0 {
  3787. // Allow the controller manager to assign a CIDR to a node if it doesn't have one.
  3788. oldNode.Spec.PodCIDR = node.Spec.PodCIDR
  3789. } else {
  3790. if oldNode.Spec.PodCIDR != node.Spec.PodCIDR {
  3791. allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "podCIDR"), "node updates may not change podCIDR except from \"\" to valid"))
  3792. }
  3793. }
  3794. // Allow controller manager updating provider ID when not set
  3795. if len(oldNode.Spec.ProviderID) == 0 {
  3796. oldNode.Spec.ProviderID = node.Spec.ProviderID
  3797. } else {
  3798. if oldNode.Spec.ProviderID != node.Spec.ProviderID {
  3799. allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "providerID"), "node updates may not change providerID except from \"\" to valid"))
  3800. }
  3801. }
  3802. if node.Spec.ConfigSource != nil {
  3803. allErrs = append(allErrs, validateNodeConfigSourceSpec(node.Spec.ConfigSource, field.NewPath("spec", "configSource"))...)
  3804. }
  3805. oldNode.Spec.ConfigSource = node.Spec.ConfigSource
  3806. if node.Status.Config != nil {
  3807. allErrs = append(allErrs, validateNodeConfigStatus(node.Status.Config, field.NewPath("status", "config"))...)
  3808. }
  3809. oldNode.Status.Config = node.Status.Config
  3810. // TODO: move reset function to its own location
  3811. // Ignore metadata changes now that they have been tested
  3812. oldNode.ObjectMeta = node.ObjectMeta
  3813. // Allow users to update capacity
  3814. oldNode.Status.Capacity = node.Status.Capacity
  3815. // Allow users to unschedule node
  3816. oldNode.Spec.Unschedulable = node.Spec.Unschedulable
  3817. // Clear status
  3818. oldNode.Status = node.Status
  3819. // update taints
  3820. if len(node.Spec.Taints) > 0 {
  3821. allErrs = append(allErrs, validateNodeTaints(node.Spec.Taints, fldPath.Child("taints"))...)
  3822. }
  3823. oldNode.Spec.Taints = node.Spec.Taints
  3824. // We made allowed changes to oldNode, and now we compare oldNode to node. Any remaining differences indicate changes to protected fields.
  3825. // TODO: Add a 'real' error type for this error and provide print actual diffs.
  3826. if !apiequality.Semantic.DeepEqual(oldNode, node) {
  3827. klog.V(4).Infof("Update failed validation %#v vs %#v", oldNode, node)
  3828. allErrs = append(allErrs, field.Forbidden(field.NewPath(""), "node updates may only change labels, taints, or capacity (or configSource, if the DynamicKubeletConfig feature gate is enabled)"))
  3829. }
  3830. return allErrs
  3831. }
  3832. // validation specific to Node.Spec.ConfigSource
  3833. func validateNodeConfigSourceSpec(source *core.NodeConfigSource, fldPath *field.Path) field.ErrorList {
  3834. allErrs := field.ErrorList{}
  3835. count := int(0)
  3836. if source.ConfigMap != nil {
  3837. count++
  3838. allErrs = append(allErrs, validateConfigMapNodeConfigSourceSpec(source.ConfigMap, fldPath.Child("configMap"))...)
  3839. }
  3840. // add more subfields here in the future as they are added to NodeConfigSource
  3841. // exactly one reference subfield must be non-nil
  3842. if count != 1 {
  3843. allErrs = append(allErrs, field.Invalid(fldPath, source, "exactly one reference subfield must be non-nil"))
  3844. }
  3845. return allErrs
  3846. }
  3847. // validation specific to Node.Spec.ConfigSource.ConfigMap
  3848. func validateConfigMapNodeConfigSourceSpec(source *core.ConfigMapNodeConfigSource, fldPath *field.Path) field.ErrorList {
  3849. allErrs := field.ErrorList{}
  3850. // uid and resourceVersion must not be set in spec
  3851. if string(source.UID) != "" {
  3852. allErrs = append(allErrs, field.Forbidden(fldPath.Child("uid"), "uid must not be set in spec"))
  3853. }
  3854. if source.ResourceVersion != "" {
  3855. allErrs = append(allErrs, field.Forbidden(fldPath.Child("resourceVersion"), "resourceVersion must not be set in spec"))
  3856. }
  3857. return append(allErrs, validateConfigMapNodeConfigSource(source, fldPath)...)
  3858. }
  3859. // validation specififc to Node.Status.Config
  3860. func validateNodeConfigStatus(status *core.NodeConfigStatus, fldPath *field.Path) field.ErrorList {
  3861. allErrs := field.ErrorList{}
  3862. if status.Assigned != nil {
  3863. allErrs = append(allErrs, validateNodeConfigSourceStatus(status.Assigned, fldPath.Child("assigned"))...)
  3864. }
  3865. if status.Active != nil {
  3866. allErrs = append(allErrs, validateNodeConfigSourceStatus(status.Active, fldPath.Child("active"))...)
  3867. }
  3868. if status.LastKnownGood != nil {
  3869. allErrs = append(allErrs, validateNodeConfigSourceStatus(status.LastKnownGood, fldPath.Child("lastKnownGood"))...)
  3870. }
  3871. return allErrs
  3872. }
  3873. // validation specific to Node.Status.Config.(Active|Assigned|LastKnownGood)
  3874. func validateNodeConfigSourceStatus(source *core.NodeConfigSource, fldPath *field.Path) field.ErrorList {
  3875. allErrs := field.ErrorList{}
  3876. count := int(0)
  3877. if source.ConfigMap != nil {
  3878. count++
  3879. allErrs = append(allErrs, validateConfigMapNodeConfigSourceStatus(source.ConfigMap, fldPath.Child("configMap"))...)
  3880. }
  3881. // add more subfields here in the future as they are added to NodeConfigSource
  3882. // exactly one reference subfield must be non-nil
  3883. if count != 1 {
  3884. allErrs = append(allErrs, field.Invalid(fldPath, source, "exactly one reference subfield must be non-nil"))
  3885. }
  3886. return allErrs
  3887. }
  3888. // validation specific to Node.Status.Config.(Active|Assigned|LastKnownGood).ConfigMap
  3889. func validateConfigMapNodeConfigSourceStatus(source *core.ConfigMapNodeConfigSource, fldPath *field.Path) field.ErrorList {
  3890. allErrs := field.ErrorList{}
  3891. // uid and resourceVersion must be set in status
  3892. if string(source.UID) == "" {
  3893. allErrs = append(allErrs, field.Required(fldPath.Child("uid"), "uid must be set in status"))
  3894. }
  3895. if source.ResourceVersion == "" {
  3896. allErrs = append(allErrs, field.Required(fldPath.Child("resourceVersion"), "resourceVersion must be set in status"))
  3897. }
  3898. return append(allErrs, validateConfigMapNodeConfigSource(source, fldPath)...)
  3899. }
  3900. // common validation
  3901. func validateConfigMapNodeConfigSource(source *core.ConfigMapNodeConfigSource, fldPath *field.Path) field.ErrorList {
  3902. allErrs := field.ErrorList{}
  3903. // validate target configmap namespace
  3904. if source.Namespace == "" {
  3905. allErrs = append(allErrs, field.Required(fldPath.Child("namespace"), "namespace must be set"))
  3906. } else {
  3907. for _, msg := range ValidateNameFunc(ValidateNamespaceName)(source.Namespace, false) {
  3908. allErrs = append(allErrs, field.Invalid(fldPath.Child("namespace"), source.Namespace, msg))
  3909. }
  3910. }
  3911. // validate target configmap name
  3912. if source.Name == "" {
  3913. allErrs = append(allErrs, field.Required(fldPath.Child("name"), "name must be set"))
  3914. } else {
  3915. for _, msg := range ValidateNameFunc(ValidateConfigMapName)(source.Name, false) {
  3916. allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), source.Name, msg))
  3917. }
  3918. }
  3919. // validate kubeletConfigKey against rules for configMap key names
  3920. if source.KubeletConfigKey == "" {
  3921. allErrs = append(allErrs, field.Required(fldPath.Child("kubeletConfigKey"), "kubeletConfigKey must be set"))
  3922. } else {
  3923. for _, msg := range validation.IsConfigMapKey(source.KubeletConfigKey) {
  3924. allErrs = append(allErrs, field.Invalid(fldPath.Child("kubeletConfigKey"), source.KubeletConfigKey, msg))
  3925. }
  3926. }
  3927. return allErrs
  3928. }
  3929. // Validate compute resource typename.
  3930. // Refer to docs/design/resources.md for more details.
  3931. func validateResourceName(value string, fldPath *field.Path) field.ErrorList {
  3932. allErrs := field.ErrorList{}
  3933. for _, msg := range validation.IsQualifiedName(value) {
  3934. allErrs = append(allErrs, field.Invalid(fldPath, value, msg))
  3935. }
  3936. if len(allErrs) != 0 {
  3937. return allErrs
  3938. }
  3939. if len(strings.Split(value, "/")) == 1 {
  3940. if !helper.IsStandardResourceName(value) {
  3941. return append(allErrs, field.Invalid(fldPath, value, "must be a standard resource type or fully qualified"))
  3942. }
  3943. }
  3944. return allErrs
  3945. }
  3946. // Validate container resource name
  3947. // Refer to docs/design/resources.md for more details.
  3948. func validateContainerResourceName(value string, fldPath *field.Path) field.ErrorList {
  3949. allErrs := validateResourceName(value, fldPath)
  3950. if len(strings.Split(value, "/")) == 1 {
  3951. if !helper.IsStandardContainerResourceName(value) {
  3952. return append(allErrs, field.Invalid(fldPath, value, "must be a standard resource for containers"))
  3953. }
  3954. } else if !helper.IsNativeResource(core.ResourceName(value)) {
  3955. if !helper.IsExtendedResourceName(core.ResourceName(value)) {
  3956. return append(allErrs, field.Invalid(fldPath, value, "doesn't follow extended resource name standard"))
  3957. }
  3958. }
  3959. return allErrs
  3960. }
  3961. // Validate resource names that can go in a resource quota
  3962. // Refer to docs/design/resources.md for more details.
  3963. func ValidateResourceQuotaResourceName(value string, fldPath *field.Path) field.ErrorList {
  3964. allErrs := validateResourceName(value, fldPath)
  3965. if len(strings.Split(value, "/")) == 1 {
  3966. if !helper.IsStandardQuotaResourceName(value) {
  3967. return append(allErrs, field.Invalid(fldPath, value, isInvalidQuotaResource))
  3968. }
  3969. }
  3970. return allErrs
  3971. }
  3972. // Validate limit range types
  3973. func validateLimitRangeTypeName(value string, fldPath *field.Path) field.ErrorList {
  3974. allErrs := field.ErrorList{}
  3975. for _, msg := range validation.IsQualifiedName(value) {
  3976. allErrs = append(allErrs, field.Invalid(fldPath, value, msg))
  3977. }
  3978. if len(allErrs) != 0 {
  3979. return allErrs
  3980. }
  3981. if len(strings.Split(value, "/")) == 1 {
  3982. if !helper.IsStandardLimitRangeType(value) {
  3983. return append(allErrs, field.Invalid(fldPath, value, "must be a standard limit type or fully qualified"))
  3984. }
  3985. }
  3986. return allErrs
  3987. }
  3988. // Validate limit range resource name
  3989. // limit types (other than Pod/Container) could contain storage not just cpu or memory
  3990. func validateLimitRangeResourceName(limitType core.LimitType, value string, fldPath *field.Path) field.ErrorList {
  3991. switch limitType {
  3992. case core.LimitTypePod, core.LimitTypeContainer:
  3993. return validateContainerResourceName(value, fldPath)
  3994. default:
  3995. return validateResourceName(value, fldPath)
  3996. }
  3997. }
  3998. // ValidateLimitRange tests if required fields in the LimitRange are set.
  3999. func ValidateLimitRange(limitRange *core.LimitRange) field.ErrorList {
  4000. allErrs := ValidateObjectMeta(&limitRange.ObjectMeta, true, ValidateLimitRangeName, field.NewPath("metadata"))
  4001. // ensure resource names are properly qualified per docs/design/resources.md
  4002. limitTypeSet := map[core.LimitType]bool{}
  4003. fldPath := field.NewPath("spec", "limits")
  4004. for i := range limitRange.Spec.Limits {
  4005. idxPath := fldPath.Index(i)
  4006. limit := &limitRange.Spec.Limits[i]
  4007. allErrs = append(allErrs, validateLimitRangeTypeName(string(limit.Type), idxPath.Child("type"))...)
  4008. _, found := limitTypeSet[limit.Type]
  4009. if found {
  4010. allErrs = append(allErrs, field.Duplicate(idxPath.Child("type"), limit.Type))
  4011. }
  4012. limitTypeSet[limit.Type] = true
  4013. keys := sets.String{}
  4014. min := map[string]resource.Quantity{}
  4015. max := map[string]resource.Quantity{}
  4016. defaults := map[string]resource.Quantity{}
  4017. defaultRequests := map[string]resource.Quantity{}
  4018. maxLimitRequestRatios := map[string]resource.Quantity{}
  4019. for k, q := range limit.Max {
  4020. allErrs = append(allErrs, validateLimitRangeResourceName(limit.Type, string(k), idxPath.Child("max").Key(string(k)))...)
  4021. keys.Insert(string(k))
  4022. max[string(k)] = q
  4023. }
  4024. for k, q := range limit.Min {
  4025. allErrs = append(allErrs, validateLimitRangeResourceName(limit.Type, string(k), idxPath.Child("min").Key(string(k)))...)
  4026. keys.Insert(string(k))
  4027. min[string(k)] = q
  4028. }
  4029. if limit.Type == core.LimitTypePod {
  4030. if len(limit.Default) > 0 {
  4031. allErrs = append(allErrs, field.Forbidden(idxPath.Child("default"), "may not be specified when `type` is 'Pod'"))
  4032. }
  4033. if len(limit.DefaultRequest) > 0 {
  4034. allErrs = append(allErrs, field.Forbidden(idxPath.Child("defaultRequest"), "may not be specified when `type` is 'Pod'"))
  4035. }
  4036. } else {
  4037. for k, q := range limit.Default {
  4038. allErrs = append(allErrs, validateLimitRangeResourceName(limit.Type, string(k), idxPath.Child("default").Key(string(k)))...)
  4039. keys.Insert(string(k))
  4040. defaults[string(k)] = q
  4041. }
  4042. for k, q := range limit.DefaultRequest {
  4043. allErrs = append(allErrs, validateLimitRangeResourceName(limit.Type, string(k), idxPath.Child("defaultRequest").Key(string(k)))...)
  4044. keys.Insert(string(k))
  4045. defaultRequests[string(k)] = q
  4046. }
  4047. }
  4048. if limit.Type == core.LimitTypePersistentVolumeClaim {
  4049. _, minQuantityFound := limit.Min[core.ResourceStorage]
  4050. _, maxQuantityFound := limit.Max[core.ResourceStorage]
  4051. if !minQuantityFound && !maxQuantityFound {
  4052. allErrs = append(allErrs, field.Required(idxPath.Child("limits"), "either minimum or maximum storage value is required, but neither was provided"))
  4053. }
  4054. }
  4055. for k, q := range limit.MaxLimitRequestRatio {
  4056. allErrs = append(allErrs, validateLimitRangeResourceName(limit.Type, string(k), idxPath.Child("maxLimitRequestRatio").Key(string(k)))...)
  4057. keys.Insert(string(k))
  4058. maxLimitRequestRatios[string(k)] = q
  4059. }
  4060. for k := range keys {
  4061. minQuantity, minQuantityFound := min[k]
  4062. maxQuantity, maxQuantityFound := max[k]
  4063. defaultQuantity, defaultQuantityFound := defaults[k]
  4064. defaultRequestQuantity, defaultRequestQuantityFound := defaultRequests[k]
  4065. maxRatio, maxRatioFound := maxLimitRequestRatios[k]
  4066. if minQuantityFound && maxQuantityFound && minQuantity.Cmp(maxQuantity) > 0 {
  4067. allErrs = append(allErrs, field.Invalid(idxPath.Child("min").Key(string(k)), minQuantity, fmt.Sprintf("min value %s is greater than max value %s", minQuantity.String(), maxQuantity.String())))
  4068. }
  4069. if defaultRequestQuantityFound && minQuantityFound && minQuantity.Cmp(defaultRequestQuantity) > 0 {
  4070. allErrs = append(allErrs, field.Invalid(idxPath.Child("defaultRequest").Key(string(k)), defaultRequestQuantity, fmt.Sprintf("min value %s is greater than default request value %s", minQuantity.String(), defaultRequestQuantity.String())))
  4071. }
  4072. if defaultRequestQuantityFound && maxQuantityFound && defaultRequestQuantity.Cmp(maxQuantity) > 0 {
  4073. allErrs = append(allErrs, field.Invalid(idxPath.Child("defaultRequest").Key(string(k)), defaultRequestQuantity, fmt.Sprintf("default request value %s is greater than max value %s", defaultRequestQuantity.String(), maxQuantity.String())))
  4074. }
  4075. if defaultRequestQuantityFound && defaultQuantityFound && defaultRequestQuantity.Cmp(defaultQuantity) > 0 {
  4076. allErrs = append(allErrs, field.Invalid(idxPath.Child("defaultRequest").Key(string(k)), defaultRequestQuantity, fmt.Sprintf("default request value %s is greater than default limit value %s", defaultRequestQuantity.String(), defaultQuantity.String())))
  4077. }
  4078. if defaultQuantityFound && minQuantityFound && minQuantity.Cmp(defaultQuantity) > 0 {
  4079. allErrs = append(allErrs, field.Invalid(idxPath.Child("default").Key(string(k)), minQuantity, fmt.Sprintf("min value %s is greater than default value %s", minQuantity.String(), defaultQuantity.String())))
  4080. }
  4081. if defaultQuantityFound && maxQuantityFound && defaultQuantity.Cmp(maxQuantity) > 0 {
  4082. allErrs = append(allErrs, field.Invalid(idxPath.Child("default").Key(string(k)), maxQuantity, fmt.Sprintf("default value %s is greater than max value %s", defaultQuantity.String(), maxQuantity.String())))
  4083. }
  4084. if maxRatioFound && maxRatio.Cmp(*resource.NewQuantity(1, resource.DecimalSI)) < 0 {
  4085. allErrs = append(allErrs, field.Invalid(idxPath.Child("maxLimitRequestRatio").Key(string(k)), maxRatio, fmt.Sprintf("ratio %s is less than 1", maxRatio.String())))
  4086. }
  4087. if maxRatioFound && minQuantityFound && maxQuantityFound {
  4088. maxRatioValue := float64(maxRatio.Value())
  4089. minQuantityValue := minQuantity.Value()
  4090. maxQuantityValue := maxQuantity.Value()
  4091. if maxRatio.Value() < resource.MaxMilliValue && minQuantityValue < resource.MaxMilliValue && maxQuantityValue < resource.MaxMilliValue {
  4092. maxRatioValue = float64(maxRatio.MilliValue()) / 1000
  4093. minQuantityValue = minQuantity.MilliValue()
  4094. maxQuantityValue = maxQuantity.MilliValue()
  4095. }
  4096. maxRatioLimit := float64(maxQuantityValue) / float64(minQuantityValue)
  4097. if maxRatioValue > maxRatioLimit {
  4098. allErrs = append(allErrs, field.Invalid(idxPath.Child("maxLimitRequestRatio").Key(string(k)), maxRatio, fmt.Sprintf("ratio %s is greater than max/min = %f", maxRatio.String(), maxRatioLimit)))
  4099. }
  4100. }
  4101. // for GPU, hugepages and other resources that are not allowed to overcommit,
  4102. // the default value and defaultRequest value must match if both are specified
  4103. if !helper.IsOvercommitAllowed(core.ResourceName(k)) && defaultQuantityFound && defaultRequestQuantityFound && defaultQuantity.Cmp(defaultRequestQuantity) != 0 {
  4104. allErrs = append(allErrs, field.Invalid(idxPath.Child("defaultRequest").Key(string(k)), defaultRequestQuantity, fmt.Sprintf("default value %s must equal to defaultRequest value %s in %s", defaultQuantity.String(), defaultRequestQuantity.String(), k)))
  4105. }
  4106. }
  4107. }
  4108. return allErrs
  4109. }
  4110. // ValidateServiceAccount tests if required fields in the ServiceAccount are set.
  4111. func ValidateServiceAccount(serviceAccount *core.ServiceAccount) field.ErrorList {
  4112. allErrs := ValidateObjectMeta(&serviceAccount.ObjectMeta, true, ValidateServiceAccountName, field.NewPath("metadata"))
  4113. return allErrs
  4114. }
  4115. // ValidateServiceAccountUpdate tests if required fields in the ServiceAccount are set.
  4116. func ValidateServiceAccountUpdate(newServiceAccount, oldServiceAccount *core.ServiceAccount) field.ErrorList {
  4117. allErrs := ValidateObjectMetaUpdate(&newServiceAccount.ObjectMeta, &oldServiceAccount.ObjectMeta, field.NewPath("metadata"))
  4118. allErrs = append(allErrs, ValidateServiceAccount(newServiceAccount)...)
  4119. return allErrs
  4120. }
  4121. // ValidateSecret tests if required fields in the Secret are set.
  4122. func ValidateSecret(secret *core.Secret) field.ErrorList {
  4123. allErrs := ValidateObjectMeta(&secret.ObjectMeta, true, ValidateSecretName, field.NewPath("metadata"))
  4124. dataPath := field.NewPath("data")
  4125. totalSize := 0
  4126. for key, value := range secret.Data {
  4127. for _, msg := range validation.IsConfigMapKey(key) {
  4128. allErrs = append(allErrs, field.Invalid(dataPath.Key(key), key, msg))
  4129. }
  4130. totalSize += len(value)
  4131. }
  4132. if totalSize > core.MaxSecretSize {
  4133. allErrs = append(allErrs, field.TooLong(dataPath, "", core.MaxSecretSize))
  4134. }
  4135. switch secret.Type {
  4136. case core.SecretTypeServiceAccountToken:
  4137. // Only require Annotations[kubernetes.io/service-account.name]
  4138. // Additional fields (like Annotations[kubernetes.io/service-account.uid] and Data[token]) might be contributed later by a controller loop
  4139. if value := secret.Annotations[core.ServiceAccountNameKey]; len(value) == 0 {
  4140. allErrs = append(allErrs, field.Required(field.NewPath("metadata", "annotations").Key(core.ServiceAccountNameKey), ""))
  4141. }
  4142. case core.SecretTypeOpaque, "":
  4143. // no-op
  4144. case core.SecretTypeDockercfg:
  4145. dockercfgBytes, exists := secret.Data[core.DockerConfigKey]
  4146. if !exists {
  4147. allErrs = append(allErrs, field.Required(dataPath.Key(core.DockerConfigKey), ""))
  4148. break
  4149. }
  4150. // make sure that the content is well-formed json.
  4151. if err := json.Unmarshal(dockercfgBytes, &map[string]interface{}{}); err != nil {
  4152. allErrs = append(allErrs, field.Invalid(dataPath.Key(core.DockerConfigKey), "<secret contents redacted>", err.Error()))
  4153. }
  4154. case core.SecretTypeDockerConfigJson:
  4155. dockerConfigJsonBytes, exists := secret.Data[core.DockerConfigJsonKey]
  4156. if !exists {
  4157. allErrs = append(allErrs, field.Required(dataPath.Key(core.DockerConfigJsonKey), ""))
  4158. break
  4159. }
  4160. // make sure that the content is well-formed json.
  4161. if err := json.Unmarshal(dockerConfigJsonBytes, &map[string]interface{}{}); err != nil {
  4162. allErrs = append(allErrs, field.Invalid(dataPath.Key(core.DockerConfigJsonKey), "<secret contents redacted>", err.Error()))
  4163. }
  4164. case core.SecretTypeBasicAuth:
  4165. _, usernameFieldExists := secret.Data[core.BasicAuthUsernameKey]
  4166. _, passwordFieldExists := secret.Data[core.BasicAuthPasswordKey]
  4167. // username or password might be empty, but the field must be present
  4168. if !usernameFieldExists && !passwordFieldExists {
  4169. allErrs = append(allErrs, field.Required(field.NewPath("data[%s]").Key(core.BasicAuthUsernameKey), ""))
  4170. allErrs = append(allErrs, field.Required(field.NewPath("data[%s]").Key(core.BasicAuthPasswordKey), ""))
  4171. break
  4172. }
  4173. case core.SecretTypeSSHAuth:
  4174. if len(secret.Data[core.SSHAuthPrivateKey]) == 0 {
  4175. allErrs = append(allErrs, field.Required(field.NewPath("data[%s]").Key(core.SSHAuthPrivateKey), ""))
  4176. break
  4177. }
  4178. case core.SecretTypeTLS:
  4179. if _, exists := secret.Data[core.TLSCertKey]; !exists {
  4180. allErrs = append(allErrs, field.Required(dataPath.Key(core.TLSCertKey), ""))
  4181. }
  4182. if _, exists := secret.Data[core.TLSPrivateKeyKey]; !exists {
  4183. allErrs = append(allErrs, field.Required(dataPath.Key(core.TLSPrivateKeyKey), ""))
  4184. }
  4185. // TODO: Verify that the key matches the cert.
  4186. default:
  4187. // no-op
  4188. }
  4189. return allErrs
  4190. }
  4191. // ValidateSecretUpdate tests if required fields in the Secret are set.
  4192. func ValidateSecretUpdate(newSecret, oldSecret *core.Secret) field.ErrorList {
  4193. allErrs := ValidateObjectMetaUpdate(&newSecret.ObjectMeta, &oldSecret.ObjectMeta, field.NewPath("metadata"))
  4194. if len(newSecret.Type) == 0 {
  4195. newSecret.Type = oldSecret.Type
  4196. }
  4197. allErrs = append(allErrs, ValidateImmutableField(newSecret.Type, oldSecret.Type, field.NewPath("type"))...)
  4198. allErrs = append(allErrs, ValidateSecret(newSecret)...)
  4199. return allErrs
  4200. }
  4201. // ValidateConfigMapName can be used to check whether the given ConfigMap name is valid.
  4202. // Prefix indicates this name will be used as part of generation, in which case
  4203. // trailing dashes are allowed.
  4204. var ValidateConfigMapName = apimachineryvalidation.NameIsDNSSubdomain
  4205. // ValidateConfigMap tests whether required fields in the ConfigMap are set.
  4206. func ValidateConfigMap(cfg *core.ConfigMap) field.ErrorList {
  4207. allErrs := field.ErrorList{}
  4208. allErrs = append(allErrs, ValidateObjectMeta(&cfg.ObjectMeta, true, ValidateConfigMapName, field.NewPath("metadata"))...)
  4209. totalSize := 0
  4210. for key, value := range cfg.Data {
  4211. for _, msg := range validation.IsConfigMapKey(key) {
  4212. allErrs = append(allErrs, field.Invalid(field.NewPath("data").Key(key), key, msg))
  4213. }
  4214. // check if we have a duplicate key in the other bag
  4215. if _, isValue := cfg.BinaryData[key]; isValue {
  4216. msg := "duplicate of key present in binaryData"
  4217. allErrs = append(allErrs, field.Invalid(field.NewPath("data").Key(key), key, msg))
  4218. }
  4219. totalSize += len(value)
  4220. }
  4221. for key, value := range cfg.BinaryData {
  4222. for _, msg := range validation.IsConfigMapKey(key) {
  4223. allErrs = append(allErrs, field.Invalid(field.NewPath("binaryData").Key(key), key, msg))
  4224. }
  4225. totalSize += len(value)
  4226. }
  4227. if totalSize > core.MaxSecretSize {
  4228. // pass back "" to indicate that the error refers to the whole object.
  4229. allErrs = append(allErrs, field.TooLong(field.NewPath(""), cfg, core.MaxSecretSize))
  4230. }
  4231. return allErrs
  4232. }
  4233. // ValidateConfigMapUpdate tests if required fields in the ConfigMap are set.
  4234. func ValidateConfigMapUpdate(newCfg, oldCfg *core.ConfigMap) field.ErrorList {
  4235. allErrs := field.ErrorList{}
  4236. allErrs = append(allErrs, ValidateObjectMetaUpdate(&newCfg.ObjectMeta, &oldCfg.ObjectMeta, field.NewPath("metadata"))...)
  4237. allErrs = append(allErrs, ValidateConfigMap(newCfg)...)
  4238. return allErrs
  4239. }
  4240. func validateBasicResource(quantity resource.Quantity, fldPath *field.Path) field.ErrorList {
  4241. if quantity.Value() < 0 {
  4242. return field.ErrorList{field.Invalid(fldPath, quantity.Value(), "must be a valid resource quantity")}
  4243. }
  4244. return field.ErrorList{}
  4245. }
  4246. // Validates resource requirement spec.
  4247. func ValidateResourceRequirements(requirements *core.ResourceRequirements, fldPath *field.Path) field.ErrorList {
  4248. allErrs := field.ErrorList{}
  4249. limPath := fldPath.Child("limits")
  4250. reqPath := fldPath.Child("requests")
  4251. limContainsCPUOrMemory := false
  4252. reqContainsCPUOrMemory := false
  4253. limContainsHugePages := false
  4254. reqContainsHugePages := false
  4255. supportedQoSComputeResources := sets.NewString(string(core.ResourceCPU), string(core.ResourceMemory))
  4256. for resourceName, quantity := range requirements.Limits {
  4257. fldPath := limPath.Key(string(resourceName))
  4258. // Validate resource name.
  4259. allErrs = append(allErrs, validateContainerResourceName(string(resourceName), fldPath)...)
  4260. // Validate resource quantity.
  4261. allErrs = append(allErrs, ValidateResourceQuantityValue(string(resourceName), quantity, fldPath)...)
  4262. if helper.IsHugePageResourceName(resourceName) {
  4263. limContainsHugePages = true
  4264. }
  4265. if supportedQoSComputeResources.Has(string(resourceName)) {
  4266. limContainsCPUOrMemory = true
  4267. }
  4268. }
  4269. for resourceName, quantity := range requirements.Requests {
  4270. fldPath := reqPath.Key(string(resourceName))
  4271. // Validate resource name.
  4272. allErrs = append(allErrs, validateContainerResourceName(string(resourceName), fldPath)...)
  4273. // Validate resource quantity.
  4274. allErrs = append(allErrs, ValidateResourceQuantityValue(string(resourceName), quantity, fldPath)...)
  4275. // Check that request <= limit.
  4276. limitQuantity, exists := requirements.Limits[resourceName]
  4277. if exists {
  4278. // For non overcommitable resources, not only requests can't exceed limits, they also can't be lower, i.e. must be equal.
  4279. if quantity.Cmp(limitQuantity) != 0 && !helper.IsOvercommitAllowed(resourceName) {
  4280. allErrs = append(allErrs, field.Invalid(reqPath, quantity.String(), fmt.Sprintf("must be equal to %s limit", resourceName)))
  4281. } else if quantity.Cmp(limitQuantity) > 0 {
  4282. allErrs = append(allErrs, field.Invalid(reqPath, quantity.String(), fmt.Sprintf("must be less than or equal to %s limit", resourceName)))
  4283. }
  4284. } else if !helper.IsOvercommitAllowed(resourceName) {
  4285. allErrs = append(allErrs, field.Required(limPath, "Limit must be set for non overcommitable resources"))
  4286. }
  4287. if helper.IsHugePageResourceName(resourceName) {
  4288. reqContainsHugePages = true
  4289. }
  4290. if supportedQoSComputeResources.Has(string(resourceName)) {
  4291. reqContainsCPUOrMemory = true
  4292. }
  4293. }
  4294. if !limContainsCPUOrMemory && !reqContainsCPUOrMemory && (reqContainsHugePages || limContainsHugePages) {
  4295. allErrs = append(allErrs, field.Forbidden(fldPath, fmt.Sprintf("HugePages require cpu or memory")))
  4296. }
  4297. return allErrs
  4298. }
  4299. // validateResourceQuotaScopes ensures that each enumerated hard resource constraint is valid for set of scopes
  4300. func validateResourceQuotaScopes(resourceQuotaSpec *core.ResourceQuotaSpec, fld *field.Path) field.ErrorList {
  4301. allErrs := field.ErrorList{}
  4302. if len(resourceQuotaSpec.Scopes) == 0 {
  4303. return allErrs
  4304. }
  4305. hardLimits := sets.NewString()
  4306. for k := range resourceQuotaSpec.Hard {
  4307. hardLimits.Insert(string(k))
  4308. }
  4309. fldPath := fld.Child("scopes")
  4310. scopeSet := sets.NewString()
  4311. for _, scope := range resourceQuotaSpec.Scopes {
  4312. if !helper.IsStandardResourceQuotaScope(string(scope)) {
  4313. allErrs = append(allErrs, field.Invalid(fldPath, resourceQuotaSpec.Scopes, "unsupported scope"))
  4314. }
  4315. for _, k := range hardLimits.List() {
  4316. if helper.IsStandardQuotaResourceName(k) && !helper.IsResourceQuotaScopeValidForResource(scope, k) {
  4317. allErrs = append(allErrs, field.Invalid(fldPath, resourceQuotaSpec.Scopes, "unsupported scope applied to resource"))
  4318. }
  4319. }
  4320. scopeSet.Insert(string(scope))
  4321. }
  4322. invalidScopePairs := []sets.String{
  4323. sets.NewString(string(core.ResourceQuotaScopeBestEffort), string(core.ResourceQuotaScopeNotBestEffort)),
  4324. sets.NewString(string(core.ResourceQuotaScopeTerminating), string(core.ResourceQuotaScopeNotTerminating)),
  4325. }
  4326. for _, invalidScopePair := range invalidScopePairs {
  4327. if scopeSet.HasAll(invalidScopePair.List()...) {
  4328. allErrs = append(allErrs, field.Invalid(fldPath, resourceQuotaSpec.Scopes, "conflicting scopes"))
  4329. }
  4330. }
  4331. return allErrs
  4332. }
  4333. // validateScopedResourceSelectorRequirement tests that the match expressions has valid data
  4334. func validateScopedResourceSelectorRequirement(resourceQuotaSpec *core.ResourceQuotaSpec, fld *field.Path) field.ErrorList {
  4335. allErrs := field.ErrorList{}
  4336. hardLimits := sets.NewString()
  4337. for k := range resourceQuotaSpec.Hard {
  4338. hardLimits.Insert(string(k))
  4339. }
  4340. fldPath := fld.Child("matchExpressions")
  4341. scopeSet := sets.NewString()
  4342. for _, req := range resourceQuotaSpec.ScopeSelector.MatchExpressions {
  4343. if !helper.IsStandardResourceQuotaScope(string(req.ScopeName)) {
  4344. allErrs = append(allErrs, field.Invalid(fldPath.Child("scopeName"), req.ScopeName, "unsupported scope"))
  4345. }
  4346. for _, k := range hardLimits.List() {
  4347. if helper.IsStandardQuotaResourceName(k) && !helper.IsResourceQuotaScopeValidForResource(req.ScopeName, k) {
  4348. allErrs = append(allErrs, field.Invalid(fldPath, resourceQuotaSpec.ScopeSelector, "unsupported scope applied to resource"))
  4349. }
  4350. }
  4351. switch req.ScopeName {
  4352. case core.ResourceQuotaScopeBestEffort, core.ResourceQuotaScopeNotBestEffort, core.ResourceQuotaScopeTerminating, core.ResourceQuotaScopeNotTerminating:
  4353. if req.Operator != core.ScopeSelectorOpExists {
  4354. allErrs = append(allErrs, field.Invalid(fldPath.Child("operator"), req.Operator,
  4355. "must be 'Exist' only operator when scope is any of ResourceQuotaScopeTerminating, ResourceQuotaScopeNotTerminating, ResourceQuotaScopeBestEffort and ResourceQuotaScopeNotBestEffort"))
  4356. }
  4357. }
  4358. switch req.Operator {
  4359. case core.ScopeSelectorOpIn, core.ScopeSelectorOpNotIn:
  4360. if len(req.Values) == 0 {
  4361. allErrs = append(allErrs, field.Required(fldPath.Child("values"),
  4362. "must be at least one value when `operator` is 'In' or 'NotIn' for scope selector"))
  4363. }
  4364. case core.ScopeSelectorOpExists, core.ScopeSelectorOpDoesNotExist:
  4365. if len(req.Values) != 0 {
  4366. allErrs = append(allErrs, field.Invalid(fldPath.Child("values"), req.Values,
  4367. "must be no value when `operator` is 'Exist' or 'DoesNotExist' for scope selector"))
  4368. }
  4369. default:
  4370. allErrs = append(allErrs, field.Invalid(fldPath.Child("operator"), req.Operator, "not a valid selector operator"))
  4371. }
  4372. scopeSet.Insert(string(req.ScopeName))
  4373. }
  4374. invalidScopePairs := []sets.String{
  4375. sets.NewString(string(core.ResourceQuotaScopeBestEffort), string(core.ResourceQuotaScopeNotBestEffort)),
  4376. sets.NewString(string(core.ResourceQuotaScopeTerminating), string(core.ResourceQuotaScopeNotTerminating)),
  4377. }
  4378. for _, invalidScopePair := range invalidScopePairs {
  4379. if scopeSet.HasAll(invalidScopePair.List()...) {
  4380. allErrs = append(allErrs, field.Invalid(fldPath, resourceQuotaSpec.Scopes, "conflicting scopes"))
  4381. }
  4382. }
  4383. return allErrs
  4384. }
  4385. // validateScopeSelector tests that the specified scope selector has valid data
  4386. func validateScopeSelector(resourceQuotaSpec *core.ResourceQuotaSpec, fld *field.Path) field.ErrorList {
  4387. allErrs := field.ErrorList{}
  4388. if resourceQuotaSpec.ScopeSelector == nil {
  4389. return allErrs
  4390. }
  4391. allErrs = append(allErrs, validateScopedResourceSelectorRequirement(resourceQuotaSpec, fld.Child("scopeSelector"))...)
  4392. return allErrs
  4393. }
  4394. // ValidateResourceQuota tests if required fields in the ResourceQuota are set.
  4395. func ValidateResourceQuota(resourceQuota *core.ResourceQuota) field.ErrorList {
  4396. allErrs := ValidateObjectMeta(&resourceQuota.ObjectMeta, true, ValidateResourceQuotaName, field.NewPath("metadata"))
  4397. allErrs = append(allErrs, ValidateResourceQuotaSpec(&resourceQuota.Spec, field.NewPath("spec"))...)
  4398. allErrs = append(allErrs, ValidateResourceQuotaStatus(&resourceQuota.Status, field.NewPath("status"))...)
  4399. return allErrs
  4400. }
  4401. func ValidateResourceQuotaStatus(status *core.ResourceQuotaStatus, fld *field.Path) field.ErrorList {
  4402. allErrs := field.ErrorList{}
  4403. fldPath := fld.Child("hard")
  4404. for k, v := range status.Hard {
  4405. resPath := fldPath.Key(string(k))
  4406. allErrs = append(allErrs, ValidateResourceQuotaResourceName(string(k), resPath)...)
  4407. allErrs = append(allErrs, ValidateResourceQuantityValue(string(k), v, resPath)...)
  4408. }
  4409. fldPath = fld.Child("used")
  4410. for k, v := range status.Used {
  4411. resPath := fldPath.Key(string(k))
  4412. allErrs = append(allErrs, ValidateResourceQuotaResourceName(string(k), resPath)...)
  4413. allErrs = append(allErrs, ValidateResourceQuantityValue(string(k), v, resPath)...)
  4414. }
  4415. return allErrs
  4416. }
  4417. func ValidateResourceQuotaSpec(resourceQuotaSpec *core.ResourceQuotaSpec, fld *field.Path) field.ErrorList {
  4418. allErrs := field.ErrorList{}
  4419. fldPath := fld.Child("hard")
  4420. for k, v := range resourceQuotaSpec.Hard {
  4421. resPath := fldPath.Key(string(k))
  4422. allErrs = append(allErrs, ValidateResourceQuotaResourceName(string(k), resPath)...)
  4423. allErrs = append(allErrs, ValidateResourceQuantityValue(string(k), v, resPath)...)
  4424. }
  4425. allErrs = append(allErrs, validateResourceQuotaScopes(resourceQuotaSpec, fld)...)
  4426. allErrs = append(allErrs, validateScopeSelector(resourceQuotaSpec, fld)...)
  4427. return allErrs
  4428. }
  4429. // ValidateResourceQuantityValue enforces that specified quantity is valid for specified resource
  4430. func ValidateResourceQuantityValue(resource string, value resource.Quantity, fldPath *field.Path) field.ErrorList {
  4431. allErrs := field.ErrorList{}
  4432. allErrs = append(allErrs, ValidateNonnegativeQuantity(value, fldPath)...)
  4433. if helper.IsIntegerResourceName(resource) {
  4434. if value.MilliValue()%int64(1000) != int64(0) {
  4435. allErrs = append(allErrs, field.Invalid(fldPath, value, isNotIntegerErrorMsg))
  4436. }
  4437. }
  4438. return allErrs
  4439. }
  4440. // ValidateResourceQuotaUpdate tests to see if the update is legal for an end user to make.
  4441. // newResourceQuota is updated with fields that cannot be changed.
  4442. func ValidateResourceQuotaUpdate(newResourceQuota, oldResourceQuota *core.ResourceQuota) field.ErrorList {
  4443. allErrs := ValidateObjectMetaUpdate(&newResourceQuota.ObjectMeta, &oldResourceQuota.ObjectMeta, field.NewPath("metadata"))
  4444. allErrs = append(allErrs, ValidateResourceQuotaSpec(&newResourceQuota.Spec, field.NewPath("spec"))...)
  4445. // ensure scopes cannot change, and that resources are still valid for scope
  4446. fldPath := field.NewPath("spec", "scopes")
  4447. oldScopes := sets.NewString()
  4448. newScopes := sets.NewString()
  4449. for _, scope := range newResourceQuota.Spec.Scopes {
  4450. newScopes.Insert(string(scope))
  4451. }
  4452. for _, scope := range oldResourceQuota.Spec.Scopes {
  4453. oldScopes.Insert(string(scope))
  4454. }
  4455. if !oldScopes.Equal(newScopes) {
  4456. allErrs = append(allErrs, field.Invalid(fldPath, newResourceQuota.Spec.Scopes, fieldImmutableErrorMsg))
  4457. }
  4458. newResourceQuota.Status = oldResourceQuota.Status
  4459. return allErrs
  4460. }
  4461. // ValidateResourceQuotaStatusUpdate tests to see if the status update is legal for an end user to make.
  4462. // newResourceQuota is updated with fields that cannot be changed.
  4463. func ValidateResourceQuotaStatusUpdate(newResourceQuota, oldResourceQuota *core.ResourceQuota) field.ErrorList {
  4464. allErrs := ValidateObjectMetaUpdate(&newResourceQuota.ObjectMeta, &oldResourceQuota.ObjectMeta, field.NewPath("metadata"))
  4465. if len(newResourceQuota.ResourceVersion) == 0 {
  4466. allErrs = append(allErrs, field.Required(field.NewPath("resourceVersion"), ""))
  4467. }
  4468. fldPath := field.NewPath("status", "hard")
  4469. for k, v := range newResourceQuota.Status.Hard {
  4470. resPath := fldPath.Key(string(k))
  4471. allErrs = append(allErrs, ValidateResourceQuotaResourceName(string(k), resPath)...)
  4472. allErrs = append(allErrs, ValidateResourceQuantityValue(string(k), v, resPath)...)
  4473. }
  4474. fldPath = field.NewPath("status", "used")
  4475. for k, v := range newResourceQuota.Status.Used {
  4476. resPath := fldPath.Key(string(k))
  4477. allErrs = append(allErrs, ValidateResourceQuotaResourceName(string(k), resPath)...)
  4478. allErrs = append(allErrs, ValidateResourceQuantityValue(string(k), v, resPath)...)
  4479. }
  4480. newResourceQuota.Spec = oldResourceQuota.Spec
  4481. return allErrs
  4482. }
  4483. // ValidateNamespace tests if required fields are set.
  4484. func ValidateNamespace(namespace *core.Namespace) field.ErrorList {
  4485. allErrs := ValidateObjectMeta(&namespace.ObjectMeta, false, ValidateNamespaceName, field.NewPath("metadata"))
  4486. for i := range namespace.Spec.Finalizers {
  4487. allErrs = append(allErrs, validateFinalizerName(string(namespace.Spec.Finalizers[i]), field.NewPath("spec", "finalizers"))...)
  4488. }
  4489. return allErrs
  4490. }
  4491. // Validate finalizer names
  4492. func validateFinalizerName(stringValue string, fldPath *field.Path) field.ErrorList {
  4493. allErrs := apimachineryvalidation.ValidateFinalizerName(stringValue, fldPath)
  4494. allErrs = append(allErrs, validateKubeFinalizerName(stringValue, fldPath)...)
  4495. return allErrs
  4496. }
  4497. // validateKubeFinalizerName checks for "standard" names of legacy finalizer
  4498. func validateKubeFinalizerName(stringValue string, fldPath *field.Path) field.ErrorList {
  4499. allErrs := field.ErrorList{}
  4500. if len(strings.Split(stringValue, "/")) == 1 {
  4501. if !helper.IsStandardFinalizerName(stringValue) {
  4502. return append(allErrs, field.Invalid(fldPath, stringValue, "name is neither a standard finalizer name nor is it fully qualified"))
  4503. }
  4504. }
  4505. return allErrs
  4506. }
  4507. // ValidateNamespaceUpdate tests to make sure a namespace update can be applied.
  4508. // newNamespace is updated with fields that cannot be changed
  4509. func ValidateNamespaceUpdate(newNamespace *core.Namespace, oldNamespace *core.Namespace) field.ErrorList {
  4510. allErrs := ValidateObjectMetaUpdate(&newNamespace.ObjectMeta, &oldNamespace.ObjectMeta, field.NewPath("metadata"))
  4511. newNamespace.Spec.Finalizers = oldNamespace.Spec.Finalizers
  4512. newNamespace.Status = oldNamespace.Status
  4513. return allErrs
  4514. }
  4515. // ValidateNamespaceStatusUpdate tests to see if the update is legal for an end user to make. newNamespace is updated with fields
  4516. // that cannot be changed.
  4517. func ValidateNamespaceStatusUpdate(newNamespace, oldNamespace *core.Namespace) field.ErrorList {
  4518. allErrs := ValidateObjectMetaUpdate(&newNamespace.ObjectMeta, &oldNamespace.ObjectMeta, field.NewPath("metadata"))
  4519. newNamespace.Spec = oldNamespace.Spec
  4520. if newNamespace.DeletionTimestamp.IsZero() {
  4521. if newNamespace.Status.Phase != core.NamespaceActive {
  4522. allErrs = append(allErrs, field.Invalid(field.NewPath("status", "Phase"), newNamespace.Status.Phase, "may only be 'Active' if `deletionTimestamp` is empty"))
  4523. }
  4524. } else {
  4525. if newNamespace.Status.Phase != core.NamespaceTerminating {
  4526. allErrs = append(allErrs, field.Invalid(field.NewPath("status", "Phase"), newNamespace.Status.Phase, "may only be 'Terminating' if `deletionTimestamp` is not empty"))
  4527. }
  4528. }
  4529. return allErrs
  4530. }
  4531. // ValidateNamespaceFinalizeUpdate tests to see if the update is legal for an end user to make.
  4532. // newNamespace is updated with fields that cannot be changed.
  4533. func ValidateNamespaceFinalizeUpdate(newNamespace, oldNamespace *core.Namespace) field.ErrorList {
  4534. allErrs := ValidateObjectMetaUpdate(&newNamespace.ObjectMeta, &oldNamespace.ObjectMeta, field.NewPath("metadata"))
  4535. fldPath := field.NewPath("spec", "finalizers")
  4536. for i := range newNamespace.Spec.Finalizers {
  4537. idxPath := fldPath.Index(i)
  4538. allErrs = append(allErrs, validateFinalizerName(string(newNamespace.Spec.Finalizers[i]), idxPath)...)
  4539. }
  4540. newNamespace.Status = oldNamespace.Status
  4541. return allErrs
  4542. }
  4543. // ValidateEndpoints tests if required fields are set.
  4544. func ValidateEndpoints(endpoints *core.Endpoints) field.ErrorList {
  4545. allErrs := ValidateObjectMeta(&endpoints.ObjectMeta, true, ValidateEndpointsName, field.NewPath("metadata"))
  4546. allErrs = append(allErrs, ValidateEndpointsSpecificAnnotations(endpoints.Annotations, field.NewPath("annotations"))...)
  4547. allErrs = append(allErrs, validateEndpointSubsets(endpoints.Subsets, field.NewPath("subsets"))...)
  4548. return allErrs
  4549. }
  4550. func validateEndpointSubsets(subsets []core.EndpointSubset, fldPath *field.Path) field.ErrorList {
  4551. allErrs := field.ErrorList{}
  4552. for i := range subsets {
  4553. ss := &subsets[i]
  4554. idxPath := fldPath.Index(i)
  4555. // EndpointSubsets must include endpoint address. For headless service, we allow its endpoints not to have ports.
  4556. if len(ss.Addresses) == 0 && len(ss.NotReadyAddresses) == 0 {
  4557. //TODO: consider adding a RequiredOneOf() error for this and similar cases
  4558. allErrs = append(allErrs, field.Required(idxPath, "must specify `addresses` or `notReadyAddresses`"))
  4559. }
  4560. for addr := range ss.Addresses {
  4561. allErrs = append(allErrs, validateEndpointAddress(&ss.Addresses[addr], idxPath.Child("addresses").Index(addr))...)
  4562. }
  4563. for addr := range ss.NotReadyAddresses {
  4564. allErrs = append(allErrs, validateEndpointAddress(&ss.NotReadyAddresses[addr], idxPath.Child("notReadyAddresses").Index(addr))...)
  4565. }
  4566. for port := range ss.Ports {
  4567. allErrs = append(allErrs, validateEndpointPort(&ss.Ports[port], len(ss.Ports) > 1, idxPath.Child("ports").Index(port))...)
  4568. }
  4569. }
  4570. return allErrs
  4571. }
  4572. func validateEndpointAddress(address *core.EndpointAddress, fldPath *field.Path) field.ErrorList {
  4573. allErrs := field.ErrorList{}
  4574. for _, msg := range validation.IsValidIP(address.IP) {
  4575. allErrs = append(allErrs, field.Invalid(fldPath.Child("ip"), address.IP, msg))
  4576. }
  4577. if len(address.Hostname) > 0 {
  4578. allErrs = append(allErrs, ValidateDNS1123Label(address.Hostname, fldPath.Child("hostname"))...)
  4579. }
  4580. // During endpoint update, verify that NodeName is a DNS subdomain and transition rules allow the update
  4581. if address.NodeName != nil {
  4582. for _, msg := range ValidateNodeName(*address.NodeName, false) {
  4583. allErrs = append(allErrs, field.Invalid(fldPath.Child("nodeName"), *address.NodeName, msg))
  4584. }
  4585. }
  4586. allErrs = append(allErrs, validateNonSpecialIP(address.IP, fldPath.Child("ip"))...)
  4587. return allErrs
  4588. }
  4589. func validateNonSpecialIP(ipAddress string, fldPath *field.Path) field.ErrorList {
  4590. // We disallow some IPs as endpoints or external-ips. Specifically,
  4591. // unspecified and loopback addresses are nonsensical and link-local
  4592. // addresses tend to be used for node-centric purposes (e.g. metadata
  4593. // service).
  4594. allErrs := field.ErrorList{}
  4595. ip := net.ParseIP(ipAddress)
  4596. if ip == nil {
  4597. allErrs = append(allErrs, field.Invalid(fldPath, ipAddress, "must be a valid IP address"))
  4598. return allErrs
  4599. }
  4600. if ip.IsUnspecified() {
  4601. allErrs = append(allErrs, field.Invalid(fldPath, ipAddress, "may not be unspecified (0.0.0.0)"))
  4602. }
  4603. if ip.IsLoopback() {
  4604. allErrs = append(allErrs, field.Invalid(fldPath, ipAddress, "may not be in the loopback range (127.0.0.0/8)"))
  4605. }
  4606. if ip.IsLinkLocalUnicast() {
  4607. allErrs = append(allErrs, field.Invalid(fldPath, ipAddress, "may not be in the link-local range (169.254.0.0/16)"))
  4608. }
  4609. if ip.IsLinkLocalMulticast() {
  4610. allErrs = append(allErrs, field.Invalid(fldPath, ipAddress, "may not be in the link-local multicast range (224.0.0.0/24)"))
  4611. }
  4612. return allErrs
  4613. }
  4614. func validateEndpointPort(port *core.EndpointPort, requireName bool, fldPath *field.Path) field.ErrorList {
  4615. allErrs := field.ErrorList{}
  4616. if requireName && len(port.Name) == 0 {
  4617. allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
  4618. } else if len(port.Name) != 0 {
  4619. allErrs = append(allErrs, ValidateDNS1123Label(port.Name, fldPath.Child("name"))...)
  4620. }
  4621. for _, msg := range validation.IsValidPortNum(int(port.Port)) {
  4622. allErrs = append(allErrs, field.Invalid(fldPath.Child("port"), port.Port, msg))
  4623. }
  4624. if len(port.Protocol) == 0 {
  4625. allErrs = append(allErrs, field.Required(fldPath.Child("protocol"), ""))
  4626. } else if !supportedPortProtocols.Has(string(port.Protocol)) {
  4627. allErrs = append(allErrs, field.NotSupported(fldPath.Child("protocol"), port.Protocol, supportedPortProtocols.List()))
  4628. }
  4629. return allErrs
  4630. }
  4631. // ValidateEndpointsUpdate tests to make sure an endpoints update can be applied.
  4632. // NodeName changes are allowed during update to accommodate the case where nodeIP or PodCIDR is reused.
  4633. // An existing endpoint ip will have a different nodeName if this happens.
  4634. func ValidateEndpointsUpdate(newEndpoints, oldEndpoints *core.Endpoints) field.ErrorList {
  4635. allErrs := ValidateObjectMetaUpdate(&newEndpoints.ObjectMeta, &oldEndpoints.ObjectMeta, field.NewPath("metadata"))
  4636. allErrs = append(allErrs, validateEndpointSubsets(newEndpoints.Subsets, field.NewPath("subsets"))...)
  4637. allErrs = append(allErrs, ValidateEndpointsSpecificAnnotations(newEndpoints.Annotations, field.NewPath("annotations"))...)
  4638. return allErrs
  4639. }
  4640. // ValidateSecurityContext ensures the security context contains valid settings
  4641. func ValidateSecurityContext(sc *core.SecurityContext, fldPath *field.Path) field.ErrorList {
  4642. allErrs := field.ErrorList{}
  4643. //this should only be true for testing since SecurityContext is defaulted by the core
  4644. if sc == nil {
  4645. return allErrs
  4646. }
  4647. if sc.Privileged != nil {
  4648. if *sc.Privileged && !capabilities.Get().AllowPrivileged {
  4649. allErrs = append(allErrs, field.Forbidden(fldPath.Child("privileged"), "disallowed by cluster policy"))
  4650. }
  4651. }
  4652. if sc.RunAsUser != nil {
  4653. for _, msg := range validation.IsValidUserID(*sc.RunAsUser) {
  4654. allErrs = append(allErrs, field.Invalid(fldPath.Child("runAsUser"), *sc.RunAsUser, msg))
  4655. }
  4656. }
  4657. if sc.RunAsGroup != nil {
  4658. for _, msg := range validation.IsValidGroupID(*sc.RunAsGroup) {
  4659. allErrs = append(allErrs, field.Invalid(fldPath.Child("runAsGroup"), *sc.RunAsGroup, msg))
  4660. }
  4661. }
  4662. if sc.ProcMount != nil {
  4663. if err := ValidateProcMountType(fldPath.Child("procMount"), *sc.ProcMount); err != nil {
  4664. allErrs = append(allErrs, err)
  4665. }
  4666. }
  4667. if sc.AllowPrivilegeEscalation != nil && !*sc.AllowPrivilegeEscalation {
  4668. if sc.Privileged != nil && *sc.Privileged {
  4669. allErrs = append(allErrs, field.Invalid(fldPath, sc, "cannot set `allowPrivilegeEscalation` to false and `privileged` to true"))
  4670. }
  4671. if sc.Capabilities != nil {
  4672. for _, cap := range sc.Capabilities.Add {
  4673. if string(cap) == "CAP_SYS_ADMIN" {
  4674. allErrs = append(allErrs, field.Invalid(fldPath, sc, "cannot set `allowPrivilegeEscalation` to false and `capabilities.Add` CAP_SYS_ADMIN"))
  4675. }
  4676. }
  4677. }
  4678. }
  4679. allErrs = append(allErrs, validateWindowsSecurityContextOptions(sc.WindowsOptions, fldPath.Child("windowsOptions"))...)
  4680. return allErrs
  4681. }
  4682. // maxGMSACredentialSpecLength is the max length, in bytes, for the actual contents
  4683. // of a GMSA cred spec. In general, those shouldn't be more than a few hundred bytes,
  4684. // so we want to give plenty of room here while still providing an upper bound.
  4685. const (
  4686. maxGMSACredentialSpecLengthInKiB = 64
  4687. maxGMSACredentialSpecLength = maxGMSACredentialSpecLengthInKiB * 1024
  4688. )
  4689. func validateWindowsSecurityContextOptions(windowsOptions *core.WindowsSecurityContextOptions, fieldPath *field.Path) field.ErrorList {
  4690. allErrs := field.ErrorList{}
  4691. if windowsOptions == nil {
  4692. return allErrs
  4693. }
  4694. if windowsOptions.GMSACredentialSpecName != nil {
  4695. // gmsaCredentialSpecName must be the name of a custom resource
  4696. for _, msg := range validation.IsDNS1123Subdomain(*windowsOptions.GMSACredentialSpecName) {
  4697. allErrs = append(allErrs, field.Invalid(fieldPath.Child("gmsaCredentialSpecName"), windowsOptions.GMSACredentialSpecName, msg))
  4698. }
  4699. }
  4700. if windowsOptions.GMSACredentialSpec != nil {
  4701. if l := len(*windowsOptions.GMSACredentialSpec); l == 0 {
  4702. allErrs = append(allErrs, field.Invalid(fieldPath.Child("gmsaCredentialSpec"), windowsOptions.GMSACredentialSpec, "gmsaCredentialSpec cannot be an empty string"))
  4703. } else if l > maxGMSACredentialSpecLength {
  4704. errMsg := fmt.Sprintf("gmsaCredentialSpec size must be under %d KiB", maxGMSACredentialSpecLengthInKiB)
  4705. allErrs = append(allErrs, field.Invalid(fieldPath.Child("gmsaCredentialSpec"), windowsOptions.GMSACredentialSpec, errMsg))
  4706. }
  4707. }
  4708. return allErrs
  4709. }
  4710. func ValidatePodLogOptions(opts *core.PodLogOptions) field.ErrorList {
  4711. allErrs := field.ErrorList{}
  4712. if opts.TailLines != nil && *opts.TailLines < 0 {
  4713. allErrs = append(allErrs, field.Invalid(field.NewPath("tailLines"), *opts.TailLines, isNegativeErrorMsg))
  4714. }
  4715. if opts.LimitBytes != nil && *opts.LimitBytes < 1 {
  4716. allErrs = append(allErrs, field.Invalid(field.NewPath("limitBytes"), *opts.LimitBytes, "must be greater than 0"))
  4717. }
  4718. switch {
  4719. case opts.SinceSeconds != nil && opts.SinceTime != nil:
  4720. allErrs = append(allErrs, field.Forbidden(field.NewPath(""), "at most one of `sinceTime` or `sinceSeconds` may be specified"))
  4721. case opts.SinceSeconds != nil:
  4722. if *opts.SinceSeconds < 1 {
  4723. allErrs = append(allErrs, field.Invalid(field.NewPath("sinceSeconds"), *opts.SinceSeconds, "must be greater than 0"))
  4724. }
  4725. }
  4726. return allErrs
  4727. }
  4728. // ValidateLoadBalancerStatus validates required fields on a LoadBalancerStatus
  4729. func ValidateLoadBalancerStatus(status *core.LoadBalancerStatus, fldPath *field.Path) field.ErrorList {
  4730. allErrs := field.ErrorList{}
  4731. for i, ingress := range status.Ingress {
  4732. idxPath := fldPath.Child("ingress").Index(i)
  4733. if len(ingress.IP) > 0 {
  4734. if isIP := (net.ParseIP(ingress.IP) != nil); !isIP {
  4735. allErrs = append(allErrs, field.Invalid(idxPath.Child("ip"), ingress.IP, "must be a valid IP address"))
  4736. }
  4737. }
  4738. if len(ingress.Hostname) > 0 {
  4739. for _, msg := range validation.IsDNS1123Subdomain(ingress.Hostname) {
  4740. allErrs = append(allErrs, field.Invalid(idxPath.Child("hostname"), ingress.Hostname, msg))
  4741. }
  4742. if isIP := (net.ParseIP(ingress.Hostname) != nil); isIP {
  4743. allErrs = append(allErrs, field.Invalid(idxPath.Child("hostname"), ingress.Hostname, "must be a DNS name, not an IP address"))
  4744. }
  4745. }
  4746. }
  4747. return allErrs
  4748. }
  4749. // validateVolumeNodeAffinity tests that the PersistentVolume.NodeAffinity has valid data
  4750. // returns:
  4751. // - true if volumeNodeAffinity is set
  4752. // - errorList if there are validation errors
  4753. func validateVolumeNodeAffinity(nodeAffinity *core.VolumeNodeAffinity, fldPath *field.Path) (bool, field.ErrorList) {
  4754. allErrs := field.ErrorList{}
  4755. if nodeAffinity == nil {
  4756. return false, allErrs
  4757. }
  4758. if nodeAffinity.Required != nil {
  4759. allErrs = append(allErrs, ValidateNodeSelector(nodeAffinity.Required, fldPath.Child("required"))...)
  4760. } else {
  4761. allErrs = append(allErrs, field.Required(fldPath.Child("required"), "must specify required node constraints"))
  4762. }
  4763. return true, allErrs
  4764. }
  4765. // ValidateCIDR validates whether a CIDR matches the conventions expected by net.ParseCIDR
  4766. func ValidateCIDR(cidr string) (*net.IPNet, error) {
  4767. _, net, err := net.ParseCIDR(cidr)
  4768. if err != nil {
  4769. return nil, err
  4770. }
  4771. return net, nil
  4772. }
  4773. func IsDecremented(update, old *int32) bool {
  4774. if update == nil && old != nil {
  4775. return true
  4776. }
  4777. if update == nil || old == nil {
  4778. return false
  4779. }
  4780. return *update < *old
  4781. }
  4782. // ValidateProcMountType tests that the argument is a valid ProcMountType.
  4783. func ValidateProcMountType(fldPath *field.Path, procMountType core.ProcMountType) *field.Error {
  4784. switch procMountType {
  4785. case core.DefaultProcMount, core.UnmaskedProcMount:
  4786. return nil
  4787. default:
  4788. return field.NotSupported(fldPath, procMountType, []string{string(core.DefaultProcMount), string(core.UnmaskedProcMount)})
  4789. }
  4790. }