validation_test.go 393 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328113291133011331113321133311334113351133611337113381133911340113411134211343113441134511346113471134811349113501135111352113531135411355113561135711358113591136011361113621136311364113651136611367113681136911370113711137211373113741137511376113771137811379113801138111382113831138411385113861138711388113891139011391113921139311394113951139611397113981139911400114011140211403114041140511406114071140811409114101141111412114131141411415114161141711418114191142011421114221142311424114251142611427114281142911430114311143211433114341143511436114371143811439114401144111442114431144411445114461144711448114491145011451114521145311454114551145611457114581145911460114611146211463114641146511466114671146811469114701147111472114731147411475114761147711478114791148011481114821148311484114851148611487114881148911490114911149211493114941149511496114971149811499115001150111502115031150411505115061150711508115091151011511115121151311514115151151611517115181151911520115211152211523115241152511526115271152811529115301153111532115331153411535115361153711538115391154011541115421154311544115451154611547115481154911550115511155211553115541155511556115571155811559115601156111562115631156411565115661156711568115691157011571115721157311574115751157611577115781157911580115811158211583115841158511586115871158811589115901159111592115931159411595115961159711598115991160011601116021160311604116051160611607116081160911610116111161211613116141161511616116171161811619116201162111622116231162411625116261162711628116291163011631116321163311634116351163611637116381163911640116411164211643116441164511646116471164811649116501165111652116531165411655116561165711658116591166011661116621166311664116651166611667116681166911670116711167211673116741167511676116771167811679116801168111682116831168411685116861168711688116891169011691116921169311694116951169611697116981169911700117011170211703117041170511706117071170811709117101171111712117131171411715117161171711718117191172011721117221172311724117251172611727117281172911730117311173211733117341173511736117371173811739117401174111742117431174411745117461174711748117491175011751117521175311754117551175611757117581175911760117611176211763117641176511766117671176811769117701177111772117731177411775117761177711778117791178011781117821178311784117851178611787117881178911790117911179211793117941179511796117971179811799118001180111802118031180411805118061180711808118091181011811118121181311814118151181611817118181181911820118211182211823118241182511826118271182811829118301183111832118331183411835118361183711838118391184011841118421184311844118451184611847118481184911850118511185211853118541185511856118571185811859118601186111862118631186411865118661186711868118691187011871118721187311874118751187611877118781187911880118811188211883118841188511886118871188811889118901189111892118931189411895118961189711898118991190011901119021190311904119051190611907119081190911910119111191211913119141191511916119171191811919119201192111922119231192411925119261192711928119291193011931119321193311934119351193611937119381193911940119411194211943119441194511946119471194811949119501195111952119531195411955119561195711958119591196011961119621196311964119651196611967119681196911970119711197211973119741197511976119771197811979119801198111982119831198411985119861198711988119891199011991119921199311994119951199611997119981199912000120011200212003120041200512006120071200812009120101201112012120131201412015120161201712018120191202012021120221202312024120251202612027120281202912030120311203212033120341203512036120371203812039120401204112042120431204412045120461204712048120491205012051120521205312054120551205612057120581205912060120611206212063120641206512066120671206812069120701207112072120731207412075120761207712078120791208012081120821208312084120851208612087120881208912090120911209212093120941209512096120971209812099121001210112102121031210412105121061210712108121091211012111121121211312114121151211612117121181211912120121211212212123121241212512126121271212812129121301213112132121331213412135121361213712138121391214012141121421214312144121451214612147121481214912150121511215212153121541215512156121571215812159121601216112162121631216412165121661216712168121691217012171121721217312174121751217612177121781217912180121811218212183121841218512186121871218812189121901219112192121931219412195121961219712198121991220012201122021220312204122051220612207122081220912210122111221212213122141221512216122171221812219122201222112222122231222412225122261222712228122291223012231122321223312234122351223612237122381223912240122411224212243122441224512246122471224812249122501225112252122531225412255122561225712258122591226012261122621226312264122651226612267122681226912270122711227212273122741227512276122771227812279122801228112282122831228412285122861228712288122891229012291122921229312294122951229612297122981229912300123011230212303123041230512306123071230812309123101231112312123131231412315123161231712318123191232012321123221232312324123251232612327123281232912330123311233212333123341233512336123371233812339123401234112342123431234412345123461234712348123491235012351123521235312354123551235612357123581235912360123611236212363123641236512366123671236812369123701237112372123731237412375123761237712378123791238012381123821238312384123851238612387123881238912390123911239212393123941239512396123971239812399124001240112402124031240412405124061240712408124091241012411124121241312414124151241612417124181241912420124211242212423124241242512426124271242812429124301243112432124331243412435124361243712438124391244012441124421244312444124451244612447124481244912450124511245212453124541245512456124571245812459124601246112462124631246412465124661246712468124691247012471124721247312474124751247612477124781247912480124811248212483124841248512486124871248812489124901249112492124931249412495124961249712498124991250012501125021250312504125051250612507125081250912510125111251212513125141251512516125171251812519125201252112522125231252412525125261252712528125291253012531125321253312534125351253612537125381253912540125411254212543125441254512546125471254812549125501255112552125531255412555125561255712558125591256012561125621256312564125651256612567125681256912570125711257212573125741257512576125771257812579125801258112582125831258412585125861258712588125891259012591125921259312594125951259612597125981259912600126011260212603126041260512606126071260812609126101261112612126131261412615126161261712618126191262012621126221262312624126251262612627126281262912630126311263212633126341263512636126371263812639126401264112642126431264412645126461264712648126491265012651126521265312654126551265612657126581265912660126611266212663126641266512666126671266812669126701267112672126731267412675126761267712678126791268012681126821268312684126851268612687126881268912690126911269212693126941269512696126971269812699127001270112702127031270412705127061270712708127091271012711127121271312714127151271612717127181271912720127211272212723127241272512726127271272812729127301273112732127331273412735127361273712738127391274012741127421274312744127451274612747127481274912750127511275212753127541275512756127571275812759127601276112762127631276412765127661276712768127691277012771127721277312774127751277612777127781277912780127811278212783127841278512786127871278812789127901279112792127931279412795127961279712798127991280012801128021280312804128051280612807128081280912810128111281212813128141281512816128171281812819128201282112822128231282412825128261282712828128291283012831128321283312834128351283612837128381283912840128411284212843128441284512846128471284812849128501285112852128531285412855128561285712858128591286012861128621286312864128651286612867128681286912870128711287212873128741287512876128771287812879128801288112882128831288412885128861288712888128891289012891128921289312894128951289612897128981289912900129011290212903129041290512906129071290812909129101291112912129131291412915129161291712918129191292012921129221292312924129251292612927129281292912930129311293212933129341293512936129371293812939129401294112942129431294412945129461294712948129491295012951129521295312954129551295612957129581295912960129611296212963129641296512966129671296812969129701297112972129731297412975129761297712978129791298012981129821298312984129851298612987129881298912990129911299212993129941299512996129971299812999130001300113002130031300413005130061300713008130091301013011130121301313014130151301613017130181301913020130211302213023130241302513026130271302813029130301303113032130331303413035130361303713038130391304013041130421304313044130451304613047130481304913050130511305213053130541305513056130571305813059130601306113062130631306413065130661306713068130691307013071130721307313074130751307613077130781307913080130811308213083130841308513086130871308813089130901309113092130931309413095130961309713098130991310013101131021310313104131051310613107131081310913110131111311213113131141311513116131171311813119131201312113122131231312413125131261312713128131291313013131131321313313134131351313613137131381313913140131411314213143131441314513146131471314813149131501315113152131531315413155131561315713158131591316013161131621316313164131651316613167131681316913170131711317213173131741317513176131771317813179131801318113182131831318413185131861318713188131891319013191131921319313194131951319613197131981319913200132011320213203132041320513206132071320813209132101321113212132131321413215132161321713218132191322013221132221322313224132251322613227132281322913230132311323213233132341323513236132371323813239132401324113242132431324413245132461324713248132491325013251132521325313254132551325613257132581325913260132611326213263132641326513266132671326813269132701327113272132731327413275132761327713278132791328013281132821328313284132851328613287132881328913290132911329213293132941329513296132971329813299133001330113302133031330413305133061330713308133091331013311133121331313314133151331613317133181331913320133211332213323133241332513326133271332813329133301333113332133331333413335133361333713338133391334013341133421334313344133451334613347133481334913350133511335213353133541335513356133571335813359133601336113362133631336413365133661336713368133691337013371133721337313374133751337613377133781337913380133811338213383133841338513386133871338813389133901339113392133931339413395133961339713398133991340013401134021340313404134051340613407134081340913410134111341213413134141341513416134171341813419134201342113422134231342413425134261342713428134291343013431134321343313434134351343613437134381343913440134411344213443134441344513446134471344813449134501345113452134531345413455134561345713458134591346013461134621346313464134651346613467134681346913470134711347213473134741347513476134771347813479134801348113482134831348413485134861348713488134891349013491134921349313494134951349613497
  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. "bytes"
  16. "math"
  17. "reflect"
  18. "strings"
  19. "testing"
  20. "k8s.io/api/core/v1"
  21. "k8s.io/apimachinery/pkg/api/resource"
  22. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  23. "k8s.io/apimachinery/pkg/util/intstr"
  24. "k8s.io/apimachinery/pkg/util/validation"
  25. "k8s.io/apimachinery/pkg/util/validation/field"
  26. utilfeature "k8s.io/apiserver/pkg/util/feature"
  27. featuregatetesting "k8s.io/component-base/featuregate/testing"
  28. _ "k8s.io/kubernetes/pkg/api/testapi"
  29. "k8s.io/kubernetes/pkg/apis/core"
  30. "k8s.io/kubernetes/pkg/capabilities"
  31. "k8s.io/kubernetes/pkg/features"
  32. "k8s.io/kubernetes/pkg/security/apparmor"
  33. utilpointer "k8s.io/utils/pointer"
  34. )
  35. const (
  36. dnsLabelErrMsg = "a DNS-1123 label must consist of"
  37. dnsSubdomainLabelErrMsg = "a DNS-1123 subdomain"
  38. envVarNameErrMsg = "a valid environment variable name must consist of"
  39. )
  40. func newHostPathType(pathType string) *core.HostPathType {
  41. hostPathType := new(core.HostPathType)
  42. *hostPathType = core.HostPathType(pathType)
  43. return hostPathType
  44. }
  45. func testVolume(name string, namespace string, spec core.PersistentVolumeSpec) *core.PersistentVolume {
  46. objMeta := metav1.ObjectMeta{Name: name}
  47. if namespace != "" {
  48. objMeta.Namespace = namespace
  49. }
  50. return &core.PersistentVolume{
  51. ObjectMeta: objMeta,
  52. Spec: spec,
  53. }
  54. }
  55. func TestValidatePersistentVolumes(t *testing.T) {
  56. validMode := core.PersistentVolumeFilesystem
  57. invalidMode := core.PersistentVolumeMode("fakeVolumeMode")
  58. scenarios := map[string]struct {
  59. isExpectedFailure bool
  60. volume *core.PersistentVolume
  61. }{
  62. "good-volume": {
  63. isExpectedFailure: false,
  64. volume: testVolume("foo", "", core.PersistentVolumeSpec{
  65. Capacity: core.ResourceList{
  66. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  67. },
  68. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  69. PersistentVolumeSource: core.PersistentVolumeSource{
  70. HostPath: &core.HostPathVolumeSource{
  71. Path: "/foo",
  72. Type: newHostPathType(string(core.HostPathDirectory)),
  73. },
  74. },
  75. }),
  76. },
  77. "good-volume-with-capacity-unit": {
  78. isExpectedFailure: false,
  79. volume: testVolume("foo", "", core.PersistentVolumeSpec{
  80. Capacity: core.ResourceList{
  81. core.ResourceName(core.ResourceStorage): resource.MustParse("10Gi"),
  82. },
  83. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  84. PersistentVolumeSource: core.PersistentVolumeSource{
  85. HostPath: &core.HostPathVolumeSource{
  86. Path: "/foo",
  87. Type: newHostPathType(string(core.HostPathDirectory)),
  88. },
  89. },
  90. }),
  91. },
  92. "good-volume-without-capacity-unit": {
  93. isExpectedFailure: false,
  94. volume: testVolume("foo", "", core.PersistentVolumeSpec{
  95. Capacity: core.ResourceList{
  96. core.ResourceName(core.ResourceStorage): resource.MustParse("10"),
  97. },
  98. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  99. PersistentVolumeSource: core.PersistentVolumeSource{
  100. HostPath: &core.HostPathVolumeSource{
  101. Path: "/foo",
  102. Type: newHostPathType(string(core.HostPathDirectory)),
  103. },
  104. },
  105. }),
  106. },
  107. "good-volume-with-storage-class": {
  108. isExpectedFailure: false,
  109. volume: testVolume("foo", "", core.PersistentVolumeSpec{
  110. Capacity: core.ResourceList{
  111. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  112. },
  113. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  114. PersistentVolumeSource: core.PersistentVolumeSource{
  115. HostPath: &core.HostPathVolumeSource{
  116. Path: "/foo",
  117. Type: newHostPathType(string(core.HostPathDirectory)),
  118. },
  119. },
  120. StorageClassName: "valid",
  121. }),
  122. },
  123. "good-volume-with-retain-policy": {
  124. isExpectedFailure: false,
  125. volume: testVolume("foo", "", core.PersistentVolumeSpec{
  126. Capacity: core.ResourceList{
  127. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  128. },
  129. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  130. PersistentVolumeSource: core.PersistentVolumeSource{
  131. HostPath: &core.HostPathVolumeSource{
  132. Path: "/foo",
  133. Type: newHostPathType(string(core.HostPathDirectory)),
  134. },
  135. },
  136. PersistentVolumeReclaimPolicy: core.PersistentVolumeReclaimRetain,
  137. }),
  138. },
  139. "good-volume-with-volume-mode": {
  140. isExpectedFailure: false,
  141. volume: testVolume("foo", "", core.PersistentVolumeSpec{
  142. Capacity: core.ResourceList{
  143. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  144. },
  145. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  146. PersistentVolumeSource: core.PersistentVolumeSource{
  147. HostPath: &core.HostPathVolumeSource{
  148. Path: "/foo",
  149. Type: newHostPathType(string(core.HostPathDirectory)),
  150. },
  151. },
  152. VolumeMode: &validMode,
  153. }),
  154. },
  155. "invalid-accessmode": {
  156. isExpectedFailure: true,
  157. volume: testVolume("foo", "", core.PersistentVolumeSpec{
  158. Capacity: core.ResourceList{
  159. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  160. },
  161. AccessModes: []core.PersistentVolumeAccessMode{"fakemode"},
  162. PersistentVolumeSource: core.PersistentVolumeSource{
  163. HostPath: &core.HostPathVolumeSource{
  164. Path: "/foo",
  165. Type: newHostPathType(string(core.HostPathDirectory)),
  166. },
  167. },
  168. }),
  169. },
  170. "invalid-reclaimpolicy": {
  171. isExpectedFailure: true,
  172. volume: testVolume("foo", "", core.PersistentVolumeSpec{
  173. Capacity: core.ResourceList{
  174. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  175. },
  176. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  177. PersistentVolumeSource: core.PersistentVolumeSource{
  178. HostPath: &core.HostPathVolumeSource{
  179. Path: "/foo",
  180. Type: newHostPathType(string(core.HostPathDirectory)),
  181. },
  182. },
  183. PersistentVolumeReclaimPolicy: "fakeReclaimPolicy",
  184. }),
  185. },
  186. "invalid-volume-mode": {
  187. isExpectedFailure: true,
  188. volume: testVolume("foo", "", core.PersistentVolumeSpec{
  189. Capacity: core.ResourceList{
  190. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  191. },
  192. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  193. PersistentVolumeSource: core.PersistentVolumeSource{
  194. HostPath: &core.HostPathVolumeSource{
  195. Path: "/foo",
  196. Type: newHostPathType(string(core.HostPathDirectory)),
  197. },
  198. },
  199. VolumeMode: &invalidMode,
  200. }),
  201. },
  202. "unexpected-namespace": {
  203. isExpectedFailure: true,
  204. volume: testVolume("foo", "unexpected-namespace", core.PersistentVolumeSpec{
  205. Capacity: core.ResourceList{
  206. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  207. },
  208. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  209. PersistentVolumeSource: core.PersistentVolumeSource{
  210. HostPath: &core.HostPathVolumeSource{
  211. Path: "/foo",
  212. Type: newHostPathType(string(core.HostPathDirectory)),
  213. },
  214. },
  215. }),
  216. },
  217. "missing-volume-source": {
  218. isExpectedFailure: true,
  219. volume: testVolume("foo", "", core.PersistentVolumeSpec{
  220. Capacity: core.ResourceList{
  221. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  222. },
  223. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  224. }),
  225. },
  226. "bad-name": {
  227. isExpectedFailure: true,
  228. volume: testVolume("123*Bad(Name", "unexpected-namespace", core.PersistentVolumeSpec{
  229. Capacity: core.ResourceList{
  230. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  231. },
  232. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  233. PersistentVolumeSource: core.PersistentVolumeSource{
  234. HostPath: &core.HostPathVolumeSource{
  235. Path: "/foo",
  236. Type: newHostPathType(string(core.HostPathDirectory)),
  237. },
  238. },
  239. }),
  240. },
  241. "missing-name": {
  242. isExpectedFailure: true,
  243. volume: testVolume("", "", core.PersistentVolumeSpec{
  244. Capacity: core.ResourceList{
  245. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  246. },
  247. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  248. PersistentVolumeSource: core.PersistentVolumeSource{
  249. HostPath: &core.HostPathVolumeSource{
  250. Path: "/foo",
  251. Type: newHostPathType(string(core.HostPathDirectory)),
  252. },
  253. },
  254. }),
  255. },
  256. "missing-capacity": {
  257. isExpectedFailure: true,
  258. volume: testVolume("foo", "", core.PersistentVolumeSpec{
  259. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  260. PersistentVolumeSource: core.PersistentVolumeSource{
  261. HostPath: &core.HostPathVolumeSource{
  262. Path: "/foo",
  263. Type: newHostPathType(string(core.HostPathDirectory)),
  264. },
  265. },
  266. }),
  267. },
  268. "bad-volume-zero-capacity": {
  269. isExpectedFailure: true,
  270. volume: testVolume("foo", "", core.PersistentVolumeSpec{
  271. Capacity: core.ResourceList{
  272. core.ResourceName(core.ResourceStorage): resource.MustParse("0"),
  273. },
  274. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  275. PersistentVolumeSource: core.PersistentVolumeSource{
  276. HostPath: &core.HostPathVolumeSource{
  277. Path: "/foo",
  278. Type: newHostPathType(string(core.HostPathDirectory)),
  279. },
  280. },
  281. }),
  282. },
  283. "missing-accessmodes": {
  284. isExpectedFailure: true,
  285. volume: testVolume("goodname", "missing-accessmodes", core.PersistentVolumeSpec{
  286. Capacity: core.ResourceList{
  287. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  288. },
  289. PersistentVolumeSource: core.PersistentVolumeSource{
  290. HostPath: &core.HostPathVolumeSource{
  291. Path: "/foo",
  292. Type: newHostPathType(string(core.HostPathDirectory)),
  293. },
  294. },
  295. }),
  296. },
  297. "too-many-sources": {
  298. isExpectedFailure: true,
  299. volume: testVolume("foo", "", core.PersistentVolumeSpec{
  300. Capacity: core.ResourceList{
  301. core.ResourceName(core.ResourceStorage): resource.MustParse("5G"),
  302. },
  303. PersistentVolumeSource: core.PersistentVolumeSource{
  304. HostPath: &core.HostPathVolumeSource{
  305. Path: "/foo",
  306. Type: newHostPathType(string(core.HostPathDirectory)),
  307. },
  308. GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{PDName: "foo", FSType: "ext4"},
  309. },
  310. }),
  311. },
  312. "host mount of / with recycle reclaim policy": {
  313. isExpectedFailure: true,
  314. volume: testVolume("bad-recycle-do-not-want", "", core.PersistentVolumeSpec{
  315. Capacity: core.ResourceList{
  316. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  317. },
  318. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  319. PersistentVolumeSource: core.PersistentVolumeSource{
  320. HostPath: &core.HostPathVolumeSource{
  321. Path: "/",
  322. Type: newHostPathType(string(core.HostPathDirectory)),
  323. },
  324. },
  325. PersistentVolumeReclaimPolicy: core.PersistentVolumeReclaimRecycle,
  326. }),
  327. },
  328. "host mount of / with recycle reclaim policy 2": {
  329. isExpectedFailure: true,
  330. volume: testVolume("bad-recycle-do-not-want", "", core.PersistentVolumeSpec{
  331. Capacity: core.ResourceList{
  332. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  333. },
  334. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  335. PersistentVolumeSource: core.PersistentVolumeSource{
  336. HostPath: &core.HostPathVolumeSource{
  337. Path: "/a/..",
  338. Type: newHostPathType(string(core.HostPathDirectory)),
  339. },
  340. },
  341. PersistentVolumeReclaimPolicy: core.PersistentVolumeReclaimRecycle,
  342. }),
  343. },
  344. "invalid-storage-class-name": {
  345. isExpectedFailure: true,
  346. volume: testVolume("invalid-storage-class-name", "", core.PersistentVolumeSpec{
  347. Capacity: core.ResourceList{
  348. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  349. },
  350. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  351. PersistentVolumeSource: core.PersistentVolumeSource{
  352. HostPath: &core.HostPathVolumeSource{
  353. Path: "/foo",
  354. Type: newHostPathType(string(core.HostPathDirectory)),
  355. },
  356. },
  357. StorageClassName: "-invalid-",
  358. }),
  359. },
  360. "bad-hostpath-volume-backsteps": {
  361. isExpectedFailure: true,
  362. volume: testVolume("foo", "", core.PersistentVolumeSpec{
  363. Capacity: core.ResourceList{
  364. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  365. },
  366. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  367. PersistentVolumeSource: core.PersistentVolumeSource{
  368. HostPath: &core.HostPathVolumeSource{
  369. Path: "/foo/..",
  370. Type: newHostPathType(string(core.HostPathDirectory)),
  371. },
  372. },
  373. StorageClassName: "backstep-hostpath",
  374. }),
  375. },
  376. "volume-node-affinity": {
  377. isExpectedFailure: false,
  378. volume: testVolumeWithNodeAffinity(simpleVolumeNodeAffinity("foo", "bar")),
  379. },
  380. "volume-empty-node-affinity": {
  381. isExpectedFailure: true,
  382. volume: testVolumeWithNodeAffinity(&core.VolumeNodeAffinity{}),
  383. },
  384. "volume-bad-node-affinity": {
  385. isExpectedFailure: true,
  386. volume: testVolumeWithNodeAffinity(
  387. &core.VolumeNodeAffinity{
  388. Required: &core.NodeSelector{
  389. NodeSelectorTerms: []core.NodeSelectorTerm{
  390. {
  391. MatchExpressions: []core.NodeSelectorRequirement{
  392. {
  393. Operator: core.NodeSelectorOpIn,
  394. Values: []string{"test-label-value"},
  395. },
  396. },
  397. },
  398. },
  399. },
  400. }),
  401. },
  402. }
  403. for name, scenario := range scenarios {
  404. t.Run(name, func(t *testing.T) {
  405. errs := ValidatePersistentVolume(scenario.volume)
  406. if len(errs) == 0 && scenario.isExpectedFailure {
  407. t.Errorf("Unexpected success for scenario: %s", name)
  408. }
  409. if len(errs) > 0 && !scenario.isExpectedFailure {
  410. t.Errorf("Unexpected failure for scenario: %s - %+v", name, errs)
  411. }
  412. })
  413. }
  414. }
  415. func TestValidatePersistentVolumeSpec(t *testing.T) {
  416. fsmode := core.PersistentVolumeFilesystem
  417. blockmode := core.PersistentVolumeBlock
  418. scenarios := map[string]struct {
  419. isExpectedFailure bool
  420. isInlineSpec bool
  421. pvSpec *core.PersistentVolumeSpec
  422. }{
  423. "pv-pvspec-valid": {
  424. isExpectedFailure: false,
  425. isInlineSpec: false,
  426. pvSpec: &core.PersistentVolumeSpec{
  427. Capacity: core.ResourceList{
  428. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  429. },
  430. StorageClassName: "testclass",
  431. PersistentVolumeReclaimPolicy: core.PersistentVolumeReclaimRecycle,
  432. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  433. PersistentVolumeSource: core.PersistentVolumeSource{
  434. HostPath: &core.HostPathVolumeSource{
  435. Path: "/foo",
  436. Type: newHostPathType(string(core.HostPathDirectory)),
  437. },
  438. },
  439. VolumeMode: &fsmode,
  440. NodeAffinity: simpleVolumeNodeAffinity("foo", "bar"),
  441. },
  442. },
  443. "inline-pvspec-with-capacity": {
  444. isExpectedFailure: true,
  445. isInlineSpec: true,
  446. pvSpec: &core.PersistentVolumeSpec{
  447. Capacity: core.ResourceList{
  448. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  449. },
  450. PersistentVolumeSource: core.PersistentVolumeSource{
  451. CSI: &core.CSIPersistentVolumeSource{Driver: "test-driver", VolumeHandle: "test-123", ReadOnly: true},
  452. },
  453. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  454. },
  455. },
  456. "inline-pvspec-with-sc": {
  457. isExpectedFailure: true,
  458. isInlineSpec: true,
  459. pvSpec: &core.PersistentVolumeSpec{
  460. PersistentVolumeSource: core.PersistentVolumeSource{
  461. CSI: &core.CSIPersistentVolumeSource{Driver: "test-driver", VolumeHandle: "test-123", ReadOnly: true},
  462. },
  463. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  464. StorageClassName: "testclass",
  465. },
  466. },
  467. "inline-pvspec-with-non-fs-volume-mode": {
  468. isExpectedFailure: true,
  469. isInlineSpec: true,
  470. pvSpec: &core.PersistentVolumeSpec{
  471. PersistentVolumeSource: core.PersistentVolumeSource{
  472. CSI: &core.CSIPersistentVolumeSource{Driver: "test-driver", VolumeHandle: "test-123", ReadOnly: true},
  473. },
  474. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  475. VolumeMode: &blockmode,
  476. },
  477. },
  478. "inline-pvspec-with-non-retain-reclaim-policy": {
  479. isExpectedFailure: true,
  480. isInlineSpec: true,
  481. pvSpec: &core.PersistentVolumeSpec{
  482. PersistentVolumeReclaimPolicy: core.PersistentVolumeReclaimRecycle,
  483. PersistentVolumeSource: core.PersistentVolumeSource{
  484. CSI: &core.CSIPersistentVolumeSource{Driver: "test-driver", VolumeHandle: "test-123", ReadOnly: true},
  485. },
  486. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  487. },
  488. },
  489. "inline-pvspec-with-node-affinity": {
  490. isExpectedFailure: true,
  491. isInlineSpec: true,
  492. pvSpec: &core.PersistentVolumeSpec{
  493. PersistentVolumeSource: core.PersistentVolumeSource{
  494. CSI: &core.CSIPersistentVolumeSource{Driver: "test-driver", VolumeHandle: "test-123", ReadOnly: true},
  495. },
  496. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  497. NodeAffinity: simpleVolumeNodeAffinity("foo", "bar"),
  498. },
  499. },
  500. "inline-pvspec-with-non-csi-source": {
  501. isExpectedFailure: true,
  502. isInlineSpec: true,
  503. pvSpec: &core.PersistentVolumeSpec{
  504. PersistentVolumeSource: core.PersistentVolumeSource{
  505. HostPath: &core.HostPathVolumeSource{
  506. Path: "/foo",
  507. Type: newHostPathType(string(core.HostPathDirectory)),
  508. },
  509. },
  510. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  511. },
  512. },
  513. "inline-pvspec-valid-with-access-modes-and-mount-options": {
  514. isExpectedFailure: false,
  515. isInlineSpec: true,
  516. pvSpec: &core.PersistentVolumeSpec{
  517. PersistentVolumeSource: core.PersistentVolumeSource{
  518. CSI: &core.CSIPersistentVolumeSource{Driver: "test-driver", VolumeHandle: "test-123", ReadOnly: true},
  519. },
  520. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  521. MountOptions: []string{"soft", "read-write"},
  522. },
  523. },
  524. "inline-pvspec-valid-with-access-modes": {
  525. isExpectedFailure: false,
  526. isInlineSpec: true,
  527. pvSpec: &core.PersistentVolumeSpec{
  528. PersistentVolumeSource: core.PersistentVolumeSource{
  529. CSI: &core.CSIPersistentVolumeSource{Driver: "test-driver", VolumeHandle: "test-123", ReadOnly: true},
  530. },
  531. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  532. },
  533. },
  534. "inline-pvspec-with-missing-acess-modes": {
  535. isExpectedFailure: true,
  536. isInlineSpec: true,
  537. pvSpec: &core.PersistentVolumeSpec{
  538. PersistentVolumeSource: core.PersistentVolumeSource{
  539. CSI: &core.CSIPersistentVolumeSource{Driver: "test-driver", VolumeHandle: "test-123", ReadOnly: true},
  540. },
  541. MountOptions: []string{"soft", "read-write"},
  542. },
  543. },
  544. }
  545. for name, scenario := range scenarios {
  546. errs := ValidatePersistentVolumeSpec(scenario.pvSpec, "", scenario.isInlineSpec, field.NewPath("field"))
  547. if len(errs) == 0 && scenario.isExpectedFailure {
  548. t.Errorf("Unexpected success for scenario: %s", name)
  549. }
  550. if len(errs) > 0 && !scenario.isExpectedFailure {
  551. t.Errorf("Unexpected failure for scenario: %s - %+v", name, errs)
  552. }
  553. }
  554. }
  555. func TestValidatePersistentVolumeSourceUpdate(t *testing.T) {
  556. validVolume := testVolume("foo", "", core.PersistentVolumeSpec{
  557. Capacity: core.ResourceList{
  558. core.ResourceName(core.ResourceStorage): resource.MustParse("1G"),
  559. },
  560. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  561. PersistentVolumeSource: core.PersistentVolumeSource{
  562. HostPath: &core.HostPathVolumeSource{
  563. Path: "/foo",
  564. Type: newHostPathType(string(core.HostPathDirectory)),
  565. },
  566. },
  567. StorageClassName: "valid",
  568. })
  569. validPvSourceNoUpdate := validVolume.DeepCopy()
  570. invalidPvSourceUpdateType := validVolume.DeepCopy()
  571. invalidPvSourceUpdateType.Spec.PersistentVolumeSource = core.PersistentVolumeSource{
  572. FlexVolume: &core.FlexPersistentVolumeSource{
  573. Driver: "kubernetes.io/blue",
  574. FSType: "ext4",
  575. },
  576. }
  577. invalidPvSourceUpdateDeep := validVolume.DeepCopy()
  578. invalidPvSourceUpdateDeep.Spec.PersistentVolumeSource = core.PersistentVolumeSource{
  579. HostPath: &core.HostPathVolumeSource{
  580. Path: "/updated",
  581. Type: newHostPathType(string(core.HostPathDirectory)),
  582. },
  583. }
  584. validCSIVolume := testVolume("csi-volume", "", core.PersistentVolumeSpec{
  585. Capacity: core.ResourceList{
  586. core.ResourceName(core.ResourceStorage): resource.MustParse("1G"),
  587. },
  588. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  589. PersistentVolumeSource: core.PersistentVolumeSource{
  590. CSI: &core.CSIPersistentVolumeSource{
  591. Driver: "come.google.gcepd",
  592. VolumeHandle: "foobar",
  593. },
  594. },
  595. StorageClassName: "gp2",
  596. })
  597. expandSecretRef := &core.SecretReference{
  598. Name: "expansion-secret",
  599. Namespace: "default",
  600. }
  601. scenarios := map[string]struct {
  602. isExpectedFailure bool
  603. csiExpansionEnabled bool
  604. oldVolume *core.PersistentVolume
  605. newVolume *core.PersistentVolume
  606. }{
  607. "condition-no-update": {
  608. isExpectedFailure: false,
  609. oldVolume: validVolume,
  610. newVolume: validPvSourceNoUpdate,
  611. },
  612. "condition-update-source-type": {
  613. isExpectedFailure: true,
  614. oldVolume: validVolume,
  615. newVolume: invalidPvSourceUpdateType,
  616. },
  617. "condition-update-source-deep": {
  618. isExpectedFailure: true,
  619. oldVolume: validVolume,
  620. newVolume: invalidPvSourceUpdateDeep,
  621. },
  622. "csi-expansion-enabled-with-pv-secret": {
  623. csiExpansionEnabled: true,
  624. isExpectedFailure: false,
  625. oldVolume: validCSIVolume,
  626. newVolume: getCSIVolumeWithSecret(validCSIVolume, expandSecretRef),
  627. },
  628. "csi-expansion-enabled-with-old-pv-secret": {
  629. csiExpansionEnabled: true,
  630. isExpectedFailure: true,
  631. oldVolume: getCSIVolumeWithSecret(validCSIVolume, expandSecretRef),
  632. newVolume: getCSIVolumeWithSecret(validCSIVolume, &core.SecretReference{
  633. Name: "foo-secret",
  634. Namespace: "default",
  635. }),
  636. },
  637. }
  638. for name, scenario := range scenarios {
  639. errs := ValidatePersistentVolumeUpdate(scenario.newVolume, scenario.oldVolume)
  640. if len(errs) == 0 && scenario.isExpectedFailure {
  641. t.Errorf("Unexpected success for scenario: %s", name)
  642. }
  643. if len(errs) > 0 && !scenario.isExpectedFailure {
  644. t.Errorf("Unexpected failure for scenario: %s - %+v", name, errs)
  645. }
  646. }
  647. }
  648. func getCSIVolumeWithSecret(pv *core.PersistentVolume, secret *core.SecretReference) *core.PersistentVolume {
  649. pvCopy := pv.DeepCopy()
  650. if secret != nil {
  651. pvCopy.Spec.CSI.ControllerExpandSecretRef = secret
  652. }
  653. return pvCopy
  654. }
  655. func testLocalVolume(path string, affinity *core.VolumeNodeAffinity) core.PersistentVolumeSpec {
  656. return core.PersistentVolumeSpec{
  657. Capacity: core.ResourceList{
  658. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  659. },
  660. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  661. PersistentVolumeSource: core.PersistentVolumeSource{
  662. Local: &core.LocalVolumeSource{
  663. Path: path,
  664. },
  665. },
  666. NodeAffinity: affinity,
  667. StorageClassName: "test-storage-class",
  668. }
  669. }
  670. func TestValidateLocalVolumes(t *testing.T) {
  671. scenarios := map[string]struct {
  672. isExpectedFailure bool
  673. volume *core.PersistentVolume
  674. }{
  675. "alpha invalid local volume nil annotations": {
  676. isExpectedFailure: true,
  677. volume: testVolume(
  678. "invalid-local-volume-nil-annotations",
  679. "",
  680. testLocalVolume("/foo", nil)),
  681. },
  682. "valid local volume": {
  683. isExpectedFailure: false,
  684. volume: testVolume("valid-local-volume", "",
  685. testLocalVolume("/foo", simpleVolumeNodeAffinity("foo", "bar"))),
  686. },
  687. "invalid local volume no node affinity": {
  688. isExpectedFailure: true,
  689. volume: testVolume("invalid-local-volume-no-node-affinity", "",
  690. testLocalVolume("/foo", nil)),
  691. },
  692. "invalid local volume empty path": {
  693. isExpectedFailure: true,
  694. volume: testVolume("invalid-local-volume-empty-path", "",
  695. testLocalVolume("", simpleVolumeNodeAffinity("foo", "bar"))),
  696. },
  697. "invalid-local-volume-backsteps": {
  698. isExpectedFailure: true,
  699. volume: testVolume("foo", "",
  700. testLocalVolume("/foo/..", simpleVolumeNodeAffinity("foo", "bar"))),
  701. },
  702. "valid-local-volume-relative-path": {
  703. isExpectedFailure: false,
  704. volume: testVolume("foo", "",
  705. testLocalVolume("foo", simpleVolumeNodeAffinity("foo", "bar"))),
  706. },
  707. }
  708. for name, scenario := range scenarios {
  709. errs := ValidatePersistentVolume(scenario.volume)
  710. if len(errs) == 0 && scenario.isExpectedFailure {
  711. t.Errorf("Unexpected success for scenario: %s", name)
  712. }
  713. if len(errs) > 0 && !scenario.isExpectedFailure {
  714. t.Errorf("Unexpected failure for scenario: %s - %+v", name, errs)
  715. }
  716. }
  717. }
  718. func testVolumeWithNodeAffinity(affinity *core.VolumeNodeAffinity) *core.PersistentVolume {
  719. return testVolume("test-affinity-volume", "",
  720. core.PersistentVolumeSpec{
  721. Capacity: core.ResourceList{
  722. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  723. },
  724. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  725. PersistentVolumeSource: core.PersistentVolumeSource{
  726. GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{
  727. PDName: "foo",
  728. },
  729. },
  730. StorageClassName: "test-storage-class",
  731. NodeAffinity: affinity,
  732. })
  733. }
  734. func simpleVolumeNodeAffinity(key, value string) *core.VolumeNodeAffinity {
  735. return &core.VolumeNodeAffinity{
  736. Required: &core.NodeSelector{
  737. NodeSelectorTerms: []core.NodeSelectorTerm{
  738. {
  739. MatchExpressions: []core.NodeSelectorRequirement{
  740. {
  741. Key: key,
  742. Operator: core.NodeSelectorOpIn,
  743. Values: []string{value},
  744. },
  745. },
  746. },
  747. },
  748. },
  749. }
  750. }
  751. func TestValidateVolumeNodeAffinityUpdate(t *testing.T) {
  752. scenarios := map[string]struct {
  753. isExpectedFailure bool
  754. oldPV *core.PersistentVolume
  755. newPV *core.PersistentVolume
  756. }{
  757. "nil-nothing-changed": {
  758. isExpectedFailure: false,
  759. oldPV: testVolumeWithNodeAffinity(nil),
  760. newPV: testVolumeWithNodeAffinity(nil),
  761. },
  762. "affinity-nothing-changed": {
  763. isExpectedFailure: false,
  764. oldPV: testVolumeWithNodeAffinity(simpleVolumeNodeAffinity("foo", "bar")),
  765. newPV: testVolumeWithNodeAffinity(simpleVolumeNodeAffinity("foo", "bar")),
  766. },
  767. "affinity-changed": {
  768. isExpectedFailure: true,
  769. oldPV: testVolumeWithNodeAffinity(simpleVolumeNodeAffinity("foo", "bar")),
  770. newPV: testVolumeWithNodeAffinity(simpleVolumeNodeAffinity("foo", "bar2")),
  771. },
  772. "nil-to-obj": {
  773. isExpectedFailure: false,
  774. oldPV: testVolumeWithNodeAffinity(nil),
  775. newPV: testVolumeWithNodeAffinity(simpleVolumeNodeAffinity("foo", "bar")),
  776. },
  777. "obj-to-nil": {
  778. isExpectedFailure: true,
  779. oldPV: testVolumeWithNodeAffinity(simpleVolumeNodeAffinity("foo", "bar")),
  780. newPV: testVolumeWithNodeAffinity(nil),
  781. },
  782. }
  783. for name, scenario := range scenarios {
  784. errs := ValidatePersistentVolumeUpdate(scenario.newPV, scenario.oldPV)
  785. if len(errs) == 0 && scenario.isExpectedFailure {
  786. t.Errorf("Unexpected success for scenario: %s", name)
  787. }
  788. if len(errs) > 0 && !scenario.isExpectedFailure {
  789. t.Errorf("Unexpected failure for scenario: %s - %+v", name, errs)
  790. }
  791. }
  792. }
  793. func testVolumeClaim(name string, namespace string, spec core.PersistentVolumeClaimSpec) *core.PersistentVolumeClaim {
  794. return &core.PersistentVolumeClaim{
  795. ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace},
  796. Spec: spec,
  797. }
  798. }
  799. func testVolumeClaimWithStatus(
  800. name, namespace string,
  801. spec core.PersistentVolumeClaimSpec,
  802. status core.PersistentVolumeClaimStatus) *core.PersistentVolumeClaim {
  803. return &core.PersistentVolumeClaim{
  804. ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace},
  805. Spec: spec,
  806. Status: status,
  807. }
  808. }
  809. func testVolumeClaimStorageClass(name string, namespace string, annval string, spec core.PersistentVolumeClaimSpec) *core.PersistentVolumeClaim {
  810. annotations := map[string]string{
  811. v1.BetaStorageClassAnnotation: annval,
  812. }
  813. return &core.PersistentVolumeClaim{
  814. ObjectMeta: metav1.ObjectMeta{
  815. Name: name,
  816. Namespace: namespace,
  817. Annotations: annotations,
  818. },
  819. Spec: spec,
  820. }
  821. }
  822. func testVolumeClaimAnnotation(name string, namespace string, ann string, annval string, spec core.PersistentVolumeClaimSpec) *core.PersistentVolumeClaim {
  823. annotations := map[string]string{
  824. ann: annval,
  825. }
  826. return &core.PersistentVolumeClaim{
  827. ObjectMeta: metav1.ObjectMeta{
  828. Name: name,
  829. Namespace: namespace,
  830. Annotations: annotations,
  831. },
  832. Spec: spec,
  833. }
  834. }
  835. func testVolumeClaimStorageClassInSpec(name, namespace, scName string, spec core.PersistentVolumeClaimSpec) *core.PersistentVolumeClaim {
  836. spec.StorageClassName = &scName
  837. return &core.PersistentVolumeClaim{
  838. ObjectMeta: metav1.ObjectMeta{
  839. Name: name,
  840. Namespace: namespace,
  841. },
  842. Spec: spec,
  843. }
  844. }
  845. func testVolumeSnapshotDataSourceInSpec(name string, kind string, apiGroup string) *core.PersistentVolumeClaimSpec {
  846. scName := "csi-plugin"
  847. dataSourceInSpec := core.PersistentVolumeClaimSpec{
  848. AccessModes: []core.PersistentVolumeAccessMode{
  849. core.ReadOnlyMany,
  850. },
  851. Resources: core.ResourceRequirements{
  852. Requests: core.ResourceList{
  853. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  854. },
  855. },
  856. StorageClassName: &scName,
  857. DataSource: &core.TypedLocalObjectReference{
  858. APIGroup: &apiGroup,
  859. Kind: kind,
  860. Name: name,
  861. },
  862. }
  863. return &dataSourceInSpec
  864. }
  865. func TestAlphaVolumeSnapshotDataSource(t *testing.T) {
  866. successTestCases := []core.PersistentVolumeClaimSpec{
  867. *testVolumeSnapshotDataSourceInSpec("test_snapshot", "VolumeSnapshot", "snapshot.storage.k8s.io"),
  868. }
  869. failedTestCases := []core.PersistentVolumeClaimSpec{
  870. *testVolumeSnapshotDataSourceInSpec("", "VolumeSnapshot", "snapshot.storage.k8s.io"),
  871. *testVolumeSnapshotDataSourceInSpec("test_snapshot", "PersistentVolumeClaim", "snapshot.storage.k8s.io"),
  872. *testVolumeSnapshotDataSourceInSpec("test_snapshot", "VolumeSnapshot", "storage.k8s.io"),
  873. }
  874. for _, tc := range successTestCases {
  875. if errs := ValidatePersistentVolumeClaimSpec(&tc, field.NewPath("spec")); len(errs) != 0 {
  876. t.Errorf("expected success: %v", errs)
  877. }
  878. }
  879. for _, tc := range failedTestCases {
  880. if errs := ValidatePersistentVolumeClaimSpec(&tc, field.NewPath("spec")); len(errs) == 0 {
  881. t.Errorf("expected failure: %v", errs)
  882. }
  883. }
  884. }
  885. func testVolumeClaimStorageClassInAnnotationAndSpec(name, namespace, scNameInAnn, scName string, spec core.PersistentVolumeClaimSpec) *core.PersistentVolumeClaim {
  886. spec.StorageClassName = &scName
  887. return &core.PersistentVolumeClaim{
  888. ObjectMeta: metav1.ObjectMeta{
  889. Name: name,
  890. Namespace: namespace,
  891. Annotations: map[string]string{v1.BetaStorageClassAnnotation: scNameInAnn},
  892. },
  893. Spec: spec,
  894. }
  895. }
  896. func TestValidatePersistentVolumeClaim(t *testing.T) {
  897. invalidClassName := "-invalid-"
  898. validClassName := "valid"
  899. invalidMode := core.PersistentVolumeMode("fakeVolumeMode")
  900. validMode := core.PersistentVolumeFilesystem
  901. scenarios := map[string]struct {
  902. isExpectedFailure bool
  903. claim *core.PersistentVolumeClaim
  904. }{
  905. "good-claim": {
  906. isExpectedFailure: false,
  907. claim: testVolumeClaim("foo", "ns", core.PersistentVolumeClaimSpec{
  908. Selector: &metav1.LabelSelector{
  909. MatchExpressions: []metav1.LabelSelectorRequirement{
  910. {
  911. Key: "key2",
  912. Operator: "Exists",
  913. },
  914. },
  915. },
  916. AccessModes: []core.PersistentVolumeAccessMode{
  917. core.ReadWriteOnce,
  918. core.ReadOnlyMany,
  919. },
  920. Resources: core.ResourceRequirements{
  921. Requests: core.ResourceList{
  922. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  923. },
  924. },
  925. StorageClassName: &validClassName,
  926. VolumeMode: &validMode,
  927. }),
  928. },
  929. "invalid-claim-zero-capacity": {
  930. isExpectedFailure: true,
  931. claim: testVolumeClaim("foo", "ns", core.PersistentVolumeClaimSpec{
  932. Selector: &metav1.LabelSelector{
  933. MatchExpressions: []metav1.LabelSelectorRequirement{
  934. {
  935. Key: "key2",
  936. Operator: "Exists",
  937. },
  938. },
  939. },
  940. AccessModes: []core.PersistentVolumeAccessMode{
  941. core.ReadWriteOnce,
  942. core.ReadOnlyMany,
  943. },
  944. Resources: core.ResourceRequirements{
  945. Requests: core.ResourceList{
  946. core.ResourceName(core.ResourceStorage): resource.MustParse("0G"),
  947. },
  948. },
  949. StorageClassName: &validClassName,
  950. }),
  951. },
  952. "invalid-label-selector": {
  953. isExpectedFailure: true,
  954. claim: testVolumeClaim("foo", "ns", core.PersistentVolumeClaimSpec{
  955. Selector: &metav1.LabelSelector{
  956. MatchExpressions: []metav1.LabelSelectorRequirement{
  957. {
  958. Key: "key2",
  959. Operator: "InvalidOp",
  960. Values: []string{"value1", "value2"},
  961. },
  962. },
  963. },
  964. AccessModes: []core.PersistentVolumeAccessMode{
  965. core.ReadWriteOnce,
  966. core.ReadOnlyMany,
  967. },
  968. Resources: core.ResourceRequirements{
  969. Requests: core.ResourceList{
  970. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  971. },
  972. },
  973. }),
  974. },
  975. "invalid-accessmode": {
  976. isExpectedFailure: true,
  977. claim: testVolumeClaim("foo", "ns", core.PersistentVolumeClaimSpec{
  978. AccessModes: []core.PersistentVolumeAccessMode{"fakemode"},
  979. Resources: core.ResourceRequirements{
  980. Requests: core.ResourceList{
  981. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  982. },
  983. },
  984. }),
  985. },
  986. "missing-namespace": {
  987. isExpectedFailure: true,
  988. claim: testVolumeClaim("foo", "", core.PersistentVolumeClaimSpec{
  989. AccessModes: []core.PersistentVolumeAccessMode{
  990. core.ReadWriteOnce,
  991. core.ReadOnlyMany,
  992. },
  993. Resources: core.ResourceRequirements{
  994. Requests: core.ResourceList{
  995. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  996. },
  997. },
  998. }),
  999. },
  1000. "no-access-modes": {
  1001. isExpectedFailure: true,
  1002. claim: testVolumeClaim("foo", "ns", core.PersistentVolumeClaimSpec{
  1003. Resources: core.ResourceRequirements{
  1004. Requests: core.ResourceList{
  1005. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  1006. },
  1007. },
  1008. }),
  1009. },
  1010. "no-resource-requests": {
  1011. isExpectedFailure: true,
  1012. claim: testVolumeClaim("foo", "ns", core.PersistentVolumeClaimSpec{
  1013. AccessModes: []core.PersistentVolumeAccessMode{
  1014. core.ReadWriteOnce,
  1015. },
  1016. }),
  1017. },
  1018. "invalid-resource-requests": {
  1019. isExpectedFailure: true,
  1020. claim: testVolumeClaim("foo", "ns", core.PersistentVolumeClaimSpec{
  1021. AccessModes: []core.PersistentVolumeAccessMode{
  1022. core.ReadWriteOnce,
  1023. },
  1024. Resources: core.ResourceRequirements{
  1025. Requests: core.ResourceList{
  1026. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  1027. },
  1028. },
  1029. }),
  1030. },
  1031. "negative-storage-request": {
  1032. isExpectedFailure: true,
  1033. claim: testVolumeClaim("foo", "ns", core.PersistentVolumeClaimSpec{
  1034. Selector: &metav1.LabelSelector{
  1035. MatchExpressions: []metav1.LabelSelectorRequirement{
  1036. {
  1037. Key: "key2",
  1038. Operator: "Exists",
  1039. },
  1040. },
  1041. },
  1042. AccessModes: []core.PersistentVolumeAccessMode{
  1043. core.ReadWriteOnce,
  1044. core.ReadOnlyMany,
  1045. },
  1046. Resources: core.ResourceRequirements{
  1047. Requests: core.ResourceList{
  1048. core.ResourceName(core.ResourceStorage): resource.MustParse("-10G"),
  1049. },
  1050. },
  1051. }),
  1052. },
  1053. "zero-storage-request": {
  1054. isExpectedFailure: true,
  1055. claim: testVolumeClaim("foo", "ns", core.PersistentVolumeClaimSpec{
  1056. Selector: &metav1.LabelSelector{
  1057. MatchExpressions: []metav1.LabelSelectorRequirement{
  1058. {
  1059. Key: "key2",
  1060. Operator: "Exists",
  1061. },
  1062. },
  1063. },
  1064. AccessModes: []core.PersistentVolumeAccessMode{
  1065. core.ReadWriteOnce,
  1066. core.ReadOnlyMany,
  1067. },
  1068. Resources: core.ResourceRequirements{
  1069. Requests: core.ResourceList{
  1070. core.ResourceName(core.ResourceStorage): resource.MustParse("0G"),
  1071. },
  1072. },
  1073. }),
  1074. },
  1075. "invalid-storage-class-name": {
  1076. isExpectedFailure: true,
  1077. claim: testVolumeClaim("foo", "ns", core.PersistentVolumeClaimSpec{
  1078. Selector: &metav1.LabelSelector{
  1079. MatchExpressions: []metav1.LabelSelectorRequirement{
  1080. {
  1081. Key: "key2",
  1082. Operator: "Exists",
  1083. },
  1084. },
  1085. },
  1086. AccessModes: []core.PersistentVolumeAccessMode{
  1087. core.ReadWriteOnce,
  1088. core.ReadOnlyMany,
  1089. },
  1090. Resources: core.ResourceRequirements{
  1091. Requests: core.ResourceList{
  1092. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  1093. },
  1094. },
  1095. StorageClassName: &invalidClassName,
  1096. }),
  1097. },
  1098. "invalid-volume-mode": {
  1099. isExpectedFailure: true,
  1100. claim: testVolumeClaim("foo", "ns", core.PersistentVolumeClaimSpec{
  1101. AccessModes: []core.PersistentVolumeAccessMode{
  1102. core.ReadWriteOnce,
  1103. core.ReadOnlyMany,
  1104. },
  1105. Resources: core.ResourceRequirements{
  1106. Requests: core.ResourceList{
  1107. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  1108. },
  1109. },
  1110. VolumeMode: &invalidMode,
  1111. }),
  1112. },
  1113. }
  1114. for name, scenario := range scenarios {
  1115. t.Run(name, func(t *testing.T) {
  1116. errs := ValidatePersistentVolumeClaim(scenario.claim)
  1117. if len(errs) == 0 && scenario.isExpectedFailure {
  1118. t.Errorf("Unexpected success for scenario: %s", name)
  1119. }
  1120. if len(errs) > 0 && !scenario.isExpectedFailure {
  1121. t.Errorf("Unexpected failure for scenario: %s - %+v", name, errs)
  1122. }
  1123. })
  1124. }
  1125. }
  1126. func TestAlphaPVVolumeModeUpdate(t *testing.T) {
  1127. block := core.PersistentVolumeBlock
  1128. file := core.PersistentVolumeFilesystem
  1129. scenarios := map[string]struct {
  1130. isExpectedFailure bool
  1131. oldPV *core.PersistentVolume
  1132. newPV *core.PersistentVolume
  1133. }{
  1134. "valid-update-volume-mode-block-to-block": {
  1135. isExpectedFailure: false,
  1136. oldPV: createTestVolModePV(&block),
  1137. newPV: createTestVolModePV(&block),
  1138. },
  1139. "valid-update-volume-mode-file-to-file": {
  1140. isExpectedFailure: false,
  1141. oldPV: createTestVolModePV(&file),
  1142. newPV: createTestVolModePV(&file),
  1143. },
  1144. "invalid-update-volume-mode-to-block": {
  1145. isExpectedFailure: true,
  1146. oldPV: createTestVolModePV(&file),
  1147. newPV: createTestVolModePV(&block),
  1148. },
  1149. "invalid-update-volume-mode-to-file": {
  1150. isExpectedFailure: true,
  1151. oldPV: createTestVolModePV(&block),
  1152. newPV: createTestVolModePV(&file),
  1153. },
  1154. "invalid-update-volume-mode-nil-to-file": {
  1155. isExpectedFailure: true,
  1156. oldPV: createTestVolModePV(nil),
  1157. newPV: createTestVolModePV(&file),
  1158. },
  1159. "invalid-update-volume-mode-nil-to-block": {
  1160. isExpectedFailure: true,
  1161. oldPV: createTestVolModePV(nil),
  1162. newPV: createTestVolModePV(&block),
  1163. },
  1164. "invalid-update-volume-mode-file-to-nil": {
  1165. isExpectedFailure: true,
  1166. oldPV: createTestVolModePV(&file),
  1167. newPV: createTestVolModePV(nil),
  1168. },
  1169. "invalid-update-volume-mode-block-to-nil": {
  1170. isExpectedFailure: true,
  1171. oldPV: createTestVolModePV(&block),
  1172. newPV: createTestVolModePV(nil),
  1173. },
  1174. "invalid-update-volume-mode-nil-to-nil": {
  1175. isExpectedFailure: false,
  1176. oldPV: createTestVolModePV(nil),
  1177. newPV: createTestVolModePV(nil),
  1178. },
  1179. "invalid-update-volume-mode-empty-to-mode": {
  1180. isExpectedFailure: true,
  1181. oldPV: createTestPV(),
  1182. newPV: createTestVolModePV(&block),
  1183. },
  1184. }
  1185. for name, scenario := range scenarios {
  1186. t.Run(name, func(t *testing.T) {
  1187. // ensure we have a resource version specified for updates
  1188. errs := ValidatePersistentVolumeUpdate(scenario.newPV, scenario.oldPV)
  1189. if len(errs) == 0 && scenario.isExpectedFailure {
  1190. t.Errorf("Unexpected success for scenario: %s", name)
  1191. }
  1192. if len(errs) > 0 && !scenario.isExpectedFailure {
  1193. t.Errorf("Unexpected failure for scenario: %s - %+v", name, errs)
  1194. }
  1195. })
  1196. }
  1197. }
  1198. func TestValidatePersistentVolumeClaimUpdate(t *testing.T) {
  1199. block := core.PersistentVolumeBlock
  1200. file := core.PersistentVolumeFilesystem
  1201. validClaim := testVolumeClaimWithStatus("foo", "ns", core.PersistentVolumeClaimSpec{
  1202. AccessModes: []core.PersistentVolumeAccessMode{
  1203. core.ReadWriteOnce,
  1204. core.ReadOnlyMany,
  1205. },
  1206. Resources: core.ResourceRequirements{
  1207. Requests: core.ResourceList{
  1208. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  1209. },
  1210. },
  1211. }, core.PersistentVolumeClaimStatus{
  1212. Phase: core.ClaimBound,
  1213. })
  1214. validClaimStorageClass := testVolumeClaimStorageClass("foo", "ns", "fast", core.PersistentVolumeClaimSpec{
  1215. AccessModes: []core.PersistentVolumeAccessMode{
  1216. core.ReadOnlyMany,
  1217. },
  1218. Resources: core.ResourceRequirements{
  1219. Requests: core.ResourceList{
  1220. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  1221. },
  1222. },
  1223. })
  1224. validClaimAnnotation := testVolumeClaimAnnotation("foo", "ns", "description", "foo-description", core.PersistentVolumeClaimSpec{
  1225. AccessModes: []core.PersistentVolumeAccessMode{
  1226. core.ReadOnlyMany,
  1227. },
  1228. Resources: core.ResourceRequirements{
  1229. Requests: core.ResourceList{
  1230. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  1231. },
  1232. },
  1233. })
  1234. validUpdateClaim := testVolumeClaim("foo", "ns", core.PersistentVolumeClaimSpec{
  1235. AccessModes: []core.PersistentVolumeAccessMode{
  1236. core.ReadWriteOnce,
  1237. core.ReadOnlyMany,
  1238. },
  1239. Resources: core.ResourceRequirements{
  1240. Requests: core.ResourceList{
  1241. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  1242. },
  1243. },
  1244. VolumeName: "volume",
  1245. })
  1246. invalidUpdateClaimResources := testVolumeClaim("foo", "ns", core.PersistentVolumeClaimSpec{
  1247. AccessModes: []core.PersistentVolumeAccessMode{
  1248. core.ReadWriteOnce,
  1249. core.ReadOnlyMany,
  1250. },
  1251. Resources: core.ResourceRequirements{
  1252. Requests: core.ResourceList{
  1253. core.ResourceName(core.ResourceStorage): resource.MustParse("20G"),
  1254. },
  1255. },
  1256. VolumeName: "volume",
  1257. })
  1258. invalidUpdateClaimAccessModes := testVolumeClaim("foo", "ns", core.PersistentVolumeClaimSpec{
  1259. AccessModes: []core.PersistentVolumeAccessMode{
  1260. core.ReadWriteOnce,
  1261. },
  1262. Resources: core.ResourceRequirements{
  1263. Requests: core.ResourceList{
  1264. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  1265. },
  1266. },
  1267. VolumeName: "volume",
  1268. })
  1269. validClaimVolumeModeFile := testVolumeClaim("foo", "ns", core.PersistentVolumeClaimSpec{
  1270. AccessModes: []core.PersistentVolumeAccessMode{
  1271. core.ReadWriteOnce,
  1272. },
  1273. VolumeMode: &file,
  1274. Resources: core.ResourceRequirements{
  1275. Requests: core.ResourceList{
  1276. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  1277. },
  1278. },
  1279. VolumeName: "volume",
  1280. })
  1281. validClaimVolumeModeBlock := testVolumeClaim("foo", "ns", core.PersistentVolumeClaimSpec{
  1282. AccessModes: []core.PersistentVolumeAccessMode{
  1283. core.ReadWriteOnce,
  1284. },
  1285. VolumeMode: &block,
  1286. Resources: core.ResourceRequirements{
  1287. Requests: core.ResourceList{
  1288. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  1289. },
  1290. },
  1291. VolumeName: "volume",
  1292. })
  1293. invalidClaimVolumeModeNil := testVolumeClaim("foo", "ns", core.PersistentVolumeClaimSpec{
  1294. AccessModes: []core.PersistentVolumeAccessMode{
  1295. core.ReadWriteOnce,
  1296. },
  1297. VolumeMode: nil,
  1298. Resources: core.ResourceRequirements{
  1299. Requests: core.ResourceList{
  1300. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  1301. },
  1302. },
  1303. VolumeName: "volume",
  1304. })
  1305. invalidUpdateClaimStorageClass := testVolumeClaimStorageClass("foo", "ns", "fast2", core.PersistentVolumeClaimSpec{
  1306. AccessModes: []core.PersistentVolumeAccessMode{
  1307. core.ReadOnlyMany,
  1308. },
  1309. Resources: core.ResourceRequirements{
  1310. Requests: core.ResourceList{
  1311. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  1312. },
  1313. },
  1314. VolumeName: "volume",
  1315. })
  1316. validUpdateClaimMutableAnnotation := testVolumeClaimAnnotation("foo", "ns", "description", "updated-or-added-foo-description", core.PersistentVolumeClaimSpec{
  1317. AccessModes: []core.PersistentVolumeAccessMode{
  1318. core.ReadOnlyMany,
  1319. },
  1320. Resources: core.ResourceRequirements{
  1321. Requests: core.ResourceList{
  1322. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  1323. },
  1324. },
  1325. VolumeName: "volume",
  1326. })
  1327. validAddClaimAnnotation := testVolumeClaimAnnotation("foo", "ns", "description", "updated-or-added-foo-description", core.PersistentVolumeClaimSpec{
  1328. AccessModes: []core.PersistentVolumeAccessMode{
  1329. core.ReadWriteOnce,
  1330. core.ReadOnlyMany,
  1331. },
  1332. Resources: core.ResourceRequirements{
  1333. Requests: core.ResourceList{
  1334. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  1335. },
  1336. },
  1337. VolumeName: "volume",
  1338. })
  1339. validSizeUpdate := testVolumeClaimWithStatus("foo", "ns", core.PersistentVolumeClaimSpec{
  1340. AccessModes: []core.PersistentVolumeAccessMode{
  1341. core.ReadWriteOnce,
  1342. core.ReadOnlyMany,
  1343. },
  1344. Resources: core.ResourceRequirements{
  1345. Requests: core.ResourceList{
  1346. core.ResourceName(core.ResourceStorage): resource.MustParse("15G"),
  1347. },
  1348. },
  1349. }, core.PersistentVolumeClaimStatus{
  1350. Phase: core.ClaimBound,
  1351. })
  1352. invalidSizeUpdate := testVolumeClaimWithStatus("foo", "ns", core.PersistentVolumeClaimSpec{
  1353. AccessModes: []core.PersistentVolumeAccessMode{
  1354. core.ReadWriteOnce,
  1355. core.ReadOnlyMany,
  1356. },
  1357. Resources: core.ResourceRequirements{
  1358. Requests: core.ResourceList{
  1359. core.ResourceName(core.ResourceStorage): resource.MustParse("5G"),
  1360. },
  1361. },
  1362. }, core.PersistentVolumeClaimStatus{
  1363. Phase: core.ClaimBound,
  1364. })
  1365. unboundSizeUpdate := testVolumeClaimWithStatus("foo", "ns", core.PersistentVolumeClaimSpec{
  1366. AccessModes: []core.PersistentVolumeAccessMode{
  1367. core.ReadWriteOnce,
  1368. core.ReadOnlyMany,
  1369. },
  1370. Resources: core.ResourceRequirements{
  1371. Requests: core.ResourceList{
  1372. core.ResourceName(core.ResourceStorage): resource.MustParse("12G"),
  1373. },
  1374. },
  1375. }, core.PersistentVolumeClaimStatus{
  1376. Phase: core.ClaimPending,
  1377. })
  1378. validClaimStorageClassInSpec := testVolumeClaimStorageClassInSpec("foo", "ns", "fast", core.PersistentVolumeClaimSpec{
  1379. AccessModes: []core.PersistentVolumeAccessMode{
  1380. core.ReadOnlyMany,
  1381. },
  1382. Resources: core.ResourceRequirements{
  1383. Requests: core.ResourceList{
  1384. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  1385. },
  1386. },
  1387. })
  1388. invalidClaimStorageClassInSpec := testVolumeClaimStorageClassInSpec("foo", "ns", "fast2", core.PersistentVolumeClaimSpec{
  1389. AccessModes: []core.PersistentVolumeAccessMode{
  1390. core.ReadOnlyMany,
  1391. },
  1392. Resources: core.ResourceRequirements{
  1393. Requests: core.ResourceList{
  1394. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  1395. },
  1396. },
  1397. })
  1398. validClaimStorageClassInAnnotationAndSpec := testVolumeClaimStorageClassInAnnotationAndSpec(
  1399. "foo", "ns", "fast", "fast", core.PersistentVolumeClaimSpec{
  1400. AccessModes: []core.PersistentVolumeAccessMode{
  1401. core.ReadOnlyMany,
  1402. },
  1403. Resources: core.ResourceRequirements{
  1404. Requests: core.ResourceList{
  1405. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  1406. },
  1407. },
  1408. })
  1409. invalidClaimStorageClassInAnnotationAndSpec := testVolumeClaimStorageClassInAnnotationAndSpec(
  1410. "foo", "ns", "fast2", "fast", core.PersistentVolumeClaimSpec{
  1411. AccessModes: []core.PersistentVolumeAccessMode{
  1412. core.ReadOnlyMany,
  1413. },
  1414. Resources: core.ResourceRequirements{
  1415. Requests: core.ResourceList{
  1416. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  1417. },
  1418. },
  1419. })
  1420. scenarios := map[string]struct {
  1421. isExpectedFailure bool
  1422. oldClaim *core.PersistentVolumeClaim
  1423. newClaim *core.PersistentVolumeClaim
  1424. enableResize bool
  1425. enableBlock bool
  1426. }{
  1427. "valid-update-volumeName-only": {
  1428. isExpectedFailure: false,
  1429. oldClaim: validClaim,
  1430. newClaim: validUpdateClaim,
  1431. enableResize: false,
  1432. enableBlock: false,
  1433. },
  1434. "valid-no-op-update": {
  1435. isExpectedFailure: false,
  1436. oldClaim: validUpdateClaim,
  1437. newClaim: validUpdateClaim,
  1438. enableResize: false,
  1439. enableBlock: false,
  1440. },
  1441. "invalid-update-change-resources-on-bound-claim": {
  1442. isExpectedFailure: true,
  1443. oldClaim: validUpdateClaim,
  1444. newClaim: invalidUpdateClaimResources,
  1445. enableResize: false,
  1446. enableBlock: false,
  1447. },
  1448. "invalid-update-change-access-modes-on-bound-claim": {
  1449. isExpectedFailure: true,
  1450. oldClaim: validUpdateClaim,
  1451. newClaim: invalidUpdateClaimAccessModes,
  1452. enableResize: false,
  1453. enableBlock: false,
  1454. },
  1455. "valid-update-volume-mode-block-to-block": {
  1456. isExpectedFailure: false,
  1457. oldClaim: validClaimVolumeModeBlock,
  1458. newClaim: validClaimVolumeModeBlock,
  1459. enableResize: false,
  1460. enableBlock: true,
  1461. },
  1462. "valid-update-volume-mode-file-to-file": {
  1463. isExpectedFailure: false,
  1464. oldClaim: validClaimVolumeModeFile,
  1465. newClaim: validClaimVolumeModeFile,
  1466. enableResize: false,
  1467. enableBlock: true,
  1468. },
  1469. "invalid-update-volume-mode-to-block": {
  1470. isExpectedFailure: true,
  1471. oldClaim: validClaimVolumeModeFile,
  1472. newClaim: validClaimVolumeModeBlock,
  1473. enableResize: false,
  1474. enableBlock: true,
  1475. },
  1476. "invalid-update-volume-mode-to-file": {
  1477. isExpectedFailure: true,
  1478. oldClaim: validClaimVolumeModeBlock,
  1479. newClaim: validClaimVolumeModeFile,
  1480. enableResize: false,
  1481. enableBlock: true,
  1482. },
  1483. "invalid-update-volume-mode-nil-to-file": {
  1484. isExpectedFailure: true,
  1485. oldClaim: invalidClaimVolumeModeNil,
  1486. newClaim: validClaimVolumeModeFile,
  1487. enableResize: false,
  1488. enableBlock: true,
  1489. },
  1490. "invalid-update-volume-mode-nil-to-block": {
  1491. isExpectedFailure: true,
  1492. oldClaim: invalidClaimVolumeModeNil,
  1493. newClaim: validClaimVolumeModeBlock,
  1494. enableResize: false,
  1495. enableBlock: true,
  1496. },
  1497. "invalid-update-volume-mode-block-to-nil": {
  1498. isExpectedFailure: true,
  1499. oldClaim: validClaimVolumeModeBlock,
  1500. newClaim: invalidClaimVolumeModeNil,
  1501. enableResize: false,
  1502. enableBlock: true,
  1503. },
  1504. "invalid-update-volume-mode-file-to-nil": {
  1505. isExpectedFailure: true,
  1506. oldClaim: validClaimVolumeModeFile,
  1507. newClaim: invalidClaimVolumeModeNil,
  1508. enableResize: false,
  1509. enableBlock: true,
  1510. },
  1511. "invalid-update-volume-mode-empty-to-mode": {
  1512. isExpectedFailure: true,
  1513. oldClaim: validClaim,
  1514. newClaim: validClaimVolumeModeBlock,
  1515. enableResize: false,
  1516. enableBlock: true,
  1517. },
  1518. "invalid-update-volume-mode-mode-to-empty": {
  1519. isExpectedFailure: true,
  1520. oldClaim: validClaimVolumeModeBlock,
  1521. newClaim: validClaim,
  1522. enableResize: false,
  1523. enableBlock: true,
  1524. },
  1525. // with the new validation changes this test should not fail
  1526. "noop-update-volumemode-allowed": {
  1527. isExpectedFailure: false,
  1528. oldClaim: validClaimVolumeModeFile,
  1529. newClaim: validClaimVolumeModeFile,
  1530. enableResize: false,
  1531. enableBlock: false,
  1532. },
  1533. "invalid-update-change-storage-class-annotation-after-creation": {
  1534. isExpectedFailure: true,
  1535. oldClaim: validClaimStorageClass,
  1536. newClaim: invalidUpdateClaimStorageClass,
  1537. enableResize: false,
  1538. enableBlock: false,
  1539. },
  1540. "valid-update-mutable-annotation": {
  1541. isExpectedFailure: false,
  1542. oldClaim: validClaimAnnotation,
  1543. newClaim: validUpdateClaimMutableAnnotation,
  1544. enableResize: false,
  1545. enableBlock: false,
  1546. },
  1547. "valid-update-add-annotation": {
  1548. isExpectedFailure: false,
  1549. oldClaim: validClaim,
  1550. newClaim: validAddClaimAnnotation,
  1551. enableResize: false,
  1552. enableBlock: false,
  1553. },
  1554. "valid-size-update-resize-disabled": {
  1555. isExpectedFailure: true,
  1556. oldClaim: validClaim,
  1557. newClaim: validSizeUpdate,
  1558. enableResize: false,
  1559. enableBlock: false,
  1560. },
  1561. "valid-size-update-resize-enabled": {
  1562. isExpectedFailure: false,
  1563. oldClaim: validClaim,
  1564. newClaim: validSizeUpdate,
  1565. enableResize: true,
  1566. enableBlock: false,
  1567. },
  1568. "invalid-size-update-resize-enabled": {
  1569. isExpectedFailure: true,
  1570. oldClaim: validClaim,
  1571. newClaim: invalidSizeUpdate,
  1572. enableResize: true,
  1573. enableBlock: false,
  1574. },
  1575. "unbound-size-update-resize-enabled": {
  1576. isExpectedFailure: true,
  1577. oldClaim: validClaim,
  1578. newClaim: unboundSizeUpdate,
  1579. enableResize: true,
  1580. enableBlock: false,
  1581. },
  1582. "valid-upgrade-storage-class-annotation-to-spec": {
  1583. isExpectedFailure: false,
  1584. oldClaim: validClaimStorageClass,
  1585. newClaim: validClaimStorageClassInSpec,
  1586. enableResize: false,
  1587. enableBlock: false,
  1588. },
  1589. "invalid-upgrade-storage-class-annotation-to-spec": {
  1590. isExpectedFailure: true,
  1591. oldClaim: validClaimStorageClass,
  1592. newClaim: invalidClaimStorageClassInSpec,
  1593. enableResize: false,
  1594. enableBlock: false,
  1595. },
  1596. "valid-upgrade-storage-class-annotation-to-annotation-and-spec": {
  1597. isExpectedFailure: false,
  1598. oldClaim: validClaimStorageClass,
  1599. newClaim: validClaimStorageClassInAnnotationAndSpec,
  1600. enableResize: false,
  1601. enableBlock: false,
  1602. },
  1603. "invalid-upgrade-storage-class-annotation-to-annotation-and-spec": {
  1604. isExpectedFailure: true,
  1605. oldClaim: validClaimStorageClass,
  1606. newClaim: invalidClaimStorageClassInAnnotationAndSpec,
  1607. enableResize: false,
  1608. enableBlock: false,
  1609. },
  1610. "invalid-upgrade-storage-class-in-spec": {
  1611. isExpectedFailure: true,
  1612. oldClaim: validClaimStorageClassInSpec,
  1613. newClaim: invalidClaimStorageClassInSpec,
  1614. enableResize: false,
  1615. enableBlock: false,
  1616. },
  1617. "invalid-downgrade-storage-class-spec-to-annotation": {
  1618. isExpectedFailure: true,
  1619. oldClaim: validClaimStorageClassInSpec,
  1620. newClaim: validClaimStorageClass,
  1621. enableResize: false,
  1622. enableBlock: false,
  1623. },
  1624. }
  1625. for name, scenario := range scenarios {
  1626. t.Run(name, func(t *testing.T) {
  1627. // ensure we have a resource version specified for updates
  1628. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ExpandPersistentVolumes, scenario.enableResize)()
  1629. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.BlockVolume, scenario.enableBlock)()
  1630. scenario.oldClaim.ResourceVersion = "1"
  1631. scenario.newClaim.ResourceVersion = "1"
  1632. errs := ValidatePersistentVolumeClaimUpdate(scenario.newClaim, scenario.oldClaim)
  1633. if len(errs) == 0 && scenario.isExpectedFailure {
  1634. t.Errorf("Unexpected success for scenario: %s", name)
  1635. }
  1636. if len(errs) > 0 && !scenario.isExpectedFailure {
  1637. t.Errorf("Unexpected failure for scenario: %s - %+v", name, errs)
  1638. }
  1639. })
  1640. }
  1641. }
  1642. func TestValidateKeyToPath(t *testing.T) {
  1643. testCases := []struct {
  1644. kp core.KeyToPath
  1645. ok bool
  1646. errtype field.ErrorType
  1647. }{
  1648. {
  1649. kp: core.KeyToPath{Key: "k", Path: "p"},
  1650. ok: true,
  1651. },
  1652. {
  1653. kp: core.KeyToPath{Key: "k", Path: "p/p/p/p"},
  1654. ok: true,
  1655. },
  1656. {
  1657. kp: core.KeyToPath{Key: "k", Path: "p/..p/p../p..p"},
  1658. ok: true,
  1659. },
  1660. {
  1661. kp: core.KeyToPath{Key: "k", Path: "p", Mode: utilpointer.Int32Ptr(0644)},
  1662. ok: true,
  1663. },
  1664. {
  1665. kp: core.KeyToPath{Key: "", Path: "p"},
  1666. ok: false,
  1667. errtype: field.ErrorTypeRequired,
  1668. },
  1669. {
  1670. kp: core.KeyToPath{Key: "k", Path: ""},
  1671. ok: false,
  1672. errtype: field.ErrorTypeRequired,
  1673. },
  1674. {
  1675. kp: core.KeyToPath{Key: "k", Path: "..p"},
  1676. ok: false,
  1677. errtype: field.ErrorTypeInvalid,
  1678. },
  1679. {
  1680. kp: core.KeyToPath{Key: "k", Path: "../p"},
  1681. ok: false,
  1682. errtype: field.ErrorTypeInvalid,
  1683. },
  1684. {
  1685. kp: core.KeyToPath{Key: "k", Path: "p/../p"},
  1686. ok: false,
  1687. errtype: field.ErrorTypeInvalid,
  1688. },
  1689. {
  1690. kp: core.KeyToPath{Key: "k", Path: "p/.."},
  1691. ok: false,
  1692. errtype: field.ErrorTypeInvalid,
  1693. },
  1694. {
  1695. kp: core.KeyToPath{Key: "k", Path: "p", Mode: utilpointer.Int32Ptr(01000)},
  1696. ok: false,
  1697. errtype: field.ErrorTypeInvalid,
  1698. },
  1699. {
  1700. kp: core.KeyToPath{Key: "k", Path: "p", Mode: utilpointer.Int32Ptr(-1)},
  1701. ok: false,
  1702. errtype: field.ErrorTypeInvalid,
  1703. },
  1704. }
  1705. for i, tc := range testCases {
  1706. errs := validateKeyToPath(&tc.kp, field.NewPath("field"))
  1707. if tc.ok && len(errs) > 0 {
  1708. t.Errorf("[%d] unexpected errors: %v", i, errs)
  1709. } else if !tc.ok && len(errs) == 0 {
  1710. t.Errorf("[%d] expected error type %v", i, tc.errtype)
  1711. } else if len(errs) > 1 {
  1712. t.Errorf("[%d] expected only one error, got %d", i, len(errs))
  1713. } else if !tc.ok {
  1714. if errs[0].Type != tc.errtype {
  1715. t.Errorf("[%d] expected error type %v, got %v", i, tc.errtype, errs[0].Type)
  1716. }
  1717. }
  1718. }
  1719. }
  1720. func TestValidateNFSVolumeSource(t *testing.T) {
  1721. testCases := []struct {
  1722. name string
  1723. nfs *core.NFSVolumeSource
  1724. errtype field.ErrorType
  1725. errfield string
  1726. errdetail string
  1727. }{
  1728. {
  1729. name: "missing server",
  1730. nfs: &core.NFSVolumeSource{Server: "", Path: "/tmp"},
  1731. errtype: field.ErrorTypeRequired,
  1732. errfield: "server",
  1733. },
  1734. {
  1735. name: "missing path",
  1736. nfs: &core.NFSVolumeSource{Server: "my-server", Path: ""},
  1737. errtype: field.ErrorTypeRequired,
  1738. errfield: "path",
  1739. },
  1740. {
  1741. name: "abs path",
  1742. nfs: &core.NFSVolumeSource{Server: "my-server", Path: "tmp"},
  1743. errtype: field.ErrorTypeInvalid,
  1744. errfield: "path",
  1745. errdetail: "must be an absolute path",
  1746. },
  1747. }
  1748. for i, tc := range testCases {
  1749. errs := validateNFSVolumeSource(tc.nfs, field.NewPath("field"))
  1750. if len(errs) > 0 && tc.errtype == "" {
  1751. t.Errorf("[%d: %q] unexpected error(s): %v", i, tc.name, errs)
  1752. } else if len(errs) == 0 && tc.errtype != "" {
  1753. t.Errorf("[%d: %q] expected error type %v", i, tc.name, tc.errtype)
  1754. } else if len(errs) >= 1 {
  1755. if errs[0].Type != tc.errtype {
  1756. t.Errorf("[%d: %q] expected error type %v, got %v", i, tc.name, tc.errtype, errs[0].Type)
  1757. } else if !strings.HasSuffix(errs[0].Field, "."+tc.errfield) {
  1758. t.Errorf("[%d: %q] expected error on field %q, got %q", i, tc.name, tc.errfield, errs[0].Field)
  1759. } else if !strings.Contains(errs[0].Detail, tc.errdetail) {
  1760. t.Errorf("[%d: %q] expected error detail %q, got %q", i, tc.name, tc.errdetail, errs[0].Detail)
  1761. }
  1762. }
  1763. }
  1764. }
  1765. func TestValidateGlusterfs(t *testing.T) {
  1766. testCases := []struct {
  1767. name string
  1768. gfs *core.GlusterfsVolumeSource
  1769. errtype field.ErrorType
  1770. errfield string
  1771. }{
  1772. {
  1773. name: "missing endpointname",
  1774. gfs: &core.GlusterfsVolumeSource{EndpointsName: "", Path: "/tmp"},
  1775. errtype: field.ErrorTypeRequired,
  1776. errfield: "endpoints",
  1777. },
  1778. {
  1779. name: "missing path",
  1780. gfs: &core.GlusterfsVolumeSource{EndpointsName: "my-endpoint", Path: ""},
  1781. errtype: field.ErrorTypeRequired,
  1782. errfield: "path",
  1783. },
  1784. {
  1785. name: "missing endpointname and path",
  1786. gfs: &core.GlusterfsVolumeSource{EndpointsName: "", Path: ""},
  1787. errtype: field.ErrorTypeRequired,
  1788. errfield: "endpoints",
  1789. },
  1790. }
  1791. for i, tc := range testCases {
  1792. errs := validateGlusterfsVolumeSource(tc.gfs, field.NewPath("field"))
  1793. if len(errs) > 0 && tc.errtype == "" {
  1794. t.Errorf("[%d: %q] unexpected error(s): %v", i, tc.name, errs)
  1795. } else if len(errs) == 0 && tc.errtype != "" {
  1796. t.Errorf("[%d: %q] expected error type %v", i, tc.name, tc.errtype)
  1797. } else if len(errs) >= 1 {
  1798. if errs[0].Type != tc.errtype {
  1799. t.Errorf("[%d: %q] expected error type %v, got %v", i, tc.name, tc.errtype, errs[0].Type)
  1800. } else if !strings.HasSuffix(errs[0].Field, "."+tc.errfield) {
  1801. t.Errorf("[%d: %q] expected error on field %q, got %q", i, tc.name, tc.errfield, errs[0].Field)
  1802. }
  1803. }
  1804. }
  1805. }
  1806. func TestValidateGlusterfsPersistentVolumeSource(t *testing.T) {
  1807. var epNs *string
  1808. namespace := ""
  1809. epNs = &namespace
  1810. testCases := []struct {
  1811. name string
  1812. gfs *core.GlusterfsPersistentVolumeSource
  1813. errtype field.ErrorType
  1814. errfield string
  1815. }{
  1816. {
  1817. name: "missing endpointname",
  1818. gfs: &core.GlusterfsPersistentVolumeSource{EndpointsName: "", Path: "/tmp"},
  1819. errtype: field.ErrorTypeRequired,
  1820. errfield: "endpoints",
  1821. },
  1822. {
  1823. name: "missing path",
  1824. gfs: &core.GlusterfsPersistentVolumeSource{EndpointsName: "my-endpoint", Path: ""},
  1825. errtype: field.ErrorTypeRequired,
  1826. errfield: "path",
  1827. },
  1828. {
  1829. name: "non null endpointnamespace with empty string",
  1830. gfs: &core.GlusterfsPersistentVolumeSource{EndpointsName: "my-endpoint", Path: "/tmp", EndpointsNamespace: epNs},
  1831. errtype: field.ErrorTypeInvalid,
  1832. errfield: "endpointsNamespace",
  1833. },
  1834. {
  1835. name: "missing endpointname and path",
  1836. gfs: &core.GlusterfsPersistentVolumeSource{EndpointsName: "", Path: ""},
  1837. errtype: field.ErrorTypeRequired,
  1838. errfield: "endpoints",
  1839. },
  1840. }
  1841. for i, tc := range testCases {
  1842. errs := validateGlusterfsPersistentVolumeSource(tc.gfs, field.NewPath("field"))
  1843. if len(errs) > 0 && tc.errtype == "" {
  1844. t.Errorf("[%d: %q] unexpected error(s): %v", i, tc.name, errs)
  1845. } else if len(errs) == 0 && tc.errtype != "" {
  1846. t.Errorf("[%d: %q] expected error type %v", i, tc.name, tc.errtype)
  1847. } else if len(errs) >= 1 {
  1848. if errs[0].Type != tc.errtype {
  1849. t.Errorf("[%d: %q] expected error type %v, got %v", i, tc.name, tc.errtype, errs[0].Type)
  1850. } else if !strings.HasSuffix(errs[0].Field, "."+tc.errfield) {
  1851. t.Errorf("[%d: %q] expected error on field %q, got %q", i, tc.name, tc.errfield, errs[0].Field)
  1852. }
  1853. }
  1854. }
  1855. }
  1856. func TestValidateCSIVolumeSource(t *testing.T) {
  1857. testCases := []struct {
  1858. name string
  1859. csi *core.CSIPersistentVolumeSource
  1860. errtype field.ErrorType
  1861. errfield string
  1862. }{
  1863. {
  1864. name: "all required fields ok",
  1865. csi: &core.CSIPersistentVolumeSource{Driver: "test-driver", VolumeHandle: "test-123", ReadOnly: true},
  1866. },
  1867. {
  1868. name: "with default values ok",
  1869. csi: &core.CSIPersistentVolumeSource{Driver: "test-driver", VolumeHandle: "test-123"},
  1870. },
  1871. {
  1872. name: "missing driver name",
  1873. csi: &core.CSIPersistentVolumeSource{VolumeHandle: "test-123"},
  1874. errtype: field.ErrorTypeRequired,
  1875. errfield: "driver",
  1876. },
  1877. {
  1878. name: "missing volume handle",
  1879. csi: &core.CSIPersistentVolumeSource{Driver: "my-driver"},
  1880. errtype: field.ErrorTypeRequired,
  1881. errfield: "volumeHandle",
  1882. },
  1883. {
  1884. name: "driver name: ok no punctuations",
  1885. csi: &core.CSIPersistentVolumeSource{Driver: "comgooglestoragecsigcepd", VolumeHandle: "test-123"},
  1886. },
  1887. {
  1888. name: "driver name: ok dot only",
  1889. csi: &core.CSIPersistentVolumeSource{Driver: "io.kubernetes.storage.csi.flex", VolumeHandle: "test-123"},
  1890. },
  1891. {
  1892. name: "driver name: ok dash only",
  1893. csi: &core.CSIPersistentVolumeSource{Driver: "io-kubernetes-storage-csi-flex", VolumeHandle: "test-123"},
  1894. },
  1895. {
  1896. name: "driver name: invalid underscore",
  1897. csi: &core.CSIPersistentVolumeSource{Driver: "io_kubernetes_storage_csi_flex", VolumeHandle: "test-123"},
  1898. errtype: field.ErrorTypeInvalid,
  1899. errfield: "driver",
  1900. },
  1901. {
  1902. name: "driver name: invalid dot underscores",
  1903. csi: &core.CSIPersistentVolumeSource{Driver: "io.kubernetes.storage_csi.flex", VolumeHandle: "test-123"},
  1904. errtype: field.ErrorTypeInvalid,
  1905. errfield: "driver",
  1906. },
  1907. {
  1908. name: "driver name: ok beginnin with number",
  1909. csi: &core.CSIPersistentVolumeSource{Driver: "2io.kubernetes.storage-csi.flex", VolumeHandle: "test-123"},
  1910. },
  1911. {
  1912. name: "driver name: ok ending with number",
  1913. csi: &core.CSIPersistentVolumeSource{Driver: "io.kubernetes.storage-csi.flex2", VolumeHandle: "test-123"},
  1914. },
  1915. {
  1916. name: "driver name: invalid dot dash underscores",
  1917. csi: &core.CSIPersistentVolumeSource{Driver: "io.kubernetes-storage.csi_flex", VolumeHandle: "test-123"},
  1918. errtype: field.ErrorTypeInvalid,
  1919. errfield: "driver",
  1920. },
  1921. {
  1922. name: "driver name: invalid length 0",
  1923. csi: &core.CSIPersistentVolumeSource{Driver: "", VolumeHandle: "test-123"},
  1924. errtype: field.ErrorTypeRequired,
  1925. errfield: "driver",
  1926. },
  1927. {
  1928. name: "driver name: ok length 1",
  1929. csi: &core.CSIPersistentVolumeSource{Driver: "a", VolumeHandle: "test-123"},
  1930. },
  1931. {
  1932. name: "driver name: invalid length > 63",
  1933. csi: &core.CSIPersistentVolumeSource{Driver: "comgooglestoragecsigcepdcomgooglestoragecsigcepdcomgooglestoragecsigcepdcomgooglestoragecsigcepd", VolumeHandle: "test-123"},
  1934. errtype: field.ErrorTypeTooLong,
  1935. errfield: "driver",
  1936. },
  1937. {
  1938. name: "driver name: invalid start char",
  1939. csi: &core.CSIPersistentVolumeSource{Driver: "_comgooglestoragecsigcepd", VolumeHandle: "test-123"},
  1940. errtype: field.ErrorTypeInvalid,
  1941. errfield: "driver",
  1942. },
  1943. {
  1944. name: "driver name: invalid end char",
  1945. csi: &core.CSIPersistentVolumeSource{Driver: "comgooglestoragecsigcepd/", VolumeHandle: "test-123"},
  1946. errtype: field.ErrorTypeInvalid,
  1947. errfield: "driver",
  1948. },
  1949. {
  1950. name: "driver name: invalid separators",
  1951. csi: &core.CSIPersistentVolumeSource{Driver: "com/google/storage/csi~gcepd", VolumeHandle: "test-123"},
  1952. errtype: field.ErrorTypeInvalid,
  1953. errfield: "driver",
  1954. },
  1955. {
  1956. name: "controllerExpandSecretRef: invalid name missing",
  1957. csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", ControllerExpandSecretRef: &core.SecretReference{Namespace: "default"}},
  1958. errtype: field.ErrorTypeRequired,
  1959. errfield: "controllerExpandSecretRef.name",
  1960. },
  1961. {
  1962. name: "controllerExpandSecretRef: invalid namespace missing",
  1963. csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", ControllerExpandSecretRef: &core.SecretReference{Name: "foobar"}},
  1964. errtype: field.ErrorTypeRequired,
  1965. errfield: "controllerExpandSecretRef.namespace",
  1966. },
  1967. {
  1968. name: "valid controllerExpandSecretRef",
  1969. csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", ControllerExpandSecretRef: &core.SecretReference{Name: "foobar", Namespace: "default"}},
  1970. },
  1971. }
  1972. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIPersistentVolume, true)()
  1973. for i, tc := range testCases {
  1974. errs := validateCSIPersistentVolumeSource(tc.csi, field.NewPath("field"))
  1975. if len(errs) > 0 && tc.errtype == "" {
  1976. t.Errorf("[%d: %q] unexpected error(s): %v", i, tc.name, errs)
  1977. } else if len(errs) == 0 && tc.errtype != "" {
  1978. t.Errorf("[%d: %q] expected error type %v", i, tc.name, tc.errtype)
  1979. } else if len(errs) >= 1 {
  1980. if errs[0].Type != tc.errtype {
  1981. t.Errorf("[%d: %q] expected error type %v, got %v", i, tc.name, tc.errtype, errs[0].Type)
  1982. } else if !strings.HasSuffix(errs[0].Field, "."+tc.errfield) {
  1983. t.Errorf("[%d: %q] expected error on field %q, got %q", i, tc.name, tc.errfield, errs[0].Field)
  1984. }
  1985. }
  1986. }
  1987. }
  1988. // This test is a little too top-to-bottom. Ideally we would test each volume
  1989. // type on its own, but we want to also make sure that the logic works through
  1990. // the one-of wrapper, so we just do it all in one place.
  1991. func TestValidateVolumes(t *testing.T) {
  1992. validInitiatorName := "iqn.2015-02.example.com:init"
  1993. invalidInitiatorName := "2015-02.example.com:init"
  1994. type verr struct {
  1995. etype field.ErrorType
  1996. field string
  1997. detail string
  1998. }
  1999. testCases := []struct {
  2000. name string
  2001. vol core.Volume
  2002. errs []verr
  2003. }{
  2004. // EmptyDir and basic volume names
  2005. {
  2006. name: "valid alpha name",
  2007. vol: core.Volume{
  2008. Name: "empty",
  2009. VolumeSource: core.VolumeSource{
  2010. EmptyDir: &core.EmptyDirVolumeSource{},
  2011. },
  2012. },
  2013. },
  2014. {
  2015. name: "valid num name",
  2016. vol: core.Volume{
  2017. Name: "123",
  2018. VolumeSource: core.VolumeSource{
  2019. EmptyDir: &core.EmptyDirVolumeSource{},
  2020. },
  2021. },
  2022. },
  2023. {
  2024. name: "valid alphanum name",
  2025. vol: core.Volume{
  2026. Name: "empty-123",
  2027. VolumeSource: core.VolumeSource{
  2028. EmptyDir: &core.EmptyDirVolumeSource{},
  2029. },
  2030. },
  2031. },
  2032. {
  2033. name: "valid numalpha name",
  2034. vol: core.Volume{
  2035. Name: "123-empty",
  2036. VolumeSource: core.VolumeSource{
  2037. EmptyDir: &core.EmptyDirVolumeSource{},
  2038. },
  2039. },
  2040. },
  2041. {
  2042. name: "zero-length name",
  2043. vol: core.Volume{
  2044. Name: "",
  2045. VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}},
  2046. },
  2047. errs: []verr{{
  2048. etype: field.ErrorTypeRequired,
  2049. field: "name",
  2050. }},
  2051. },
  2052. {
  2053. name: "name > 63 characters",
  2054. vol: core.Volume{
  2055. Name: strings.Repeat("a", 64),
  2056. VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}},
  2057. },
  2058. errs: []verr{{
  2059. etype: field.ErrorTypeInvalid,
  2060. field: "name",
  2061. detail: "must be no more than",
  2062. }},
  2063. },
  2064. {
  2065. name: "name not a DNS label",
  2066. vol: core.Volume{
  2067. Name: "a.b.c",
  2068. VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}},
  2069. },
  2070. errs: []verr{{
  2071. etype: field.ErrorTypeInvalid,
  2072. field: "name",
  2073. detail: dnsLabelErrMsg,
  2074. }},
  2075. },
  2076. // More than one source field specified.
  2077. {
  2078. name: "more than one source",
  2079. vol: core.Volume{
  2080. Name: "dups",
  2081. VolumeSource: core.VolumeSource{
  2082. EmptyDir: &core.EmptyDirVolumeSource{},
  2083. HostPath: &core.HostPathVolumeSource{
  2084. Path: "/mnt/path",
  2085. Type: newHostPathType(string(core.HostPathDirectory)),
  2086. },
  2087. },
  2088. },
  2089. errs: []verr{{
  2090. etype: field.ErrorTypeForbidden,
  2091. field: "hostPath",
  2092. detail: "may not specify more than 1 volume",
  2093. }},
  2094. },
  2095. // HostPath Default
  2096. {
  2097. name: "default HostPath",
  2098. vol: core.Volume{
  2099. Name: "hostpath",
  2100. VolumeSource: core.VolumeSource{
  2101. HostPath: &core.HostPathVolumeSource{
  2102. Path: "/mnt/path",
  2103. Type: newHostPathType(string(core.HostPathDirectory)),
  2104. },
  2105. },
  2106. },
  2107. },
  2108. // HostPath Supported
  2109. {
  2110. name: "valid HostPath",
  2111. vol: core.Volume{
  2112. Name: "hostpath",
  2113. VolumeSource: core.VolumeSource{
  2114. HostPath: &core.HostPathVolumeSource{
  2115. Path: "/mnt/path",
  2116. Type: newHostPathType(string(core.HostPathSocket)),
  2117. },
  2118. },
  2119. },
  2120. },
  2121. // HostPath Invalid
  2122. {
  2123. name: "invalid HostPath",
  2124. vol: core.Volume{
  2125. Name: "hostpath",
  2126. VolumeSource: core.VolumeSource{
  2127. HostPath: &core.HostPathVolumeSource{
  2128. Path: "/mnt/path",
  2129. Type: newHostPathType("invalid"),
  2130. },
  2131. },
  2132. },
  2133. errs: []verr{{
  2134. etype: field.ErrorTypeNotSupported,
  2135. field: "type",
  2136. }},
  2137. },
  2138. {
  2139. name: "invalid HostPath backsteps",
  2140. vol: core.Volume{
  2141. Name: "hostpath",
  2142. VolumeSource: core.VolumeSource{
  2143. HostPath: &core.HostPathVolumeSource{
  2144. Path: "/mnt/path/..",
  2145. Type: newHostPathType(string(core.HostPathDirectory)),
  2146. },
  2147. },
  2148. },
  2149. errs: []verr{{
  2150. etype: field.ErrorTypeInvalid,
  2151. field: "path",
  2152. detail: "must not contain '..'",
  2153. }},
  2154. },
  2155. // GcePersistentDisk
  2156. {
  2157. name: "valid GcePersistentDisk",
  2158. vol: core.Volume{
  2159. Name: "gce-pd",
  2160. VolumeSource: core.VolumeSource{
  2161. GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{
  2162. PDName: "my-PD",
  2163. FSType: "ext4",
  2164. Partition: 1,
  2165. ReadOnly: false,
  2166. },
  2167. },
  2168. },
  2169. },
  2170. // AWSElasticBlockStore
  2171. {
  2172. name: "valid AWSElasticBlockStore",
  2173. vol: core.Volume{
  2174. Name: "aws-ebs",
  2175. VolumeSource: core.VolumeSource{
  2176. AWSElasticBlockStore: &core.AWSElasticBlockStoreVolumeSource{
  2177. VolumeID: "my-PD",
  2178. FSType: "ext4",
  2179. Partition: 1,
  2180. ReadOnly: false,
  2181. },
  2182. },
  2183. },
  2184. },
  2185. // GitRepo
  2186. {
  2187. name: "valid GitRepo",
  2188. vol: core.Volume{
  2189. Name: "git-repo",
  2190. VolumeSource: core.VolumeSource{
  2191. GitRepo: &core.GitRepoVolumeSource{
  2192. Repository: "my-repo",
  2193. Revision: "hashstring",
  2194. Directory: "target",
  2195. },
  2196. },
  2197. },
  2198. },
  2199. {
  2200. name: "valid GitRepo in .",
  2201. vol: core.Volume{
  2202. Name: "git-repo-dot",
  2203. VolumeSource: core.VolumeSource{
  2204. GitRepo: &core.GitRepoVolumeSource{
  2205. Repository: "my-repo",
  2206. Directory: ".",
  2207. },
  2208. },
  2209. },
  2210. },
  2211. {
  2212. name: "valid GitRepo with .. in name",
  2213. vol: core.Volume{
  2214. Name: "git-repo-dot-dot-foo",
  2215. VolumeSource: core.VolumeSource{
  2216. GitRepo: &core.GitRepoVolumeSource{
  2217. Repository: "my-repo",
  2218. Directory: "..foo",
  2219. },
  2220. },
  2221. },
  2222. },
  2223. {
  2224. name: "GitRepo starts with ../",
  2225. vol: core.Volume{
  2226. Name: "gitrepo",
  2227. VolumeSource: core.VolumeSource{
  2228. GitRepo: &core.GitRepoVolumeSource{
  2229. Repository: "foo",
  2230. Directory: "../dots/bar",
  2231. },
  2232. },
  2233. },
  2234. errs: []verr{{
  2235. etype: field.ErrorTypeInvalid,
  2236. field: "gitRepo.directory",
  2237. detail: `must not contain '..'`,
  2238. }},
  2239. },
  2240. {
  2241. name: "GitRepo contains ..",
  2242. vol: core.Volume{
  2243. Name: "gitrepo",
  2244. VolumeSource: core.VolumeSource{
  2245. GitRepo: &core.GitRepoVolumeSource{
  2246. Repository: "foo",
  2247. Directory: "dots/../bar",
  2248. },
  2249. },
  2250. },
  2251. errs: []verr{{
  2252. etype: field.ErrorTypeInvalid,
  2253. field: "gitRepo.directory",
  2254. detail: `must not contain '..'`,
  2255. }},
  2256. },
  2257. {
  2258. name: "GitRepo absolute target",
  2259. vol: core.Volume{
  2260. Name: "gitrepo",
  2261. VolumeSource: core.VolumeSource{
  2262. GitRepo: &core.GitRepoVolumeSource{
  2263. Repository: "foo",
  2264. Directory: "/abstarget",
  2265. },
  2266. },
  2267. },
  2268. errs: []verr{{
  2269. etype: field.ErrorTypeInvalid,
  2270. field: "gitRepo.directory",
  2271. }},
  2272. },
  2273. // ISCSI
  2274. {
  2275. name: "valid ISCSI",
  2276. vol: core.Volume{
  2277. Name: "iscsi",
  2278. VolumeSource: core.VolumeSource{
  2279. ISCSI: &core.ISCSIVolumeSource{
  2280. TargetPortal: "127.0.0.1",
  2281. IQN: "iqn.2015-02.example.com:test",
  2282. Lun: 1,
  2283. FSType: "ext4",
  2284. ReadOnly: false,
  2285. },
  2286. },
  2287. },
  2288. },
  2289. {
  2290. name: "valid IQN: eui format",
  2291. vol: core.Volume{
  2292. Name: "iscsi",
  2293. VolumeSource: core.VolumeSource{
  2294. ISCSI: &core.ISCSIVolumeSource{
  2295. TargetPortal: "127.0.0.1",
  2296. IQN: "eui.0123456789ABCDEF",
  2297. Lun: 1,
  2298. FSType: "ext4",
  2299. ReadOnly: false,
  2300. },
  2301. },
  2302. },
  2303. },
  2304. {
  2305. name: "valid IQN: naa format",
  2306. vol: core.Volume{
  2307. Name: "iscsi",
  2308. VolumeSource: core.VolumeSource{
  2309. ISCSI: &core.ISCSIVolumeSource{
  2310. TargetPortal: "127.0.0.1",
  2311. IQN: "naa.62004567BA64678D0123456789ABCDEF",
  2312. Lun: 1,
  2313. FSType: "ext4",
  2314. ReadOnly: false,
  2315. },
  2316. },
  2317. },
  2318. },
  2319. {
  2320. name: "empty portal",
  2321. vol: core.Volume{
  2322. Name: "iscsi",
  2323. VolumeSource: core.VolumeSource{
  2324. ISCSI: &core.ISCSIVolumeSource{
  2325. TargetPortal: "",
  2326. IQN: "iqn.2015-02.example.com:test",
  2327. Lun: 1,
  2328. FSType: "ext4",
  2329. ReadOnly: false,
  2330. },
  2331. },
  2332. },
  2333. errs: []verr{{
  2334. etype: field.ErrorTypeRequired,
  2335. field: "iscsi.targetPortal",
  2336. }},
  2337. },
  2338. {
  2339. name: "empty iqn",
  2340. vol: core.Volume{
  2341. Name: "iscsi",
  2342. VolumeSource: core.VolumeSource{
  2343. ISCSI: &core.ISCSIVolumeSource{
  2344. TargetPortal: "127.0.0.1",
  2345. IQN: "",
  2346. Lun: 1,
  2347. FSType: "ext4",
  2348. ReadOnly: false,
  2349. },
  2350. },
  2351. },
  2352. errs: []verr{{
  2353. etype: field.ErrorTypeRequired,
  2354. field: "iscsi.iqn",
  2355. }},
  2356. },
  2357. {
  2358. name: "invalid IQN: iqn format",
  2359. vol: core.Volume{
  2360. Name: "iscsi",
  2361. VolumeSource: core.VolumeSource{
  2362. ISCSI: &core.ISCSIVolumeSource{
  2363. TargetPortal: "127.0.0.1",
  2364. IQN: "iqn.2015-02.example.com:test;ls;",
  2365. Lun: 1,
  2366. FSType: "ext4",
  2367. ReadOnly: false,
  2368. },
  2369. },
  2370. },
  2371. errs: []verr{{
  2372. etype: field.ErrorTypeInvalid,
  2373. field: "iscsi.iqn",
  2374. }},
  2375. },
  2376. {
  2377. name: "invalid IQN: eui format",
  2378. vol: core.Volume{
  2379. Name: "iscsi",
  2380. VolumeSource: core.VolumeSource{
  2381. ISCSI: &core.ISCSIVolumeSource{
  2382. TargetPortal: "127.0.0.1",
  2383. IQN: "eui.0123456789ABCDEFGHIJ",
  2384. Lun: 1,
  2385. FSType: "ext4",
  2386. ReadOnly: false,
  2387. },
  2388. },
  2389. },
  2390. errs: []verr{{
  2391. etype: field.ErrorTypeInvalid,
  2392. field: "iscsi.iqn",
  2393. }},
  2394. },
  2395. {
  2396. name: "invalid IQN: naa format",
  2397. vol: core.Volume{
  2398. Name: "iscsi",
  2399. VolumeSource: core.VolumeSource{
  2400. ISCSI: &core.ISCSIVolumeSource{
  2401. TargetPortal: "127.0.0.1",
  2402. IQN: "naa.62004567BA_4-78D.123456789ABCDEF",
  2403. Lun: 1,
  2404. FSType: "ext4",
  2405. ReadOnly: false,
  2406. },
  2407. },
  2408. },
  2409. errs: []verr{{
  2410. etype: field.ErrorTypeInvalid,
  2411. field: "iscsi.iqn",
  2412. }},
  2413. },
  2414. {
  2415. name: "valid initiatorName",
  2416. vol: core.Volume{
  2417. Name: "iscsi",
  2418. VolumeSource: core.VolumeSource{
  2419. ISCSI: &core.ISCSIVolumeSource{
  2420. TargetPortal: "127.0.0.1",
  2421. IQN: "iqn.2015-02.example.com:test",
  2422. Lun: 1,
  2423. InitiatorName: &validInitiatorName,
  2424. FSType: "ext4",
  2425. ReadOnly: false,
  2426. },
  2427. },
  2428. },
  2429. },
  2430. {
  2431. name: "invalid initiatorName",
  2432. vol: core.Volume{
  2433. Name: "iscsi",
  2434. VolumeSource: core.VolumeSource{
  2435. ISCSI: &core.ISCSIVolumeSource{
  2436. TargetPortal: "127.0.0.1",
  2437. IQN: "iqn.2015-02.example.com:test",
  2438. Lun: 1,
  2439. InitiatorName: &invalidInitiatorName,
  2440. FSType: "ext4",
  2441. ReadOnly: false,
  2442. },
  2443. },
  2444. },
  2445. errs: []verr{{
  2446. etype: field.ErrorTypeInvalid,
  2447. field: "iscsi.initiatorname",
  2448. }},
  2449. },
  2450. {
  2451. name: "empty secret",
  2452. vol: core.Volume{
  2453. Name: "iscsi",
  2454. VolumeSource: core.VolumeSource{
  2455. ISCSI: &core.ISCSIVolumeSource{
  2456. TargetPortal: "127.0.0.1",
  2457. IQN: "iqn.2015-02.example.com:test",
  2458. Lun: 1,
  2459. FSType: "ext4",
  2460. ReadOnly: false,
  2461. DiscoveryCHAPAuth: true,
  2462. },
  2463. },
  2464. },
  2465. errs: []verr{{
  2466. etype: field.ErrorTypeRequired,
  2467. field: "iscsi.secretRef",
  2468. }},
  2469. },
  2470. {
  2471. name: "empty secret",
  2472. vol: core.Volume{
  2473. Name: "iscsi",
  2474. VolumeSource: core.VolumeSource{
  2475. ISCSI: &core.ISCSIVolumeSource{
  2476. TargetPortal: "127.0.0.1",
  2477. IQN: "iqn.2015-02.example.com:test",
  2478. Lun: 1,
  2479. FSType: "ext4",
  2480. ReadOnly: false,
  2481. SessionCHAPAuth: true,
  2482. },
  2483. },
  2484. },
  2485. errs: []verr{{
  2486. etype: field.ErrorTypeRequired,
  2487. field: "iscsi.secretRef",
  2488. }},
  2489. },
  2490. // Secret
  2491. {
  2492. name: "valid Secret",
  2493. vol: core.Volume{
  2494. Name: "secret",
  2495. VolumeSource: core.VolumeSource{
  2496. Secret: &core.SecretVolumeSource{
  2497. SecretName: "my-secret",
  2498. },
  2499. },
  2500. },
  2501. },
  2502. {
  2503. name: "valid Secret with defaultMode",
  2504. vol: core.Volume{
  2505. Name: "secret",
  2506. VolumeSource: core.VolumeSource{
  2507. Secret: &core.SecretVolumeSource{
  2508. SecretName: "my-secret",
  2509. DefaultMode: utilpointer.Int32Ptr(0644),
  2510. },
  2511. },
  2512. },
  2513. },
  2514. {
  2515. name: "valid Secret with projection and mode",
  2516. vol: core.Volume{
  2517. Name: "secret",
  2518. VolumeSource: core.VolumeSource{
  2519. Secret: &core.SecretVolumeSource{
  2520. SecretName: "my-secret",
  2521. Items: []core.KeyToPath{{
  2522. Key: "key",
  2523. Path: "filename",
  2524. Mode: utilpointer.Int32Ptr(0644),
  2525. }},
  2526. },
  2527. },
  2528. },
  2529. },
  2530. {
  2531. name: "valid Secret with subdir projection",
  2532. vol: core.Volume{
  2533. Name: "secret",
  2534. VolumeSource: core.VolumeSource{
  2535. Secret: &core.SecretVolumeSource{
  2536. SecretName: "my-secret",
  2537. Items: []core.KeyToPath{{
  2538. Key: "key",
  2539. Path: "dir/filename",
  2540. }},
  2541. },
  2542. },
  2543. },
  2544. },
  2545. {
  2546. name: "secret with missing path",
  2547. vol: core.Volume{
  2548. Name: "secret",
  2549. VolumeSource: core.VolumeSource{
  2550. Secret: &core.SecretVolumeSource{
  2551. SecretName: "s",
  2552. Items: []core.KeyToPath{{Key: "key", Path: ""}},
  2553. },
  2554. },
  2555. },
  2556. errs: []verr{{
  2557. etype: field.ErrorTypeRequired,
  2558. field: "secret.items[0].path",
  2559. }},
  2560. },
  2561. {
  2562. name: "secret with leading ..",
  2563. vol: core.Volume{
  2564. Name: "secret",
  2565. VolumeSource: core.VolumeSource{
  2566. Secret: &core.SecretVolumeSource{
  2567. SecretName: "s",
  2568. Items: []core.KeyToPath{{Key: "key", Path: "../foo"}},
  2569. },
  2570. },
  2571. },
  2572. errs: []verr{{
  2573. etype: field.ErrorTypeInvalid,
  2574. field: "secret.items[0].path",
  2575. }},
  2576. },
  2577. {
  2578. name: "secret with .. inside",
  2579. vol: core.Volume{
  2580. Name: "secret",
  2581. VolumeSource: core.VolumeSource{
  2582. Secret: &core.SecretVolumeSource{
  2583. SecretName: "s",
  2584. Items: []core.KeyToPath{{Key: "key", Path: "foo/../bar"}},
  2585. },
  2586. },
  2587. },
  2588. errs: []verr{{
  2589. etype: field.ErrorTypeInvalid,
  2590. field: "secret.items[0].path",
  2591. }},
  2592. },
  2593. {
  2594. name: "secret with invalid positive defaultMode",
  2595. vol: core.Volume{
  2596. Name: "secret",
  2597. VolumeSource: core.VolumeSource{
  2598. Secret: &core.SecretVolumeSource{
  2599. SecretName: "s",
  2600. DefaultMode: utilpointer.Int32Ptr(01000),
  2601. },
  2602. },
  2603. },
  2604. errs: []verr{{
  2605. etype: field.ErrorTypeInvalid,
  2606. field: "secret.defaultMode",
  2607. }},
  2608. },
  2609. {
  2610. name: "secret with invalid negative defaultMode",
  2611. vol: core.Volume{
  2612. Name: "secret",
  2613. VolumeSource: core.VolumeSource{
  2614. Secret: &core.SecretVolumeSource{
  2615. SecretName: "s",
  2616. DefaultMode: utilpointer.Int32Ptr(-1),
  2617. },
  2618. },
  2619. },
  2620. errs: []verr{{
  2621. etype: field.ErrorTypeInvalid,
  2622. field: "secret.defaultMode",
  2623. }},
  2624. },
  2625. // ConfigMap
  2626. {
  2627. name: "valid ConfigMap",
  2628. vol: core.Volume{
  2629. Name: "cfgmap",
  2630. VolumeSource: core.VolumeSource{
  2631. ConfigMap: &core.ConfigMapVolumeSource{
  2632. LocalObjectReference: core.LocalObjectReference{
  2633. Name: "my-cfgmap",
  2634. },
  2635. },
  2636. },
  2637. },
  2638. },
  2639. {
  2640. name: "valid ConfigMap with defaultMode",
  2641. vol: core.Volume{
  2642. Name: "cfgmap",
  2643. VolumeSource: core.VolumeSource{
  2644. ConfigMap: &core.ConfigMapVolumeSource{
  2645. LocalObjectReference: core.LocalObjectReference{
  2646. Name: "my-cfgmap",
  2647. },
  2648. DefaultMode: utilpointer.Int32Ptr(0644),
  2649. },
  2650. },
  2651. },
  2652. },
  2653. {
  2654. name: "valid ConfigMap with projection and mode",
  2655. vol: core.Volume{
  2656. Name: "cfgmap",
  2657. VolumeSource: core.VolumeSource{
  2658. ConfigMap: &core.ConfigMapVolumeSource{
  2659. LocalObjectReference: core.LocalObjectReference{
  2660. Name: "my-cfgmap"},
  2661. Items: []core.KeyToPath{{
  2662. Key: "key",
  2663. Path: "filename",
  2664. Mode: utilpointer.Int32Ptr(0644),
  2665. }},
  2666. },
  2667. },
  2668. },
  2669. },
  2670. {
  2671. name: "valid ConfigMap with subdir projection",
  2672. vol: core.Volume{
  2673. Name: "cfgmap",
  2674. VolumeSource: core.VolumeSource{
  2675. ConfigMap: &core.ConfigMapVolumeSource{
  2676. LocalObjectReference: core.LocalObjectReference{
  2677. Name: "my-cfgmap"},
  2678. Items: []core.KeyToPath{{
  2679. Key: "key",
  2680. Path: "dir/filename",
  2681. }},
  2682. },
  2683. },
  2684. },
  2685. },
  2686. {
  2687. name: "configmap with missing path",
  2688. vol: core.Volume{
  2689. Name: "cfgmap",
  2690. VolumeSource: core.VolumeSource{
  2691. ConfigMap: &core.ConfigMapVolumeSource{
  2692. LocalObjectReference: core.LocalObjectReference{Name: "c"},
  2693. Items: []core.KeyToPath{{Key: "key", Path: ""}},
  2694. },
  2695. },
  2696. },
  2697. errs: []verr{{
  2698. etype: field.ErrorTypeRequired,
  2699. field: "configMap.items[0].path",
  2700. }},
  2701. },
  2702. {
  2703. name: "configmap with leading ..",
  2704. vol: core.Volume{
  2705. Name: "cfgmap",
  2706. VolumeSource: core.VolumeSource{
  2707. ConfigMap: &core.ConfigMapVolumeSource{
  2708. LocalObjectReference: core.LocalObjectReference{Name: "c"},
  2709. Items: []core.KeyToPath{{Key: "key", Path: "../foo"}},
  2710. },
  2711. },
  2712. },
  2713. errs: []verr{{
  2714. etype: field.ErrorTypeInvalid,
  2715. field: "configMap.items[0].path",
  2716. }},
  2717. },
  2718. {
  2719. name: "configmap with .. inside",
  2720. vol: core.Volume{
  2721. Name: "cfgmap",
  2722. VolumeSource: core.VolumeSource{
  2723. ConfigMap: &core.ConfigMapVolumeSource{
  2724. LocalObjectReference: core.LocalObjectReference{Name: "c"},
  2725. Items: []core.KeyToPath{{Key: "key", Path: "foo/../bar"}},
  2726. },
  2727. },
  2728. },
  2729. errs: []verr{{
  2730. etype: field.ErrorTypeInvalid,
  2731. field: "configMap.items[0].path",
  2732. }},
  2733. },
  2734. {
  2735. name: "configmap with invalid positive defaultMode",
  2736. vol: core.Volume{
  2737. Name: "cfgmap",
  2738. VolumeSource: core.VolumeSource{
  2739. ConfigMap: &core.ConfigMapVolumeSource{
  2740. LocalObjectReference: core.LocalObjectReference{Name: "c"},
  2741. DefaultMode: utilpointer.Int32Ptr(01000),
  2742. },
  2743. },
  2744. },
  2745. errs: []verr{{
  2746. etype: field.ErrorTypeInvalid,
  2747. field: "configMap.defaultMode",
  2748. }},
  2749. },
  2750. {
  2751. name: "configmap with invalid negative defaultMode",
  2752. vol: core.Volume{
  2753. Name: "cfgmap",
  2754. VolumeSource: core.VolumeSource{
  2755. ConfigMap: &core.ConfigMapVolumeSource{
  2756. LocalObjectReference: core.LocalObjectReference{Name: "c"},
  2757. DefaultMode: utilpointer.Int32Ptr(-1),
  2758. },
  2759. },
  2760. },
  2761. errs: []verr{{
  2762. etype: field.ErrorTypeInvalid,
  2763. field: "configMap.defaultMode",
  2764. }},
  2765. },
  2766. // Glusterfs
  2767. {
  2768. name: "valid Glusterfs",
  2769. vol: core.Volume{
  2770. Name: "glusterfs",
  2771. VolumeSource: core.VolumeSource{
  2772. Glusterfs: &core.GlusterfsVolumeSource{
  2773. EndpointsName: "host1",
  2774. Path: "path",
  2775. ReadOnly: false,
  2776. },
  2777. },
  2778. },
  2779. },
  2780. {
  2781. name: "empty hosts",
  2782. vol: core.Volume{
  2783. Name: "glusterfs",
  2784. VolumeSource: core.VolumeSource{
  2785. Glusterfs: &core.GlusterfsVolumeSource{
  2786. EndpointsName: "",
  2787. Path: "path",
  2788. ReadOnly: false,
  2789. },
  2790. },
  2791. },
  2792. errs: []verr{{
  2793. etype: field.ErrorTypeRequired,
  2794. field: "glusterfs.endpoints",
  2795. }},
  2796. },
  2797. {
  2798. name: "empty path",
  2799. vol: core.Volume{
  2800. Name: "glusterfs",
  2801. VolumeSource: core.VolumeSource{
  2802. Glusterfs: &core.GlusterfsVolumeSource{
  2803. EndpointsName: "host",
  2804. Path: "",
  2805. ReadOnly: false,
  2806. },
  2807. },
  2808. },
  2809. errs: []verr{{
  2810. etype: field.ErrorTypeRequired,
  2811. field: "glusterfs.path",
  2812. }},
  2813. },
  2814. // Flocker
  2815. {
  2816. name: "valid Flocker -- datasetUUID",
  2817. vol: core.Volume{
  2818. Name: "flocker",
  2819. VolumeSource: core.VolumeSource{
  2820. Flocker: &core.FlockerVolumeSource{
  2821. DatasetUUID: "d846b09d-223d-43df-ab5b-d6db2206a0e4",
  2822. },
  2823. },
  2824. },
  2825. },
  2826. {
  2827. name: "valid Flocker -- datasetName",
  2828. vol: core.Volume{
  2829. Name: "flocker",
  2830. VolumeSource: core.VolumeSource{
  2831. Flocker: &core.FlockerVolumeSource{
  2832. DatasetName: "datasetName",
  2833. },
  2834. },
  2835. },
  2836. },
  2837. {
  2838. name: "both empty",
  2839. vol: core.Volume{
  2840. Name: "flocker",
  2841. VolumeSource: core.VolumeSource{
  2842. Flocker: &core.FlockerVolumeSource{
  2843. DatasetName: "",
  2844. },
  2845. },
  2846. },
  2847. errs: []verr{{
  2848. etype: field.ErrorTypeRequired,
  2849. field: "flocker",
  2850. }},
  2851. },
  2852. {
  2853. name: "both specified",
  2854. vol: core.Volume{
  2855. Name: "flocker",
  2856. VolumeSource: core.VolumeSource{
  2857. Flocker: &core.FlockerVolumeSource{
  2858. DatasetName: "datasetName",
  2859. DatasetUUID: "d846b09d-223d-43df-ab5b-d6db2206a0e4",
  2860. },
  2861. },
  2862. },
  2863. errs: []verr{{
  2864. etype: field.ErrorTypeInvalid,
  2865. field: "flocker",
  2866. }},
  2867. },
  2868. {
  2869. name: "slash in flocker datasetName",
  2870. vol: core.Volume{
  2871. Name: "flocker",
  2872. VolumeSource: core.VolumeSource{
  2873. Flocker: &core.FlockerVolumeSource{
  2874. DatasetName: "foo/bar",
  2875. },
  2876. },
  2877. },
  2878. errs: []verr{{
  2879. etype: field.ErrorTypeInvalid,
  2880. field: "flocker.datasetName",
  2881. detail: "must not contain '/'",
  2882. }},
  2883. },
  2884. // RBD
  2885. {
  2886. name: "valid RBD",
  2887. vol: core.Volume{
  2888. Name: "rbd",
  2889. VolumeSource: core.VolumeSource{
  2890. RBD: &core.RBDVolumeSource{
  2891. CephMonitors: []string{"foo"},
  2892. RBDImage: "bar",
  2893. FSType: "ext4",
  2894. },
  2895. },
  2896. },
  2897. },
  2898. {
  2899. name: "empty rbd monitors",
  2900. vol: core.Volume{
  2901. Name: "rbd",
  2902. VolumeSource: core.VolumeSource{
  2903. RBD: &core.RBDVolumeSource{
  2904. CephMonitors: []string{},
  2905. RBDImage: "bar",
  2906. FSType: "ext4",
  2907. },
  2908. },
  2909. },
  2910. errs: []verr{{
  2911. etype: field.ErrorTypeRequired,
  2912. field: "rbd.monitors",
  2913. }},
  2914. },
  2915. {
  2916. name: "empty image",
  2917. vol: core.Volume{
  2918. Name: "rbd",
  2919. VolumeSource: core.VolumeSource{
  2920. RBD: &core.RBDVolumeSource{
  2921. CephMonitors: []string{"foo"},
  2922. RBDImage: "",
  2923. FSType: "ext4",
  2924. },
  2925. },
  2926. },
  2927. errs: []verr{{
  2928. etype: field.ErrorTypeRequired,
  2929. field: "rbd.image",
  2930. }},
  2931. },
  2932. // Cinder
  2933. {
  2934. name: "valid Cinder",
  2935. vol: core.Volume{
  2936. Name: "cinder",
  2937. VolumeSource: core.VolumeSource{
  2938. Cinder: &core.CinderVolumeSource{
  2939. VolumeID: "29ea5088-4f60-4757-962e-dba678767887",
  2940. FSType: "ext4",
  2941. ReadOnly: false,
  2942. },
  2943. },
  2944. },
  2945. },
  2946. // CephFS
  2947. {
  2948. name: "valid CephFS",
  2949. vol: core.Volume{
  2950. Name: "cephfs",
  2951. VolumeSource: core.VolumeSource{
  2952. CephFS: &core.CephFSVolumeSource{
  2953. Monitors: []string{"foo"},
  2954. },
  2955. },
  2956. },
  2957. },
  2958. {
  2959. name: "empty cephfs monitors",
  2960. vol: core.Volume{
  2961. Name: "cephfs",
  2962. VolumeSource: core.VolumeSource{
  2963. CephFS: &core.CephFSVolumeSource{
  2964. Monitors: []string{},
  2965. },
  2966. },
  2967. },
  2968. errs: []verr{{
  2969. etype: field.ErrorTypeRequired,
  2970. field: "cephfs.monitors",
  2971. }},
  2972. },
  2973. // DownwardAPI
  2974. {
  2975. name: "valid DownwardAPI",
  2976. vol: core.Volume{
  2977. Name: "downwardapi",
  2978. VolumeSource: core.VolumeSource{
  2979. DownwardAPI: &core.DownwardAPIVolumeSource{
  2980. Items: []core.DownwardAPIVolumeFile{
  2981. {
  2982. Path: "labels",
  2983. FieldRef: &core.ObjectFieldSelector{
  2984. APIVersion: "v1",
  2985. FieldPath: "metadata.labels",
  2986. },
  2987. },
  2988. {
  2989. Path: "labels with subscript",
  2990. FieldRef: &core.ObjectFieldSelector{
  2991. APIVersion: "v1",
  2992. FieldPath: "metadata.labels['key']",
  2993. },
  2994. },
  2995. {
  2996. Path: "labels with complex subscript",
  2997. FieldRef: &core.ObjectFieldSelector{
  2998. APIVersion: "v1",
  2999. FieldPath: "metadata.labels['test.example.com/key']",
  3000. },
  3001. },
  3002. {
  3003. Path: "annotations",
  3004. FieldRef: &core.ObjectFieldSelector{
  3005. APIVersion: "v1",
  3006. FieldPath: "metadata.annotations",
  3007. },
  3008. },
  3009. {
  3010. Path: "annotations with subscript",
  3011. FieldRef: &core.ObjectFieldSelector{
  3012. APIVersion: "v1",
  3013. FieldPath: "metadata.annotations['key']",
  3014. },
  3015. },
  3016. {
  3017. Path: "annotations with complex subscript",
  3018. FieldRef: &core.ObjectFieldSelector{
  3019. APIVersion: "v1",
  3020. FieldPath: "metadata.annotations['TEST.EXAMPLE.COM/key']",
  3021. },
  3022. },
  3023. {
  3024. Path: "namespace",
  3025. FieldRef: &core.ObjectFieldSelector{
  3026. APIVersion: "v1",
  3027. FieldPath: "metadata.namespace",
  3028. },
  3029. },
  3030. {
  3031. Path: "name",
  3032. FieldRef: &core.ObjectFieldSelector{
  3033. APIVersion: "v1",
  3034. FieldPath: "metadata.name",
  3035. },
  3036. },
  3037. {
  3038. Path: "path/with/subdirs",
  3039. FieldRef: &core.ObjectFieldSelector{
  3040. APIVersion: "v1",
  3041. FieldPath: "metadata.labels",
  3042. },
  3043. },
  3044. {
  3045. Path: "path/./withdot",
  3046. FieldRef: &core.ObjectFieldSelector{
  3047. APIVersion: "v1",
  3048. FieldPath: "metadata.labels",
  3049. },
  3050. },
  3051. {
  3052. Path: "path/with/embedded..dotdot",
  3053. FieldRef: &core.ObjectFieldSelector{
  3054. APIVersion: "v1",
  3055. FieldPath: "metadata.labels",
  3056. },
  3057. },
  3058. {
  3059. Path: "path/with/leading/..dotdot",
  3060. FieldRef: &core.ObjectFieldSelector{
  3061. APIVersion: "v1",
  3062. FieldPath: "metadata.labels",
  3063. },
  3064. },
  3065. {
  3066. Path: "cpu_limit",
  3067. ResourceFieldRef: &core.ResourceFieldSelector{
  3068. ContainerName: "test-container",
  3069. Resource: "limits.cpu",
  3070. },
  3071. },
  3072. {
  3073. Path: "cpu_request",
  3074. ResourceFieldRef: &core.ResourceFieldSelector{
  3075. ContainerName: "test-container",
  3076. Resource: "requests.cpu",
  3077. },
  3078. },
  3079. {
  3080. Path: "memory_limit",
  3081. ResourceFieldRef: &core.ResourceFieldSelector{
  3082. ContainerName: "test-container",
  3083. Resource: "limits.memory",
  3084. },
  3085. },
  3086. {
  3087. Path: "memory_request",
  3088. ResourceFieldRef: &core.ResourceFieldSelector{
  3089. ContainerName: "test-container",
  3090. Resource: "requests.memory",
  3091. },
  3092. },
  3093. },
  3094. },
  3095. },
  3096. },
  3097. },
  3098. {
  3099. name: "downapi valid defaultMode",
  3100. vol: core.Volume{
  3101. Name: "downapi",
  3102. VolumeSource: core.VolumeSource{
  3103. DownwardAPI: &core.DownwardAPIVolumeSource{
  3104. DefaultMode: utilpointer.Int32Ptr(0644),
  3105. },
  3106. },
  3107. },
  3108. },
  3109. {
  3110. name: "downapi valid item mode",
  3111. vol: core.Volume{
  3112. Name: "downapi",
  3113. VolumeSource: core.VolumeSource{
  3114. DownwardAPI: &core.DownwardAPIVolumeSource{
  3115. Items: []core.DownwardAPIVolumeFile{{
  3116. Mode: utilpointer.Int32Ptr(0644),
  3117. Path: "path",
  3118. FieldRef: &core.ObjectFieldSelector{
  3119. APIVersion: "v1",
  3120. FieldPath: "metadata.labels",
  3121. },
  3122. }},
  3123. },
  3124. },
  3125. },
  3126. },
  3127. {
  3128. name: "downapi invalid positive item mode",
  3129. vol: core.Volume{
  3130. Name: "downapi",
  3131. VolumeSource: core.VolumeSource{
  3132. DownwardAPI: &core.DownwardAPIVolumeSource{
  3133. Items: []core.DownwardAPIVolumeFile{{
  3134. Mode: utilpointer.Int32Ptr(01000),
  3135. Path: "path",
  3136. FieldRef: &core.ObjectFieldSelector{
  3137. APIVersion: "v1",
  3138. FieldPath: "metadata.labels",
  3139. },
  3140. }},
  3141. },
  3142. },
  3143. },
  3144. errs: []verr{{
  3145. etype: field.ErrorTypeInvalid,
  3146. field: "downwardAPI.mode",
  3147. }},
  3148. },
  3149. {
  3150. name: "downapi invalid negative item mode",
  3151. vol: core.Volume{
  3152. Name: "downapi",
  3153. VolumeSource: core.VolumeSource{
  3154. DownwardAPI: &core.DownwardAPIVolumeSource{
  3155. Items: []core.DownwardAPIVolumeFile{{
  3156. Mode: utilpointer.Int32Ptr(-1),
  3157. Path: "path",
  3158. FieldRef: &core.ObjectFieldSelector{
  3159. APIVersion: "v1",
  3160. FieldPath: "metadata.labels",
  3161. },
  3162. }},
  3163. },
  3164. },
  3165. },
  3166. errs: []verr{{
  3167. etype: field.ErrorTypeInvalid,
  3168. field: "downwardAPI.mode",
  3169. }},
  3170. },
  3171. {
  3172. name: "downapi empty metatada path",
  3173. vol: core.Volume{
  3174. Name: "downapi",
  3175. VolumeSource: core.VolumeSource{
  3176. DownwardAPI: &core.DownwardAPIVolumeSource{
  3177. Items: []core.DownwardAPIVolumeFile{{
  3178. Path: "",
  3179. FieldRef: &core.ObjectFieldSelector{
  3180. APIVersion: "v1",
  3181. FieldPath: "metadata.labels",
  3182. },
  3183. }},
  3184. },
  3185. },
  3186. },
  3187. errs: []verr{{
  3188. etype: field.ErrorTypeRequired,
  3189. field: "downwardAPI.path",
  3190. }},
  3191. },
  3192. {
  3193. name: "downapi absolute path",
  3194. vol: core.Volume{
  3195. Name: "downapi",
  3196. VolumeSource: core.VolumeSource{
  3197. DownwardAPI: &core.DownwardAPIVolumeSource{
  3198. Items: []core.DownwardAPIVolumeFile{{
  3199. Path: "/absolutepath",
  3200. FieldRef: &core.ObjectFieldSelector{
  3201. APIVersion: "v1",
  3202. FieldPath: "metadata.labels",
  3203. },
  3204. }},
  3205. },
  3206. },
  3207. },
  3208. errs: []verr{{
  3209. etype: field.ErrorTypeInvalid,
  3210. field: "downwardAPI.path",
  3211. }},
  3212. },
  3213. {
  3214. name: "downapi dot dot path",
  3215. vol: core.Volume{
  3216. Name: "downapi",
  3217. VolumeSource: core.VolumeSource{
  3218. DownwardAPI: &core.DownwardAPIVolumeSource{
  3219. Items: []core.DownwardAPIVolumeFile{{
  3220. Path: "../../passwd",
  3221. FieldRef: &core.ObjectFieldSelector{
  3222. APIVersion: "v1",
  3223. FieldPath: "metadata.labels",
  3224. },
  3225. }},
  3226. },
  3227. },
  3228. },
  3229. errs: []verr{{
  3230. etype: field.ErrorTypeInvalid,
  3231. field: "downwardAPI.path",
  3232. detail: `must not contain '..'`,
  3233. }},
  3234. },
  3235. {
  3236. name: "downapi dot dot file name",
  3237. vol: core.Volume{
  3238. Name: "downapi",
  3239. VolumeSource: core.VolumeSource{
  3240. DownwardAPI: &core.DownwardAPIVolumeSource{
  3241. Items: []core.DownwardAPIVolumeFile{{
  3242. Path: "..badFileName",
  3243. FieldRef: &core.ObjectFieldSelector{
  3244. APIVersion: "v1",
  3245. FieldPath: "metadata.labels",
  3246. },
  3247. }},
  3248. },
  3249. },
  3250. },
  3251. errs: []verr{{
  3252. etype: field.ErrorTypeInvalid,
  3253. field: "downwardAPI.path",
  3254. detail: `must not start with '..'`,
  3255. }},
  3256. },
  3257. {
  3258. name: "downapi dot dot first level dirent",
  3259. vol: core.Volume{
  3260. Name: "downapi",
  3261. VolumeSource: core.VolumeSource{
  3262. DownwardAPI: &core.DownwardAPIVolumeSource{
  3263. Items: []core.DownwardAPIVolumeFile{{
  3264. Path: "..badDirName/goodFileName",
  3265. FieldRef: &core.ObjectFieldSelector{
  3266. APIVersion: "v1",
  3267. FieldPath: "metadata.labels",
  3268. },
  3269. }},
  3270. },
  3271. },
  3272. },
  3273. errs: []verr{{
  3274. etype: field.ErrorTypeInvalid,
  3275. field: "downwardAPI.path",
  3276. detail: `must not start with '..'`,
  3277. }},
  3278. },
  3279. {
  3280. name: "downapi fieldRef and ResourceFieldRef together",
  3281. vol: core.Volume{
  3282. Name: "downapi",
  3283. VolumeSource: core.VolumeSource{
  3284. DownwardAPI: &core.DownwardAPIVolumeSource{
  3285. Items: []core.DownwardAPIVolumeFile{{
  3286. Path: "test",
  3287. FieldRef: &core.ObjectFieldSelector{
  3288. APIVersion: "v1",
  3289. FieldPath: "metadata.labels",
  3290. },
  3291. ResourceFieldRef: &core.ResourceFieldSelector{
  3292. ContainerName: "test-container",
  3293. Resource: "requests.memory",
  3294. },
  3295. }},
  3296. },
  3297. },
  3298. },
  3299. errs: []verr{{
  3300. etype: field.ErrorTypeInvalid,
  3301. field: "downwardAPI",
  3302. detail: "fieldRef and resourceFieldRef can not be specified simultaneously",
  3303. }},
  3304. },
  3305. {
  3306. name: "downapi invalid positive defaultMode",
  3307. vol: core.Volume{
  3308. Name: "downapi",
  3309. VolumeSource: core.VolumeSource{
  3310. DownwardAPI: &core.DownwardAPIVolumeSource{
  3311. DefaultMode: utilpointer.Int32Ptr(01000),
  3312. },
  3313. },
  3314. },
  3315. errs: []verr{{
  3316. etype: field.ErrorTypeInvalid,
  3317. field: "downwardAPI.defaultMode",
  3318. }},
  3319. },
  3320. {
  3321. name: "downapi invalid negative defaultMode",
  3322. vol: core.Volume{
  3323. Name: "downapi",
  3324. VolumeSource: core.VolumeSource{
  3325. DownwardAPI: &core.DownwardAPIVolumeSource{
  3326. DefaultMode: utilpointer.Int32Ptr(-1),
  3327. },
  3328. },
  3329. },
  3330. errs: []verr{{
  3331. etype: field.ErrorTypeInvalid,
  3332. field: "downwardAPI.defaultMode",
  3333. }},
  3334. },
  3335. // FC
  3336. {
  3337. name: "FC valid targetWWNs and lun",
  3338. vol: core.Volume{
  3339. Name: "fc",
  3340. VolumeSource: core.VolumeSource{
  3341. FC: &core.FCVolumeSource{
  3342. TargetWWNs: []string{"some_wwn"},
  3343. Lun: utilpointer.Int32Ptr(1),
  3344. FSType: "ext4",
  3345. ReadOnly: false,
  3346. },
  3347. },
  3348. },
  3349. },
  3350. {
  3351. name: "FC valid wwids",
  3352. vol: core.Volume{
  3353. Name: "fc",
  3354. VolumeSource: core.VolumeSource{
  3355. FC: &core.FCVolumeSource{
  3356. WWIDs: []string{"some_wwid"},
  3357. FSType: "ext4",
  3358. ReadOnly: false,
  3359. },
  3360. },
  3361. },
  3362. },
  3363. {
  3364. name: "FC empty targetWWNs and wwids",
  3365. vol: core.Volume{
  3366. Name: "fc",
  3367. VolumeSource: core.VolumeSource{
  3368. FC: &core.FCVolumeSource{
  3369. TargetWWNs: []string{},
  3370. Lun: utilpointer.Int32Ptr(1),
  3371. WWIDs: []string{},
  3372. FSType: "ext4",
  3373. ReadOnly: false,
  3374. },
  3375. },
  3376. },
  3377. errs: []verr{{
  3378. etype: field.ErrorTypeRequired,
  3379. field: "fc.targetWWNs",
  3380. detail: "must specify either targetWWNs or wwids",
  3381. }},
  3382. },
  3383. {
  3384. name: "FC invalid: both targetWWNs and wwids simultaneously",
  3385. vol: core.Volume{
  3386. Name: "fc",
  3387. VolumeSource: core.VolumeSource{
  3388. FC: &core.FCVolumeSource{
  3389. TargetWWNs: []string{"some_wwn"},
  3390. Lun: utilpointer.Int32Ptr(1),
  3391. WWIDs: []string{"some_wwid"},
  3392. FSType: "ext4",
  3393. ReadOnly: false,
  3394. },
  3395. },
  3396. },
  3397. errs: []verr{{
  3398. etype: field.ErrorTypeInvalid,
  3399. field: "fc.targetWWNs",
  3400. detail: "targetWWNs and wwids can not be specified simultaneously",
  3401. }},
  3402. },
  3403. {
  3404. name: "FC valid targetWWNs and empty lun",
  3405. vol: core.Volume{
  3406. Name: "fc",
  3407. VolumeSource: core.VolumeSource{
  3408. FC: &core.FCVolumeSource{
  3409. TargetWWNs: []string{"wwn"},
  3410. Lun: nil,
  3411. FSType: "ext4",
  3412. ReadOnly: false,
  3413. },
  3414. },
  3415. },
  3416. errs: []verr{{
  3417. etype: field.ErrorTypeRequired,
  3418. field: "fc.lun",
  3419. detail: "lun is required if targetWWNs is specified",
  3420. }},
  3421. },
  3422. {
  3423. name: "FC valid targetWWNs and invalid lun",
  3424. vol: core.Volume{
  3425. Name: "fc",
  3426. VolumeSource: core.VolumeSource{
  3427. FC: &core.FCVolumeSource{
  3428. TargetWWNs: []string{"wwn"},
  3429. Lun: utilpointer.Int32Ptr(256),
  3430. FSType: "ext4",
  3431. ReadOnly: false,
  3432. },
  3433. },
  3434. },
  3435. errs: []verr{{
  3436. etype: field.ErrorTypeInvalid,
  3437. field: "fc.lun",
  3438. detail: validation.InclusiveRangeError(0, 255),
  3439. }},
  3440. },
  3441. // FlexVolume
  3442. {
  3443. name: "valid FlexVolume",
  3444. vol: core.Volume{
  3445. Name: "flex-volume",
  3446. VolumeSource: core.VolumeSource{
  3447. FlexVolume: &core.FlexVolumeSource{
  3448. Driver: "kubernetes.io/blue",
  3449. FSType: "ext4",
  3450. },
  3451. },
  3452. },
  3453. },
  3454. // AzureFile
  3455. {
  3456. name: "valid AzureFile",
  3457. vol: core.Volume{
  3458. Name: "azure-file",
  3459. VolumeSource: core.VolumeSource{
  3460. AzureFile: &core.AzureFileVolumeSource{
  3461. SecretName: "key",
  3462. ShareName: "share",
  3463. ReadOnly: false,
  3464. },
  3465. },
  3466. },
  3467. },
  3468. {
  3469. name: "AzureFile empty secret",
  3470. vol: core.Volume{
  3471. Name: "azure-file",
  3472. VolumeSource: core.VolumeSource{
  3473. AzureFile: &core.AzureFileVolumeSource{
  3474. SecretName: "",
  3475. ShareName: "share",
  3476. ReadOnly: false,
  3477. },
  3478. },
  3479. },
  3480. errs: []verr{{
  3481. etype: field.ErrorTypeRequired,
  3482. field: "azureFile.secretName",
  3483. }},
  3484. },
  3485. {
  3486. name: "AzureFile empty share",
  3487. vol: core.Volume{
  3488. Name: "azure-file",
  3489. VolumeSource: core.VolumeSource{
  3490. AzureFile: &core.AzureFileVolumeSource{
  3491. SecretName: "name",
  3492. ShareName: "",
  3493. ReadOnly: false,
  3494. },
  3495. },
  3496. },
  3497. errs: []verr{{
  3498. etype: field.ErrorTypeRequired,
  3499. field: "azureFile.shareName",
  3500. }},
  3501. },
  3502. // Quobyte
  3503. {
  3504. name: "valid Quobyte",
  3505. vol: core.Volume{
  3506. Name: "quobyte",
  3507. VolumeSource: core.VolumeSource{
  3508. Quobyte: &core.QuobyteVolumeSource{
  3509. Registry: "registry:7861",
  3510. Volume: "volume",
  3511. ReadOnly: false,
  3512. User: "root",
  3513. Group: "root",
  3514. Tenant: "ThisIsSomeTenantUUID",
  3515. },
  3516. },
  3517. },
  3518. },
  3519. {
  3520. name: "empty registry quobyte",
  3521. vol: core.Volume{
  3522. Name: "quobyte",
  3523. VolumeSource: core.VolumeSource{
  3524. Quobyte: &core.QuobyteVolumeSource{
  3525. Volume: "/test",
  3526. Tenant: "ThisIsSomeTenantUUID",
  3527. },
  3528. },
  3529. },
  3530. errs: []verr{{
  3531. etype: field.ErrorTypeRequired,
  3532. field: "quobyte.registry",
  3533. }},
  3534. },
  3535. {
  3536. name: "wrong format registry quobyte",
  3537. vol: core.Volume{
  3538. Name: "quobyte",
  3539. VolumeSource: core.VolumeSource{
  3540. Quobyte: &core.QuobyteVolumeSource{
  3541. Registry: "registry7861",
  3542. Volume: "/test",
  3543. Tenant: "ThisIsSomeTenantUUID",
  3544. },
  3545. },
  3546. },
  3547. errs: []verr{{
  3548. etype: field.ErrorTypeInvalid,
  3549. field: "quobyte.registry",
  3550. }},
  3551. },
  3552. {
  3553. name: "wrong format multiple registries quobyte",
  3554. vol: core.Volume{
  3555. Name: "quobyte",
  3556. VolumeSource: core.VolumeSource{
  3557. Quobyte: &core.QuobyteVolumeSource{
  3558. Registry: "registry:7861,reg2",
  3559. Volume: "/test",
  3560. Tenant: "ThisIsSomeTenantUUID",
  3561. },
  3562. },
  3563. },
  3564. errs: []verr{{
  3565. etype: field.ErrorTypeInvalid,
  3566. field: "quobyte.registry",
  3567. }},
  3568. },
  3569. {
  3570. name: "empty volume quobyte",
  3571. vol: core.Volume{
  3572. Name: "quobyte",
  3573. VolumeSource: core.VolumeSource{
  3574. Quobyte: &core.QuobyteVolumeSource{
  3575. Registry: "registry:7861",
  3576. Tenant: "ThisIsSomeTenantUUID",
  3577. },
  3578. },
  3579. },
  3580. errs: []verr{{
  3581. etype: field.ErrorTypeRequired,
  3582. field: "quobyte.volume",
  3583. }},
  3584. },
  3585. {
  3586. name: "empty tenant quobyte",
  3587. vol: core.Volume{
  3588. Name: "quobyte",
  3589. VolumeSource: core.VolumeSource{
  3590. Quobyte: &core.QuobyteVolumeSource{
  3591. Registry: "registry:7861",
  3592. Volume: "/test",
  3593. Tenant: "",
  3594. },
  3595. },
  3596. },
  3597. },
  3598. {
  3599. name: "too long tenant quobyte",
  3600. vol: core.Volume{
  3601. Name: "quobyte",
  3602. VolumeSource: core.VolumeSource{
  3603. Quobyte: &core.QuobyteVolumeSource{
  3604. Registry: "registry:7861",
  3605. Volume: "/test",
  3606. Tenant: "this is too long to be a valid uuid so this test has to fail on the maximum length validation of the tenant.",
  3607. },
  3608. },
  3609. },
  3610. errs: []verr{{
  3611. etype: field.ErrorTypeRequired,
  3612. field: "quobyte.tenant",
  3613. }},
  3614. },
  3615. // AzureDisk
  3616. {
  3617. name: "valid AzureDisk",
  3618. vol: core.Volume{
  3619. Name: "azure-disk",
  3620. VolumeSource: core.VolumeSource{
  3621. AzureDisk: &core.AzureDiskVolumeSource{
  3622. DiskName: "foo",
  3623. DataDiskURI: "https://blob/vhds/bar.vhd",
  3624. },
  3625. },
  3626. },
  3627. },
  3628. {
  3629. name: "AzureDisk empty disk name",
  3630. vol: core.Volume{
  3631. Name: "azure-disk",
  3632. VolumeSource: core.VolumeSource{
  3633. AzureDisk: &core.AzureDiskVolumeSource{
  3634. DiskName: "",
  3635. DataDiskURI: "https://blob/vhds/bar.vhd",
  3636. },
  3637. },
  3638. },
  3639. errs: []verr{{
  3640. etype: field.ErrorTypeRequired,
  3641. field: "azureDisk.diskName",
  3642. }},
  3643. },
  3644. {
  3645. name: "AzureDisk empty disk uri",
  3646. vol: core.Volume{
  3647. Name: "azure-disk",
  3648. VolumeSource: core.VolumeSource{
  3649. AzureDisk: &core.AzureDiskVolumeSource{
  3650. DiskName: "foo",
  3651. DataDiskURI: "",
  3652. },
  3653. },
  3654. },
  3655. errs: []verr{{
  3656. etype: field.ErrorTypeRequired,
  3657. field: "azureDisk.diskURI",
  3658. }},
  3659. },
  3660. // ScaleIO
  3661. {
  3662. name: "valid scaleio volume",
  3663. vol: core.Volume{
  3664. Name: "scaleio-volume",
  3665. VolumeSource: core.VolumeSource{
  3666. ScaleIO: &core.ScaleIOVolumeSource{
  3667. Gateway: "http://abcd/efg",
  3668. System: "test-system",
  3669. VolumeName: "test-vol-1",
  3670. },
  3671. },
  3672. },
  3673. },
  3674. {
  3675. name: "ScaleIO with empty name",
  3676. vol: core.Volume{
  3677. Name: "scaleio-volume",
  3678. VolumeSource: core.VolumeSource{
  3679. ScaleIO: &core.ScaleIOVolumeSource{
  3680. Gateway: "http://abcd/efg",
  3681. System: "test-system",
  3682. VolumeName: "",
  3683. },
  3684. },
  3685. },
  3686. errs: []verr{{
  3687. etype: field.ErrorTypeRequired,
  3688. field: "scaleIO.volumeName",
  3689. }},
  3690. },
  3691. {
  3692. name: "ScaleIO with empty gateway",
  3693. vol: core.Volume{
  3694. Name: "scaleio-volume",
  3695. VolumeSource: core.VolumeSource{
  3696. ScaleIO: &core.ScaleIOVolumeSource{
  3697. Gateway: "",
  3698. System: "test-system",
  3699. VolumeName: "test-vol-1",
  3700. },
  3701. },
  3702. },
  3703. errs: []verr{{
  3704. etype: field.ErrorTypeRequired,
  3705. field: "scaleIO.gateway",
  3706. }},
  3707. },
  3708. {
  3709. name: "ScaleIO with empty system",
  3710. vol: core.Volume{
  3711. Name: "scaleio-volume",
  3712. VolumeSource: core.VolumeSource{
  3713. ScaleIO: &core.ScaleIOVolumeSource{
  3714. Gateway: "http://agc/efg/gateway",
  3715. System: "",
  3716. VolumeName: "test-vol-1",
  3717. },
  3718. },
  3719. },
  3720. errs: []verr{{
  3721. etype: field.ErrorTypeRequired,
  3722. field: "scaleIO.system",
  3723. }},
  3724. },
  3725. // ProjectedVolumeSource
  3726. {
  3727. name: "ProjectedVolumeSource more than one projection in a source",
  3728. vol: core.Volume{
  3729. Name: "projected-volume",
  3730. VolumeSource: core.VolumeSource{
  3731. Projected: &core.ProjectedVolumeSource{
  3732. Sources: []core.VolumeProjection{
  3733. {
  3734. Secret: &core.SecretProjection{
  3735. LocalObjectReference: core.LocalObjectReference{
  3736. Name: "foo",
  3737. },
  3738. },
  3739. },
  3740. {
  3741. Secret: &core.SecretProjection{
  3742. LocalObjectReference: core.LocalObjectReference{
  3743. Name: "foo",
  3744. },
  3745. },
  3746. DownwardAPI: &core.DownwardAPIProjection{},
  3747. },
  3748. },
  3749. },
  3750. },
  3751. },
  3752. errs: []verr{{
  3753. etype: field.ErrorTypeForbidden,
  3754. field: "projected.sources[1]",
  3755. }},
  3756. },
  3757. {
  3758. name: "ProjectedVolumeSource more than one projection in a source",
  3759. vol: core.Volume{
  3760. Name: "projected-volume",
  3761. VolumeSource: core.VolumeSource{
  3762. Projected: &core.ProjectedVolumeSource{
  3763. Sources: []core.VolumeProjection{
  3764. {
  3765. Secret: &core.SecretProjection{},
  3766. },
  3767. {
  3768. Secret: &core.SecretProjection{},
  3769. DownwardAPI: &core.DownwardAPIProjection{},
  3770. },
  3771. },
  3772. },
  3773. },
  3774. },
  3775. errs: []verr{
  3776. {
  3777. etype: field.ErrorTypeRequired,
  3778. field: "projected.sources[0].secret.name",
  3779. },
  3780. {
  3781. etype: field.ErrorTypeRequired,
  3782. field: "projected.sources[1].secret.name",
  3783. },
  3784. {
  3785. etype: field.ErrorTypeForbidden,
  3786. field: "projected.sources[1]",
  3787. },
  3788. },
  3789. },
  3790. }
  3791. for _, tc := range testCases {
  3792. t.Run(tc.name, func(t *testing.T) {
  3793. names, errs := ValidateVolumes([]core.Volume{tc.vol}, field.NewPath("field"))
  3794. if len(errs) != len(tc.errs) {
  3795. t.Fatalf("unexpected error(s): got %d, want %d: %v", len(tc.errs), len(errs), errs)
  3796. }
  3797. if len(errs) == 0 && (len(names) > 1 || !IsMatchedVolume(tc.vol.Name, names)) {
  3798. t.Errorf("wrong names result: %v", names)
  3799. }
  3800. for i, err := range errs {
  3801. expErr := tc.errs[i]
  3802. if err.Type != expErr.etype {
  3803. t.Errorf("unexpected error type: got %v, want %v", expErr.etype, err.Type)
  3804. }
  3805. if !strings.HasSuffix(err.Field, "."+expErr.field) {
  3806. t.Errorf("unexpected error field: got %v, want %v", expErr.field, err.Field)
  3807. }
  3808. if !strings.Contains(err.Detail, expErr.detail) {
  3809. t.Errorf("unexpected error detail: got %v, want %v", expErr.detail, err.Detail)
  3810. }
  3811. }
  3812. })
  3813. }
  3814. dupsCase := []core.Volume{
  3815. {Name: "abc", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}},
  3816. {Name: "abc", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}},
  3817. }
  3818. _, errs := ValidateVolumes(dupsCase, field.NewPath("field"))
  3819. if len(errs) == 0 {
  3820. t.Errorf("expected error")
  3821. } else if len(errs) != 1 {
  3822. t.Errorf("expected 1 error, got %d: %v", len(errs), errs)
  3823. } else if errs[0].Type != field.ErrorTypeDuplicate {
  3824. t.Errorf("expected error type %v, got %v", field.ErrorTypeDuplicate, errs[0].Type)
  3825. }
  3826. // Validate HugePages medium type for EmptyDir
  3827. hugePagesCase := core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{Medium: core.StorageMediumHugePages}}
  3828. // Enable HugePages
  3829. if errs := validateVolumeSource(&hugePagesCase, field.NewPath("field").Index(0), "working"); len(errs) != 0 {
  3830. t.Errorf("Unexpected error when HugePages feature is enabled.")
  3831. }
  3832. }
  3833. func TestHugePagesIsolation(t *testing.T) {
  3834. successCases := []core.Pod{
  3835. { // Basic fields.
  3836. ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
  3837. Spec: core.PodSpec{
  3838. Containers: []core.Container{
  3839. {
  3840. Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
  3841. Resources: core.ResourceRequirements{
  3842. Requests: core.ResourceList{
  3843. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  3844. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  3845. core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
  3846. },
  3847. Limits: core.ResourceList{
  3848. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  3849. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  3850. core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
  3851. },
  3852. },
  3853. },
  3854. },
  3855. RestartPolicy: core.RestartPolicyAlways,
  3856. DNSPolicy: core.DNSClusterFirst,
  3857. },
  3858. },
  3859. }
  3860. failureCases := []core.Pod{
  3861. { // Basic fields.
  3862. ObjectMeta: metav1.ObjectMeta{Name: "hugepages-requireCpuOrMemory", Namespace: "ns"},
  3863. Spec: core.PodSpec{
  3864. Containers: []core.Container{
  3865. {
  3866. Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
  3867. Resources: core.ResourceRequirements{
  3868. Requests: core.ResourceList{
  3869. core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
  3870. },
  3871. Limits: core.ResourceList{
  3872. core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
  3873. },
  3874. },
  3875. },
  3876. },
  3877. RestartPolicy: core.RestartPolicyAlways,
  3878. DNSPolicy: core.DNSClusterFirst,
  3879. },
  3880. },
  3881. { // Basic fields.
  3882. ObjectMeta: metav1.ObjectMeta{Name: "hugepages-shared", Namespace: "ns"},
  3883. Spec: core.PodSpec{
  3884. Containers: []core.Container{
  3885. {
  3886. Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
  3887. Resources: core.ResourceRequirements{
  3888. Requests: core.ResourceList{
  3889. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  3890. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  3891. core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
  3892. },
  3893. Limits: core.ResourceList{
  3894. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  3895. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  3896. core.ResourceName("hugepages-2Mi"): resource.MustParse("2Gi"),
  3897. },
  3898. },
  3899. },
  3900. },
  3901. RestartPolicy: core.RestartPolicyAlways,
  3902. DNSPolicy: core.DNSClusterFirst,
  3903. },
  3904. },
  3905. { // Basic fields.
  3906. ObjectMeta: metav1.ObjectMeta{Name: "hugepages-multiple", Namespace: "ns"},
  3907. Spec: core.PodSpec{
  3908. Containers: []core.Container{
  3909. {
  3910. Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
  3911. Resources: core.ResourceRequirements{
  3912. Requests: core.ResourceList{
  3913. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  3914. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  3915. core.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
  3916. },
  3917. Limits: core.ResourceList{
  3918. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  3919. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  3920. core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
  3921. core.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
  3922. },
  3923. },
  3924. },
  3925. },
  3926. RestartPolicy: core.RestartPolicyAlways,
  3927. DNSPolicy: core.DNSClusterFirst,
  3928. },
  3929. },
  3930. }
  3931. for i := range successCases {
  3932. pod := &successCases[i]
  3933. if errs := ValidatePod(pod); len(errs) != 0 {
  3934. t.Errorf("Unexpected error for case[%d], err: %v", i, errs)
  3935. }
  3936. }
  3937. for i := range failureCases {
  3938. pod := &failureCases[i]
  3939. if errs := ValidatePod(pod); len(errs) == 0 {
  3940. t.Errorf("Expected error for case[%d], pod: %v", i, pod.Name)
  3941. }
  3942. }
  3943. }
  3944. func TestPVCVolumeMode(t *testing.T) {
  3945. // Enable feature BlockVolume for PVC
  3946. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.BlockVolume, true)()
  3947. block := core.PersistentVolumeBlock
  3948. file := core.PersistentVolumeFilesystem
  3949. fake := core.PersistentVolumeMode("fake")
  3950. empty := core.PersistentVolumeMode("")
  3951. // Success Cases
  3952. successCasesPVC := map[string]*core.PersistentVolumeClaim{
  3953. "valid block value": createTestVolModePVC(&block),
  3954. "valid filesystem value": createTestVolModePVC(&file),
  3955. "valid nil value": createTestVolModePVC(nil),
  3956. }
  3957. for k, v := range successCasesPVC {
  3958. if errs := ValidatePersistentVolumeClaim(v); len(errs) != 0 {
  3959. t.Errorf("expected success for %s", k)
  3960. }
  3961. }
  3962. // Error Cases
  3963. errorCasesPVC := map[string]*core.PersistentVolumeClaim{
  3964. "invalid value": createTestVolModePVC(&fake),
  3965. "empty value": createTestVolModePVC(&empty),
  3966. }
  3967. for k, v := range errorCasesPVC {
  3968. if errs := ValidatePersistentVolumeClaim(v); len(errs) == 0 {
  3969. t.Errorf("expected failure for %s", k)
  3970. }
  3971. }
  3972. }
  3973. func TestPVVolumeMode(t *testing.T) {
  3974. // Enable feature BlockVolume for PVC
  3975. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.BlockVolume, true)()
  3976. block := core.PersistentVolumeBlock
  3977. file := core.PersistentVolumeFilesystem
  3978. fake := core.PersistentVolumeMode("fake")
  3979. empty := core.PersistentVolumeMode("")
  3980. // Success Cases
  3981. successCasesPV := map[string]*core.PersistentVolume{
  3982. "valid block value": createTestVolModePV(&block),
  3983. "valid filesystem value": createTestVolModePV(&file),
  3984. "valid nil value": createTestVolModePV(nil),
  3985. }
  3986. for k, v := range successCasesPV {
  3987. if errs := ValidatePersistentVolume(v); len(errs) != 0 {
  3988. t.Errorf("expected success for %s", k)
  3989. }
  3990. }
  3991. // Error Cases
  3992. errorCasesPV := map[string]*core.PersistentVolume{
  3993. "invalid value": createTestVolModePV(&fake),
  3994. "empty value": createTestVolModePV(&empty),
  3995. }
  3996. for k, v := range errorCasesPV {
  3997. if errs := ValidatePersistentVolume(v); len(errs) == 0 {
  3998. t.Errorf("expected failure for %s", k)
  3999. }
  4000. }
  4001. }
  4002. func createTestVolModePVC(vmode *core.PersistentVolumeMode) *core.PersistentVolumeClaim {
  4003. validName := "valid-storage-class"
  4004. pvc := core.PersistentVolumeClaim{
  4005. ObjectMeta: metav1.ObjectMeta{
  4006. Name: "foo",
  4007. Namespace: "default",
  4008. },
  4009. Spec: core.PersistentVolumeClaimSpec{
  4010. Resources: core.ResourceRequirements{
  4011. Requests: core.ResourceList{
  4012. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  4013. },
  4014. },
  4015. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  4016. StorageClassName: &validName,
  4017. VolumeMode: vmode,
  4018. },
  4019. }
  4020. return &pvc
  4021. }
  4022. func createTestVolModePV(vmode *core.PersistentVolumeMode) *core.PersistentVolume {
  4023. // PersistentVolume with VolumeMode set (valid and invalid)
  4024. pv := core.PersistentVolume{
  4025. ObjectMeta: metav1.ObjectMeta{
  4026. Name: "foo",
  4027. Namespace: "",
  4028. },
  4029. Spec: core.PersistentVolumeSpec{
  4030. Capacity: core.ResourceList{
  4031. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  4032. },
  4033. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  4034. PersistentVolumeSource: core.PersistentVolumeSource{
  4035. HostPath: &core.HostPathVolumeSource{
  4036. Path: "/foo",
  4037. Type: newHostPathType(string(core.HostPathDirectory)),
  4038. },
  4039. },
  4040. StorageClassName: "test-storage-class",
  4041. VolumeMode: vmode,
  4042. },
  4043. }
  4044. return &pv
  4045. }
  4046. func createTestPV() *core.PersistentVolume {
  4047. // PersistentVolume with VolumeMode set (valid and invalid)
  4048. pv := core.PersistentVolume{
  4049. ObjectMeta: metav1.ObjectMeta{
  4050. Name: "foo",
  4051. Namespace: "",
  4052. },
  4053. Spec: core.PersistentVolumeSpec{
  4054. Capacity: core.ResourceList{
  4055. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  4056. },
  4057. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  4058. PersistentVolumeSource: core.PersistentVolumeSource{
  4059. HostPath: &core.HostPathVolumeSource{
  4060. Path: "/foo",
  4061. Type: newHostPathType(string(core.HostPathDirectory)),
  4062. },
  4063. },
  4064. StorageClassName: "test-storage-class",
  4065. },
  4066. }
  4067. return &pv
  4068. }
  4069. func TestAlphaLocalStorageCapacityIsolation(t *testing.T) {
  4070. testCases := []core.VolumeSource{
  4071. {EmptyDir: &core.EmptyDirVolumeSource{SizeLimit: resource.NewQuantity(int64(5), resource.BinarySI)}},
  4072. }
  4073. for _, tc := range testCases {
  4074. if errs := validateVolumeSource(&tc, field.NewPath("spec"), "tmpvol"); len(errs) != 0 {
  4075. t.Errorf("expected success: %v", errs)
  4076. }
  4077. }
  4078. containerLimitCase := core.ResourceRequirements{
  4079. Limits: core.ResourceList{
  4080. core.ResourceEphemeralStorage: *resource.NewMilliQuantity(
  4081. int64(40000),
  4082. resource.BinarySI),
  4083. },
  4084. }
  4085. if errs := ValidateResourceRequirements(&containerLimitCase, field.NewPath("resources")); len(errs) != 0 {
  4086. t.Errorf("expected success: %v", errs)
  4087. }
  4088. }
  4089. func TestValidateResourceQuotaWithAlphaLocalStorageCapacityIsolation(t *testing.T) {
  4090. spec := core.ResourceQuotaSpec{
  4091. Hard: core.ResourceList{
  4092. core.ResourceCPU: resource.MustParse("100"),
  4093. core.ResourceMemory: resource.MustParse("10000"),
  4094. core.ResourceRequestsCPU: resource.MustParse("100"),
  4095. core.ResourceRequestsMemory: resource.MustParse("10000"),
  4096. core.ResourceLimitsCPU: resource.MustParse("100"),
  4097. core.ResourceLimitsMemory: resource.MustParse("10000"),
  4098. core.ResourcePods: resource.MustParse("10"),
  4099. core.ResourceServices: resource.MustParse("0"),
  4100. core.ResourceReplicationControllers: resource.MustParse("10"),
  4101. core.ResourceQuotas: resource.MustParse("10"),
  4102. core.ResourceConfigMaps: resource.MustParse("10"),
  4103. core.ResourceSecrets: resource.MustParse("10"),
  4104. core.ResourceEphemeralStorage: resource.MustParse("10000"),
  4105. core.ResourceRequestsEphemeralStorage: resource.MustParse("10000"),
  4106. core.ResourceLimitsEphemeralStorage: resource.MustParse("10000"),
  4107. },
  4108. }
  4109. resourceQuota := &core.ResourceQuota{
  4110. ObjectMeta: metav1.ObjectMeta{
  4111. Name: "abc",
  4112. Namespace: "foo",
  4113. },
  4114. Spec: spec,
  4115. }
  4116. if errs := ValidateResourceQuota(resourceQuota); len(errs) != 0 {
  4117. t.Errorf("expected success: %v", errs)
  4118. }
  4119. }
  4120. func TestValidatePorts(t *testing.T) {
  4121. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SCTPSupport, true)()
  4122. successCase := []core.ContainerPort{
  4123. {Name: "abc", ContainerPort: 80, HostPort: 80, Protocol: "TCP"},
  4124. {Name: "easy", ContainerPort: 82, Protocol: "TCP"},
  4125. {Name: "as", ContainerPort: 83, Protocol: "UDP"},
  4126. {Name: "do-re-me", ContainerPort: 84, Protocol: "UDP"},
  4127. {ContainerPort: 85, Protocol: "TCP"},
  4128. }
  4129. if errs := validateContainerPorts(successCase, field.NewPath("field")); len(errs) != 0 {
  4130. t.Errorf("expected success: %v", errs)
  4131. }
  4132. nonCanonicalCase := []core.ContainerPort{
  4133. {ContainerPort: 80, Protocol: "TCP"},
  4134. }
  4135. if errs := validateContainerPorts(nonCanonicalCase, field.NewPath("field")); len(errs) != 0 {
  4136. t.Errorf("expected success: %v", errs)
  4137. }
  4138. errorCases := map[string]struct {
  4139. P []core.ContainerPort
  4140. T field.ErrorType
  4141. F string
  4142. D string
  4143. }{
  4144. "name > 15 characters": {
  4145. []core.ContainerPort{{Name: strings.Repeat("a", 16), ContainerPort: 80, Protocol: "TCP"}},
  4146. field.ErrorTypeInvalid,
  4147. "name", "15",
  4148. },
  4149. "name contains invalid characters": {
  4150. []core.ContainerPort{{Name: "a.b.c", ContainerPort: 80, Protocol: "TCP"}},
  4151. field.ErrorTypeInvalid,
  4152. "name", "alpha-numeric",
  4153. },
  4154. "name is a number": {
  4155. []core.ContainerPort{{Name: "80", ContainerPort: 80, Protocol: "TCP"}},
  4156. field.ErrorTypeInvalid,
  4157. "name", "at least one letter",
  4158. },
  4159. "name not unique": {
  4160. []core.ContainerPort{
  4161. {Name: "abc", ContainerPort: 80, Protocol: "TCP"},
  4162. {Name: "abc", ContainerPort: 81, Protocol: "TCP"},
  4163. },
  4164. field.ErrorTypeDuplicate,
  4165. "[1].name", "",
  4166. },
  4167. "zero container port": {
  4168. []core.ContainerPort{{ContainerPort: 0, Protocol: "TCP"}},
  4169. field.ErrorTypeRequired,
  4170. "containerPort", "",
  4171. },
  4172. "invalid container port": {
  4173. []core.ContainerPort{{ContainerPort: 65536, Protocol: "TCP"}},
  4174. field.ErrorTypeInvalid,
  4175. "containerPort", "between",
  4176. },
  4177. "invalid host port": {
  4178. []core.ContainerPort{{ContainerPort: 80, HostPort: 65536, Protocol: "TCP"}},
  4179. field.ErrorTypeInvalid,
  4180. "hostPort", "between",
  4181. },
  4182. "invalid protocol case": {
  4183. []core.ContainerPort{{ContainerPort: 80, Protocol: "tcp"}},
  4184. field.ErrorTypeNotSupported,
  4185. "protocol", `supported values: "SCTP", "TCP", "UDP"`,
  4186. },
  4187. "invalid protocol": {
  4188. []core.ContainerPort{{ContainerPort: 80, Protocol: "ICMP"}},
  4189. field.ErrorTypeNotSupported,
  4190. "protocol", `supported values: "SCTP", "TCP", "UDP"`,
  4191. },
  4192. "protocol required": {
  4193. []core.ContainerPort{{Name: "abc", ContainerPort: 80}},
  4194. field.ErrorTypeRequired,
  4195. "protocol", "",
  4196. },
  4197. }
  4198. for k, v := range errorCases {
  4199. errs := validateContainerPorts(v.P, field.NewPath("field"))
  4200. if len(errs) == 0 {
  4201. t.Errorf("expected failure for %s", k)
  4202. }
  4203. for i := range errs {
  4204. if errs[i].Type != v.T {
  4205. t.Errorf("%s: expected error to have type %q: %q", k, v.T, errs[i].Type)
  4206. }
  4207. if !strings.Contains(errs[i].Field, v.F) {
  4208. t.Errorf("%s: expected error field %q: %q", k, v.F, errs[i].Field)
  4209. }
  4210. if !strings.Contains(errs[i].Detail, v.D) {
  4211. t.Errorf("%s: expected error detail %q, got %q", k, v.D, errs[i].Detail)
  4212. }
  4213. }
  4214. }
  4215. }
  4216. func TestLocalStorageEnvWithFeatureGate(t *testing.T) {
  4217. testCases := []core.EnvVar{
  4218. {
  4219. Name: "ephemeral-storage-limits",
  4220. ValueFrom: &core.EnvVarSource{
  4221. ResourceFieldRef: &core.ResourceFieldSelector{
  4222. ContainerName: "test-container",
  4223. Resource: "limits.ephemeral-storage",
  4224. },
  4225. },
  4226. },
  4227. {
  4228. Name: "ephemeral-storage-requests",
  4229. ValueFrom: &core.EnvVarSource{
  4230. ResourceFieldRef: &core.ResourceFieldSelector{
  4231. ContainerName: "test-container",
  4232. Resource: "requests.ephemeral-storage",
  4233. },
  4234. },
  4235. },
  4236. }
  4237. for _, testCase := range testCases {
  4238. if errs := validateEnvVarValueFrom(testCase, field.NewPath("field")); len(errs) != 0 {
  4239. t.Errorf("expected success, got: %v", errs)
  4240. }
  4241. }
  4242. }
  4243. func TestValidateEnv(t *testing.T) {
  4244. successCase := []core.EnvVar{
  4245. {Name: "abc", Value: "value"},
  4246. {Name: "ABC", Value: "value"},
  4247. {Name: "AbC_123", Value: "value"},
  4248. {Name: "abc", Value: ""},
  4249. {Name: "a.b.c", Value: "value"},
  4250. {Name: "a-b-c", Value: "value"},
  4251. {
  4252. Name: "abc",
  4253. ValueFrom: &core.EnvVarSource{
  4254. FieldRef: &core.ObjectFieldSelector{
  4255. APIVersion: "v1",
  4256. FieldPath: "metadata.annotations['key']",
  4257. },
  4258. },
  4259. },
  4260. {
  4261. Name: "abc",
  4262. ValueFrom: &core.EnvVarSource{
  4263. FieldRef: &core.ObjectFieldSelector{
  4264. APIVersion: "v1",
  4265. FieldPath: "metadata.labels['key']",
  4266. },
  4267. },
  4268. },
  4269. {
  4270. Name: "abc",
  4271. ValueFrom: &core.EnvVarSource{
  4272. FieldRef: &core.ObjectFieldSelector{
  4273. APIVersion: "v1",
  4274. FieldPath: "metadata.name",
  4275. },
  4276. },
  4277. },
  4278. {
  4279. Name: "abc",
  4280. ValueFrom: &core.EnvVarSource{
  4281. FieldRef: &core.ObjectFieldSelector{
  4282. APIVersion: "v1",
  4283. FieldPath: "metadata.namespace",
  4284. },
  4285. },
  4286. },
  4287. {
  4288. Name: "abc",
  4289. ValueFrom: &core.EnvVarSource{
  4290. FieldRef: &core.ObjectFieldSelector{
  4291. APIVersion: "v1",
  4292. FieldPath: "metadata.uid",
  4293. },
  4294. },
  4295. },
  4296. {
  4297. Name: "abc",
  4298. ValueFrom: &core.EnvVarSource{
  4299. FieldRef: &core.ObjectFieldSelector{
  4300. APIVersion: "v1",
  4301. FieldPath: "spec.nodeName",
  4302. },
  4303. },
  4304. },
  4305. {
  4306. Name: "abc",
  4307. ValueFrom: &core.EnvVarSource{
  4308. FieldRef: &core.ObjectFieldSelector{
  4309. APIVersion: "v1",
  4310. FieldPath: "spec.serviceAccountName",
  4311. },
  4312. },
  4313. },
  4314. {
  4315. Name: "abc",
  4316. ValueFrom: &core.EnvVarSource{
  4317. FieldRef: &core.ObjectFieldSelector{
  4318. APIVersion: "v1",
  4319. FieldPath: "status.hostIP",
  4320. },
  4321. },
  4322. },
  4323. {
  4324. Name: "abc",
  4325. ValueFrom: &core.EnvVarSource{
  4326. FieldRef: &core.ObjectFieldSelector{
  4327. APIVersion: "v1",
  4328. FieldPath: "status.podIP",
  4329. },
  4330. },
  4331. },
  4332. {
  4333. Name: "secret_value",
  4334. ValueFrom: &core.EnvVarSource{
  4335. SecretKeyRef: &core.SecretKeySelector{
  4336. LocalObjectReference: core.LocalObjectReference{
  4337. Name: "some-secret",
  4338. },
  4339. Key: "secret-key",
  4340. },
  4341. },
  4342. },
  4343. {
  4344. Name: "ENV_VAR_1",
  4345. ValueFrom: &core.EnvVarSource{
  4346. ConfigMapKeyRef: &core.ConfigMapKeySelector{
  4347. LocalObjectReference: core.LocalObjectReference{
  4348. Name: "some-config-map",
  4349. },
  4350. Key: "some-key",
  4351. },
  4352. },
  4353. },
  4354. }
  4355. if errs := ValidateEnv(successCase, field.NewPath("field")); len(errs) != 0 {
  4356. t.Errorf("expected success, got: %v", errs)
  4357. }
  4358. errorCases := []struct {
  4359. name string
  4360. envs []core.EnvVar
  4361. expectedError string
  4362. }{
  4363. {
  4364. name: "zero-length name",
  4365. envs: []core.EnvVar{{Name: ""}},
  4366. expectedError: "[0].name: Required value",
  4367. },
  4368. {
  4369. name: "illegal character",
  4370. envs: []core.EnvVar{{Name: "a!b"}},
  4371. expectedError: `[0].name: Invalid value: "a!b": ` + envVarNameErrMsg,
  4372. },
  4373. {
  4374. name: "dot only",
  4375. envs: []core.EnvVar{{Name: "."}},
  4376. expectedError: `[0].name: Invalid value: ".": must not be`,
  4377. },
  4378. {
  4379. name: "double dots only",
  4380. envs: []core.EnvVar{{Name: ".."}},
  4381. expectedError: `[0].name: Invalid value: "..": must not be`,
  4382. },
  4383. {
  4384. name: "leading double dots",
  4385. envs: []core.EnvVar{{Name: "..abc"}},
  4386. expectedError: `[0].name: Invalid value: "..abc": must not start with`,
  4387. },
  4388. {
  4389. name: "value and valueFrom specified",
  4390. envs: []core.EnvVar{{
  4391. Name: "abc",
  4392. Value: "foo",
  4393. ValueFrom: &core.EnvVarSource{
  4394. FieldRef: &core.ObjectFieldSelector{
  4395. APIVersion: "v1",
  4396. FieldPath: "metadata.name",
  4397. },
  4398. },
  4399. }},
  4400. expectedError: "[0].valueFrom: Invalid value: \"\": may not be specified when `value` is not empty",
  4401. },
  4402. {
  4403. name: "valueFrom without a source",
  4404. envs: []core.EnvVar{{
  4405. Name: "abc",
  4406. ValueFrom: &core.EnvVarSource{},
  4407. }},
  4408. expectedError: "[0].valueFrom: Invalid value: \"\": must specify one of: `fieldRef`, `resourceFieldRef`, `configMapKeyRef` or `secretKeyRef`",
  4409. },
  4410. {
  4411. name: "valueFrom.fieldRef and valueFrom.secretKeyRef specified",
  4412. envs: []core.EnvVar{{
  4413. Name: "abc",
  4414. ValueFrom: &core.EnvVarSource{
  4415. FieldRef: &core.ObjectFieldSelector{
  4416. APIVersion: "v1",
  4417. FieldPath: "metadata.name",
  4418. },
  4419. SecretKeyRef: &core.SecretKeySelector{
  4420. LocalObjectReference: core.LocalObjectReference{
  4421. Name: "a-secret",
  4422. },
  4423. Key: "a-key",
  4424. },
  4425. },
  4426. }},
  4427. expectedError: "[0].valueFrom: Invalid value: \"\": may not have more than one field specified at a time",
  4428. },
  4429. {
  4430. name: "valueFrom.fieldRef and valueFrom.configMapKeyRef set",
  4431. envs: []core.EnvVar{{
  4432. Name: "some_var_name",
  4433. ValueFrom: &core.EnvVarSource{
  4434. FieldRef: &core.ObjectFieldSelector{
  4435. APIVersion: "v1",
  4436. FieldPath: "metadata.name",
  4437. },
  4438. ConfigMapKeyRef: &core.ConfigMapKeySelector{
  4439. LocalObjectReference: core.LocalObjectReference{
  4440. Name: "some-config-map",
  4441. },
  4442. Key: "some-key",
  4443. },
  4444. },
  4445. }},
  4446. expectedError: `[0].valueFrom: Invalid value: "": may not have more than one field specified at a time`,
  4447. },
  4448. {
  4449. name: "valueFrom.fieldRef and valueFrom.secretKeyRef specified",
  4450. envs: []core.EnvVar{{
  4451. Name: "abc",
  4452. ValueFrom: &core.EnvVarSource{
  4453. FieldRef: &core.ObjectFieldSelector{
  4454. APIVersion: "v1",
  4455. FieldPath: "metadata.name",
  4456. },
  4457. SecretKeyRef: &core.SecretKeySelector{
  4458. LocalObjectReference: core.LocalObjectReference{
  4459. Name: "a-secret",
  4460. },
  4461. Key: "a-key",
  4462. },
  4463. ConfigMapKeyRef: &core.ConfigMapKeySelector{
  4464. LocalObjectReference: core.LocalObjectReference{
  4465. Name: "some-config-map",
  4466. },
  4467. Key: "some-key",
  4468. },
  4469. },
  4470. }},
  4471. expectedError: `[0].valueFrom: Invalid value: "": may not have more than one field specified at a time`,
  4472. },
  4473. {
  4474. name: "valueFrom.secretKeyRef.name invalid",
  4475. envs: []core.EnvVar{{
  4476. Name: "abc",
  4477. ValueFrom: &core.EnvVarSource{
  4478. SecretKeyRef: &core.SecretKeySelector{
  4479. LocalObjectReference: core.LocalObjectReference{
  4480. Name: "$%^&*#",
  4481. },
  4482. Key: "a-key",
  4483. },
  4484. },
  4485. }},
  4486. },
  4487. {
  4488. name: "valueFrom.configMapKeyRef.name invalid",
  4489. envs: []core.EnvVar{{
  4490. Name: "abc",
  4491. ValueFrom: &core.EnvVarSource{
  4492. ConfigMapKeyRef: &core.ConfigMapKeySelector{
  4493. LocalObjectReference: core.LocalObjectReference{
  4494. Name: "$%^&*#",
  4495. },
  4496. Key: "some-key",
  4497. },
  4498. },
  4499. }},
  4500. },
  4501. {
  4502. name: "missing FieldPath on ObjectFieldSelector",
  4503. envs: []core.EnvVar{{
  4504. Name: "abc",
  4505. ValueFrom: &core.EnvVarSource{
  4506. FieldRef: &core.ObjectFieldSelector{
  4507. APIVersion: "v1",
  4508. },
  4509. },
  4510. }},
  4511. expectedError: `[0].valueFrom.fieldRef.fieldPath: Required value`,
  4512. },
  4513. {
  4514. name: "missing APIVersion on ObjectFieldSelector",
  4515. envs: []core.EnvVar{{
  4516. Name: "abc",
  4517. ValueFrom: &core.EnvVarSource{
  4518. FieldRef: &core.ObjectFieldSelector{
  4519. FieldPath: "metadata.name",
  4520. },
  4521. },
  4522. }},
  4523. expectedError: `[0].valueFrom.fieldRef.apiVersion: Required value`,
  4524. },
  4525. {
  4526. name: "invalid fieldPath",
  4527. envs: []core.EnvVar{{
  4528. Name: "abc",
  4529. ValueFrom: &core.EnvVarSource{
  4530. FieldRef: &core.ObjectFieldSelector{
  4531. FieldPath: "metadata.whoops",
  4532. APIVersion: "v1",
  4533. },
  4534. },
  4535. }},
  4536. expectedError: `[0].valueFrom.fieldRef.fieldPath: Invalid value: "metadata.whoops": error converting fieldPath`,
  4537. },
  4538. {
  4539. name: "metadata.name with subscript",
  4540. envs: []core.EnvVar{{
  4541. Name: "labels",
  4542. ValueFrom: &core.EnvVarSource{
  4543. FieldRef: &core.ObjectFieldSelector{
  4544. FieldPath: "metadata.name['key']",
  4545. APIVersion: "v1",
  4546. },
  4547. },
  4548. }},
  4549. expectedError: `[0].valueFrom.fieldRef.fieldPath: Invalid value: "metadata.name['key']": error converting fieldPath: field label does not support subscript`,
  4550. },
  4551. {
  4552. name: "metadata.labels without subscript",
  4553. envs: []core.EnvVar{{
  4554. Name: "labels",
  4555. ValueFrom: &core.EnvVarSource{
  4556. FieldRef: &core.ObjectFieldSelector{
  4557. FieldPath: "metadata.labels",
  4558. APIVersion: "v1",
  4559. },
  4560. },
  4561. }},
  4562. expectedError: `[0].valueFrom.fieldRef.fieldPath: Unsupported value: "metadata.labels": supported values: "metadata.name", "metadata.namespace", "metadata.uid", "spec.nodeName", "spec.serviceAccountName", "status.hostIP", "status.podIP"`,
  4563. },
  4564. {
  4565. name: "metadata.annotations without subscript",
  4566. envs: []core.EnvVar{{
  4567. Name: "abc",
  4568. ValueFrom: &core.EnvVarSource{
  4569. FieldRef: &core.ObjectFieldSelector{
  4570. FieldPath: "metadata.annotations",
  4571. APIVersion: "v1",
  4572. },
  4573. },
  4574. }},
  4575. expectedError: `[0].valueFrom.fieldRef.fieldPath: Unsupported value: "metadata.annotations": supported values: "metadata.name", "metadata.namespace", "metadata.uid", "spec.nodeName", "spec.serviceAccountName", "status.hostIP", "status.podIP"`,
  4576. },
  4577. {
  4578. name: "metadata.annotations with invalid key",
  4579. envs: []core.EnvVar{{
  4580. Name: "abc",
  4581. ValueFrom: &core.EnvVarSource{
  4582. FieldRef: &core.ObjectFieldSelector{
  4583. FieldPath: "metadata.annotations['invalid~key']",
  4584. APIVersion: "v1",
  4585. },
  4586. },
  4587. }},
  4588. expectedError: `field[0].valueFrom.fieldRef: Invalid value: "invalid~key"`,
  4589. },
  4590. {
  4591. name: "metadata.labels with invalid key",
  4592. envs: []core.EnvVar{{
  4593. Name: "abc",
  4594. ValueFrom: &core.EnvVarSource{
  4595. FieldRef: &core.ObjectFieldSelector{
  4596. FieldPath: "metadata.labels['Www.k8s.io/test']",
  4597. APIVersion: "v1",
  4598. },
  4599. },
  4600. }},
  4601. expectedError: `field[0].valueFrom.fieldRef: Invalid value: "Www.k8s.io/test"`,
  4602. },
  4603. {
  4604. name: "unsupported fieldPath",
  4605. envs: []core.EnvVar{{
  4606. Name: "abc",
  4607. ValueFrom: &core.EnvVarSource{
  4608. FieldRef: &core.ObjectFieldSelector{
  4609. FieldPath: "status.phase",
  4610. APIVersion: "v1",
  4611. },
  4612. },
  4613. }},
  4614. expectedError: `valueFrom.fieldRef.fieldPath: Unsupported value: "status.phase": supported values: "metadata.name", "metadata.namespace", "metadata.uid", "spec.nodeName", "spec.serviceAccountName", "status.hostIP", "status.podIP"`,
  4615. },
  4616. }
  4617. for _, tc := range errorCases {
  4618. if errs := ValidateEnv(tc.envs, field.NewPath("field")); len(errs) == 0 {
  4619. t.Errorf("expected failure for %s", tc.name)
  4620. } else {
  4621. for i := range errs {
  4622. str := errs[i].Error()
  4623. if str != "" && !strings.Contains(str, tc.expectedError) {
  4624. t.Errorf("%s: expected error detail either empty or %q, got %q", tc.name, tc.expectedError, str)
  4625. }
  4626. }
  4627. }
  4628. }
  4629. }
  4630. func TestValidateEnvFrom(t *testing.T) {
  4631. successCase := []core.EnvFromSource{
  4632. {
  4633. ConfigMapRef: &core.ConfigMapEnvSource{
  4634. LocalObjectReference: core.LocalObjectReference{Name: "abc"},
  4635. },
  4636. },
  4637. {
  4638. Prefix: "pre_",
  4639. ConfigMapRef: &core.ConfigMapEnvSource{
  4640. LocalObjectReference: core.LocalObjectReference{Name: "abc"},
  4641. },
  4642. },
  4643. {
  4644. Prefix: "a.b",
  4645. ConfigMapRef: &core.ConfigMapEnvSource{
  4646. LocalObjectReference: core.LocalObjectReference{Name: "abc"},
  4647. },
  4648. },
  4649. {
  4650. SecretRef: &core.SecretEnvSource{
  4651. LocalObjectReference: core.LocalObjectReference{Name: "abc"},
  4652. },
  4653. },
  4654. {
  4655. Prefix: "pre_",
  4656. SecretRef: &core.SecretEnvSource{
  4657. LocalObjectReference: core.LocalObjectReference{Name: "abc"},
  4658. },
  4659. },
  4660. {
  4661. Prefix: "a.b",
  4662. SecretRef: &core.SecretEnvSource{
  4663. LocalObjectReference: core.LocalObjectReference{Name: "abc"},
  4664. },
  4665. },
  4666. }
  4667. if errs := ValidateEnvFrom(successCase, field.NewPath("field")); len(errs) != 0 {
  4668. t.Errorf("expected success: %v", errs)
  4669. }
  4670. errorCases := []struct {
  4671. name string
  4672. envs []core.EnvFromSource
  4673. expectedError string
  4674. }{
  4675. {
  4676. name: "zero-length name",
  4677. envs: []core.EnvFromSource{
  4678. {
  4679. ConfigMapRef: &core.ConfigMapEnvSource{
  4680. LocalObjectReference: core.LocalObjectReference{Name: ""}},
  4681. },
  4682. },
  4683. expectedError: "field[0].configMapRef.name: Required value",
  4684. },
  4685. {
  4686. name: "invalid name",
  4687. envs: []core.EnvFromSource{
  4688. {
  4689. ConfigMapRef: &core.ConfigMapEnvSource{
  4690. LocalObjectReference: core.LocalObjectReference{Name: "$"}},
  4691. },
  4692. },
  4693. expectedError: "field[0].configMapRef.name: Invalid value",
  4694. },
  4695. {
  4696. name: "invalid prefix",
  4697. envs: []core.EnvFromSource{
  4698. {
  4699. Prefix: "a!b",
  4700. ConfigMapRef: &core.ConfigMapEnvSource{
  4701. LocalObjectReference: core.LocalObjectReference{Name: "abc"}},
  4702. },
  4703. },
  4704. expectedError: `field[0].prefix: Invalid value: "a!b": ` + envVarNameErrMsg,
  4705. },
  4706. {
  4707. name: "zero-length name",
  4708. envs: []core.EnvFromSource{
  4709. {
  4710. SecretRef: &core.SecretEnvSource{
  4711. LocalObjectReference: core.LocalObjectReference{Name: ""}},
  4712. },
  4713. },
  4714. expectedError: "field[0].secretRef.name: Required value",
  4715. },
  4716. {
  4717. name: "invalid name",
  4718. envs: []core.EnvFromSource{
  4719. {
  4720. SecretRef: &core.SecretEnvSource{
  4721. LocalObjectReference: core.LocalObjectReference{Name: "&"}},
  4722. },
  4723. },
  4724. expectedError: "field[0].secretRef.name: Invalid value",
  4725. },
  4726. {
  4727. name: "invalid prefix",
  4728. envs: []core.EnvFromSource{
  4729. {
  4730. Prefix: "a!b",
  4731. SecretRef: &core.SecretEnvSource{
  4732. LocalObjectReference: core.LocalObjectReference{Name: "abc"}},
  4733. },
  4734. },
  4735. expectedError: `field[0].prefix: Invalid value: "a!b": ` + envVarNameErrMsg,
  4736. },
  4737. {
  4738. name: "no refs",
  4739. envs: []core.EnvFromSource{
  4740. {},
  4741. },
  4742. expectedError: "field: Invalid value: \"\": must specify one of: `configMapRef` or `secretRef`",
  4743. },
  4744. {
  4745. name: "multiple refs",
  4746. envs: []core.EnvFromSource{
  4747. {
  4748. SecretRef: &core.SecretEnvSource{
  4749. LocalObjectReference: core.LocalObjectReference{Name: "abc"}},
  4750. ConfigMapRef: &core.ConfigMapEnvSource{
  4751. LocalObjectReference: core.LocalObjectReference{Name: "abc"}},
  4752. },
  4753. },
  4754. expectedError: "field: Invalid value: \"\": may not have more than one field specified at a time",
  4755. },
  4756. {
  4757. name: "invalid secret ref name",
  4758. envs: []core.EnvFromSource{
  4759. {
  4760. SecretRef: &core.SecretEnvSource{
  4761. LocalObjectReference: core.LocalObjectReference{Name: "$%^&*#"}},
  4762. },
  4763. },
  4764. expectedError: "field[0].secretRef.name: Invalid value: \"$%^&*#\": " + dnsSubdomainLabelErrMsg,
  4765. },
  4766. {
  4767. name: "invalid config ref name",
  4768. envs: []core.EnvFromSource{
  4769. {
  4770. ConfigMapRef: &core.ConfigMapEnvSource{
  4771. LocalObjectReference: core.LocalObjectReference{Name: "$%^&*#"}},
  4772. },
  4773. },
  4774. expectedError: "field[0].configMapRef.name: Invalid value: \"$%^&*#\": " + dnsSubdomainLabelErrMsg,
  4775. },
  4776. }
  4777. for _, tc := range errorCases {
  4778. if errs := ValidateEnvFrom(tc.envs, field.NewPath("field")); len(errs) == 0 {
  4779. t.Errorf("expected failure for %s", tc.name)
  4780. } else {
  4781. for i := range errs {
  4782. str := errs[i].Error()
  4783. if str != "" && !strings.Contains(str, tc.expectedError) {
  4784. t.Errorf("%s: expected error detail either empty or %q, got %q", tc.name, tc.expectedError, str)
  4785. }
  4786. }
  4787. }
  4788. }
  4789. }
  4790. func TestValidateVolumeMounts(t *testing.T) {
  4791. volumes := []core.Volume{
  4792. {Name: "abc", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "testclaim1"}}},
  4793. {Name: "abc-123", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "testclaim2"}}},
  4794. {Name: "123", VolumeSource: core.VolumeSource{HostPath: &core.HostPathVolumeSource{Path: "/foo/baz", Type: newHostPathType(string(core.HostPathUnset))}}},
  4795. }
  4796. vols, v1err := ValidateVolumes(volumes, field.NewPath("field"))
  4797. if len(v1err) > 0 {
  4798. t.Errorf("Invalid test volume - expected success %v", v1err)
  4799. return
  4800. }
  4801. container := core.Container{
  4802. SecurityContext: nil,
  4803. }
  4804. propagation := core.MountPropagationBidirectional
  4805. successCase := []core.VolumeMount{
  4806. {Name: "abc", MountPath: "/foo"},
  4807. {Name: "123", MountPath: "/bar"},
  4808. {Name: "abc-123", MountPath: "/baz"},
  4809. {Name: "abc-123", MountPath: "/baa", SubPath: ""},
  4810. {Name: "abc-123", MountPath: "/bab", SubPath: "baz"},
  4811. {Name: "abc-123", MountPath: "d:", SubPath: ""},
  4812. {Name: "abc-123", MountPath: "F:", SubPath: ""},
  4813. {Name: "abc-123", MountPath: "G:\\mount", SubPath: ""},
  4814. {Name: "abc-123", MountPath: "/bac", SubPath: ".baz"},
  4815. {Name: "abc-123", MountPath: "/bad", SubPath: "..baz"},
  4816. }
  4817. goodVolumeDevices := []core.VolumeDevice{
  4818. {Name: "xyz", DevicePath: "/foofoo"},
  4819. {Name: "uvw", DevicePath: "/foofoo/share/test"},
  4820. }
  4821. if errs := ValidateVolumeMounts(successCase, GetVolumeDeviceMap(goodVolumeDevices), vols, &container, field.NewPath("field")); len(errs) != 0 {
  4822. t.Errorf("expected success: %v", errs)
  4823. }
  4824. errorCases := map[string][]core.VolumeMount{
  4825. "empty name": {{Name: "", MountPath: "/foo"}},
  4826. "name not found": {{Name: "", MountPath: "/foo"}},
  4827. "empty mountpath": {{Name: "abc", MountPath: ""}},
  4828. "mountpath collision": {{Name: "foo", MountPath: "/path/a"}, {Name: "bar", MountPath: "/path/a"}},
  4829. "absolute subpath": {{Name: "abc", MountPath: "/bar", SubPath: "/baz"}},
  4830. "subpath in ..": {{Name: "abc", MountPath: "/bar", SubPath: "../baz"}},
  4831. "subpath contains ..": {{Name: "abc", MountPath: "/bar", SubPath: "baz/../bat"}},
  4832. "subpath ends in ..": {{Name: "abc", MountPath: "/bar", SubPath: "./.."}},
  4833. "disabled MountPropagation feature gate": {{Name: "abc", MountPath: "/bar", MountPropagation: &propagation}},
  4834. "name exists in volumeDevice": {{Name: "xyz", MountPath: "/bar"}},
  4835. "mountpath exists in volumeDevice": {{Name: "uvw", MountPath: "/mnt/exists"}},
  4836. "both exist in volumeDevice": {{Name: "xyz", MountPath: "/mnt/exists"}},
  4837. }
  4838. badVolumeDevice := []core.VolumeDevice{
  4839. {Name: "xyz", DevicePath: "/mnt/exists"},
  4840. }
  4841. for k, v := range errorCases {
  4842. if errs := ValidateVolumeMounts(v, GetVolumeDeviceMap(badVolumeDevice), vols, &container, field.NewPath("field")); len(errs) == 0 {
  4843. t.Errorf("expected failure for %s", k)
  4844. }
  4845. }
  4846. }
  4847. func TestValidateDisabledSubpath(t *testing.T) {
  4848. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeSubpath, false)()
  4849. volumes := []core.Volume{
  4850. {Name: "abc", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "testclaim1"}}},
  4851. {Name: "abc-123", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "testclaim2"}}},
  4852. {Name: "123", VolumeSource: core.VolumeSource{HostPath: &core.HostPathVolumeSource{Path: "/foo/baz", Type: newHostPathType(string(core.HostPathUnset))}}},
  4853. }
  4854. vols, v1err := ValidateVolumes(volumes, field.NewPath("field"))
  4855. if len(v1err) > 0 {
  4856. t.Errorf("Invalid test volume - expected success %v", v1err)
  4857. return
  4858. }
  4859. container := core.Container{
  4860. SecurityContext: nil,
  4861. }
  4862. goodVolumeDevices := []core.VolumeDevice{
  4863. {Name: "xyz", DevicePath: "/foofoo"},
  4864. {Name: "uvw", DevicePath: "/foofoo/share/test"},
  4865. }
  4866. cases := map[string]struct {
  4867. mounts []core.VolumeMount
  4868. expectError bool
  4869. }{
  4870. "subpath not specified": {
  4871. []core.VolumeMount{
  4872. {
  4873. Name: "abc-123",
  4874. MountPath: "/bab",
  4875. },
  4876. },
  4877. false,
  4878. },
  4879. "subpath specified": {
  4880. []core.VolumeMount{
  4881. {
  4882. Name: "abc-123",
  4883. MountPath: "/bab",
  4884. SubPath: "baz",
  4885. },
  4886. },
  4887. false, // validation should not fail, dropping the field is handled in PrepareForCreate/PrepareForUpdate
  4888. },
  4889. }
  4890. for name, test := range cases {
  4891. errs := ValidateVolumeMounts(test.mounts, GetVolumeDeviceMap(goodVolumeDevices), vols, &container, field.NewPath("field"))
  4892. if len(errs) != 0 && !test.expectError {
  4893. t.Errorf("test %v failed: %+v", name, errs)
  4894. }
  4895. if len(errs) == 0 && test.expectError {
  4896. t.Errorf("test %v failed, expected error", name)
  4897. }
  4898. }
  4899. }
  4900. func TestValidateSubpathMutuallyExclusive(t *testing.T) {
  4901. // Enable feature VolumeSubpathEnvExpansion and VolumeSubpath
  4902. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeSubpathEnvExpansion, true)()
  4903. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeSubpath, true)()
  4904. volumes := []core.Volume{
  4905. {Name: "abc", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "testclaim1"}}},
  4906. {Name: "abc-123", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "testclaim2"}}},
  4907. {Name: "123", VolumeSource: core.VolumeSource{HostPath: &core.HostPathVolumeSource{Path: "/foo/baz", Type: newHostPathType(string(core.HostPathUnset))}}},
  4908. }
  4909. vols, v1err := ValidateVolumes(volumes, field.NewPath("field"))
  4910. if len(v1err) > 0 {
  4911. t.Errorf("Invalid test volume - expected success %v", v1err)
  4912. return
  4913. }
  4914. container := core.Container{
  4915. SecurityContext: nil,
  4916. }
  4917. goodVolumeDevices := []core.VolumeDevice{
  4918. {Name: "xyz", DevicePath: "/foofoo"},
  4919. {Name: "uvw", DevicePath: "/foofoo/share/test"},
  4920. }
  4921. cases := map[string]struct {
  4922. mounts []core.VolumeMount
  4923. expectError bool
  4924. }{
  4925. "subpath and subpathexpr not specified": {
  4926. []core.VolumeMount{
  4927. {
  4928. Name: "abc-123",
  4929. MountPath: "/bab",
  4930. },
  4931. },
  4932. false,
  4933. },
  4934. "subpath expr specified": {
  4935. []core.VolumeMount{
  4936. {
  4937. Name: "abc-123",
  4938. MountPath: "/bab",
  4939. SubPathExpr: "$(POD_NAME)",
  4940. },
  4941. },
  4942. false,
  4943. },
  4944. "subpath specified": {
  4945. []core.VolumeMount{
  4946. {
  4947. Name: "abc-123",
  4948. MountPath: "/bab",
  4949. SubPath: "baz",
  4950. },
  4951. },
  4952. false,
  4953. },
  4954. "subpath and subpathexpr specified": {
  4955. []core.VolumeMount{
  4956. {
  4957. Name: "abc-123",
  4958. MountPath: "/bab",
  4959. SubPath: "baz",
  4960. SubPathExpr: "$(POD_NAME)",
  4961. },
  4962. },
  4963. true,
  4964. },
  4965. }
  4966. for name, test := range cases {
  4967. errs := ValidateVolumeMounts(test.mounts, GetVolumeDeviceMap(goodVolumeDevices), vols, &container, field.NewPath("field"))
  4968. if len(errs) != 0 && !test.expectError {
  4969. t.Errorf("test %v failed: %+v", name, errs)
  4970. }
  4971. if len(errs) == 0 && test.expectError {
  4972. t.Errorf("test %v failed, expected error", name)
  4973. }
  4974. }
  4975. }
  4976. func TestValidateDisabledSubpathExpr(t *testing.T) {
  4977. // Enable feature VolumeSubpathEnvExpansion
  4978. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeSubpathEnvExpansion, true)()
  4979. volumes := []core.Volume{
  4980. {Name: "abc", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "testclaim1"}}},
  4981. {Name: "abc-123", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "testclaim2"}}},
  4982. {Name: "123", VolumeSource: core.VolumeSource{HostPath: &core.HostPathVolumeSource{Path: "/foo/baz", Type: newHostPathType(string(core.HostPathUnset))}}},
  4983. }
  4984. vols, v1err := ValidateVolumes(volumes, field.NewPath("field"))
  4985. if len(v1err) > 0 {
  4986. t.Errorf("Invalid test volume - expected success %v", v1err)
  4987. return
  4988. }
  4989. container := core.Container{
  4990. SecurityContext: nil,
  4991. }
  4992. goodVolumeDevices := []core.VolumeDevice{
  4993. {Name: "xyz", DevicePath: "/foofoo"},
  4994. {Name: "uvw", DevicePath: "/foofoo/share/test"},
  4995. }
  4996. cases := map[string]struct {
  4997. mounts []core.VolumeMount
  4998. expectError bool
  4999. }{
  5000. "subpath expr not specified": {
  5001. []core.VolumeMount{
  5002. {
  5003. Name: "abc-123",
  5004. MountPath: "/bab",
  5005. },
  5006. },
  5007. false,
  5008. },
  5009. "subpath expr specified": {
  5010. []core.VolumeMount{
  5011. {
  5012. Name: "abc-123",
  5013. MountPath: "/bab",
  5014. SubPathExpr: "$(POD_NAME)",
  5015. },
  5016. },
  5017. false,
  5018. },
  5019. }
  5020. for name, test := range cases {
  5021. errs := ValidateVolumeMounts(test.mounts, GetVolumeDeviceMap(goodVolumeDevices), vols, &container, field.NewPath("field"))
  5022. if len(errs) != 0 && !test.expectError {
  5023. t.Errorf("test %v failed: %+v", name, errs)
  5024. }
  5025. if len(errs) == 0 && test.expectError {
  5026. t.Errorf("test %v failed, expected error", name)
  5027. }
  5028. }
  5029. // Repeat with feature gate off
  5030. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeSubpathEnvExpansion, false)()
  5031. cases = map[string]struct {
  5032. mounts []core.VolumeMount
  5033. expectError bool
  5034. }{
  5035. "subpath expr not specified": {
  5036. []core.VolumeMount{
  5037. {
  5038. Name: "abc-123",
  5039. MountPath: "/bab",
  5040. },
  5041. },
  5042. false,
  5043. },
  5044. "subpath expr specified": {
  5045. []core.VolumeMount{
  5046. {
  5047. Name: "abc-123",
  5048. MountPath: "/bab",
  5049. SubPathExpr: "$(POD_NAME)",
  5050. },
  5051. },
  5052. false, // validation should not fail, dropping the field is handled in PrepareForCreate/PrepareForUpdate
  5053. },
  5054. }
  5055. for name, test := range cases {
  5056. errs := ValidateVolumeMounts(test.mounts, GetVolumeDeviceMap(goodVolumeDevices), vols, &container, field.NewPath("field"))
  5057. if len(errs) != 0 && !test.expectError {
  5058. t.Errorf("test %v failed: %+v", name, errs)
  5059. }
  5060. if len(errs) == 0 && test.expectError {
  5061. t.Errorf("test %v failed, expected error", name)
  5062. }
  5063. }
  5064. // Repeat with subpath feature gate off
  5065. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeSubpath, false)()
  5066. cases = map[string]struct {
  5067. mounts []core.VolumeMount
  5068. expectError bool
  5069. }{
  5070. "subpath expr not specified": {
  5071. []core.VolumeMount{
  5072. {
  5073. Name: "abc-123",
  5074. MountPath: "/bab",
  5075. },
  5076. },
  5077. false,
  5078. },
  5079. "subpath expr specified": {
  5080. []core.VolumeMount{
  5081. {
  5082. Name: "abc-123",
  5083. MountPath: "/bab",
  5084. SubPathExpr: "$(POD_NAME)",
  5085. },
  5086. },
  5087. false, // validation should not fail, dropping the field is handled in PrepareForCreate/PrepareForUpdate
  5088. },
  5089. }
  5090. for name, test := range cases {
  5091. errs := ValidateVolumeMounts(test.mounts, GetVolumeDeviceMap(goodVolumeDevices), vols, &container, field.NewPath("field"))
  5092. if len(errs) != 0 && !test.expectError {
  5093. t.Errorf("test %v failed: %+v", name, errs)
  5094. }
  5095. if len(errs) == 0 && test.expectError {
  5096. t.Errorf("test %v failed, expected error", name)
  5097. }
  5098. }
  5099. }
  5100. func TestValidateMountPropagation(t *testing.T) {
  5101. bTrue := true
  5102. bFalse := false
  5103. privilegedContainer := &core.Container{
  5104. SecurityContext: &core.SecurityContext{
  5105. Privileged: &bTrue,
  5106. },
  5107. }
  5108. nonPrivilegedContainer := &core.Container{
  5109. SecurityContext: &core.SecurityContext{
  5110. Privileged: &bFalse,
  5111. },
  5112. }
  5113. defaultContainer := &core.Container{}
  5114. propagationBidirectional := core.MountPropagationBidirectional
  5115. propagationHostToContainer := core.MountPropagationHostToContainer
  5116. propagationNone := core.MountPropagationNone
  5117. propagationInvalid := core.MountPropagationMode("invalid")
  5118. tests := []struct {
  5119. mount core.VolumeMount
  5120. container *core.Container
  5121. expectError bool
  5122. }{
  5123. {
  5124. // implicitly non-privileged container + no propagation
  5125. core.VolumeMount{Name: "foo", MountPath: "/foo"},
  5126. defaultContainer,
  5127. false,
  5128. },
  5129. {
  5130. // implicitly non-privileged container + HostToContainer
  5131. core.VolumeMount{Name: "foo", MountPath: "/foo", MountPropagation: &propagationHostToContainer},
  5132. defaultContainer,
  5133. false,
  5134. },
  5135. {
  5136. // non-privileged container + None
  5137. core.VolumeMount{Name: "foo", MountPath: "/foo", MountPropagation: &propagationNone},
  5138. defaultContainer,
  5139. false,
  5140. },
  5141. {
  5142. // error: implicitly non-privileged container + Bidirectional
  5143. core.VolumeMount{Name: "foo", MountPath: "/foo", MountPropagation: &propagationBidirectional},
  5144. defaultContainer,
  5145. true,
  5146. },
  5147. {
  5148. // explicitly non-privileged container + no propagation
  5149. core.VolumeMount{Name: "foo", MountPath: "/foo"},
  5150. nonPrivilegedContainer,
  5151. false,
  5152. },
  5153. {
  5154. // explicitly non-privileged container + HostToContainer
  5155. core.VolumeMount{Name: "foo", MountPath: "/foo", MountPropagation: &propagationHostToContainer},
  5156. nonPrivilegedContainer,
  5157. false,
  5158. },
  5159. {
  5160. // explicitly non-privileged container + HostToContainer
  5161. core.VolumeMount{Name: "foo", MountPath: "/foo", MountPropagation: &propagationBidirectional},
  5162. nonPrivilegedContainer,
  5163. true,
  5164. },
  5165. {
  5166. // privileged container + no propagation
  5167. core.VolumeMount{Name: "foo", MountPath: "/foo"},
  5168. privilegedContainer,
  5169. false,
  5170. },
  5171. {
  5172. // privileged container + HostToContainer
  5173. core.VolumeMount{Name: "foo", MountPath: "/foo", MountPropagation: &propagationHostToContainer},
  5174. privilegedContainer,
  5175. false,
  5176. },
  5177. {
  5178. // privileged container + Bidirectional
  5179. core.VolumeMount{Name: "foo", MountPath: "/foo", MountPropagation: &propagationBidirectional},
  5180. privilegedContainer,
  5181. false,
  5182. },
  5183. {
  5184. // error: privileged container + invalid mount propagation
  5185. core.VolumeMount{Name: "foo", MountPath: "/foo", MountPropagation: &propagationInvalid},
  5186. privilegedContainer,
  5187. true,
  5188. },
  5189. {
  5190. // no container + Bidirectional
  5191. core.VolumeMount{Name: "foo", MountPath: "/foo", MountPropagation: &propagationBidirectional},
  5192. nil,
  5193. false,
  5194. },
  5195. }
  5196. volumes := []core.Volume{
  5197. {Name: "foo", VolumeSource: core.VolumeSource{HostPath: &core.HostPathVolumeSource{Path: "/foo/baz", Type: newHostPathType(string(core.HostPathUnset))}}},
  5198. }
  5199. vols2, v2err := ValidateVolumes(volumes, field.NewPath("field"))
  5200. if len(v2err) > 0 {
  5201. t.Errorf("Invalid test volume - expected success %v", v2err)
  5202. return
  5203. }
  5204. for i, test := range tests {
  5205. errs := ValidateVolumeMounts([]core.VolumeMount{test.mount}, nil, vols2, test.container, field.NewPath("field"))
  5206. if test.expectError && len(errs) == 0 {
  5207. t.Errorf("test %d expected error, got none", i)
  5208. }
  5209. if !test.expectError && len(errs) != 0 {
  5210. t.Errorf("test %d expected success, got error: %v", i, errs)
  5211. }
  5212. }
  5213. }
  5214. func TestAlphaValidateVolumeDevices(t *testing.T) {
  5215. volumes := []core.Volume{
  5216. {Name: "abc", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "testclaim1"}}},
  5217. {Name: "abc-123", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "testclaim2"}}},
  5218. {Name: "def", VolumeSource: core.VolumeSource{HostPath: &core.HostPathVolumeSource{Path: "/foo/baz", Type: newHostPathType(string(core.HostPathUnset))}}},
  5219. }
  5220. vols, v1err := ValidateVolumes(volumes, field.NewPath("field"))
  5221. if len(v1err) > 0 {
  5222. t.Errorf("Invalid test volumes - expected success %v", v1err)
  5223. return
  5224. }
  5225. successCase := []core.VolumeDevice{
  5226. {Name: "abc", DevicePath: "/foo"},
  5227. {Name: "abc-123", DevicePath: "/usr/share/test"},
  5228. }
  5229. goodVolumeMounts := []core.VolumeMount{
  5230. {Name: "xyz", MountPath: "/foofoo"},
  5231. {Name: "ghi", MountPath: "/foo/usr/share/test"},
  5232. }
  5233. errorCases := map[string][]core.VolumeDevice{
  5234. "empty name": {{Name: "", DevicePath: "/foo"}},
  5235. "duplicate name": {{Name: "abc", DevicePath: "/foo"}, {Name: "abc", DevicePath: "/foo/bar"}},
  5236. "name not found": {{Name: "not-found", DevicePath: "/usr/share/test"}},
  5237. "name found but invalid source": {{Name: "def", DevicePath: "/usr/share/test"}},
  5238. "empty devicepath": {{Name: "abc", DevicePath: ""}},
  5239. "relative devicepath": {{Name: "abc-123", DevicePath: "baz"}},
  5240. "duplicate devicepath": {{Name: "abc", DevicePath: "/foo"}, {Name: "abc-123", DevicePath: "/foo"}},
  5241. "no backsteps": {{Name: "def", DevicePath: "/baz/../"}},
  5242. "name exists in volumemounts": {{Name: "abc", DevicePath: "/baz/../"}},
  5243. "path exists in volumemounts": {{Name: "xyz", DevicePath: "/this/path/exists"}},
  5244. "both exist in volumemounts": {{Name: "abc", DevicePath: "/this/path/exists"}},
  5245. }
  5246. badVolumeMounts := []core.VolumeMount{
  5247. {Name: "abc", MountPath: "/foo"},
  5248. {Name: "abc-123", MountPath: "/this/path/exists"},
  5249. }
  5250. // Success Cases:
  5251. // Validate normal success cases - only PVC volumeSource
  5252. if errs := ValidateVolumeDevices(successCase, GetVolumeMountMap(goodVolumeMounts), vols, field.NewPath("field")); len(errs) != 0 {
  5253. t.Errorf("expected success: %v", errs)
  5254. }
  5255. // Error Cases:
  5256. // Validate normal error cases - only PVC volumeSource
  5257. for k, v := range errorCases {
  5258. if errs := ValidateVolumeDevices(v, GetVolumeMountMap(badVolumeMounts), vols, field.NewPath("field")); len(errs) == 0 {
  5259. t.Errorf("expected failure for %s", k)
  5260. }
  5261. }
  5262. }
  5263. func TestValidateProbe(t *testing.T) {
  5264. handler := core.Handler{Exec: &core.ExecAction{Command: []string{"echo"}}}
  5265. // These fields must be positive.
  5266. positiveFields := [...]string{"InitialDelaySeconds", "TimeoutSeconds", "PeriodSeconds", "SuccessThreshold", "FailureThreshold"}
  5267. successCases := []*core.Probe{nil}
  5268. for _, field := range positiveFields {
  5269. probe := &core.Probe{Handler: handler}
  5270. reflect.ValueOf(probe).Elem().FieldByName(field).SetInt(10)
  5271. successCases = append(successCases, probe)
  5272. }
  5273. for _, p := range successCases {
  5274. if errs := validateProbe(p, field.NewPath("field")); len(errs) != 0 {
  5275. t.Errorf("expected success: %v", errs)
  5276. }
  5277. }
  5278. errorCases := []*core.Probe{{TimeoutSeconds: 10, InitialDelaySeconds: 10}}
  5279. for _, field := range positiveFields {
  5280. probe := &core.Probe{Handler: handler}
  5281. reflect.ValueOf(probe).Elem().FieldByName(field).SetInt(-10)
  5282. errorCases = append(errorCases, probe)
  5283. }
  5284. for _, p := range errorCases {
  5285. if errs := validateProbe(p, field.NewPath("field")); len(errs) == 0 {
  5286. t.Errorf("expected failure for %v", p)
  5287. }
  5288. }
  5289. }
  5290. func TestValidateHandler(t *testing.T) {
  5291. successCases := []core.Handler{
  5292. {Exec: &core.ExecAction{Command: []string{"echo"}}},
  5293. {HTTPGet: &core.HTTPGetAction{Path: "/", Port: intstr.FromInt(1), Host: "", Scheme: "HTTP"}},
  5294. {HTTPGet: &core.HTTPGetAction{Path: "/foo", Port: intstr.FromInt(65535), Host: "host", Scheme: "HTTP"}},
  5295. {HTTPGet: &core.HTTPGetAction{Path: "/", Port: intstr.FromString("port"), Host: "", Scheme: "HTTP"}},
  5296. {HTTPGet: &core.HTTPGetAction{Path: "/", Port: intstr.FromString("port"), Host: "", Scheme: "HTTP", HTTPHeaders: []core.HTTPHeader{{Name: "Host", Value: "foo.example.com"}}}},
  5297. {HTTPGet: &core.HTTPGetAction{Path: "/", Port: intstr.FromString("port"), Host: "", Scheme: "HTTP", HTTPHeaders: []core.HTTPHeader{{Name: "X-Forwarded-For", Value: "1.2.3.4"}, {Name: "X-Forwarded-For", Value: "5.6.7.8"}}}},
  5298. }
  5299. for _, h := range successCases {
  5300. if errs := validateHandler(&h, field.NewPath("field")); len(errs) != 0 {
  5301. t.Errorf("expected success: %v", errs)
  5302. }
  5303. }
  5304. errorCases := []core.Handler{
  5305. {},
  5306. {Exec: &core.ExecAction{Command: []string{}}},
  5307. {HTTPGet: &core.HTTPGetAction{Path: "", Port: intstr.FromInt(0), Host: ""}},
  5308. {HTTPGet: &core.HTTPGetAction{Path: "/foo", Port: intstr.FromInt(65536), Host: "host"}},
  5309. {HTTPGet: &core.HTTPGetAction{Path: "", Port: intstr.FromString(""), Host: ""}},
  5310. {HTTPGet: &core.HTTPGetAction{Path: "/", Port: intstr.FromString("port"), Host: "", Scheme: "HTTP", HTTPHeaders: []core.HTTPHeader{{Name: "Host:", Value: "foo.example.com"}}}},
  5311. {HTTPGet: &core.HTTPGetAction{Path: "/", Port: intstr.FromString("port"), Host: "", Scheme: "HTTP", HTTPHeaders: []core.HTTPHeader{{Name: "X_Forwarded_For", Value: "foo.example.com"}}}},
  5312. }
  5313. for _, h := range errorCases {
  5314. if errs := validateHandler(&h, field.NewPath("field")); len(errs) == 0 {
  5315. t.Errorf("expected failure for %#v", h)
  5316. }
  5317. }
  5318. }
  5319. func TestValidatePullPolicy(t *testing.T) {
  5320. type T struct {
  5321. Container core.Container
  5322. ExpectedPolicy core.PullPolicy
  5323. }
  5324. testCases := map[string]T{
  5325. "NotPresent1": {
  5326. core.Container{Name: "abc", Image: "image:latest", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
  5327. core.PullIfNotPresent,
  5328. },
  5329. "NotPresent2": {
  5330. core.Container{Name: "abc1", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
  5331. core.PullIfNotPresent,
  5332. },
  5333. "Always1": {
  5334. core.Container{Name: "123", Image: "image:latest", ImagePullPolicy: "Always"},
  5335. core.PullAlways,
  5336. },
  5337. "Always2": {
  5338. core.Container{Name: "1234", Image: "image", ImagePullPolicy: "Always"},
  5339. core.PullAlways,
  5340. },
  5341. "Never1": {
  5342. core.Container{Name: "abc-123", Image: "image:latest", ImagePullPolicy: "Never"},
  5343. core.PullNever,
  5344. },
  5345. "Never2": {
  5346. core.Container{Name: "abc-1234", Image: "image", ImagePullPolicy: "Never"},
  5347. core.PullNever,
  5348. },
  5349. }
  5350. for k, v := range testCases {
  5351. ctr := &v.Container
  5352. errs := validatePullPolicy(ctr.ImagePullPolicy, field.NewPath("field"))
  5353. if len(errs) != 0 {
  5354. t.Errorf("case[%s] expected success, got %#v", k, errs)
  5355. }
  5356. if ctr.ImagePullPolicy != v.ExpectedPolicy {
  5357. t.Errorf("case[%s] expected policy %v, got %v", k, v.ExpectedPolicy, ctr.ImagePullPolicy)
  5358. }
  5359. }
  5360. }
  5361. func getResourceLimits(cpu, memory string) core.ResourceList {
  5362. res := core.ResourceList{}
  5363. res[core.ResourceCPU] = resource.MustParse(cpu)
  5364. res[core.ResourceMemory] = resource.MustParse(memory)
  5365. return res
  5366. }
  5367. func TestValidateContainers(t *testing.T) {
  5368. volumeDevices := make(map[string]core.VolumeSource)
  5369. capabilities.SetForTests(capabilities.Capabilities{
  5370. AllowPrivileged: true,
  5371. })
  5372. successCase := []core.Container{
  5373. {Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
  5374. // backwards compatibility to ensure containers in pod template spec do not check for this
  5375. {Name: "def", Image: " ", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
  5376. {Name: "ghi", Image: " some ", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
  5377. {Name: "123", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
  5378. {Name: "abc-123", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
  5379. {
  5380. Name: "life-123",
  5381. Image: "image",
  5382. Lifecycle: &core.Lifecycle{
  5383. PreStop: &core.Handler{
  5384. Exec: &core.ExecAction{Command: []string{"ls", "-l"}},
  5385. },
  5386. },
  5387. ImagePullPolicy: "IfNotPresent",
  5388. TerminationMessagePolicy: "File",
  5389. },
  5390. {
  5391. Name: "resources-test",
  5392. Image: "image",
  5393. Resources: core.ResourceRequirements{
  5394. Limits: core.ResourceList{
  5395. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  5396. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  5397. core.ResourceName("my.org/resource"): resource.MustParse("10"),
  5398. },
  5399. },
  5400. ImagePullPolicy: "IfNotPresent",
  5401. TerminationMessagePolicy: "File",
  5402. },
  5403. {
  5404. Name: "resources-test-with-request-and-limit",
  5405. Image: "image",
  5406. Resources: core.ResourceRequirements{
  5407. Requests: core.ResourceList{
  5408. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  5409. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  5410. },
  5411. Limits: core.ResourceList{
  5412. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  5413. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  5414. },
  5415. },
  5416. ImagePullPolicy: "IfNotPresent",
  5417. TerminationMessagePolicy: "File",
  5418. },
  5419. {
  5420. Name: "resources-request-limit-simple",
  5421. Image: "image",
  5422. Resources: core.ResourceRequirements{
  5423. Requests: core.ResourceList{
  5424. core.ResourceName(core.ResourceCPU): resource.MustParse("8"),
  5425. },
  5426. Limits: core.ResourceList{
  5427. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  5428. },
  5429. },
  5430. ImagePullPolicy: "IfNotPresent",
  5431. TerminationMessagePolicy: "File",
  5432. },
  5433. {
  5434. Name: "resources-request-limit-edge",
  5435. Image: "image",
  5436. Resources: core.ResourceRequirements{
  5437. Requests: core.ResourceList{
  5438. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  5439. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  5440. core.ResourceName("my.org/resource"): resource.MustParse("10"),
  5441. },
  5442. Limits: core.ResourceList{
  5443. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  5444. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  5445. core.ResourceName("my.org/resource"): resource.MustParse("10"),
  5446. },
  5447. },
  5448. ImagePullPolicy: "IfNotPresent",
  5449. TerminationMessagePolicy: "File",
  5450. },
  5451. {
  5452. Name: "resources-request-limit-partials",
  5453. Image: "image",
  5454. Resources: core.ResourceRequirements{
  5455. Requests: core.ResourceList{
  5456. core.ResourceName(core.ResourceCPU): resource.MustParse("9.5"),
  5457. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  5458. },
  5459. Limits: core.ResourceList{
  5460. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  5461. core.ResourceName("my.org/resource"): resource.MustParse("10"),
  5462. },
  5463. },
  5464. ImagePullPolicy: "IfNotPresent",
  5465. TerminationMessagePolicy: "File",
  5466. },
  5467. {
  5468. Name: "resources-request",
  5469. Image: "image",
  5470. Resources: core.ResourceRequirements{
  5471. Requests: core.ResourceList{
  5472. core.ResourceName(core.ResourceCPU): resource.MustParse("9.5"),
  5473. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  5474. },
  5475. },
  5476. ImagePullPolicy: "IfNotPresent",
  5477. TerminationMessagePolicy: "File",
  5478. },
  5479. {
  5480. Name: "same-host-port-different-protocol",
  5481. Image: "image",
  5482. Ports: []core.ContainerPort{
  5483. {ContainerPort: 80, HostPort: 80, Protocol: "TCP"},
  5484. {ContainerPort: 80, HostPort: 80, Protocol: "UDP"},
  5485. },
  5486. ImagePullPolicy: "IfNotPresent",
  5487. TerminationMessagePolicy: "File",
  5488. },
  5489. {
  5490. Name: "fallback-to-logs-termination-message",
  5491. Image: "image",
  5492. ImagePullPolicy: "IfNotPresent",
  5493. TerminationMessagePolicy: "FallbackToLogsOnError",
  5494. },
  5495. {
  5496. Name: "file-termination-message",
  5497. Image: "image",
  5498. ImagePullPolicy: "IfNotPresent",
  5499. TerminationMessagePolicy: "File",
  5500. },
  5501. {
  5502. Name: "env-from-source",
  5503. Image: "image",
  5504. ImagePullPolicy: "IfNotPresent",
  5505. TerminationMessagePolicy: "File",
  5506. EnvFrom: []core.EnvFromSource{
  5507. {
  5508. ConfigMapRef: &core.ConfigMapEnvSource{
  5509. LocalObjectReference: core.LocalObjectReference{
  5510. Name: "test",
  5511. },
  5512. },
  5513. },
  5514. },
  5515. },
  5516. {Name: "abc-1234", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File", SecurityContext: fakeValidSecurityContext(true)},
  5517. }
  5518. if errs := validateContainers(successCase, false, volumeDevices, field.NewPath("field")); len(errs) != 0 {
  5519. t.Errorf("expected success: %v", errs)
  5520. }
  5521. capabilities.SetForTests(capabilities.Capabilities{
  5522. AllowPrivileged: false,
  5523. })
  5524. errorCases := map[string][]core.Container{
  5525. "zero-length name": {{Name: "", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  5526. "zero-length-image": {{Name: "abc", Image: "", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  5527. "name > 63 characters": {{Name: strings.Repeat("a", 64), Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  5528. "name not a DNS label": {{Name: "a.b.c", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  5529. "name not unique": {
  5530. {Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
  5531. {Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
  5532. },
  5533. "zero-length image": {{Name: "abc", Image: "", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  5534. "host port not unique": {
  5535. {Name: "abc", Image: "image", Ports: []core.ContainerPort{{ContainerPort: 80, HostPort: 80, Protocol: "TCP"}},
  5536. ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
  5537. {Name: "def", Image: "image", Ports: []core.ContainerPort{{ContainerPort: 81, HostPort: 80, Protocol: "TCP"}},
  5538. ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
  5539. },
  5540. "invalid env var name": {
  5541. {Name: "abc", Image: "image", Env: []core.EnvVar{{Name: "ev!1"}}, ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
  5542. },
  5543. "unknown volume name": {
  5544. {Name: "abc", Image: "image", VolumeMounts: []core.VolumeMount{{Name: "anything", MountPath: "/foo"}},
  5545. ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
  5546. },
  5547. "invalid lifecycle, no exec command.": {
  5548. {
  5549. Name: "life-123",
  5550. Image: "image",
  5551. Lifecycle: &core.Lifecycle{
  5552. PreStop: &core.Handler{
  5553. Exec: &core.ExecAction{},
  5554. },
  5555. },
  5556. ImagePullPolicy: "IfNotPresent",
  5557. TerminationMessagePolicy: "File",
  5558. },
  5559. },
  5560. "invalid lifecycle, no http path.": {
  5561. {
  5562. Name: "life-123",
  5563. Image: "image",
  5564. Lifecycle: &core.Lifecycle{
  5565. PreStop: &core.Handler{
  5566. HTTPGet: &core.HTTPGetAction{},
  5567. },
  5568. },
  5569. ImagePullPolicy: "IfNotPresent",
  5570. TerminationMessagePolicy: "File",
  5571. },
  5572. },
  5573. "invalid lifecycle, no tcp socket port.": {
  5574. {
  5575. Name: "life-123",
  5576. Image: "image",
  5577. Lifecycle: &core.Lifecycle{
  5578. PreStop: &core.Handler{
  5579. TCPSocket: &core.TCPSocketAction{},
  5580. },
  5581. },
  5582. ImagePullPolicy: "IfNotPresent",
  5583. TerminationMessagePolicy: "File",
  5584. },
  5585. },
  5586. "invalid lifecycle, zero tcp socket port.": {
  5587. {
  5588. Name: "life-123",
  5589. Image: "image",
  5590. Lifecycle: &core.Lifecycle{
  5591. PreStop: &core.Handler{
  5592. TCPSocket: &core.TCPSocketAction{
  5593. Port: intstr.FromInt(0),
  5594. },
  5595. },
  5596. },
  5597. ImagePullPolicy: "IfNotPresent",
  5598. TerminationMessagePolicy: "File",
  5599. },
  5600. },
  5601. "invalid lifecycle, no action.": {
  5602. {
  5603. Name: "life-123",
  5604. Image: "image",
  5605. Lifecycle: &core.Lifecycle{
  5606. PreStop: &core.Handler{},
  5607. },
  5608. ImagePullPolicy: "IfNotPresent",
  5609. TerminationMessagePolicy: "File",
  5610. },
  5611. },
  5612. "invalid liveness probe, no tcp socket port.": {
  5613. {
  5614. Name: "life-123",
  5615. Image: "image",
  5616. LivenessProbe: &core.Probe{
  5617. Handler: core.Handler{
  5618. TCPSocket: &core.TCPSocketAction{},
  5619. },
  5620. },
  5621. ImagePullPolicy: "IfNotPresent",
  5622. TerminationMessagePolicy: "File",
  5623. },
  5624. },
  5625. "invalid liveness probe, no action.": {
  5626. {
  5627. Name: "life-123",
  5628. Image: "image",
  5629. LivenessProbe: &core.Probe{
  5630. Handler: core.Handler{},
  5631. },
  5632. ImagePullPolicy: "IfNotPresent",
  5633. TerminationMessagePolicy: "File",
  5634. },
  5635. },
  5636. "invalid message termination policy": {
  5637. {
  5638. Name: "life-123",
  5639. Image: "image",
  5640. ImagePullPolicy: "IfNotPresent",
  5641. TerminationMessagePolicy: "Unknown",
  5642. },
  5643. },
  5644. "empty message termination policy": {
  5645. {
  5646. Name: "life-123",
  5647. Image: "image",
  5648. ImagePullPolicy: "IfNotPresent",
  5649. TerminationMessagePolicy: "",
  5650. },
  5651. },
  5652. "privilege disabled": {
  5653. {Name: "abc", Image: "image", SecurityContext: fakeValidSecurityContext(true)},
  5654. },
  5655. "invalid compute resource": {
  5656. {
  5657. Name: "abc-123",
  5658. Image: "image",
  5659. Resources: core.ResourceRequirements{
  5660. Limits: core.ResourceList{
  5661. "disk": resource.MustParse("10G"),
  5662. },
  5663. },
  5664. ImagePullPolicy: "IfNotPresent",
  5665. TerminationMessagePolicy: "File",
  5666. },
  5667. },
  5668. "Resource CPU invalid": {
  5669. {
  5670. Name: "abc-123",
  5671. Image: "image",
  5672. Resources: core.ResourceRequirements{
  5673. Limits: getResourceLimits("-10", "0"),
  5674. },
  5675. ImagePullPolicy: "IfNotPresent",
  5676. TerminationMessagePolicy: "File",
  5677. },
  5678. },
  5679. "Resource Requests CPU invalid": {
  5680. {
  5681. Name: "abc-123",
  5682. Image: "image",
  5683. Resources: core.ResourceRequirements{
  5684. Requests: getResourceLimits("-10", "0"),
  5685. },
  5686. ImagePullPolicy: "IfNotPresent",
  5687. TerminationMessagePolicy: "File",
  5688. },
  5689. },
  5690. "Resource Memory invalid": {
  5691. {
  5692. Name: "abc-123",
  5693. Image: "image",
  5694. Resources: core.ResourceRequirements{
  5695. Limits: getResourceLimits("0", "-10"),
  5696. },
  5697. ImagePullPolicy: "IfNotPresent",
  5698. TerminationMessagePolicy: "File",
  5699. },
  5700. },
  5701. "Request limit simple invalid": {
  5702. {
  5703. Name: "abc-123",
  5704. Image: "image",
  5705. Resources: core.ResourceRequirements{
  5706. Limits: getResourceLimits("5", "3"),
  5707. Requests: getResourceLimits("6", "3"),
  5708. },
  5709. ImagePullPolicy: "IfNotPresent",
  5710. TerminationMessagePolicy: "File",
  5711. },
  5712. },
  5713. "Invalid storage limit request": {
  5714. {
  5715. Name: "abc-123",
  5716. Image: "image",
  5717. Resources: core.ResourceRequirements{
  5718. Limits: core.ResourceList{
  5719. core.ResourceName("attachable-volumes-aws-ebs"): *resource.NewQuantity(10, resource.DecimalSI),
  5720. },
  5721. },
  5722. ImagePullPolicy: "IfNotPresent",
  5723. TerminationMessagePolicy: "File",
  5724. },
  5725. },
  5726. "Request limit multiple invalid": {
  5727. {
  5728. Name: "abc-123",
  5729. Image: "image",
  5730. Resources: core.ResourceRequirements{
  5731. Limits: getResourceLimits("5", "3"),
  5732. Requests: getResourceLimits("6", "4"),
  5733. },
  5734. ImagePullPolicy: "IfNotPresent",
  5735. TerminationMessagePolicy: "File",
  5736. },
  5737. },
  5738. "Invalid env from": {
  5739. {
  5740. Name: "env-from-source",
  5741. Image: "image",
  5742. ImagePullPolicy: "IfNotPresent",
  5743. TerminationMessagePolicy: "File",
  5744. EnvFrom: []core.EnvFromSource{
  5745. {
  5746. ConfigMapRef: &core.ConfigMapEnvSource{
  5747. LocalObjectReference: core.LocalObjectReference{
  5748. Name: "$%^&*#",
  5749. },
  5750. },
  5751. },
  5752. },
  5753. },
  5754. },
  5755. }
  5756. for k, v := range errorCases {
  5757. if errs := validateContainers(v, false, volumeDevices, field.NewPath("field")); len(errs) == 0 {
  5758. t.Errorf("expected failure for %s", k)
  5759. }
  5760. }
  5761. }
  5762. func TestValidateInitContainers(t *testing.T) {
  5763. volumeDevices := make(map[string]core.VolumeSource)
  5764. capabilities.SetForTests(capabilities.Capabilities{
  5765. AllowPrivileged: true,
  5766. })
  5767. successCase := []core.Container{
  5768. {
  5769. Name: "container-1-same-host-port-different-protocol",
  5770. Image: "image",
  5771. Ports: []core.ContainerPort{
  5772. {ContainerPort: 80, HostPort: 80, Protocol: "TCP"},
  5773. {ContainerPort: 80, HostPort: 80, Protocol: "UDP"},
  5774. },
  5775. ImagePullPolicy: "IfNotPresent",
  5776. TerminationMessagePolicy: "File",
  5777. },
  5778. {
  5779. Name: "container-2-same-host-port-different-protocol",
  5780. Image: "image",
  5781. Ports: []core.ContainerPort{
  5782. {ContainerPort: 80, HostPort: 80, Protocol: "TCP"},
  5783. {ContainerPort: 80, HostPort: 80, Protocol: "UDP"},
  5784. },
  5785. ImagePullPolicy: "IfNotPresent",
  5786. TerminationMessagePolicy: "File",
  5787. },
  5788. }
  5789. if errs := validateContainers(successCase, true, volumeDevices, field.NewPath("field")); len(errs) != 0 {
  5790. t.Errorf("expected success: %v", errs)
  5791. }
  5792. capabilities.SetForTests(capabilities.Capabilities{
  5793. AllowPrivileged: false,
  5794. })
  5795. errorCases := map[string][]core.Container{
  5796. "duplicate ports": {
  5797. {
  5798. Name: "abc",
  5799. Image: "image",
  5800. Ports: []core.ContainerPort{
  5801. {
  5802. ContainerPort: 8080, HostPort: 8080, Protocol: "TCP",
  5803. },
  5804. {
  5805. ContainerPort: 8080, HostPort: 8080, Protocol: "TCP",
  5806. },
  5807. },
  5808. ImagePullPolicy: "IfNotPresent",
  5809. TerminationMessagePolicy: "File",
  5810. },
  5811. },
  5812. }
  5813. for k, v := range errorCases {
  5814. if errs := validateContainers(v, true, volumeDevices, field.NewPath("field")); len(errs) == 0 {
  5815. t.Errorf("expected failure for %s", k)
  5816. }
  5817. }
  5818. }
  5819. func TestValidateRestartPolicy(t *testing.T) {
  5820. successCases := []core.RestartPolicy{
  5821. core.RestartPolicyAlways,
  5822. core.RestartPolicyOnFailure,
  5823. core.RestartPolicyNever,
  5824. }
  5825. for _, policy := range successCases {
  5826. if errs := validateRestartPolicy(&policy, field.NewPath("field")); len(errs) != 0 {
  5827. t.Errorf("expected success: %v", errs)
  5828. }
  5829. }
  5830. errorCases := []core.RestartPolicy{"", "newpolicy"}
  5831. for k, policy := range errorCases {
  5832. if errs := validateRestartPolicy(&policy, field.NewPath("field")); len(errs) == 0 {
  5833. t.Errorf("expected failure for %d", k)
  5834. }
  5835. }
  5836. }
  5837. func TestValidateDNSPolicy(t *testing.T) {
  5838. successCases := []core.DNSPolicy{core.DNSClusterFirst, core.DNSDefault, core.DNSPolicy(core.DNSClusterFirst), core.DNSNone}
  5839. for _, policy := range successCases {
  5840. if errs := validateDNSPolicy(&policy, field.NewPath("field")); len(errs) != 0 {
  5841. t.Errorf("expected success: %v", errs)
  5842. }
  5843. }
  5844. errorCases := []core.DNSPolicy{core.DNSPolicy("invalid")}
  5845. for _, policy := range errorCases {
  5846. if errs := validateDNSPolicy(&policy, field.NewPath("field")); len(errs) == 0 {
  5847. t.Errorf("expected failure for %v", policy)
  5848. }
  5849. }
  5850. }
  5851. func TestValidatePodDNSConfig(t *testing.T) {
  5852. generateTestSearchPathFunc := func(numChars int) string {
  5853. res := ""
  5854. for i := 0; i < numChars; i++ {
  5855. res = res + "a"
  5856. }
  5857. return res
  5858. }
  5859. testOptionValue := "2"
  5860. testDNSNone := core.DNSNone
  5861. testDNSClusterFirst := core.DNSClusterFirst
  5862. testCases := []struct {
  5863. desc string
  5864. dnsConfig *core.PodDNSConfig
  5865. dnsPolicy *core.DNSPolicy
  5866. expectedError bool
  5867. }{
  5868. {
  5869. desc: "valid: empty DNSConfig",
  5870. dnsConfig: &core.PodDNSConfig{},
  5871. expectedError: false,
  5872. },
  5873. {
  5874. desc: "valid: 1 option",
  5875. dnsConfig: &core.PodDNSConfig{
  5876. Options: []core.PodDNSConfigOption{
  5877. {Name: "ndots", Value: &testOptionValue},
  5878. },
  5879. },
  5880. expectedError: false,
  5881. },
  5882. {
  5883. desc: "valid: 1 nameserver",
  5884. dnsConfig: &core.PodDNSConfig{
  5885. Nameservers: []string{"127.0.0.1"},
  5886. },
  5887. expectedError: false,
  5888. },
  5889. {
  5890. desc: "valid: DNSNone with 1 nameserver",
  5891. dnsConfig: &core.PodDNSConfig{
  5892. Nameservers: []string{"127.0.0.1"},
  5893. },
  5894. dnsPolicy: &testDNSNone,
  5895. expectedError: false,
  5896. },
  5897. {
  5898. desc: "valid: 1 search path",
  5899. dnsConfig: &core.PodDNSConfig{
  5900. Searches: []string{"custom"},
  5901. },
  5902. expectedError: false,
  5903. },
  5904. {
  5905. desc: "valid: 1 search path with trailing period",
  5906. dnsConfig: &core.PodDNSConfig{
  5907. Searches: []string{"custom."},
  5908. },
  5909. expectedError: false,
  5910. },
  5911. {
  5912. desc: "valid: 3 nameservers and 6 search paths",
  5913. dnsConfig: &core.PodDNSConfig{
  5914. Nameservers: []string{"127.0.0.1", "10.0.0.10", "8.8.8.8"},
  5915. Searches: []string{"custom", "mydomain.com", "local", "cluster.local", "svc.cluster.local", "default.svc.cluster.local."},
  5916. },
  5917. expectedError: false,
  5918. },
  5919. {
  5920. desc: "valid: 256 characters in search path list",
  5921. dnsConfig: &core.PodDNSConfig{
  5922. // We can have 256 - (6 - 1) = 251 characters in total for 6 search paths.
  5923. Searches: []string{
  5924. generateTestSearchPathFunc(1),
  5925. generateTestSearchPathFunc(50),
  5926. generateTestSearchPathFunc(50),
  5927. generateTestSearchPathFunc(50),
  5928. generateTestSearchPathFunc(50),
  5929. generateTestSearchPathFunc(50),
  5930. },
  5931. },
  5932. expectedError: false,
  5933. },
  5934. {
  5935. desc: "valid: ipv6 nameserver",
  5936. dnsConfig: &core.PodDNSConfig{
  5937. Nameservers: []string{"FE80::0202:B3FF:FE1E:8329"},
  5938. },
  5939. expectedError: false,
  5940. },
  5941. {
  5942. desc: "invalid: 4 nameservers",
  5943. dnsConfig: &core.PodDNSConfig{
  5944. Nameservers: []string{"127.0.0.1", "10.0.0.10", "8.8.8.8", "1.2.3.4"},
  5945. },
  5946. expectedError: true,
  5947. },
  5948. {
  5949. desc: "invalid: 7 search paths",
  5950. dnsConfig: &core.PodDNSConfig{
  5951. Searches: []string{"custom", "mydomain.com", "local", "cluster.local", "svc.cluster.local", "default.svc.cluster.local", "exceeded"},
  5952. },
  5953. expectedError: true,
  5954. },
  5955. {
  5956. desc: "invalid: 257 characters in search path list",
  5957. dnsConfig: &core.PodDNSConfig{
  5958. // We can have 256 - (6 - 1) = 251 characters in total for 6 search paths.
  5959. Searches: []string{
  5960. generateTestSearchPathFunc(2),
  5961. generateTestSearchPathFunc(50),
  5962. generateTestSearchPathFunc(50),
  5963. generateTestSearchPathFunc(50),
  5964. generateTestSearchPathFunc(50),
  5965. generateTestSearchPathFunc(50),
  5966. },
  5967. },
  5968. expectedError: true,
  5969. },
  5970. {
  5971. desc: "invalid search path",
  5972. dnsConfig: &core.PodDNSConfig{
  5973. Searches: []string{"custom?"},
  5974. },
  5975. expectedError: true,
  5976. },
  5977. {
  5978. desc: "invalid nameserver",
  5979. dnsConfig: &core.PodDNSConfig{
  5980. Nameservers: []string{"invalid"},
  5981. },
  5982. expectedError: true,
  5983. },
  5984. {
  5985. desc: "invalid empty option name",
  5986. dnsConfig: &core.PodDNSConfig{
  5987. Options: []core.PodDNSConfigOption{
  5988. {Value: &testOptionValue},
  5989. },
  5990. },
  5991. expectedError: true,
  5992. },
  5993. {
  5994. desc: "invalid: DNSNone with 0 nameserver",
  5995. dnsConfig: &core.PodDNSConfig{
  5996. Searches: []string{"custom"},
  5997. },
  5998. dnsPolicy: &testDNSNone,
  5999. expectedError: true,
  6000. },
  6001. }
  6002. for _, tc := range testCases {
  6003. if tc.dnsPolicy == nil {
  6004. tc.dnsPolicy = &testDNSClusterFirst
  6005. }
  6006. errs := validatePodDNSConfig(tc.dnsConfig, tc.dnsPolicy, field.NewPath("dnsConfig"))
  6007. if len(errs) != 0 && !tc.expectedError {
  6008. t.Errorf("%v: validatePodDNSConfig(%v) = %v, want nil", tc.desc, tc.dnsConfig, errs)
  6009. } else if len(errs) == 0 && tc.expectedError {
  6010. t.Errorf("%v: validatePodDNSConfig(%v) = nil, want error", tc.desc, tc.dnsConfig)
  6011. }
  6012. }
  6013. }
  6014. func TestValidatePodReadinessGates(t *testing.T) {
  6015. successCases := []struct {
  6016. desc string
  6017. readinessGates []core.PodReadinessGate
  6018. }{
  6019. {
  6020. "no gate",
  6021. []core.PodReadinessGate{},
  6022. },
  6023. {
  6024. "one readiness gate",
  6025. []core.PodReadinessGate{
  6026. {
  6027. ConditionType: core.PodConditionType("example.com/condition"),
  6028. },
  6029. },
  6030. },
  6031. {
  6032. "two readiness gates",
  6033. []core.PodReadinessGate{
  6034. {
  6035. ConditionType: core.PodConditionType("example.com/condition1"),
  6036. },
  6037. {
  6038. ConditionType: core.PodConditionType("example.com/condition2"),
  6039. },
  6040. },
  6041. },
  6042. }
  6043. for _, tc := range successCases {
  6044. if errs := validateReadinessGates(tc.readinessGates, field.NewPath("field")); len(errs) != 0 {
  6045. t.Errorf("expect tc %q to success: %v", tc.desc, errs)
  6046. }
  6047. }
  6048. errorCases := []struct {
  6049. desc string
  6050. readinessGates []core.PodReadinessGate
  6051. }{
  6052. {
  6053. "invalid condition type",
  6054. []core.PodReadinessGate{
  6055. {
  6056. ConditionType: core.PodConditionType("invalid/condition/type"),
  6057. },
  6058. },
  6059. },
  6060. }
  6061. for _, tc := range errorCases {
  6062. if errs := validateReadinessGates(tc.readinessGates, field.NewPath("field")); len(errs) == 0 {
  6063. t.Errorf("expected tc %q to fail", tc.desc)
  6064. }
  6065. }
  6066. }
  6067. func TestValidatePodConditions(t *testing.T) {
  6068. successCases := []struct {
  6069. desc string
  6070. podConditions []core.PodCondition
  6071. }{
  6072. {
  6073. "no condition",
  6074. []core.PodCondition{},
  6075. },
  6076. {
  6077. "one system condition",
  6078. []core.PodCondition{
  6079. {
  6080. Type: core.PodReady,
  6081. Status: core.ConditionTrue,
  6082. },
  6083. },
  6084. },
  6085. {
  6086. "one system condition and one custom condition",
  6087. []core.PodCondition{
  6088. {
  6089. Type: core.PodReady,
  6090. Status: core.ConditionTrue,
  6091. },
  6092. {
  6093. Type: core.PodConditionType("example.com/condition"),
  6094. Status: core.ConditionFalse,
  6095. },
  6096. },
  6097. },
  6098. {
  6099. "two custom condition",
  6100. []core.PodCondition{
  6101. {
  6102. Type: core.PodConditionType("foobar"),
  6103. Status: core.ConditionTrue,
  6104. },
  6105. {
  6106. Type: core.PodConditionType("example.com/condition"),
  6107. Status: core.ConditionFalse,
  6108. },
  6109. },
  6110. },
  6111. }
  6112. for _, tc := range successCases {
  6113. if errs := validatePodConditions(tc.podConditions, field.NewPath("field")); len(errs) != 0 {
  6114. t.Errorf("expected tc %q to success, but got: %v", tc.desc, errs)
  6115. }
  6116. }
  6117. errorCases := []struct {
  6118. desc string
  6119. podConditions []core.PodCondition
  6120. }{
  6121. {
  6122. "one system condition and a invalid custom condition",
  6123. []core.PodCondition{
  6124. {
  6125. Type: core.PodReady,
  6126. Status: core.ConditionStatus("True"),
  6127. },
  6128. {
  6129. Type: core.PodConditionType("invalid/custom/condition"),
  6130. Status: core.ConditionStatus("True"),
  6131. },
  6132. },
  6133. },
  6134. }
  6135. for _, tc := range errorCases {
  6136. if errs := validatePodConditions(tc.podConditions, field.NewPath("field")); len(errs) == 0 {
  6137. t.Errorf("expected tc %q to fail", tc.desc)
  6138. }
  6139. }
  6140. }
  6141. func TestValidatePodSpec(t *testing.T) {
  6142. activeDeadlineSeconds := int64(30)
  6143. activeDeadlineSecondsMax := int64(math.MaxInt32)
  6144. minUserID := int64(0)
  6145. maxUserID := int64(2147483647)
  6146. minGroupID := int64(0)
  6147. maxGroupID := int64(2147483647)
  6148. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodPriority, true)()
  6149. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.RuntimeClass, true)()
  6150. successCases := []core.PodSpec{
  6151. { // Populate basic fields, leave defaults for most.
  6152. Volumes: []core.Volume{{Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}}},
  6153. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6154. RestartPolicy: core.RestartPolicyAlways,
  6155. DNSPolicy: core.DNSClusterFirst,
  6156. },
  6157. { // Populate all fields.
  6158. Volumes: []core.Volume{
  6159. {Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}},
  6160. },
  6161. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6162. InitContainers: []core.Container{{Name: "ictr", Image: "iimage", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6163. RestartPolicy: core.RestartPolicyAlways,
  6164. NodeSelector: map[string]string{
  6165. "key": "value",
  6166. },
  6167. NodeName: "foobar",
  6168. DNSPolicy: core.DNSClusterFirst,
  6169. ActiveDeadlineSeconds: &activeDeadlineSeconds,
  6170. ServiceAccountName: "acct",
  6171. },
  6172. { // Populate all fields with larger active deadline.
  6173. Volumes: []core.Volume{
  6174. {Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}},
  6175. },
  6176. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6177. InitContainers: []core.Container{{Name: "ictr", Image: "iimage", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6178. RestartPolicy: core.RestartPolicyAlways,
  6179. NodeSelector: map[string]string{
  6180. "key": "value",
  6181. },
  6182. NodeName: "foobar",
  6183. DNSPolicy: core.DNSClusterFirst,
  6184. ActiveDeadlineSeconds: &activeDeadlineSecondsMax,
  6185. ServiceAccountName: "acct",
  6186. },
  6187. { // Populate HostNetwork.
  6188. Containers: []core.Container{
  6189. {Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
  6190. Ports: []core.ContainerPort{
  6191. {HostPort: 8080, ContainerPort: 8080, Protocol: "TCP"}},
  6192. },
  6193. },
  6194. SecurityContext: &core.PodSecurityContext{
  6195. HostNetwork: true,
  6196. },
  6197. RestartPolicy: core.RestartPolicyAlways,
  6198. DNSPolicy: core.DNSClusterFirst,
  6199. },
  6200. { // Populate RunAsUser SupplementalGroups FSGroup with minID 0
  6201. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6202. SecurityContext: &core.PodSecurityContext{
  6203. SupplementalGroups: []int64{minGroupID},
  6204. RunAsUser: &minUserID,
  6205. FSGroup: &minGroupID,
  6206. },
  6207. RestartPolicy: core.RestartPolicyAlways,
  6208. DNSPolicy: core.DNSClusterFirst,
  6209. },
  6210. { // Populate RunAsUser SupplementalGroups FSGroup with maxID 2147483647
  6211. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6212. SecurityContext: &core.PodSecurityContext{
  6213. SupplementalGroups: []int64{maxGroupID},
  6214. RunAsUser: &maxUserID,
  6215. FSGroup: &maxGroupID,
  6216. },
  6217. RestartPolicy: core.RestartPolicyAlways,
  6218. DNSPolicy: core.DNSClusterFirst,
  6219. },
  6220. { // Populate HostIPC.
  6221. SecurityContext: &core.PodSecurityContext{
  6222. HostIPC: true,
  6223. },
  6224. Volumes: []core.Volume{{Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}}},
  6225. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6226. RestartPolicy: core.RestartPolicyAlways,
  6227. DNSPolicy: core.DNSClusterFirst,
  6228. },
  6229. { // Populate HostPID.
  6230. SecurityContext: &core.PodSecurityContext{
  6231. HostPID: true,
  6232. },
  6233. Volumes: []core.Volume{{Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}}},
  6234. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6235. RestartPolicy: core.RestartPolicyAlways,
  6236. DNSPolicy: core.DNSClusterFirst,
  6237. },
  6238. { // Populate Affinity.
  6239. Volumes: []core.Volume{{Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}}},
  6240. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6241. RestartPolicy: core.RestartPolicyAlways,
  6242. DNSPolicy: core.DNSClusterFirst,
  6243. },
  6244. { // Populate HostAliases.
  6245. HostAliases: []core.HostAlias{{IP: "12.34.56.78", Hostnames: []string{"host1", "host2"}}},
  6246. Volumes: []core.Volume{{Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}}},
  6247. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6248. RestartPolicy: core.RestartPolicyAlways,
  6249. DNSPolicy: core.DNSClusterFirst,
  6250. },
  6251. { // Populate HostAliases with `foo.bar` hostnames.
  6252. HostAliases: []core.HostAlias{{IP: "12.34.56.78", Hostnames: []string{"host1.foo", "host2.bar"}}},
  6253. Volumes: []core.Volume{{Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}}},
  6254. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6255. RestartPolicy: core.RestartPolicyAlways,
  6256. DNSPolicy: core.DNSClusterFirst,
  6257. },
  6258. { // Populate HostAliases with HostNetwork.
  6259. HostAliases: []core.HostAlias{{IP: "12.34.56.78", Hostnames: []string{"host1.foo", "host2.bar"}}},
  6260. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6261. SecurityContext: &core.PodSecurityContext{
  6262. HostNetwork: true,
  6263. },
  6264. RestartPolicy: core.RestartPolicyAlways,
  6265. DNSPolicy: core.DNSClusterFirst,
  6266. },
  6267. { // Populate PriorityClassName.
  6268. Volumes: []core.Volume{{Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}}},
  6269. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6270. RestartPolicy: core.RestartPolicyAlways,
  6271. DNSPolicy: core.DNSClusterFirst,
  6272. PriorityClassName: "valid-name",
  6273. },
  6274. { // Populate ShareProcessNamespace
  6275. Volumes: []core.Volume{{Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}}},
  6276. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6277. RestartPolicy: core.RestartPolicyAlways,
  6278. DNSPolicy: core.DNSClusterFirst,
  6279. SecurityContext: &core.PodSecurityContext{
  6280. ShareProcessNamespace: &[]bool{true}[0],
  6281. },
  6282. },
  6283. { // Populate RuntimeClassName
  6284. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6285. RestartPolicy: core.RestartPolicyAlways,
  6286. DNSPolicy: core.DNSClusterFirst,
  6287. RuntimeClassName: utilpointer.StringPtr("valid-sandbox"),
  6288. },
  6289. }
  6290. for i := range successCases {
  6291. if errs := ValidatePodSpec(&successCases[i], field.NewPath("field")); len(errs) != 0 {
  6292. t.Errorf("expected success: %v", errs)
  6293. }
  6294. }
  6295. activeDeadlineSeconds = int64(0)
  6296. activeDeadlineSecondsTooLarge := int64(math.MaxInt32 + 1)
  6297. minUserID = int64(-1)
  6298. maxUserID = int64(2147483648)
  6299. minGroupID = int64(-1)
  6300. maxGroupID = int64(2147483648)
  6301. failureCases := map[string]core.PodSpec{
  6302. "bad volume": {
  6303. Volumes: []core.Volume{{}},
  6304. RestartPolicy: core.RestartPolicyAlways,
  6305. DNSPolicy: core.DNSClusterFirst,
  6306. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6307. },
  6308. "no containers": {
  6309. RestartPolicy: core.RestartPolicyAlways,
  6310. DNSPolicy: core.DNSClusterFirst,
  6311. },
  6312. "bad container": {
  6313. Containers: []core.Container{{}},
  6314. RestartPolicy: core.RestartPolicyAlways,
  6315. DNSPolicy: core.DNSClusterFirst,
  6316. },
  6317. "bad init container": {
  6318. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6319. InitContainers: []core.Container{{}},
  6320. RestartPolicy: core.RestartPolicyAlways,
  6321. DNSPolicy: core.DNSClusterFirst,
  6322. },
  6323. "bad DNS policy": {
  6324. DNSPolicy: core.DNSPolicy("invalid"),
  6325. RestartPolicy: core.RestartPolicyAlways,
  6326. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6327. },
  6328. "bad service account name": {
  6329. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6330. RestartPolicy: core.RestartPolicyAlways,
  6331. DNSPolicy: core.DNSClusterFirst,
  6332. ServiceAccountName: "invalidName",
  6333. },
  6334. "bad restart policy": {
  6335. RestartPolicy: "UnknowPolicy",
  6336. DNSPolicy: core.DNSClusterFirst,
  6337. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6338. },
  6339. "with hostNetwork hostPort not equal to containerPort": {
  6340. Containers: []core.Container{
  6341. {Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", Ports: []core.ContainerPort{
  6342. {HostPort: 8080, ContainerPort: 2600, Protocol: "TCP"}},
  6343. },
  6344. },
  6345. SecurityContext: &core.PodSecurityContext{
  6346. HostNetwork: true,
  6347. },
  6348. RestartPolicy: core.RestartPolicyAlways,
  6349. DNSPolicy: core.DNSClusterFirst,
  6350. },
  6351. "with hostAliases with invalid IP": {
  6352. SecurityContext: &core.PodSecurityContext{
  6353. HostNetwork: false,
  6354. },
  6355. HostAliases: []core.HostAlias{{IP: "999.999.999.999", Hostnames: []string{"host1", "host2"}}},
  6356. },
  6357. "with hostAliases with invalid hostname": {
  6358. SecurityContext: &core.PodSecurityContext{
  6359. HostNetwork: false,
  6360. },
  6361. HostAliases: []core.HostAlias{{IP: "12.34.56.78", Hostnames: []string{"@#$^#@#$"}}},
  6362. },
  6363. "bad supplementalGroups large than math.MaxInt32": {
  6364. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6365. SecurityContext: &core.PodSecurityContext{
  6366. HostNetwork: false,
  6367. SupplementalGroups: []int64{maxGroupID, 1234},
  6368. },
  6369. RestartPolicy: core.RestartPolicyAlways,
  6370. DNSPolicy: core.DNSClusterFirst,
  6371. },
  6372. "bad supplementalGroups less than 0": {
  6373. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6374. SecurityContext: &core.PodSecurityContext{
  6375. HostNetwork: false,
  6376. SupplementalGroups: []int64{minGroupID, 1234},
  6377. },
  6378. RestartPolicy: core.RestartPolicyAlways,
  6379. DNSPolicy: core.DNSClusterFirst,
  6380. },
  6381. "bad runAsUser large than math.MaxInt32": {
  6382. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6383. SecurityContext: &core.PodSecurityContext{
  6384. HostNetwork: false,
  6385. RunAsUser: &maxUserID,
  6386. },
  6387. RestartPolicy: core.RestartPolicyAlways,
  6388. DNSPolicy: core.DNSClusterFirst,
  6389. },
  6390. "bad runAsUser less than 0": {
  6391. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6392. SecurityContext: &core.PodSecurityContext{
  6393. HostNetwork: false,
  6394. RunAsUser: &minUserID,
  6395. },
  6396. RestartPolicy: core.RestartPolicyAlways,
  6397. DNSPolicy: core.DNSClusterFirst,
  6398. },
  6399. "bad fsGroup large than math.MaxInt32": {
  6400. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6401. SecurityContext: &core.PodSecurityContext{
  6402. HostNetwork: false,
  6403. FSGroup: &maxGroupID,
  6404. },
  6405. RestartPolicy: core.RestartPolicyAlways,
  6406. DNSPolicy: core.DNSClusterFirst,
  6407. },
  6408. "bad fsGroup less than 0": {
  6409. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6410. SecurityContext: &core.PodSecurityContext{
  6411. HostNetwork: false,
  6412. FSGroup: &minGroupID,
  6413. },
  6414. RestartPolicy: core.RestartPolicyAlways,
  6415. DNSPolicy: core.DNSClusterFirst,
  6416. },
  6417. "bad-active-deadline-seconds": {
  6418. Volumes: []core.Volume{
  6419. {Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}},
  6420. },
  6421. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6422. RestartPolicy: core.RestartPolicyAlways,
  6423. NodeSelector: map[string]string{
  6424. "key": "value",
  6425. },
  6426. NodeName: "foobar",
  6427. DNSPolicy: core.DNSClusterFirst,
  6428. ActiveDeadlineSeconds: &activeDeadlineSeconds,
  6429. },
  6430. "active-deadline-seconds-too-large": {
  6431. Volumes: []core.Volume{
  6432. {Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}},
  6433. },
  6434. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6435. RestartPolicy: core.RestartPolicyAlways,
  6436. NodeSelector: map[string]string{
  6437. "key": "value",
  6438. },
  6439. NodeName: "foobar",
  6440. DNSPolicy: core.DNSClusterFirst,
  6441. ActiveDeadlineSeconds: &activeDeadlineSecondsTooLarge,
  6442. },
  6443. "bad nodeName": {
  6444. NodeName: "node name",
  6445. Volumes: []core.Volume{{Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}}},
  6446. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6447. RestartPolicy: core.RestartPolicyAlways,
  6448. DNSPolicy: core.DNSClusterFirst,
  6449. },
  6450. "bad PriorityClassName": {
  6451. Volumes: []core.Volume{{Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}}},
  6452. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6453. RestartPolicy: core.RestartPolicyAlways,
  6454. DNSPolicy: core.DNSClusterFirst,
  6455. PriorityClassName: "InvalidName",
  6456. },
  6457. "ShareProcessNamespace and HostPID both set": {
  6458. Volumes: []core.Volume{{Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}}},
  6459. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6460. RestartPolicy: core.RestartPolicyAlways,
  6461. DNSPolicy: core.DNSClusterFirst,
  6462. SecurityContext: &core.PodSecurityContext{
  6463. HostPID: true,
  6464. ShareProcessNamespace: &[]bool{true}[0],
  6465. },
  6466. },
  6467. "bad RuntimeClassName": {
  6468. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6469. RestartPolicy: core.RestartPolicyAlways,
  6470. DNSPolicy: core.DNSClusterFirst,
  6471. RuntimeClassName: utilpointer.StringPtr("invalid/sandbox"),
  6472. },
  6473. }
  6474. for k, v := range failureCases {
  6475. if errs := ValidatePodSpec(&v, field.NewPath("field")); len(errs) == 0 {
  6476. t.Errorf("expected failure for %q", k)
  6477. }
  6478. }
  6479. }
  6480. func extendPodSpecwithTolerations(in core.PodSpec, tolerations []core.Toleration) core.PodSpec {
  6481. var out core.PodSpec
  6482. out.Containers = in.Containers
  6483. out.RestartPolicy = in.RestartPolicy
  6484. out.DNSPolicy = in.DNSPolicy
  6485. out.Tolerations = tolerations
  6486. return out
  6487. }
  6488. func TestValidatePod(t *testing.T) {
  6489. validPodSpec := func(affinity *core.Affinity) core.PodSpec {
  6490. spec := core.PodSpec{
  6491. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6492. RestartPolicy: core.RestartPolicyAlways,
  6493. DNSPolicy: core.DNSClusterFirst,
  6494. }
  6495. if affinity != nil {
  6496. spec.Affinity = affinity
  6497. }
  6498. return spec
  6499. }
  6500. successCases := []core.Pod{
  6501. { // Basic fields.
  6502. ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
  6503. Spec: core.PodSpec{
  6504. Volumes: []core.Volume{{Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}}},
  6505. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6506. RestartPolicy: core.RestartPolicyAlways,
  6507. DNSPolicy: core.DNSClusterFirst,
  6508. },
  6509. },
  6510. { // Just about everything.
  6511. ObjectMeta: metav1.ObjectMeta{Name: "abc.123.do-re-mi", Namespace: "ns"},
  6512. Spec: core.PodSpec{
  6513. Volumes: []core.Volume{
  6514. {Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}},
  6515. },
  6516. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6517. RestartPolicy: core.RestartPolicyAlways,
  6518. DNSPolicy: core.DNSClusterFirst,
  6519. NodeSelector: map[string]string{
  6520. "key": "value",
  6521. },
  6522. NodeName: "foobar",
  6523. },
  6524. },
  6525. { // Serialized node affinity requirements.
  6526. ObjectMeta: metav1.ObjectMeta{
  6527. Name: "123",
  6528. Namespace: "ns",
  6529. },
  6530. Spec: validPodSpec(
  6531. // TODO: Uncomment and move this block and move inside NodeAffinity once
  6532. // RequiredDuringSchedulingRequiredDuringExecution is implemented
  6533. // RequiredDuringSchedulingRequiredDuringExecution: &core.NodeSelector{
  6534. // NodeSelectorTerms: []core.NodeSelectorTerm{
  6535. // {
  6536. // MatchExpressions: []core.NodeSelectorRequirement{
  6537. // {
  6538. // Key: "key1",
  6539. // Operator: core.NodeSelectorOpExists
  6540. // },
  6541. // },
  6542. // },
  6543. // },
  6544. // },
  6545. &core.Affinity{
  6546. NodeAffinity: &core.NodeAffinity{
  6547. RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
  6548. NodeSelectorTerms: []core.NodeSelectorTerm{
  6549. {
  6550. MatchExpressions: []core.NodeSelectorRequirement{
  6551. {
  6552. Key: "key2",
  6553. Operator: core.NodeSelectorOpIn,
  6554. Values: []string{"value1", "value2"},
  6555. },
  6556. },
  6557. MatchFields: []core.NodeSelectorRequirement{
  6558. {
  6559. Key: "metadata.name",
  6560. Operator: core.NodeSelectorOpIn,
  6561. Values: []string{"host1"},
  6562. },
  6563. },
  6564. },
  6565. },
  6566. },
  6567. PreferredDuringSchedulingIgnoredDuringExecution: []core.PreferredSchedulingTerm{
  6568. {
  6569. Weight: 10,
  6570. Preference: core.NodeSelectorTerm{
  6571. MatchExpressions: []core.NodeSelectorRequirement{
  6572. {
  6573. Key: "foo",
  6574. Operator: core.NodeSelectorOpIn,
  6575. Values: []string{"bar"},
  6576. },
  6577. },
  6578. },
  6579. },
  6580. },
  6581. },
  6582. },
  6583. ),
  6584. },
  6585. { // Serialized node affinity requirements.
  6586. ObjectMeta: metav1.ObjectMeta{
  6587. Name: "123",
  6588. Namespace: "ns",
  6589. },
  6590. Spec: validPodSpec(
  6591. // TODO: Uncomment and move this block and move inside NodeAffinity once
  6592. // RequiredDuringSchedulingRequiredDuringExecution is implemented
  6593. // RequiredDuringSchedulingRequiredDuringExecution: &core.NodeSelector{
  6594. // NodeSelectorTerms: []core.NodeSelectorTerm{
  6595. // {
  6596. // MatchExpressions: []core.NodeSelectorRequirement{
  6597. // {
  6598. // Key: "key1",
  6599. // Operator: core.NodeSelectorOpExists
  6600. // },
  6601. // },
  6602. // },
  6603. // },
  6604. // },
  6605. &core.Affinity{
  6606. NodeAffinity: &core.NodeAffinity{
  6607. RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
  6608. NodeSelectorTerms: []core.NodeSelectorTerm{
  6609. {
  6610. MatchExpressions: []core.NodeSelectorRequirement{},
  6611. },
  6612. },
  6613. },
  6614. PreferredDuringSchedulingIgnoredDuringExecution: []core.PreferredSchedulingTerm{
  6615. {
  6616. Weight: 10,
  6617. Preference: core.NodeSelectorTerm{
  6618. MatchExpressions: []core.NodeSelectorRequirement{},
  6619. },
  6620. },
  6621. },
  6622. },
  6623. },
  6624. ),
  6625. },
  6626. { // Serialized pod affinity in affinity requirements in annotations.
  6627. ObjectMeta: metav1.ObjectMeta{
  6628. Name: "123",
  6629. Namespace: "ns",
  6630. // TODO: Uncomment and move this block into Annotations map once
  6631. // RequiredDuringSchedulingRequiredDuringExecution is implemented
  6632. // "requiredDuringSchedulingRequiredDuringExecution": [{
  6633. // "labelSelector": {
  6634. // "matchExpressions": [{
  6635. // "key": "key2",
  6636. // "operator": "In",
  6637. // "values": ["value1", "value2"]
  6638. // }]
  6639. // },
  6640. // "namespaces":["ns"],
  6641. // "topologyKey": "zone"
  6642. // }]
  6643. },
  6644. Spec: validPodSpec(&core.Affinity{
  6645. PodAffinity: &core.PodAffinity{
  6646. RequiredDuringSchedulingIgnoredDuringExecution: []core.PodAffinityTerm{
  6647. {
  6648. LabelSelector: &metav1.LabelSelector{
  6649. MatchExpressions: []metav1.LabelSelectorRequirement{
  6650. {
  6651. Key: "key2",
  6652. Operator: metav1.LabelSelectorOpIn,
  6653. Values: []string{"value1", "value2"},
  6654. },
  6655. },
  6656. },
  6657. TopologyKey: "zone",
  6658. Namespaces: []string{"ns"},
  6659. },
  6660. },
  6661. PreferredDuringSchedulingIgnoredDuringExecution: []core.WeightedPodAffinityTerm{
  6662. {
  6663. Weight: 10,
  6664. PodAffinityTerm: core.PodAffinityTerm{
  6665. LabelSelector: &metav1.LabelSelector{
  6666. MatchExpressions: []metav1.LabelSelectorRequirement{
  6667. {
  6668. Key: "key2",
  6669. Operator: metav1.LabelSelectorOpNotIn,
  6670. Values: []string{"value1", "value2"},
  6671. },
  6672. },
  6673. },
  6674. Namespaces: []string{"ns"},
  6675. TopologyKey: "region",
  6676. },
  6677. },
  6678. },
  6679. },
  6680. }),
  6681. },
  6682. { // Serialized pod anti affinity with different Label Operators in affinity requirements in annotations.
  6683. ObjectMeta: metav1.ObjectMeta{
  6684. Name: "123",
  6685. Namespace: "ns",
  6686. // TODO: Uncomment and move this block into Annotations map once
  6687. // RequiredDuringSchedulingRequiredDuringExecution is implemented
  6688. // "requiredDuringSchedulingRequiredDuringExecution": [{
  6689. // "labelSelector": {
  6690. // "matchExpressions": [{
  6691. // "key": "key2",
  6692. // "operator": "In",
  6693. // "values": ["value1", "value2"]
  6694. // }]
  6695. // },
  6696. // "namespaces":["ns"],
  6697. // "topologyKey": "zone"
  6698. // }]
  6699. },
  6700. Spec: validPodSpec(&core.Affinity{
  6701. PodAntiAffinity: &core.PodAntiAffinity{
  6702. RequiredDuringSchedulingIgnoredDuringExecution: []core.PodAffinityTerm{
  6703. {
  6704. LabelSelector: &metav1.LabelSelector{
  6705. MatchExpressions: []metav1.LabelSelectorRequirement{
  6706. {
  6707. Key: "key2",
  6708. Operator: metav1.LabelSelectorOpExists,
  6709. },
  6710. },
  6711. },
  6712. TopologyKey: "zone",
  6713. Namespaces: []string{"ns"},
  6714. },
  6715. },
  6716. PreferredDuringSchedulingIgnoredDuringExecution: []core.WeightedPodAffinityTerm{
  6717. {
  6718. Weight: 10,
  6719. PodAffinityTerm: core.PodAffinityTerm{
  6720. LabelSelector: &metav1.LabelSelector{
  6721. MatchExpressions: []metav1.LabelSelectorRequirement{
  6722. {
  6723. Key: "key2",
  6724. Operator: metav1.LabelSelectorOpDoesNotExist,
  6725. },
  6726. },
  6727. },
  6728. Namespaces: []string{"ns"},
  6729. TopologyKey: "region",
  6730. },
  6731. },
  6732. },
  6733. },
  6734. }),
  6735. },
  6736. { // populate forgiveness tolerations with exists operator in annotations.
  6737. ObjectMeta: metav1.ObjectMeta{
  6738. Name: "123",
  6739. Namespace: "ns",
  6740. },
  6741. Spec: extendPodSpecwithTolerations(validPodSpec(nil), []core.Toleration{{Key: "foo", Operator: "Exists", Value: "", Effect: "NoExecute", TolerationSeconds: &[]int64{60}[0]}}),
  6742. },
  6743. { // populate forgiveness tolerations with equal operator in annotations.
  6744. ObjectMeta: metav1.ObjectMeta{
  6745. Name: "123",
  6746. Namespace: "ns",
  6747. },
  6748. Spec: extendPodSpecwithTolerations(validPodSpec(nil), []core.Toleration{{Key: "foo", Operator: "Equal", Value: "bar", Effect: "NoExecute", TolerationSeconds: &[]int64{60}[0]}}),
  6749. },
  6750. { // populate tolerations equal operator in annotations.
  6751. ObjectMeta: metav1.ObjectMeta{
  6752. Name: "123",
  6753. Namespace: "ns",
  6754. },
  6755. Spec: extendPodSpecwithTolerations(validPodSpec(nil), []core.Toleration{{Key: "foo", Operator: "Equal", Value: "bar", Effect: "NoSchedule"}}),
  6756. },
  6757. { // populate tolerations exists operator in annotations.
  6758. ObjectMeta: metav1.ObjectMeta{
  6759. Name: "123",
  6760. Namespace: "ns",
  6761. },
  6762. Spec: validPodSpec(nil),
  6763. },
  6764. { // empty key with Exists operator is OK for toleration, empty toleration key means match all taint keys.
  6765. ObjectMeta: metav1.ObjectMeta{
  6766. Name: "123",
  6767. Namespace: "ns",
  6768. },
  6769. Spec: extendPodSpecwithTolerations(validPodSpec(nil), []core.Toleration{{Operator: "Exists", Effect: "NoSchedule"}}),
  6770. },
  6771. { // empty operator is OK for toleration, defaults to Equal.
  6772. ObjectMeta: metav1.ObjectMeta{
  6773. Name: "123",
  6774. Namespace: "ns",
  6775. },
  6776. Spec: extendPodSpecwithTolerations(validPodSpec(nil), []core.Toleration{{Key: "foo", Value: "bar", Effect: "NoSchedule"}}),
  6777. },
  6778. { // empty effect is OK for toleration, empty toleration effect means match all taint effects.
  6779. ObjectMeta: metav1.ObjectMeta{
  6780. Name: "123",
  6781. Namespace: "ns",
  6782. },
  6783. Spec: extendPodSpecwithTolerations(validPodSpec(nil), []core.Toleration{{Key: "foo", Operator: "Equal", Value: "bar"}}),
  6784. },
  6785. { // negative tolerationSeconds is OK for toleration.
  6786. ObjectMeta: metav1.ObjectMeta{
  6787. Name: "pod-forgiveness-invalid",
  6788. Namespace: "ns",
  6789. },
  6790. Spec: extendPodSpecwithTolerations(validPodSpec(nil), []core.Toleration{{Key: "node.kubernetes.io/not-ready", Operator: "Exists", Effect: "NoExecute", TolerationSeconds: &[]int64{-2}[0]}}),
  6791. },
  6792. { // runtime default seccomp profile
  6793. ObjectMeta: metav1.ObjectMeta{
  6794. Name: "123",
  6795. Namespace: "ns",
  6796. Annotations: map[string]string{
  6797. core.SeccompPodAnnotationKey: core.SeccompProfileRuntimeDefault,
  6798. },
  6799. },
  6800. Spec: validPodSpec(nil),
  6801. },
  6802. { // docker default seccomp profile
  6803. ObjectMeta: metav1.ObjectMeta{
  6804. Name: "123",
  6805. Namespace: "ns",
  6806. Annotations: map[string]string{
  6807. core.SeccompPodAnnotationKey: core.DeprecatedSeccompProfileDockerDefault,
  6808. },
  6809. },
  6810. Spec: validPodSpec(nil),
  6811. },
  6812. { // unconfined seccomp profile
  6813. ObjectMeta: metav1.ObjectMeta{
  6814. Name: "123",
  6815. Namespace: "ns",
  6816. Annotations: map[string]string{
  6817. core.SeccompPodAnnotationKey: "unconfined",
  6818. },
  6819. },
  6820. Spec: validPodSpec(nil),
  6821. },
  6822. { // localhost seccomp profile
  6823. ObjectMeta: metav1.ObjectMeta{
  6824. Name: "123",
  6825. Namespace: "ns",
  6826. Annotations: map[string]string{
  6827. core.SeccompPodAnnotationKey: "localhost/foo",
  6828. },
  6829. },
  6830. Spec: validPodSpec(nil),
  6831. },
  6832. { // localhost seccomp profile for a container
  6833. ObjectMeta: metav1.ObjectMeta{
  6834. Name: "123",
  6835. Namespace: "ns",
  6836. Annotations: map[string]string{
  6837. core.SeccompContainerAnnotationKeyPrefix + "foo": "localhost/foo",
  6838. },
  6839. },
  6840. Spec: validPodSpec(nil),
  6841. },
  6842. { // default AppArmor profile for a container
  6843. ObjectMeta: metav1.ObjectMeta{
  6844. Name: "123",
  6845. Namespace: "ns",
  6846. Annotations: map[string]string{
  6847. apparmor.ContainerAnnotationKeyPrefix + "ctr": apparmor.ProfileRuntimeDefault,
  6848. },
  6849. },
  6850. Spec: validPodSpec(nil),
  6851. },
  6852. { // default AppArmor profile for an init container
  6853. ObjectMeta: metav1.ObjectMeta{
  6854. Name: "123",
  6855. Namespace: "ns",
  6856. Annotations: map[string]string{
  6857. apparmor.ContainerAnnotationKeyPrefix + "init-ctr": apparmor.ProfileRuntimeDefault,
  6858. },
  6859. },
  6860. Spec: core.PodSpec{
  6861. InitContainers: []core.Container{{Name: "init-ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6862. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6863. RestartPolicy: core.RestartPolicyAlways,
  6864. DNSPolicy: core.DNSClusterFirst,
  6865. },
  6866. },
  6867. { // localhost AppArmor profile for a container
  6868. ObjectMeta: metav1.ObjectMeta{
  6869. Name: "123",
  6870. Namespace: "ns",
  6871. Annotations: map[string]string{
  6872. apparmor.ContainerAnnotationKeyPrefix + "ctr": apparmor.ProfileNamePrefix + "foo",
  6873. },
  6874. },
  6875. Spec: validPodSpec(nil),
  6876. },
  6877. { // syntactically valid sysctls
  6878. ObjectMeta: metav1.ObjectMeta{
  6879. Name: "123",
  6880. Namespace: "ns",
  6881. },
  6882. Spec: core.PodSpec{
  6883. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6884. RestartPolicy: core.RestartPolicyAlways,
  6885. DNSPolicy: core.DNSClusterFirst,
  6886. SecurityContext: &core.PodSecurityContext{
  6887. Sysctls: []core.Sysctl{
  6888. {
  6889. Name: "kernel.shmmni",
  6890. Value: "32768",
  6891. },
  6892. {
  6893. Name: "kernel.shmmax",
  6894. Value: "1000000000",
  6895. },
  6896. {
  6897. Name: "knet.ipv4.route.min_pmtu",
  6898. Value: "1000",
  6899. },
  6900. },
  6901. },
  6902. },
  6903. },
  6904. { // valid extended resources for init container
  6905. ObjectMeta: metav1.ObjectMeta{Name: "valid-extended", Namespace: "ns"},
  6906. Spec: core.PodSpec{
  6907. InitContainers: []core.Container{
  6908. {
  6909. Name: "valid-extended",
  6910. Image: "image",
  6911. ImagePullPolicy: "IfNotPresent",
  6912. Resources: core.ResourceRequirements{
  6913. Requests: core.ResourceList{
  6914. core.ResourceName("example.com/a"): resource.MustParse("10"),
  6915. },
  6916. Limits: core.ResourceList{
  6917. core.ResourceName("example.com/a"): resource.MustParse("10"),
  6918. },
  6919. },
  6920. TerminationMessagePolicy: "File",
  6921. },
  6922. },
  6923. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6924. RestartPolicy: core.RestartPolicyAlways,
  6925. DNSPolicy: core.DNSClusterFirst,
  6926. },
  6927. },
  6928. { // valid extended resources for regular container
  6929. ObjectMeta: metav1.ObjectMeta{Name: "valid-extended", Namespace: "ns"},
  6930. Spec: core.PodSpec{
  6931. InitContainers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6932. Containers: []core.Container{
  6933. {
  6934. Name: "valid-extended",
  6935. Image: "image",
  6936. ImagePullPolicy: "IfNotPresent",
  6937. Resources: core.ResourceRequirements{
  6938. Requests: core.ResourceList{
  6939. core.ResourceName("example.com/a"): resource.MustParse("10"),
  6940. },
  6941. Limits: core.ResourceList{
  6942. core.ResourceName("example.com/a"): resource.MustParse("10"),
  6943. },
  6944. },
  6945. TerminationMessagePolicy: "File",
  6946. },
  6947. },
  6948. RestartPolicy: core.RestartPolicyAlways,
  6949. DNSPolicy: core.DNSClusterFirst,
  6950. },
  6951. },
  6952. { // valid serviceaccount token projected volume with serviceaccount name specified
  6953. ObjectMeta: metav1.ObjectMeta{Name: "valid-extended", Namespace: "ns"},
  6954. Spec: core.PodSpec{
  6955. ServiceAccountName: "some-service-account",
  6956. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6957. RestartPolicy: core.RestartPolicyAlways,
  6958. DNSPolicy: core.DNSClusterFirst,
  6959. Volumes: []core.Volume{
  6960. {
  6961. Name: "projected-volume",
  6962. VolumeSource: core.VolumeSource{
  6963. Projected: &core.ProjectedVolumeSource{
  6964. Sources: []core.VolumeProjection{
  6965. {
  6966. ServiceAccountToken: &core.ServiceAccountTokenProjection{
  6967. Audience: "foo-audience",
  6968. ExpirationSeconds: 6000,
  6969. Path: "foo-path",
  6970. },
  6971. },
  6972. },
  6973. },
  6974. },
  6975. },
  6976. },
  6977. },
  6978. },
  6979. }
  6980. for _, pod := range successCases {
  6981. if errs := ValidatePod(&pod); len(errs) != 0 {
  6982. t.Errorf("expected success: %v", errs)
  6983. }
  6984. }
  6985. errorCases := map[string]struct {
  6986. spec core.Pod
  6987. expectedError string
  6988. }{
  6989. "bad name": {
  6990. expectedError: "metadata.name",
  6991. spec: core.Pod{
  6992. ObjectMeta: metav1.ObjectMeta{Name: "", Namespace: "ns"},
  6993. Spec: core.PodSpec{
  6994. RestartPolicy: core.RestartPolicyAlways,
  6995. DNSPolicy: core.DNSClusterFirst,
  6996. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6997. },
  6998. },
  6999. },
  7000. "image whitespace": {
  7001. expectedError: "spec.containers[0].image",
  7002. spec: core.Pod{
  7003. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "ns"},
  7004. Spec: core.PodSpec{
  7005. RestartPolicy: core.RestartPolicyAlways,
  7006. DNSPolicy: core.DNSClusterFirst,
  7007. Containers: []core.Container{{Name: "ctr", Image: " ", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  7008. },
  7009. },
  7010. },
  7011. "image leading and trailing whitespace": {
  7012. expectedError: "spec.containers[0].image",
  7013. spec: core.Pod{
  7014. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "ns"},
  7015. Spec: core.PodSpec{
  7016. RestartPolicy: core.RestartPolicyAlways,
  7017. DNSPolicy: core.DNSClusterFirst,
  7018. Containers: []core.Container{{Name: "ctr", Image: " something ", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  7019. },
  7020. },
  7021. },
  7022. "bad namespace": {
  7023. expectedError: "metadata.namespace",
  7024. spec: core.Pod{
  7025. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: ""},
  7026. Spec: core.PodSpec{
  7027. RestartPolicy: core.RestartPolicyAlways,
  7028. DNSPolicy: core.DNSClusterFirst,
  7029. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  7030. },
  7031. },
  7032. },
  7033. "bad spec": {
  7034. expectedError: "spec.containers[0].name",
  7035. spec: core.Pod{
  7036. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "ns"},
  7037. Spec: core.PodSpec{
  7038. Containers: []core.Container{{}},
  7039. },
  7040. },
  7041. },
  7042. "bad label": {
  7043. expectedError: "NoUppercaseOrSpecialCharsLike=Equals",
  7044. spec: core.Pod{
  7045. ObjectMeta: metav1.ObjectMeta{
  7046. Name: "abc",
  7047. Namespace: "ns",
  7048. Labels: map[string]string{
  7049. "NoUppercaseOrSpecialCharsLike=Equals": "bar",
  7050. },
  7051. },
  7052. Spec: core.PodSpec{
  7053. RestartPolicy: core.RestartPolicyAlways,
  7054. DNSPolicy: core.DNSClusterFirst,
  7055. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  7056. },
  7057. },
  7058. },
  7059. "invalid node selector requirement in node affinity, operator can't be null": {
  7060. expectedError: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].operator",
  7061. spec: core.Pod{
  7062. ObjectMeta: metav1.ObjectMeta{
  7063. Name: "123",
  7064. Namespace: "ns",
  7065. },
  7066. Spec: validPodSpec(&core.Affinity{
  7067. NodeAffinity: &core.NodeAffinity{
  7068. RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
  7069. NodeSelectorTerms: []core.NodeSelectorTerm{
  7070. {
  7071. MatchExpressions: []core.NodeSelectorRequirement{
  7072. {
  7073. Key: "key1",
  7074. },
  7075. },
  7076. },
  7077. },
  7078. },
  7079. },
  7080. }),
  7081. },
  7082. },
  7083. "invalid node selector requirement in node affinity, key is invalid": {
  7084. expectedError: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].key",
  7085. spec: core.Pod{
  7086. ObjectMeta: metav1.ObjectMeta{
  7087. Name: "123",
  7088. Namespace: "ns",
  7089. },
  7090. Spec: validPodSpec(&core.Affinity{
  7091. NodeAffinity: &core.NodeAffinity{
  7092. RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
  7093. NodeSelectorTerms: []core.NodeSelectorTerm{
  7094. {
  7095. MatchExpressions: []core.NodeSelectorRequirement{
  7096. {
  7097. Key: "invalid key ___@#",
  7098. Operator: core.NodeSelectorOpExists,
  7099. },
  7100. },
  7101. },
  7102. },
  7103. },
  7104. },
  7105. }),
  7106. },
  7107. },
  7108. "invalid node field selector requirement in node affinity, more values for field selector": {
  7109. expectedError: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchFields[0].values",
  7110. spec: core.Pod{
  7111. ObjectMeta: metav1.ObjectMeta{
  7112. Name: "123",
  7113. Namespace: "ns",
  7114. },
  7115. Spec: validPodSpec(&core.Affinity{
  7116. NodeAffinity: &core.NodeAffinity{
  7117. RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
  7118. NodeSelectorTerms: []core.NodeSelectorTerm{
  7119. {
  7120. MatchFields: []core.NodeSelectorRequirement{
  7121. {
  7122. Key: "metadata.name",
  7123. Operator: core.NodeSelectorOpIn,
  7124. Values: []string{"host1", "host2"},
  7125. },
  7126. },
  7127. },
  7128. },
  7129. },
  7130. },
  7131. }),
  7132. },
  7133. },
  7134. "invalid node field selector requirement in node affinity, invalid operator": {
  7135. expectedError: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchFields[0].operator",
  7136. spec: core.Pod{
  7137. ObjectMeta: metav1.ObjectMeta{
  7138. Name: "123",
  7139. Namespace: "ns",
  7140. },
  7141. Spec: validPodSpec(&core.Affinity{
  7142. NodeAffinity: &core.NodeAffinity{
  7143. RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
  7144. NodeSelectorTerms: []core.NodeSelectorTerm{
  7145. {
  7146. MatchFields: []core.NodeSelectorRequirement{
  7147. {
  7148. Key: "metadata.name",
  7149. Operator: core.NodeSelectorOpExists,
  7150. },
  7151. },
  7152. },
  7153. },
  7154. },
  7155. },
  7156. }),
  7157. },
  7158. },
  7159. "invalid node field selector requirement in node affinity, invalid key": {
  7160. expectedError: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchFields[0].key",
  7161. spec: core.Pod{
  7162. ObjectMeta: metav1.ObjectMeta{
  7163. Name: "123",
  7164. Namespace: "ns",
  7165. },
  7166. Spec: validPodSpec(&core.Affinity{
  7167. NodeAffinity: &core.NodeAffinity{
  7168. RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
  7169. NodeSelectorTerms: []core.NodeSelectorTerm{
  7170. {
  7171. MatchFields: []core.NodeSelectorRequirement{
  7172. {
  7173. Key: "metadata.namespace",
  7174. Operator: core.NodeSelectorOpIn,
  7175. Values: []string{"ns1"},
  7176. },
  7177. },
  7178. },
  7179. },
  7180. },
  7181. },
  7182. }),
  7183. },
  7184. },
  7185. "invalid preferredSchedulingTerm in node affinity, weight should be in range 1-100": {
  7186. expectedError: "must be in the range 1-100",
  7187. spec: core.Pod{
  7188. ObjectMeta: metav1.ObjectMeta{
  7189. Name: "123",
  7190. Namespace: "ns",
  7191. },
  7192. Spec: validPodSpec(&core.Affinity{
  7193. NodeAffinity: &core.NodeAffinity{
  7194. PreferredDuringSchedulingIgnoredDuringExecution: []core.PreferredSchedulingTerm{
  7195. {
  7196. Weight: 199,
  7197. Preference: core.NodeSelectorTerm{
  7198. MatchExpressions: []core.NodeSelectorRequirement{
  7199. {
  7200. Key: "foo",
  7201. Operator: core.NodeSelectorOpIn,
  7202. Values: []string{"bar"},
  7203. },
  7204. },
  7205. },
  7206. },
  7207. },
  7208. },
  7209. }),
  7210. },
  7211. },
  7212. "invalid requiredDuringSchedulingIgnoredDuringExecution node selector, nodeSelectorTerms must have at least one term": {
  7213. expectedError: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms",
  7214. spec: core.Pod{
  7215. ObjectMeta: metav1.ObjectMeta{
  7216. Name: "123",
  7217. Namespace: "ns",
  7218. },
  7219. Spec: validPodSpec(&core.Affinity{
  7220. NodeAffinity: &core.NodeAffinity{
  7221. RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
  7222. NodeSelectorTerms: []core.NodeSelectorTerm{},
  7223. },
  7224. },
  7225. }),
  7226. },
  7227. },
  7228. "invalid weight in preferredDuringSchedulingIgnoredDuringExecution in pod affinity annotations, weight should be in range 1-100": {
  7229. expectedError: "must be in the range 1-100",
  7230. spec: core.Pod{
  7231. ObjectMeta: metav1.ObjectMeta{
  7232. Name: "123",
  7233. Namespace: "ns",
  7234. },
  7235. Spec: validPodSpec(&core.Affinity{
  7236. PodAffinity: &core.PodAffinity{
  7237. PreferredDuringSchedulingIgnoredDuringExecution: []core.WeightedPodAffinityTerm{
  7238. {
  7239. Weight: 109,
  7240. PodAffinityTerm: core.PodAffinityTerm{
  7241. LabelSelector: &metav1.LabelSelector{
  7242. MatchExpressions: []metav1.LabelSelectorRequirement{
  7243. {
  7244. Key: "key2",
  7245. Operator: metav1.LabelSelectorOpNotIn,
  7246. Values: []string{"value1", "value2"},
  7247. },
  7248. },
  7249. },
  7250. Namespaces: []string{"ns"},
  7251. TopologyKey: "region",
  7252. },
  7253. },
  7254. },
  7255. },
  7256. }),
  7257. },
  7258. },
  7259. "invalid labelSelector in preferredDuringSchedulingIgnoredDuringExecution in podaffinity annotations, values should be empty if the operator is Exists": {
  7260. expectedError: "spec.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution[0].podAffinityTerm.matchExpressions.matchExpressions[0].values",
  7261. spec: core.Pod{
  7262. ObjectMeta: metav1.ObjectMeta{
  7263. Name: "123",
  7264. Namespace: "ns",
  7265. },
  7266. Spec: validPodSpec(&core.Affinity{
  7267. PodAntiAffinity: &core.PodAntiAffinity{
  7268. PreferredDuringSchedulingIgnoredDuringExecution: []core.WeightedPodAffinityTerm{
  7269. {
  7270. Weight: 10,
  7271. PodAffinityTerm: core.PodAffinityTerm{
  7272. LabelSelector: &metav1.LabelSelector{
  7273. MatchExpressions: []metav1.LabelSelectorRequirement{
  7274. {
  7275. Key: "key2",
  7276. Operator: metav1.LabelSelectorOpExists,
  7277. Values: []string{"value1", "value2"},
  7278. },
  7279. },
  7280. },
  7281. Namespaces: []string{"ns"},
  7282. TopologyKey: "region",
  7283. },
  7284. },
  7285. },
  7286. },
  7287. }),
  7288. },
  7289. },
  7290. "invalid name space in preferredDuringSchedulingIgnoredDuringExecution in podaffinity annotations, name space shouldbe valid": {
  7291. expectedError: "spec.affinity.podAffinity.preferredDuringSchedulingIgnoredDuringExecution[0].podAffinityTerm.namespace",
  7292. spec: core.Pod{
  7293. ObjectMeta: metav1.ObjectMeta{
  7294. Name: "123",
  7295. Namespace: "ns",
  7296. },
  7297. Spec: validPodSpec(&core.Affinity{
  7298. PodAffinity: &core.PodAffinity{
  7299. PreferredDuringSchedulingIgnoredDuringExecution: []core.WeightedPodAffinityTerm{
  7300. {
  7301. Weight: 10,
  7302. PodAffinityTerm: core.PodAffinityTerm{
  7303. LabelSelector: &metav1.LabelSelector{
  7304. MatchExpressions: []metav1.LabelSelectorRequirement{
  7305. {
  7306. Key: "key2",
  7307. Operator: metav1.LabelSelectorOpExists,
  7308. },
  7309. },
  7310. },
  7311. Namespaces: []string{"INVALID_NAMESPACE"},
  7312. TopologyKey: "region",
  7313. },
  7314. },
  7315. },
  7316. },
  7317. }),
  7318. },
  7319. },
  7320. "invalid hard pod affinity, empty topologyKey is not allowed for hard pod affinity": {
  7321. expectedError: "can not be empty",
  7322. spec: core.Pod{
  7323. ObjectMeta: metav1.ObjectMeta{
  7324. Name: "123",
  7325. Namespace: "ns",
  7326. },
  7327. Spec: validPodSpec(&core.Affinity{
  7328. PodAffinity: &core.PodAffinity{
  7329. RequiredDuringSchedulingIgnoredDuringExecution: []core.PodAffinityTerm{
  7330. {
  7331. LabelSelector: &metav1.LabelSelector{
  7332. MatchExpressions: []metav1.LabelSelectorRequirement{
  7333. {
  7334. Key: "key2",
  7335. Operator: metav1.LabelSelectorOpIn,
  7336. Values: []string{"value1", "value2"},
  7337. },
  7338. },
  7339. },
  7340. Namespaces: []string{"ns"},
  7341. },
  7342. },
  7343. },
  7344. }),
  7345. },
  7346. },
  7347. "invalid hard pod anti-affinity, empty topologyKey is not allowed for hard pod anti-affinity": {
  7348. expectedError: "can not be empty",
  7349. spec: core.Pod{
  7350. ObjectMeta: metav1.ObjectMeta{
  7351. Name: "123",
  7352. Namespace: "ns",
  7353. },
  7354. Spec: validPodSpec(&core.Affinity{
  7355. PodAntiAffinity: &core.PodAntiAffinity{
  7356. RequiredDuringSchedulingIgnoredDuringExecution: []core.PodAffinityTerm{
  7357. {
  7358. LabelSelector: &metav1.LabelSelector{
  7359. MatchExpressions: []metav1.LabelSelectorRequirement{
  7360. {
  7361. Key: "key2",
  7362. Operator: metav1.LabelSelectorOpIn,
  7363. Values: []string{"value1", "value2"},
  7364. },
  7365. },
  7366. },
  7367. Namespaces: []string{"ns"},
  7368. },
  7369. },
  7370. },
  7371. }),
  7372. },
  7373. },
  7374. "invalid soft pod affinity, empty topologyKey is not allowed for soft pod affinity": {
  7375. expectedError: "can not be empty",
  7376. spec: core.Pod{
  7377. ObjectMeta: metav1.ObjectMeta{
  7378. Name: "123",
  7379. Namespace: "ns",
  7380. },
  7381. Spec: validPodSpec(&core.Affinity{
  7382. PodAffinity: &core.PodAffinity{
  7383. PreferredDuringSchedulingIgnoredDuringExecution: []core.WeightedPodAffinityTerm{
  7384. {
  7385. Weight: 10,
  7386. PodAffinityTerm: core.PodAffinityTerm{
  7387. LabelSelector: &metav1.LabelSelector{
  7388. MatchExpressions: []metav1.LabelSelectorRequirement{
  7389. {
  7390. Key: "key2",
  7391. Operator: metav1.LabelSelectorOpNotIn,
  7392. Values: []string{"value1", "value2"},
  7393. },
  7394. },
  7395. },
  7396. Namespaces: []string{"ns"},
  7397. },
  7398. },
  7399. },
  7400. },
  7401. }),
  7402. },
  7403. },
  7404. "invalid soft pod anti-affinity, empty topologyKey is not allowed for soft pod anti-affinity": {
  7405. expectedError: "can not be empty",
  7406. spec: core.Pod{
  7407. ObjectMeta: metav1.ObjectMeta{
  7408. Name: "123",
  7409. Namespace: "ns",
  7410. },
  7411. Spec: validPodSpec(&core.Affinity{
  7412. PodAntiAffinity: &core.PodAntiAffinity{
  7413. PreferredDuringSchedulingIgnoredDuringExecution: []core.WeightedPodAffinityTerm{
  7414. {
  7415. Weight: 10,
  7416. PodAffinityTerm: core.PodAffinityTerm{
  7417. LabelSelector: &metav1.LabelSelector{
  7418. MatchExpressions: []metav1.LabelSelectorRequirement{
  7419. {
  7420. Key: "key2",
  7421. Operator: metav1.LabelSelectorOpNotIn,
  7422. Values: []string{"value1", "value2"},
  7423. },
  7424. },
  7425. },
  7426. Namespaces: []string{"ns"},
  7427. },
  7428. },
  7429. },
  7430. },
  7431. }),
  7432. },
  7433. },
  7434. "invalid toleration key": {
  7435. expectedError: "spec.tolerations[0].key",
  7436. spec: core.Pod{
  7437. ObjectMeta: metav1.ObjectMeta{
  7438. Name: "123",
  7439. Namespace: "ns",
  7440. },
  7441. Spec: extendPodSpecwithTolerations(validPodSpec(nil), []core.Toleration{{Key: "nospecialchars^=@", Operator: "Equal", Value: "bar", Effect: "NoSchedule"}}),
  7442. },
  7443. },
  7444. "invalid toleration operator": {
  7445. expectedError: "spec.tolerations[0].operator",
  7446. spec: core.Pod{
  7447. ObjectMeta: metav1.ObjectMeta{
  7448. Name: "123",
  7449. Namespace: "ns",
  7450. },
  7451. Spec: extendPodSpecwithTolerations(validPodSpec(nil), []core.Toleration{{Key: "foo", Operator: "In", Value: "bar", Effect: "NoSchedule"}}),
  7452. },
  7453. },
  7454. "value must be empty when `operator` is 'Exists'": {
  7455. expectedError: "spec.tolerations[0].operator",
  7456. spec: core.Pod{
  7457. ObjectMeta: metav1.ObjectMeta{
  7458. Name: "123",
  7459. Namespace: "ns",
  7460. },
  7461. Spec: extendPodSpecwithTolerations(validPodSpec(nil), []core.Toleration{{Key: "foo", Operator: "Exists", Value: "bar", Effect: "NoSchedule"}}),
  7462. },
  7463. },
  7464. "operator must be 'Exists' when `key` is empty": {
  7465. expectedError: "spec.tolerations[0].operator",
  7466. spec: core.Pod{
  7467. ObjectMeta: metav1.ObjectMeta{
  7468. Name: "123",
  7469. Namespace: "ns",
  7470. },
  7471. Spec: extendPodSpecwithTolerations(validPodSpec(nil), []core.Toleration{{Operator: "Equal", Value: "bar", Effect: "NoSchedule"}}),
  7472. },
  7473. },
  7474. "effect must be 'NoExecute' when `TolerationSeconds` is set": {
  7475. expectedError: "spec.tolerations[0].effect",
  7476. spec: core.Pod{
  7477. ObjectMeta: metav1.ObjectMeta{
  7478. Name: "pod-forgiveness-invalid",
  7479. Namespace: "ns",
  7480. },
  7481. Spec: extendPodSpecwithTolerations(validPodSpec(nil), []core.Toleration{{Key: "node.kubernetes.io/not-ready", Operator: "Exists", Effect: "NoSchedule", TolerationSeconds: &[]int64{20}[0]}}),
  7482. },
  7483. },
  7484. "must be a valid pod seccomp profile": {
  7485. expectedError: "must be a valid seccomp profile",
  7486. spec: core.Pod{
  7487. ObjectMeta: metav1.ObjectMeta{
  7488. Name: "123",
  7489. Namespace: "ns",
  7490. Annotations: map[string]string{
  7491. core.SeccompPodAnnotationKey: "foo",
  7492. },
  7493. },
  7494. Spec: validPodSpec(nil),
  7495. },
  7496. },
  7497. "must be a valid container seccomp profile": {
  7498. expectedError: "must be a valid seccomp profile",
  7499. spec: core.Pod{
  7500. ObjectMeta: metav1.ObjectMeta{
  7501. Name: "123",
  7502. Namespace: "ns",
  7503. Annotations: map[string]string{
  7504. core.SeccompContainerAnnotationKeyPrefix + "foo": "foo",
  7505. },
  7506. },
  7507. Spec: validPodSpec(nil),
  7508. },
  7509. },
  7510. "must be a non-empty container name in seccomp annotation": {
  7511. expectedError: "name part must be non-empty",
  7512. spec: core.Pod{
  7513. ObjectMeta: metav1.ObjectMeta{
  7514. Name: "123",
  7515. Namespace: "ns",
  7516. Annotations: map[string]string{
  7517. core.SeccompContainerAnnotationKeyPrefix: "foo",
  7518. },
  7519. },
  7520. Spec: validPodSpec(nil),
  7521. },
  7522. },
  7523. "must be a non-empty container profile in seccomp annotation": {
  7524. expectedError: "must be a valid seccomp profile",
  7525. spec: core.Pod{
  7526. ObjectMeta: metav1.ObjectMeta{
  7527. Name: "123",
  7528. Namespace: "ns",
  7529. Annotations: map[string]string{
  7530. core.SeccompContainerAnnotationKeyPrefix + "foo": "",
  7531. },
  7532. },
  7533. Spec: validPodSpec(nil),
  7534. },
  7535. },
  7536. "must be a relative path in a node-local seccomp profile annotation": {
  7537. expectedError: "must be a relative path",
  7538. spec: core.Pod{
  7539. ObjectMeta: metav1.ObjectMeta{
  7540. Name: "123",
  7541. Namespace: "ns",
  7542. Annotations: map[string]string{
  7543. core.SeccompPodAnnotationKey: "localhost//foo",
  7544. },
  7545. },
  7546. Spec: validPodSpec(nil),
  7547. },
  7548. },
  7549. "must not start with '../'": {
  7550. expectedError: "must not contain '..'",
  7551. spec: core.Pod{
  7552. ObjectMeta: metav1.ObjectMeta{
  7553. Name: "123",
  7554. Namespace: "ns",
  7555. Annotations: map[string]string{
  7556. core.SeccompPodAnnotationKey: "localhost/../foo",
  7557. },
  7558. },
  7559. Spec: validPodSpec(nil),
  7560. },
  7561. },
  7562. "AppArmor profile must apply to a container": {
  7563. expectedError: "metadata.annotations[container.apparmor.security.beta.kubernetes.io/fake-ctr]",
  7564. spec: core.Pod{
  7565. ObjectMeta: metav1.ObjectMeta{
  7566. Name: "123",
  7567. Namespace: "ns",
  7568. Annotations: map[string]string{
  7569. apparmor.ContainerAnnotationKeyPrefix + "ctr": apparmor.ProfileRuntimeDefault,
  7570. apparmor.ContainerAnnotationKeyPrefix + "init-ctr": apparmor.ProfileRuntimeDefault,
  7571. apparmor.ContainerAnnotationKeyPrefix + "fake-ctr": apparmor.ProfileRuntimeDefault,
  7572. },
  7573. },
  7574. Spec: core.PodSpec{
  7575. InitContainers: []core.Container{{Name: "init-ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  7576. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  7577. RestartPolicy: core.RestartPolicyAlways,
  7578. DNSPolicy: core.DNSClusterFirst,
  7579. },
  7580. },
  7581. },
  7582. "AppArmor profile format must be valid": {
  7583. expectedError: "invalid AppArmor profile name",
  7584. spec: core.Pod{
  7585. ObjectMeta: metav1.ObjectMeta{
  7586. Name: "123",
  7587. Namespace: "ns",
  7588. Annotations: map[string]string{
  7589. apparmor.ContainerAnnotationKeyPrefix + "ctr": "bad-name",
  7590. },
  7591. },
  7592. Spec: validPodSpec(nil),
  7593. },
  7594. },
  7595. "only default AppArmor profile may start with runtime/": {
  7596. expectedError: "invalid AppArmor profile name",
  7597. spec: core.Pod{
  7598. ObjectMeta: metav1.ObjectMeta{
  7599. Name: "123",
  7600. Namespace: "ns",
  7601. Annotations: map[string]string{
  7602. apparmor.ContainerAnnotationKeyPrefix + "ctr": "runtime/foo",
  7603. },
  7604. },
  7605. Spec: validPodSpec(nil),
  7606. },
  7607. },
  7608. "invalid extended resource name in container request": {
  7609. expectedError: "must be a standard resource for containers",
  7610. spec: core.Pod{
  7611. ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
  7612. Spec: core.PodSpec{
  7613. Containers: []core.Container{
  7614. {
  7615. Name: "invalid",
  7616. Image: "image",
  7617. ImagePullPolicy: "IfNotPresent",
  7618. Resources: core.ResourceRequirements{
  7619. Requests: core.ResourceList{
  7620. core.ResourceName("invalid-name"): resource.MustParse("2"),
  7621. },
  7622. Limits: core.ResourceList{
  7623. core.ResourceName("invalid-name"): resource.MustParse("2"),
  7624. },
  7625. },
  7626. },
  7627. },
  7628. RestartPolicy: core.RestartPolicyAlways,
  7629. DNSPolicy: core.DNSClusterFirst,
  7630. },
  7631. },
  7632. },
  7633. "invalid extended resource requirement: request must be == limit": {
  7634. expectedError: "must be equal to example.com/a",
  7635. spec: core.Pod{
  7636. ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
  7637. Spec: core.PodSpec{
  7638. Containers: []core.Container{
  7639. {
  7640. Name: "invalid",
  7641. Image: "image",
  7642. ImagePullPolicy: "IfNotPresent",
  7643. Resources: core.ResourceRequirements{
  7644. Requests: core.ResourceList{
  7645. core.ResourceName("example.com/a"): resource.MustParse("2"),
  7646. },
  7647. Limits: core.ResourceList{
  7648. core.ResourceName("example.com/a"): resource.MustParse("1"),
  7649. },
  7650. },
  7651. },
  7652. },
  7653. RestartPolicy: core.RestartPolicyAlways,
  7654. DNSPolicy: core.DNSClusterFirst,
  7655. },
  7656. },
  7657. },
  7658. "invalid extended resource requirement without limit": {
  7659. expectedError: "Limit must be set",
  7660. spec: core.Pod{
  7661. ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
  7662. Spec: core.PodSpec{
  7663. Containers: []core.Container{
  7664. {
  7665. Name: "invalid",
  7666. Image: "image",
  7667. ImagePullPolicy: "IfNotPresent",
  7668. Resources: core.ResourceRequirements{
  7669. Requests: core.ResourceList{
  7670. core.ResourceName("example.com/a"): resource.MustParse("2"),
  7671. },
  7672. },
  7673. },
  7674. },
  7675. RestartPolicy: core.RestartPolicyAlways,
  7676. DNSPolicy: core.DNSClusterFirst,
  7677. },
  7678. },
  7679. },
  7680. "invalid fractional extended resource in container request": {
  7681. expectedError: "must be an integer",
  7682. spec: core.Pod{
  7683. ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
  7684. Spec: core.PodSpec{
  7685. Containers: []core.Container{
  7686. {
  7687. Name: "invalid",
  7688. Image: "image",
  7689. ImagePullPolicy: "IfNotPresent",
  7690. Resources: core.ResourceRequirements{
  7691. Requests: core.ResourceList{
  7692. core.ResourceName("example.com/a"): resource.MustParse("500m"),
  7693. },
  7694. },
  7695. },
  7696. },
  7697. RestartPolicy: core.RestartPolicyAlways,
  7698. DNSPolicy: core.DNSClusterFirst,
  7699. },
  7700. },
  7701. },
  7702. "invalid fractional extended resource in init container request": {
  7703. expectedError: "must be an integer",
  7704. spec: core.Pod{
  7705. ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
  7706. Spec: core.PodSpec{
  7707. InitContainers: []core.Container{
  7708. {
  7709. Name: "invalid",
  7710. Image: "image",
  7711. ImagePullPolicy: "IfNotPresent",
  7712. Resources: core.ResourceRequirements{
  7713. Requests: core.ResourceList{
  7714. core.ResourceName("example.com/a"): resource.MustParse("500m"),
  7715. },
  7716. },
  7717. },
  7718. },
  7719. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  7720. RestartPolicy: core.RestartPolicyAlways,
  7721. DNSPolicy: core.DNSClusterFirst,
  7722. },
  7723. },
  7724. },
  7725. "invalid fractional extended resource in container limit": {
  7726. expectedError: "must be an integer",
  7727. spec: core.Pod{
  7728. ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
  7729. Spec: core.PodSpec{
  7730. Containers: []core.Container{
  7731. {
  7732. Name: "invalid",
  7733. Image: "image",
  7734. ImagePullPolicy: "IfNotPresent",
  7735. Resources: core.ResourceRequirements{
  7736. Requests: core.ResourceList{
  7737. core.ResourceName("example.com/a"): resource.MustParse("5"),
  7738. },
  7739. Limits: core.ResourceList{
  7740. core.ResourceName("example.com/a"): resource.MustParse("2.5"),
  7741. },
  7742. },
  7743. },
  7744. },
  7745. RestartPolicy: core.RestartPolicyAlways,
  7746. DNSPolicy: core.DNSClusterFirst,
  7747. },
  7748. },
  7749. },
  7750. "invalid fractional extended resource in init container limit": {
  7751. expectedError: "must be an integer",
  7752. spec: core.Pod{
  7753. ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
  7754. Spec: core.PodSpec{
  7755. InitContainers: []core.Container{
  7756. {
  7757. Name: "invalid",
  7758. Image: "image",
  7759. ImagePullPolicy: "IfNotPresent",
  7760. Resources: core.ResourceRequirements{
  7761. Requests: core.ResourceList{
  7762. core.ResourceName("example.com/a"): resource.MustParse("2.5"),
  7763. },
  7764. Limits: core.ResourceList{
  7765. core.ResourceName("example.com/a"): resource.MustParse("2.5"),
  7766. },
  7767. },
  7768. },
  7769. },
  7770. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  7771. RestartPolicy: core.RestartPolicyAlways,
  7772. DNSPolicy: core.DNSClusterFirst,
  7773. },
  7774. },
  7775. },
  7776. "mirror-pod present without nodeName": {
  7777. expectedError: "mirror",
  7778. spec: core.Pod{
  7779. ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns", Annotations: map[string]string{core.MirrorPodAnnotationKey: ""}},
  7780. Spec: core.PodSpec{
  7781. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  7782. RestartPolicy: core.RestartPolicyAlways,
  7783. DNSPolicy: core.DNSClusterFirst,
  7784. },
  7785. },
  7786. },
  7787. "mirror-pod populated without nodeName": {
  7788. expectedError: "mirror",
  7789. spec: core.Pod{
  7790. ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns", Annotations: map[string]string{core.MirrorPodAnnotationKey: "foo"}},
  7791. Spec: core.PodSpec{
  7792. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  7793. RestartPolicy: core.RestartPolicyAlways,
  7794. DNSPolicy: core.DNSClusterFirst,
  7795. },
  7796. },
  7797. },
  7798. "serviceaccount token projected volume with no serviceaccount name specified": {
  7799. expectedError: "must not be specified when serviceAccountName is not set",
  7800. spec: core.Pod{
  7801. ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
  7802. Spec: core.PodSpec{
  7803. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  7804. RestartPolicy: core.RestartPolicyAlways,
  7805. DNSPolicy: core.DNSClusterFirst,
  7806. Volumes: []core.Volume{
  7807. {
  7808. Name: "projected-volume",
  7809. VolumeSource: core.VolumeSource{
  7810. Projected: &core.ProjectedVolumeSource{
  7811. Sources: []core.VolumeProjection{
  7812. {
  7813. ServiceAccountToken: &core.ServiceAccountTokenProjection{
  7814. Audience: "foo-audience",
  7815. ExpirationSeconds: 6000,
  7816. Path: "foo-path",
  7817. },
  7818. },
  7819. },
  7820. },
  7821. },
  7822. },
  7823. },
  7824. },
  7825. },
  7826. },
  7827. }
  7828. for k, v := range errorCases {
  7829. if errs := ValidatePod(&v.spec); len(errs) == 0 {
  7830. t.Errorf("expected failure for %q", k)
  7831. } else if v.expectedError == "" {
  7832. t.Errorf("missing expectedError for %q, got %q", k, errs.ToAggregate().Error())
  7833. } else if actualError := errs.ToAggregate().Error(); !strings.Contains(actualError, v.expectedError) {
  7834. t.Errorf("expected error for %q to contain %q, got %q", k, v.expectedError, actualError)
  7835. }
  7836. }
  7837. }
  7838. func TestValidatePodUpdate(t *testing.T) {
  7839. var (
  7840. activeDeadlineSecondsZero = int64(0)
  7841. activeDeadlineSecondsNegative = int64(-30)
  7842. activeDeadlineSecondsPositive = int64(30)
  7843. activeDeadlineSecondsLarger = int64(31)
  7844. now = metav1.Now()
  7845. grace = int64(30)
  7846. grace2 = int64(31)
  7847. )
  7848. tests := []struct {
  7849. new core.Pod
  7850. old core.Pod
  7851. err string
  7852. test string
  7853. }{
  7854. {core.Pod{}, core.Pod{}, "", "nothing"},
  7855. {
  7856. core.Pod{
  7857. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  7858. },
  7859. core.Pod{
  7860. ObjectMeta: metav1.ObjectMeta{Name: "bar"},
  7861. },
  7862. "metadata.name",
  7863. "ids",
  7864. },
  7865. {
  7866. core.Pod{
  7867. ObjectMeta: metav1.ObjectMeta{
  7868. Name: "foo",
  7869. Labels: map[string]string{
  7870. "foo": "bar",
  7871. },
  7872. },
  7873. },
  7874. core.Pod{
  7875. ObjectMeta: metav1.ObjectMeta{
  7876. Name: "foo",
  7877. Labels: map[string]string{
  7878. "bar": "foo",
  7879. },
  7880. },
  7881. },
  7882. "",
  7883. "labels",
  7884. },
  7885. {
  7886. core.Pod{
  7887. ObjectMeta: metav1.ObjectMeta{
  7888. Name: "foo",
  7889. Annotations: map[string]string{
  7890. "foo": "bar",
  7891. },
  7892. },
  7893. },
  7894. core.Pod{
  7895. ObjectMeta: metav1.ObjectMeta{
  7896. Name: "foo",
  7897. Annotations: map[string]string{
  7898. "bar": "foo",
  7899. },
  7900. },
  7901. },
  7902. "",
  7903. "annotations",
  7904. },
  7905. {
  7906. core.Pod{
  7907. ObjectMeta: metav1.ObjectMeta{
  7908. Name: "foo",
  7909. },
  7910. Spec: core.PodSpec{
  7911. Containers: []core.Container{
  7912. {
  7913. Image: "foo:V1",
  7914. },
  7915. },
  7916. },
  7917. },
  7918. core.Pod{
  7919. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  7920. Spec: core.PodSpec{
  7921. Containers: []core.Container{
  7922. {
  7923. Image: "foo:V2",
  7924. },
  7925. {
  7926. Image: "bar:V2",
  7927. },
  7928. },
  7929. },
  7930. },
  7931. "may not add or remove containers",
  7932. "less containers",
  7933. },
  7934. {
  7935. core.Pod{
  7936. ObjectMeta: metav1.ObjectMeta{
  7937. Name: "foo",
  7938. },
  7939. Spec: core.PodSpec{
  7940. Containers: []core.Container{
  7941. {
  7942. Image: "foo:V1",
  7943. },
  7944. {
  7945. Image: "bar:V2",
  7946. },
  7947. },
  7948. },
  7949. },
  7950. core.Pod{
  7951. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  7952. Spec: core.PodSpec{
  7953. Containers: []core.Container{
  7954. {
  7955. Image: "foo:V2",
  7956. },
  7957. },
  7958. },
  7959. },
  7960. "may not add or remove containers",
  7961. "more containers",
  7962. },
  7963. {
  7964. core.Pod{
  7965. ObjectMeta: metav1.ObjectMeta{
  7966. Name: "foo",
  7967. },
  7968. Spec: core.PodSpec{
  7969. InitContainers: []core.Container{
  7970. {
  7971. Image: "foo:V1",
  7972. },
  7973. },
  7974. },
  7975. },
  7976. core.Pod{
  7977. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  7978. Spec: core.PodSpec{
  7979. InitContainers: []core.Container{
  7980. {
  7981. Image: "foo:V2",
  7982. },
  7983. {
  7984. Image: "bar:V2",
  7985. },
  7986. },
  7987. },
  7988. },
  7989. "may not add or remove containers",
  7990. "more init containers",
  7991. },
  7992. {
  7993. core.Pod{
  7994. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  7995. Spec: core.PodSpec{Containers: []core.Container{{Image: "foo:V1"}}},
  7996. },
  7997. core.Pod{
  7998. ObjectMeta: metav1.ObjectMeta{Name: "foo", DeletionTimestamp: &now},
  7999. Spec: core.PodSpec{Containers: []core.Container{{Image: "foo:V1"}}},
  8000. },
  8001. "metadata.deletionTimestamp",
  8002. "deletion timestamp removed",
  8003. },
  8004. {
  8005. core.Pod{
  8006. ObjectMeta: metav1.ObjectMeta{Name: "foo", DeletionTimestamp: &now},
  8007. Spec: core.PodSpec{Containers: []core.Container{{Image: "foo:V1"}}},
  8008. },
  8009. core.Pod{
  8010. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  8011. Spec: core.PodSpec{Containers: []core.Container{{Image: "foo:V1"}}},
  8012. },
  8013. "metadata.deletionTimestamp",
  8014. "deletion timestamp added",
  8015. },
  8016. {
  8017. core.Pod{
  8018. ObjectMeta: metav1.ObjectMeta{Name: "foo", DeletionTimestamp: &now, DeletionGracePeriodSeconds: &grace},
  8019. Spec: core.PodSpec{Containers: []core.Container{{Image: "foo:V1"}}},
  8020. },
  8021. core.Pod{
  8022. ObjectMeta: metav1.ObjectMeta{Name: "foo", DeletionTimestamp: &now, DeletionGracePeriodSeconds: &grace2},
  8023. Spec: core.PodSpec{Containers: []core.Container{{Image: "foo:V1"}}},
  8024. },
  8025. "metadata.deletionGracePeriodSeconds",
  8026. "deletion grace period seconds changed",
  8027. },
  8028. {
  8029. core.Pod{
  8030. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  8031. Spec: core.PodSpec{
  8032. Containers: []core.Container{
  8033. {
  8034. Image: "foo:V1",
  8035. },
  8036. },
  8037. },
  8038. },
  8039. core.Pod{
  8040. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  8041. Spec: core.PodSpec{
  8042. Containers: []core.Container{
  8043. {
  8044. Image: "foo:V2",
  8045. },
  8046. },
  8047. },
  8048. },
  8049. "",
  8050. "image change",
  8051. },
  8052. {
  8053. core.Pod{
  8054. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  8055. Spec: core.PodSpec{
  8056. InitContainers: []core.Container{
  8057. {
  8058. Image: "foo:V1",
  8059. },
  8060. },
  8061. },
  8062. },
  8063. core.Pod{
  8064. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  8065. Spec: core.PodSpec{
  8066. InitContainers: []core.Container{
  8067. {
  8068. Image: "foo:V2",
  8069. },
  8070. },
  8071. },
  8072. },
  8073. "",
  8074. "init container image change",
  8075. },
  8076. {
  8077. core.Pod{
  8078. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  8079. Spec: core.PodSpec{
  8080. Containers: []core.Container{
  8081. {},
  8082. },
  8083. },
  8084. },
  8085. core.Pod{
  8086. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  8087. Spec: core.PodSpec{
  8088. Containers: []core.Container{
  8089. {
  8090. Image: "foo:V2",
  8091. },
  8092. },
  8093. },
  8094. },
  8095. "spec.containers[0].image",
  8096. "image change to empty",
  8097. },
  8098. {
  8099. core.Pod{
  8100. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  8101. Spec: core.PodSpec{
  8102. InitContainers: []core.Container{
  8103. {},
  8104. },
  8105. },
  8106. },
  8107. core.Pod{
  8108. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  8109. Spec: core.PodSpec{
  8110. InitContainers: []core.Container{
  8111. {
  8112. Image: "foo:V2",
  8113. },
  8114. },
  8115. },
  8116. },
  8117. "spec.initContainers[0].image",
  8118. "init container image change to empty",
  8119. },
  8120. {
  8121. core.Pod{
  8122. Spec: core.PodSpec{},
  8123. },
  8124. core.Pod{
  8125. Spec: core.PodSpec{},
  8126. },
  8127. "",
  8128. "activeDeadlineSeconds no change, nil",
  8129. },
  8130. {
  8131. core.Pod{
  8132. Spec: core.PodSpec{
  8133. ActiveDeadlineSeconds: &activeDeadlineSecondsPositive,
  8134. },
  8135. },
  8136. core.Pod{
  8137. Spec: core.PodSpec{
  8138. ActiveDeadlineSeconds: &activeDeadlineSecondsPositive,
  8139. },
  8140. },
  8141. "",
  8142. "activeDeadlineSeconds no change, set",
  8143. },
  8144. {
  8145. core.Pod{
  8146. Spec: core.PodSpec{
  8147. ActiveDeadlineSeconds: &activeDeadlineSecondsPositive,
  8148. },
  8149. },
  8150. core.Pod{},
  8151. "",
  8152. "activeDeadlineSeconds change to positive from nil",
  8153. },
  8154. {
  8155. core.Pod{
  8156. Spec: core.PodSpec{
  8157. ActiveDeadlineSeconds: &activeDeadlineSecondsPositive,
  8158. },
  8159. },
  8160. core.Pod{
  8161. Spec: core.PodSpec{
  8162. ActiveDeadlineSeconds: &activeDeadlineSecondsLarger,
  8163. },
  8164. },
  8165. "",
  8166. "activeDeadlineSeconds change to smaller positive",
  8167. },
  8168. {
  8169. core.Pod{
  8170. Spec: core.PodSpec{
  8171. ActiveDeadlineSeconds: &activeDeadlineSecondsLarger,
  8172. },
  8173. },
  8174. core.Pod{
  8175. Spec: core.PodSpec{
  8176. ActiveDeadlineSeconds: &activeDeadlineSecondsPositive,
  8177. },
  8178. },
  8179. "spec.activeDeadlineSeconds",
  8180. "activeDeadlineSeconds change to larger positive",
  8181. },
  8182. {
  8183. core.Pod{
  8184. Spec: core.PodSpec{
  8185. ActiveDeadlineSeconds: &activeDeadlineSecondsNegative,
  8186. },
  8187. },
  8188. core.Pod{},
  8189. "spec.activeDeadlineSeconds",
  8190. "activeDeadlineSeconds change to negative from nil",
  8191. },
  8192. {
  8193. core.Pod{
  8194. Spec: core.PodSpec{
  8195. ActiveDeadlineSeconds: &activeDeadlineSecondsNegative,
  8196. },
  8197. },
  8198. core.Pod{
  8199. Spec: core.PodSpec{
  8200. ActiveDeadlineSeconds: &activeDeadlineSecondsPositive,
  8201. },
  8202. },
  8203. "spec.activeDeadlineSeconds",
  8204. "activeDeadlineSeconds change to negative from positive",
  8205. },
  8206. {
  8207. core.Pod{
  8208. Spec: core.PodSpec{
  8209. ActiveDeadlineSeconds: &activeDeadlineSecondsZero,
  8210. },
  8211. },
  8212. core.Pod{
  8213. Spec: core.PodSpec{
  8214. ActiveDeadlineSeconds: &activeDeadlineSecondsPositive,
  8215. },
  8216. },
  8217. "",
  8218. "activeDeadlineSeconds change to zero from positive",
  8219. },
  8220. {
  8221. core.Pod{
  8222. Spec: core.PodSpec{
  8223. ActiveDeadlineSeconds: &activeDeadlineSecondsZero,
  8224. },
  8225. },
  8226. core.Pod{},
  8227. "",
  8228. "activeDeadlineSeconds change to zero from nil",
  8229. },
  8230. {
  8231. core.Pod{},
  8232. core.Pod{
  8233. Spec: core.PodSpec{
  8234. ActiveDeadlineSeconds: &activeDeadlineSecondsPositive,
  8235. },
  8236. },
  8237. "spec.activeDeadlineSeconds",
  8238. "activeDeadlineSeconds change to nil from positive",
  8239. },
  8240. {
  8241. core.Pod{
  8242. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  8243. Spec: core.PodSpec{
  8244. Containers: []core.Container{
  8245. {
  8246. Image: "foo:V1",
  8247. Resources: core.ResourceRequirements{
  8248. Limits: getResourceLimits("100m", "0"),
  8249. },
  8250. },
  8251. },
  8252. },
  8253. },
  8254. core.Pod{
  8255. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  8256. Spec: core.PodSpec{
  8257. Containers: []core.Container{
  8258. {
  8259. Image: "foo:V2",
  8260. Resources: core.ResourceRequirements{
  8261. Limits: getResourceLimits("1000m", "0"),
  8262. },
  8263. },
  8264. },
  8265. },
  8266. },
  8267. "spec: Forbidden: pod updates may not change fields",
  8268. "cpu change",
  8269. },
  8270. {
  8271. core.Pod{
  8272. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  8273. Spec: core.PodSpec{
  8274. Containers: []core.Container{
  8275. {
  8276. Image: "foo:V1",
  8277. Ports: []core.ContainerPort{
  8278. {HostPort: 8080, ContainerPort: 80},
  8279. },
  8280. },
  8281. },
  8282. },
  8283. },
  8284. core.Pod{
  8285. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  8286. Spec: core.PodSpec{
  8287. Containers: []core.Container{
  8288. {
  8289. Image: "foo:V2",
  8290. Ports: []core.ContainerPort{
  8291. {HostPort: 8000, ContainerPort: 80},
  8292. },
  8293. },
  8294. },
  8295. },
  8296. },
  8297. "spec: Forbidden: pod updates may not change fields",
  8298. "port change",
  8299. },
  8300. {
  8301. core.Pod{
  8302. ObjectMeta: metav1.ObjectMeta{
  8303. Name: "foo",
  8304. Labels: map[string]string{
  8305. "foo": "bar",
  8306. },
  8307. },
  8308. },
  8309. core.Pod{
  8310. ObjectMeta: metav1.ObjectMeta{
  8311. Name: "foo",
  8312. Labels: map[string]string{
  8313. "Bar": "foo",
  8314. },
  8315. },
  8316. },
  8317. "",
  8318. "bad label change",
  8319. },
  8320. {
  8321. core.Pod{
  8322. ObjectMeta: metav1.ObjectMeta{
  8323. Name: "foo",
  8324. },
  8325. Spec: core.PodSpec{
  8326. NodeName: "node1",
  8327. Tolerations: []core.Toleration{{Key: "key1", Value: "value2"}},
  8328. },
  8329. },
  8330. core.Pod{
  8331. ObjectMeta: metav1.ObjectMeta{
  8332. Name: "foo",
  8333. },
  8334. Spec: core.PodSpec{
  8335. NodeName: "node1",
  8336. Tolerations: []core.Toleration{{Key: "key1", Value: "value1"}},
  8337. },
  8338. },
  8339. "spec.tolerations: Forbidden",
  8340. "existing toleration value modified in pod spec updates",
  8341. },
  8342. {
  8343. core.Pod{
  8344. ObjectMeta: metav1.ObjectMeta{
  8345. Name: "foo",
  8346. },
  8347. Spec: core.PodSpec{
  8348. NodeName: "node1",
  8349. Tolerations: []core.Toleration{{Key: "key1", Value: "value2", Operator: "Equal", Effect: "NoExecute", TolerationSeconds: nil}},
  8350. },
  8351. },
  8352. core.Pod{
  8353. ObjectMeta: metav1.ObjectMeta{
  8354. Name: "foo",
  8355. },
  8356. Spec: core.PodSpec{
  8357. NodeName: "node1",
  8358. Tolerations: []core.Toleration{{Key: "key1", Value: "value1", Operator: "Equal", Effect: "NoExecute", TolerationSeconds: &[]int64{10}[0]}},
  8359. },
  8360. },
  8361. "spec.tolerations: Forbidden",
  8362. "existing toleration value modified in pod spec updates with modified tolerationSeconds",
  8363. },
  8364. {
  8365. core.Pod{
  8366. ObjectMeta: metav1.ObjectMeta{
  8367. Name: "foo",
  8368. },
  8369. Spec: core.PodSpec{
  8370. NodeName: "node1",
  8371. Tolerations: []core.Toleration{{Key: "key1", Value: "value1", Operator: "Equal", Effect: "NoExecute", TolerationSeconds: &[]int64{10}[0]}},
  8372. },
  8373. },
  8374. core.Pod{
  8375. ObjectMeta: metav1.ObjectMeta{
  8376. Name: "foo",
  8377. },
  8378. Spec: core.PodSpec{
  8379. NodeName: "node1",
  8380. Tolerations: []core.Toleration{{Key: "key1", Value: "value1", Operator: "Equal", Effect: "NoExecute", TolerationSeconds: &[]int64{20}[0]}},
  8381. }},
  8382. "",
  8383. "modified tolerationSeconds in existing toleration value in pod spec updates",
  8384. },
  8385. {
  8386. core.Pod{
  8387. ObjectMeta: metav1.ObjectMeta{
  8388. Name: "foo",
  8389. },
  8390. Spec: core.PodSpec{
  8391. Tolerations: []core.Toleration{{Key: "key1", Value: "value2"}},
  8392. },
  8393. },
  8394. core.Pod{
  8395. ObjectMeta: metav1.ObjectMeta{
  8396. Name: "foo",
  8397. },
  8398. Spec: core.PodSpec{
  8399. NodeName: "",
  8400. Tolerations: []core.Toleration{{Key: "key1", Value: "value1"}},
  8401. },
  8402. },
  8403. "spec.tolerations: Forbidden",
  8404. "toleration modified in updates to an unscheduled pod",
  8405. },
  8406. {
  8407. core.Pod{
  8408. ObjectMeta: metav1.ObjectMeta{
  8409. Name: "foo",
  8410. },
  8411. Spec: core.PodSpec{
  8412. NodeName: "node1",
  8413. Tolerations: []core.Toleration{{Key: "key1", Value: "value1"}},
  8414. },
  8415. },
  8416. core.Pod{
  8417. ObjectMeta: metav1.ObjectMeta{
  8418. Name: "foo",
  8419. },
  8420. Spec: core.PodSpec{
  8421. NodeName: "node1",
  8422. Tolerations: []core.Toleration{{Key: "key1", Value: "value1"}},
  8423. },
  8424. },
  8425. "",
  8426. "tolerations unmodified in updates to a scheduled pod",
  8427. },
  8428. {
  8429. core.Pod{
  8430. ObjectMeta: metav1.ObjectMeta{
  8431. Name: "foo",
  8432. },
  8433. Spec: core.PodSpec{
  8434. NodeName: "node1",
  8435. Tolerations: []core.Toleration{
  8436. {Key: "key1", Value: "value1", Operator: "Equal", Effect: "NoExecute", TolerationSeconds: &[]int64{20}[0]},
  8437. {Key: "key2", Value: "value2", Operator: "Equal", Effect: "NoExecute", TolerationSeconds: &[]int64{30}[0]},
  8438. },
  8439. }},
  8440. core.Pod{
  8441. ObjectMeta: metav1.ObjectMeta{
  8442. Name: "foo",
  8443. },
  8444. Spec: core.PodSpec{
  8445. NodeName: "node1",
  8446. Tolerations: []core.Toleration{{Key: "key1", Value: "value1", Operator: "Equal", Effect: "NoExecute", TolerationSeconds: &[]int64{10}[0]}},
  8447. },
  8448. },
  8449. "",
  8450. "added valid new toleration to existing tolerations in pod spec updates",
  8451. },
  8452. {
  8453. core.Pod{
  8454. ObjectMeta: metav1.ObjectMeta{Name: "foo"}, Spec: core.PodSpec{
  8455. NodeName: "node1",
  8456. Tolerations: []core.Toleration{
  8457. {Key: "key1", Value: "value1", Operator: "Equal", Effect: "NoExecute", TolerationSeconds: &[]int64{20}[0]},
  8458. {Key: "key2", Value: "value2", Operator: "Equal", Effect: "NoSchedule", TolerationSeconds: &[]int64{30}[0]},
  8459. },
  8460. }},
  8461. core.Pod{
  8462. ObjectMeta: metav1.ObjectMeta{
  8463. Name: "foo",
  8464. },
  8465. Spec: core.PodSpec{
  8466. NodeName: "node1", Tolerations: []core.Toleration{{Key: "key1", Value: "value1", Operator: "Equal", Effect: "NoExecute", TolerationSeconds: &[]int64{10}[0]}},
  8467. }},
  8468. "spec.tolerations[1].effect",
  8469. "added invalid new toleration to existing tolerations in pod spec updates",
  8470. },
  8471. {
  8472. core.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}, Spec: core.PodSpec{NodeName: "foo"}},
  8473. core.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}},
  8474. "spec: Forbidden: pod updates may not change fields",
  8475. "removed nodeName from pod spec",
  8476. },
  8477. {
  8478. core.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Annotations: map[string]string{core.MirrorPodAnnotationKey: ""}}, Spec: core.PodSpec{NodeName: "foo"}},
  8479. core.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}, Spec: core.PodSpec{NodeName: "foo"}},
  8480. "metadata.annotations[kubernetes.io/config.mirror]",
  8481. "added mirror pod annotation",
  8482. },
  8483. {
  8484. core.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}, Spec: core.PodSpec{NodeName: "foo"}},
  8485. core.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Annotations: map[string]string{core.MirrorPodAnnotationKey: ""}}, Spec: core.PodSpec{NodeName: "foo"}},
  8486. "metadata.annotations[kubernetes.io/config.mirror]",
  8487. "removed mirror pod annotation",
  8488. },
  8489. {
  8490. core.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Annotations: map[string]string{core.MirrorPodAnnotationKey: "foo"}}, Spec: core.PodSpec{NodeName: "foo"}},
  8491. core.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Annotations: map[string]string{core.MirrorPodAnnotationKey: "bar"}}, Spec: core.PodSpec{NodeName: "foo"}},
  8492. "metadata.annotations[kubernetes.io/config.mirror]",
  8493. "changed mirror pod annotation",
  8494. },
  8495. {
  8496. core.Pod{
  8497. ObjectMeta: metav1.ObjectMeta{
  8498. Name: "foo",
  8499. },
  8500. Spec: core.PodSpec{
  8501. NodeName: "node1",
  8502. PriorityClassName: "bar-priority",
  8503. },
  8504. },
  8505. core.Pod{
  8506. ObjectMeta: metav1.ObjectMeta{
  8507. Name: "foo",
  8508. },
  8509. Spec: core.PodSpec{
  8510. NodeName: "node1",
  8511. PriorityClassName: "foo-priority",
  8512. },
  8513. },
  8514. "spec: Forbidden: pod updates",
  8515. "changed priority class name",
  8516. },
  8517. {
  8518. core.Pod{
  8519. ObjectMeta: metav1.ObjectMeta{
  8520. Name: "foo",
  8521. },
  8522. Spec: core.PodSpec{
  8523. NodeName: "node1",
  8524. PriorityClassName: "",
  8525. },
  8526. },
  8527. core.Pod{
  8528. ObjectMeta: metav1.ObjectMeta{
  8529. Name: "foo",
  8530. },
  8531. Spec: core.PodSpec{
  8532. NodeName: "node1",
  8533. PriorityClassName: "foo-priority",
  8534. },
  8535. },
  8536. "spec: Forbidden: pod updates",
  8537. "removed priority class name",
  8538. },
  8539. }
  8540. for _, test := range tests {
  8541. test.new.ObjectMeta.ResourceVersion = "1"
  8542. test.old.ObjectMeta.ResourceVersion = "1"
  8543. errs := ValidatePodUpdate(&test.new, &test.old)
  8544. if test.err == "" {
  8545. if len(errs) != 0 {
  8546. t.Errorf("unexpected invalid: %s (%+v)\nA: %+v\nB: %+v", test.test, errs, test.new, test.old)
  8547. }
  8548. } else {
  8549. if len(errs) == 0 {
  8550. t.Errorf("unexpected valid: %s\nA: %+v\nB: %+v", test.test, test.new, test.old)
  8551. } else if actualErr := errs.ToAggregate().Error(); !strings.Contains(actualErr, test.err) {
  8552. t.Errorf("unexpected error message: %s\nExpected error: %s\nActual error: %s", test.test, test.err, actualErr)
  8553. }
  8554. }
  8555. }
  8556. }
  8557. func TestValidatePodStatusUpdate(t *testing.T) {
  8558. tests := []struct {
  8559. new core.Pod
  8560. old core.Pod
  8561. err string
  8562. test string
  8563. }{
  8564. {
  8565. core.Pod{
  8566. ObjectMeta: metav1.ObjectMeta{
  8567. Name: "foo",
  8568. },
  8569. Spec: core.PodSpec{
  8570. NodeName: "node1",
  8571. },
  8572. Status: core.PodStatus{
  8573. NominatedNodeName: "node1",
  8574. },
  8575. },
  8576. core.Pod{
  8577. ObjectMeta: metav1.ObjectMeta{
  8578. Name: "foo",
  8579. },
  8580. Spec: core.PodSpec{
  8581. NodeName: "node1",
  8582. },
  8583. Status: core.PodStatus{},
  8584. },
  8585. "",
  8586. "removed nominatedNodeName",
  8587. },
  8588. {
  8589. core.Pod{
  8590. ObjectMeta: metav1.ObjectMeta{
  8591. Name: "foo",
  8592. },
  8593. Spec: core.PodSpec{
  8594. NodeName: "node1",
  8595. },
  8596. },
  8597. core.Pod{
  8598. ObjectMeta: metav1.ObjectMeta{
  8599. Name: "foo",
  8600. },
  8601. Spec: core.PodSpec{
  8602. NodeName: "node1",
  8603. },
  8604. Status: core.PodStatus{
  8605. NominatedNodeName: "node1",
  8606. },
  8607. },
  8608. "",
  8609. "add valid nominatedNodeName",
  8610. },
  8611. {
  8612. core.Pod{
  8613. ObjectMeta: metav1.ObjectMeta{
  8614. Name: "foo",
  8615. },
  8616. Spec: core.PodSpec{
  8617. NodeName: "node1",
  8618. },
  8619. Status: core.PodStatus{
  8620. NominatedNodeName: "Node1",
  8621. },
  8622. },
  8623. core.Pod{
  8624. ObjectMeta: metav1.ObjectMeta{
  8625. Name: "foo",
  8626. },
  8627. Spec: core.PodSpec{
  8628. NodeName: "node1",
  8629. },
  8630. },
  8631. "nominatedNodeName",
  8632. "Add invalid nominatedNodeName",
  8633. },
  8634. {
  8635. core.Pod{
  8636. ObjectMeta: metav1.ObjectMeta{
  8637. Name: "foo",
  8638. },
  8639. Spec: core.PodSpec{
  8640. NodeName: "node1",
  8641. },
  8642. Status: core.PodStatus{
  8643. NominatedNodeName: "node1",
  8644. },
  8645. },
  8646. core.Pod{
  8647. ObjectMeta: metav1.ObjectMeta{
  8648. Name: "foo",
  8649. },
  8650. Spec: core.PodSpec{
  8651. NodeName: "node1",
  8652. },
  8653. Status: core.PodStatus{
  8654. NominatedNodeName: "node2",
  8655. },
  8656. },
  8657. "",
  8658. "Update nominatedNodeName",
  8659. },
  8660. }
  8661. for _, test := range tests {
  8662. test.new.ObjectMeta.ResourceVersion = "1"
  8663. test.old.ObjectMeta.ResourceVersion = "1"
  8664. errs := ValidatePodStatusUpdate(&test.new, &test.old)
  8665. if test.err == "" {
  8666. if len(errs) != 0 {
  8667. t.Errorf("unexpected invalid: %s (%+v)\nA: %+v\nB: %+v", test.test, errs, test.new, test.old)
  8668. }
  8669. } else {
  8670. if len(errs) == 0 {
  8671. t.Errorf("unexpected valid: %s\nA: %+v\nB: %+v", test.test, test.new, test.old)
  8672. } else if actualErr := errs.ToAggregate().Error(); !strings.Contains(actualErr, test.err) {
  8673. t.Errorf("unexpected error message: %s\nExpected error: %s\nActual error: %s", test.test, test.err, actualErr)
  8674. }
  8675. }
  8676. }
  8677. }
  8678. func makeValidService() core.Service {
  8679. return core.Service{
  8680. ObjectMeta: metav1.ObjectMeta{
  8681. Name: "valid",
  8682. Namespace: "valid",
  8683. Labels: map[string]string{},
  8684. Annotations: map[string]string{},
  8685. ResourceVersion: "1",
  8686. },
  8687. Spec: core.ServiceSpec{
  8688. Selector: map[string]string{"key": "val"},
  8689. SessionAffinity: "None",
  8690. Type: core.ServiceTypeClusterIP,
  8691. Ports: []core.ServicePort{{Name: "p", Protocol: "TCP", Port: 8675, TargetPort: intstr.FromInt(8675)}},
  8692. },
  8693. }
  8694. }
  8695. func TestValidateService(t *testing.T) {
  8696. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SCTPSupport, true)()
  8697. testCases := []struct {
  8698. name string
  8699. tweakSvc func(svc *core.Service) // given a basic valid service, each test case can customize it
  8700. numErrs int
  8701. }{
  8702. {
  8703. name: "missing namespace",
  8704. tweakSvc: func(s *core.Service) {
  8705. s.Namespace = ""
  8706. },
  8707. numErrs: 1,
  8708. },
  8709. {
  8710. name: "invalid namespace",
  8711. tweakSvc: func(s *core.Service) {
  8712. s.Namespace = "-123"
  8713. },
  8714. numErrs: 1,
  8715. },
  8716. {
  8717. name: "missing name",
  8718. tweakSvc: func(s *core.Service) {
  8719. s.Name = ""
  8720. },
  8721. numErrs: 1,
  8722. },
  8723. {
  8724. name: "invalid name",
  8725. tweakSvc: func(s *core.Service) {
  8726. s.Name = "-123"
  8727. },
  8728. numErrs: 1,
  8729. },
  8730. {
  8731. name: "too long name",
  8732. tweakSvc: func(s *core.Service) {
  8733. s.Name = strings.Repeat("a", 64)
  8734. },
  8735. numErrs: 1,
  8736. },
  8737. {
  8738. name: "invalid generateName",
  8739. tweakSvc: func(s *core.Service) {
  8740. s.GenerateName = "-123"
  8741. },
  8742. numErrs: 1,
  8743. },
  8744. {
  8745. name: "too long generateName",
  8746. tweakSvc: func(s *core.Service) {
  8747. s.GenerateName = strings.Repeat("a", 64)
  8748. },
  8749. numErrs: 1,
  8750. },
  8751. {
  8752. name: "invalid label",
  8753. tweakSvc: func(s *core.Service) {
  8754. s.Labels["NoUppercaseOrSpecialCharsLike=Equals"] = "bar"
  8755. },
  8756. numErrs: 1,
  8757. },
  8758. {
  8759. name: "invalid annotation",
  8760. tweakSvc: func(s *core.Service) {
  8761. s.Annotations["NoSpecialCharsLike=Equals"] = "bar"
  8762. },
  8763. numErrs: 1,
  8764. },
  8765. {
  8766. name: "nil selector",
  8767. tweakSvc: func(s *core.Service) {
  8768. s.Spec.Selector = nil
  8769. },
  8770. numErrs: 0,
  8771. },
  8772. {
  8773. name: "invalid selector",
  8774. tweakSvc: func(s *core.Service) {
  8775. s.Spec.Selector["NoSpecialCharsLike=Equals"] = "bar"
  8776. },
  8777. numErrs: 1,
  8778. },
  8779. {
  8780. name: "missing session affinity",
  8781. tweakSvc: func(s *core.Service) {
  8782. s.Spec.SessionAffinity = ""
  8783. },
  8784. numErrs: 1,
  8785. },
  8786. {
  8787. name: "missing type",
  8788. tweakSvc: func(s *core.Service) {
  8789. s.Spec.Type = ""
  8790. },
  8791. numErrs: 1,
  8792. },
  8793. {
  8794. name: "missing ports",
  8795. tweakSvc: func(s *core.Service) {
  8796. s.Spec.Ports = nil
  8797. },
  8798. numErrs: 1,
  8799. },
  8800. {
  8801. name: "missing ports but headless",
  8802. tweakSvc: func(s *core.Service) {
  8803. s.Spec.Ports = nil
  8804. s.Spec.ClusterIP = core.ClusterIPNone
  8805. },
  8806. numErrs: 0,
  8807. },
  8808. {
  8809. name: "empty port[0] name",
  8810. tweakSvc: func(s *core.Service) {
  8811. s.Spec.Ports[0].Name = ""
  8812. },
  8813. numErrs: 0,
  8814. },
  8815. {
  8816. name: "empty port[1] name",
  8817. tweakSvc: func(s *core.Service) {
  8818. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "", Protocol: "TCP", Port: 12345, TargetPort: intstr.FromInt(12345)})
  8819. },
  8820. numErrs: 1,
  8821. },
  8822. {
  8823. name: "empty multi-port port[0] name",
  8824. tweakSvc: func(s *core.Service) {
  8825. s.Spec.Ports[0].Name = ""
  8826. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "p", Protocol: "TCP", Port: 12345, TargetPort: intstr.FromInt(12345)})
  8827. },
  8828. numErrs: 1,
  8829. },
  8830. {
  8831. name: "invalid port name",
  8832. tweakSvc: func(s *core.Service) {
  8833. s.Spec.Ports[0].Name = "INVALID"
  8834. },
  8835. numErrs: 1,
  8836. },
  8837. {
  8838. name: "missing protocol",
  8839. tweakSvc: func(s *core.Service) {
  8840. s.Spec.Ports[0].Protocol = ""
  8841. },
  8842. numErrs: 1,
  8843. },
  8844. {
  8845. name: "invalid protocol",
  8846. tweakSvc: func(s *core.Service) {
  8847. s.Spec.Ports[0].Protocol = "INVALID"
  8848. },
  8849. numErrs: 1,
  8850. },
  8851. {
  8852. name: "invalid cluster ip",
  8853. tweakSvc: func(s *core.Service) {
  8854. s.Spec.ClusterIP = "invalid"
  8855. },
  8856. numErrs: 1,
  8857. },
  8858. {
  8859. name: "missing port",
  8860. tweakSvc: func(s *core.Service) {
  8861. s.Spec.Ports[0].Port = 0
  8862. },
  8863. numErrs: 1,
  8864. },
  8865. {
  8866. name: "invalid port",
  8867. tweakSvc: func(s *core.Service) {
  8868. s.Spec.Ports[0].Port = 65536
  8869. },
  8870. numErrs: 1,
  8871. },
  8872. {
  8873. name: "invalid TargetPort int",
  8874. tweakSvc: func(s *core.Service) {
  8875. s.Spec.Ports[0].TargetPort = intstr.FromInt(65536)
  8876. },
  8877. numErrs: 1,
  8878. },
  8879. {
  8880. name: "valid port headless",
  8881. tweakSvc: func(s *core.Service) {
  8882. s.Spec.Ports[0].Port = 11722
  8883. s.Spec.Ports[0].TargetPort = intstr.FromInt(11722)
  8884. s.Spec.ClusterIP = core.ClusterIPNone
  8885. },
  8886. numErrs: 0,
  8887. },
  8888. {
  8889. name: "invalid port headless 1",
  8890. tweakSvc: func(s *core.Service) {
  8891. s.Spec.Ports[0].Port = 11722
  8892. s.Spec.Ports[0].TargetPort = intstr.FromInt(11721)
  8893. s.Spec.ClusterIP = core.ClusterIPNone
  8894. },
  8895. // in the v1 API, targetPorts on headless services were tolerated.
  8896. // once we have version-specific validation, we can reject this on newer API versions, but until then, we have to tolerate it for compatibility.
  8897. // numErrs: 1,
  8898. numErrs: 0,
  8899. },
  8900. {
  8901. name: "invalid port headless 2",
  8902. tweakSvc: func(s *core.Service) {
  8903. s.Spec.Ports[0].Port = 11722
  8904. s.Spec.Ports[0].TargetPort = intstr.FromString("target")
  8905. s.Spec.ClusterIP = core.ClusterIPNone
  8906. },
  8907. // in the v1 API, targetPorts on headless services were tolerated.
  8908. // once we have version-specific validation, we can reject this on newer API versions, but until then, we have to tolerate it for compatibility.
  8909. // numErrs: 1,
  8910. numErrs: 0,
  8911. },
  8912. {
  8913. name: "invalid publicIPs localhost",
  8914. tweakSvc: func(s *core.Service) {
  8915. s.Spec.ExternalIPs = []string{"127.0.0.1"}
  8916. },
  8917. numErrs: 1,
  8918. },
  8919. {
  8920. name: "invalid publicIPs unspecified",
  8921. tweakSvc: func(s *core.Service) {
  8922. s.Spec.ExternalIPs = []string{"0.0.0.0"}
  8923. },
  8924. numErrs: 1,
  8925. },
  8926. {
  8927. name: "invalid publicIPs loopback",
  8928. tweakSvc: func(s *core.Service) {
  8929. s.Spec.ExternalIPs = []string{"127.0.0.1"}
  8930. },
  8931. numErrs: 1,
  8932. },
  8933. {
  8934. name: "invalid publicIPs host",
  8935. tweakSvc: func(s *core.Service) {
  8936. s.Spec.ExternalIPs = []string{"myhost.mydomain"}
  8937. },
  8938. numErrs: 1,
  8939. },
  8940. {
  8941. name: "dup port name",
  8942. tweakSvc: func(s *core.Service) {
  8943. s.Spec.Ports[0].Name = "p"
  8944. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "p", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt(12345)})
  8945. },
  8946. numErrs: 1,
  8947. },
  8948. {
  8949. name: "valid load balancer protocol UDP 1",
  8950. tweakSvc: func(s *core.Service) {
  8951. s.Spec.Type = core.ServiceTypeLoadBalancer
  8952. s.Spec.Ports[0].Protocol = "UDP"
  8953. },
  8954. numErrs: 0,
  8955. },
  8956. {
  8957. name: "valid load balancer protocol UDP 2",
  8958. tweakSvc: func(s *core.Service) {
  8959. s.Spec.Type = core.ServiceTypeLoadBalancer
  8960. s.Spec.Ports[0] = core.ServicePort{Name: "q", Port: 12345, Protocol: "UDP", TargetPort: intstr.FromInt(12345)}
  8961. },
  8962. numErrs: 0,
  8963. },
  8964. {
  8965. name: "invalid load balancer with mix protocol",
  8966. tweakSvc: func(s *core.Service) {
  8967. s.Spec.Type = core.ServiceTypeLoadBalancer
  8968. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "UDP", TargetPort: intstr.FromInt(12345)})
  8969. },
  8970. numErrs: 1,
  8971. },
  8972. {
  8973. name: "valid 1",
  8974. tweakSvc: func(s *core.Service) {
  8975. // do nothing
  8976. },
  8977. numErrs: 0,
  8978. },
  8979. {
  8980. name: "valid 2",
  8981. tweakSvc: func(s *core.Service) {
  8982. s.Spec.Ports[0].Protocol = "UDP"
  8983. s.Spec.Ports[0].TargetPort = intstr.FromInt(12345)
  8984. },
  8985. numErrs: 0,
  8986. },
  8987. {
  8988. name: "valid 3",
  8989. tweakSvc: func(s *core.Service) {
  8990. s.Spec.Ports[0].TargetPort = intstr.FromString("http")
  8991. },
  8992. numErrs: 0,
  8993. },
  8994. {
  8995. name: "valid cluster ip - none ",
  8996. tweakSvc: func(s *core.Service) {
  8997. s.Spec.ClusterIP = "None"
  8998. },
  8999. numErrs: 0,
  9000. },
  9001. {
  9002. name: "valid cluster ip - empty",
  9003. tweakSvc: func(s *core.Service) {
  9004. s.Spec.ClusterIP = ""
  9005. s.Spec.Ports[0].TargetPort = intstr.FromString("http")
  9006. },
  9007. numErrs: 0,
  9008. },
  9009. {
  9010. name: "valid type - cluster",
  9011. tweakSvc: func(s *core.Service) {
  9012. s.Spec.Type = core.ServiceTypeClusterIP
  9013. },
  9014. numErrs: 0,
  9015. },
  9016. {
  9017. name: "valid type - loadbalancer",
  9018. tweakSvc: func(s *core.Service) {
  9019. s.Spec.Type = core.ServiceTypeLoadBalancer
  9020. },
  9021. numErrs: 0,
  9022. },
  9023. {
  9024. name: "valid type loadbalancer 2 ports",
  9025. tweakSvc: func(s *core.Service) {
  9026. s.Spec.Type = core.ServiceTypeLoadBalancer
  9027. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt(12345)})
  9028. },
  9029. numErrs: 0,
  9030. },
  9031. {
  9032. name: "valid external load balancer 2 ports",
  9033. tweakSvc: func(s *core.Service) {
  9034. s.Spec.Type = core.ServiceTypeLoadBalancer
  9035. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt(12345)})
  9036. },
  9037. numErrs: 0,
  9038. },
  9039. {
  9040. name: "duplicate nodeports",
  9041. tweakSvc: func(s *core.Service) {
  9042. s.Spec.Type = core.ServiceTypeNodePort
  9043. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 1, Protocol: "TCP", NodePort: 1, TargetPort: intstr.FromInt(1)})
  9044. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "r", Port: 2, Protocol: "TCP", NodePort: 1, TargetPort: intstr.FromInt(2)})
  9045. },
  9046. numErrs: 1,
  9047. },
  9048. {
  9049. name: "duplicate nodeports (different protocols)",
  9050. tweakSvc: func(s *core.Service) {
  9051. s.Spec.Type = core.ServiceTypeNodePort
  9052. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 1, Protocol: "TCP", NodePort: 1, TargetPort: intstr.FromInt(1)})
  9053. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "r", Port: 2, Protocol: "UDP", NodePort: 1, TargetPort: intstr.FromInt(2)})
  9054. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "s", Port: 3, Protocol: "SCTP", NodePort: 1, TargetPort: intstr.FromInt(3)})
  9055. },
  9056. numErrs: 0,
  9057. },
  9058. {
  9059. name: "invalid duplicate ports (with same protocol)",
  9060. tweakSvc: func(s *core.Service) {
  9061. s.Spec.Type = core.ServiceTypeClusterIP
  9062. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt(8080)})
  9063. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "r", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt(80)})
  9064. },
  9065. numErrs: 1,
  9066. },
  9067. {
  9068. name: "valid duplicate ports (with different protocols)",
  9069. tweakSvc: func(s *core.Service) {
  9070. s.Spec.Type = core.ServiceTypeClusterIP
  9071. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt(8080)})
  9072. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "r", Port: 12345, Protocol: "UDP", TargetPort: intstr.FromInt(80)})
  9073. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "s", Port: 12345, Protocol: "SCTP", TargetPort: intstr.FromInt(8088)})
  9074. },
  9075. numErrs: 0,
  9076. },
  9077. {
  9078. name: "valid type - cluster",
  9079. tweakSvc: func(s *core.Service) {
  9080. s.Spec.Type = core.ServiceTypeClusterIP
  9081. },
  9082. numErrs: 0,
  9083. },
  9084. {
  9085. name: "valid type - nodeport",
  9086. tweakSvc: func(s *core.Service) {
  9087. s.Spec.Type = core.ServiceTypeNodePort
  9088. },
  9089. numErrs: 0,
  9090. },
  9091. {
  9092. name: "valid type - loadbalancer",
  9093. tweakSvc: func(s *core.Service) {
  9094. s.Spec.Type = core.ServiceTypeLoadBalancer
  9095. },
  9096. numErrs: 0,
  9097. },
  9098. {
  9099. name: "valid type loadbalancer 2 ports",
  9100. tweakSvc: func(s *core.Service) {
  9101. s.Spec.Type = core.ServiceTypeLoadBalancer
  9102. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt(12345)})
  9103. },
  9104. numErrs: 0,
  9105. },
  9106. {
  9107. name: "valid type loadbalancer with NodePort",
  9108. tweakSvc: func(s *core.Service) {
  9109. s.Spec.Type = core.ServiceTypeLoadBalancer
  9110. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", NodePort: 12345, TargetPort: intstr.FromInt(12345)})
  9111. },
  9112. numErrs: 0,
  9113. },
  9114. {
  9115. name: "valid type=NodePort service with NodePort",
  9116. tweakSvc: func(s *core.Service) {
  9117. s.Spec.Type = core.ServiceTypeNodePort
  9118. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", NodePort: 12345, TargetPort: intstr.FromInt(12345)})
  9119. },
  9120. numErrs: 0,
  9121. },
  9122. {
  9123. name: "valid type=NodePort service without NodePort",
  9124. tweakSvc: func(s *core.Service) {
  9125. s.Spec.Type = core.ServiceTypeNodePort
  9126. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt(12345)})
  9127. },
  9128. numErrs: 0,
  9129. },
  9130. {
  9131. name: "valid cluster service without NodePort",
  9132. tweakSvc: func(s *core.Service) {
  9133. s.Spec.Type = core.ServiceTypeClusterIP
  9134. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt(12345)})
  9135. },
  9136. numErrs: 0,
  9137. },
  9138. {
  9139. name: "invalid cluster service with NodePort",
  9140. tweakSvc: func(s *core.Service) {
  9141. s.Spec.Type = core.ServiceTypeClusterIP
  9142. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", NodePort: 12345, TargetPort: intstr.FromInt(12345)})
  9143. },
  9144. numErrs: 1,
  9145. },
  9146. {
  9147. name: "invalid public service with duplicate NodePort",
  9148. tweakSvc: func(s *core.Service) {
  9149. s.Spec.Type = core.ServiceTypeNodePort
  9150. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "p1", Port: 1, Protocol: "TCP", NodePort: 1, TargetPort: intstr.FromInt(1)})
  9151. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "p2", Port: 2, Protocol: "TCP", NodePort: 1, TargetPort: intstr.FromInt(2)})
  9152. },
  9153. numErrs: 1,
  9154. },
  9155. {
  9156. name: "valid type=LoadBalancer",
  9157. tweakSvc: func(s *core.Service) {
  9158. s.Spec.Type = core.ServiceTypeLoadBalancer
  9159. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt(12345)})
  9160. },
  9161. numErrs: 0,
  9162. },
  9163. {
  9164. // For now we open firewalls, and its insecure if we open 10250, remove this
  9165. // when we have better protections in place.
  9166. name: "invalid port type=LoadBalancer",
  9167. tweakSvc: func(s *core.Service) {
  9168. s.Spec.Type = core.ServiceTypeLoadBalancer
  9169. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "kubelet", Port: 10250, Protocol: "TCP", TargetPort: intstr.FromInt(12345)})
  9170. },
  9171. numErrs: 1,
  9172. },
  9173. {
  9174. name: "valid LoadBalancer source range annotation",
  9175. tweakSvc: func(s *core.Service) {
  9176. s.Spec.Type = core.ServiceTypeLoadBalancer
  9177. s.Annotations[core.AnnotationLoadBalancerSourceRangesKey] = "1.2.3.4/8, 5.6.7.8/16"
  9178. },
  9179. numErrs: 0,
  9180. },
  9181. {
  9182. name: "empty LoadBalancer source range annotation",
  9183. tweakSvc: func(s *core.Service) {
  9184. s.Spec.Type = core.ServiceTypeLoadBalancer
  9185. s.Annotations[core.AnnotationLoadBalancerSourceRangesKey] = ""
  9186. },
  9187. numErrs: 0,
  9188. },
  9189. {
  9190. name: "invalid LoadBalancer source range annotation (hostname)",
  9191. tweakSvc: func(s *core.Service) {
  9192. s.Annotations[core.AnnotationLoadBalancerSourceRangesKey] = "foo.bar"
  9193. },
  9194. numErrs: 2,
  9195. },
  9196. {
  9197. name: "invalid LoadBalancer source range annotation (invalid CIDR)",
  9198. tweakSvc: func(s *core.Service) {
  9199. s.Spec.Type = core.ServiceTypeLoadBalancer
  9200. s.Annotations[core.AnnotationLoadBalancerSourceRangesKey] = "1.2.3.4/33"
  9201. },
  9202. numErrs: 1,
  9203. },
  9204. {
  9205. name: "invalid source range for non LoadBalancer type service",
  9206. tweakSvc: func(s *core.Service) {
  9207. s.Spec.LoadBalancerSourceRanges = []string{"1.2.3.4/8", "5.6.7.8/16"}
  9208. },
  9209. numErrs: 1,
  9210. },
  9211. {
  9212. name: "valid LoadBalancer source range",
  9213. tweakSvc: func(s *core.Service) {
  9214. s.Spec.Type = core.ServiceTypeLoadBalancer
  9215. s.Spec.LoadBalancerSourceRanges = []string{"1.2.3.4/8", "5.6.7.8/16"}
  9216. },
  9217. numErrs: 0,
  9218. },
  9219. {
  9220. name: "empty LoadBalancer source range",
  9221. tweakSvc: func(s *core.Service) {
  9222. s.Spec.Type = core.ServiceTypeLoadBalancer
  9223. s.Spec.LoadBalancerSourceRanges = []string{" "}
  9224. },
  9225. numErrs: 1,
  9226. },
  9227. {
  9228. name: "invalid LoadBalancer source range",
  9229. tweakSvc: func(s *core.Service) {
  9230. s.Spec.Type = core.ServiceTypeLoadBalancer
  9231. s.Spec.LoadBalancerSourceRanges = []string{"foo.bar"}
  9232. },
  9233. numErrs: 1,
  9234. },
  9235. {
  9236. name: "valid ExternalName",
  9237. tweakSvc: func(s *core.Service) {
  9238. s.Spec.Type = core.ServiceTypeExternalName
  9239. s.Spec.ClusterIP = ""
  9240. s.Spec.ExternalName = "foo.bar.example.com"
  9241. },
  9242. numErrs: 0,
  9243. },
  9244. {
  9245. name: "valid ExternalName (trailing dot)",
  9246. tweakSvc: func(s *core.Service) {
  9247. s.Spec.Type = core.ServiceTypeExternalName
  9248. s.Spec.ClusterIP = ""
  9249. s.Spec.ExternalName = "foo.bar.example.com."
  9250. },
  9251. numErrs: 0,
  9252. },
  9253. {
  9254. name: "invalid ExternalName clusterIP (valid IP)",
  9255. tweakSvc: func(s *core.Service) {
  9256. s.Spec.Type = core.ServiceTypeExternalName
  9257. s.Spec.ClusterIP = "1.2.3.4"
  9258. s.Spec.ExternalName = "foo.bar.example.com"
  9259. },
  9260. numErrs: 1,
  9261. },
  9262. {
  9263. name: "invalid ExternalName clusterIP (None)",
  9264. tweakSvc: func(s *core.Service) {
  9265. s.Spec.Type = core.ServiceTypeExternalName
  9266. s.Spec.ClusterIP = "None"
  9267. s.Spec.ExternalName = "foo.bar.example.com"
  9268. },
  9269. numErrs: 1,
  9270. },
  9271. {
  9272. name: "invalid ExternalName (not a DNS name)",
  9273. tweakSvc: func(s *core.Service) {
  9274. s.Spec.Type = core.ServiceTypeExternalName
  9275. s.Spec.ClusterIP = ""
  9276. s.Spec.ExternalName = "-123"
  9277. },
  9278. numErrs: 1,
  9279. },
  9280. {
  9281. name: "LoadBalancer type cannot have None ClusterIP",
  9282. tweakSvc: func(s *core.Service) {
  9283. s.Spec.ClusterIP = "None"
  9284. s.Spec.Type = core.ServiceTypeLoadBalancer
  9285. },
  9286. numErrs: 1,
  9287. },
  9288. {
  9289. name: "invalid node port with clusterIP None",
  9290. tweakSvc: func(s *core.Service) {
  9291. s.Spec.Type = core.ServiceTypeNodePort
  9292. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 1, Protocol: "TCP", NodePort: 1, TargetPort: intstr.FromInt(1)})
  9293. s.Spec.ClusterIP = "None"
  9294. },
  9295. numErrs: 1,
  9296. },
  9297. // ESIPP section begins.
  9298. {
  9299. name: "invalid externalTraffic field",
  9300. tweakSvc: func(s *core.Service) {
  9301. s.Spec.Type = core.ServiceTypeLoadBalancer
  9302. s.Spec.ExternalTrafficPolicy = "invalid"
  9303. },
  9304. numErrs: 1,
  9305. },
  9306. {
  9307. name: "nagative healthCheckNodePort field",
  9308. tweakSvc: func(s *core.Service) {
  9309. s.Spec.Type = core.ServiceTypeLoadBalancer
  9310. s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyTypeLocal
  9311. s.Spec.HealthCheckNodePort = -1
  9312. },
  9313. numErrs: 1,
  9314. },
  9315. {
  9316. name: "nagative healthCheckNodePort field",
  9317. tweakSvc: func(s *core.Service) {
  9318. s.Spec.Type = core.ServiceTypeLoadBalancer
  9319. s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyTypeLocal
  9320. s.Spec.HealthCheckNodePort = 31100
  9321. },
  9322. numErrs: 0,
  9323. },
  9324. // ESIPP section ends.
  9325. {
  9326. name: "invalid timeoutSeconds field",
  9327. tweakSvc: func(s *core.Service) {
  9328. s.Spec.Type = core.ServiceTypeClusterIP
  9329. s.Spec.SessionAffinity = core.ServiceAffinityClientIP
  9330. s.Spec.SessionAffinityConfig = &core.SessionAffinityConfig{
  9331. ClientIP: &core.ClientIPConfig{
  9332. TimeoutSeconds: utilpointer.Int32Ptr(-1),
  9333. },
  9334. }
  9335. },
  9336. numErrs: 1,
  9337. },
  9338. {
  9339. name: "sessionAffinityConfig can't be set when session affinity is None",
  9340. tweakSvc: func(s *core.Service) {
  9341. s.Spec.Type = core.ServiceTypeLoadBalancer
  9342. s.Spec.SessionAffinity = core.ServiceAffinityNone
  9343. s.Spec.SessionAffinityConfig = &core.SessionAffinityConfig{
  9344. ClientIP: &core.ClientIPConfig{
  9345. TimeoutSeconds: utilpointer.Int32Ptr(90),
  9346. },
  9347. }
  9348. },
  9349. numErrs: 1,
  9350. },
  9351. }
  9352. for _, tc := range testCases {
  9353. svc := makeValidService()
  9354. tc.tweakSvc(&svc)
  9355. errs := ValidateService(&svc)
  9356. if len(errs) != tc.numErrs {
  9357. t.Errorf("Unexpected error list for case %q: %v", tc.name, errs.ToAggregate())
  9358. }
  9359. }
  9360. }
  9361. func TestValidateServiceExternalTrafficFieldsCombination(t *testing.T) {
  9362. testCases := []struct {
  9363. name string
  9364. tweakSvc func(svc *core.Service) // Given a basic valid service, each test case can customize it.
  9365. numErrs int
  9366. }{
  9367. {
  9368. name: "valid loadBalancer service with externalTrafficPolicy and healthCheckNodePort set",
  9369. tweakSvc: func(s *core.Service) {
  9370. s.Spec.Type = core.ServiceTypeLoadBalancer
  9371. s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyTypeLocal
  9372. s.Spec.HealthCheckNodePort = 34567
  9373. },
  9374. numErrs: 0,
  9375. },
  9376. {
  9377. name: "valid nodePort service with externalTrafficPolicy set",
  9378. tweakSvc: func(s *core.Service) {
  9379. s.Spec.Type = core.ServiceTypeNodePort
  9380. s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyTypeLocal
  9381. },
  9382. numErrs: 0,
  9383. },
  9384. {
  9385. name: "valid clusterIP service with none of externalTrafficPolicy and healthCheckNodePort set",
  9386. tweakSvc: func(s *core.Service) {
  9387. s.Spec.Type = core.ServiceTypeClusterIP
  9388. },
  9389. numErrs: 0,
  9390. },
  9391. {
  9392. name: "cannot set healthCheckNodePort field on loadBalancer service with externalTrafficPolicy!=Local",
  9393. tweakSvc: func(s *core.Service) {
  9394. s.Spec.Type = core.ServiceTypeLoadBalancer
  9395. s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyTypeCluster
  9396. s.Spec.HealthCheckNodePort = 34567
  9397. },
  9398. numErrs: 1,
  9399. },
  9400. {
  9401. name: "cannot set healthCheckNodePort field on nodePort service",
  9402. tweakSvc: func(s *core.Service) {
  9403. s.Spec.Type = core.ServiceTypeNodePort
  9404. s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyTypeLocal
  9405. s.Spec.HealthCheckNodePort = 34567
  9406. },
  9407. numErrs: 1,
  9408. },
  9409. {
  9410. name: "cannot set externalTrafficPolicy or healthCheckNodePort fields on clusterIP service",
  9411. tweakSvc: func(s *core.Service) {
  9412. s.Spec.Type = core.ServiceTypeClusterIP
  9413. s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyTypeLocal
  9414. s.Spec.HealthCheckNodePort = 34567
  9415. },
  9416. numErrs: 2,
  9417. },
  9418. }
  9419. for _, tc := range testCases {
  9420. svc := makeValidService()
  9421. tc.tweakSvc(&svc)
  9422. errs := ValidateServiceExternalTrafficFieldsCombination(&svc)
  9423. if len(errs) != tc.numErrs {
  9424. t.Errorf("Unexpected error list for case %q: %v", tc.name, errs.ToAggregate())
  9425. }
  9426. }
  9427. }
  9428. func TestValidateReplicationControllerStatus(t *testing.T) {
  9429. tests := []struct {
  9430. name string
  9431. replicas int32
  9432. fullyLabeledReplicas int32
  9433. readyReplicas int32
  9434. availableReplicas int32
  9435. observedGeneration int64
  9436. expectedErr bool
  9437. }{
  9438. {
  9439. name: "valid status",
  9440. replicas: 3,
  9441. fullyLabeledReplicas: 3,
  9442. readyReplicas: 2,
  9443. availableReplicas: 1,
  9444. observedGeneration: 2,
  9445. expectedErr: false,
  9446. },
  9447. {
  9448. name: "invalid replicas",
  9449. replicas: -1,
  9450. fullyLabeledReplicas: 3,
  9451. readyReplicas: 2,
  9452. availableReplicas: 1,
  9453. observedGeneration: 2,
  9454. expectedErr: true,
  9455. },
  9456. {
  9457. name: "invalid fullyLabeledReplicas",
  9458. replicas: 3,
  9459. fullyLabeledReplicas: -1,
  9460. readyReplicas: 2,
  9461. availableReplicas: 1,
  9462. observedGeneration: 2,
  9463. expectedErr: true,
  9464. },
  9465. {
  9466. name: "invalid readyReplicas",
  9467. replicas: 3,
  9468. fullyLabeledReplicas: 3,
  9469. readyReplicas: -1,
  9470. availableReplicas: 1,
  9471. observedGeneration: 2,
  9472. expectedErr: true,
  9473. },
  9474. {
  9475. name: "invalid availableReplicas",
  9476. replicas: 3,
  9477. fullyLabeledReplicas: 3,
  9478. readyReplicas: 3,
  9479. availableReplicas: -1,
  9480. observedGeneration: 2,
  9481. expectedErr: true,
  9482. },
  9483. {
  9484. name: "invalid observedGeneration",
  9485. replicas: 3,
  9486. fullyLabeledReplicas: 3,
  9487. readyReplicas: 3,
  9488. availableReplicas: 3,
  9489. observedGeneration: -1,
  9490. expectedErr: true,
  9491. },
  9492. {
  9493. name: "fullyLabeledReplicas greater than replicas",
  9494. replicas: 3,
  9495. fullyLabeledReplicas: 4,
  9496. readyReplicas: 3,
  9497. availableReplicas: 3,
  9498. observedGeneration: 1,
  9499. expectedErr: true,
  9500. },
  9501. {
  9502. name: "readyReplicas greater than replicas",
  9503. replicas: 3,
  9504. fullyLabeledReplicas: 3,
  9505. readyReplicas: 4,
  9506. availableReplicas: 3,
  9507. observedGeneration: 1,
  9508. expectedErr: true,
  9509. },
  9510. {
  9511. name: "availableReplicas greater than replicas",
  9512. replicas: 3,
  9513. fullyLabeledReplicas: 3,
  9514. readyReplicas: 3,
  9515. availableReplicas: 4,
  9516. observedGeneration: 1,
  9517. expectedErr: true,
  9518. },
  9519. {
  9520. name: "availableReplicas greater than readyReplicas",
  9521. replicas: 3,
  9522. fullyLabeledReplicas: 3,
  9523. readyReplicas: 2,
  9524. availableReplicas: 3,
  9525. observedGeneration: 1,
  9526. expectedErr: true,
  9527. },
  9528. }
  9529. for _, test := range tests {
  9530. status := core.ReplicationControllerStatus{
  9531. Replicas: test.replicas,
  9532. FullyLabeledReplicas: test.fullyLabeledReplicas,
  9533. ReadyReplicas: test.readyReplicas,
  9534. AvailableReplicas: test.availableReplicas,
  9535. ObservedGeneration: test.observedGeneration,
  9536. }
  9537. if hasErr := len(ValidateReplicationControllerStatus(status, field.NewPath("status"))) > 0; hasErr != test.expectedErr {
  9538. t.Errorf("%s: expected error: %t, got error: %t", test.name, test.expectedErr, hasErr)
  9539. }
  9540. }
  9541. }
  9542. func TestValidateReplicationControllerStatusUpdate(t *testing.T) {
  9543. validSelector := map[string]string{"a": "b"}
  9544. validPodTemplate := core.PodTemplate{
  9545. Template: core.PodTemplateSpec{
  9546. ObjectMeta: metav1.ObjectMeta{
  9547. Labels: validSelector,
  9548. },
  9549. Spec: core.PodSpec{
  9550. RestartPolicy: core.RestartPolicyAlways,
  9551. DNSPolicy: core.DNSClusterFirst,
  9552. Containers: []core.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  9553. },
  9554. },
  9555. }
  9556. type rcUpdateTest struct {
  9557. old core.ReplicationController
  9558. update core.ReplicationController
  9559. }
  9560. successCases := []rcUpdateTest{
  9561. {
  9562. old: core.ReplicationController{
  9563. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  9564. Spec: core.ReplicationControllerSpec{
  9565. Selector: validSelector,
  9566. Template: &validPodTemplate.Template,
  9567. },
  9568. Status: core.ReplicationControllerStatus{
  9569. Replicas: 2,
  9570. },
  9571. },
  9572. update: core.ReplicationController{
  9573. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  9574. Spec: core.ReplicationControllerSpec{
  9575. Replicas: 3,
  9576. Selector: validSelector,
  9577. Template: &validPodTemplate.Template,
  9578. },
  9579. Status: core.ReplicationControllerStatus{
  9580. Replicas: 4,
  9581. },
  9582. },
  9583. },
  9584. }
  9585. for _, successCase := range successCases {
  9586. successCase.old.ObjectMeta.ResourceVersion = "1"
  9587. successCase.update.ObjectMeta.ResourceVersion = "1"
  9588. if errs := ValidateReplicationControllerStatusUpdate(&successCase.update, &successCase.old); len(errs) != 0 {
  9589. t.Errorf("expected success: %v", errs)
  9590. }
  9591. }
  9592. errorCases := map[string]rcUpdateTest{
  9593. "negative replicas": {
  9594. old: core.ReplicationController{
  9595. ObjectMeta: metav1.ObjectMeta{Name: "", Namespace: metav1.NamespaceDefault},
  9596. Spec: core.ReplicationControllerSpec{
  9597. Selector: validSelector,
  9598. Template: &validPodTemplate.Template,
  9599. },
  9600. Status: core.ReplicationControllerStatus{
  9601. Replicas: 3,
  9602. },
  9603. },
  9604. update: core.ReplicationController{
  9605. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  9606. Spec: core.ReplicationControllerSpec{
  9607. Replicas: 2,
  9608. Selector: validSelector,
  9609. Template: &validPodTemplate.Template,
  9610. },
  9611. Status: core.ReplicationControllerStatus{
  9612. Replicas: -3,
  9613. },
  9614. },
  9615. },
  9616. }
  9617. for testName, errorCase := range errorCases {
  9618. if errs := ValidateReplicationControllerStatusUpdate(&errorCase.update, &errorCase.old); len(errs) == 0 {
  9619. t.Errorf("expected failure: %s", testName)
  9620. }
  9621. }
  9622. }
  9623. func TestValidateReplicationControllerUpdate(t *testing.T) {
  9624. validSelector := map[string]string{"a": "b"}
  9625. validPodTemplate := core.PodTemplate{
  9626. Template: core.PodTemplateSpec{
  9627. ObjectMeta: metav1.ObjectMeta{
  9628. Labels: validSelector,
  9629. },
  9630. Spec: core.PodSpec{
  9631. RestartPolicy: core.RestartPolicyAlways,
  9632. DNSPolicy: core.DNSClusterFirst,
  9633. Containers: []core.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  9634. },
  9635. },
  9636. }
  9637. readWriteVolumePodTemplate := core.PodTemplate{
  9638. Template: core.PodTemplateSpec{
  9639. ObjectMeta: metav1.ObjectMeta{
  9640. Labels: validSelector,
  9641. },
  9642. Spec: core.PodSpec{
  9643. RestartPolicy: core.RestartPolicyAlways,
  9644. DNSPolicy: core.DNSClusterFirst,
  9645. Containers: []core.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  9646. Volumes: []core.Volume{{Name: "gcepd", VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{PDName: "my-PD", FSType: "ext4", Partition: 1, ReadOnly: false}}}},
  9647. },
  9648. },
  9649. }
  9650. invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
  9651. invalidPodTemplate := core.PodTemplate{
  9652. Template: core.PodTemplateSpec{
  9653. Spec: core.PodSpec{
  9654. RestartPolicy: core.RestartPolicyAlways,
  9655. DNSPolicy: core.DNSClusterFirst,
  9656. },
  9657. ObjectMeta: metav1.ObjectMeta{
  9658. Labels: invalidSelector,
  9659. },
  9660. },
  9661. }
  9662. type rcUpdateTest struct {
  9663. old core.ReplicationController
  9664. update core.ReplicationController
  9665. }
  9666. successCases := []rcUpdateTest{
  9667. {
  9668. old: core.ReplicationController{
  9669. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  9670. Spec: core.ReplicationControllerSpec{
  9671. Selector: validSelector,
  9672. Template: &validPodTemplate.Template,
  9673. },
  9674. },
  9675. update: core.ReplicationController{
  9676. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  9677. Spec: core.ReplicationControllerSpec{
  9678. Replicas: 3,
  9679. Selector: validSelector,
  9680. Template: &validPodTemplate.Template,
  9681. },
  9682. },
  9683. },
  9684. {
  9685. old: core.ReplicationController{
  9686. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  9687. Spec: core.ReplicationControllerSpec{
  9688. Selector: validSelector,
  9689. Template: &validPodTemplate.Template,
  9690. },
  9691. },
  9692. update: core.ReplicationController{
  9693. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  9694. Spec: core.ReplicationControllerSpec{
  9695. Replicas: 1,
  9696. Selector: validSelector,
  9697. Template: &readWriteVolumePodTemplate.Template,
  9698. },
  9699. },
  9700. },
  9701. }
  9702. for _, successCase := range successCases {
  9703. successCase.old.ObjectMeta.ResourceVersion = "1"
  9704. successCase.update.ObjectMeta.ResourceVersion = "1"
  9705. if errs := ValidateReplicationControllerUpdate(&successCase.update, &successCase.old); len(errs) != 0 {
  9706. t.Errorf("expected success: %v", errs)
  9707. }
  9708. }
  9709. errorCases := map[string]rcUpdateTest{
  9710. "more than one read/write": {
  9711. old: core.ReplicationController{
  9712. ObjectMeta: metav1.ObjectMeta{Name: "", Namespace: metav1.NamespaceDefault},
  9713. Spec: core.ReplicationControllerSpec{
  9714. Selector: validSelector,
  9715. Template: &validPodTemplate.Template,
  9716. },
  9717. },
  9718. update: core.ReplicationController{
  9719. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  9720. Spec: core.ReplicationControllerSpec{
  9721. Replicas: 2,
  9722. Selector: validSelector,
  9723. Template: &readWriteVolumePodTemplate.Template,
  9724. },
  9725. },
  9726. },
  9727. "invalid selector": {
  9728. old: core.ReplicationController{
  9729. ObjectMeta: metav1.ObjectMeta{Name: "", Namespace: metav1.NamespaceDefault},
  9730. Spec: core.ReplicationControllerSpec{
  9731. Selector: validSelector,
  9732. Template: &validPodTemplate.Template,
  9733. },
  9734. },
  9735. update: core.ReplicationController{
  9736. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  9737. Spec: core.ReplicationControllerSpec{
  9738. Replicas: 2,
  9739. Selector: invalidSelector,
  9740. Template: &validPodTemplate.Template,
  9741. },
  9742. },
  9743. },
  9744. "invalid pod": {
  9745. old: core.ReplicationController{
  9746. ObjectMeta: metav1.ObjectMeta{Name: "", Namespace: metav1.NamespaceDefault},
  9747. Spec: core.ReplicationControllerSpec{
  9748. Selector: validSelector,
  9749. Template: &validPodTemplate.Template,
  9750. },
  9751. },
  9752. update: core.ReplicationController{
  9753. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  9754. Spec: core.ReplicationControllerSpec{
  9755. Replicas: 2,
  9756. Selector: validSelector,
  9757. Template: &invalidPodTemplate.Template,
  9758. },
  9759. },
  9760. },
  9761. "negative replicas": {
  9762. old: core.ReplicationController{
  9763. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  9764. Spec: core.ReplicationControllerSpec{
  9765. Selector: validSelector,
  9766. Template: &validPodTemplate.Template,
  9767. },
  9768. },
  9769. update: core.ReplicationController{
  9770. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  9771. Spec: core.ReplicationControllerSpec{
  9772. Replicas: -1,
  9773. Selector: validSelector,
  9774. Template: &validPodTemplate.Template,
  9775. },
  9776. },
  9777. },
  9778. }
  9779. for testName, errorCase := range errorCases {
  9780. if errs := ValidateReplicationControllerUpdate(&errorCase.update, &errorCase.old); len(errs) == 0 {
  9781. t.Errorf("expected failure: %s", testName)
  9782. }
  9783. }
  9784. }
  9785. func TestValidateReplicationController(t *testing.T) {
  9786. validSelector := map[string]string{"a": "b"}
  9787. validPodTemplate := core.PodTemplate{
  9788. Template: core.PodTemplateSpec{
  9789. ObjectMeta: metav1.ObjectMeta{
  9790. Labels: validSelector,
  9791. },
  9792. Spec: core.PodSpec{
  9793. RestartPolicy: core.RestartPolicyAlways,
  9794. DNSPolicy: core.DNSClusterFirst,
  9795. Containers: []core.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  9796. },
  9797. },
  9798. }
  9799. readWriteVolumePodTemplate := core.PodTemplate{
  9800. Template: core.PodTemplateSpec{
  9801. ObjectMeta: metav1.ObjectMeta{
  9802. Labels: validSelector,
  9803. },
  9804. Spec: core.PodSpec{
  9805. Volumes: []core.Volume{{Name: "gcepd", VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{PDName: "my-PD", FSType: "ext4", Partition: 1, ReadOnly: false}}}},
  9806. RestartPolicy: core.RestartPolicyAlways,
  9807. DNSPolicy: core.DNSClusterFirst,
  9808. Containers: []core.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  9809. },
  9810. },
  9811. }
  9812. invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
  9813. invalidPodTemplate := core.PodTemplate{
  9814. Template: core.PodTemplateSpec{
  9815. Spec: core.PodSpec{
  9816. RestartPolicy: core.RestartPolicyAlways,
  9817. DNSPolicy: core.DNSClusterFirst,
  9818. },
  9819. ObjectMeta: metav1.ObjectMeta{
  9820. Labels: invalidSelector,
  9821. },
  9822. },
  9823. }
  9824. successCases := []core.ReplicationController{
  9825. {
  9826. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  9827. Spec: core.ReplicationControllerSpec{
  9828. Selector: validSelector,
  9829. Template: &validPodTemplate.Template,
  9830. },
  9831. },
  9832. {
  9833. ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault},
  9834. Spec: core.ReplicationControllerSpec{
  9835. Selector: validSelector,
  9836. Template: &validPodTemplate.Template,
  9837. },
  9838. },
  9839. {
  9840. ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault},
  9841. Spec: core.ReplicationControllerSpec{
  9842. Replicas: 1,
  9843. Selector: validSelector,
  9844. Template: &readWriteVolumePodTemplate.Template,
  9845. },
  9846. },
  9847. }
  9848. for _, successCase := range successCases {
  9849. if errs := ValidateReplicationController(&successCase); len(errs) != 0 {
  9850. t.Errorf("expected success: %v", errs)
  9851. }
  9852. }
  9853. errorCases := map[string]core.ReplicationController{
  9854. "zero-length ID": {
  9855. ObjectMeta: metav1.ObjectMeta{Name: "", Namespace: metav1.NamespaceDefault},
  9856. Spec: core.ReplicationControllerSpec{
  9857. Selector: validSelector,
  9858. Template: &validPodTemplate.Template,
  9859. },
  9860. },
  9861. "missing-namespace": {
  9862. ObjectMeta: metav1.ObjectMeta{Name: "abc-123"},
  9863. Spec: core.ReplicationControllerSpec{
  9864. Selector: validSelector,
  9865. Template: &validPodTemplate.Template,
  9866. },
  9867. },
  9868. "empty selector": {
  9869. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  9870. Spec: core.ReplicationControllerSpec{
  9871. Template: &validPodTemplate.Template,
  9872. },
  9873. },
  9874. "selector_doesnt_match": {
  9875. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  9876. Spec: core.ReplicationControllerSpec{
  9877. Selector: map[string]string{"foo": "bar"},
  9878. Template: &validPodTemplate.Template,
  9879. },
  9880. },
  9881. "invalid manifest": {
  9882. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  9883. Spec: core.ReplicationControllerSpec{
  9884. Selector: validSelector,
  9885. },
  9886. },
  9887. "read-write persistent disk with > 1 pod": {
  9888. ObjectMeta: metav1.ObjectMeta{Name: "abc"},
  9889. Spec: core.ReplicationControllerSpec{
  9890. Replicas: 2,
  9891. Selector: validSelector,
  9892. Template: &readWriteVolumePodTemplate.Template,
  9893. },
  9894. },
  9895. "negative_replicas": {
  9896. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  9897. Spec: core.ReplicationControllerSpec{
  9898. Replicas: -1,
  9899. Selector: validSelector,
  9900. },
  9901. },
  9902. "invalid_label": {
  9903. ObjectMeta: metav1.ObjectMeta{
  9904. Name: "abc-123",
  9905. Namespace: metav1.NamespaceDefault,
  9906. Labels: map[string]string{
  9907. "NoUppercaseOrSpecialCharsLike=Equals": "bar",
  9908. },
  9909. },
  9910. Spec: core.ReplicationControllerSpec{
  9911. Selector: validSelector,
  9912. Template: &validPodTemplate.Template,
  9913. },
  9914. },
  9915. "invalid_label 2": {
  9916. ObjectMeta: metav1.ObjectMeta{
  9917. Name: "abc-123",
  9918. Namespace: metav1.NamespaceDefault,
  9919. Labels: map[string]string{
  9920. "NoUppercaseOrSpecialCharsLike=Equals": "bar",
  9921. },
  9922. },
  9923. Spec: core.ReplicationControllerSpec{
  9924. Template: &invalidPodTemplate.Template,
  9925. },
  9926. },
  9927. "invalid_annotation": {
  9928. ObjectMeta: metav1.ObjectMeta{
  9929. Name: "abc-123",
  9930. Namespace: metav1.NamespaceDefault,
  9931. Annotations: map[string]string{
  9932. "NoUppercaseOrSpecialCharsLike=Equals": "bar",
  9933. },
  9934. },
  9935. Spec: core.ReplicationControllerSpec{
  9936. Selector: validSelector,
  9937. Template: &validPodTemplate.Template,
  9938. },
  9939. },
  9940. "invalid restart policy 1": {
  9941. ObjectMeta: metav1.ObjectMeta{
  9942. Name: "abc-123",
  9943. Namespace: metav1.NamespaceDefault,
  9944. },
  9945. Spec: core.ReplicationControllerSpec{
  9946. Selector: validSelector,
  9947. Template: &core.PodTemplateSpec{
  9948. Spec: core.PodSpec{
  9949. RestartPolicy: core.RestartPolicyOnFailure,
  9950. DNSPolicy: core.DNSClusterFirst,
  9951. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  9952. },
  9953. ObjectMeta: metav1.ObjectMeta{
  9954. Labels: validSelector,
  9955. },
  9956. },
  9957. },
  9958. },
  9959. "invalid restart policy 2": {
  9960. ObjectMeta: metav1.ObjectMeta{
  9961. Name: "abc-123",
  9962. Namespace: metav1.NamespaceDefault,
  9963. },
  9964. Spec: core.ReplicationControllerSpec{
  9965. Selector: validSelector,
  9966. Template: &core.PodTemplateSpec{
  9967. Spec: core.PodSpec{
  9968. RestartPolicy: core.RestartPolicyNever,
  9969. DNSPolicy: core.DNSClusterFirst,
  9970. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  9971. },
  9972. ObjectMeta: metav1.ObjectMeta{
  9973. Labels: validSelector,
  9974. },
  9975. },
  9976. },
  9977. },
  9978. }
  9979. for k, v := range errorCases {
  9980. errs := ValidateReplicationController(&v)
  9981. if len(errs) == 0 {
  9982. t.Errorf("expected failure for %s", k)
  9983. }
  9984. for i := range errs {
  9985. field := errs[i].Field
  9986. if !strings.HasPrefix(field, "spec.template.") &&
  9987. field != "metadata.name" &&
  9988. field != "metadata.namespace" &&
  9989. field != "spec.selector" &&
  9990. field != "spec.template" &&
  9991. field != "GCEPersistentDisk.ReadOnly" &&
  9992. field != "spec.replicas" &&
  9993. field != "spec.template.labels" &&
  9994. field != "metadata.annotations" &&
  9995. field != "metadata.labels" &&
  9996. field != "status.replicas" {
  9997. t.Errorf("%s: missing prefix for: %v", k, errs[i])
  9998. }
  9999. }
  10000. }
  10001. }
  10002. func TestValidateNode(t *testing.T) {
  10003. validSelector := map[string]string{"a": "b"}
  10004. invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
  10005. successCases := []core.Node{
  10006. {
  10007. ObjectMeta: metav1.ObjectMeta{
  10008. Name: "abc",
  10009. Labels: validSelector,
  10010. },
  10011. Status: core.NodeStatus{
  10012. Addresses: []core.NodeAddress{
  10013. {Type: core.NodeExternalIP, Address: "something"},
  10014. },
  10015. Capacity: core.ResourceList{
  10016. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  10017. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  10018. core.ResourceName("my.org/gpu"): resource.MustParse("10"),
  10019. core.ResourceName("hugepages-2Mi"): resource.MustParse("10Gi"),
  10020. core.ResourceName("hugepages-1Gi"): resource.MustParse("0"),
  10021. },
  10022. },
  10023. },
  10024. {
  10025. ObjectMeta: metav1.ObjectMeta{
  10026. Name: "abc",
  10027. },
  10028. Status: core.NodeStatus{
  10029. Addresses: []core.NodeAddress{
  10030. {Type: core.NodeExternalIP, Address: "something"},
  10031. },
  10032. Capacity: core.ResourceList{
  10033. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  10034. core.ResourceName(core.ResourceMemory): resource.MustParse("0"),
  10035. },
  10036. },
  10037. },
  10038. {
  10039. ObjectMeta: metav1.ObjectMeta{
  10040. Name: "dedicated-node1",
  10041. },
  10042. Status: core.NodeStatus{
  10043. Addresses: []core.NodeAddress{
  10044. {Type: core.NodeExternalIP, Address: "something"},
  10045. },
  10046. Capacity: core.ResourceList{
  10047. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  10048. core.ResourceName(core.ResourceMemory): resource.MustParse("0"),
  10049. },
  10050. },
  10051. Spec: core.NodeSpec{
  10052. // Add a valid taint to a node
  10053. Taints: []core.Taint{{Key: "GPU", Value: "true", Effect: "NoSchedule"}},
  10054. },
  10055. },
  10056. {
  10057. ObjectMeta: metav1.ObjectMeta{
  10058. Name: "abc",
  10059. Annotations: map[string]string{
  10060. core.PreferAvoidPodsAnnotationKey: `
  10061. {
  10062. "preferAvoidPods": [
  10063. {
  10064. "podSignature": {
  10065. "podController": {
  10066. "apiVersion": "v1",
  10067. "kind": "ReplicationController",
  10068. "name": "foo",
  10069. "uid": "abcdef123456",
  10070. "controller": true
  10071. }
  10072. },
  10073. "reason": "some reason",
  10074. "message": "some message"
  10075. }
  10076. ]
  10077. }`,
  10078. },
  10079. },
  10080. Status: core.NodeStatus{
  10081. Addresses: []core.NodeAddress{
  10082. {Type: core.NodeExternalIP, Address: "something"},
  10083. },
  10084. Capacity: core.ResourceList{
  10085. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  10086. core.ResourceName(core.ResourceMemory): resource.MustParse("0"),
  10087. },
  10088. },
  10089. },
  10090. {
  10091. ObjectMeta: metav1.ObjectMeta{
  10092. Name: "abc",
  10093. },
  10094. Status: core.NodeStatus{
  10095. Addresses: []core.NodeAddress{
  10096. {Type: core.NodeExternalIP, Address: "something"},
  10097. },
  10098. Capacity: core.ResourceList{
  10099. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  10100. core.ResourceName(core.ResourceMemory): resource.MustParse("0"),
  10101. },
  10102. },
  10103. Spec: core.NodeSpec{
  10104. PodCIDR: "192.168.0.0/16",
  10105. },
  10106. },
  10107. }
  10108. for _, successCase := range successCases {
  10109. if errs := ValidateNode(&successCase); len(errs) != 0 {
  10110. t.Errorf("expected success: %v", errs)
  10111. }
  10112. }
  10113. errorCases := map[string]core.Node{
  10114. "zero-length Name": {
  10115. ObjectMeta: metav1.ObjectMeta{
  10116. Name: "",
  10117. Labels: validSelector,
  10118. },
  10119. Status: core.NodeStatus{
  10120. Addresses: []core.NodeAddress{},
  10121. Capacity: core.ResourceList{
  10122. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  10123. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  10124. },
  10125. },
  10126. },
  10127. "invalid-labels": {
  10128. ObjectMeta: metav1.ObjectMeta{
  10129. Name: "abc-123",
  10130. Labels: invalidSelector,
  10131. },
  10132. Status: core.NodeStatus{
  10133. Capacity: core.ResourceList{
  10134. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  10135. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  10136. },
  10137. },
  10138. },
  10139. "missing-taint-key": {
  10140. ObjectMeta: metav1.ObjectMeta{
  10141. Name: "dedicated-node1",
  10142. },
  10143. Spec: core.NodeSpec{
  10144. // Add a taint with an empty key to a node
  10145. Taints: []core.Taint{{Key: "", Value: "special-user-1", Effect: "NoSchedule"}},
  10146. },
  10147. },
  10148. "bad-taint-key": {
  10149. ObjectMeta: metav1.ObjectMeta{
  10150. Name: "dedicated-node1",
  10151. },
  10152. Spec: core.NodeSpec{
  10153. // Add a taint with an invalid key to a node
  10154. Taints: []core.Taint{{Key: "NoUppercaseOrSpecialCharsLike=Equals", Value: "special-user-1", Effect: "NoSchedule"}},
  10155. },
  10156. },
  10157. "bad-taint-value": {
  10158. ObjectMeta: metav1.ObjectMeta{
  10159. Name: "dedicated-node2",
  10160. },
  10161. Status: core.NodeStatus{
  10162. Addresses: []core.NodeAddress{
  10163. {Type: core.NodeExternalIP, Address: "something"},
  10164. },
  10165. Capacity: core.ResourceList{
  10166. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  10167. core.ResourceName(core.ResourceMemory): resource.MustParse("0"),
  10168. },
  10169. },
  10170. Spec: core.NodeSpec{
  10171. // Add a taint with a bad value to a node
  10172. Taints: []core.Taint{{Key: "dedicated", Value: "some\\bad\\value", Effect: "NoSchedule"}},
  10173. },
  10174. },
  10175. "missing-taint-effect": {
  10176. ObjectMeta: metav1.ObjectMeta{
  10177. Name: "dedicated-node3",
  10178. },
  10179. Status: core.NodeStatus{
  10180. Addresses: []core.NodeAddress{
  10181. {Type: core.NodeExternalIP, Address: "something"},
  10182. },
  10183. Capacity: core.ResourceList{
  10184. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  10185. core.ResourceName(core.ResourceMemory): resource.MustParse("0"),
  10186. },
  10187. },
  10188. Spec: core.NodeSpec{
  10189. // Add a taint with an empty effect to a node
  10190. Taints: []core.Taint{{Key: "dedicated", Value: "special-user-3", Effect: ""}},
  10191. },
  10192. },
  10193. "invalid-taint-effect": {
  10194. ObjectMeta: metav1.ObjectMeta{
  10195. Name: "dedicated-node3",
  10196. },
  10197. Status: core.NodeStatus{
  10198. Addresses: []core.NodeAddress{
  10199. {Type: core.NodeExternalIP, Address: "something"},
  10200. },
  10201. Capacity: core.ResourceList{
  10202. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  10203. core.ResourceName(core.ResourceMemory): resource.MustParse("0"),
  10204. },
  10205. },
  10206. Spec: core.NodeSpec{
  10207. // Add a taint with NoExecute effect to a node
  10208. Taints: []core.Taint{{Key: "dedicated", Value: "special-user-3", Effect: "NoScheduleNoAdmit"}},
  10209. },
  10210. },
  10211. "duplicated-taints-with-same-key-effect": {
  10212. ObjectMeta: metav1.ObjectMeta{
  10213. Name: "dedicated-node1",
  10214. },
  10215. Spec: core.NodeSpec{
  10216. // Add two taints to the node with the same key and effect; should be rejected.
  10217. Taints: []core.Taint{
  10218. {Key: "dedicated", Value: "special-user-1", Effect: "NoSchedule"},
  10219. {Key: "dedicated", Value: "special-user-2", Effect: "NoSchedule"},
  10220. },
  10221. },
  10222. },
  10223. "missing-podSignature": {
  10224. ObjectMeta: metav1.ObjectMeta{
  10225. Name: "abc-123",
  10226. Annotations: map[string]string{
  10227. core.PreferAvoidPodsAnnotationKey: `
  10228. {
  10229. "preferAvoidPods": [
  10230. {
  10231. "reason": "some reason",
  10232. "message": "some message"
  10233. }
  10234. ]
  10235. }`,
  10236. },
  10237. },
  10238. Status: core.NodeStatus{
  10239. Addresses: []core.NodeAddress{},
  10240. Capacity: core.ResourceList{
  10241. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  10242. core.ResourceName(core.ResourceMemory): resource.MustParse("0"),
  10243. },
  10244. },
  10245. },
  10246. "invalid-podController": {
  10247. ObjectMeta: metav1.ObjectMeta{
  10248. Name: "abc-123",
  10249. Annotations: map[string]string{
  10250. core.PreferAvoidPodsAnnotationKey: `
  10251. {
  10252. "preferAvoidPods": [
  10253. {
  10254. "podSignature": {
  10255. "podController": {
  10256. "apiVersion": "v1",
  10257. "kind": "ReplicationController",
  10258. "name": "foo",
  10259. "uid": "abcdef123456",
  10260. "controller": false
  10261. }
  10262. },
  10263. "reason": "some reason",
  10264. "message": "some message"
  10265. }
  10266. ]
  10267. }`,
  10268. },
  10269. },
  10270. Status: core.NodeStatus{
  10271. Addresses: []core.NodeAddress{},
  10272. Capacity: core.ResourceList{
  10273. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  10274. core.ResourceName(core.ResourceMemory): resource.MustParse("0"),
  10275. },
  10276. },
  10277. },
  10278. "multiple-pre-allocated-hugepages": {
  10279. ObjectMeta: metav1.ObjectMeta{
  10280. Name: "abc",
  10281. Labels: validSelector,
  10282. },
  10283. Status: core.NodeStatus{
  10284. Addresses: []core.NodeAddress{
  10285. {Type: core.NodeExternalIP, Address: "something"},
  10286. },
  10287. Capacity: core.ResourceList{
  10288. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  10289. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  10290. core.ResourceName("my.org/gpu"): resource.MustParse("10"),
  10291. core.ResourceName("hugepages-2Mi"): resource.MustParse("10Gi"),
  10292. core.ResourceName("hugepages-1Gi"): resource.MustParse("10Gi"),
  10293. },
  10294. },
  10295. },
  10296. "invalid-pod-cidr": {
  10297. ObjectMeta: metav1.ObjectMeta{
  10298. Name: "abc",
  10299. },
  10300. Status: core.NodeStatus{
  10301. Addresses: []core.NodeAddress{
  10302. {Type: core.NodeExternalIP, Address: "something"},
  10303. },
  10304. Capacity: core.ResourceList{
  10305. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  10306. core.ResourceName(core.ResourceMemory): resource.MustParse("0"),
  10307. },
  10308. },
  10309. Spec: core.NodeSpec{
  10310. PodCIDR: "192.168.0.0",
  10311. },
  10312. },
  10313. }
  10314. for k, v := range errorCases {
  10315. errs := ValidateNode(&v)
  10316. if len(errs) == 0 {
  10317. t.Errorf("expected failure for %s", k)
  10318. }
  10319. for i := range errs {
  10320. field := errs[i].Field
  10321. expectedFields := map[string]bool{
  10322. "metadata.name": true,
  10323. "metadata.labels": true,
  10324. "metadata.annotations": true,
  10325. "metadata.namespace": true,
  10326. "spec.externalID": true,
  10327. "spec.taints[0].key": true,
  10328. "spec.taints[0].value": true,
  10329. "spec.taints[0].effect": true,
  10330. "metadata.annotations.scheduler.alpha.kubernetes.io/preferAvoidPods[0].PodSignature": true,
  10331. "metadata.annotations.scheduler.alpha.kubernetes.io/preferAvoidPods[0].PodSignature.PodController.Controller": true,
  10332. }
  10333. if val, ok := expectedFields[field]; ok {
  10334. if !val {
  10335. t.Errorf("%s: missing prefix for: %v", k, errs[i])
  10336. }
  10337. }
  10338. }
  10339. }
  10340. }
  10341. func TestValidateNodeUpdate(t *testing.T) {
  10342. tests := []struct {
  10343. oldNode core.Node
  10344. node core.Node
  10345. valid bool
  10346. }{
  10347. {core.Node{}, core.Node{}, true},
  10348. {core.Node{
  10349. ObjectMeta: metav1.ObjectMeta{
  10350. Name: "foo"}},
  10351. core.Node{
  10352. ObjectMeta: metav1.ObjectMeta{
  10353. Name: "bar"},
  10354. }, false},
  10355. {core.Node{
  10356. ObjectMeta: metav1.ObjectMeta{
  10357. Name: "foo",
  10358. Labels: map[string]string{"foo": "bar"},
  10359. },
  10360. }, core.Node{
  10361. ObjectMeta: metav1.ObjectMeta{
  10362. Name: "foo",
  10363. Labels: map[string]string{"foo": "baz"},
  10364. },
  10365. }, true},
  10366. {core.Node{
  10367. ObjectMeta: metav1.ObjectMeta{
  10368. Name: "foo",
  10369. },
  10370. }, core.Node{
  10371. ObjectMeta: metav1.ObjectMeta{
  10372. Name: "foo",
  10373. Labels: map[string]string{"foo": "baz"},
  10374. },
  10375. }, true},
  10376. {core.Node{
  10377. ObjectMeta: metav1.ObjectMeta{
  10378. Name: "foo",
  10379. Labels: map[string]string{"bar": "foo"},
  10380. },
  10381. }, core.Node{
  10382. ObjectMeta: metav1.ObjectMeta{
  10383. Name: "foo",
  10384. Labels: map[string]string{"foo": "baz"},
  10385. },
  10386. }, true},
  10387. {core.Node{
  10388. ObjectMeta: metav1.ObjectMeta{
  10389. Name: "foo",
  10390. },
  10391. Spec: core.NodeSpec{
  10392. PodCIDR: "",
  10393. },
  10394. }, core.Node{
  10395. ObjectMeta: metav1.ObjectMeta{
  10396. Name: "foo",
  10397. },
  10398. Spec: core.NodeSpec{
  10399. PodCIDR: "192.168.0.0/16",
  10400. },
  10401. }, true},
  10402. {core.Node{
  10403. ObjectMeta: metav1.ObjectMeta{
  10404. Name: "foo",
  10405. },
  10406. Spec: core.NodeSpec{
  10407. PodCIDR: "192.123.0.0/16",
  10408. },
  10409. }, core.Node{
  10410. ObjectMeta: metav1.ObjectMeta{
  10411. Name: "foo",
  10412. },
  10413. Spec: core.NodeSpec{
  10414. PodCIDR: "192.168.0.0/16",
  10415. },
  10416. }, false},
  10417. {core.Node{
  10418. ObjectMeta: metav1.ObjectMeta{
  10419. Name: "foo",
  10420. },
  10421. Status: core.NodeStatus{
  10422. Capacity: core.ResourceList{
  10423. core.ResourceCPU: resource.MustParse("10000"),
  10424. core.ResourceMemory: resource.MustParse("100"),
  10425. },
  10426. },
  10427. }, core.Node{
  10428. ObjectMeta: metav1.ObjectMeta{
  10429. Name: "foo",
  10430. },
  10431. Status: core.NodeStatus{
  10432. Capacity: core.ResourceList{
  10433. core.ResourceCPU: resource.MustParse("100"),
  10434. core.ResourceMemory: resource.MustParse("10000"),
  10435. },
  10436. },
  10437. }, true},
  10438. {core.Node{
  10439. ObjectMeta: metav1.ObjectMeta{
  10440. Name: "foo",
  10441. Labels: map[string]string{"bar": "foo"},
  10442. },
  10443. Status: core.NodeStatus{
  10444. Capacity: core.ResourceList{
  10445. core.ResourceCPU: resource.MustParse("10000"),
  10446. core.ResourceMemory: resource.MustParse("100"),
  10447. },
  10448. },
  10449. }, core.Node{
  10450. ObjectMeta: metav1.ObjectMeta{
  10451. Name: "foo",
  10452. Labels: map[string]string{"bar": "fooobaz"},
  10453. },
  10454. Status: core.NodeStatus{
  10455. Capacity: core.ResourceList{
  10456. core.ResourceCPU: resource.MustParse("100"),
  10457. core.ResourceMemory: resource.MustParse("10000"),
  10458. },
  10459. },
  10460. }, true},
  10461. {core.Node{
  10462. ObjectMeta: metav1.ObjectMeta{
  10463. Name: "foo",
  10464. Labels: map[string]string{"bar": "foo"},
  10465. },
  10466. Status: core.NodeStatus{
  10467. Addresses: []core.NodeAddress{
  10468. {Type: core.NodeExternalIP, Address: "1.2.3.4"},
  10469. },
  10470. },
  10471. }, core.Node{
  10472. ObjectMeta: metav1.ObjectMeta{
  10473. Name: "foo",
  10474. Labels: map[string]string{"bar": "fooobaz"},
  10475. },
  10476. }, true},
  10477. {core.Node{
  10478. ObjectMeta: metav1.ObjectMeta{
  10479. Name: "foo",
  10480. Labels: map[string]string{"foo": "baz"},
  10481. },
  10482. }, core.Node{
  10483. ObjectMeta: metav1.ObjectMeta{
  10484. Name: "foo",
  10485. Labels: map[string]string{"Foo": "baz"},
  10486. },
  10487. }, true},
  10488. {core.Node{
  10489. ObjectMeta: metav1.ObjectMeta{
  10490. Name: "foo",
  10491. },
  10492. Spec: core.NodeSpec{
  10493. Unschedulable: false,
  10494. },
  10495. }, core.Node{
  10496. ObjectMeta: metav1.ObjectMeta{
  10497. Name: "foo",
  10498. },
  10499. Spec: core.NodeSpec{
  10500. Unschedulable: true,
  10501. },
  10502. }, true},
  10503. {core.Node{
  10504. ObjectMeta: metav1.ObjectMeta{
  10505. Name: "foo",
  10506. },
  10507. Spec: core.NodeSpec{
  10508. Unschedulable: false,
  10509. },
  10510. }, core.Node{
  10511. ObjectMeta: metav1.ObjectMeta{
  10512. Name: "foo",
  10513. },
  10514. Status: core.NodeStatus{
  10515. Addresses: []core.NodeAddress{
  10516. {Type: core.NodeExternalIP, Address: "1.1.1.1"},
  10517. {Type: core.NodeExternalIP, Address: "1.1.1.1"},
  10518. },
  10519. },
  10520. }, false},
  10521. {core.Node{
  10522. ObjectMeta: metav1.ObjectMeta{
  10523. Name: "foo",
  10524. },
  10525. Spec: core.NodeSpec{
  10526. Unschedulable: false,
  10527. },
  10528. }, core.Node{
  10529. ObjectMeta: metav1.ObjectMeta{
  10530. Name: "foo",
  10531. },
  10532. Status: core.NodeStatus{
  10533. Addresses: []core.NodeAddress{
  10534. {Type: core.NodeExternalIP, Address: "1.1.1.1"},
  10535. {Type: core.NodeInternalIP, Address: "10.1.1.1"},
  10536. },
  10537. },
  10538. }, true},
  10539. {core.Node{
  10540. ObjectMeta: metav1.ObjectMeta{
  10541. Name: "foo",
  10542. },
  10543. }, core.Node{
  10544. ObjectMeta: metav1.ObjectMeta{
  10545. Name: "foo",
  10546. Annotations: map[string]string{
  10547. core.PreferAvoidPodsAnnotationKey: `
  10548. {
  10549. "preferAvoidPods": [
  10550. {
  10551. "podSignature": {
  10552. "podController": {
  10553. "apiVersion": "v1",
  10554. "kind": "ReplicationController",
  10555. "name": "foo",
  10556. "uid": "abcdef123456",
  10557. "controller": true
  10558. }
  10559. },
  10560. "reason": "some reason",
  10561. "message": "some message"
  10562. }
  10563. ]
  10564. }`,
  10565. },
  10566. },
  10567. Spec: core.NodeSpec{
  10568. Unschedulable: false,
  10569. },
  10570. }, true},
  10571. {core.Node{
  10572. ObjectMeta: metav1.ObjectMeta{
  10573. Name: "foo",
  10574. },
  10575. }, core.Node{
  10576. ObjectMeta: metav1.ObjectMeta{
  10577. Name: "foo",
  10578. Annotations: map[string]string{
  10579. core.PreferAvoidPodsAnnotationKey: `
  10580. {
  10581. "preferAvoidPods": [
  10582. {
  10583. "reason": "some reason",
  10584. "message": "some message"
  10585. }
  10586. ]
  10587. }`,
  10588. },
  10589. },
  10590. }, false},
  10591. {core.Node{
  10592. ObjectMeta: metav1.ObjectMeta{
  10593. Name: "foo",
  10594. },
  10595. }, core.Node{
  10596. ObjectMeta: metav1.ObjectMeta{
  10597. Name: "foo",
  10598. Annotations: map[string]string{
  10599. core.PreferAvoidPodsAnnotationKey: `
  10600. {
  10601. "preferAvoidPods": [
  10602. {
  10603. "podSignature": {
  10604. "podController": {
  10605. "apiVersion": "v1",
  10606. "kind": "ReplicationController",
  10607. "name": "foo",
  10608. "uid": "abcdef123456",
  10609. "controller": false
  10610. }
  10611. },
  10612. "reason": "some reason",
  10613. "message": "some message"
  10614. }
  10615. ]
  10616. }`,
  10617. },
  10618. },
  10619. }, false},
  10620. {core.Node{
  10621. ObjectMeta: metav1.ObjectMeta{
  10622. Name: "valid-extended-resources",
  10623. },
  10624. }, core.Node{
  10625. ObjectMeta: metav1.ObjectMeta{
  10626. Name: "valid-extended-resources",
  10627. },
  10628. Status: core.NodeStatus{
  10629. Capacity: core.ResourceList{
  10630. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  10631. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  10632. core.ResourceName("example.com/a"): resource.MustParse("5"),
  10633. core.ResourceName("example.com/b"): resource.MustParse("10"),
  10634. },
  10635. },
  10636. }, true},
  10637. {core.Node{
  10638. ObjectMeta: metav1.ObjectMeta{
  10639. Name: "invalid-fractional-extended-capacity",
  10640. },
  10641. }, core.Node{
  10642. ObjectMeta: metav1.ObjectMeta{
  10643. Name: "invalid-fractional-extended-capacity",
  10644. },
  10645. Status: core.NodeStatus{
  10646. Capacity: core.ResourceList{
  10647. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  10648. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  10649. core.ResourceName("example.com/a"): resource.MustParse("500m"),
  10650. },
  10651. },
  10652. }, false},
  10653. {core.Node{
  10654. ObjectMeta: metav1.ObjectMeta{
  10655. Name: "invalid-fractional-extended-allocatable",
  10656. },
  10657. }, core.Node{
  10658. ObjectMeta: metav1.ObjectMeta{
  10659. Name: "invalid-fractional-extended-allocatable",
  10660. },
  10661. Status: core.NodeStatus{
  10662. Capacity: core.ResourceList{
  10663. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  10664. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  10665. core.ResourceName("example.com/a"): resource.MustParse("5"),
  10666. },
  10667. Allocatable: core.ResourceList{
  10668. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  10669. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  10670. core.ResourceName("example.com/a"): resource.MustParse("4.5"),
  10671. },
  10672. },
  10673. }, false},
  10674. {core.Node{
  10675. ObjectMeta: metav1.ObjectMeta{
  10676. Name: "update-provider-id-when-not-set",
  10677. },
  10678. }, core.Node{
  10679. ObjectMeta: metav1.ObjectMeta{
  10680. Name: "update-provider-id-when-not-set",
  10681. },
  10682. Spec: core.NodeSpec{
  10683. ProviderID: "provider:///new",
  10684. },
  10685. }, true},
  10686. {core.Node{
  10687. ObjectMeta: metav1.ObjectMeta{
  10688. Name: "update-provider-id-when-set",
  10689. },
  10690. Spec: core.NodeSpec{
  10691. ProviderID: "provider:///old",
  10692. },
  10693. }, core.Node{
  10694. ObjectMeta: metav1.ObjectMeta{
  10695. Name: "update-provider-id-when-set",
  10696. },
  10697. Spec: core.NodeSpec{
  10698. ProviderID: "provider:///new",
  10699. },
  10700. }, false},
  10701. }
  10702. for i, test := range tests {
  10703. test.oldNode.ObjectMeta.ResourceVersion = "1"
  10704. test.node.ObjectMeta.ResourceVersion = "1"
  10705. errs := ValidateNodeUpdate(&test.node, &test.oldNode)
  10706. if test.valid && len(errs) > 0 {
  10707. t.Errorf("%d: Unexpected error: %v", i, errs)
  10708. t.Logf("%#v vs %#v", test.oldNode.ObjectMeta, test.node.ObjectMeta)
  10709. }
  10710. if !test.valid && len(errs) == 0 {
  10711. t.Errorf("%d: Unexpected non-error", i)
  10712. }
  10713. }
  10714. }
  10715. func TestValidateServiceUpdate(t *testing.T) {
  10716. testCases := []struct {
  10717. name string
  10718. tweakSvc func(oldSvc, newSvc *core.Service) // given basic valid services, each test case can customize them
  10719. numErrs int
  10720. }{
  10721. {
  10722. name: "no change",
  10723. tweakSvc: func(oldSvc, newSvc *core.Service) {
  10724. // do nothing
  10725. },
  10726. numErrs: 0,
  10727. },
  10728. {
  10729. name: "change name",
  10730. tweakSvc: func(oldSvc, newSvc *core.Service) {
  10731. newSvc.Name += "2"
  10732. },
  10733. numErrs: 1,
  10734. },
  10735. {
  10736. name: "change namespace",
  10737. tweakSvc: func(oldSvc, newSvc *core.Service) {
  10738. newSvc.Namespace += "2"
  10739. },
  10740. numErrs: 1,
  10741. },
  10742. {
  10743. name: "change label valid",
  10744. tweakSvc: func(oldSvc, newSvc *core.Service) {
  10745. newSvc.Labels["key"] = "other-value"
  10746. },
  10747. numErrs: 0,
  10748. },
  10749. {
  10750. name: "add label",
  10751. tweakSvc: func(oldSvc, newSvc *core.Service) {
  10752. newSvc.Labels["key2"] = "value2"
  10753. },
  10754. numErrs: 0,
  10755. },
  10756. {
  10757. name: "change cluster IP",
  10758. tweakSvc: func(oldSvc, newSvc *core.Service) {
  10759. oldSvc.Spec.ClusterIP = "1.2.3.4"
  10760. newSvc.Spec.ClusterIP = "8.6.7.5"
  10761. },
  10762. numErrs: 1,
  10763. },
  10764. {
  10765. name: "remove cluster IP",
  10766. tweakSvc: func(oldSvc, newSvc *core.Service) {
  10767. oldSvc.Spec.ClusterIP = "1.2.3.4"
  10768. newSvc.Spec.ClusterIP = ""
  10769. },
  10770. numErrs: 1,
  10771. },
  10772. {
  10773. name: "change affinity",
  10774. tweakSvc: func(oldSvc, newSvc *core.Service) {
  10775. newSvc.Spec.SessionAffinity = "ClientIP"
  10776. newSvc.Spec.SessionAffinityConfig = &core.SessionAffinityConfig{
  10777. ClientIP: &core.ClientIPConfig{
  10778. TimeoutSeconds: utilpointer.Int32Ptr(90),
  10779. },
  10780. }
  10781. },
  10782. numErrs: 0,
  10783. },
  10784. {
  10785. name: "remove affinity",
  10786. tweakSvc: func(oldSvc, newSvc *core.Service) {
  10787. newSvc.Spec.SessionAffinity = ""
  10788. },
  10789. numErrs: 1,
  10790. },
  10791. {
  10792. name: "change type",
  10793. tweakSvc: func(oldSvc, newSvc *core.Service) {
  10794. newSvc.Spec.Type = core.ServiceTypeLoadBalancer
  10795. },
  10796. numErrs: 0,
  10797. },
  10798. {
  10799. name: "remove type",
  10800. tweakSvc: func(oldSvc, newSvc *core.Service) {
  10801. newSvc.Spec.Type = ""
  10802. },
  10803. numErrs: 1,
  10804. },
  10805. {
  10806. name: "change type -> nodeport",
  10807. tweakSvc: func(oldSvc, newSvc *core.Service) {
  10808. newSvc.Spec.Type = core.ServiceTypeNodePort
  10809. },
  10810. numErrs: 0,
  10811. },
  10812. {
  10813. name: "add loadBalancerSourceRanges",
  10814. tweakSvc: func(oldSvc, newSvc *core.Service) {
  10815. oldSvc.Spec.Type = core.ServiceTypeLoadBalancer
  10816. newSvc.Spec.Type = core.ServiceTypeLoadBalancer
  10817. newSvc.Spec.LoadBalancerSourceRanges = []string{"10.0.0.0/8"}
  10818. },
  10819. numErrs: 0,
  10820. },
  10821. {
  10822. name: "update loadBalancerSourceRanges",
  10823. tweakSvc: func(oldSvc, newSvc *core.Service) {
  10824. oldSvc.Spec.Type = core.ServiceTypeLoadBalancer
  10825. oldSvc.Spec.LoadBalancerSourceRanges = []string{"10.0.0.0/8"}
  10826. newSvc.Spec.Type = core.ServiceTypeLoadBalancer
  10827. newSvc.Spec.LoadBalancerSourceRanges = []string{"10.100.0.0/16"}
  10828. },
  10829. numErrs: 0,
  10830. },
  10831. {
  10832. name: "LoadBalancer type cannot have None ClusterIP",
  10833. tweakSvc: func(oldSvc, newSvc *core.Service) {
  10834. newSvc.Spec.ClusterIP = "None"
  10835. newSvc.Spec.Type = core.ServiceTypeLoadBalancer
  10836. },
  10837. numErrs: 1,
  10838. },
  10839. {
  10840. name: "`None` ClusterIP cannot be changed",
  10841. tweakSvc: func(oldSvc, newSvc *core.Service) {
  10842. oldSvc.Spec.ClusterIP = "None"
  10843. newSvc.Spec.ClusterIP = "1.2.3.4"
  10844. },
  10845. numErrs: 1,
  10846. },
  10847. {
  10848. name: "`None` ClusterIP cannot be removed",
  10849. tweakSvc: func(oldSvc, newSvc *core.Service) {
  10850. oldSvc.Spec.ClusterIP = "None"
  10851. newSvc.Spec.ClusterIP = ""
  10852. },
  10853. numErrs: 1,
  10854. },
  10855. {
  10856. name: "Service with ClusterIP type cannot change its set ClusterIP",
  10857. tweakSvc: func(oldSvc, newSvc *core.Service) {
  10858. oldSvc.Spec.Type = core.ServiceTypeClusterIP
  10859. newSvc.Spec.Type = core.ServiceTypeClusterIP
  10860. oldSvc.Spec.ClusterIP = "1.2.3.4"
  10861. newSvc.Spec.ClusterIP = "1.2.3.5"
  10862. },
  10863. numErrs: 1,
  10864. },
  10865. {
  10866. name: "Service with ClusterIP type can change its empty ClusterIP",
  10867. tweakSvc: func(oldSvc, newSvc *core.Service) {
  10868. oldSvc.Spec.Type = core.ServiceTypeClusterIP
  10869. newSvc.Spec.Type = core.ServiceTypeClusterIP
  10870. oldSvc.Spec.ClusterIP = ""
  10871. newSvc.Spec.ClusterIP = "1.2.3.5"
  10872. },
  10873. numErrs: 0,
  10874. },
  10875. {
  10876. name: "Service with ClusterIP type cannot change its set ClusterIP when changing type to NodePort",
  10877. tweakSvc: func(oldSvc, newSvc *core.Service) {
  10878. oldSvc.Spec.Type = core.ServiceTypeClusterIP
  10879. newSvc.Spec.Type = core.ServiceTypeNodePort
  10880. oldSvc.Spec.ClusterIP = "1.2.3.4"
  10881. newSvc.Spec.ClusterIP = "1.2.3.5"
  10882. },
  10883. numErrs: 1,
  10884. },
  10885. {
  10886. name: "Service with ClusterIP type can change its empty ClusterIP when changing type to NodePort",
  10887. tweakSvc: func(oldSvc, newSvc *core.Service) {
  10888. oldSvc.Spec.Type = core.ServiceTypeClusterIP
  10889. newSvc.Spec.Type = core.ServiceTypeNodePort
  10890. oldSvc.Spec.ClusterIP = ""
  10891. newSvc.Spec.ClusterIP = "1.2.3.5"
  10892. },
  10893. numErrs: 0,
  10894. },
  10895. {
  10896. name: "Service with ClusterIP type cannot change its ClusterIP when changing type to LoadBalancer",
  10897. tweakSvc: func(oldSvc, newSvc *core.Service) {
  10898. oldSvc.Spec.Type = core.ServiceTypeClusterIP
  10899. newSvc.Spec.Type = core.ServiceTypeLoadBalancer
  10900. oldSvc.Spec.ClusterIP = "1.2.3.4"
  10901. newSvc.Spec.ClusterIP = "1.2.3.5"
  10902. },
  10903. numErrs: 1,
  10904. },
  10905. {
  10906. name: "Service with ClusterIP type can change its empty ClusterIP when changing type to LoadBalancer",
  10907. tweakSvc: func(oldSvc, newSvc *core.Service) {
  10908. oldSvc.Spec.Type = core.ServiceTypeClusterIP
  10909. newSvc.Spec.Type = core.ServiceTypeLoadBalancer
  10910. oldSvc.Spec.ClusterIP = ""
  10911. newSvc.Spec.ClusterIP = "1.2.3.5"
  10912. },
  10913. numErrs: 0,
  10914. },
  10915. {
  10916. name: "Service with NodePort type cannot change its set ClusterIP",
  10917. tweakSvc: func(oldSvc, newSvc *core.Service) {
  10918. oldSvc.Spec.Type = core.ServiceTypeNodePort
  10919. newSvc.Spec.Type = core.ServiceTypeNodePort
  10920. oldSvc.Spec.ClusterIP = "1.2.3.4"
  10921. newSvc.Spec.ClusterIP = "1.2.3.5"
  10922. },
  10923. numErrs: 1,
  10924. },
  10925. {
  10926. name: "Service with NodePort type can change its empty ClusterIP",
  10927. tweakSvc: func(oldSvc, newSvc *core.Service) {
  10928. oldSvc.Spec.Type = core.ServiceTypeNodePort
  10929. newSvc.Spec.Type = core.ServiceTypeNodePort
  10930. oldSvc.Spec.ClusterIP = ""
  10931. newSvc.Spec.ClusterIP = "1.2.3.5"
  10932. },
  10933. numErrs: 0,
  10934. },
  10935. {
  10936. name: "Service with NodePort type cannot change its set ClusterIP when changing type to ClusterIP",
  10937. tweakSvc: func(oldSvc, newSvc *core.Service) {
  10938. oldSvc.Spec.Type = core.ServiceTypeNodePort
  10939. newSvc.Spec.Type = core.ServiceTypeClusterIP
  10940. oldSvc.Spec.ClusterIP = "1.2.3.4"
  10941. newSvc.Spec.ClusterIP = "1.2.3.5"
  10942. },
  10943. numErrs: 1,
  10944. },
  10945. {
  10946. name: "Service with NodePort type can change its empty ClusterIP when changing type to ClusterIP",
  10947. tweakSvc: func(oldSvc, newSvc *core.Service) {
  10948. oldSvc.Spec.Type = core.ServiceTypeNodePort
  10949. newSvc.Spec.Type = core.ServiceTypeClusterIP
  10950. oldSvc.Spec.ClusterIP = ""
  10951. newSvc.Spec.ClusterIP = "1.2.3.5"
  10952. },
  10953. numErrs: 0,
  10954. },
  10955. {
  10956. name: "Service with NodePort type cannot change its set ClusterIP when changing type to LoadBalancer",
  10957. tweakSvc: func(oldSvc, newSvc *core.Service) {
  10958. oldSvc.Spec.Type = core.ServiceTypeNodePort
  10959. newSvc.Spec.Type = core.ServiceTypeLoadBalancer
  10960. oldSvc.Spec.ClusterIP = "1.2.3.4"
  10961. newSvc.Spec.ClusterIP = "1.2.3.5"
  10962. },
  10963. numErrs: 1,
  10964. },
  10965. {
  10966. name: "Service with NodePort type can change its empty ClusterIP when changing type to LoadBalancer",
  10967. tweakSvc: func(oldSvc, newSvc *core.Service) {
  10968. oldSvc.Spec.Type = core.ServiceTypeNodePort
  10969. newSvc.Spec.Type = core.ServiceTypeLoadBalancer
  10970. oldSvc.Spec.ClusterIP = ""
  10971. newSvc.Spec.ClusterIP = "1.2.3.5"
  10972. },
  10973. numErrs: 0,
  10974. },
  10975. {
  10976. name: "Service with LoadBalancer type cannot change its set ClusterIP",
  10977. tweakSvc: func(oldSvc, newSvc *core.Service) {
  10978. oldSvc.Spec.Type = core.ServiceTypeLoadBalancer
  10979. newSvc.Spec.Type = core.ServiceTypeLoadBalancer
  10980. oldSvc.Spec.ClusterIP = "1.2.3.4"
  10981. newSvc.Spec.ClusterIP = "1.2.3.5"
  10982. },
  10983. numErrs: 1,
  10984. },
  10985. {
  10986. name: "Service with LoadBalancer type can change its empty ClusterIP",
  10987. tweakSvc: func(oldSvc, newSvc *core.Service) {
  10988. oldSvc.Spec.Type = core.ServiceTypeLoadBalancer
  10989. newSvc.Spec.Type = core.ServiceTypeLoadBalancer
  10990. oldSvc.Spec.ClusterIP = ""
  10991. newSvc.Spec.ClusterIP = "1.2.3.5"
  10992. },
  10993. numErrs: 0,
  10994. },
  10995. {
  10996. name: "Service with LoadBalancer type cannot change its set ClusterIP when changing type to ClusterIP",
  10997. tweakSvc: func(oldSvc, newSvc *core.Service) {
  10998. oldSvc.Spec.Type = core.ServiceTypeLoadBalancer
  10999. newSvc.Spec.Type = core.ServiceTypeClusterIP
  11000. oldSvc.Spec.ClusterIP = "1.2.3.4"
  11001. newSvc.Spec.ClusterIP = "1.2.3.5"
  11002. },
  11003. numErrs: 1,
  11004. },
  11005. {
  11006. name: "Service with LoadBalancer type can change its empty ClusterIP when changing type to ClusterIP",
  11007. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11008. oldSvc.Spec.Type = core.ServiceTypeLoadBalancer
  11009. newSvc.Spec.Type = core.ServiceTypeClusterIP
  11010. oldSvc.Spec.ClusterIP = ""
  11011. newSvc.Spec.ClusterIP = "1.2.3.5"
  11012. },
  11013. numErrs: 0,
  11014. },
  11015. {
  11016. name: "Service with LoadBalancer type cannot change its set ClusterIP when changing type to NodePort",
  11017. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11018. oldSvc.Spec.Type = core.ServiceTypeLoadBalancer
  11019. newSvc.Spec.Type = core.ServiceTypeNodePort
  11020. oldSvc.Spec.ClusterIP = "1.2.3.4"
  11021. newSvc.Spec.ClusterIP = "1.2.3.5"
  11022. },
  11023. numErrs: 1,
  11024. },
  11025. {
  11026. name: "Service with LoadBalancer type can change its empty ClusterIP when changing type to NodePort",
  11027. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11028. oldSvc.Spec.Type = core.ServiceTypeLoadBalancer
  11029. newSvc.Spec.Type = core.ServiceTypeNodePort
  11030. oldSvc.Spec.ClusterIP = ""
  11031. newSvc.Spec.ClusterIP = "1.2.3.5"
  11032. },
  11033. numErrs: 0,
  11034. },
  11035. {
  11036. name: "Service with ExternalName type can change its empty ClusterIP when changing type to ClusterIP",
  11037. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11038. oldSvc.Spec.Type = core.ServiceTypeExternalName
  11039. newSvc.Spec.Type = core.ServiceTypeClusterIP
  11040. oldSvc.Spec.ClusterIP = ""
  11041. newSvc.Spec.ClusterIP = "1.2.3.5"
  11042. },
  11043. numErrs: 0,
  11044. },
  11045. {
  11046. name: "Service with ExternalName type can change its set ClusterIP when changing type to ClusterIP",
  11047. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11048. oldSvc.Spec.Type = core.ServiceTypeExternalName
  11049. newSvc.Spec.Type = core.ServiceTypeClusterIP
  11050. oldSvc.Spec.ClusterIP = "1.2.3.4"
  11051. newSvc.Spec.ClusterIP = "1.2.3.5"
  11052. },
  11053. numErrs: 0,
  11054. },
  11055. {
  11056. name: "invalid node port with clusterIP None",
  11057. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11058. oldSvc.Spec.Type = core.ServiceTypeNodePort
  11059. newSvc.Spec.Type = core.ServiceTypeNodePort
  11060. oldSvc.Spec.Ports = append(oldSvc.Spec.Ports, core.ServicePort{Name: "q", Port: 1, Protocol: "TCP", NodePort: 1, TargetPort: intstr.FromInt(1)})
  11061. newSvc.Spec.Ports = append(newSvc.Spec.Ports, core.ServicePort{Name: "q", Port: 1, Protocol: "TCP", NodePort: 1, TargetPort: intstr.FromInt(1)})
  11062. oldSvc.Spec.ClusterIP = ""
  11063. newSvc.Spec.ClusterIP = "None"
  11064. },
  11065. numErrs: 1,
  11066. },
  11067. }
  11068. for _, tc := range testCases {
  11069. oldSvc := makeValidService()
  11070. newSvc := makeValidService()
  11071. tc.tweakSvc(&oldSvc, &newSvc)
  11072. errs := ValidateServiceUpdate(&newSvc, &oldSvc)
  11073. if len(errs) != tc.numErrs {
  11074. t.Errorf("Unexpected error list for case %q: %v", tc.name, errs.ToAggregate())
  11075. }
  11076. }
  11077. }
  11078. func TestValidateResourceNames(t *testing.T) {
  11079. table := []struct {
  11080. input string
  11081. success bool
  11082. expect string
  11083. }{
  11084. {"memory", true, ""},
  11085. {"cpu", true, ""},
  11086. {"storage", true, ""},
  11087. {"requests.cpu", true, ""},
  11088. {"requests.memory", true, ""},
  11089. {"requests.storage", true, ""},
  11090. {"limits.cpu", true, ""},
  11091. {"limits.memory", true, ""},
  11092. {"network", false, ""},
  11093. {"disk", false, ""},
  11094. {"", false, ""},
  11095. {".", false, ""},
  11096. {"..", false, ""},
  11097. {"my.favorite.app.co/12345", true, ""},
  11098. {"my.favorite.app.co/_12345", false, ""},
  11099. {"my.favorite.app.co/12345_", false, ""},
  11100. {"kubernetes.io/..", false, ""},
  11101. {"kubernetes.io/" + strings.Repeat("a", 63), true, ""},
  11102. {"kubernetes.io/" + strings.Repeat("a", 64), false, ""},
  11103. {"kubernetes.io//", false, ""},
  11104. {"kubernetes.io", false, ""},
  11105. {"kubernetes.io/will/not/work/", false, ""},
  11106. }
  11107. for k, item := range table {
  11108. err := validateResourceName(item.input, field.NewPath("field"))
  11109. if len(err) != 0 && item.success {
  11110. t.Errorf("expected no failure for input %q", item.input)
  11111. } else if len(err) == 0 && !item.success {
  11112. t.Errorf("expected failure for input %q", item.input)
  11113. for i := range err {
  11114. detail := err[i].Detail
  11115. if detail != "" && !strings.Contains(detail, item.expect) {
  11116. t.Errorf("%d: expected error detail either empty or %s, got %s", k, item.expect, detail)
  11117. }
  11118. }
  11119. }
  11120. }
  11121. }
  11122. func getResourceList(cpu, memory string) core.ResourceList {
  11123. res := core.ResourceList{}
  11124. if cpu != "" {
  11125. res[core.ResourceCPU] = resource.MustParse(cpu)
  11126. }
  11127. if memory != "" {
  11128. res[core.ResourceMemory] = resource.MustParse(memory)
  11129. }
  11130. return res
  11131. }
  11132. func getStorageResourceList(storage string) core.ResourceList {
  11133. res := core.ResourceList{}
  11134. if storage != "" {
  11135. res[core.ResourceStorage] = resource.MustParse(storage)
  11136. }
  11137. return res
  11138. }
  11139. func getLocalStorageResourceList(ephemeralStorage string) core.ResourceList {
  11140. res := core.ResourceList{}
  11141. if ephemeralStorage != "" {
  11142. res[core.ResourceEphemeralStorage] = resource.MustParse(ephemeralStorage)
  11143. }
  11144. return res
  11145. }
  11146. func TestValidateLimitRangeForLocalStorage(t *testing.T) {
  11147. testCases := []struct {
  11148. name string
  11149. spec core.LimitRangeSpec
  11150. }{
  11151. {
  11152. name: "all-fields-valid",
  11153. spec: core.LimitRangeSpec{
  11154. Limits: []core.LimitRangeItem{
  11155. {
  11156. Type: core.LimitTypePod,
  11157. Max: getLocalStorageResourceList("10000Mi"),
  11158. Min: getLocalStorageResourceList("100Mi"),
  11159. MaxLimitRequestRatio: getLocalStorageResourceList(""),
  11160. },
  11161. {
  11162. Type: core.LimitTypeContainer,
  11163. Max: getLocalStorageResourceList("10000Mi"),
  11164. Min: getLocalStorageResourceList("100Mi"),
  11165. Default: getLocalStorageResourceList("500Mi"),
  11166. DefaultRequest: getLocalStorageResourceList("200Mi"),
  11167. MaxLimitRequestRatio: getLocalStorageResourceList(""),
  11168. },
  11169. },
  11170. },
  11171. },
  11172. }
  11173. for _, testCase := range testCases {
  11174. limitRange := &core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: testCase.name, Namespace: "foo"}, Spec: testCase.spec}
  11175. if errs := ValidateLimitRange(limitRange); len(errs) != 0 {
  11176. t.Errorf("Case %v, unexpected error: %v", testCase.name, errs)
  11177. }
  11178. }
  11179. }
  11180. func TestValidateLimitRange(t *testing.T) {
  11181. successCases := []struct {
  11182. name string
  11183. spec core.LimitRangeSpec
  11184. }{
  11185. {
  11186. name: "all-fields-valid",
  11187. spec: core.LimitRangeSpec{
  11188. Limits: []core.LimitRangeItem{
  11189. {
  11190. Type: core.LimitTypePod,
  11191. Max: getResourceList("100m", "10000Mi"),
  11192. Min: getResourceList("5m", "100Mi"),
  11193. MaxLimitRequestRatio: getResourceList("10", ""),
  11194. },
  11195. {
  11196. Type: core.LimitTypeContainer,
  11197. Max: getResourceList("100m", "10000Mi"),
  11198. Min: getResourceList("5m", "100Mi"),
  11199. Default: getResourceList("50m", "500Mi"),
  11200. DefaultRequest: getResourceList("10m", "200Mi"),
  11201. MaxLimitRequestRatio: getResourceList("10", ""),
  11202. },
  11203. {
  11204. Type: core.LimitTypePersistentVolumeClaim,
  11205. Max: getStorageResourceList("10Gi"),
  11206. Min: getStorageResourceList("5Gi"),
  11207. },
  11208. },
  11209. },
  11210. },
  11211. {
  11212. name: "pvc-min-only",
  11213. spec: core.LimitRangeSpec{
  11214. Limits: []core.LimitRangeItem{
  11215. {
  11216. Type: core.LimitTypePersistentVolumeClaim,
  11217. Min: getStorageResourceList("5Gi"),
  11218. },
  11219. },
  11220. },
  11221. },
  11222. {
  11223. name: "pvc-max-only",
  11224. spec: core.LimitRangeSpec{
  11225. Limits: []core.LimitRangeItem{
  11226. {
  11227. Type: core.LimitTypePersistentVolumeClaim,
  11228. Max: getStorageResourceList("10Gi"),
  11229. },
  11230. },
  11231. },
  11232. },
  11233. {
  11234. name: "all-fields-valid-big-numbers",
  11235. spec: core.LimitRangeSpec{
  11236. Limits: []core.LimitRangeItem{
  11237. {
  11238. Type: core.LimitTypeContainer,
  11239. Max: getResourceList("100m", "10000T"),
  11240. Min: getResourceList("5m", "100Mi"),
  11241. Default: getResourceList("50m", "500Mi"),
  11242. DefaultRequest: getResourceList("10m", "200Mi"),
  11243. MaxLimitRequestRatio: getResourceList("10", ""),
  11244. },
  11245. },
  11246. },
  11247. },
  11248. {
  11249. name: "thirdparty-fields-all-valid-standard-container-resources",
  11250. spec: core.LimitRangeSpec{
  11251. Limits: []core.LimitRangeItem{
  11252. {
  11253. Type: "thirdparty.com/foo",
  11254. Max: getResourceList("100m", "10000T"),
  11255. Min: getResourceList("5m", "100Mi"),
  11256. Default: getResourceList("50m", "500Mi"),
  11257. DefaultRequest: getResourceList("10m", "200Mi"),
  11258. MaxLimitRequestRatio: getResourceList("10", ""),
  11259. },
  11260. },
  11261. },
  11262. },
  11263. {
  11264. name: "thirdparty-fields-all-valid-storage-resources",
  11265. spec: core.LimitRangeSpec{
  11266. Limits: []core.LimitRangeItem{
  11267. {
  11268. Type: "thirdparty.com/foo",
  11269. Max: getStorageResourceList("10000T"),
  11270. Min: getStorageResourceList("100Mi"),
  11271. Default: getStorageResourceList("500Mi"),
  11272. DefaultRequest: getStorageResourceList("200Mi"),
  11273. MaxLimitRequestRatio: getStorageResourceList(""),
  11274. },
  11275. },
  11276. },
  11277. },
  11278. }
  11279. for _, successCase := range successCases {
  11280. limitRange := &core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: successCase.name, Namespace: "foo"}, Spec: successCase.spec}
  11281. if errs := ValidateLimitRange(limitRange); len(errs) != 0 {
  11282. t.Errorf("Case %v, unexpected error: %v", successCase.name, errs)
  11283. }
  11284. }
  11285. errorCases := map[string]struct {
  11286. R core.LimitRange
  11287. D string
  11288. }{
  11289. "zero-length-name": {
  11290. core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "", Namespace: "foo"}, Spec: core.LimitRangeSpec{}},
  11291. "name or generateName is required",
  11292. },
  11293. "zero-length-namespace": {
  11294. core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: ""}, Spec: core.LimitRangeSpec{}},
  11295. "",
  11296. },
  11297. "invalid-name": {
  11298. core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "^Invalid", Namespace: "foo"}, Spec: core.LimitRangeSpec{}},
  11299. dnsSubdomainLabelErrMsg,
  11300. },
  11301. "invalid-namespace": {
  11302. core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "^Invalid"}, Spec: core.LimitRangeSpec{}},
  11303. dnsLabelErrMsg,
  11304. },
  11305. "duplicate-limit-type": {
  11306. core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: core.LimitRangeSpec{
  11307. Limits: []core.LimitRangeItem{
  11308. {
  11309. Type: core.LimitTypePod,
  11310. Max: getResourceList("100m", "10000m"),
  11311. Min: getResourceList("0m", "100m"),
  11312. },
  11313. {
  11314. Type: core.LimitTypePod,
  11315. Min: getResourceList("0m", "100m"),
  11316. },
  11317. },
  11318. }},
  11319. "",
  11320. },
  11321. "default-limit-type-pod": {
  11322. core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: core.LimitRangeSpec{
  11323. Limits: []core.LimitRangeItem{
  11324. {
  11325. Type: core.LimitTypePod,
  11326. Max: getResourceList("100m", "10000m"),
  11327. Min: getResourceList("0m", "100m"),
  11328. Default: getResourceList("10m", "100m"),
  11329. },
  11330. },
  11331. }},
  11332. "may not be specified when `type` is 'Pod'",
  11333. },
  11334. "default-request-limit-type-pod": {
  11335. core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: core.LimitRangeSpec{
  11336. Limits: []core.LimitRangeItem{
  11337. {
  11338. Type: core.LimitTypePod,
  11339. Max: getResourceList("100m", "10000m"),
  11340. Min: getResourceList("0m", "100m"),
  11341. DefaultRequest: getResourceList("10m", "100m"),
  11342. },
  11343. },
  11344. }},
  11345. "may not be specified when `type` is 'Pod'",
  11346. },
  11347. "min value 100m is greater than max value 10m": {
  11348. core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: core.LimitRangeSpec{
  11349. Limits: []core.LimitRangeItem{
  11350. {
  11351. Type: core.LimitTypePod,
  11352. Max: getResourceList("10m", ""),
  11353. Min: getResourceList("100m", ""),
  11354. },
  11355. },
  11356. }},
  11357. "min value 100m is greater than max value 10m",
  11358. },
  11359. "invalid spec default outside range": {
  11360. core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: core.LimitRangeSpec{
  11361. Limits: []core.LimitRangeItem{
  11362. {
  11363. Type: core.LimitTypeContainer,
  11364. Max: getResourceList("1", ""),
  11365. Min: getResourceList("100m", ""),
  11366. Default: getResourceList("2000m", ""),
  11367. },
  11368. },
  11369. }},
  11370. "default value 2 is greater than max value 1",
  11371. },
  11372. "invalid spec default request outside range": {
  11373. core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: core.LimitRangeSpec{
  11374. Limits: []core.LimitRangeItem{
  11375. {
  11376. Type: core.LimitTypeContainer,
  11377. Max: getResourceList("1", ""),
  11378. Min: getResourceList("100m", ""),
  11379. DefaultRequest: getResourceList("2000m", ""),
  11380. },
  11381. },
  11382. }},
  11383. "default request value 2 is greater than max value 1",
  11384. },
  11385. "invalid spec default request more than default": {
  11386. core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: core.LimitRangeSpec{
  11387. Limits: []core.LimitRangeItem{
  11388. {
  11389. Type: core.LimitTypeContainer,
  11390. Max: getResourceList("2", ""),
  11391. Min: getResourceList("100m", ""),
  11392. Default: getResourceList("500m", ""),
  11393. DefaultRequest: getResourceList("800m", ""),
  11394. },
  11395. },
  11396. }},
  11397. "default request value 800m is greater than default limit value 500m",
  11398. },
  11399. "invalid spec maxLimitRequestRatio less than 1": {
  11400. core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: core.LimitRangeSpec{
  11401. Limits: []core.LimitRangeItem{
  11402. {
  11403. Type: core.LimitTypePod,
  11404. MaxLimitRequestRatio: getResourceList("800m", ""),
  11405. },
  11406. },
  11407. }},
  11408. "ratio 800m is less than 1",
  11409. },
  11410. "invalid spec maxLimitRequestRatio greater than max/min": {
  11411. core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: core.LimitRangeSpec{
  11412. Limits: []core.LimitRangeItem{
  11413. {
  11414. Type: core.LimitTypeContainer,
  11415. Max: getResourceList("", "2Gi"),
  11416. Min: getResourceList("", "512Mi"),
  11417. MaxLimitRequestRatio: getResourceList("", "10"),
  11418. },
  11419. },
  11420. }},
  11421. "ratio 10 is greater than max/min = 4.000000",
  11422. },
  11423. "invalid non standard limit type": {
  11424. core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: core.LimitRangeSpec{
  11425. Limits: []core.LimitRangeItem{
  11426. {
  11427. Type: "foo",
  11428. Max: getStorageResourceList("10000T"),
  11429. Min: getStorageResourceList("100Mi"),
  11430. Default: getStorageResourceList("500Mi"),
  11431. DefaultRequest: getStorageResourceList("200Mi"),
  11432. MaxLimitRequestRatio: getStorageResourceList(""),
  11433. },
  11434. },
  11435. }},
  11436. "must be a standard limit type or fully qualified",
  11437. },
  11438. "min and max values missing, one required": {
  11439. core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: core.LimitRangeSpec{
  11440. Limits: []core.LimitRangeItem{
  11441. {
  11442. Type: core.LimitTypePersistentVolumeClaim,
  11443. },
  11444. },
  11445. }},
  11446. "either minimum or maximum storage value is required, but neither was provided",
  11447. },
  11448. "invalid min greater than max": {
  11449. core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: core.LimitRangeSpec{
  11450. Limits: []core.LimitRangeItem{
  11451. {
  11452. Type: core.LimitTypePersistentVolumeClaim,
  11453. Min: getStorageResourceList("10Gi"),
  11454. Max: getStorageResourceList("1Gi"),
  11455. },
  11456. },
  11457. }},
  11458. "min value 10Gi is greater than max value 1Gi",
  11459. },
  11460. }
  11461. for k, v := range errorCases {
  11462. errs := ValidateLimitRange(&v.R)
  11463. if len(errs) == 0 {
  11464. t.Errorf("expected failure for %s", k)
  11465. }
  11466. for i := range errs {
  11467. detail := errs[i].Detail
  11468. if !strings.Contains(detail, v.D) {
  11469. t.Errorf("[%s]: expected error detail either empty or %q, got %q", k, v.D, detail)
  11470. }
  11471. }
  11472. }
  11473. }
  11474. func TestValidatePersistentVolumeClaimStatusUpdate(t *testing.T) {
  11475. validClaim := testVolumeClaim("foo", "ns", core.PersistentVolumeClaimSpec{
  11476. AccessModes: []core.PersistentVolumeAccessMode{
  11477. core.ReadWriteOnce,
  11478. core.ReadOnlyMany,
  11479. },
  11480. Resources: core.ResourceRequirements{
  11481. Requests: core.ResourceList{
  11482. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  11483. },
  11484. },
  11485. })
  11486. validConditionUpdate := testVolumeClaimWithStatus("foo", "ns", core.PersistentVolumeClaimSpec{
  11487. AccessModes: []core.PersistentVolumeAccessMode{
  11488. core.ReadWriteOnce,
  11489. core.ReadOnlyMany,
  11490. },
  11491. Resources: core.ResourceRequirements{
  11492. Requests: core.ResourceList{
  11493. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  11494. },
  11495. },
  11496. }, core.PersistentVolumeClaimStatus{
  11497. Phase: core.ClaimPending,
  11498. Conditions: []core.PersistentVolumeClaimCondition{
  11499. {Type: core.PersistentVolumeClaimResizing, Status: core.ConditionTrue},
  11500. },
  11501. })
  11502. scenarios := map[string]struct {
  11503. isExpectedFailure bool
  11504. oldClaim *core.PersistentVolumeClaim
  11505. newClaim *core.PersistentVolumeClaim
  11506. enableResize bool
  11507. }{
  11508. "condition-update-with-enabled-feature-gate": {
  11509. isExpectedFailure: false,
  11510. oldClaim: validClaim,
  11511. newClaim: validConditionUpdate,
  11512. enableResize: true,
  11513. },
  11514. }
  11515. for name, scenario := range scenarios {
  11516. t.Run(name, func(t *testing.T) {
  11517. // ensure we have a resource version specified for updates
  11518. scenario.oldClaim.ResourceVersion = "1"
  11519. scenario.newClaim.ResourceVersion = "1"
  11520. errs := ValidatePersistentVolumeClaimStatusUpdate(scenario.newClaim, scenario.oldClaim)
  11521. if len(errs) == 0 && scenario.isExpectedFailure {
  11522. t.Errorf("Unexpected success for scenario: %s", name)
  11523. }
  11524. if len(errs) > 0 && !scenario.isExpectedFailure {
  11525. t.Errorf("Unexpected failure for scenario: %s - %+v", name, errs)
  11526. }
  11527. })
  11528. }
  11529. }
  11530. func TestValidateResourceQuota(t *testing.T) {
  11531. spec := core.ResourceQuotaSpec{
  11532. Hard: core.ResourceList{
  11533. core.ResourceCPU: resource.MustParse("100"),
  11534. core.ResourceMemory: resource.MustParse("10000"),
  11535. core.ResourceRequestsCPU: resource.MustParse("100"),
  11536. core.ResourceRequestsMemory: resource.MustParse("10000"),
  11537. core.ResourceLimitsCPU: resource.MustParse("100"),
  11538. core.ResourceLimitsMemory: resource.MustParse("10000"),
  11539. core.ResourcePods: resource.MustParse("10"),
  11540. core.ResourceServices: resource.MustParse("0"),
  11541. core.ResourceReplicationControllers: resource.MustParse("10"),
  11542. core.ResourceQuotas: resource.MustParse("10"),
  11543. core.ResourceConfigMaps: resource.MustParse("10"),
  11544. core.ResourceSecrets: resource.MustParse("10"),
  11545. },
  11546. }
  11547. terminatingSpec := core.ResourceQuotaSpec{
  11548. Hard: core.ResourceList{
  11549. core.ResourceCPU: resource.MustParse("100"),
  11550. core.ResourceLimitsCPU: resource.MustParse("200"),
  11551. },
  11552. Scopes: []core.ResourceQuotaScope{core.ResourceQuotaScopeTerminating},
  11553. }
  11554. nonTerminatingSpec := core.ResourceQuotaSpec{
  11555. Hard: core.ResourceList{
  11556. core.ResourceCPU: resource.MustParse("100"),
  11557. },
  11558. Scopes: []core.ResourceQuotaScope{core.ResourceQuotaScopeNotTerminating},
  11559. }
  11560. bestEffortSpec := core.ResourceQuotaSpec{
  11561. Hard: core.ResourceList{
  11562. core.ResourcePods: resource.MustParse("100"),
  11563. },
  11564. Scopes: []core.ResourceQuotaScope{core.ResourceQuotaScopeBestEffort},
  11565. }
  11566. nonBestEffortSpec := core.ResourceQuotaSpec{
  11567. Hard: core.ResourceList{
  11568. core.ResourceCPU: resource.MustParse("100"),
  11569. },
  11570. Scopes: []core.ResourceQuotaScope{core.ResourceQuotaScopeNotBestEffort},
  11571. }
  11572. scopeSelectorSpec := core.ResourceQuotaSpec{
  11573. ScopeSelector: &core.ScopeSelector{
  11574. MatchExpressions: []core.ScopedResourceSelectorRequirement{
  11575. {
  11576. ScopeName: core.ResourceQuotaScopePriorityClass,
  11577. Operator: core.ScopeSelectorOpIn,
  11578. Values: []string{"cluster-services"},
  11579. },
  11580. },
  11581. },
  11582. }
  11583. // storage is not yet supported as a quota tracked resource
  11584. invalidQuotaResourceSpec := core.ResourceQuotaSpec{
  11585. Hard: core.ResourceList{
  11586. core.ResourceStorage: resource.MustParse("10"),
  11587. },
  11588. }
  11589. negativeSpec := core.ResourceQuotaSpec{
  11590. Hard: core.ResourceList{
  11591. core.ResourceCPU: resource.MustParse("-100"),
  11592. core.ResourceMemory: resource.MustParse("-10000"),
  11593. core.ResourcePods: resource.MustParse("-10"),
  11594. core.ResourceServices: resource.MustParse("-10"),
  11595. core.ResourceReplicationControllers: resource.MustParse("-10"),
  11596. core.ResourceQuotas: resource.MustParse("-10"),
  11597. core.ResourceConfigMaps: resource.MustParse("-10"),
  11598. core.ResourceSecrets: resource.MustParse("-10"),
  11599. },
  11600. }
  11601. fractionalComputeSpec := core.ResourceQuotaSpec{
  11602. Hard: core.ResourceList{
  11603. core.ResourceCPU: resource.MustParse("100m"),
  11604. },
  11605. }
  11606. fractionalPodSpec := core.ResourceQuotaSpec{
  11607. Hard: core.ResourceList{
  11608. core.ResourcePods: resource.MustParse(".1"),
  11609. core.ResourceServices: resource.MustParse(".5"),
  11610. core.ResourceReplicationControllers: resource.MustParse("1.25"),
  11611. core.ResourceQuotas: resource.MustParse("2.5"),
  11612. },
  11613. }
  11614. invalidTerminatingScopePairsSpec := core.ResourceQuotaSpec{
  11615. Hard: core.ResourceList{
  11616. core.ResourceCPU: resource.MustParse("100"),
  11617. },
  11618. Scopes: []core.ResourceQuotaScope{core.ResourceQuotaScopeTerminating, core.ResourceQuotaScopeNotTerminating},
  11619. }
  11620. invalidBestEffortScopePairsSpec := core.ResourceQuotaSpec{
  11621. Hard: core.ResourceList{
  11622. core.ResourcePods: resource.MustParse("100"),
  11623. },
  11624. Scopes: []core.ResourceQuotaScope{core.ResourceQuotaScopeBestEffort, core.ResourceQuotaScopeNotBestEffort},
  11625. }
  11626. invalidScopeNameSpec := core.ResourceQuotaSpec{
  11627. Hard: core.ResourceList{
  11628. core.ResourceCPU: resource.MustParse("100"),
  11629. },
  11630. Scopes: []core.ResourceQuotaScope{core.ResourceQuotaScope("foo")},
  11631. }
  11632. successCases := []core.ResourceQuota{
  11633. {
  11634. ObjectMeta: metav1.ObjectMeta{
  11635. Name: "abc",
  11636. Namespace: "foo",
  11637. },
  11638. Spec: spec,
  11639. },
  11640. {
  11641. ObjectMeta: metav1.ObjectMeta{
  11642. Name: "abc",
  11643. Namespace: "foo",
  11644. },
  11645. Spec: fractionalComputeSpec,
  11646. },
  11647. {
  11648. ObjectMeta: metav1.ObjectMeta{
  11649. Name: "abc",
  11650. Namespace: "foo",
  11651. },
  11652. Spec: terminatingSpec,
  11653. },
  11654. {
  11655. ObjectMeta: metav1.ObjectMeta{
  11656. Name: "abc",
  11657. Namespace: "foo",
  11658. },
  11659. Spec: nonTerminatingSpec,
  11660. },
  11661. {
  11662. ObjectMeta: metav1.ObjectMeta{
  11663. Name: "abc",
  11664. Namespace: "foo",
  11665. },
  11666. Spec: bestEffortSpec,
  11667. },
  11668. {
  11669. ObjectMeta: metav1.ObjectMeta{
  11670. Name: "abc",
  11671. Namespace: "foo",
  11672. },
  11673. Spec: scopeSelectorSpec,
  11674. },
  11675. {
  11676. ObjectMeta: metav1.ObjectMeta{
  11677. Name: "abc",
  11678. Namespace: "foo",
  11679. },
  11680. Spec: nonBestEffortSpec,
  11681. },
  11682. }
  11683. for _, successCase := range successCases {
  11684. if errs := ValidateResourceQuota(&successCase); len(errs) != 0 {
  11685. t.Errorf("expected success: %v", errs)
  11686. }
  11687. }
  11688. errorCases := map[string]struct {
  11689. R core.ResourceQuota
  11690. D string
  11691. }{
  11692. "zero-length Name": {
  11693. core.ResourceQuota{ObjectMeta: metav1.ObjectMeta{Name: "", Namespace: "foo"}, Spec: spec},
  11694. "name or generateName is required",
  11695. },
  11696. "zero-length Namespace": {
  11697. core.ResourceQuota{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: ""}, Spec: spec},
  11698. "",
  11699. },
  11700. "invalid Name": {
  11701. core.ResourceQuota{ObjectMeta: metav1.ObjectMeta{Name: "^Invalid", Namespace: "foo"}, Spec: spec},
  11702. dnsSubdomainLabelErrMsg,
  11703. },
  11704. "invalid Namespace": {
  11705. core.ResourceQuota{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "^Invalid"}, Spec: spec},
  11706. dnsLabelErrMsg,
  11707. },
  11708. "negative-limits": {
  11709. core.ResourceQuota{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: negativeSpec},
  11710. isNegativeErrorMsg,
  11711. },
  11712. "fractional-api-resource": {
  11713. core.ResourceQuota{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: fractionalPodSpec},
  11714. isNotIntegerErrorMsg,
  11715. },
  11716. "invalid-quota-resource": {
  11717. core.ResourceQuota{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: invalidQuotaResourceSpec},
  11718. isInvalidQuotaResource,
  11719. },
  11720. "invalid-quota-terminating-pair": {
  11721. core.ResourceQuota{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: invalidTerminatingScopePairsSpec},
  11722. "conflicting scopes",
  11723. },
  11724. "invalid-quota-besteffort-pair": {
  11725. core.ResourceQuota{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: invalidBestEffortScopePairsSpec},
  11726. "conflicting scopes",
  11727. },
  11728. "invalid-quota-scope-name": {
  11729. core.ResourceQuota{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: invalidScopeNameSpec},
  11730. "unsupported scope",
  11731. },
  11732. }
  11733. for k, v := range errorCases {
  11734. errs := ValidateResourceQuota(&v.R)
  11735. if len(errs) == 0 {
  11736. t.Errorf("expected failure for %s", k)
  11737. }
  11738. for i := range errs {
  11739. if !strings.Contains(errs[i].Detail, v.D) {
  11740. t.Errorf("[%s]: expected error detail either empty or %s, got %s", k, v.D, errs[i].Detail)
  11741. }
  11742. }
  11743. }
  11744. }
  11745. func TestValidateNamespace(t *testing.T) {
  11746. validLabels := map[string]string{"a": "b"}
  11747. invalidLabels := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
  11748. successCases := []core.Namespace{
  11749. {
  11750. ObjectMeta: metav1.ObjectMeta{Name: "abc", Labels: validLabels},
  11751. },
  11752. {
  11753. ObjectMeta: metav1.ObjectMeta{Name: "abc-123"},
  11754. Spec: core.NamespaceSpec{
  11755. Finalizers: []core.FinalizerName{"example.com/something", "example.com/other"},
  11756. },
  11757. },
  11758. }
  11759. for _, successCase := range successCases {
  11760. if errs := ValidateNamespace(&successCase); len(errs) != 0 {
  11761. t.Errorf("expected success: %v", errs)
  11762. }
  11763. }
  11764. errorCases := map[string]struct {
  11765. R core.Namespace
  11766. D string
  11767. }{
  11768. "zero-length name": {
  11769. core.Namespace{ObjectMeta: metav1.ObjectMeta{Name: ""}},
  11770. "",
  11771. },
  11772. "defined-namespace": {
  11773. core.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: "makesnosense"}},
  11774. "",
  11775. },
  11776. "invalid-labels": {
  11777. core.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "abc", Labels: invalidLabels}},
  11778. "",
  11779. },
  11780. }
  11781. for k, v := range errorCases {
  11782. errs := ValidateNamespace(&v.R)
  11783. if len(errs) == 0 {
  11784. t.Errorf("expected failure for %s", k)
  11785. }
  11786. }
  11787. }
  11788. func TestValidateNamespaceFinalizeUpdate(t *testing.T) {
  11789. tests := []struct {
  11790. oldNamespace core.Namespace
  11791. namespace core.Namespace
  11792. valid bool
  11793. }{
  11794. {core.Namespace{}, core.Namespace{}, true},
  11795. {core.Namespace{
  11796. ObjectMeta: metav1.ObjectMeta{
  11797. Name: "foo"}},
  11798. core.Namespace{
  11799. ObjectMeta: metav1.ObjectMeta{
  11800. Name: "foo"},
  11801. Spec: core.NamespaceSpec{
  11802. Finalizers: []core.FinalizerName{"Foo"},
  11803. },
  11804. }, false},
  11805. {core.Namespace{
  11806. ObjectMeta: metav1.ObjectMeta{
  11807. Name: "foo"},
  11808. Spec: core.NamespaceSpec{
  11809. Finalizers: []core.FinalizerName{"foo.com/bar"},
  11810. },
  11811. },
  11812. core.Namespace{
  11813. ObjectMeta: metav1.ObjectMeta{
  11814. Name: "foo"},
  11815. Spec: core.NamespaceSpec{
  11816. Finalizers: []core.FinalizerName{"foo.com/bar", "what.com/bar"},
  11817. },
  11818. }, true},
  11819. {core.Namespace{
  11820. ObjectMeta: metav1.ObjectMeta{
  11821. Name: "fooemptyfinalizer"},
  11822. Spec: core.NamespaceSpec{
  11823. Finalizers: []core.FinalizerName{"foo.com/bar"},
  11824. },
  11825. },
  11826. core.Namespace{
  11827. ObjectMeta: metav1.ObjectMeta{
  11828. Name: "fooemptyfinalizer"},
  11829. Spec: core.NamespaceSpec{
  11830. Finalizers: []core.FinalizerName{"", "foo.com/bar", "what.com/bar"},
  11831. },
  11832. }, false},
  11833. }
  11834. for i, test := range tests {
  11835. test.namespace.ObjectMeta.ResourceVersion = "1"
  11836. test.oldNamespace.ObjectMeta.ResourceVersion = "1"
  11837. errs := ValidateNamespaceFinalizeUpdate(&test.namespace, &test.oldNamespace)
  11838. if test.valid && len(errs) > 0 {
  11839. t.Errorf("%d: Unexpected error: %v", i, errs)
  11840. t.Logf("%#v vs %#v", test.oldNamespace, test.namespace)
  11841. }
  11842. if !test.valid && len(errs) == 0 {
  11843. t.Errorf("%d: Unexpected non-error", i)
  11844. }
  11845. }
  11846. }
  11847. func TestValidateNamespaceStatusUpdate(t *testing.T) {
  11848. now := metav1.Now()
  11849. tests := []struct {
  11850. oldNamespace core.Namespace
  11851. namespace core.Namespace
  11852. valid bool
  11853. }{
  11854. {core.Namespace{}, core.Namespace{
  11855. Status: core.NamespaceStatus{
  11856. Phase: core.NamespaceActive,
  11857. },
  11858. }, true},
  11859. // Cannot set deletionTimestamp via status update
  11860. {core.Namespace{
  11861. ObjectMeta: metav1.ObjectMeta{
  11862. Name: "foo"}},
  11863. core.Namespace{
  11864. ObjectMeta: metav1.ObjectMeta{
  11865. Name: "foo",
  11866. DeletionTimestamp: &now},
  11867. Status: core.NamespaceStatus{
  11868. Phase: core.NamespaceTerminating,
  11869. },
  11870. }, false},
  11871. // Can update phase via status update
  11872. {core.Namespace{
  11873. ObjectMeta: metav1.ObjectMeta{
  11874. Name: "foo",
  11875. DeletionTimestamp: &now}},
  11876. core.Namespace{
  11877. ObjectMeta: metav1.ObjectMeta{
  11878. Name: "foo",
  11879. DeletionTimestamp: &now},
  11880. Status: core.NamespaceStatus{
  11881. Phase: core.NamespaceTerminating,
  11882. },
  11883. }, true},
  11884. {core.Namespace{
  11885. ObjectMeta: metav1.ObjectMeta{
  11886. Name: "foo"}},
  11887. core.Namespace{
  11888. ObjectMeta: metav1.ObjectMeta{
  11889. Name: "foo"},
  11890. Status: core.NamespaceStatus{
  11891. Phase: core.NamespaceTerminating,
  11892. },
  11893. }, false},
  11894. {core.Namespace{
  11895. ObjectMeta: metav1.ObjectMeta{
  11896. Name: "foo"}},
  11897. core.Namespace{
  11898. ObjectMeta: metav1.ObjectMeta{
  11899. Name: "bar"},
  11900. Status: core.NamespaceStatus{
  11901. Phase: core.NamespaceTerminating,
  11902. },
  11903. }, false},
  11904. }
  11905. for i, test := range tests {
  11906. test.namespace.ObjectMeta.ResourceVersion = "1"
  11907. test.oldNamespace.ObjectMeta.ResourceVersion = "1"
  11908. errs := ValidateNamespaceStatusUpdate(&test.namespace, &test.oldNamespace)
  11909. if test.valid && len(errs) > 0 {
  11910. t.Errorf("%d: Unexpected error: %v", i, errs)
  11911. t.Logf("%#v vs %#v", test.oldNamespace.ObjectMeta, test.namespace.ObjectMeta)
  11912. }
  11913. if !test.valid && len(errs) == 0 {
  11914. t.Errorf("%d: Unexpected non-error", i)
  11915. }
  11916. }
  11917. }
  11918. func TestValidateNamespaceUpdate(t *testing.T) {
  11919. tests := []struct {
  11920. oldNamespace core.Namespace
  11921. namespace core.Namespace
  11922. valid bool
  11923. }{
  11924. {core.Namespace{}, core.Namespace{}, true},
  11925. {core.Namespace{
  11926. ObjectMeta: metav1.ObjectMeta{
  11927. Name: "foo1"}},
  11928. core.Namespace{
  11929. ObjectMeta: metav1.ObjectMeta{
  11930. Name: "bar1"},
  11931. }, false},
  11932. {core.Namespace{
  11933. ObjectMeta: metav1.ObjectMeta{
  11934. Name: "foo2",
  11935. Labels: map[string]string{"foo": "bar"},
  11936. },
  11937. }, core.Namespace{
  11938. ObjectMeta: metav1.ObjectMeta{
  11939. Name: "foo2",
  11940. Labels: map[string]string{"foo": "baz"},
  11941. },
  11942. }, true},
  11943. {core.Namespace{
  11944. ObjectMeta: metav1.ObjectMeta{
  11945. Name: "foo3",
  11946. },
  11947. }, core.Namespace{
  11948. ObjectMeta: metav1.ObjectMeta{
  11949. Name: "foo3",
  11950. Labels: map[string]string{"foo": "baz"},
  11951. },
  11952. }, true},
  11953. {core.Namespace{
  11954. ObjectMeta: metav1.ObjectMeta{
  11955. Name: "foo4",
  11956. Labels: map[string]string{"bar": "foo"},
  11957. },
  11958. }, core.Namespace{
  11959. ObjectMeta: metav1.ObjectMeta{
  11960. Name: "foo4",
  11961. Labels: map[string]string{"foo": "baz"},
  11962. },
  11963. }, true},
  11964. {core.Namespace{
  11965. ObjectMeta: metav1.ObjectMeta{
  11966. Name: "foo5",
  11967. Labels: map[string]string{"foo": "baz"},
  11968. },
  11969. }, core.Namespace{
  11970. ObjectMeta: metav1.ObjectMeta{
  11971. Name: "foo5",
  11972. Labels: map[string]string{"Foo": "baz"},
  11973. },
  11974. }, true},
  11975. {core.Namespace{
  11976. ObjectMeta: metav1.ObjectMeta{
  11977. Name: "foo6",
  11978. Labels: map[string]string{"foo": "baz"},
  11979. },
  11980. }, core.Namespace{
  11981. ObjectMeta: metav1.ObjectMeta{
  11982. Name: "foo6",
  11983. Labels: map[string]string{"Foo": "baz"},
  11984. },
  11985. Spec: core.NamespaceSpec{
  11986. Finalizers: []core.FinalizerName{"kubernetes"},
  11987. },
  11988. Status: core.NamespaceStatus{
  11989. Phase: core.NamespaceTerminating,
  11990. },
  11991. }, true},
  11992. }
  11993. for i, test := range tests {
  11994. test.namespace.ObjectMeta.ResourceVersion = "1"
  11995. test.oldNamespace.ObjectMeta.ResourceVersion = "1"
  11996. errs := ValidateNamespaceUpdate(&test.namespace, &test.oldNamespace)
  11997. if test.valid && len(errs) > 0 {
  11998. t.Errorf("%d: Unexpected error: %v", i, errs)
  11999. t.Logf("%#v vs %#v", test.oldNamespace.ObjectMeta, test.namespace.ObjectMeta)
  12000. }
  12001. if !test.valid && len(errs) == 0 {
  12002. t.Errorf("%d: Unexpected non-error", i)
  12003. }
  12004. }
  12005. }
  12006. func TestValidateSecret(t *testing.T) {
  12007. // Opaque secret validation
  12008. validSecret := func() core.Secret {
  12009. return core.Secret{
  12010. ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"},
  12011. Data: map[string][]byte{
  12012. "data-1": []byte("bar"),
  12013. },
  12014. }
  12015. }
  12016. var (
  12017. emptyName = validSecret()
  12018. invalidName = validSecret()
  12019. emptyNs = validSecret()
  12020. invalidNs = validSecret()
  12021. overMaxSize = validSecret()
  12022. invalidKey = validSecret()
  12023. leadingDotKey = validSecret()
  12024. dotKey = validSecret()
  12025. doubleDotKey = validSecret()
  12026. )
  12027. emptyName.Name = ""
  12028. invalidName.Name = "NoUppercaseOrSpecialCharsLike=Equals"
  12029. emptyNs.Namespace = ""
  12030. invalidNs.Namespace = "NoUppercaseOrSpecialCharsLike=Equals"
  12031. overMaxSize.Data = map[string][]byte{
  12032. "over": make([]byte, core.MaxSecretSize+1),
  12033. }
  12034. invalidKey.Data["a*b"] = []byte("whoops")
  12035. leadingDotKey.Data[".key"] = []byte("bar")
  12036. dotKey.Data["."] = []byte("bar")
  12037. doubleDotKey.Data[".."] = []byte("bar")
  12038. // kubernetes.io/service-account-token secret validation
  12039. validServiceAccountTokenSecret := func() core.Secret {
  12040. return core.Secret{
  12041. ObjectMeta: metav1.ObjectMeta{
  12042. Name: "foo",
  12043. Namespace: "bar",
  12044. Annotations: map[string]string{
  12045. core.ServiceAccountNameKey: "foo",
  12046. },
  12047. },
  12048. Type: core.SecretTypeServiceAccountToken,
  12049. Data: map[string][]byte{
  12050. "data-1": []byte("bar"),
  12051. },
  12052. }
  12053. }
  12054. var (
  12055. emptyTokenAnnotation = validServiceAccountTokenSecret()
  12056. missingTokenAnnotation = validServiceAccountTokenSecret()
  12057. missingTokenAnnotations = validServiceAccountTokenSecret()
  12058. )
  12059. emptyTokenAnnotation.Annotations[core.ServiceAccountNameKey] = ""
  12060. delete(missingTokenAnnotation.Annotations, core.ServiceAccountNameKey)
  12061. missingTokenAnnotations.Annotations = nil
  12062. tests := map[string]struct {
  12063. secret core.Secret
  12064. valid bool
  12065. }{
  12066. "valid": {validSecret(), true},
  12067. "empty name": {emptyName, false},
  12068. "invalid name": {invalidName, false},
  12069. "empty namespace": {emptyNs, false},
  12070. "invalid namespace": {invalidNs, false},
  12071. "over max size": {overMaxSize, false},
  12072. "invalid key": {invalidKey, false},
  12073. "valid service-account-token secret": {validServiceAccountTokenSecret(), true},
  12074. "empty service-account-token annotation": {emptyTokenAnnotation, false},
  12075. "missing service-account-token annotation": {missingTokenAnnotation, false},
  12076. "missing service-account-token annotations": {missingTokenAnnotations, false},
  12077. "leading dot key": {leadingDotKey, true},
  12078. "dot key": {dotKey, false},
  12079. "double dot key": {doubleDotKey, false},
  12080. }
  12081. for name, tc := range tests {
  12082. errs := ValidateSecret(&tc.secret)
  12083. if tc.valid && len(errs) > 0 {
  12084. t.Errorf("%v: Unexpected error: %v", name, errs)
  12085. }
  12086. if !tc.valid && len(errs) == 0 {
  12087. t.Errorf("%v: Unexpected non-error", name)
  12088. }
  12089. }
  12090. }
  12091. func TestValidateDockerConfigSecret(t *testing.T) {
  12092. validDockerSecret := func() core.Secret {
  12093. return core.Secret{
  12094. ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"},
  12095. Type: core.SecretTypeDockercfg,
  12096. Data: map[string][]byte{
  12097. core.DockerConfigKey: []byte(`{"https://index.docker.io/v1/": {"auth": "Y2x1ZWRyb29sZXIwMDAxOnBhc3N3b3Jk","email": "fake@example.com"}}`),
  12098. },
  12099. }
  12100. }
  12101. validDockerSecret2 := func() core.Secret {
  12102. return core.Secret{
  12103. ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"},
  12104. Type: core.SecretTypeDockerConfigJson,
  12105. Data: map[string][]byte{
  12106. core.DockerConfigJsonKey: []byte(`{"auths":{"https://index.docker.io/v1/": {"auth": "Y2x1ZWRyb29sZXIwMDAxOnBhc3N3b3Jk","email": "fake@example.com"}}}`),
  12107. },
  12108. }
  12109. }
  12110. var (
  12111. missingDockerConfigKey = validDockerSecret()
  12112. emptyDockerConfigKey = validDockerSecret()
  12113. invalidDockerConfigKey = validDockerSecret()
  12114. missingDockerConfigKey2 = validDockerSecret2()
  12115. emptyDockerConfigKey2 = validDockerSecret2()
  12116. invalidDockerConfigKey2 = validDockerSecret2()
  12117. )
  12118. delete(missingDockerConfigKey.Data, core.DockerConfigKey)
  12119. emptyDockerConfigKey.Data[core.DockerConfigKey] = []byte("")
  12120. invalidDockerConfigKey.Data[core.DockerConfigKey] = []byte("bad")
  12121. delete(missingDockerConfigKey2.Data, core.DockerConfigJsonKey)
  12122. emptyDockerConfigKey2.Data[core.DockerConfigJsonKey] = []byte("")
  12123. invalidDockerConfigKey2.Data[core.DockerConfigJsonKey] = []byte("bad")
  12124. tests := map[string]struct {
  12125. secret core.Secret
  12126. valid bool
  12127. }{
  12128. "valid dockercfg": {validDockerSecret(), true},
  12129. "missing dockercfg": {missingDockerConfigKey, false},
  12130. "empty dockercfg": {emptyDockerConfigKey, false},
  12131. "invalid dockercfg": {invalidDockerConfigKey, false},
  12132. "valid config.json": {validDockerSecret2(), true},
  12133. "missing config.json": {missingDockerConfigKey2, false},
  12134. "empty config.json": {emptyDockerConfigKey2, false},
  12135. "invalid config.json": {invalidDockerConfigKey2, false},
  12136. }
  12137. for name, tc := range tests {
  12138. errs := ValidateSecret(&tc.secret)
  12139. if tc.valid && len(errs) > 0 {
  12140. t.Errorf("%v: Unexpected error: %v", name, errs)
  12141. }
  12142. if !tc.valid && len(errs) == 0 {
  12143. t.Errorf("%v: Unexpected non-error", name)
  12144. }
  12145. }
  12146. }
  12147. func TestValidateBasicAuthSecret(t *testing.T) {
  12148. validBasicAuthSecret := func() core.Secret {
  12149. return core.Secret{
  12150. ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"},
  12151. Type: core.SecretTypeBasicAuth,
  12152. Data: map[string][]byte{
  12153. core.BasicAuthUsernameKey: []byte("username"),
  12154. core.BasicAuthPasswordKey: []byte("password"),
  12155. },
  12156. }
  12157. }
  12158. var (
  12159. missingBasicAuthUsernamePasswordKeys = validBasicAuthSecret()
  12160. )
  12161. delete(missingBasicAuthUsernamePasswordKeys.Data, core.BasicAuthUsernameKey)
  12162. delete(missingBasicAuthUsernamePasswordKeys.Data, core.BasicAuthPasswordKey)
  12163. tests := map[string]struct {
  12164. secret core.Secret
  12165. valid bool
  12166. }{
  12167. "valid": {validBasicAuthSecret(), true},
  12168. "missing username and password": {missingBasicAuthUsernamePasswordKeys, false},
  12169. }
  12170. for name, tc := range tests {
  12171. errs := ValidateSecret(&tc.secret)
  12172. if tc.valid && len(errs) > 0 {
  12173. t.Errorf("%v: Unexpected error: %v", name, errs)
  12174. }
  12175. if !tc.valid && len(errs) == 0 {
  12176. t.Errorf("%v: Unexpected non-error", name)
  12177. }
  12178. }
  12179. }
  12180. func TestValidateSSHAuthSecret(t *testing.T) {
  12181. validSSHAuthSecret := func() core.Secret {
  12182. return core.Secret{
  12183. ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"},
  12184. Type: core.SecretTypeSSHAuth,
  12185. Data: map[string][]byte{
  12186. core.SSHAuthPrivateKey: []byte("foo-bar-baz"),
  12187. },
  12188. }
  12189. }
  12190. missingSSHAuthPrivateKey := validSSHAuthSecret()
  12191. delete(missingSSHAuthPrivateKey.Data, core.SSHAuthPrivateKey)
  12192. tests := map[string]struct {
  12193. secret core.Secret
  12194. valid bool
  12195. }{
  12196. "valid": {validSSHAuthSecret(), true},
  12197. "missing private key": {missingSSHAuthPrivateKey, false},
  12198. }
  12199. for name, tc := range tests {
  12200. errs := ValidateSecret(&tc.secret)
  12201. if tc.valid && len(errs) > 0 {
  12202. t.Errorf("%v: Unexpected error: %v", name, errs)
  12203. }
  12204. if !tc.valid && len(errs) == 0 {
  12205. t.Errorf("%v: Unexpected non-error", name)
  12206. }
  12207. }
  12208. }
  12209. func TestValidateEndpoints(t *testing.T) {
  12210. successCases := map[string]core.Endpoints{
  12211. "simple endpoint": {
  12212. ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
  12213. Subsets: []core.EndpointSubset{
  12214. {
  12215. Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}, {IP: "10.10.2.2"}},
  12216. Ports: []core.EndpointPort{{Name: "a", Port: 8675, Protocol: "TCP"}, {Name: "b", Port: 309, Protocol: "TCP"}},
  12217. },
  12218. {
  12219. Addresses: []core.EndpointAddress{{IP: "10.10.3.3"}},
  12220. Ports: []core.EndpointPort{{Name: "a", Port: 93, Protocol: "TCP"}, {Name: "b", Port: 76, Protocol: "TCP"}},
  12221. },
  12222. },
  12223. },
  12224. "empty subsets": {
  12225. ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
  12226. },
  12227. "no name required for singleton port": {
  12228. ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
  12229. Subsets: []core.EndpointSubset{
  12230. {
  12231. Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}},
  12232. Ports: []core.EndpointPort{{Port: 8675, Protocol: "TCP"}},
  12233. },
  12234. },
  12235. },
  12236. "empty ports": {
  12237. ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
  12238. Subsets: []core.EndpointSubset{
  12239. {
  12240. Addresses: []core.EndpointAddress{{IP: "10.10.3.3"}},
  12241. },
  12242. },
  12243. },
  12244. }
  12245. for k, v := range successCases {
  12246. if errs := ValidateEndpoints(&v); len(errs) != 0 {
  12247. t.Errorf("Expected success for %s, got %v", k, errs)
  12248. }
  12249. }
  12250. errorCases := map[string]struct {
  12251. endpoints core.Endpoints
  12252. errorType field.ErrorType
  12253. errorDetail string
  12254. }{
  12255. "missing namespace": {
  12256. endpoints: core.Endpoints{ObjectMeta: metav1.ObjectMeta{Name: "mysvc"}},
  12257. errorType: "FieldValueRequired",
  12258. },
  12259. "missing name": {
  12260. endpoints: core.Endpoints{ObjectMeta: metav1.ObjectMeta{Namespace: "namespace"}},
  12261. errorType: "FieldValueRequired",
  12262. },
  12263. "invalid namespace": {
  12264. endpoints: core.Endpoints{ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "no@#invalid.;chars\"allowed"}},
  12265. errorType: "FieldValueInvalid",
  12266. errorDetail: dnsLabelErrMsg,
  12267. },
  12268. "invalid name": {
  12269. endpoints: core.Endpoints{ObjectMeta: metav1.ObjectMeta{Name: "-_Invliad^&Characters", Namespace: "namespace"}},
  12270. errorType: "FieldValueInvalid",
  12271. errorDetail: dnsSubdomainLabelErrMsg,
  12272. },
  12273. "empty addresses": {
  12274. endpoints: core.Endpoints{
  12275. ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
  12276. Subsets: []core.EndpointSubset{
  12277. {
  12278. Ports: []core.EndpointPort{{Name: "a", Port: 93, Protocol: "TCP"}},
  12279. },
  12280. },
  12281. },
  12282. errorType: "FieldValueRequired",
  12283. },
  12284. "invalid IP": {
  12285. endpoints: core.Endpoints{
  12286. ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
  12287. Subsets: []core.EndpointSubset{
  12288. {
  12289. Addresses: []core.EndpointAddress{{IP: "[2001:0db8:85a3:0042:1000:8a2e:0370:7334]"}},
  12290. Ports: []core.EndpointPort{{Name: "a", Port: 93, Protocol: "TCP"}},
  12291. },
  12292. },
  12293. },
  12294. errorType: "FieldValueInvalid",
  12295. errorDetail: "must be a valid IP address",
  12296. },
  12297. "Multiple ports, one without name": {
  12298. endpoints: core.Endpoints{
  12299. ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
  12300. Subsets: []core.EndpointSubset{
  12301. {
  12302. Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}},
  12303. Ports: []core.EndpointPort{{Port: 8675, Protocol: "TCP"}, {Name: "b", Port: 309, Protocol: "TCP"}},
  12304. },
  12305. },
  12306. },
  12307. errorType: "FieldValueRequired",
  12308. },
  12309. "Invalid port number": {
  12310. endpoints: core.Endpoints{
  12311. ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
  12312. Subsets: []core.EndpointSubset{
  12313. {
  12314. Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}},
  12315. Ports: []core.EndpointPort{{Name: "a", Port: 66000, Protocol: "TCP"}},
  12316. },
  12317. },
  12318. },
  12319. errorType: "FieldValueInvalid",
  12320. errorDetail: "between",
  12321. },
  12322. "Invalid protocol": {
  12323. endpoints: core.Endpoints{
  12324. ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
  12325. Subsets: []core.EndpointSubset{
  12326. {
  12327. Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}},
  12328. Ports: []core.EndpointPort{{Name: "a", Port: 93, Protocol: "Protocol"}},
  12329. },
  12330. },
  12331. },
  12332. errorType: "FieldValueNotSupported",
  12333. },
  12334. "Address missing IP": {
  12335. endpoints: core.Endpoints{
  12336. ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
  12337. Subsets: []core.EndpointSubset{
  12338. {
  12339. Addresses: []core.EndpointAddress{{}},
  12340. Ports: []core.EndpointPort{{Name: "a", Port: 93, Protocol: "TCP"}},
  12341. },
  12342. },
  12343. },
  12344. errorType: "FieldValueInvalid",
  12345. errorDetail: "must be a valid IP address",
  12346. },
  12347. "Port missing number": {
  12348. endpoints: core.Endpoints{
  12349. ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
  12350. Subsets: []core.EndpointSubset{
  12351. {
  12352. Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}},
  12353. Ports: []core.EndpointPort{{Name: "a", Protocol: "TCP"}},
  12354. },
  12355. },
  12356. },
  12357. errorType: "FieldValueInvalid",
  12358. errorDetail: "between",
  12359. },
  12360. "Port missing protocol": {
  12361. endpoints: core.Endpoints{
  12362. ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
  12363. Subsets: []core.EndpointSubset{
  12364. {
  12365. Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}},
  12366. Ports: []core.EndpointPort{{Name: "a", Port: 93}},
  12367. },
  12368. },
  12369. },
  12370. errorType: "FieldValueRequired",
  12371. },
  12372. "Address is loopback": {
  12373. endpoints: core.Endpoints{
  12374. ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
  12375. Subsets: []core.EndpointSubset{
  12376. {
  12377. Addresses: []core.EndpointAddress{{IP: "127.0.0.1"}},
  12378. Ports: []core.EndpointPort{{Name: "p", Port: 93, Protocol: "TCP"}},
  12379. },
  12380. },
  12381. },
  12382. errorType: "FieldValueInvalid",
  12383. errorDetail: "loopback",
  12384. },
  12385. "Address is link-local": {
  12386. endpoints: core.Endpoints{
  12387. ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
  12388. Subsets: []core.EndpointSubset{
  12389. {
  12390. Addresses: []core.EndpointAddress{{IP: "169.254.169.254"}},
  12391. Ports: []core.EndpointPort{{Name: "p", Port: 93, Protocol: "TCP"}},
  12392. },
  12393. },
  12394. },
  12395. errorType: "FieldValueInvalid",
  12396. errorDetail: "link-local",
  12397. },
  12398. "Address is link-local multicast": {
  12399. endpoints: core.Endpoints{
  12400. ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
  12401. Subsets: []core.EndpointSubset{
  12402. {
  12403. Addresses: []core.EndpointAddress{{IP: "224.0.0.1"}},
  12404. Ports: []core.EndpointPort{{Name: "p", Port: 93, Protocol: "TCP"}},
  12405. },
  12406. },
  12407. },
  12408. errorType: "FieldValueInvalid",
  12409. errorDetail: "link-local multicast",
  12410. },
  12411. }
  12412. for k, v := range errorCases {
  12413. if errs := ValidateEndpoints(&v.endpoints); len(errs) == 0 || errs[0].Type != v.errorType || !strings.Contains(errs[0].Detail, v.errorDetail) {
  12414. t.Errorf("[%s] Expected error type %s with detail %q, got %v", k, v.errorType, v.errorDetail, errs)
  12415. }
  12416. }
  12417. }
  12418. func TestValidateTLSSecret(t *testing.T) {
  12419. successCases := map[string]core.Secret{
  12420. "empty certificate chain": {
  12421. ObjectMeta: metav1.ObjectMeta{Name: "tls-cert", Namespace: "namespace"},
  12422. Data: map[string][]byte{
  12423. core.TLSCertKey: []byte("public key"),
  12424. core.TLSPrivateKeyKey: []byte("private key"),
  12425. },
  12426. },
  12427. }
  12428. for k, v := range successCases {
  12429. if errs := ValidateSecret(&v); len(errs) != 0 {
  12430. t.Errorf("Expected success for %s, got %v", k, errs)
  12431. }
  12432. }
  12433. errorCases := map[string]struct {
  12434. secrets core.Secret
  12435. errorType field.ErrorType
  12436. errorDetail string
  12437. }{
  12438. "missing public key": {
  12439. secrets: core.Secret{
  12440. ObjectMeta: metav1.ObjectMeta{Name: "tls-cert"},
  12441. Data: map[string][]byte{
  12442. core.TLSCertKey: []byte("public key"),
  12443. },
  12444. },
  12445. errorType: "FieldValueRequired",
  12446. },
  12447. "missing private key": {
  12448. secrets: core.Secret{
  12449. ObjectMeta: metav1.ObjectMeta{Name: "tls-cert"},
  12450. Data: map[string][]byte{
  12451. core.TLSCertKey: []byte("public key"),
  12452. },
  12453. },
  12454. errorType: "FieldValueRequired",
  12455. },
  12456. }
  12457. for k, v := range errorCases {
  12458. if errs := ValidateSecret(&v.secrets); len(errs) == 0 || errs[0].Type != v.errorType || !strings.Contains(errs[0].Detail, v.errorDetail) {
  12459. t.Errorf("[%s] Expected error type %s with detail %q, got %v", k, v.errorType, v.errorDetail, errs)
  12460. }
  12461. }
  12462. }
  12463. func TestValidateSecurityContext(t *testing.T) {
  12464. runAsUser := int64(1)
  12465. fullValidSC := func() *core.SecurityContext {
  12466. return &core.SecurityContext{
  12467. Privileged: utilpointer.BoolPtr(false),
  12468. Capabilities: &core.Capabilities{
  12469. Add: []core.Capability{"foo"},
  12470. Drop: []core.Capability{"bar"},
  12471. },
  12472. SELinuxOptions: &core.SELinuxOptions{
  12473. User: "user",
  12474. Role: "role",
  12475. Type: "type",
  12476. Level: "level",
  12477. },
  12478. RunAsUser: &runAsUser,
  12479. }
  12480. }
  12481. //setup data
  12482. allSettings := fullValidSC()
  12483. noCaps := fullValidSC()
  12484. noCaps.Capabilities = nil
  12485. noSELinux := fullValidSC()
  12486. noSELinux.SELinuxOptions = nil
  12487. noPrivRequest := fullValidSC()
  12488. noPrivRequest.Privileged = nil
  12489. noRunAsUser := fullValidSC()
  12490. noRunAsUser.RunAsUser = nil
  12491. successCases := map[string]struct {
  12492. sc *core.SecurityContext
  12493. }{
  12494. "all settings": {allSettings},
  12495. "no capabilities": {noCaps},
  12496. "no selinux": {noSELinux},
  12497. "no priv request": {noPrivRequest},
  12498. "no run as user": {noRunAsUser},
  12499. }
  12500. for k, v := range successCases {
  12501. if errs := ValidateSecurityContext(v.sc, field.NewPath("field")); len(errs) != 0 {
  12502. t.Errorf("[%s] Expected success, got %v", k, errs)
  12503. }
  12504. }
  12505. privRequestWithGlobalDeny := fullValidSC()
  12506. privRequestWithGlobalDeny.Privileged = utilpointer.BoolPtr(true)
  12507. negativeRunAsUser := fullValidSC()
  12508. negativeUser := int64(-1)
  12509. negativeRunAsUser.RunAsUser = &negativeUser
  12510. privWithoutEscalation := fullValidSC()
  12511. privWithoutEscalation.Privileged = utilpointer.BoolPtr(true)
  12512. privWithoutEscalation.AllowPrivilegeEscalation = utilpointer.BoolPtr(false)
  12513. capSysAdminWithoutEscalation := fullValidSC()
  12514. capSysAdminWithoutEscalation.Capabilities.Add = []core.Capability{"CAP_SYS_ADMIN"}
  12515. capSysAdminWithoutEscalation.AllowPrivilegeEscalation = utilpointer.BoolPtr(false)
  12516. errorCases := map[string]struct {
  12517. sc *core.SecurityContext
  12518. errorType field.ErrorType
  12519. errorDetail string
  12520. capAllowPriv bool
  12521. }{
  12522. "request privileged when capabilities forbids": {
  12523. sc: privRequestWithGlobalDeny,
  12524. errorType: "FieldValueForbidden",
  12525. errorDetail: "disallowed by cluster policy",
  12526. },
  12527. "negative RunAsUser": {
  12528. sc: negativeRunAsUser,
  12529. errorType: "FieldValueInvalid",
  12530. errorDetail: "must be between",
  12531. },
  12532. "with CAP_SYS_ADMIN and allowPrivilegeEscalation false": {
  12533. sc: capSysAdminWithoutEscalation,
  12534. errorType: "FieldValueInvalid",
  12535. errorDetail: "cannot set `allowPrivilegeEscalation` to false and `capabilities.Add` CAP_SYS_ADMIN",
  12536. },
  12537. "with privileged and allowPrivilegeEscalation false": {
  12538. sc: privWithoutEscalation,
  12539. errorType: "FieldValueInvalid",
  12540. errorDetail: "cannot set `allowPrivilegeEscalation` to false and `privileged` to true",
  12541. capAllowPriv: true,
  12542. },
  12543. }
  12544. for k, v := range errorCases {
  12545. capabilities.SetForTests(capabilities.Capabilities{
  12546. AllowPrivileged: v.capAllowPriv,
  12547. })
  12548. if errs := ValidateSecurityContext(v.sc, field.NewPath("field")); len(errs) == 0 || errs[0].Type != v.errorType || !strings.Contains(errs[0].Detail, v.errorDetail) {
  12549. t.Errorf("[%s] Expected error type %q with detail %q, got %v", k, v.errorType, v.errorDetail, errs)
  12550. }
  12551. }
  12552. }
  12553. func fakeValidSecurityContext(priv bool) *core.SecurityContext {
  12554. return &core.SecurityContext{
  12555. Privileged: &priv,
  12556. }
  12557. }
  12558. func TestValidPodLogOptions(t *testing.T) {
  12559. now := metav1.Now()
  12560. negative := int64(-1)
  12561. zero := int64(0)
  12562. positive := int64(1)
  12563. tests := []struct {
  12564. opt core.PodLogOptions
  12565. errs int
  12566. }{
  12567. {core.PodLogOptions{}, 0},
  12568. {core.PodLogOptions{Previous: true}, 0},
  12569. {core.PodLogOptions{Follow: true}, 0},
  12570. {core.PodLogOptions{TailLines: &zero}, 0},
  12571. {core.PodLogOptions{TailLines: &negative}, 1},
  12572. {core.PodLogOptions{TailLines: &positive}, 0},
  12573. {core.PodLogOptions{LimitBytes: &zero}, 1},
  12574. {core.PodLogOptions{LimitBytes: &negative}, 1},
  12575. {core.PodLogOptions{LimitBytes: &positive}, 0},
  12576. {core.PodLogOptions{SinceSeconds: &negative}, 1},
  12577. {core.PodLogOptions{SinceSeconds: &positive}, 0},
  12578. {core.PodLogOptions{SinceSeconds: &zero}, 1},
  12579. {core.PodLogOptions{SinceTime: &now}, 0},
  12580. }
  12581. for i, test := range tests {
  12582. errs := ValidatePodLogOptions(&test.opt)
  12583. if test.errs != len(errs) {
  12584. t.Errorf("%d: Unexpected errors: %v", i, errs)
  12585. }
  12586. }
  12587. }
  12588. func TestValidateConfigMap(t *testing.T) {
  12589. newConfigMap := func(name, namespace string, data map[string]string, binaryData map[string][]byte) core.ConfigMap {
  12590. return core.ConfigMap{
  12591. ObjectMeta: metav1.ObjectMeta{
  12592. Name: name,
  12593. Namespace: namespace,
  12594. },
  12595. Data: data,
  12596. BinaryData: binaryData,
  12597. }
  12598. }
  12599. var (
  12600. validConfigMap = newConfigMap("validname", "validns", map[string]string{"key": "value"}, map[string][]byte{"bin": []byte("value")})
  12601. maxKeyLength = newConfigMap("validname", "validns", map[string]string{strings.Repeat("a", 253): "value"}, nil)
  12602. emptyName = newConfigMap("", "validns", nil, nil)
  12603. invalidName = newConfigMap("NoUppercaseOrSpecialCharsLike=Equals", "validns", nil, nil)
  12604. emptyNs = newConfigMap("validname", "", nil, nil)
  12605. invalidNs = newConfigMap("validname", "NoUppercaseOrSpecialCharsLike=Equals", nil, nil)
  12606. invalidKey = newConfigMap("validname", "validns", map[string]string{"a*b": "value"}, nil)
  12607. leadingDotKey = newConfigMap("validname", "validns", map[string]string{".ab": "value"}, nil)
  12608. dotKey = newConfigMap("validname", "validns", map[string]string{".": "value"}, nil)
  12609. doubleDotKey = newConfigMap("validname", "validns", map[string]string{"..": "value"}, nil)
  12610. overMaxKeyLength = newConfigMap("validname", "validns", map[string]string{strings.Repeat("a", 254): "value"}, nil)
  12611. overMaxSize = newConfigMap("validname", "validns", map[string]string{"key": strings.Repeat("a", v1.MaxSecretSize+1)}, nil)
  12612. duplicatedKey = newConfigMap("validname", "validns", map[string]string{"key": "value1"}, map[string][]byte{"key": []byte("value2")})
  12613. binDataInvalidKey = newConfigMap("validname", "validns", nil, map[string][]byte{"a*b": []byte("value")})
  12614. binDataLeadingDotKey = newConfigMap("validname", "validns", nil, map[string][]byte{".ab": []byte("value")})
  12615. binDataDotKey = newConfigMap("validname", "validns", nil, map[string][]byte{".": []byte("value")})
  12616. binDataDoubleDotKey = newConfigMap("validname", "validns", nil, map[string][]byte{"..": []byte("value")})
  12617. binDataOverMaxKeyLength = newConfigMap("validname", "validns", nil, map[string][]byte{strings.Repeat("a", 254): []byte("value")})
  12618. binDataOverMaxSize = newConfigMap("validname", "validns", nil, map[string][]byte{"bin": bytes.Repeat([]byte("a"), v1.MaxSecretSize+1)})
  12619. binNonUtf8Value = newConfigMap("validname", "validns", nil, map[string][]byte{"key": {0, 0xFE, 0, 0xFF}})
  12620. )
  12621. tests := map[string]struct {
  12622. cfg core.ConfigMap
  12623. isValid bool
  12624. }{
  12625. "valid": {validConfigMap, true},
  12626. "max key length": {maxKeyLength, true},
  12627. "leading dot key": {leadingDotKey, true},
  12628. "empty name": {emptyName, false},
  12629. "invalid name": {invalidName, false},
  12630. "invalid key": {invalidKey, false},
  12631. "empty namespace": {emptyNs, false},
  12632. "invalid namespace": {invalidNs, false},
  12633. "dot key": {dotKey, false},
  12634. "double dot key": {doubleDotKey, false},
  12635. "over max key length": {overMaxKeyLength, false},
  12636. "over max size": {overMaxSize, false},
  12637. "duplicated key": {duplicatedKey, false},
  12638. "binary data invalid key": {binDataInvalidKey, false},
  12639. "binary data leading dot key": {binDataLeadingDotKey, true},
  12640. "binary data dot key": {binDataDotKey, false},
  12641. "binary data double dot key": {binDataDoubleDotKey, false},
  12642. "binary data over max key length": {binDataOverMaxKeyLength, false},
  12643. "binary data max size": {binDataOverMaxSize, false},
  12644. "binary data non utf-8 bytes": {binNonUtf8Value, true},
  12645. }
  12646. for name, tc := range tests {
  12647. errs := ValidateConfigMap(&tc.cfg)
  12648. if tc.isValid && len(errs) > 0 {
  12649. t.Errorf("%v: unexpected error: %v", name, errs)
  12650. }
  12651. if !tc.isValid && len(errs) == 0 {
  12652. t.Errorf("%v: unexpected non-error", name)
  12653. }
  12654. }
  12655. }
  12656. func TestValidateConfigMapUpdate(t *testing.T) {
  12657. newConfigMap := func(version, name, namespace string, data map[string]string) core.ConfigMap {
  12658. return core.ConfigMap{
  12659. ObjectMeta: metav1.ObjectMeta{
  12660. Name: name,
  12661. Namespace: namespace,
  12662. ResourceVersion: version,
  12663. },
  12664. Data: data,
  12665. }
  12666. }
  12667. var (
  12668. validConfigMap = newConfigMap("1", "validname", "validns", map[string]string{"key": "value"})
  12669. noVersion = newConfigMap("", "validname", "validns", map[string]string{"key": "value"})
  12670. )
  12671. cases := []struct {
  12672. name string
  12673. newCfg core.ConfigMap
  12674. oldCfg core.ConfigMap
  12675. isValid bool
  12676. }{
  12677. {
  12678. name: "valid",
  12679. newCfg: validConfigMap,
  12680. oldCfg: validConfigMap,
  12681. isValid: true,
  12682. },
  12683. {
  12684. name: "invalid",
  12685. newCfg: noVersion,
  12686. oldCfg: validConfigMap,
  12687. isValid: false,
  12688. },
  12689. }
  12690. for _, tc := range cases {
  12691. errs := ValidateConfigMapUpdate(&tc.newCfg, &tc.oldCfg)
  12692. if tc.isValid && len(errs) > 0 {
  12693. t.Errorf("%v: unexpected error: %v", tc.name, errs)
  12694. }
  12695. if !tc.isValid && len(errs) == 0 {
  12696. t.Errorf("%v: unexpected non-error", tc.name)
  12697. }
  12698. }
  12699. }
  12700. func TestValidateHasLabel(t *testing.T) {
  12701. successCase := metav1.ObjectMeta{
  12702. Name: "123",
  12703. Namespace: "ns",
  12704. Labels: map[string]string{
  12705. "other": "blah",
  12706. "foo": "bar",
  12707. },
  12708. }
  12709. if errs := ValidateHasLabel(successCase, field.NewPath("field"), "foo", "bar"); len(errs) != 0 {
  12710. t.Errorf("expected success: %v", errs)
  12711. }
  12712. missingCase := metav1.ObjectMeta{
  12713. Name: "123",
  12714. Namespace: "ns",
  12715. Labels: map[string]string{
  12716. "other": "blah",
  12717. },
  12718. }
  12719. if errs := ValidateHasLabel(missingCase, field.NewPath("field"), "foo", "bar"); len(errs) == 0 {
  12720. t.Errorf("expected failure")
  12721. }
  12722. wrongValueCase := metav1.ObjectMeta{
  12723. Name: "123",
  12724. Namespace: "ns",
  12725. Labels: map[string]string{
  12726. "other": "blah",
  12727. "foo": "notbar",
  12728. },
  12729. }
  12730. if errs := ValidateHasLabel(wrongValueCase, field.NewPath("field"), "foo", "bar"); len(errs) == 0 {
  12731. t.Errorf("expected failure")
  12732. }
  12733. }
  12734. func TestIsValidSysctlName(t *testing.T) {
  12735. valid := []string{
  12736. "a.b.c.d",
  12737. "a",
  12738. "a_b",
  12739. "a-b",
  12740. "abc",
  12741. "abc.def",
  12742. }
  12743. invalid := []string{
  12744. "",
  12745. "*",
  12746. "ä",
  12747. "a_",
  12748. "_",
  12749. "__",
  12750. "_a",
  12751. "_a._b",
  12752. "-",
  12753. ".",
  12754. "a.",
  12755. ".a",
  12756. "a.b.",
  12757. "a*.b",
  12758. "a*b",
  12759. "*a",
  12760. "a.*",
  12761. "*",
  12762. "abc*",
  12763. "a.abc*",
  12764. "a.b.*",
  12765. "Abc",
  12766. func(n int) string {
  12767. x := make([]byte, n)
  12768. for i := range x {
  12769. x[i] = byte('a')
  12770. }
  12771. return string(x)
  12772. }(256),
  12773. }
  12774. for _, s := range valid {
  12775. if !IsValidSysctlName(s) {
  12776. t.Errorf("%q expected to be a valid sysctl name", s)
  12777. }
  12778. }
  12779. for _, s := range invalid {
  12780. if IsValidSysctlName(s) {
  12781. t.Errorf("%q expected to be an invalid sysctl name", s)
  12782. }
  12783. }
  12784. }
  12785. func TestValidateSysctls(t *testing.T) {
  12786. valid := []string{
  12787. "net.foo.bar",
  12788. "kernel.shmmax",
  12789. }
  12790. invalid := []string{
  12791. "i..nvalid",
  12792. "_invalid",
  12793. }
  12794. duplicates := []string{
  12795. "kernel.shmmax",
  12796. "kernel.shmmax",
  12797. }
  12798. sysctls := make([]core.Sysctl, len(valid))
  12799. for i, sysctl := range valid {
  12800. sysctls[i].Name = sysctl
  12801. }
  12802. errs := validateSysctls(sysctls, field.NewPath("foo"))
  12803. if len(errs) != 0 {
  12804. t.Errorf("unexpected validation errors: %v", errs)
  12805. }
  12806. sysctls = make([]core.Sysctl, len(invalid))
  12807. for i, sysctl := range invalid {
  12808. sysctls[i].Name = sysctl
  12809. }
  12810. errs = validateSysctls(sysctls, field.NewPath("foo"))
  12811. if len(errs) != 2 {
  12812. t.Errorf("expected 2 validation errors. Got: %v", errs)
  12813. } else {
  12814. if got, expected := errs[0].Error(), "foo"; !strings.Contains(got, expected) {
  12815. t.Errorf("unexpected errors: expected=%q, got=%q", expected, got)
  12816. }
  12817. if got, expected := errs[1].Error(), "foo"; !strings.Contains(got, expected) {
  12818. t.Errorf("unexpected errors: expected=%q, got=%q", expected, got)
  12819. }
  12820. }
  12821. sysctls = make([]core.Sysctl, len(duplicates))
  12822. for i, sysctl := range duplicates {
  12823. sysctls[i].Name = sysctl
  12824. }
  12825. errs = validateSysctls(sysctls, field.NewPath("foo"))
  12826. if len(errs) != 1 {
  12827. t.Errorf("unexpected validation errors: %v", errs)
  12828. } else if errs[0].Type != field.ErrorTypeDuplicate {
  12829. t.Errorf("expected error type %v, got %v", field.ErrorTypeDuplicate, errs[0].Type)
  12830. }
  12831. }
  12832. func newNodeNameEndpoint(nodeName string) *core.Endpoints {
  12833. ep := &core.Endpoints{
  12834. ObjectMeta: metav1.ObjectMeta{
  12835. Name: "foo",
  12836. Namespace: metav1.NamespaceDefault,
  12837. ResourceVersion: "1",
  12838. },
  12839. Subsets: []core.EndpointSubset{
  12840. {
  12841. NotReadyAddresses: []core.EndpointAddress{},
  12842. Ports: []core.EndpointPort{{Name: "https", Port: 443, Protocol: "TCP"}},
  12843. Addresses: []core.EndpointAddress{
  12844. {
  12845. IP: "8.8.8.8",
  12846. Hostname: "zookeeper1",
  12847. NodeName: &nodeName}}}}}
  12848. return ep
  12849. }
  12850. func TestEndpointAddressNodeNameUpdateRestrictions(t *testing.T) {
  12851. oldEndpoint := newNodeNameEndpoint("kubernetes-node-setup-by-backend")
  12852. updatedEndpoint := newNodeNameEndpoint("kubernetes-changed-nodename")
  12853. // Check that NodeName can be changed during update, this is to accommodate the case where nodeIP or PodCIDR is reused.
  12854. // The same ip will now have a different nodeName.
  12855. errList := ValidateEndpoints(updatedEndpoint)
  12856. errList = append(errList, ValidateEndpointsUpdate(updatedEndpoint, oldEndpoint)...)
  12857. if len(errList) != 0 {
  12858. t.Error("Endpoint should allow changing of Subset.Addresses.NodeName on update")
  12859. }
  12860. }
  12861. func TestEndpointAddressNodeNameInvalidDNSSubdomain(t *testing.T) {
  12862. // Check NodeName DNS validation
  12863. endpoint := newNodeNameEndpoint("illegal*.nodename")
  12864. errList := ValidateEndpoints(endpoint)
  12865. if len(errList) == 0 {
  12866. t.Error("Endpoint should reject invalid NodeName")
  12867. }
  12868. }
  12869. func TestEndpointAddressNodeNameCanBeAnIPAddress(t *testing.T) {
  12870. endpoint := newNodeNameEndpoint("10.10.1.1")
  12871. errList := ValidateEndpoints(endpoint)
  12872. if len(errList) != 0 {
  12873. t.Error("Endpoint should accept a NodeName that is an IP address")
  12874. }
  12875. }
  12876. func TestValidateFlexVolumeSource(t *testing.T) {
  12877. testcases := map[string]struct {
  12878. source *core.FlexVolumeSource
  12879. expectedErrs map[string]string
  12880. }{
  12881. "valid": {
  12882. source: &core.FlexVolumeSource{Driver: "foo"},
  12883. expectedErrs: map[string]string{},
  12884. },
  12885. "valid with options": {
  12886. source: &core.FlexVolumeSource{Driver: "foo", Options: map[string]string{"foo": "bar"}},
  12887. expectedErrs: map[string]string{},
  12888. },
  12889. "no driver": {
  12890. source: &core.FlexVolumeSource{Driver: ""},
  12891. expectedErrs: map[string]string{"driver": "Required value"},
  12892. },
  12893. "reserved option keys": {
  12894. source: &core.FlexVolumeSource{
  12895. Driver: "foo",
  12896. Options: map[string]string{
  12897. // valid options
  12898. "myns.io": "A",
  12899. "myns.io/bar": "A",
  12900. "myns.io/kubernetes.io": "A",
  12901. // invalid options
  12902. "KUBERNETES.IO": "A",
  12903. "kubernetes.io": "A",
  12904. "kubernetes.io/": "A",
  12905. "kubernetes.io/foo": "A",
  12906. "alpha.kubernetes.io": "A",
  12907. "alpha.kubernetes.io/": "A",
  12908. "alpha.kubernetes.io/foo": "A",
  12909. "k8s.io": "A",
  12910. "k8s.io/": "A",
  12911. "k8s.io/foo": "A",
  12912. "alpha.k8s.io": "A",
  12913. "alpha.k8s.io/": "A",
  12914. "alpha.k8s.io/foo": "A",
  12915. },
  12916. },
  12917. expectedErrs: map[string]string{
  12918. "options[KUBERNETES.IO]": "reserved",
  12919. "options[kubernetes.io]": "reserved",
  12920. "options[kubernetes.io/]": "reserved",
  12921. "options[kubernetes.io/foo]": "reserved",
  12922. "options[alpha.kubernetes.io]": "reserved",
  12923. "options[alpha.kubernetes.io/]": "reserved",
  12924. "options[alpha.kubernetes.io/foo]": "reserved",
  12925. "options[k8s.io]": "reserved",
  12926. "options[k8s.io/]": "reserved",
  12927. "options[k8s.io/foo]": "reserved",
  12928. "options[alpha.k8s.io]": "reserved",
  12929. "options[alpha.k8s.io/]": "reserved",
  12930. "options[alpha.k8s.io/foo]": "reserved",
  12931. },
  12932. },
  12933. }
  12934. for k, tc := range testcases {
  12935. errs := validateFlexVolumeSource(tc.source, nil)
  12936. for _, err := range errs {
  12937. expectedErr, ok := tc.expectedErrs[err.Field]
  12938. if !ok {
  12939. t.Errorf("%s: unexpected err on field %s: %v", k, err.Field, err)
  12940. continue
  12941. }
  12942. if !strings.Contains(err.Error(), expectedErr) {
  12943. t.Errorf("%s: expected err on field %s to contain '%s', was %v", k, err.Field, expectedErr, err.Error())
  12944. continue
  12945. }
  12946. }
  12947. if len(errs) != len(tc.expectedErrs) {
  12948. t.Errorf("%s: expected errs %#v, got %#v", k, tc.expectedErrs, errs)
  12949. continue
  12950. }
  12951. }
  12952. }
  12953. func TestValidateOrSetClientIPAffinityConfig(t *testing.T) {
  12954. successCases := map[string]*core.SessionAffinityConfig{
  12955. "non-empty config, valid timeout: 1": {
  12956. ClientIP: &core.ClientIPConfig{
  12957. TimeoutSeconds: utilpointer.Int32Ptr(1),
  12958. },
  12959. },
  12960. "non-empty config, valid timeout: core.MaxClientIPServiceAffinitySeconds-1": {
  12961. ClientIP: &core.ClientIPConfig{
  12962. TimeoutSeconds: utilpointer.Int32Ptr(core.MaxClientIPServiceAffinitySeconds - 1),
  12963. },
  12964. },
  12965. "non-empty config, valid timeout: core.MaxClientIPServiceAffinitySeconds": {
  12966. ClientIP: &core.ClientIPConfig{
  12967. TimeoutSeconds: utilpointer.Int32Ptr(core.MaxClientIPServiceAffinitySeconds),
  12968. },
  12969. },
  12970. }
  12971. for name, test := range successCases {
  12972. if errs := validateClientIPAffinityConfig(test, field.NewPath("field")); len(errs) != 0 {
  12973. t.Errorf("case: %s, expected success: %v", name, errs)
  12974. }
  12975. }
  12976. errorCases := map[string]*core.SessionAffinityConfig{
  12977. "empty session affinity config": nil,
  12978. "empty client IP config": {
  12979. ClientIP: nil,
  12980. },
  12981. "empty timeoutSeconds": {
  12982. ClientIP: &core.ClientIPConfig{
  12983. TimeoutSeconds: nil,
  12984. },
  12985. },
  12986. "non-empty config, invalid timeout: core.MaxClientIPServiceAffinitySeconds+1": {
  12987. ClientIP: &core.ClientIPConfig{
  12988. TimeoutSeconds: utilpointer.Int32Ptr(core.MaxClientIPServiceAffinitySeconds + 1),
  12989. },
  12990. },
  12991. "non-empty config, invalid timeout: -1": {
  12992. ClientIP: &core.ClientIPConfig{
  12993. TimeoutSeconds: utilpointer.Int32Ptr(-1),
  12994. },
  12995. },
  12996. "non-empty config, invalid timeout: 0": {
  12997. ClientIP: &core.ClientIPConfig{
  12998. TimeoutSeconds: utilpointer.Int32Ptr(0),
  12999. },
  13000. },
  13001. }
  13002. for name, test := range errorCases {
  13003. if errs := validateClientIPAffinityConfig(test, field.NewPath("field")); len(errs) == 0 {
  13004. t.Errorf("case: %v, expected failures: %v", name, errs)
  13005. }
  13006. }
  13007. }
  13008. func TestValidateWindowsSecurityContextOptions(t *testing.T) {
  13009. toPtr := func(s string) *string {
  13010. return &s
  13011. }
  13012. testCases := []struct {
  13013. testName string
  13014. windowsOptions *core.WindowsSecurityContextOptions
  13015. expectedErrorSubstring string
  13016. }{
  13017. {
  13018. testName: "a nil pointer",
  13019. },
  13020. {
  13021. testName: "an empty struct",
  13022. windowsOptions: &core.WindowsSecurityContextOptions{},
  13023. },
  13024. {
  13025. testName: "a valid input",
  13026. windowsOptions: &core.WindowsSecurityContextOptions{
  13027. GMSACredentialSpecName: toPtr("dummy-gmsa-crep-spec-name"),
  13028. GMSACredentialSpec: toPtr("dummy-gmsa-crep-spec-contents"),
  13029. },
  13030. },
  13031. {
  13032. testName: "a GMSA cred spec name that is not a valid resource name",
  13033. windowsOptions: &core.WindowsSecurityContextOptions{
  13034. // invalid because of the underscore
  13035. GMSACredentialSpecName: toPtr("not_a-valid-gmsa-crep-spec-name"),
  13036. },
  13037. expectedErrorSubstring: dnsSubdomainLabelErrMsg,
  13038. },
  13039. {
  13040. testName: "empty GMSA cred spec contents",
  13041. windowsOptions: &core.WindowsSecurityContextOptions{
  13042. GMSACredentialSpec: toPtr(""),
  13043. },
  13044. expectedErrorSubstring: "gmsaCredentialSpec cannot be an empty string",
  13045. },
  13046. {
  13047. testName: "GMSA cred spec contents that are too long",
  13048. windowsOptions: &core.WindowsSecurityContextOptions{
  13049. GMSACredentialSpec: toPtr(strings.Repeat("a", maxGMSACredentialSpecLength+1)),
  13050. },
  13051. expectedErrorSubstring: "gmsaCredentialSpec size must be under",
  13052. },
  13053. }
  13054. for _, testCase := range testCases {
  13055. t.Run("validateWindowsSecurityContextOptions with"+testCase.testName, func(t *testing.T) {
  13056. errs := validateWindowsSecurityContextOptions(testCase.windowsOptions, field.NewPath("field"))
  13057. switch len(errs) {
  13058. case 0:
  13059. if testCase.expectedErrorSubstring != "" {
  13060. t.Errorf("expected a failure containing the substring: %q", testCase.expectedErrorSubstring)
  13061. }
  13062. case 1:
  13063. if testCase.expectedErrorSubstring == "" {
  13064. t.Errorf("didn't expect a failure, got: %q", errs[0].Error())
  13065. } else if !strings.Contains(errs[0].Error(), testCase.expectedErrorSubstring) {
  13066. t.Errorf("expected a failure with the substring %q, got %q instead", testCase.expectedErrorSubstring, errs[0].Error())
  13067. }
  13068. default:
  13069. t.Errorf("got %d failures", len(errs))
  13070. for i, err := range errs {
  13071. t.Errorf("error %d: %q", i, err.Error())
  13072. }
  13073. }
  13074. })
  13075. }
  13076. }
  13077. func testDataSourceInSpec(name string, kind string, apiGroup string) *core.PersistentVolumeClaimSpec {
  13078. scName := "csi-plugin"
  13079. dataSourceInSpec := core.PersistentVolumeClaimSpec{
  13080. AccessModes: []core.PersistentVolumeAccessMode{
  13081. core.ReadOnlyMany,
  13082. },
  13083. Resources: core.ResourceRequirements{
  13084. Requests: core.ResourceList{
  13085. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  13086. },
  13087. },
  13088. StorageClassName: &scName,
  13089. DataSource: &core.TypedLocalObjectReference{
  13090. APIGroup: &apiGroup,
  13091. Kind: kind,
  13092. Name: name,
  13093. },
  13094. }
  13095. return &dataSourceInSpec
  13096. }
  13097. func TestAlphaVolumePVCDataSource(t *testing.T) {
  13098. testCases := []struct {
  13099. testName string
  13100. claimSpec core.PersistentVolumeClaimSpec
  13101. expectedFail bool
  13102. }{
  13103. {
  13104. testName: "test create from valid snapshot source",
  13105. claimSpec: *testDataSourceInSpec("test_snapshot", "VolumeSnapshot", "snapshot.storage.k8s.io"),
  13106. },
  13107. {
  13108. testName: "test create from valid pvc source",
  13109. claimSpec: *testDataSourceInSpec("test_pvc", "PersistentVolumeClaim", ""),
  13110. },
  13111. {
  13112. testName: "test missing name in snapshot datasource should fail",
  13113. claimSpec: *testDataSourceInSpec("", "VolumeSnapshot", "snapshot.storage.k8s.io"),
  13114. expectedFail: true,
  13115. },
  13116. {
  13117. testName: "test specifying pvc with snapshot api group should fail",
  13118. claimSpec: *testDataSourceInSpec("test_snapshot", "PersistentVolumeClaim", "snapshot.storage.k8s.io"),
  13119. expectedFail: true,
  13120. },
  13121. {
  13122. testName: "test invalid group name in snapshot datasource should fail",
  13123. claimSpec: *testDataSourceInSpec("test_snapshot", "VolumeSnapshot", "storage.k8s.io"),
  13124. expectedFail: true,
  13125. },
  13126. }
  13127. for _, tc := range testCases {
  13128. if tc.expectedFail {
  13129. if errs := ValidatePersistentVolumeClaimSpec(&tc.claimSpec, field.NewPath("spec")); len(errs) == 0 {
  13130. t.Errorf("expected failure: %v", errs)
  13131. }
  13132. } else {
  13133. if errs := ValidatePersistentVolumeClaimSpec(&tc.claimSpec, field.NewPath("spec")); len(errs) != 0 {
  13134. t.Errorf("expected success: %v", errs)
  13135. }
  13136. }
  13137. }
  13138. }