validation_test.go 439 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389939093919392939393949395939693979398939994009401940294039404940594069407940894099410941194129413941494159416941794189419942094219422942394249425942694279428942994309431943294339434943594369437943894399440944194429443944494459446944794489449945094519452945394549455945694579458945994609461946294639464946594669467946894699470947194729473947494759476947794789479948094819482948394849485948694879488948994909491949294939494949594969497949894999500950195029503950495059506950795089509951095119512951395149515951695179518951995209521952295239524952595269527952895299530953195329533953495359536953795389539954095419542954395449545954695479548954995509551955295539554955595569557955895599560956195629563956495659566956795689569957095719572957395749575957695779578957995809581958295839584958595869587958895899590959195929593959495959596959795989599960096019602960396049605960696079608960996109611961296139614961596169617961896199620962196229623962496259626962796289629963096319632963396349635963696379638963996409641964296439644964596469647964896499650965196529653965496559656965796589659966096619662966396649665966696679668966996709671967296739674967596769677967896799680968196829683968496859686968796889689969096919692969396949695969696979698969997009701970297039704970597069707970897099710971197129713971497159716971797189719972097219722972397249725972697279728972997309731973297339734973597369737973897399740974197429743974497459746974797489749975097519752975397549755975697579758975997609761976297639764976597669767976897699770977197729773977497759776977797789779978097819782978397849785978697879788978997909791979297939794979597969797979897999800980198029803980498059806980798089809981098119812981398149815981698179818981998209821982298239824982598269827982898299830983198329833983498359836983798389839984098419842984398449845984698479848984998509851985298539854985598569857985898599860986198629863986498659866986798689869987098719872987398749875987698779878987998809881988298839884988598869887988898899890989198929893989498959896989798989899990099019902990399049905990699079908990999109911991299139914991599169917991899199920992199229923992499259926992799289929993099319932993399349935993699379938993999409941994299439944994599469947994899499950995199529953995499559956995799589959996099619962996399649965996699679968996999709971997299739974997599769977997899799980998199829983998499859986998799889989999099919992999399949995999699979998999910000100011000210003100041000510006100071000810009100101001110012100131001410015100161001710018100191002010021100221002310024100251002610027100281002910030100311003210033100341003510036100371003810039100401004110042100431004410045100461004710048100491005010051100521005310054100551005610057100581005910060100611006210063100641006510066100671006810069100701007110072100731007410075100761007710078100791008010081100821008310084100851008610087100881008910090100911009210093100941009510096100971009810099101001010110102101031010410105101061010710108101091011010111101121011310114101151011610117101181011910120101211012210123101241012510126101271012810129101301013110132101331013410135101361013710138101391014010141101421014310144101451014610147101481014910150101511015210153101541015510156101571015810159101601016110162101631016410165101661016710168101691017010171101721017310174101751017610177101781017910180101811018210183101841018510186101871018810189101901019110192101931019410195101961019710198101991020010201102021020310204102051020610207102081020910210102111021210213102141021510216102171021810219102201022110222102231022410225102261022710228102291023010231102321023310234102351023610237102381023910240102411024210243102441024510246102471024810249102501025110252102531025410255102561025710258102591026010261102621026310264102651026610267102681026910270102711027210273102741027510276102771027810279102801028110282102831028410285102861028710288102891029010291102921029310294102951029610297102981029910300103011030210303103041030510306103071030810309103101031110312103131031410315103161031710318103191032010321103221032310324103251032610327103281032910330103311033210333103341033510336103371033810339103401034110342103431034410345103461034710348103491035010351103521035310354103551035610357103581035910360103611036210363103641036510366103671036810369103701037110372103731037410375103761037710378103791038010381103821038310384103851038610387103881038910390103911039210393103941039510396103971039810399104001040110402104031040410405104061040710408104091041010411104121041310414104151041610417104181041910420104211042210423104241042510426104271042810429104301043110432104331043410435104361043710438104391044010441104421044310444104451044610447104481044910450104511045210453104541045510456104571045810459104601046110462104631046410465104661046710468104691047010471104721047310474104751047610477104781047910480104811048210483104841048510486104871048810489104901049110492104931049410495104961049710498104991050010501105021050310504105051050610507105081050910510105111051210513105141051510516105171051810519105201052110522105231052410525105261052710528105291053010531105321053310534105351053610537105381053910540105411054210543105441054510546105471054810549105501055110552105531055410555105561055710558105591056010561105621056310564105651056610567105681056910570105711057210573105741057510576105771057810579105801058110582105831058410585105861058710588105891059010591105921059310594105951059610597105981059910600106011060210603106041060510606106071060810609106101061110612106131061410615106161061710618106191062010621106221062310624106251062610627106281062910630106311063210633106341063510636106371063810639106401064110642106431064410645106461064710648106491065010651106521065310654106551065610657106581065910660106611066210663106641066510666106671066810669106701067110672106731067410675106761067710678106791068010681106821068310684106851068610687106881068910690106911069210693106941069510696106971069810699107001070110702107031070410705107061070710708107091071010711107121071310714107151071610717107181071910720107211072210723107241072510726107271072810729107301073110732107331073410735107361073710738107391074010741107421074310744107451074610747107481074910750107511075210753107541075510756107571075810759107601076110762107631076410765107661076710768107691077010771107721077310774107751077610777107781077910780107811078210783107841078510786107871078810789107901079110792107931079410795107961079710798107991080010801108021080310804108051080610807108081080910810108111081210813108141081510816108171081810819108201082110822108231082410825108261082710828108291083010831108321083310834108351083610837108381083910840108411084210843108441084510846108471084810849108501085110852108531085410855108561085710858108591086010861108621086310864108651086610867108681086910870108711087210873108741087510876108771087810879108801088110882108831088410885108861088710888108891089010891108921089310894108951089610897108981089910900109011090210903109041090510906109071090810909109101091110912109131091410915109161091710918109191092010921109221092310924109251092610927109281092910930109311093210933109341093510936109371093810939109401094110942109431094410945109461094710948109491095010951109521095310954109551095610957109581095910960109611096210963109641096510966109671096810969109701097110972109731097410975109761097710978109791098010981109821098310984109851098610987109881098910990109911099210993109941099510996109971099810999110001100111002110031100411005110061100711008110091101011011110121101311014110151101611017110181101911020110211102211023110241102511026110271102811029110301103111032110331103411035110361103711038110391104011041110421104311044110451104611047110481104911050110511105211053110541105511056110571105811059110601106111062110631106411065110661106711068110691107011071110721107311074110751107611077110781107911080110811108211083110841108511086110871108811089110901109111092110931109411095110961109711098110991110011101111021110311104111051110611107111081110911110111111111211113111141111511116111171111811119111201112111122111231112411125111261112711128111291113011131111321113311134111351113611137111381113911140111411114211143111441114511146111471114811149111501115111152111531115411155111561115711158111591116011161111621116311164111651116611167111681116911170111711117211173111741117511176111771117811179111801118111182111831118411185111861118711188111891119011191111921119311194111951119611197111981119911200112011120211203112041120511206112071120811209112101121111212112131121411215112161121711218112191122011221112221122311224112251122611227112281122911230112311123211233112341123511236112371123811239112401124111242112431124411245112461124711248112491125011251112521125311254112551125611257112581125911260112611126211263112641126511266112671126811269112701127111272112731127411275112761127711278112791128011281112821128311284112851128611287112881128911290112911129211293112941129511296112971129811299113001130111302113031130411305113061130711308113091131011311113121131311314113151131611317113181131911320113211132211323113241132511326113271132811329113301133111332113331133411335113361133711338113391134011341113421134311344113451134611347113481134911350113511135211353113541135511356113571135811359113601136111362113631136411365113661136711368113691137011371113721137311374113751137611377113781137911380113811138211383113841138511386113871138811389113901139111392113931139411395113961139711398113991140011401114021140311404114051140611407114081140911410114111141211413114141141511416114171141811419114201142111422114231142411425114261142711428114291143011431114321143311434114351143611437114381143911440114411144211443114441144511446114471144811449114501145111452114531145411455114561145711458114591146011461114621146311464114651146611467114681146911470114711147211473114741147511476114771147811479114801148111482114831148411485114861148711488114891149011491114921149311494114951149611497114981149911500115011150211503115041150511506115071150811509115101151111512115131151411515115161151711518115191152011521115221152311524115251152611527115281152911530115311153211533115341153511536115371153811539115401154111542115431154411545115461154711548115491155011551115521155311554115551155611557115581155911560115611156211563115641156511566115671156811569115701157111572115731157411575115761157711578115791158011581115821158311584115851158611587115881158911590115911159211593115941159511596115971159811599116001160111602116031160411605116061160711608116091161011611116121161311614116151161611617116181161911620116211162211623116241162511626116271162811629116301163111632116331163411635116361163711638116391164011641116421164311644116451164611647116481164911650116511165211653116541165511656116571165811659116601166111662116631166411665116661166711668116691167011671116721167311674116751167611677116781167911680116811168211683116841168511686116871168811689116901169111692116931169411695116961169711698116991170011701117021170311704117051170611707117081170911710117111171211713117141171511716117171171811719117201172111722117231172411725117261172711728117291173011731117321173311734117351173611737117381173911740117411174211743117441174511746117471174811749117501175111752117531175411755117561175711758117591176011761117621176311764117651176611767117681176911770117711177211773117741177511776117771177811779117801178111782117831178411785117861178711788117891179011791117921179311794117951179611797117981179911800118011180211803118041180511806118071180811809118101181111812118131181411815118161181711818118191182011821118221182311824118251182611827118281182911830118311183211833118341183511836118371183811839118401184111842118431184411845118461184711848118491185011851118521185311854118551185611857118581185911860118611186211863118641186511866118671186811869118701187111872118731187411875118761187711878118791188011881118821188311884118851188611887118881188911890118911189211893118941189511896118971189811899119001190111902119031190411905119061190711908119091191011911119121191311914119151191611917119181191911920119211192211923119241192511926119271192811929119301193111932119331193411935119361193711938119391194011941119421194311944119451194611947119481194911950119511195211953119541195511956119571195811959119601196111962119631196411965119661196711968119691197011971119721197311974119751197611977119781197911980119811198211983119841198511986119871198811989119901199111992119931199411995119961199711998119991200012001120021200312004120051200612007120081200912010120111201212013120141201512016120171201812019120201202112022120231202412025120261202712028120291203012031120321203312034120351203612037120381203912040120411204212043120441204512046120471204812049120501205112052120531205412055120561205712058120591206012061120621206312064120651206612067120681206912070120711207212073120741207512076120771207812079120801208112082120831208412085120861208712088120891209012091120921209312094120951209612097120981209912100121011210212103121041210512106121071210812109121101211112112121131211412115121161211712118121191212012121121221212312124121251212612127121281212912130121311213212133121341213512136121371213812139121401214112142121431214412145121461214712148121491215012151121521215312154121551215612157121581215912160121611216212163121641216512166121671216812169121701217112172121731217412175121761217712178121791218012181121821218312184121851218612187121881218912190121911219212193121941219512196121971219812199122001220112202122031220412205122061220712208122091221012211122121221312214122151221612217122181221912220122211222212223122241222512226122271222812229122301223112232122331223412235122361223712238122391224012241122421224312244122451224612247122481224912250122511225212253122541225512256122571225812259122601226112262122631226412265122661226712268122691227012271122721227312274122751227612277122781227912280122811228212283122841228512286122871228812289122901229112292122931229412295122961229712298122991230012301123021230312304123051230612307123081230912310123111231212313123141231512316123171231812319123201232112322123231232412325123261232712328123291233012331123321233312334123351233612337123381233912340123411234212343123441234512346123471234812349123501235112352123531235412355123561235712358123591236012361123621236312364123651236612367123681236912370123711237212373123741237512376123771237812379123801238112382123831238412385123861238712388123891239012391123921239312394123951239612397123981239912400124011240212403124041240512406124071240812409124101241112412124131241412415124161241712418124191242012421124221242312424124251242612427124281242912430124311243212433124341243512436124371243812439124401244112442124431244412445124461244712448124491245012451124521245312454124551245612457124581245912460124611246212463124641246512466124671246812469124701247112472124731247412475124761247712478124791248012481124821248312484124851248612487124881248912490124911249212493124941249512496124971249812499125001250112502125031250412505125061250712508125091251012511125121251312514125151251612517125181251912520125211252212523125241252512526125271252812529125301253112532125331253412535125361253712538125391254012541125421254312544125451254612547125481254912550125511255212553125541255512556125571255812559125601256112562125631256412565125661256712568125691257012571125721257312574125751257612577125781257912580125811258212583125841258512586125871258812589125901259112592125931259412595125961259712598125991260012601126021260312604126051260612607126081260912610126111261212613126141261512616126171261812619126201262112622126231262412625126261262712628126291263012631126321263312634126351263612637126381263912640126411264212643126441264512646126471264812649126501265112652126531265412655126561265712658126591266012661126621266312664126651266612667126681266912670126711267212673126741267512676126771267812679126801268112682126831268412685126861268712688126891269012691126921269312694126951269612697126981269912700127011270212703127041270512706127071270812709127101271112712127131271412715127161271712718127191272012721127221272312724127251272612727127281272912730127311273212733127341273512736127371273812739127401274112742127431274412745127461274712748127491275012751127521275312754127551275612757127581275912760127611276212763127641276512766127671276812769127701277112772127731277412775127761277712778127791278012781127821278312784127851278612787127881278912790127911279212793127941279512796127971279812799128001280112802128031280412805128061280712808128091281012811128121281312814128151281612817128181281912820128211282212823128241282512826128271282812829128301283112832128331283412835128361283712838128391284012841128421284312844128451284612847128481284912850128511285212853128541285512856128571285812859128601286112862128631286412865128661286712868128691287012871128721287312874128751287612877128781287912880128811288212883128841288512886128871288812889128901289112892128931289412895128961289712898128991290012901129021290312904129051290612907129081290912910129111291212913129141291512916129171291812919129201292112922129231292412925129261292712928129291293012931129321293312934129351293612937129381293912940129411294212943129441294512946129471294812949129501295112952129531295412955129561295712958129591296012961129621296312964129651296612967129681296912970129711297212973129741297512976129771297812979129801298112982129831298412985129861298712988129891299012991129921299312994129951299612997129981299913000130011300213003130041300513006130071300813009130101301113012130131301413015130161301713018130191302013021130221302313024130251302613027130281302913030130311303213033130341303513036130371303813039130401304113042130431304413045130461304713048130491305013051130521305313054130551305613057130581305913060130611306213063130641306513066130671306813069130701307113072130731307413075130761307713078130791308013081130821308313084130851308613087130881308913090130911309213093130941309513096130971309813099131001310113102131031310413105131061310713108131091311013111131121311313114131151311613117131181311913120131211312213123131241312513126131271312813129131301313113132131331313413135131361313713138131391314013141131421314313144131451314613147131481314913150131511315213153131541315513156131571315813159131601316113162131631316413165131661316713168131691317013171131721317313174131751317613177131781317913180131811318213183131841318513186131871318813189131901319113192131931319413195131961319713198131991320013201132021320313204132051320613207132081320913210132111321213213132141321513216132171321813219132201322113222132231322413225132261322713228132291323013231132321323313234132351323613237132381323913240132411324213243132441324513246132471324813249132501325113252132531325413255132561325713258132591326013261132621326313264132651326613267132681326913270132711327213273132741327513276132771327813279132801328113282132831328413285132861328713288132891329013291132921329313294132951329613297132981329913300133011330213303133041330513306133071330813309133101331113312133131331413315133161331713318133191332013321133221332313324133251332613327133281332913330133311333213333133341333513336133371333813339133401334113342133431334413345133461334713348133491335013351133521335313354133551335613357133581335913360133611336213363133641336513366133671336813369133701337113372133731337413375133761337713378133791338013381133821338313384133851338613387133881338913390133911339213393133941339513396133971339813399134001340113402134031340413405134061340713408134091341013411134121341313414134151341613417134181341913420134211342213423134241342513426134271342813429134301343113432134331343413435134361343713438134391344013441134421344313444134451344613447134481344913450134511345213453134541345513456134571345813459134601346113462134631346413465134661346713468134691347013471134721347313474134751347613477134781347913480134811348213483134841348513486134871348813489134901349113492134931349413495134961349713498134991350013501135021350313504135051350613507135081350913510135111351213513135141351513516135171351813519135201352113522135231352413525135261352713528135291353013531135321353313534135351353613537135381353913540135411354213543135441354513546135471354813549135501355113552135531355413555135561355713558135591356013561135621356313564135651356613567135681356913570135711357213573135741357513576135771357813579135801358113582135831358413585135861358713588135891359013591135921359313594135951359613597135981359913600136011360213603136041360513606136071360813609136101361113612136131361413615136161361713618136191362013621136221362313624136251362613627136281362913630136311363213633136341363513636136371363813639136401364113642136431364413645136461364713648136491365013651136521365313654136551365613657136581365913660136611366213663136641366513666136671366813669136701367113672136731367413675136761367713678136791368013681136821368313684136851368613687136881368913690136911369213693136941369513696136971369813699137001370113702137031370413705137061370713708137091371013711137121371313714137151371613717137181371913720137211372213723137241372513726137271372813729137301373113732137331373413735137361373713738137391374013741137421374313744137451374613747137481374913750137511375213753137541375513756137571375813759137601376113762137631376413765137661376713768137691377013771137721377313774137751377613777137781377913780137811378213783137841378513786137871378813789137901379113792137931379413795137961379713798137991380013801138021380313804138051380613807138081380913810138111381213813138141381513816138171381813819138201382113822138231382413825138261382713828138291383013831138321383313834138351383613837138381383913840138411384213843138441384513846138471384813849138501385113852138531385413855138561385713858138591386013861138621386313864138651386613867138681386913870138711387213873138741387513876138771387813879138801388113882138831388413885138861388713888138891389013891138921389313894138951389613897138981389913900139011390213903139041390513906139071390813909139101391113912139131391413915139161391713918139191392013921139221392313924139251392613927139281392913930139311393213933139341393513936139371393813939139401394113942139431394413945139461394713948139491395013951139521395313954139551395613957139581395913960139611396213963139641396513966139671396813969139701397113972139731397413975139761397713978139791398013981139821398313984139851398613987139881398913990139911399213993139941399513996139971399813999140001400114002140031400414005140061400714008140091401014011140121401314014140151401614017140181401914020140211402214023140241402514026140271402814029140301403114032140331403414035140361403714038140391404014041140421404314044140451404614047140481404914050140511405214053140541405514056140571405814059140601406114062140631406414065140661406714068140691407014071140721407314074140751407614077140781407914080140811408214083140841408514086140871408814089140901409114092140931409414095140961409714098140991410014101141021410314104141051410614107141081410914110141111411214113141141411514116141171411814119141201412114122141231412414125141261412714128141291413014131141321413314134141351413614137141381413914140141411414214143141441414514146141471414814149141501415114152141531415414155141561415714158141591416014161141621416314164141651416614167141681416914170141711417214173141741417514176141771417814179141801418114182141831418414185141861418714188141891419014191141921419314194141951419614197141981419914200142011420214203142041420514206142071420814209142101421114212142131421414215142161421714218142191422014221142221422314224142251422614227142281422914230142311423214233142341423514236142371423814239142401424114242142431424414245142461424714248142491425014251142521425314254142551425614257142581425914260142611426214263142641426514266142671426814269142701427114272142731427414275142761427714278142791428014281142821428314284142851428614287142881428914290142911429214293142941429514296142971429814299143001430114302143031430414305143061430714308143091431014311143121431314314143151431614317143181431914320143211432214323143241432514326143271432814329143301433114332143331433414335143361433714338143391434014341143421434314344143451434614347143481434914350143511435214353143541435514356143571435814359143601436114362143631436414365143661436714368143691437014371143721437314374143751437614377143781437914380143811438214383143841438514386143871438814389143901439114392143931439414395143961439714398143991440014401144021440314404144051440614407144081440914410144111441214413144141441514416144171441814419144201442114422144231442414425144261442714428144291443014431144321443314434144351443614437144381443914440144411444214443144441444514446144471444814449144501445114452144531445414455144561445714458144591446014461144621446314464144651446614467144681446914470144711447214473144741447514476144771447814479144801448114482144831448414485144861448714488144891449014491144921449314494144951449614497144981449914500145011450214503145041450514506145071450814509145101451114512145131451414515145161451714518145191452014521145221452314524145251452614527145281452914530145311453214533145341453514536145371453814539145401454114542145431454414545145461454714548145491455014551145521455314554145551455614557145581455914560145611456214563145641456514566145671456814569145701457114572145731457414575145761457714578145791458014581145821458314584145851458614587145881458914590145911459214593145941459514596145971459814599146001460114602146031460414605146061460714608146091461014611146121461314614146151461614617146181461914620146211462214623146241462514626146271462814629146301463114632146331463414635146361463714638146391464014641146421464314644146451464614647146481464914650146511465214653146541465514656146571465814659146601466114662146631466414665146661466714668146691467014671146721467314674146751467614677146781467914680146811468214683146841468514686146871468814689146901469114692146931469414695146961469714698146991470014701147021470314704147051470614707147081470914710147111471214713147141471514716147171471814719147201472114722147231472414725147261472714728147291473014731147321473314734147351473614737147381473914740147411474214743147441474514746147471474814749147501475114752147531475414755147561475714758147591476014761147621476314764147651476614767147681476914770147711477214773147741477514776147771477814779147801478114782147831478414785147861478714788147891479014791147921479314794147951479614797147981479914800148011480214803148041480514806148071480814809148101481114812148131481414815148161481714818148191482014821148221482314824148251482614827148281482914830148311483214833148341483514836148371483814839148401484114842148431484414845148461484714848148491485014851148521485314854148551485614857148581485914860148611486214863148641486514866148671486814869148701487114872148731487414875148761487714878148791488014881148821488314884148851488614887148881488914890148911489214893148941489514896148971489814899149001490114902149031490414905149061490714908149091491014911149121491314914149151491614917149181491914920149211492214923149241492514926149271492814929149301493114932149331493414935149361493714938149391494014941149421494314944149451494614947149481494914950149511495214953149541495514956149571495814959149601496114962149631496414965149661496714968149691497014971149721497314974149751497614977149781497914980149811498214983149841498514986
  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. "fmt"
  17. "math"
  18. "reflect"
  19. "strings"
  20. "testing"
  21. "k8s.io/api/core/v1"
  22. "k8s.io/apimachinery/pkg/api/resource"
  23. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  24. "k8s.io/apimachinery/pkg/util/intstr"
  25. "k8s.io/apimachinery/pkg/util/validation"
  26. "k8s.io/apimachinery/pkg/util/validation/field"
  27. utilfeature "k8s.io/apiserver/pkg/util/feature"
  28. featuregatetesting "k8s.io/component-base/featuregate/testing"
  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. for i, tc := range testCases {
  1973. errs := validateCSIPersistentVolumeSource(tc.csi, field.NewPath("field"))
  1974. if len(errs) > 0 && tc.errtype == "" {
  1975. t.Errorf("[%d: %q] unexpected error(s): %v", i, tc.name, errs)
  1976. } else if len(errs) == 0 && tc.errtype != "" {
  1977. t.Errorf("[%d: %q] expected error type %v", i, tc.name, tc.errtype)
  1978. } else if len(errs) >= 1 {
  1979. if errs[0].Type != tc.errtype {
  1980. t.Errorf("[%d: %q] expected error type %v, got %v", i, tc.name, tc.errtype, errs[0].Type)
  1981. } else if !strings.HasSuffix(errs[0].Field, "."+tc.errfield) {
  1982. t.Errorf("[%d: %q] expected error on field %q, got %q", i, tc.name, tc.errfield, errs[0].Field)
  1983. }
  1984. }
  1985. }
  1986. }
  1987. // This test is a little too top-to-bottom. Ideally we would test each volume
  1988. // type on its own, but we want to also make sure that the logic works through
  1989. // the one-of wrapper, so we just do it all in one place.
  1990. func TestValidateVolumes(t *testing.T) {
  1991. validInitiatorName := "iqn.2015-02.example.com:init"
  1992. invalidInitiatorName := "2015-02.example.com:init"
  1993. type verr struct {
  1994. etype field.ErrorType
  1995. field string
  1996. detail string
  1997. }
  1998. testCases := []struct {
  1999. name string
  2000. vol core.Volume
  2001. errs []verr
  2002. }{
  2003. // EmptyDir and basic volume names
  2004. {
  2005. name: "valid alpha name",
  2006. vol: core.Volume{
  2007. Name: "empty",
  2008. VolumeSource: core.VolumeSource{
  2009. EmptyDir: &core.EmptyDirVolumeSource{},
  2010. },
  2011. },
  2012. },
  2013. {
  2014. name: "valid num name",
  2015. vol: core.Volume{
  2016. Name: "123",
  2017. VolumeSource: core.VolumeSource{
  2018. EmptyDir: &core.EmptyDirVolumeSource{},
  2019. },
  2020. },
  2021. },
  2022. {
  2023. name: "valid alphanum name",
  2024. vol: core.Volume{
  2025. Name: "empty-123",
  2026. VolumeSource: core.VolumeSource{
  2027. EmptyDir: &core.EmptyDirVolumeSource{},
  2028. },
  2029. },
  2030. },
  2031. {
  2032. name: "valid numalpha name",
  2033. vol: core.Volume{
  2034. Name: "123-empty",
  2035. VolumeSource: core.VolumeSource{
  2036. EmptyDir: &core.EmptyDirVolumeSource{},
  2037. },
  2038. },
  2039. },
  2040. {
  2041. name: "zero-length name",
  2042. vol: core.Volume{
  2043. Name: "",
  2044. VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}},
  2045. },
  2046. errs: []verr{{
  2047. etype: field.ErrorTypeRequired,
  2048. field: "name",
  2049. }},
  2050. },
  2051. {
  2052. name: "name > 63 characters",
  2053. vol: core.Volume{
  2054. Name: strings.Repeat("a", 64),
  2055. VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}},
  2056. },
  2057. errs: []verr{{
  2058. etype: field.ErrorTypeInvalid,
  2059. field: "name",
  2060. detail: "must be no more than",
  2061. }},
  2062. },
  2063. {
  2064. name: "name not a DNS label",
  2065. vol: core.Volume{
  2066. Name: "a.b.c",
  2067. VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}},
  2068. },
  2069. errs: []verr{{
  2070. etype: field.ErrorTypeInvalid,
  2071. field: "name",
  2072. detail: dnsLabelErrMsg,
  2073. }},
  2074. },
  2075. // More than one source field specified.
  2076. {
  2077. name: "more than one source",
  2078. vol: core.Volume{
  2079. Name: "dups",
  2080. VolumeSource: core.VolumeSource{
  2081. EmptyDir: &core.EmptyDirVolumeSource{},
  2082. HostPath: &core.HostPathVolumeSource{
  2083. Path: "/mnt/path",
  2084. Type: newHostPathType(string(core.HostPathDirectory)),
  2085. },
  2086. },
  2087. },
  2088. errs: []verr{{
  2089. etype: field.ErrorTypeForbidden,
  2090. field: "hostPath",
  2091. detail: "may not specify more than 1 volume",
  2092. }},
  2093. },
  2094. // HostPath Default
  2095. {
  2096. name: "default HostPath",
  2097. vol: core.Volume{
  2098. Name: "hostpath",
  2099. VolumeSource: core.VolumeSource{
  2100. HostPath: &core.HostPathVolumeSource{
  2101. Path: "/mnt/path",
  2102. Type: newHostPathType(string(core.HostPathDirectory)),
  2103. },
  2104. },
  2105. },
  2106. },
  2107. // HostPath Supported
  2108. {
  2109. name: "valid HostPath",
  2110. vol: core.Volume{
  2111. Name: "hostpath",
  2112. VolumeSource: core.VolumeSource{
  2113. HostPath: &core.HostPathVolumeSource{
  2114. Path: "/mnt/path",
  2115. Type: newHostPathType(string(core.HostPathSocket)),
  2116. },
  2117. },
  2118. },
  2119. },
  2120. // HostPath Invalid
  2121. {
  2122. name: "invalid HostPath",
  2123. vol: core.Volume{
  2124. Name: "hostpath",
  2125. VolumeSource: core.VolumeSource{
  2126. HostPath: &core.HostPathVolumeSource{
  2127. Path: "/mnt/path",
  2128. Type: newHostPathType("invalid"),
  2129. },
  2130. },
  2131. },
  2132. errs: []verr{{
  2133. etype: field.ErrorTypeNotSupported,
  2134. field: "type",
  2135. }},
  2136. },
  2137. {
  2138. name: "invalid HostPath backsteps",
  2139. vol: core.Volume{
  2140. Name: "hostpath",
  2141. VolumeSource: core.VolumeSource{
  2142. HostPath: &core.HostPathVolumeSource{
  2143. Path: "/mnt/path/..",
  2144. Type: newHostPathType(string(core.HostPathDirectory)),
  2145. },
  2146. },
  2147. },
  2148. errs: []verr{{
  2149. etype: field.ErrorTypeInvalid,
  2150. field: "path",
  2151. detail: "must not contain '..'",
  2152. }},
  2153. },
  2154. // GcePersistentDisk
  2155. {
  2156. name: "valid GcePersistentDisk",
  2157. vol: core.Volume{
  2158. Name: "gce-pd",
  2159. VolumeSource: core.VolumeSource{
  2160. GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{
  2161. PDName: "my-PD",
  2162. FSType: "ext4",
  2163. Partition: 1,
  2164. ReadOnly: false,
  2165. },
  2166. },
  2167. },
  2168. },
  2169. // AWSElasticBlockStore
  2170. {
  2171. name: "valid AWSElasticBlockStore",
  2172. vol: core.Volume{
  2173. Name: "aws-ebs",
  2174. VolumeSource: core.VolumeSource{
  2175. AWSElasticBlockStore: &core.AWSElasticBlockStoreVolumeSource{
  2176. VolumeID: "my-PD",
  2177. FSType: "ext4",
  2178. Partition: 1,
  2179. ReadOnly: false,
  2180. },
  2181. },
  2182. },
  2183. },
  2184. // GitRepo
  2185. {
  2186. name: "valid GitRepo",
  2187. vol: core.Volume{
  2188. Name: "git-repo",
  2189. VolumeSource: core.VolumeSource{
  2190. GitRepo: &core.GitRepoVolumeSource{
  2191. Repository: "my-repo",
  2192. Revision: "hashstring",
  2193. Directory: "target",
  2194. },
  2195. },
  2196. },
  2197. },
  2198. {
  2199. name: "valid GitRepo in .",
  2200. vol: core.Volume{
  2201. Name: "git-repo-dot",
  2202. VolumeSource: core.VolumeSource{
  2203. GitRepo: &core.GitRepoVolumeSource{
  2204. Repository: "my-repo",
  2205. Directory: ".",
  2206. },
  2207. },
  2208. },
  2209. },
  2210. {
  2211. name: "valid GitRepo with .. in name",
  2212. vol: core.Volume{
  2213. Name: "git-repo-dot-dot-foo",
  2214. VolumeSource: core.VolumeSource{
  2215. GitRepo: &core.GitRepoVolumeSource{
  2216. Repository: "my-repo",
  2217. Directory: "..foo",
  2218. },
  2219. },
  2220. },
  2221. },
  2222. {
  2223. name: "GitRepo starts with ../",
  2224. vol: core.Volume{
  2225. Name: "gitrepo",
  2226. VolumeSource: core.VolumeSource{
  2227. GitRepo: &core.GitRepoVolumeSource{
  2228. Repository: "foo",
  2229. Directory: "../dots/bar",
  2230. },
  2231. },
  2232. },
  2233. errs: []verr{{
  2234. etype: field.ErrorTypeInvalid,
  2235. field: "gitRepo.directory",
  2236. detail: `must not contain '..'`,
  2237. }},
  2238. },
  2239. {
  2240. name: "GitRepo contains ..",
  2241. vol: core.Volume{
  2242. Name: "gitrepo",
  2243. VolumeSource: core.VolumeSource{
  2244. GitRepo: &core.GitRepoVolumeSource{
  2245. Repository: "foo",
  2246. Directory: "dots/../bar",
  2247. },
  2248. },
  2249. },
  2250. errs: []verr{{
  2251. etype: field.ErrorTypeInvalid,
  2252. field: "gitRepo.directory",
  2253. detail: `must not contain '..'`,
  2254. }},
  2255. },
  2256. {
  2257. name: "GitRepo absolute target",
  2258. vol: core.Volume{
  2259. Name: "gitrepo",
  2260. VolumeSource: core.VolumeSource{
  2261. GitRepo: &core.GitRepoVolumeSource{
  2262. Repository: "foo",
  2263. Directory: "/abstarget",
  2264. },
  2265. },
  2266. },
  2267. errs: []verr{{
  2268. etype: field.ErrorTypeInvalid,
  2269. field: "gitRepo.directory",
  2270. }},
  2271. },
  2272. // ISCSI
  2273. {
  2274. name: "valid ISCSI",
  2275. vol: core.Volume{
  2276. Name: "iscsi",
  2277. VolumeSource: core.VolumeSource{
  2278. ISCSI: &core.ISCSIVolumeSource{
  2279. TargetPortal: "127.0.0.1",
  2280. IQN: "iqn.2015-02.example.com:test",
  2281. Lun: 1,
  2282. FSType: "ext4",
  2283. ReadOnly: false,
  2284. },
  2285. },
  2286. },
  2287. },
  2288. {
  2289. name: "valid IQN: eui format",
  2290. vol: core.Volume{
  2291. Name: "iscsi",
  2292. VolumeSource: core.VolumeSource{
  2293. ISCSI: &core.ISCSIVolumeSource{
  2294. TargetPortal: "127.0.0.1",
  2295. IQN: "eui.0123456789ABCDEF",
  2296. Lun: 1,
  2297. FSType: "ext4",
  2298. ReadOnly: false,
  2299. },
  2300. },
  2301. },
  2302. },
  2303. {
  2304. name: "valid IQN: naa format",
  2305. vol: core.Volume{
  2306. Name: "iscsi",
  2307. VolumeSource: core.VolumeSource{
  2308. ISCSI: &core.ISCSIVolumeSource{
  2309. TargetPortal: "127.0.0.1",
  2310. IQN: "naa.62004567BA64678D0123456789ABCDEF",
  2311. Lun: 1,
  2312. FSType: "ext4",
  2313. ReadOnly: false,
  2314. },
  2315. },
  2316. },
  2317. },
  2318. {
  2319. name: "empty portal",
  2320. vol: core.Volume{
  2321. Name: "iscsi",
  2322. VolumeSource: core.VolumeSource{
  2323. ISCSI: &core.ISCSIVolumeSource{
  2324. TargetPortal: "",
  2325. IQN: "iqn.2015-02.example.com:test",
  2326. Lun: 1,
  2327. FSType: "ext4",
  2328. ReadOnly: false,
  2329. },
  2330. },
  2331. },
  2332. errs: []verr{{
  2333. etype: field.ErrorTypeRequired,
  2334. field: "iscsi.targetPortal",
  2335. }},
  2336. },
  2337. {
  2338. name: "empty iqn",
  2339. vol: core.Volume{
  2340. Name: "iscsi",
  2341. VolumeSource: core.VolumeSource{
  2342. ISCSI: &core.ISCSIVolumeSource{
  2343. TargetPortal: "127.0.0.1",
  2344. IQN: "",
  2345. Lun: 1,
  2346. FSType: "ext4",
  2347. ReadOnly: false,
  2348. },
  2349. },
  2350. },
  2351. errs: []verr{{
  2352. etype: field.ErrorTypeRequired,
  2353. field: "iscsi.iqn",
  2354. }},
  2355. },
  2356. {
  2357. name: "invalid IQN: iqn format",
  2358. vol: core.Volume{
  2359. Name: "iscsi",
  2360. VolumeSource: core.VolumeSource{
  2361. ISCSI: &core.ISCSIVolumeSource{
  2362. TargetPortal: "127.0.0.1",
  2363. IQN: "iqn.2015-02.example.com:test;ls;",
  2364. Lun: 1,
  2365. FSType: "ext4",
  2366. ReadOnly: false,
  2367. },
  2368. },
  2369. },
  2370. errs: []verr{{
  2371. etype: field.ErrorTypeInvalid,
  2372. field: "iscsi.iqn",
  2373. }},
  2374. },
  2375. {
  2376. name: "invalid IQN: eui format",
  2377. vol: core.Volume{
  2378. Name: "iscsi",
  2379. VolumeSource: core.VolumeSource{
  2380. ISCSI: &core.ISCSIVolumeSource{
  2381. TargetPortal: "127.0.0.1",
  2382. IQN: "eui.0123456789ABCDEFGHIJ",
  2383. Lun: 1,
  2384. FSType: "ext4",
  2385. ReadOnly: false,
  2386. },
  2387. },
  2388. },
  2389. errs: []verr{{
  2390. etype: field.ErrorTypeInvalid,
  2391. field: "iscsi.iqn",
  2392. }},
  2393. },
  2394. {
  2395. name: "invalid IQN: naa format",
  2396. vol: core.Volume{
  2397. Name: "iscsi",
  2398. VolumeSource: core.VolumeSource{
  2399. ISCSI: &core.ISCSIVolumeSource{
  2400. TargetPortal: "127.0.0.1",
  2401. IQN: "naa.62004567BA_4-78D.123456789ABCDEF",
  2402. Lun: 1,
  2403. FSType: "ext4",
  2404. ReadOnly: false,
  2405. },
  2406. },
  2407. },
  2408. errs: []verr{{
  2409. etype: field.ErrorTypeInvalid,
  2410. field: "iscsi.iqn",
  2411. }},
  2412. },
  2413. {
  2414. name: "valid initiatorName",
  2415. vol: core.Volume{
  2416. Name: "iscsi",
  2417. VolumeSource: core.VolumeSource{
  2418. ISCSI: &core.ISCSIVolumeSource{
  2419. TargetPortal: "127.0.0.1",
  2420. IQN: "iqn.2015-02.example.com:test",
  2421. Lun: 1,
  2422. InitiatorName: &validInitiatorName,
  2423. FSType: "ext4",
  2424. ReadOnly: false,
  2425. },
  2426. },
  2427. },
  2428. },
  2429. {
  2430. name: "invalid initiatorName",
  2431. vol: core.Volume{
  2432. Name: "iscsi",
  2433. VolumeSource: core.VolumeSource{
  2434. ISCSI: &core.ISCSIVolumeSource{
  2435. TargetPortal: "127.0.0.1",
  2436. IQN: "iqn.2015-02.example.com:test",
  2437. Lun: 1,
  2438. InitiatorName: &invalidInitiatorName,
  2439. FSType: "ext4",
  2440. ReadOnly: false,
  2441. },
  2442. },
  2443. },
  2444. errs: []verr{{
  2445. etype: field.ErrorTypeInvalid,
  2446. field: "iscsi.initiatorname",
  2447. }},
  2448. },
  2449. {
  2450. name: "empty secret",
  2451. vol: core.Volume{
  2452. Name: "iscsi",
  2453. VolumeSource: core.VolumeSource{
  2454. ISCSI: &core.ISCSIVolumeSource{
  2455. TargetPortal: "127.0.0.1",
  2456. IQN: "iqn.2015-02.example.com:test",
  2457. Lun: 1,
  2458. FSType: "ext4",
  2459. ReadOnly: false,
  2460. DiscoveryCHAPAuth: true,
  2461. },
  2462. },
  2463. },
  2464. errs: []verr{{
  2465. etype: field.ErrorTypeRequired,
  2466. field: "iscsi.secretRef",
  2467. }},
  2468. },
  2469. {
  2470. name: "empty secret",
  2471. vol: core.Volume{
  2472. Name: "iscsi",
  2473. VolumeSource: core.VolumeSource{
  2474. ISCSI: &core.ISCSIVolumeSource{
  2475. TargetPortal: "127.0.0.1",
  2476. IQN: "iqn.2015-02.example.com:test",
  2477. Lun: 1,
  2478. FSType: "ext4",
  2479. ReadOnly: false,
  2480. SessionCHAPAuth: true,
  2481. },
  2482. },
  2483. },
  2484. errs: []verr{{
  2485. etype: field.ErrorTypeRequired,
  2486. field: "iscsi.secretRef",
  2487. }},
  2488. },
  2489. // Secret
  2490. {
  2491. name: "valid Secret",
  2492. vol: core.Volume{
  2493. Name: "secret",
  2494. VolumeSource: core.VolumeSource{
  2495. Secret: &core.SecretVolumeSource{
  2496. SecretName: "my-secret",
  2497. },
  2498. },
  2499. },
  2500. },
  2501. {
  2502. name: "valid Secret with defaultMode",
  2503. vol: core.Volume{
  2504. Name: "secret",
  2505. VolumeSource: core.VolumeSource{
  2506. Secret: &core.SecretVolumeSource{
  2507. SecretName: "my-secret",
  2508. DefaultMode: utilpointer.Int32Ptr(0644),
  2509. },
  2510. },
  2511. },
  2512. },
  2513. {
  2514. name: "valid Secret with projection and mode",
  2515. vol: core.Volume{
  2516. Name: "secret",
  2517. VolumeSource: core.VolumeSource{
  2518. Secret: &core.SecretVolumeSource{
  2519. SecretName: "my-secret",
  2520. Items: []core.KeyToPath{{
  2521. Key: "key",
  2522. Path: "filename",
  2523. Mode: utilpointer.Int32Ptr(0644),
  2524. }},
  2525. },
  2526. },
  2527. },
  2528. },
  2529. {
  2530. name: "valid Secret with subdir projection",
  2531. vol: core.Volume{
  2532. Name: "secret",
  2533. VolumeSource: core.VolumeSource{
  2534. Secret: &core.SecretVolumeSource{
  2535. SecretName: "my-secret",
  2536. Items: []core.KeyToPath{{
  2537. Key: "key",
  2538. Path: "dir/filename",
  2539. }},
  2540. },
  2541. },
  2542. },
  2543. },
  2544. {
  2545. name: "secret with missing path",
  2546. vol: core.Volume{
  2547. Name: "secret",
  2548. VolumeSource: core.VolumeSource{
  2549. Secret: &core.SecretVolumeSource{
  2550. SecretName: "s",
  2551. Items: []core.KeyToPath{{Key: "key", Path: ""}},
  2552. },
  2553. },
  2554. },
  2555. errs: []verr{{
  2556. etype: field.ErrorTypeRequired,
  2557. field: "secret.items[0].path",
  2558. }},
  2559. },
  2560. {
  2561. name: "secret with leading ..",
  2562. vol: core.Volume{
  2563. Name: "secret",
  2564. VolumeSource: core.VolumeSource{
  2565. Secret: &core.SecretVolumeSource{
  2566. SecretName: "s",
  2567. Items: []core.KeyToPath{{Key: "key", Path: "../foo"}},
  2568. },
  2569. },
  2570. },
  2571. errs: []verr{{
  2572. etype: field.ErrorTypeInvalid,
  2573. field: "secret.items[0].path",
  2574. }},
  2575. },
  2576. {
  2577. name: "secret with .. inside",
  2578. vol: core.Volume{
  2579. Name: "secret",
  2580. VolumeSource: core.VolumeSource{
  2581. Secret: &core.SecretVolumeSource{
  2582. SecretName: "s",
  2583. Items: []core.KeyToPath{{Key: "key", Path: "foo/../bar"}},
  2584. },
  2585. },
  2586. },
  2587. errs: []verr{{
  2588. etype: field.ErrorTypeInvalid,
  2589. field: "secret.items[0].path",
  2590. }},
  2591. },
  2592. {
  2593. name: "secret with invalid positive defaultMode",
  2594. vol: core.Volume{
  2595. Name: "secret",
  2596. VolumeSource: core.VolumeSource{
  2597. Secret: &core.SecretVolumeSource{
  2598. SecretName: "s",
  2599. DefaultMode: utilpointer.Int32Ptr(01000),
  2600. },
  2601. },
  2602. },
  2603. errs: []verr{{
  2604. etype: field.ErrorTypeInvalid,
  2605. field: "secret.defaultMode",
  2606. }},
  2607. },
  2608. {
  2609. name: "secret with invalid negative defaultMode",
  2610. vol: core.Volume{
  2611. Name: "secret",
  2612. VolumeSource: core.VolumeSource{
  2613. Secret: &core.SecretVolumeSource{
  2614. SecretName: "s",
  2615. DefaultMode: utilpointer.Int32Ptr(-1),
  2616. },
  2617. },
  2618. },
  2619. errs: []verr{{
  2620. etype: field.ErrorTypeInvalid,
  2621. field: "secret.defaultMode",
  2622. }},
  2623. },
  2624. // ConfigMap
  2625. {
  2626. name: "valid ConfigMap",
  2627. vol: core.Volume{
  2628. Name: "cfgmap",
  2629. VolumeSource: core.VolumeSource{
  2630. ConfigMap: &core.ConfigMapVolumeSource{
  2631. LocalObjectReference: core.LocalObjectReference{
  2632. Name: "my-cfgmap",
  2633. },
  2634. },
  2635. },
  2636. },
  2637. },
  2638. {
  2639. name: "valid ConfigMap with defaultMode",
  2640. vol: core.Volume{
  2641. Name: "cfgmap",
  2642. VolumeSource: core.VolumeSource{
  2643. ConfigMap: &core.ConfigMapVolumeSource{
  2644. LocalObjectReference: core.LocalObjectReference{
  2645. Name: "my-cfgmap",
  2646. },
  2647. DefaultMode: utilpointer.Int32Ptr(0644),
  2648. },
  2649. },
  2650. },
  2651. },
  2652. {
  2653. name: "valid ConfigMap with projection and mode",
  2654. vol: core.Volume{
  2655. Name: "cfgmap",
  2656. VolumeSource: core.VolumeSource{
  2657. ConfigMap: &core.ConfigMapVolumeSource{
  2658. LocalObjectReference: core.LocalObjectReference{
  2659. Name: "my-cfgmap"},
  2660. Items: []core.KeyToPath{{
  2661. Key: "key",
  2662. Path: "filename",
  2663. Mode: utilpointer.Int32Ptr(0644),
  2664. }},
  2665. },
  2666. },
  2667. },
  2668. },
  2669. {
  2670. name: "valid ConfigMap with subdir projection",
  2671. vol: core.Volume{
  2672. Name: "cfgmap",
  2673. VolumeSource: core.VolumeSource{
  2674. ConfigMap: &core.ConfigMapVolumeSource{
  2675. LocalObjectReference: core.LocalObjectReference{
  2676. Name: "my-cfgmap"},
  2677. Items: []core.KeyToPath{{
  2678. Key: "key",
  2679. Path: "dir/filename",
  2680. }},
  2681. },
  2682. },
  2683. },
  2684. },
  2685. {
  2686. name: "configmap with missing path",
  2687. vol: core.Volume{
  2688. Name: "cfgmap",
  2689. VolumeSource: core.VolumeSource{
  2690. ConfigMap: &core.ConfigMapVolumeSource{
  2691. LocalObjectReference: core.LocalObjectReference{Name: "c"},
  2692. Items: []core.KeyToPath{{Key: "key", Path: ""}},
  2693. },
  2694. },
  2695. },
  2696. errs: []verr{{
  2697. etype: field.ErrorTypeRequired,
  2698. field: "configMap.items[0].path",
  2699. }},
  2700. },
  2701. {
  2702. name: "configmap with leading ..",
  2703. vol: core.Volume{
  2704. Name: "cfgmap",
  2705. VolumeSource: core.VolumeSource{
  2706. ConfigMap: &core.ConfigMapVolumeSource{
  2707. LocalObjectReference: core.LocalObjectReference{Name: "c"},
  2708. Items: []core.KeyToPath{{Key: "key", Path: "../foo"}},
  2709. },
  2710. },
  2711. },
  2712. errs: []verr{{
  2713. etype: field.ErrorTypeInvalid,
  2714. field: "configMap.items[0].path",
  2715. }},
  2716. },
  2717. {
  2718. name: "configmap with .. inside",
  2719. vol: core.Volume{
  2720. Name: "cfgmap",
  2721. VolumeSource: core.VolumeSource{
  2722. ConfigMap: &core.ConfigMapVolumeSource{
  2723. LocalObjectReference: core.LocalObjectReference{Name: "c"},
  2724. Items: []core.KeyToPath{{Key: "key", Path: "foo/../bar"}},
  2725. },
  2726. },
  2727. },
  2728. errs: []verr{{
  2729. etype: field.ErrorTypeInvalid,
  2730. field: "configMap.items[0].path",
  2731. }},
  2732. },
  2733. {
  2734. name: "configmap with invalid positive defaultMode",
  2735. vol: core.Volume{
  2736. Name: "cfgmap",
  2737. VolumeSource: core.VolumeSource{
  2738. ConfigMap: &core.ConfigMapVolumeSource{
  2739. LocalObjectReference: core.LocalObjectReference{Name: "c"},
  2740. DefaultMode: utilpointer.Int32Ptr(01000),
  2741. },
  2742. },
  2743. },
  2744. errs: []verr{{
  2745. etype: field.ErrorTypeInvalid,
  2746. field: "configMap.defaultMode",
  2747. }},
  2748. },
  2749. {
  2750. name: "configmap with invalid negative defaultMode",
  2751. vol: core.Volume{
  2752. Name: "cfgmap",
  2753. VolumeSource: core.VolumeSource{
  2754. ConfigMap: &core.ConfigMapVolumeSource{
  2755. LocalObjectReference: core.LocalObjectReference{Name: "c"},
  2756. DefaultMode: utilpointer.Int32Ptr(-1),
  2757. },
  2758. },
  2759. },
  2760. errs: []verr{{
  2761. etype: field.ErrorTypeInvalid,
  2762. field: "configMap.defaultMode",
  2763. }},
  2764. },
  2765. // Glusterfs
  2766. {
  2767. name: "valid Glusterfs",
  2768. vol: core.Volume{
  2769. Name: "glusterfs",
  2770. VolumeSource: core.VolumeSource{
  2771. Glusterfs: &core.GlusterfsVolumeSource{
  2772. EndpointsName: "host1",
  2773. Path: "path",
  2774. ReadOnly: false,
  2775. },
  2776. },
  2777. },
  2778. },
  2779. {
  2780. name: "empty hosts",
  2781. vol: core.Volume{
  2782. Name: "glusterfs",
  2783. VolumeSource: core.VolumeSource{
  2784. Glusterfs: &core.GlusterfsVolumeSource{
  2785. EndpointsName: "",
  2786. Path: "path",
  2787. ReadOnly: false,
  2788. },
  2789. },
  2790. },
  2791. errs: []verr{{
  2792. etype: field.ErrorTypeRequired,
  2793. field: "glusterfs.endpoints",
  2794. }},
  2795. },
  2796. {
  2797. name: "empty path",
  2798. vol: core.Volume{
  2799. Name: "glusterfs",
  2800. VolumeSource: core.VolumeSource{
  2801. Glusterfs: &core.GlusterfsVolumeSource{
  2802. EndpointsName: "host",
  2803. Path: "",
  2804. ReadOnly: false,
  2805. },
  2806. },
  2807. },
  2808. errs: []verr{{
  2809. etype: field.ErrorTypeRequired,
  2810. field: "glusterfs.path",
  2811. }},
  2812. },
  2813. // Flocker
  2814. {
  2815. name: "valid Flocker -- datasetUUID",
  2816. vol: core.Volume{
  2817. Name: "flocker",
  2818. VolumeSource: core.VolumeSource{
  2819. Flocker: &core.FlockerVolumeSource{
  2820. DatasetUUID: "d846b09d-223d-43df-ab5b-d6db2206a0e4",
  2821. },
  2822. },
  2823. },
  2824. },
  2825. {
  2826. name: "valid Flocker -- datasetName",
  2827. vol: core.Volume{
  2828. Name: "flocker",
  2829. VolumeSource: core.VolumeSource{
  2830. Flocker: &core.FlockerVolumeSource{
  2831. DatasetName: "datasetName",
  2832. },
  2833. },
  2834. },
  2835. },
  2836. {
  2837. name: "both empty",
  2838. vol: core.Volume{
  2839. Name: "flocker",
  2840. VolumeSource: core.VolumeSource{
  2841. Flocker: &core.FlockerVolumeSource{
  2842. DatasetName: "",
  2843. },
  2844. },
  2845. },
  2846. errs: []verr{{
  2847. etype: field.ErrorTypeRequired,
  2848. field: "flocker",
  2849. }},
  2850. },
  2851. {
  2852. name: "both specified",
  2853. vol: core.Volume{
  2854. Name: "flocker",
  2855. VolumeSource: core.VolumeSource{
  2856. Flocker: &core.FlockerVolumeSource{
  2857. DatasetName: "datasetName",
  2858. DatasetUUID: "d846b09d-223d-43df-ab5b-d6db2206a0e4",
  2859. },
  2860. },
  2861. },
  2862. errs: []verr{{
  2863. etype: field.ErrorTypeInvalid,
  2864. field: "flocker",
  2865. }},
  2866. },
  2867. {
  2868. name: "slash in flocker datasetName",
  2869. vol: core.Volume{
  2870. Name: "flocker",
  2871. VolumeSource: core.VolumeSource{
  2872. Flocker: &core.FlockerVolumeSource{
  2873. DatasetName: "foo/bar",
  2874. },
  2875. },
  2876. },
  2877. errs: []verr{{
  2878. etype: field.ErrorTypeInvalid,
  2879. field: "flocker.datasetName",
  2880. detail: "must not contain '/'",
  2881. }},
  2882. },
  2883. // RBD
  2884. {
  2885. name: "valid RBD",
  2886. vol: core.Volume{
  2887. Name: "rbd",
  2888. VolumeSource: core.VolumeSource{
  2889. RBD: &core.RBDVolumeSource{
  2890. CephMonitors: []string{"foo"},
  2891. RBDImage: "bar",
  2892. FSType: "ext4",
  2893. },
  2894. },
  2895. },
  2896. },
  2897. {
  2898. name: "empty rbd monitors",
  2899. vol: core.Volume{
  2900. Name: "rbd",
  2901. VolumeSource: core.VolumeSource{
  2902. RBD: &core.RBDVolumeSource{
  2903. CephMonitors: []string{},
  2904. RBDImage: "bar",
  2905. FSType: "ext4",
  2906. },
  2907. },
  2908. },
  2909. errs: []verr{{
  2910. etype: field.ErrorTypeRequired,
  2911. field: "rbd.monitors",
  2912. }},
  2913. },
  2914. {
  2915. name: "empty image",
  2916. vol: core.Volume{
  2917. Name: "rbd",
  2918. VolumeSource: core.VolumeSource{
  2919. RBD: &core.RBDVolumeSource{
  2920. CephMonitors: []string{"foo"},
  2921. RBDImage: "",
  2922. FSType: "ext4",
  2923. },
  2924. },
  2925. },
  2926. errs: []verr{{
  2927. etype: field.ErrorTypeRequired,
  2928. field: "rbd.image",
  2929. }},
  2930. },
  2931. // Cinder
  2932. {
  2933. name: "valid Cinder",
  2934. vol: core.Volume{
  2935. Name: "cinder",
  2936. VolumeSource: core.VolumeSource{
  2937. Cinder: &core.CinderVolumeSource{
  2938. VolumeID: "29ea5088-4f60-4757-962e-dba678767887",
  2939. FSType: "ext4",
  2940. ReadOnly: false,
  2941. },
  2942. },
  2943. },
  2944. },
  2945. // CephFS
  2946. {
  2947. name: "valid CephFS",
  2948. vol: core.Volume{
  2949. Name: "cephfs",
  2950. VolumeSource: core.VolumeSource{
  2951. CephFS: &core.CephFSVolumeSource{
  2952. Monitors: []string{"foo"},
  2953. },
  2954. },
  2955. },
  2956. },
  2957. {
  2958. name: "empty cephfs monitors",
  2959. vol: core.Volume{
  2960. Name: "cephfs",
  2961. VolumeSource: core.VolumeSource{
  2962. CephFS: &core.CephFSVolumeSource{
  2963. Monitors: []string{},
  2964. },
  2965. },
  2966. },
  2967. errs: []verr{{
  2968. etype: field.ErrorTypeRequired,
  2969. field: "cephfs.monitors",
  2970. }},
  2971. },
  2972. // DownwardAPI
  2973. {
  2974. name: "valid DownwardAPI",
  2975. vol: core.Volume{
  2976. Name: "downwardapi",
  2977. VolumeSource: core.VolumeSource{
  2978. DownwardAPI: &core.DownwardAPIVolumeSource{
  2979. Items: []core.DownwardAPIVolumeFile{
  2980. {
  2981. Path: "labels",
  2982. FieldRef: &core.ObjectFieldSelector{
  2983. APIVersion: "v1",
  2984. FieldPath: "metadata.labels",
  2985. },
  2986. },
  2987. {
  2988. Path: "labels with subscript",
  2989. FieldRef: &core.ObjectFieldSelector{
  2990. APIVersion: "v1",
  2991. FieldPath: "metadata.labels['key']",
  2992. },
  2993. },
  2994. {
  2995. Path: "labels with complex subscript",
  2996. FieldRef: &core.ObjectFieldSelector{
  2997. APIVersion: "v1",
  2998. FieldPath: "metadata.labels['test.example.com/key']",
  2999. },
  3000. },
  3001. {
  3002. Path: "annotations",
  3003. FieldRef: &core.ObjectFieldSelector{
  3004. APIVersion: "v1",
  3005. FieldPath: "metadata.annotations",
  3006. },
  3007. },
  3008. {
  3009. Path: "annotations with subscript",
  3010. FieldRef: &core.ObjectFieldSelector{
  3011. APIVersion: "v1",
  3012. FieldPath: "metadata.annotations['key']",
  3013. },
  3014. },
  3015. {
  3016. Path: "annotations with complex subscript",
  3017. FieldRef: &core.ObjectFieldSelector{
  3018. APIVersion: "v1",
  3019. FieldPath: "metadata.annotations['TEST.EXAMPLE.COM/key']",
  3020. },
  3021. },
  3022. {
  3023. Path: "namespace",
  3024. FieldRef: &core.ObjectFieldSelector{
  3025. APIVersion: "v1",
  3026. FieldPath: "metadata.namespace",
  3027. },
  3028. },
  3029. {
  3030. Path: "name",
  3031. FieldRef: &core.ObjectFieldSelector{
  3032. APIVersion: "v1",
  3033. FieldPath: "metadata.name",
  3034. },
  3035. },
  3036. {
  3037. Path: "path/with/subdirs",
  3038. FieldRef: &core.ObjectFieldSelector{
  3039. APIVersion: "v1",
  3040. FieldPath: "metadata.labels",
  3041. },
  3042. },
  3043. {
  3044. Path: "path/./withdot",
  3045. FieldRef: &core.ObjectFieldSelector{
  3046. APIVersion: "v1",
  3047. FieldPath: "metadata.labels",
  3048. },
  3049. },
  3050. {
  3051. Path: "path/with/embedded..dotdot",
  3052. FieldRef: &core.ObjectFieldSelector{
  3053. APIVersion: "v1",
  3054. FieldPath: "metadata.labels",
  3055. },
  3056. },
  3057. {
  3058. Path: "path/with/leading/..dotdot",
  3059. FieldRef: &core.ObjectFieldSelector{
  3060. APIVersion: "v1",
  3061. FieldPath: "metadata.labels",
  3062. },
  3063. },
  3064. {
  3065. Path: "cpu_limit",
  3066. ResourceFieldRef: &core.ResourceFieldSelector{
  3067. ContainerName: "test-container",
  3068. Resource: "limits.cpu",
  3069. },
  3070. },
  3071. {
  3072. Path: "cpu_request",
  3073. ResourceFieldRef: &core.ResourceFieldSelector{
  3074. ContainerName: "test-container",
  3075. Resource: "requests.cpu",
  3076. },
  3077. },
  3078. {
  3079. Path: "memory_limit",
  3080. ResourceFieldRef: &core.ResourceFieldSelector{
  3081. ContainerName: "test-container",
  3082. Resource: "limits.memory",
  3083. },
  3084. },
  3085. {
  3086. Path: "memory_request",
  3087. ResourceFieldRef: &core.ResourceFieldSelector{
  3088. ContainerName: "test-container",
  3089. Resource: "requests.memory",
  3090. },
  3091. },
  3092. },
  3093. },
  3094. },
  3095. },
  3096. },
  3097. {
  3098. name: "downapi valid defaultMode",
  3099. vol: core.Volume{
  3100. Name: "downapi",
  3101. VolumeSource: core.VolumeSource{
  3102. DownwardAPI: &core.DownwardAPIVolumeSource{
  3103. DefaultMode: utilpointer.Int32Ptr(0644),
  3104. },
  3105. },
  3106. },
  3107. },
  3108. {
  3109. name: "downapi valid item mode",
  3110. vol: core.Volume{
  3111. Name: "downapi",
  3112. VolumeSource: core.VolumeSource{
  3113. DownwardAPI: &core.DownwardAPIVolumeSource{
  3114. Items: []core.DownwardAPIVolumeFile{{
  3115. Mode: utilpointer.Int32Ptr(0644),
  3116. Path: "path",
  3117. FieldRef: &core.ObjectFieldSelector{
  3118. APIVersion: "v1",
  3119. FieldPath: "metadata.labels",
  3120. },
  3121. }},
  3122. },
  3123. },
  3124. },
  3125. },
  3126. {
  3127. name: "downapi invalid positive item mode",
  3128. vol: core.Volume{
  3129. Name: "downapi",
  3130. VolumeSource: core.VolumeSource{
  3131. DownwardAPI: &core.DownwardAPIVolumeSource{
  3132. Items: []core.DownwardAPIVolumeFile{{
  3133. Mode: utilpointer.Int32Ptr(01000),
  3134. Path: "path",
  3135. FieldRef: &core.ObjectFieldSelector{
  3136. APIVersion: "v1",
  3137. FieldPath: "metadata.labels",
  3138. },
  3139. }},
  3140. },
  3141. },
  3142. },
  3143. errs: []verr{{
  3144. etype: field.ErrorTypeInvalid,
  3145. field: "downwardAPI.mode",
  3146. }},
  3147. },
  3148. {
  3149. name: "downapi invalid negative item mode",
  3150. vol: core.Volume{
  3151. Name: "downapi",
  3152. VolumeSource: core.VolumeSource{
  3153. DownwardAPI: &core.DownwardAPIVolumeSource{
  3154. Items: []core.DownwardAPIVolumeFile{{
  3155. Mode: utilpointer.Int32Ptr(-1),
  3156. Path: "path",
  3157. FieldRef: &core.ObjectFieldSelector{
  3158. APIVersion: "v1",
  3159. FieldPath: "metadata.labels",
  3160. },
  3161. }},
  3162. },
  3163. },
  3164. },
  3165. errs: []verr{{
  3166. etype: field.ErrorTypeInvalid,
  3167. field: "downwardAPI.mode",
  3168. }},
  3169. },
  3170. {
  3171. name: "downapi empty metatada path",
  3172. vol: core.Volume{
  3173. Name: "downapi",
  3174. VolumeSource: core.VolumeSource{
  3175. DownwardAPI: &core.DownwardAPIVolumeSource{
  3176. Items: []core.DownwardAPIVolumeFile{{
  3177. Path: "",
  3178. FieldRef: &core.ObjectFieldSelector{
  3179. APIVersion: "v1",
  3180. FieldPath: "metadata.labels",
  3181. },
  3182. }},
  3183. },
  3184. },
  3185. },
  3186. errs: []verr{{
  3187. etype: field.ErrorTypeRequired,
  3188. field: "downwardAPI.path",
  3189. }},
  3190. },
  3191. {
  3192. name: "downapi absolute path",
  3193. vol: core.Volume{
  3194. Name: "downapi",
  3195. VolumeSource: core.VolumeSource{
  3196. DownwardAPI: &core.DownwardAPIVolumeSource{
  3197. Items: []core.DownwardAPIVolumeFile{{
  3198. Path: "/absolutepath",
  3199. FieldRef: &core.ObjectFieldSelector{
  3200. APIVersion: "v1",
  3201. FieldPath: "metadata.labels",
  3202. },
  3203. }},
  3204. },
  3205. },
  3206. },
  3207. errs: []verr{{
  3208. etype: field.ErrorTypeInvalid,
  3209. field: "downwardAPI.path",
  3210. }},
  3211. },
  3212. {
  3213. name: "downapi dot dot path",
  3214. vol: core.Volume{
  3215. Name: "downapi",
  3216. VolumeSource: core.VolumeSource{
  3217. DownwardAPI: &core.DownwardAPIVolumeSource{
  3218. Items: []core.DownwardAPIVolumeFile{{
  3219. Path: "../../passwd",
  3220. FieldRef: &core.ObjectFieldSelector{
  3221. APIVersion: "v1",
  3222. FieldPath: "metadata.labels",
  3223. },
  3224. }},
  3225. },
  3226. },
  3227. },
  3228. errs: []verr{{
  3229. etype: field.ErrorTypeInvalid,
  3230. field: "downwardAPI.path",
  3231. detail: `must not contain '..'`,
  3232. }},
  3233. },
  3234. {
  3235. name: "downapi dot dot file name",
  3236. vol: core.Volume{
  3237. Name: "downapi",
  3238. VolumeSource: core.VolumeSource{
  3239. DownwardAPI: &core.DownwardAPIVolumeSource{
  3240. Items: []core.DownwardAPIVolumeFile{{
  3241. Path: "..badFileName",
  3242. FieldRef: &core.ObjectFieldSelector{
  3243. APIVersion: "v1",
  3244. FieldPath: "metadata.labels",
  3245. },
  3246. }},
  3247. },
  3248. },
  3249. },
  3250. errs: []verr{{
  3251. etype: field.ErrorTypeInvalid,
  3252. field: "downwardAPI.path",
  3253. detail: `must not start with '..'`,
  3254. }},
  3255. },
  3256. {
  3257. name: "downapi dot dot first level dirent",
  3258. vol: core.Volume{
  3259. Name: "downapi",
  3260. VolumeSource: core.VolumeSource{
  3261. DownwardAPI: &core.DownwardAPIVolumeSource{
  3262. Items: []core.DownwardAPIVolumeFile{{
  3263. Path: "..badDirName/goodFileName",
  3264. FieldRef: &core.ObjectFieldSelector{
  3265. APIVersion: "v1",
  3266. FieldPath: "metadata.labels",
  3267. },
  3268. }},
  3269. },
  3270. },
  3271. },
  3272. errs: []verr{{
  3273. etype: field.ErrorTypeInvalid,
  3274. field: "downwardAPI.path",
  3275. detail: `must not start with '..'`,
  3276. }},
  3277. },
  3278. {
  3279. name: "downapi fieldRef and ResourceFieldRef together",
  3280. vol: core.Volume{
  3281. Name: "downapi",
  3282. VolumeSource: core.VolumeSource{
  3283. DownwardAPI: &core.DownwardAPIVolumeSource{
  3284. Items: []core.DownwardAPIVolumeFile{{
  3285. Path: "test",
  3286. FieldRef: &core.ObjectFieldSelector{
  3287. APIVersion: "v1",
  3288. FieldPath: "metadata.labels",
  3289. },
  3290. ResourceFieldRef: &core.ResourceFieldSelector{
  3291. ContainerName: "test-container",
  3292. Resource: "requests.memory",
  3293. },
  3294. }},
  3295. },
  3296. },
  3297. },
  3298. errs: []verr{{
  3299. etype: field.ErrorTypeInvalid,
  3300. field: "downwardAPI",
  3301. detail: "fieldRef and resourceFieldRef can not be specified simultaneously",
  3302. }},
  3303. },
  3304. {
  3305. name: "downapi invalid positive defaultMode",
  3306. vol: core.Volume{
  3307. Name: "downapi",
  3308. VolumeSource: core.VolumeSource{
  3309. DownwardAPI: &core.DownwardAPIVolumeSource{
  3310. DefaultMode: utilpointer.Int32Ptr(01000),
  3311. },
  3312. },
  3313. },
  3314. errs: []verr{{
  3315. etype: field.ErrorTypeInvalid,
  3316. field: "downwardAPI.defaultMode",
  3317. }},
  3318. },
  3319. {
  3320. name: "downapi invalid negative defaultMode",
  3321. vol: core.Volume{
  3322. Name: "downapi",
  3323. VolumeSource: core.VolumeSource{
  3324. DownwardAPI: &core.DownwardAPIVolumeSource{
  3325. DefaultMode: utilpointer.Int32Ptr(-1),
  3326. },
  3327. },
  3328. },
  3329. errs: []verr{{
  3330. etype: field.ErrorTypeInvalid,
  3331. field: "downwardAPI.defaultMode",
  3332. }},
  3333. },
  3334. // FC
  3335. {
  3336. name: "FC valid targetWWNs and lun",
  3337. vol: core.Volume{
  3338. Name: "fc",
  3339. VolumeSource: core.VolumeSource{
  3340. FC: &core.FCVolumeSource{
  3341. TargetWWNs: []string{"some_wwn"},
  3342. Lun: utilpointer.Int32Ptr(1),
  3343. FSType: "ext4",
  3344. ReadOnly: false,
  3345. },
  3346. },
  3347. },
  3348. },
  3349. {
  3350. name: "FC valid wwids",
  3351. vol: core.Volume{
  3352. Name: "fc",
  3353. VolumeSource: core.VolumeSource{
  3354. FC: &core.FCVolumeSource{
  3355. WWIDs: []string{"some_wwid"},
  3356. FSType: "ext4",
  3357. ReadOnly: false,
  3358. },
  3359. },
  3360. },
  3361. },
  3362. {
  3363. name: "FC empty targetWWNs and wwids",
  3364. vol: core.Volume{
  3365. Name: "fc",
  3366. VolumeSource: core.VolumeSource{
  3367. FC: &core.FCVolumeSource{
  3368. TargetWWNs: []string{},
  3369. Lun: utilpointer.Int32Ptr(1),
  3370. WWIDs: []string{},
  3371. FSType: "ext4",
  3372. ReadOnly: false,
  3373. },
  3374. },
  3375. },
  3376. errs: []verr{{
  3377. etype: field.ErrorTypeRequired,
  3378. field: "fc.targetWWNs",
  3379. detail: "must specify either targetWWNs or wwids",
  3380. }},
  3381. },
  3382. {
  3383. name: "FC invalid: both targetWWNs and wwids simultaneously",
  3384. vol: core.Volume{
  3385. Name: "fc",
  3386. VolumeSource: core.VolumeSource{
  3387. FC: &core.FCVolumeSource{
  3388. TargetWWNs: []string{"some_wwn"},
  3389. Lun: utilpointer.Int32Ptr(1),
  3390. WWIDs: []string{"some_wwid"},
  3391. FSType: "ext4",
  3392. ReadOnly: false,
  3393. },
  3394. },
  3395. },
  3396. errs: []verr{{
  3397. etype: field.ErrorTypeInvalid,
  3398. field: "fc.targetWWNs",
  3399. detail: "targetWWNs and wwids can not be specified simultaneously",
  3400. }},
  3401. },
  3402. {
  3403. name: "FC valid targetWWNs and empty lun",
  3404. vol: core.Volume{
  3405. Name: "fc",
  3406. VolumeSource: core.VolumeSource{
  3407. FC: &core.FCVolumeSource{
  3408. TargetWWNs: []string{"wwn"},
  3409. Lun: nil,
  3410. FSType: "ext4",
  3411. ReadOnly: false,
  3412. },
  3413. },
  3414. },
  3415. errs: []verr{{
  3416. etype: field.ErrorTypeRequired,
  3417. field: "fc.lun",
  3418. detail: "lun is required if targetWWNs is specified",
  3419. }},
  3420. },
  3421. {
  3422. name: "FC valid targetWWNs and invalid lun",
  3423. vol: core.Volume{
  3424. Name: "fc",
  3425. VolumeSource: core.VolumeSource{
  3426. FC: &core.FCVolumeSource{
  3427. TargetWWNs: []string{"wwn"},
  3428. Lun: utilpointer.Int32Ptr(256),
  3429. FSType: "ext4",
  3430. ReadOnly: false,
  3431. },
  3432. },
  3433. },
  3434. errs: []verr{{
  3435. etype: field.ErrorTypeInvalid,
  3436. field: "fc.lun",
  3437. detail: validation.InclusiveRangeError(0, 255),
  3438. }},
  3439. },
  3440. // FlexVolume
  3441. {
  3442. name: "valid FlexVolume",
  3443. vol: core.Volume{
  3444. Name: "flex-volume",
  3445. VolumeSource: core.VolumeSource{
  3446. FlexVolume: &core.FlexVolumeSource{
  3447. Driver: "kubernetes.io/blue",
  3448. FSType: "ext4",
  3449. },
  3450. },
  3451. },
  3452. },
  3453. // AzureFile
  3454. {
  3455. name: "valid AzureFile",
  3456. vol: core.Volume{
  3457. Name: "azure-file",
  3458. VolumeSource: core.VolumeSource{
  3459. AzureFile: &core.AzureFileVolumeSource{
  3460. SecretName: "key",
  3461. ShareName: "share",
  3462. ReadOnly: false,
  3463. },
  3464. },
  3465. },
  3466. },
  3467. {
  3468. name: "AzureFile empty secret",
  3469. vol: core.Volume{
  3470. Name: "azure-file",
  3471. VolumeSource: core.VolumeSource{
  3472. AzureFile: &core.AzureFileVolumeSource{
  3473. SecretName: "",
  3474. ShareName: "share",
  3475. ReadOnly: false,
  3476. },
  3477. },
  3478. },
  3479. errs: []verr{{
  3480. etype: field.ErrorTypeRequired,
  3481. field: "azureFile.secretName",
  3482. }},
  3483. },
  3484. {
  3485. name: "AzureFile empty share",
  3486. vol: core.Volume{
  3487. Name: "azure-file",
  3488. VolumeSource: core.VolumeSource{
  3489. AzureFile: &core.AzureFileVolumeSource{
  3490. SecretName: "name",
  3491. ShareName: "",
  3492. ReadOnly: false,
  3493. },
  3494. },
  3495. },
  3496. errs: []verr{{
  3497. etype: field.ErrorTypeRequired,
  3498. field: "azureFile.shareName",
  3499. }},
  3500. },
  3501. // Quobyte
  3502. {
  3503. name: "valid Quobyte",
  3504. vol: core.Volume{
  3505. Name: "quobyte",
  3506. VolumeSource: core.VolumeSource{
  3507. Quobyte: &core.QuobyteVolumeSource{
  3508. Registry: "registry:7861",
  3509. Volume: "volume",
  3510. ReadOnly: false,
  3511. User: "root",
  3512. Group: "root",
  3513. Tenant: "ThisIsSomeTenantUUID",
  3514. },
  3515. },
  3516. },
  3517. },
  3518. {
  3519. name: "empty registry quobyte",
  3520. vol: core.Volume{
  3521. Name: "quobyte",
  3522. VolumeSource: core.VolumeSource{
  3523. Quobyte: &core.QuobyteVolumeSource{
  3524. Volume: "/test",
  3525. Tenant: "ThisIsSomeTenantUUID",
  3526. },
  3527. },
  3528. },
  3529. errs: []verr{{
  3530. etype: field.ErrorTypeRequired,
  3531. field: "quobyte.registry",
  3532. }},
  3533. },
  3534. {
  3535. name: "wrong format registry quobyte",
  3536. vol: core.Volume{
  3537. Name: "quobyte",
  3538. VolumeSource: core.VolumeSource{
  3539. Quobyte: &core.QuobyteVolumeSource{
  3540. Registry: "registry7861",
  3541. Volume: "/test",
  3542. Tenant: "ThisIsSomeTenantUUID",
  3543. },
  3544. },
  3545. },
  3546. errs: []verr{{
  3547. etype: field.ErrorTypeInvalid,
  3548. field: "quobyte.registry",
  3549. }},
  3550. },
  3551. {
  3552. name: "wrong format multiple registries quobyte",
  3553. vol: core.Volume{
  3554. Name: "quobyte",
  3555. VolumeSource: core.VolumeSource{
  3556. Quobyte: &core.QuobyteVolumeSource{
  3557. Registry: "registry:7861,reg2",
  3558. Volume: "/test",
  3559. Tenant: "ThisIsSomeTenantUUID",
  3560. },
  3561. },
  3562. },
  3563. errs: []verr{{
  3564. etype: field.ErrorTypeInvalid,
  3565. field: "quobyte.registry",
  3566. }},
  3567. },
  3568. {
  3569. name: "empty volume quobyte",
  3570. vol: core.Volume{
  3571. Name: "quobyte",
  3572. VolumeSource: core.VolumeSource{
  3573. Quobyte: &core.QuobyteVolumeSource{
  3574. Registry: "registry:7861",
  3575. Tenant: "ThisIsSomeTenantUUID",
  3576. },
  3577. },
  3578. },
  3579. errs: []verr{{
  3580. etype: field.ErrorTypeRequired,
  3581. field: "quobyte.volume",
  3582. }},
  3583. },
  3584. {
  3585. name: "empty tenant quobyte",
  3586. vol: core.Volume{
  3587. Name: "quobyte",
  3588. VolumeSource: core.VolumeSource{
  3589. Quobyte: &core.QuobyteVolumeSource{
  3590. Registry: "registry:7861",
  3591. Volume: "/test",
  3592. Tenant: "",
  3593. },
  3594. },
  3595. },
  3596. },
  3597. {
  3598. name: "too long tenant quobyte",
  3599. vol: core.Volume{
  3600. Name: "quobyte",
  3601. VolumeSource: core.VolumeSource{
  3602. Quobyte: &core.QuobyteVolumeSource{
  3603. Registry: "registry:7861",
  3604. Volume: "/test",
  3605. Tenant: "this is too long to be a valid uuid so this test has to fail on the maximum length validation of the tenant.",
  3606. },
  3607. },
  3608. },
  3609. errs: []verr{{
  3610. etype: field.ErrorTypeRequired,
  3611. field: "quobyte.tenant",
  3612. }},
  3613. },
  3614. // AzureDisk
  3615. {
  3616. name: "valid AzureDisk",
  3617. vol: core.Volume{
  3618. Name: "azure-disk",
  3619. VolumeSource: core.VolumeSource{
  3620. AzureDisk: &core.AzureDiskVolumeSource{
  3621. DiskName: "foo",
  3622. DataDiskURI: "https://blob/vhds/bar.vhd",
  3623. },
  3624. },
  3625. },
  3626. },
  3627. {
  3628. name: "AzureDisk empty disk name",
  3629. vol: core.Volume{
  3630. Name: "azure-disk",
  3631. VolumeSource: core.VolumeSource{
  3632. AzureDisk: &core.AzureDiskVolumeSource{
  3633. DiskName: "",
  3634. DataDiskURI: "https://blob/vhds/bar.vhd",
  3635. },
  3636. },
  3637. },
  3638. errs: []verr{{
  3639. etype: field.ErrorTypeRequired,
  3640. field: "azureDisk.diskName",
  3641. }},
  3642. },
  3643. {
  3644. name: "AzureDisk empty disk uri",
  3645. vol: core.Volume{
  3646. Name: "azure-disk",
  3647. VolumeSource: core.VolumeSource{
  3648. AzureDisk: &core.AzureDiskVolumeSource{
  3649. DiskName: "foo",
  3650. DataDiskURI: "",
  3651. },
  3652. },
  3653. },
  3654. errs: []verr{{
  3655. etype: field.ErrorTypeRequired,
  3656. field: "azureDisk.diskURI",
  3657. }},
  3658. },
  3659. // ScaleIO
  3660. {
  3661. name: "valid scaleio volume",
  3662. vol: core.Volume{
  3663. Name: "scaleio-volume",
  3664. VolumeSource: core.VolumeSource{
  3665. ScaleIO: &core.ScaleIOVolumeSource{
  3666. Gateway: "http://abcd/efg",
  3667. System: "test-system",
  3668. VolumeName: "test-vol-1",
  3669. },
  3670. },
  3671. },
  3672. },
  3673. {
  3674. name: "ScaleIO with empty name",
  3675. vol: core.Volume{
  3676. Name: "scaleio-volume",
  3677. VolumeSource: core.VolumeSource{
  3678. ScaleIO: &core.ScaleIOVolumeSource{
  3679. Gateway: "http://abcd/efg",
  3680. System: "test-system",
  3681. VolumeName: "",
  3682. },
  3683. },
  3684. },
  3685. errs: []verr{{
  3686. etype: field.ErrorTypeRequired,
  3687. field: "scaleIO.volumeName",
  3688. }},
  3689. },
  3690. {
  3691. name: "ScaleIO with empty gateway",
  3692. vol: core.Volume{
  3693. Name: "scaleio-volume",
  3694. VolumeSource: core.VolumeSource{
  3695. ScaleIO: &core.ScaleIOVolumeSource{
  3696. Gateway: "",
  3697. System: "test-system",
  3698. VolumeName: "test-vol-1",
  3699. },
  3700. },
  3701. },
  3702. errs: []verr{{
  3703. etype: field.ErrorTypeRequired,
  3704. field: "scaleIO.gateway",
  3705. }},
  3706. },
  3707. {
  3708. name: "ScaleIO with empty system",
  3709. vol: core.Volume{
  3710. Name: "scaleio-volume",
  3711. VolumeSource: core.VolumeSource{
  3712. ScaleIO: &core.ScaleIOVolumeSource{
  3713. Gateway: "http://agc/efg/gateway",
  3714. System: "",
  3715. VolumeName: "test-vol-1",
  3716. },
  3717. },
  3718. },
  3719. errs: []verr{{
  3720. etype: field.ErrorTypeRequired,
  3721. field: "scaleIO.system",
  3722. }},
  3723. },
  3724. // ProjectedVolumeSource
  3725. {
  3726. name: "ProjectedVolumeSource more than one projection in a source",
  3727. vol: core.Volume{
  3728. Name: "projected-volume",
  3729. VolumeSource: core.VolumeSource{
  3730. Projected: &core.ProjectedVolumeSource{
  3731. Sources: []core.VolumeProjection{
  3732. {
  3733. Secret: &core.SecretProjection{
  3734. LocalObjectReference: core.LocalObjectReference{
  3735. Name: "foo",
  3736. },
  3737. },
  3738. },
  3739. {
  3740. Secret: &core.SecretProjection{
  3741. LocalObjectReference: core.LocalObjectReference{
  3742. Name: "foo",
  3743. },
  3744. },
  3745. DownwardAPI: &core.DownwardAPIProjection{},
  3746. },
  3747. },
  3748. },
  3749. },
  3750. },
  3751. errs: []verr{{
  3752. etype: field.ErrorTypeForbidden,
  3753. field: "projected.sources[1]",
  3754. }},
  3755. },
  3756. {
  3757. name: "ProjectedVolumeSource more than one projection in a source",
  3758. vol: core.Volume{
  3759. Name: "projected-volume",
  3760. VolumeSource: core.VolumeSource{
  3761. Projected: &core.ProjectedVolumeSource{
  3762. Sources: []core.VolumeProjection{
  3763. {
  3764. Secret: &core.SecretProjection{},
  3765. },
  3766. {
  3767. Secret: &core.SecretProjection{},
  3768. DownwardAPI: &core.DownwardAPIProjection{},
  3769. },
  3770. },
  3771. },
  3772. },
  3773. },
  3774. errs: []verr{
  3775. {
  3776. etype: field.ErrorTypeRequired,
  3777. field: "projected.sources[0].secret.name",
  3778. },
  3779. {
  3780. etype: field.ErrorTypeRequired,
  3781. field: "projected.sources[1].secret.name",
  3782. },
  3783. {
  3784. etype: field.ErrorTypeForbidden,
  3785. field: "projected.sources[1]",
  3786. },
  3787. },
  3788. },
  3789. }
  3790. for _, tc := range testCases {
  3791. t.Run(tc.name, func(t *testing.T) {
  3792. names, errs := ValidateVolumes([]core.Volume{tc.vol}, field.NewPath("field"))
  3793. if len(errs) != len(tc.errs) {
  3794. t.Fatalf("unexpected error(s): got %d, want %d: %v", len(tc.errs), len(errs), errs)
  3795. }
  3796. if len(errs) == 0 && (len(names) > 1 || !IsMatchedVolume(tc.vol.Name, names)) {
  3797. t.Errorf("wrong names result: %v", names)
  3798. }
  3799. for i, err := range errs {
  3800. expErr := tc.errs[i]
  3801. if err.Type != expErr.etype {
  3802. t.Errorf("unexpected error type: got %v, want %v", expErr.etype, err.Type)
  3803. }
  3804. if !strings.HasSuffix(err.Field, "."+expErr.field) {
  3805. t.Errorf("unexpected error field: got %v, want %v", expErr.field, err.Field)
  3806. }
  3807. if !strings.Contains(err.Detail, expErr.detail) {
  3808. t.Errorf("unexpected error detail: got %v, want %v", expErr.detail, err.Detail)
  3809. }
  3810. }
  3811. })
  3812. }
  3813. dupsCase := []core.Volume{
  3814. {Name: "abc", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}},
  3815. {Name: "abc", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}},
  3816. }
  3817. _, errs := ValidateVolumes(dupsCase, field.NewPath("field"))
  3818. if len(errs) == 0 {
  3819. t.Errorf("expected error")
  3820. } else if len(errs) != 1 {
  3821. t.Errorf("expected 1 error, got %d: %v", len(errs), errs)
  3822. } else if errs[0].Type != field.ErrorTypeDuplicate {
  3823. t.Errorf("expected error type %v, got %v", field.ErrorTypeDuplicate, errs[0].Type)
  3824. }
  3825. // Validate HugePages medium type for EmptyDir
  3826. hugePagesCase := core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{Medium: core.StorageMediumHugePages}}
  3827. // Enable HugePages
  3828. if errs := validateVolumeSource(&hugePagesCase, field.NewPath("field").Index(0), "working"); len(errs) != 0 {
  3829. t.Errorf("Unexpected error when HugePages feature is enabled.")
  3830. }
  3831. }
  3832. func TestHugePagesIsolation(t *testing.T) {
  3833. successCases := []core.Pod{
  3834. { // Basic fields.
  3835. ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
  3836. Spec: core.PodSpec{
  3837. Containers: []core.Container{
  3838. {
  3839. Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
  3840. Resources: core.ResourceRequirements{
  3841. Requests: core.ResourceList{
  3842. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  3843. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  3844. core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
  3845. },
  3846. Limits: core.ResourceList{
  3847. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  3848. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  3849. core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
  3850. },
  3851. },
  3852. },
  3853. },
  3854. RestartPolicy: core.RestartPolicyAlways,
  3855. DNSPolicy: core.DNSClusterFirst,
  3856. },
  3857. },
  3858. }
  3859. failureCases := []core.Pod{
  3860. { // Basic fields.
  3861. ObjectMeta: metav1.ObjectMeta{Name: "hugepages-requireCpuOrMemory", Namespace: "ns"},
  3862. Spec: core.PodSpec{
  3863. Containers: []core.Container{
  3864. {
  3865. Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
  3866. Resources: core.ResourceRequirements{
  3867. Requests: core.ResourceList{
  3868. core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
  3869. },
  3870. Limits: core.ResourceList{
  3871. core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
  3872. },
  3873. },
  3874. },
  3875. },
  3876. RestartPolicy: core.RestartPolicyAlways,
  3877. DNSPolicy: core.DNSClusterFirst,
  3878. },
  3879. },
  3880. { // Basic fields.
  3881. ObjectMeta: metav1.ObjectMeta{Name: "hugepages-shared", Namespace: "ns"},
  3882. Spec: core.PodSpec{
  3883. Containers: []core.Container{
  3884. {
  3885. Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
  3886. Resources: core.ResourceRequirements{
  3887. Requests: core.ResourceList{
  3888. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  3889. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  3890. core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
  3891. },
  3892. Limits: core.ResourceList{
  3893. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  3894. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  3895. core.ResourceName("hugepages-2Mi"): resource.MustParse("2Gi"),
  3896. },
  3897. },
  3898. },
  3899. },
  3900. RestartPolicy: core.RestartPolicyAlways,
  3901. DNSPolicy: core.DNSClusterFirst,
  3902. },
  3903. },
  3904. { // Basic fields.
  3905. ObjectMeta: metav1.ObjectMeta{Name: "hugepages-multiple", Namespace: "ns"},
  3906. Spec: core.PodSpec{
  3907. Containers: []core.Container{
  3908. {
  3909. Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
  3910. Resources: core.ResourceRequirements{
  3911. Requests: core.ResourceList{
  3912. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  3913. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  3914. core.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
  3915. },
  3916. Limits: core.ResourceList{
  3917. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  3918. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  3919. core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
  3920. core.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
  3921. },
  3922. },
  3923. },
  3924. },
  3925. RestartPolicy: core.RestartPolicyAlways,
  3926. DNSPolicy: core.DNSClusterFirst,
  3927. },
  3928. },
  3929. }
  3930. for i := range successCases {
  3931. pod := &successCases[i]
  3932. if errs := ValidatePod(pod); len(errs) != 0 {
  3933. t.Errorf("Unexpected error for case[%d], err: %v", i, errs)
  3934. }
  3935. }
  3936. for i := range failureCases {
  3937. pod := &failureCases[i]
  3938. if errs := ValidatePod(pod); len(errs) == 0 {
  3939. t.Errorf("Expected error for case[%d], pod: %v", i, pod.Name)
  3940. }
  3941. }
  3942. }
  3943. func TestPVCVolumeMode(t *testing.T) {
  3944. // Enable feature BlockVolume for PVC
  3945. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.BlockVolume, true)()
  3946. block := core.PersistentVolumeBlock
  3947. file := core.PersistentVolumeFilesystem
  3948. fake := core.PersistentVolumeMode("fake")
  3949. empty := core.PersistentVolumeMode("")
  3950. // Success Cases
  3951. successCasesPVC := map[string]*core.PersistentVolumeClaim{
  3952. "valid block value": createTestVolModePVC(&block),
  3953. "valid filesystem value": createTestVolModePVC(&file),
  3954. "valid nil value": createTestVolModePVC(nil),
  3955. }
  3956. for k, v := range successCasesPVC {
  3957. if errs := ValidatePersistentVolumeClaim(v); len(errs) != 0 {
  3958. t.Errorf("expected success for %s", k)
  3959. }
  3960. }
  3961. // Error Cases
  3962. errorCasesPVC := map[string]*core.PersistentVolumeClaim{
  3963. "invalid value": createTestVolModePVC(&fake),
  3964. "empty value": createTestVolModePVC(&empty),
  3965. }
  3966. for k, v := range errorCasesPVC {
  3967. if errs := ValidatePersistentVolumeClaim(v); len(errs) == 0 {
  3968. t.Errorf("expected failure for %s", k)
  3969. }
  3970. }
  3971. }
  3972. func TestPVVolumeMode(t *testing.T) {
  3973. // Enable feature BlockVolume for PVC
  3974. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.BlockVolume, true)()
  3975. block := core.PersistentVolumeBlock
  3976. file := core.PersistentVolumeFilesystem
  3977. fake := core.PersistentVolumeMode("fake")
  3978. empty := core.PersistentVolumeMode("")
  3979. // Success Cases
  3980. successCasesPV := map[string]*core.PersistentVolume{
  3981. "valid block value": createTestVolModePV(&block),
  3982. "valid filesystem value": createTestVolModePV(&file),
  3983. "valid nil value": createTestVolModePV(nil),
  3984. }
  3985. for k, v := range successCasesPV {
  3986. if errs := ValidatePersistentVolume(v); len(errs) != 0 {
  3987. t.Errorf("expected success for %s", k)
  3988. }
  3989. }
  3990. // Error Cases
  3991. errorCasesPV := map[string]*core.PersistentVolume{
  3992. "invalid value": createTestVolModePV(&fake),
  3993. "empty value": createTestVolModePV(&empty),
  3994. }
  3995. for k, v := range errorCasesPV {
  3996. if errs := ValidatePersistentVolume(v); len(errs) == 0 {
  3997. t.Errorf("expected failure for %s", k)
  3998. }
  3999. }
  4000. }
  4001. func createTestVolModePVC(vmode *core.PersistentVolumeMode) *core.PersistentVolumeClaim {
  4002. validName := "valid-storage-class"
  4003. pvc := core.PersistentVolumeClaim{
  4004. ObjectMeta: metav1.ObjectMeta{
  4005. Name: "foo",
  4006. Namespace: "default",
  4007. },
  4008. Spec: core.PersistentVolumeClaimSpec{
  4009. Resources: core.ResourceRequirements{
  4010. Requests: core.ResourceList{
  4011. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  4012. },
  4013. },
  4014. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  4015. StorageClassName: &validName,
  4016. VolumeMode: vmode,
  4017. },
  4018. }
  4019. return &pvc
  4020. }
  4021. func createTestVolModePV(vmode *core.PersistentVolumeMode) *core.PersistentVolume {
  4022. // PersistentVolume with VolumeMode set (valid and invalid)
  4023. pv := core.PersistentVolume{
  4024. ObjectMeta: metav1.ObjectMeta{
  4025. Name: "foo",
  4026. Namespace: "",
  4027. },
  4028. Spec: core.PersistentVolumeSpec{
  4029. Capacity: core.ResourceList{
  4030. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  4031. },
  4032. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  4033. PersistentVolumeSource: core.PersistentVolumeSource{
  4034. HostPath: &core.HostPathVolumeSource{
  4035. Path: "/foo",
  4036. Type: newHostPathType(string(core.HostPathDirectory)),
  4037. },
  4038. },
  4039. StorageClassName: "test-storage-class",
  4040. VolumeMode: vmode,
  4041. },
  4042. }
  4043. return &pv
  4044. }
  4045. func createTestPV() *core.PersistentVolume {
  4046. // PersistentVolume with VolumeMode set (valid and invalid)
  4047. pv := core.PersistentVolume{
  4048. ObjectMeta: metav1.ObjectMeta{
  4049. Name: "foo",
  4050. Namespace: "",
  4051. },
  4052. Spec: core.PersistentVolumeSpec{
  4053. Capacity: core.ResourceList{
  4054. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  4055. },
  4056. AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
  4057. PersistentVolumeSource: core.PersistentVolumeSource{
  4058. HostPath: &core.HostPathVolumeSource{
  4059. Path: "/foo",
  4060. Type: newHostPathType(string(core.HostPathDirectory)),
  4061. },
  4062. },
  4063. StorageClassName: "test-storage-class",
  4064. },
  4065. }
  4066. return &pv
  4067. }
  4068. func TestAlphaLocalStorageCapacityIsolation(t *testing.T) {
  4069. testCases := []core.VolumeSource{
  4070. {EmptyDir: &core.EmptyDirVolumeSource{SizeLimit: resource.NewQuantity(int64(5), resource.BinarySI)}},
  4071. }
  4072. for _, tc := range testCases {
  4073. if errs := validateVolumeSource(&tc, field.NewPath("spec"), "tmpvol"); len(errs) != 0 {
  4074. t.Errorf("expected success: %v", errs)
  4075. }
  4076. }
  4077. containerLimitCase := core.ResourceRequirements{
  4078. Limits: core.ResourceList{
  4079. core.ResourceEphemeralStorage: *resource.NewMilliQuantity(
  4080. int64(40000),
  4081. resource.BinarySI),
  4082. },
  4083. }
  4084. if errs := ValidateResourceRequirements(&containerLimitCase, field.NewPath("resources")); len(errs) != 0 {
  4085. t.Errorf("expected success: %v", errs)
  4086. }
  4087. }
  4088. func TestValidateResourceQuotaWithAlphaLocalStorageCapacityIsolation(t *testing.T) {
  4089. spec := core.ResourceQuotaSpec{
  4090. Hard: core.ResourceList{
  4091. core.ResourceCPU: resource.MustParse("100"),
  4092. core.ResourceMemory: resource.MustParse("10000"),
  4093. core.ResourceRequestsCPU: resource.MustParse("100"),
  4094. core.ResourceRequestsMemory: resource.MustParse("10000"),
  4095. core.ResourceLimitsCPU: resource.MustParse("100"),
  4096. core.ResourceLimitsMemory: resource.MustParse("10000"),
  4097. core.ResourcePods: resource.MustParse("10"),
  4098. core.ResourceServices: resource.MustParse("0"),
  4099. core.ResourceReplicationControllers: resource.MustParse("10"),
  4100. core.ResourceQuotas: resource.MustParse("10"),
  4101. core.ResourceConfigMaps: resource.MustParse("10"),
  4102. core.ResourceSecrets: resource.MustParse("10"),
  4103. core.ResourceEphemeralStorage: resource.MustParse("10000"),
  4104. core.ResourceRequestsEphemeralStorage: resource.MustParse("10000"),
  4105. core.ResourceLimitsEphemeralStorage: resource.MustParse("10000"),
  4106. },
  4107. }
  4108. resourceQuota := &core.ResourceQuota{
  4109. ObjectMeta: metav1.ObjectMeta{
  4110. Name: "abc",
  4111. Namespace: "foo",
  4112. },
  4113. Spec: spec,
  4114. }
  4115. if errs := ValidateResourceQuota(resourceQuota); len(errs) != 0 {
  4116. t.Errorf("expected success: %v", errs)
  4117. }
  4118. }
  4119. func TestValidatePorts(t *testing.T) {
  4120. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SCTPSupport, true)()
  4121. successCase := []core.ContainerPort{
  4122. {Name: "abc", ContainerPort: 80, HostPort: 80, Protocol: "TCP"},
  4123. {Name: "easy", ContainerPort: 82, Protocol: "TCP"},
  4124. {Name: "as", ContainerPort: 83, Protocol: "UDP"},
  4125. {Name: "do-re-me", ContainerPort: 84, Protocol: "UDP"},
  4126. {ContainerPort: 85, Protocol: "TCP"},
  4127. }
  4128. if errs := validateContainerPorts(successCase, field.NewPath("field")); len(errs) != 0 {
  4129. t.Errorf("expected success: %v", errs)
  4130. }
  4131. nonCanonicalCase := []core.ContainerPort{
  4132. {ContainerPort: 80, Protocol: "TCP"},
  4133. }
  4134. if errs := validateContainerPorts(nonCanonicalCase, field.NewPath("field")); len(errs) != 0 {
  4135. t.Errorf("expected success: %v", errs)
  4136. }
  4137. errorCases := map[string]struct {
  4138. P []core.ContainerPort
  4139. T field.ErrorType
  4140. F string
  4141. D string
  4142. }{
  4143. "name > 15 characters": {
  4144. []core.ContainerPort{{Name: strings.Repeat("a", 16), ContainerPort: 80, Protocol: "TCP"}},
  4145. field.ErrorTypeInvalid,
  4146. "name", "15",
  4147. },
  4148. "name contains invalid characters": {
  4149. []core.ContainerPort{{Name: "a.b.c", ContainerPort: 80, Protocol: "TCP"}},
  4150. field.ErrorTypeInvalid,
  4151. "name", "alpha-numeric",
  4152. },
  4153. "name is a number": {
  4154. []core.ContainerPort{{Name: "80", ContainerPort: 80, Protocol: "TCP"}},
  4155. field.ErrorTypeInvalid,
  4156. "name", "at least one letter",
  4157. },
  4158. "name not unique": {
  4159. []core.ContainerPort{
  4160. {Name: "abc", ContainerPort: 80, Protocol: "TCP"},
  4161. {Name: "abc", ContainerPort: 81, Protocol: "TCP"},
  4162. },
  4163. field.ErrorTypeDuplicate,
  4164. "[1].name", "",
  4165. },
  4166. "zero container port": {
  4167. []core.ContainerPort{{ContainerPort: 0, Protocol: "TCP"}},
  4168. field.ErrorTypeRequired,
  4169. "containerPort", "",
  4170. },
  4171. "invalid container port": {
  4172. []core.ContainerPort{{ContainerPort: 65536, Protocol: "TCP"}},
  4173. field.ErrorTypeInvalid,
  4174. "containerPort", "between",
  4175. },
  4176. "invalid host port": {
  4177. []core.ContainerPort{{ContainerPort: 80, HostPort: 65536, Protocol: "TCP"}},
  4178. field.ErrorTypeInvalid,
  4179. "hostPort", "between",
  4180. },
  4181. "invalid protocol case": {
  4182. []core.ContainerPort{{ContainerPort: 80, Protocol: "tcp"}},
  4183. field.ErrorTypeNotSupported,
  4184. "protocol", `supported values: "SCTP", "TCP", "UDP"`,
  4185. },
  4186. "invalid protocol": {
  4187. []core.ContainerPort{{ContainerPort: 80, Protocol: "ICMP"}},
  4188. field.ErrorTypeNotSupported,
  4189. "protocol", `supported values: "SCTP", "TCP", "UDP"`,
  4190. },
  4191. "protocol required": {
  4192. []core.ContainerPort{{Name: "abc", ContainerPort: 80}},
  4193. field.ErrorTypeRequired,
  4194. "protocol", "",
  4195. },
  4196. }
  4197. for k, v := range errorCases {
  4198. errs := validateContainerPorts(v.P, field.NewPath("field"))
  4199. if len(errs) == 0 {
  4200. t.Errorf("expected failure for %s", k)
  4201. }
  4202. for i := range errs {
  4203. if errs[i].Type != v.T {
  4204. t.Errorf("%s: expected error to have type %q: %q", k, v.T, errs[i].Type)
  4205. }
  4206. if !strings.Contains(errs[i].Field, v.F) {
  4207. t.Errorf("%s: expected error field %q: %q", k, v.F, errs[i].Field)
  4208. }
  4209. if !strings.Contains(errs[i].Detail, v.D) {
  4210. t.Errorf("%s: expected error detail %q, got %q", k, v.D, errs[i].Detail)
  4211. }
  4212. }
  4213. }
  4214. }
  4215. func TestLocalStorageEnvWithFeatureGate(t *testing.T) {
  4216. testCases := []core.EnvVar{
  4217. {
  4218. Name: "ephemeral-storage-limits",
  4219. ValueFrom: &core.EnvVarSource{
  4220. ResourceFieldRef: &core.ResourceFieldSelector{
  4221. ContainerName: "test-container",
  4222. Resource: "limits.ephemeral-storage",
  4223. },
  4224. },
  4225. },
  4226. {
  4227. Name: "ephemeral-storage-requests",
  4228. ValueFrom: &core.EnvVarSource{
  4229. ResourceFieldRef: &core.ResourceFieldSelector{
  4230. ContainerName: "test-container",
  4231. Resource: "requests.ephemeral-storage",
  4232. },
  4233. },
  4234. },
  4235. }
  4236. for _, testCase := range testCases {
  4237. if errs := validateEnvVarValueFrom(testCase, field.NewPath("field")); len(errs) != 0 {
  4238. t.Errorf("expected success, got: %v", errs)
  4239. }
  4240. }
  4241. }
  4242. func TestValidateEnv(t *testing.T) {
  4243. successCase := []core.EnvVar{
  4244. {Name: "abc", Value: "value"},
  4245. {Name: "ABC", Value: "value"},
  4246. {Name: "AbC_123", Value: "value"},
  4247. {Name: "abc", Value: ""},
  4248. {Name: "a.b.c", Value: "value"},
  4249. {Name: "a-b-c", Value: "value"},
  4250. {
  4251. Name: "abc",
  4252. ValueFrom: &core.EnvVarSource{
  4253. FieldRef: &core.ObjectFieldSelector{
  4254. APIVersion: "v1",
  4255. FieldPath: "metadata.annotations['key']",
  4256. },
  4257. },
  4258. },
  4259. {
  4260. Name: "abc",
  4261. ValueFrom: &core.EnvVarSource{
  4262. FieldRef: &core.ObjectFieldSelector{
  4263. APIVersion: "v1",
  4264. FieldPath: "metadata.labels['key']",
  4265. },
  4266. },
  4267. },
  4268. {
  4269. Name: "abc",
  4270. ValueFrom: &core.EnvVarSource{
  4271. FieldRef: &core.ObjectFieldSelector{
  4272. APIVersion: "v1",
  4273. FieldPath: "metadata.name",
  4274. },
  4275. },
  4276. },
  4277. {
  4278. Name: "abc",
  4279. ValueFrom: &core.EnvVarSource{
  4280. FieldRef: &core.ObjectFieldSelector{
  4281. APIVersion: "v1",
  4282. FieldPath: "metadata.namespace",
  4283. },
  4284. },
  4285. },
  4286. {
  4287. Name: "abc",
  4288. ValueFrom: &core.EnvVarSource{
  4289. FieldRef: &core.ObjectFieldSelector{
  4290. APIVersion: "v1",
  4291. FieldPath: "metadata.uid",
  4292. },
  4293. },
  4294. },
  4295. {
  4296. Name: "abc",
  4297. ValueFrom: &core.EnvVarSource{
  4298. FieldRef: &core.ObjectFieldSelector{
  4299. APIVersion: "v1",
  4300. FieldPath: "spec.nodeName",
  4301. },
  4302. },
  4303. },
  4304. {
  4305. Name: "abc",
  4306. ValueFrom: &core.EnvVarSource{
  4307. FieldRef: &core.ObjectFieldSelector{
  4308. APIVersion: "v1",
  4309. FieldPath: "spec.serviceAccountName",
  4310. },
  4311. },
  4312. },
  4313. {
  4314. Name: "abc",
  4315. ValueFrom: &core.EnvVarSource{
  4316. FieldRef: &core.ObjectFieldSelector{
  4317. APIVersion: "v1",
  4318. FieldPath: "status.hostIP",
  4319. },
  4320. },
  4321. },
  4322. {
  4323. Name: "abc",
  4324. ValueFrom: &core.EnvVarSource{
  4325. FieldRef: &core.ObjectFieldSelector{
  4326. APIVersion: "v1",
  4327. FieldPath: "status.podIP",
  4328. },
  4329. },
  4330. },
  4331. {
  4332. Name: "abc",
  4333. ValueFrom: &core.EnvVarSource{
  4334. FieldRef: &core.ObjectFieldSelector{
  4335. APIVersion: "v1",
  4336. FieldPath: "status.podIPs",
  4337. },
  4338. },
  4339. },
  4340. {
  4341. Name: "secret_value",
  4342. ValueFrom: &core.EnvVarSource{
  4343. SecretKeyRef: &core.SecretKeySelector{
  4344. LocalObjectReference: core.LocalObjectReference{
  4345. Name: "some-secret",
  4346. },
  4347. Key: "secret-key",
  4348. },
  4349. },
  4350. },
  4351. {
  4352. Name: "ENV_VAR_1",
  4353. ValueFrom: &core.EnvVarSource{
  4354. ConfigMapKeyRef: &core.ConfigMapKeySelector{
  4355. LocalObjectReference: core.LocalObjectReference{
  4356. Name: "some-config-map",
  4357. },
  4358. Key: "some-key",
  4359. },
  4360. },
  4361. },
  4362. }
  4363. if errs := ValidateEnv(successCase, field.NewPath("field")); len(errs) != 0 {
  4364. t.Errorf("expected success, got: %v", errs)
  4365. }
  4366. errorCases := []struct {
  4367. name string
  4368. envs []core.EnvVar
  4369. expectedError string
  4370. }{
  4371. {
  4372. name: "zero-length name",
  4373. envs: []core.EnvVar{{Name: ""}},
  4374. expectedError: "[0].name: Required value",
  4375. },
  4376. {
  4377. name: "illegal character",
  4378. envs: []core.EnvVar{{Name: "a!b"}},
  4379. expectedError: `[0].name: Invalid value: "a!b": ` + envVarNameErrMsg,
  4380. },
  4381. {
  4382. name: "dot only",
  4383. envs: []core.EnvVar{{Name: "."}},
  4384. expectedError: `[0].name: Invalid value: ".": must not be`,
  4385. },
  4386. {
  4387. name: "double dots only",
  4388. envs: []core.EnvVar{{Name: ".."}},
  4389. expectedError: `[0].name: Invalid value: "..": must not be`,
  4390. },
  4391. {
  4392. name: "leading double dots",
  4393. envs: []core.EnvVar{{Name: "..abc"}},
  4394. expectedError: `[0].name: Invalid value: "..abc": must not start with`,
  4395. },
  4396. {
  4397. name: "value and valueFrom specified",
  4398. envs: []core.EnvVar{{
  4399. Name: "abc",
  4400. Value: "foo",
  4401. ValueFrom: &core.EnvVarSource{
  4402. FieldRef: &core.ObjectFieldSelector{
  4403. APIVersion: "v1",
  4404. FieldPath: "metadata.name",
  4405. },
  4406. },
  4407. }},
  4408. expectedError: "[0].valueFrom: Invalid value: \"\": may not be specified when `value` is not empty",
  4409. },
  4410. {
  4411. name: "valueFrom without a source",
  4412. envs: []core.EnvVar{{
  4413. Name: "abc",
  4414. ValueFrom: &core.EnvVarSource{},
  4415. }},
  4416. expectedError: "[0].valueFrom: Invalid value: \"\": must specify one of: `fieldRef`, `resourceFieldRef`, `configMapKeyRef` or `secretKeyRef`",
  4417. },
  4418. {
  4419. name: "valueFrom.fieldRef and valueFrom.secretKeyRef specified",
  4420. envs: []core.EnvVar{{
  4421. Name: "abc",
  4422. ValueFrom: &core.EnvVarSource{
  4423. FieldRef: &core.ObjectFieldSelector{
  4424. APIVersion: "v1",
  4425. FieldPath: "metadata.name",
  4426. },
  4427. SecretKeyRef: &core.SecretKeySelector{
  4428. LocalObjectReference: core.LocalObjectReference{
  4429. Name: "a-secret",
  4430. },
  4431. Key: "a-key",
  4432. },
  4433. },
  4434. }},
  4435. expectedError: "[0].valueFrom: Invalid value: \"\": may not have more than one field specified at a time",
  4436. },
  4437. {
  4438. name: "valueFrom.fieldRef and valueFrom.configMapKeyRef set",
  4439. envs: []core.EnvVar{{
  4440. Name: "some_var_name",
  4441. ValueFrom: &core.EnvVarSource{
  4442. FieldRef: &core.ObjectFieldSelector{
  4443. APIVersion: "v1",
  4444. FieldPath: "metadata.name",
  4445. },
  4446. ConfigMapKeyRef: &core.ConfigMapKeySelector{
  4447. LocalObjectReference: core.LocalObjectReference{
  4448. Name: "some-config-map",
  4449. },
  4450. Key: "some-key",
  4451. },
  4452. },
  4453. }},
  4454. expectedError: `[0].valueFrom: Invalid value: "": may not have more than one field specified at a time`,
  4455. },
  4456. {
  4457. name: "valueFrom.fieldRef and valueFrom.secretKeyRef specified",
  4458. envs: []core.EnvVar{{
  4459. Name: "abc",
  4460. ValueFrom: &core.EnvVarSource{
  4461. FieldRef: &core.ObjectFieldSelector{
  4462. APIVersion: "v1",
  4463. FieldPath: "metadata.name",
  4464. },
  4465. SecretKeyRef: &core.SecretKeySelector{
  4466. LocalObjectReference: core.LocalObjectReference{
  4467. Name: "a-secret",
  4468. },
  4469. Key: "a-key",
  4470. },
  4471. ConfigMapKeyRef: &core.ConfigMapKeySelector{
  4472. LocalObjectReference: core.LocalObjectReference{
  4473. Name: "some-config-map",
  4474. },
  4475. Key: "some-key",
  4476. },
  4477. },
  4478. }},
  4479. expectedError: `[0].valueFrom: Invalid value: "": may not have more than one field specified at a time`,
  4480. },
  4481. {
  4482. name: "valueFrom.secretKeyRef.name invalid",
  4483. envs: []core.EnvVar{{
  4484. Name: "abc",
  4485. ValueFrom: &core.EnvVarSource{
  4486. SecretKeyRef: &core.SecretKeySelector{
  4487. LocalObjectReference: core.LocalObjectReference{
  4488. Name: "$%^&*#",
  4489. },
  4490. Key: "a-key",
  4491. },
  4492. },
  4493. }},
  4494. },
  4495. {
  4496. name: "valueFrom.configMapKeyRef.name invalid",
  4497. envs: []core.EnvVar{{
  4498. Name: "abc",
  4499. ValueFrom: &core.EnvVarSource{
  4500. ConfigMapKeyRef: &core.ConfigMapKeySelector{
  4501. LocalObjectReference: core.LocalObjectReference{
  4502. Name: "$%^&*#",
  4503. },
  4504. Key: "some-key",
  4505. },
  4506. },
  4507. }},
  4508. },
  4509. {
  4510. name: "missing FieldPath on ObjectFieldSelector",
  4511. envs: []core.EnvVar{{
  4512. Name: "abc",
  4513. ValueFrom: &core.EnvVarSource{
  4514. FieldRef: &core.ObjectFieldSelector{
  4515. APIVersion: "v1",
  4516. },
  4517. },
  4518. }},
  4519. expectedError: `[0].valueFrom.fieldRef.fieldPath: Required value`,
  4520. },
  4521. {
  4522. name: "missing APIVersion on ObjectFieldSelector",
  4523. envs: []core.EnvVar{{
  4524. Name: "abc",
  4525. ValueFrom: &core.EnvVarSource{
  4526. FieldRef: &core.ObjectFieldSelector{
  4527. FieldPath: "metadata.name",
  4528. },
  4529. },
  4530. }},
  4531. expectedError: `[0].valueFrom.fieldRef.apiVersion: Required value`,
  4532. },
  4533. {
  4534. name: "invalid fieldPath",
  4535. envs: []core.EnvVar{{
  4536. Name: "abc",
  4537. ValueFrom: &core.EnvVarSource{
  4538. FieldRef: &core.ObjectFieldSelector{
  4539. FieldPath: "metadata.whoops",
  4540. APIVersion: "v1",
  4541. },
  4542. },
  4543. }},
  4544. expectedError: `[0].valueFrom.fieldRef.fieldPath: Invalid value: "metadata.whoops": error converting fieldPath`,
  4545. },
  4546. {
  4547. name: "metadata.name with subscript",
  4548. envs: []core.EnvVar{{
  4549. Name: "labels",
  4550. ValueFrom: &core.EnvVarSource{
  4551. FieldRef: &core.ObjectFieldSelector{
  4552. FieldPath: "metadata.name['key']",
  4553. APIVersion: "v1",
  4554. },
  4555. },
  4556. }},
  4557. expectedError: `[0].valueFrom.fieldRef.fieldPath: Invalid value: "metadata.name['key']": error converting fieldPath: field label does not support subscript`,
  4558. },
  4559. {
  4560. name: "metadata.labels without subscript",
  4561. envs: []core.EnvVar{{
  4562. Name: "labels",
  4563. ValueFrom: &core.EnvVarSource{
  4564. FieldRef: &core.ObjectFieldSelector{
  4565. FieldPath: "metadata.labels",
  4566. APIVersion: "v1",
  4567. },
  4568. },
  4569. }},
  4570. 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", "status.podIPs"`,
  4571. },
  4572. {
  4573. name: "metadata.annotations without subscript",
  4574. envs: []core.EnvVar{{
  4575. Name: "abc",
  4576. ValueFrom: &core.EnvVarSource{
  4577. FieldRef: &core.ObjectFieldSelector{
  4578. FieldPath: "metadata.annotations",
  4579. APIVersion: "v1",
  4580. },
  4581. },
  4582. }},
  4583. 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", "status.podIPs"`,
  4584. },
  4585. {
  4586. name: "metadata.annotations with invalid key",
  4587. envs: []core.EnvVar{{
  4588. Name: "abc",
  4589. ValueFrom: &core.EnvVarSource{
  4590. FieldRef: &core.ObjectFieldSelector{
  4591. FieldPath: "metadata.annotations['invalid~key']",
  4592. APIVersion: "v1",
  4593. },
  4594. },
  4595. }},
  4596. expectedError: `field[0].valueFrom.fieldRef: Invalid value: "invalid~key"`,
  4597. },
  4598. {
  4599. name: "metadata.labels with invalid key",
  4600. envs: []core.EnvVar{{
  4601. Name: "abc",
  4602. ValueFrom: &core.EnvVarSource{
  4603. FieldRef: &core.ObjectFieldSelector{
  4604. FieldPath: "metadata.labels['Www.k8s.io/test']",
  4605. APIVersion: "v1",
  4606. },
  4607. },
  4608. }},
  4609. expectedError: `field[0].valueFrom.fieldRef: Invalid value: "Www.k8s.io/test"`,
  4610. },
  4611. {
  4612. name: "unsupported fieldPath",
  4613. envs: []core.EnvVar{{
  4614. Name: "abc",
  4615. ValueFrom: &core.EnvVarSource{
  4616. FieldRef: &core.ObjectFieldSelector{
  4617. FieldPath: "status.phase",
  4618. APIVersion: "v1",
  4619. },
  4620. },
  4621. }},
  4622. expectedError: `valueFrom.fieldRef.fieldPath: Unsupported value: "status.phase": supported values: "metadata.name", "metadata.namespace", "metadata.uid", "spec.nodeName", "spec.serviceAccountName", "status.hostIP", "status.podIP", "status.podIPs"`,
  4623. },
  4624. }
  4625. for _, tc := range errorCases {
  4626. if errs := ValidateEnv(tc.envs, field.NewPath("field")); len(errs) == 0 {
  4627. t.Errorf("expected failure for %s", tc.name)
  4628. } else {
  4629. for i := range errs {
  4630. str := errs[i].Error()
  4631. if str != "" && !strings.Contains(str, tc.expectedError) {
  4632. t.Errorf("%s: expected error detail either empty or %q, got %q", tc.name, tc.expectedError, str)
  4633. }
  4634. }
  4635. }
  4636. }
  4637. }
  4638. func TestValidateEnvFrom(t *testing.T) {
  4639. successCase := []core.EnvFromSource{
  4640. {
  4641. ConfigMapRef: &core.ConfigMapEnvSource{
  4642. LocalObjectReference: core.LocalObjectReference{Name: "abc"},
  4643. },
  4644. },
  4645. {
  4646. Prefix: "pre_",
  4647. ConfigMapRef: &core.ConfigMapEnvSource{
  4648. LocalObjectReference: core.LocalObjectReference{Name: "abc"},
  4649. },
  4650. },
  4651. {
  4652. Prefix: "a.b",
  4653. ConfigMapRef: &core.ConfigMapEnvSource{
  4654. LocalObjectReference: core.LocalObjectReference{Name: "abc"},
  4655. },
  4656. },
  4657. {
  4658. SecretRef: &core.SecretEnvSource{
  4659. LocalObjectReference: core.LocalObjectReference{Name: "abc"},
  4660. },
  4661. },
  4662. {
  4663. Prefix: "pre_",
  4664. SecretRef: &core.SecretEnvSource{
  4665. LocalObjectReference: core.LocalObjectReference{Name: "abc"},
  4666. },
  4667. },
  4668. {
  4669. Prefix: "a.b",
  4670. SecretRef: &core.SecretEnvSource{
  4671. LocalObjectReference: core.LocalObjectReference{Name: "abc"},
  4672. },
  4673. },
  4674. }
  4675. if errs := ValidateEnvFrom(successCase, field.NewPath("field")); len(errs) != 0 {
  4676. t.Errorf("expected success: %v", errs)
  4677. }
  4678. errorCases := []struct {
  4679. name string
  4680. envs []core.EnvFromSource
  4681. expectedError string
  4682. }{
  4683. {
  4684. name: "zero-length name",
  4685. envs: []core.EnvFromSource{
  4686. {
  4687. ConfigMapRef: &core.ConfigMapEnvSource{
  4688. LocalObjectReference: core.LocalObjectReference{Name: ""}},
  4689. },
  4690. },
  4691. expectedError: "field[0].configMapRef.name: Required value",
  4692. },
  4693. {
  4694. name: "invalid name",
  4695. envs: []core.EnvFromSource{
  4696. {
  4697. ConfigMapRef: &core.ConfigMapEnvSource{
  4698. LocalObjectReference: core.LocalObjectReference{Name: "$"}},
  4699. },
  4700. },
  4701. expectedError: "field[0].configMapRef.name: Invalid value",
  4702. },
  4703. {
  4704. name: "invalid prefix",
  4705. envs: []core.EnvFromSource{
  4706. {
  4707. Prefix: "a!b",
  4708. ConfigMapRef: &core.ConfigMapEnvSource{
  4709. LocalObjectReference: core.LocalObjectReference{Name: "abc"}},
  4710. },
  4711. },
  4712. expectedError: `field[0].prefix: Invalid value: "a!b": ` + envVarNameErrMsg,
  4713. },
  4714. {
  4715. name: "zero-length name",
  4716. envs: []core.EnvFromSource{
  4717. {
  4718. SecretRef: &core.SecretEnvSource{
  4719. LocalObjectReference: core.LocalObjectReference{Name: ""}},
  4720. },
  4721. },
  4722. expectedError: "field[0].secretRef.name: Required value",
  4723. },
  4724. {
  4725. name: "invalid name",
  4726. envs: []core.EnvFromSource{
  4727. {
  4728. SecretRef: &core.SecretEnvSource{
  4729. LocalObjectReference: core.LocalObjectReference{Name: "&"}},
  4730. },
  4731. },
  4732. expectedError: "field[0].secretRef.name: Invalid value",
  4733. },
  4734. {
  4735. name: "invalid prefix",
  4736. envs: []core.EnvFromSource{
  4737. {
  4738. Prefix: "a!b",
  4739. SecretRef: &core.SecretEnvSource{
  4740. LocalObjectReference: core.LocalObjectReference{Name: "abc"}},
  4741. },
  4742. },
  4743. expectedError: `field[0].prefix: Invalid value: "a!b": ` + envVarNameErrMsg,
  4744. },
  4745. {
  4746. name: "no refs",
  4747. envs: []core.EnvFromSource{
  4748. {},
  4749. },
  4750. expectedError: "field: Invalid value: \"\": must specify one of: `configMapRef` or `secretRef`",
  4751. },
  4752. {
  4753. name: "multiple refs",
  4754. envs: []core.EnvFromSource{
  4755. {
  4756. SecretRef: &core.SecretEnvSource{
  4757. LocalObjectReference: core.LocalObjectReference{Name: "abc"}},
  4758. ConfigMapRef: &core.ConfigMapEnvSource{
  4759. LocalObjectReference: core.LocalObjectReference{Name: "abc"}},
  4760. },
  4761. },
  4762. expectedError: "field: Invalid value: \"\": may not have more than one field specified at a time",
  4763. },
  4764. {
  4765. name: "invalid secret ref name",
  4766. envs: []core.EnvFromSource{
  4767. {
  4768. SecretRef: &core.SecretEnvSource{
  4769. LocalObjectReference: core.LocalObjectReference{Name: "$%^&*#"}},
  4770. },
  4771. },
  4772. expectedError: "field[0].secretRef.name: Invalid value: \"$%^&*#\": " + dnsSubdomainLabelErrMsg,
  4773. },
  4774. {
  4775. name: "invalid config ref name",
  4776. envs: []core.EnvFromSource{
  4777. {
  4778. ConfigMapRef: &core.ConfigMapEnvSource{
  4779. LocalObjectReference: core.LocalObjectReference{Name: "$%^&*#"}},
  4780. },
  4781. },
  4782. expectedError: "field[0].configMapRef.name: Invalid value: \"$%^&*#\": " + dnsSubdomainLabelErrMsg,
  4783. },
  4784. }
  4785. for _, tc := range errorCases {
  4786. if errs := ValidateEnvFrom(tc.envs, field.NewPath("field")); len(errs) == 0 {
  4787. t.Errorf("expected failure for %s", tc.name)
  4788. } else {
  4789. for i := range errs {
  4790. str := errs[i].Error()
  4791. if str != "" && !strings.Contains(str, tc.expectedError) {
  4792. t.Errorf("%s: expected error detail either empty or %q, got %q", tc.name, tc.expectedError, str)
  4793. }
  4794. }
  4795. }
  4796. }
  4797. }
  4798. func TestValidateVolumeMounts(t *testing.T) {
  4799. volumes := []core.Volume{
  4800. {Name: "abc", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "testclaim1"}}},
  4801. {Name: "abc-123", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "testclaim2"}}},
  4802. {Name: "123", VolumeSource: core.VolumeSource{HostPath: &core.HostPathVolumeSource{Path: "/foo/baz", Type: newHostPathType(string(core.HostPathUnset))}}},
  4803. }
  4804. vols, v1err := ValidateVolumes(volumes, field.NewPath("field"))
  4805. if len(v1err) > 0 {
  4806. t.Errorf("Invalid test volume - expected success %v", v1err)
  4807. return
  4808. }
  4809. container := core.Container{
  4810. SecurityContext: nil,
  4811. }
  4812. propagation := core.MountPropagationBidirectional
  4813. successCase := []core.VolumeMount{
  4814. {Name: "abc", MountPath: "/foo"},
  4815. {Name: "123", MountPath: "/bar"},
  4816. {Name: "abc-123", MountPath: "/baz"},
  4817. {Name: "abc-123", MountPath: "/baa", SubPath: ""},
  4818. {Name: "abc-123", MountPath: "/bab", SubPath: "baz"},
  4819. {Name: "abc-123", MountPath: "d:", SubPath: ""},
  4820. {Name: "abc-123", MountPath: "F:", SubPath: ""},
  4821. {Name: "abc-123", MountPath: "G:\\mount", SubPath: ""},
  4822. {Name: "abc-123", MountPath: "/bac", SubPath: ".baz"},
  4823. {Name: "abc-123", MountPath: "/bad", SubPath: "..baz"},
  4824. }
  4825. goodVolumeDevices := []core.VolumeDevice{
  4826. {Name: "xyz", DevicePath: "/foofoo"},
  4827. {Name: "uvw", DevicePath: "/foofoo/share/test"},
  4828. }
  4829. if errs := ValidateVolumeMounts(successCase, GetVolumeDeviceMap(goodVolumeDevices), vols, &container, field.NewPath("field")); len(errs) != 0 {
  4830. t.Errorf("expected success: %v", errs)
  4831. }
  4832. errorCases := map[string][]core.VolumeMount{
  4833. "empty name": {{Name: "", MountPath: "/foo"}},
  4834. "name not found": {{Name: "", MountPath: "/foo"}},
  4835. "empty mountpath": {{Name: "abc", MountPath: ""}},
  4836. "mountpath collision": {{Name: "foo", MountPath: "/path/a"}, {Name: "bar", MountPath: "/path/a"}},
  4837. "absolute subpath": {{Name: "abc", MountPath: "/bar", SubPath: "/baz"}},
  4838. "subpath in ..": {{Name: "abc", MountPath: "/bar", SubPath: "../baz"}},
  4839. "subpath contains ..": {{Name: "abc", MountPath: "/bar", SubPath: "baz/../bat"}},
  4840. "subpath ends in ..": {{Name: "abc", MountPath: "/bar", SubPath: "./.."}},
  4841. "disabled MountPropagation feature gate": {{Name: "abc", MountPath: "/bar", MountPropagation: &propagation}},
  4842. "name exists in volumeDevice": {{Name: "xyz", MountPath: "/bar"}},
  4843. "mountpath exists in volumeDevice": {{Name: "uvw", MountPath: "/mnt/exists"}},
  4844. "both exist in volumeDevice": {{Name: "xyz", MountPath: "/mnt/exists"}},
  4845. }
  4846. badVolumeDevice := []core.VolumeDevice{
  4847. {Name: "xyz", DevicePath: "/mnt/exists"},
  4848. }
  4849. for k, v := range errorCases {
  4850. if errs := ValidateVolumeMounts(v, GetVolumeDeviceMap(badVolumeDevice), vols, &container, field.NewPath("field")); len(errs) == 0 {
  4851. t.Errorf("expected failure for %s", k)
  4852. }
  4853. }
  4854. }
  4855. func TestValidateDisabledSubpath(t *testing.T) {
  4856. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeSubpath, false)()
  4857. volumes := []core.Volume{
  4858. {Name: "abc", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "testclaim1"}}},
  4859. {Name: "abc-123", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "testclaim2"}}},
  4860. {Name: "123", VolumeSource: core.VolumeSource{HostPath: &core.HostPathVolumeSource{Path: "/foo/baz", Type: newHostPathType(string(core.HostPathUnset))}}},
  4861. }
  4862. vols, v1err := ValidateVolumes(volumes, field.NewPath("field"))
  4863. if len(v1err) > 0 {
  4864. t.Errorf("Invalid test volume - expected success %v", v1err)
  4865. return
  4866. }
  4867. container := core.Container{
  4868. SecurityContext: nil,
  4869. }
  4870. goodVolumeDevices := []core.VolumeDevice{
  4871. {Name: "xyz", DevicePath: "/foofoo"},
  4872. {Name: "uvw", DevicePath: "/foofoo/share/test"},
  4873. }
  4874. cases := map[string]struct {
  4875. mounts []core.VolumeMount
  4876. expectError bool
  4877. }{
  4878. "subpath not specified": {
  4879. []core.VolumeMount{
  4880. {
  4881. Name: "abc-123",
  4882. MountPath: "/bab",
  4883. },
  4884. },
  4885. false,
  4886. },
  4887. "subpath specified": {
  4888. []core.VolumeMount{
  4889. {
  4890. Name: "abc-123",
  4891. MountPath: "/bab",
  4892. SubPath: "baz",
  4893. },
  4894. },
  4895. false, // validation should not fail, dropping the field is handled in PrepareForCreate/PrepareForUpdate
  4896. },
  4897. }
  4898. for name, test := range cases {
  4899. errs := ValidateVolumeMounts(test.mounts, GetVolumeDeviceMap(goodVolumeDevices), vols, &container, field.NewPath("field"))
  4900. if len(errs) != 0 && !test.expectError {
  4901. t.Errorf("test %v failed: %+v", name, errs)
  4902. }
  4903. if len(errs) == 0 && test.expectError {
  4904. t.Errorf("test %v failed, expected error", name)
  4905. }
  4906. }
  4907. }
  4908. func TestValidateSubpathMutuallyExclusive(t *testing.T) {
  4909. // Enable feature VolumeSubpathEnvExpansion and VolumeSubpath
  4910. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeSubpathEnvExpansion, true)()
  4911. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeSubpath, true)()
  4912. volumes := []core.Volume{
  4913. {Name: "abc", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "testclaim1"}}},
  4914. {Name: "abc-123", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "testclaim2"}}},
  4915. {Name: "123", VolumeSource: core.VolumeSource{HostPath: &core.HostPathVolumeSource{Path: "/foo/baz", Type: newHostPathType(string(core.HostPathUnset))}}},
  4916. }
  4917. vols, v1err := ValidateVolumes(volumes, field.NewPath("field"))
  4918. if len(v1err) > 0 {
  4919. t.Errorf("Invalid test volume - expected success %v", v1err)
  4920. return
  4921. }
  4922. container := core.Container{
  4923. SecurityContext: nil,
  4924. }
  4925. goodVolumeDevices := []core.VolumeDevice{
  4926. {Name: "xyz", DevicePath: "/foofoo"},
  4927. {Name: "uvw", DevicePath: "/foofoo/share/test"},
  4928. }
  4929. cases := map[string]struct {
  4930. mounts []core.VolumeMount
  4931. expectError bool
  4932. }{
  4933. "subpath and subpathexpr not specified": {
  4934. []core.VolumeMount{
  4935. {
  4936. Name: "abc-123",
  4937. MountPath: "/bab",
  4938. },
  4939. },
  4940. false,
  4941. },
  4942. "subpath expr specified": {
  4943. []core.VolumeMount{
  4944. {
  4945. Name: "abc-123",
  4946. MountPath: "/bab",
  4947. SubPathExpr: "$(POD_NAME)",
  4948. },
  4949. },
  4950. false,
  4951. },
  4952. "subpath specified": {
  4953. []core.VolumeMount{
  4954. {
  4955. Name: "abc-123",
  4956. MountPath: "/bab",
  4957. SubPath: "baz",
  4958. },
  4959. },
  4960. false,
  4961. },
  4962. "subpath and subpathexpr specified": {
  4963. []core.VolumeMount{
  4964. {
  4965. Name: "abc-123",
  4966. MountPath: "/bab",
  4967. SubPath: "baz",
  4968. SubPathExpr: "$(POD_NAME)",
  4969. },
  4970. },
  4971. true,
  4972. },
  4973. }
  4974. for name, test := range cases {
  4975. errs := ValidateVolumeMounts(test.mounts, GetVolumeDeviceMap(goodVolumeDevices), vols, &container, field.NewPath("field"))
  4976. if len(errs) != 0 && !test.expectError {
  4977. t.Errorf("test %v failed: %+v", name, errs)
  4978. }
  4979. if len(errs) == 0 && test.expectError {
  4980. t.Errorf("test %v failed, expected error", name)
  4981. }
  4982. }
  4983. }
  4984. func TestValidateDisabledSubpathExpr(t *testing.T) {
  4985. // Enable feature VolumeSubpathEnvExpansion
  4986. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeSubpathEnvExpansion, true)()
  4987. volumes := []core.Volume{
  4988. {Name: "abc", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "testclaim1"}}},
  4989. {Name: "abc-123", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "testclaim2"}}},
  4990. {Name: "123", VolumeSource: core.VolumeSource{HostPath: &core.HostPathVolumeSource{Path: "/foo/baz", Type: newHostPathType(string(core.HostPathUnset))}}},
  4991. }
  4992. vols, v1err := ValidateVolumes(volumes, field.NewPath("field"))
  4993. if len(v1err) > 0 {
  4994. t.Errorf("Invalid test volume - expected success %v", v1err)
  4995. return
  4996. }
  4997. container := core.Container{
  4998. SecurityContext: nil,
  4999. }
  5000. goodVolumeDevices := []core.VolumeDevice{
  5001. {Name: "xyz", DevicePath: "/foofoo"},
  5002. {Name: "uvw", DevicePath: "/foofoo/share/test"},
  5003. }
  5004. cases := map[string]struct {
  5005. mounts []core.VolumeMount
  5006. expectError bool
  5007. }{
  5008. "subpath expr not specified": {
  5009. []core.VolumeMount{
  5010. {
  5011. Name: "abc-123",
  5012. MountPath: "/bab",
  5013. },
  5014. },
  5015. false,
  5016. },
  5017. "subpath expr specified": {
  5018. []core.VolumeMount{
  5019. {
  5020. Name: "abc-123",
  5021. MountPath: "/bab",
  5022. SubPathExpr: "$(POD_NAME)",
  5023. },
  5024. },
  5025. false,
  5026. },
  5027. }
  5028. for name, test := range cases {
  5029. errs := ValidateVolumeMounts(test.mounts, GetVolumeDeviceMap(goodVolumeDevices), vols, &container, field.NewPath("field"))
  5030. if len(errs) != 0 && !test.expectError {
  5031. t.Errorf("test %v failed: %+v", name, errs)
  5032. }
  5033. if len(errs) == 0 && test.expectError {
  5034. t.Errorf("test %v failed, expected error", name)
  5035. }
  5036. }
  5037. // Repeat with subpath feature gate off
  5038. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeSubpath, false)()
  5039. cases = map[string]struct {
  5040. mounts []core.VolumeMount
  5041. expectError bool
  5042. }{
  5043. "subpath expr not specified": {
  5044. []core.VolumeMount{
  5045. {
  5046. Name: "abc-123",
  5047. MountPath: "/bab",
  5048. },
  5049. },
  5050. false,
  5051. },
  5052. "subpath expr specified": {
  5053. []core.VolumeMount{
  5054. {
  5055. Name: "abc-123",
  5056. MountPath: "/bab",
  5057. SubPathExpr: "$(POD_NAME)",
  5058. },
  5059. },
  5060. false, // validation should not fail, dropping the field is handled in PrepareForCreate/PrepareForUpdate
  5061. },
  5062. }
  5063. for name, test := range cases {
  5064. errs := ValidateVolumeMounts(test.mounts, GetVolumeDeviceMap(goodVolumeDevices), vols, &container, field.NewPath("field"))
  5065. if len(errs) != 0 && !test.expectError {
  5066. t.Errorf("test %v failed: %+v", name, errs)
  5067. }
  5068. if len(errs) == 0 && test.expectError {
  5069. t.Errorf("test %v failed, expected error", name)
  5070. }
  5071. }
  5072. }
  5073. func TestValidateMountPropagation(t *testing.T) {
  5074. bTrue := true
  5075. bFalse := false
  5076. privilegedContainer := &core.Container{
  5077. SecurityContext: &core.SecurityContext{
  5078. Privileged: &bTrue,
  5079. },
  5080. }
  5081. nonPrivilegedContainer := &core.Container{
  5082. SecurityContext: &core.SecurityContext{
  5083. Privileged: &bFalse,
  5084. },
  5085. }
  5086. defaultContainer := &core.Container{}
  5087. propagationBidirectional := core.MountPropagationBidirectional
  5088. propagationHostToContainer := core.MountPropagationHostToContainer
  5089. propagationNone := core.MountPropagationNone
  5090. propagationInvalid := core.MountPropagationMode("invalid")
  5091. tests := []struct {
  5092. mount core.VolumeMount
  5093. container *core.Container
  5094. expectError bool
  5095. }{
  5096. {
  5097. // implicitly non-privileged container + no propagation
  5098. core.VolumeMount{Name: "foo", MountPath: "/foo"},
  5099. defaultContainer,
  5100. false,
  5101. },
  5102. {
  5103. // implicitly non-privileged container + HostToContainer
  5104. core.VolumeMount{Name: "foo", MountPath: "/foo", MountPropagation: &propagationHostToContainer},
  5105. defaultContainer,
  5106. false,
  5107. },
  5108. {
  5109. // non-privileged container + None
  5110. core.VolumeMount{Name: "foo", MountPath: "/foo", MountPropagation: &propagationNone},
  5111. defaultContainer,
  5112. false,
  5113. },
  5114. {
  5115. // error: implicitly non-privileged container + Bidirectional
  5116. core.VolumeMount{Name: "foo", MountPath: "/foo", MountPropagation: &propagationBidirectional},
  5117. defaultContainer,
  5118. true,
  5119. },
  5120. {
  5121. // explicitly non-privileged container + no propagation
  5122. core.VolumeMount{Name: "foo", MountPath: "/foo"},
  5123. nonPrivilegedContainer,
  5124. false,
  5125. },
  5126. {
  5127. // explicitly non-privileged container + HostToContainer
  5128. core.VolumeMount{Name: "foo", MountPath: "/foo", MountPropagation: &propagationHostToContainer},
  5129. nonPrivilegedContainer,
  5130. false,
  5131. },
  5132. {
  5133. // explicitly non-privileged container + HostToContainer
  5134. core.VolumeMount{Name: "foo", MountPath: "/foo", MountPropagation: &propagationBidirectional},
  5135. nonPrivilegedContainer,
  5136. true,
  5137. },
  5138. {
  5139. // privileged container + no propagation
  5140. core.VolumeMount{Name: "foo", MountPath: "/foo"},
  5141. privilegedContainer,
  5142. false,
  5143. },
  5144. {
  5145. // privileged container + HostToContainer
  5146. core.VolumeMount{Name: "foo", MountPath: "/foo", MountPropagation: &propagationHostToContainer},
  5147. privilegedContainer,
  5148. false,
  5149. },
  5150. {
  5151. // privileged container + Bidirectional
  5152. core.VolumeMount{Name: "foo", MountPath: "/foo", MountPropagation: &propagationBidirectional},
  5153. privilegedContainer,
  5154. false,
  5155. },
  5156. {
  5157. // error: privileged container + invalid mount propagation
  5158. core.VolumeMount{Name: "foo", MountPath: "/foo", MountPropagation: &propagationInvalid},
  5159. privilegedContainer,
  5160. true,
  5161. },
  5162. {
  5163. // no container + Bidirectional
  5164. core.VolumeMount{Name: "foo", MountPath: "/foo", MountPropagation: &propagationBidirectional},
  5165. nil,
  5166. false,
  5167. },
  5168. }
  5169. volumes := []core.Volume{
  5170. {Name: "foo", VolumeSource: core.VolumeSource{HostPath: &core.HostPathVolumeSource{Path: "/foo/baz", Type: newHostPathType(string(core.HostPathUnset))}}},
  5171. }
  5172. vols2, v2err := ValidateVolumes(volumes, field.NewPath("field"))
  5173. if len(v2err) > 0 {
  5174. t.Errorf("Invalid test volume - expected success %v", v2err)
  5175. return
  5176. }
  5177. for i, test := range tests {
  5178. errs := ValidateVolumeMounts([]core.VolumeMount{test.mount}, nil, vols2, test.container, field.NewPath("field"))
  5179. if test.expectError && len(errs) == 0 {
  5180. t.Errorf("test %d expected error, got none", i)
  5181. }
  5182. if !test.expectError && len(errs) != 0 {
  5183. t.Errorf("test %d expected success, got error: %v", i, errs)
  5184. }
  5185. }
  5186. }
  5187. func TestAlphaValidateVolumeDevices(t *testing.T) {
  5188. volumes := []core.Volume{
  5189. {Name: "abc", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "testclaim1"}}},
  5190. {Name: "abc-123", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "testclaim2"}}},
  5191. {Name: "def", VolumeSource: core.VolumeSource{HostPath: &core.HostPathVolumeSource{Path: "/foo/baz", Type: newHostPathType(string(core.HostPathUnset))}}},
  5192. }
  5193. vols, v1err := ValidateVolumes(volumes, field.NewPath("field"))
  5194. if len(v1err) > 0 {
  5195. t.Errorf("Invalid test volumes - expected success %v", v1err)
  5196. return
  5197. }
  5198. successCase := []core.VolumeDevice{
  5199. {Name: "abc", DevicePath: "/foo"},
  5200. {Name: "abc-123", DevicePath: "/usr/share/test"},
  5201. }
  5202. goodVolumeMounts := []core.VolumeMount{
  5203. {Name: "xyz", MountPath: "/foofoo"},
  5204. {Name: "ghi", MountPath: "/foo/usr/share/test"},
  5205. }
  5206. errorCases := map[string][]core.VolumeDevice{
  5207. "empty name": {{Name: "", DevicePath: "/foo"}},
  5208. "duplicate name": {{Name: "abc", DevicePath: "/foo"}, {Name: "abc", DevicePath: "/foo/bar"}},
  5209. "name not found": {{Name: "not-found", DevicePath: "/usr/share/test"}},
  5210. "name found but invalid source": {{Name: "def", DevicePath: "/usr/share/test"}},
  5211. "empty devicepath": {{Name: "abc", DevicePath: ""}},
  5212. "relative devicepath": {{Name: "abc-123", DevicePath: "baz"}},
  5213. "duplicate devicepath": {{Name: "abc", DevicePath: "/foo"}, {Name: "abc-123", DevicePath: "/foo"}},
  5214. "no backsteps": {{Name: "def", DevicePath: "/baz/../"}},
  5215. "name exists in volumemounts": {{Name: "abc", DevicePath: "/baz/../"}},
  5216. "path exists in volumemounts": {{Name: "xyz", DevicePath: "/this/path/exists"}},
  5217. "both exist in volumemounts": {{Name: "abc", DevicePath: "/this/path/exists"}},
  5218. }
  5219. badVolumeMounts := []core.VolumeMount{
  5220. {Name: "abc", MountPath: "/foo"},
  5221. {Name: "abc-123", MountPath: "/this/path/exists"},
  5222. }
  5223. // Success Cases:
  5224. // Validate normal success cases - only PVC volumeSource
  5225. if errs := ValidateVolumeDevices(successCase, GetVolumeMountMap(goodVolumeMounts), vols, field.NewPath("field")); len(errs) != 0 {
  5226. t.Errorf("expected success: %v", errs)
  5227. }
  5228. // Error Cases:
  5229. // Validate normal error cases - only PVC volumeSource
  5230. for k, v := range errorCases {
  5231. if errs := ValidateVolumeDevices(v, GetVolumeMountMap(badVolumeMounts), vols, field.NewPath("field")); len(errs) == 0 {
  5232. t.Errorf("expected failure for %s", k)
  5233. }
  5234. }
  5235. }
  5236. func TestValidateProbe(t *testing.T) {
  5237. handler := core.Handler{Exec: &core.ExecAction{Command: []string{"echo"}}}
  5238. // These fields must be positive.
  5239. positiveFields := [...]string{"InitialDelaySeconds", "TimeoutSeconds", "PeriodSeconds", "SuccessThreshold", "FailureThreshold"}
  5240. successCases := []*core.Probe{nil}
  5241. for _, field := range positiveFields {
  5242. probe := &core.Probe{Handler: handler}
  5243. reflect.ValueOf(probe).Elem().FieldByName(field).SetInt(10)
  5244. successCases = append(successCases, probe)
  5245. }
  5246. for _, p := range successCases {
  5247. if errs := validateProbe(p, field.NewPath("field")); len(errs) != 0 {
  5248. t.Errorf("expected success: %v", errs)
  5249. }
  5250. }
  5251. errorCases := []*core.Probe{{TimeoutSeconds: 10, InitialDelaySeconds: 10}}
  5252. for _, field := range positiveFields {
  5253. probe := &core.Probe{Handler: handler}
  5254. reflect.ValueOf(probe).Elem().FieldByName(field).SetInt(-10)
  5255. errorCases = append(errorCases, probe)
  5256. }
  5257. for _, p := range errorCases {
  5258. if errs := validateProbe(p, field.NewPath("field")); len(errs) == 0 {
  5259. t.Errorf("expected failure for %v", p)
  5260. }
  5261. }
  5262. }
  5263. func TestValidateHandler(t *testing.T) {
  5264. successCases := []core.Handler{
  5265. {Exec: &core.ExecAction{Command: []string{"echo"}}},
  5266. {HTTPGet: &core.HTTPGetAction{Path: "/", Port: intstr.FromInt(1), Host: "", Scheme: "HTTP"}},
  5267. {HTTPGet: &core.HTTPGetAction{Path: "/foo", Port: intstr.FromInt(65535), Host: "host", Scheme: "HTTP"}},
  5268. {HTTPGet: &core.HTTPGetAction{Path: "/", Port: intstr.FromString("port"), Host: "", Scheme: "HTTP"}},
  5269. {HTTPGet: &core.HTTPGetAction{Path: "/", Port: intstr.FromString("port"), Host: "", Scheme: "HTTP", HTTPHeaders: []core.HTTPHeader{{Name: "Host", Value: "foo.example.com"}}}},
  5270. {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"}}}},
  5271. }
  5272. for _, h := range successCases {
  5273. if errs := validateHandler(&h, field.NewPath("field")); len(errs) != 0 {
  5274. t.Errorf("expected success: %v", errs)
  5275. }
  5276. }
  5277. errorCases := []core.Handler{
  5278. {},
  5279. {Exec: &core.ExecAction{Command: []string{}}},
  5280. {HTTPGet: &core.HTTPGetAction{Path: "", Port: intstr.FromInt(0), Host: ""}},
  5281. {HTTPGet: &core.HTTPGetAction{Path: "/foo", Port: intstr.FromInt(65536), Host: "host"}},
  5282. {HTTPGet: &core.HTTPGetAction{Path: "", Port: intstr.FromString(""), Host: ""}},
  5283. {HTTPGet: &core.HTTPGetAction{Path: "/", Port: intstr.FromString("port"), Host: "", Scheme: "HTTP", HTTPHeaders: []core.HTTPHeader{{Name: "Host:", Value: "foo.example.com"}}}},
  5284. {HTTPGet: &core.HTTPGetAction{Path: "/", Port: intstr.FromString("port"), Host: "", Scheme: "HTTP", HTTPHeaders: []core.HTTPHeader{{Name: "X_Forwarded_For", Value: "foo.example.com"}}}},
  5285. }
  5286. for _, h := range errorCases {
  5287. if errs := validateHandler(&h, field.NewPath("field")); len(errs) == 0 {
  5288. t.Errorf("expected failure for %#v", h)
  5289. }
  5290. }
  5291. }
  5292. func TestValidatePullPolicy(t *testing.T) {
  5293. type T struct {
  5294. Container core.Container
  5295. ExpectedPolicy core.PullPolicy
  5296. }
  5297. testCases := map[string]T{
  5298. "NotPresent1": {
  5299. core.Container{Name: "abc", Image: "image:latest", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
  5300. core.PullIfNotPresent,
  5301. },
  5302. "NotPresent2": {
  5303. core.Container{Name: "abc1", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
  5304. core.PullIfNotPresent,
  5305. },
  5306. "Always1": {
  5307. core.Container{Name: "123", Image: "image:latest", ImagePullPolicy: "Always"},
  5308. core.PullAlways,
  5309. },
  5310. "Always2": {
  5311. core.Container{Name: "1234", Image: "image", ImagePullPolicy: "Always"},
  5312. core.PullAlways,
  5313. },
  5314. "Never1": {
  5315. core.Container{Name: "abc-123", Image: "image:latest", ImagePullPolicy: "Never"},
  5316. core.PullNever,
  5317. },
  5318. "Never2": {
  5319. core.Container{Name: "abc-1234", Image: "image", ImagePullPolicy: "Never"},
  5320. core.PullNever,
  5321. },
  5322. }
  5323. for k, v := range testCases {
  5324. ctr := &v.Container
  5325. errs := validatePullPolicy(ctr.ImagePullPolicy, field.NewPath("field"))
  5326. if len(errs) != 0 {
  5327. t.Errorf("case[%s] expected success, got %#v", k, errs)
  5328. }
  5329. if ctr.ImagePullPolicy != v.ExpectedPolicy {
  5330. t.Errorf("case[%s] expected policy %v, got %v", k, v.ExpectedPolicy, ctr.ImagePullPolicy)
  5331. }
  5332. }
  5333. }
  5334. func getResourceLimits(cpu, memory string) core.ResourceList {
  5335. res := core.ResourceList{}
  5336. res[core.ResourceCPU] = resource.MustParse(cpu)
  5337. res[core.ResourceMemory] = resource.MustParse(memory)
  5338. return res
  5339. }
  5340. func TestValidateEphemeralContainers(t *testing.T) {
  5341. containers := []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}}
  5342. initContainers := []core.Container{{Name: "ictr", Image: "iimage", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}}
  5343. vols := map[string]core.VolumeSource{"vol": {EmptyDir: &core.EmptyDirVolumeSource{}}}
  5344. // Success Cases
  5345. for title, ephemeralContainers := range map[string][]core.EphemeralContainer{
  5346. "Empty Ephemeral Containers": {},
  5347. "Single Container": {
  5348. {EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "debug", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  5349. },
  5350. "Multiple Containers": {
  5351. {EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "debug1", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  5352. {EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "debug2", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  5353. },
  5354. "Single Container with Target": {
  5355. {
  5356. EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "debug", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
  5357. TargetContainerName: "ctr",
  5358. },
  5359. },
  5360. "All Whitelisted Fields": {
  5361. {
  5362. EphemeralContainerCommon: core.EphemeralContainerCommon{
  5363. Name: "debug",
  5364. Image: "image",
  5365. Command: []string{"bash"},
  5366. Args: []string{"bash"},
  5367. WorkingDir: "/",
  5368. EnvFrom: []core.EnvFromSource{
  5369. {
  5370. ConfigMapRef: &core.ConfigMapEnvSource{
  5371. LocalObjectReference: core.LocalObjectReference{Name: "dummy"},
  5372. Optional: &[]bool{true}[0],
  5373. },
  5374. },
  5375. },
  5376. Env: []core.EnvVar{
  5377. {Name: "TEST", Value: "TRUE"},
  5378. },
  5379. VolumeMounts: []core.VolumeMount{
  5380. {Name: "vol", MountPath: "/vol"},
  5381. },
  5382. TerminationMessagePath: "/dev/termination-log",
  5383. TerminationMessagePolicy: "File",
  5384. ImagePullPolicy: "IfNotPresent",
  5385. Stdin: true,
  5386. StdinOnce: true,
  5387. TTY: true,
  5388. },
  5389. },
  5390. },
  5391. } {
  5392. if errs := validateEphemeralContainers(ephemeralContainers, containers, initContainers, vols, field.NewPath("ephemeralContainers")); len(errs) != 0 {
  5393. t.Errorf("expected success for '%s' but got errors: %v", title, errs)
  5394. }
  5395. }
  5396. // Failure Cases
  5397. tcs := []struct {
  5398. title string
  5399. ephemeralContainers []core.EphemeralContainer
  5400. expectedError field.Error
  5401. }{
  5402. {
  5403. "Name Collision with Container.Containers",
  5404. []core.EphemeralContainer{
  5405. {EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  5406. {EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "debug1", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  5407. },
  5408. field.Error{Type: field.ErrorTypeDuplicate, Field: "ephemeralContainers[0].name"},
  5409. },
  5410. {
  5411. "Name Collision with Container.InitContainers",
  5412. []core.EphemeralContainer{
  5413. {EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "ictr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  5414. {EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "debug1", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  5415. },
  5416. field.Error{Type: field.ErrorTypeDuplicate, Field: "ephemeralContainers[0].name"},
  5417. },
  5418. {
  5419. "Name Collision with EphemeralContainers",
  5420. []core.EphemeralContainer{
  5421. {EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "debug1", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  5422. {EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "debug1", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  5423. },
  5424. field.Error{Type: field.ErrorTypeDuplicate, Field: "ephemeralContainers[1].name"},
  5425. },
  5426. {
  5427. "empty Container Container",
  5428. []core.EphemeralContainer{
  5429. {EphemeralContainerCommon: core.EphemeralContainerCommon{}},
  5430. },
  5431. field.Error{Type: field.ErrorTypeRequired, Field: "ephemeralContainers[0]"},
  5432. },
  5433. {
  5434. "empty Container Name",
  5435. []core.EphemeralContainer{
  5436. {EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  5437. },
  5438. field.Error{Type: field.ErrorTypeRequired, Field: "ephemeralContainers[0]"},
  5439. },
  5440. {
  5441. "whitespace padded image name",
  5442. []core.EphemeralContainer{
  5443. {EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "debug", Image: " image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  5444. },
  5445. field.Error{Type: field.ErrorTypeInvalid, Field: "ephemeralContainers[0][0].image"},
  5446. },
  5447. {
  5448. "TargetContainerName doesn't exist",
  5449. []core.EphemeralContainer{
  5450. {
  5451. EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "debug", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
  5452. TargetContainerName: "bogus",
  5453. },
  5454. },
  5455. field.Error{Type: field.ErrorTypeNotFound, Field: "ephemeralContainers[0].targetContainerName"},
  5456. },
  5457. {
  5458. "Container uses non-whitelisted field: Lifecycle",
  5459. []core.EphemeralContainer{
  5460. {
  5461. EphemeralContainerCommon: core.EphemeralContainerCommon{
  5462. Name: "debug",
  5463. Image: "image",
  5464. ImagePullPolicy: "IfNotPresent",
  5465. TerminationMessagePolicy: "File",
  5466. Lifecycle: &core.Lifecycle{
  5467. PreStop: &core.Handler{
  5468. Exec: &core.ExecAction{Command: []string{"ls", "-l"}},
  5469. },
  5470. },
  5471. },
  5472. },
  5473. },
  5474. field.Error{Type: field.ErrorTypeForbidden, Field: "ephemeralContainers[0].lifecycle"},
  5475. },
  5476. {
  5477. "Container uses non-whitelisted field: LivenessProbe",
  5478. []core.EphemeralContainer{
  5479. {
  5480. EphemeralContainerCommon: core.EphemeralContainerCommon{
  5481. Name: "debug",
  5482. Image: "image",
  5483. ImagePullPolicy: "IfNotPresent",
  5484. TerminationMessagePolicy: "File",
  5485. LivenessProbe: &core.Probe{
  5486. Handler: core.Handler{
  5487. TCPSocket: &core.TCPSocketAction{Port: intstr.FromInt(80)},
  5488. },
  5489. SuccessThreshold: 1,
  5490. },
  5491. },
  5492. },
  5493. },
  5494. field.Error{Type: field.ErrorTypeForbidden, Field: "ephemeralContainers[0].livenessProbe"},
  5495. },
  5496. {
  5497. "Container uses non-whitelisted field: Ports",
  5498. []core.EphemeralContainer{
  5499. {
  5500. EphemeralContainerCommon: core.EphemeralContainerCommon{
  5501. Name: "debug",
  5502. Image: "image",
  5503. ImagePullPolicy: "IfNotPresent",
  5504. TerminationMessagePolicy: "File",
  5505. Ports: []core.ContainerPort{
  5506. {Protocol: "TCP", ContainerPort: 80},
  5507. },
  5508. },
  5509. },
  5510. },
  5511. field.Error{Type: field.ErrorTypeForbidden, Field: "ephemeralContainers[0].ports"},
  5512. },
  5513. {
  5514. "Container uses non-whitelisted field: ReadinessProbe",
  5515. []core.EphemeralContainer{
  5516. {
  5517. EphemeralContainerCommon: core.EphemeralContainerCommon{
  5518. Name: "debug",
  5519. Image: "image",
  5520. ImagePullPolicy: "IfNotPresent",
  5521. TerminationMessagePolicy: "File",
  5522. ReadinessProbe: &core.Probe{
  5523. Handler: core.Handler{
  5524. TCPSocket: &core.TCPSocketAction{Port: intstr.FromInt(80)},
  5525. },
  5526. },
  5527. },
  5528. },
  5529. },
  5530. field.Error{Type: field.ErrorTypeForbidden, Field: "ephemeralContainers[0].readinessProbe"},
  5531. },
  5532. {
  5533. "Container uses non-whitelisted field: Resources",
  5534. []core.EphemeralContainer{
  5535. {
  5536. EphemeralContainerCommon: core.EphemeralContainerCommon{
  5537. Name: "debug",
  5538. Image: "image",
  5539. ImagePullPolicy: "IfNotPresent",
  5540. TerminationMessagePolicy: "File",
  5541. Resources: core.ResourceRequirements{
  5542. Limits: core.ResourceList{
  5543. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  5544. },
  5545. },
  5546. },
  5547. },
  5548. },
  5549. field.Error{Type: field.ErrorTypeForbidden, Field: "ephemeralContainers[0].resources"},
  5550. },
  5551. }
  5552. for _, tc := range tcs {
  5553. errs := validateEphemeralContainers(tc.ephemeralContainers, containers, initContainers, vols, field.NewPath("ephemeralContainers"))
  5554. if len(errs) == 0 {
  5555. t.Errorf("for test %q, expected error but received none", tc.title)
  5556. } else if len(errs) > 1 {
  5557. t.Errorf("for test %q, expected 1 error but received %d: %q", tc.title, len(errs), errs)
  5558. } else {
  5559. if errs[0].Type != tc.expectedError.Type {
  5560. t.Errorf("for test %q, expected error type %q but received %q: %q", tc.title, string(tc.expectedError.Type), string(errs[0].Type), errs)
  5561. }
  5562. if errs[0].Field != tc.expectedError.Field {
  5563. t.Errorf("for test %q, expected error for field %q but received error for field %q: %q", tc.title, tc.expectedError.Field, errs[0].Field, errs)
  5564. }
  5565. }
  5566. }
  5567. }
  5568. func TestValidateContainers(t *testing.T) {
  5569. volumeDevices := make(map[string]core.VolumeSource)
  5570. capabilities.SetForTests(capabilities.Capabilities{
  5571. AllowPrivileged: true,
  5572. })
  5573. successCase := []core.Container{
  5574. {Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
  5575. // backwards compatibility to ensure containers in pod template spec do not check for this
  5576. {Name: "def", Image: " ", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
  5577. {Name: "ghi", Image: " some ", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
  5578. {Name: "123", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
  5579. {Name: "abc-123", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
  5580. {
  5581. Name: "life-123",
  5582. Image: "image",
  5583. Lifecycle: &core.Lifecycle{
  5584. PreStop: &core.Handler{
  5585. Exec: &core.ExecAction{Command: []string{"ls", "-l"}},
  5586. },
  5587. },
  5588. ImagePullPolicy: "IfNotPresent",
  5589. TerminationMessagePolicy: "File",
  5590. },
  5591. {
  5592. Name: "resources-test",
  5593. Image: "image",
  5594. Resources: core.ResourceRequirements{
  5595. Limits: core.ResourceList{
  5596. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  5597. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  5598. core.ResourceName("my.org/resource"): resource.MustParse("10"),
  5599. },
  5600. },
  5601. ImagePullPolicy: "IfNotPresent",
  5602. TerminationMessagePolicy: "File",
  5603. },
  5604. {
  5605. Name: "resources-test-with-request-and-limit",
  5606. Image: "image",
  5607. Resources: core.ResourceRequirements{
  5608. Requests: core.ResourceList{
  5609. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  5610. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  5611. },
  5612. Limits: core.ResourceList{
  5613. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  5614. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  5615. },
  5616. },
  5617. ImagePullPolicy: "IfNotPresent",
  5618. TerminationMessagePolicy: "File",
  5619. },
  5620. {
  5621. Name: "resources-request-limit-simple",
  5622. Image: "image",
  5623. Resources: core.ResourceRequirements{
  5624. Requests: core.ResourceList{
  5625. core.ResourceName(core.ResourceCPU): resource.MustParse("8"),
  5626. },
  5627. Limits: core.ResourceList{
  5628. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  5629. },
  5630. },
  5631. ImagePullPolicy: "IfNotPresent",
  5632. TerminationMessagePolicy: "File",
  5633. },
  5634. {
  5635. Name: "resources-request-limit-edge",
  5636. Image: "image",
  5637. Resources: core.ResourceRequirements{
  5638. Requests: core.ResourceList{
  5639. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  5640. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  5641. core.ResourceName("my.org/resource"): resource.MustParse("10"),
  5642. },
  5643. Limits: core.ResourceList{
  5644. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  5645. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  5646. core.ResourceName("my.org/resource"): resource.MustParse("10"),
  5647. },
  5648. },
  5649. ImagePullPolicy: "IfNotPresent",
  5650. TerminationMessagePolicy: "File",
  5651. },
  5652. {
  5653. Name: "resources-request-limit-partials",
  5654. Image: "image",
  5655. Resources: core.ResourceRequirements{
  5656. Requests: core.ResourceList{
  5657. core.ResourceName(core.ResourceCPU): resource.MustParse("9.5"),
  5658. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  5659. },
  5660. Limits: core.ResourceList{
  5661. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  5662. core.ResourceName("my.org/resource"): resource.MustParse("10"),
  5663. },
  5664. },
  5665. ImagePullPolicy: "IfNotPresent",
  5666. TerminationMessagePolicy: "File",
  5667. },
  5668. {
  5669. Name: "resources-request",
  5670. Image: "image",
  5671. Resources: core.ResourceRequirements{
  5672. Requests: core.ResourceList{
  5673. core.ResourceName(core.ResourceCPU): resource.MustParse("9.5"),
  5674. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  5675. },
  5676. },
  5677. ImagePullPolicy: "IfNotPresent",
  5678. TerminationMessagePolicy: "File",
  5679. },
  5680. {
  5681. Name: "same-host-port-different-protocol",
  5682. Image: "image",
  5683. Ports: []core.ContainerPort{
  5684. {ContainerPort: 80, HostPort: 80, Protocol: "TCP"},
  5685. {ContainerPort: 80, HostPort: 80, Protocol: "UDP"},
  5686. },
  5687. ImagePullPolicy: "IfNotPresent",
  5688. TerminationMessagePolicy: "File",
  5689. },
  5690. {
  5691. Name: "fallback-to-logs-termination-message",
  5692. Image: "image",
  5693. ImagePullPolicy: "IfNotPresent",
  5694. TerminationMessagePolicy: "FallbackToLogsOnError",
  5695. },
  5696. {
  5697. Name: "file-termination-message",
  5698. Image: "image",
  5699. ImagePullPolicy: "IfNotPresent",
  5700. TerminationMessagePolicy: "File",
  5701. },
  5702. {
  5703. Name: "env-from-source",
  5704. Image: "image",
  5705. ImagePullPolicy: "IfNotPresent",
  5706. TerminationMessagePolicy: "File",
  5707. EnvFrom: []core.EnvFromSource{
  5708. {
  5709. ConfigMapRef: &core.ConfigMapEnvSource{
  5710. LocalObjectReference: core.LocalObjectReference{
  5711. Name: "test",
  5712. },
  5713. },
  5714. },
  5715. },
  5716. },
  5717. {Name: "abc-1234", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File", SecurityContext: fakeValidSecurityContext(true)},
  5718. }
  5719. if errs := validateContainers(successCase, false, volumeDevices, field.NewPath("field")); len(errs) != 0 {
  5720. t.Errorf("expected success: %v", errs)
  5721. }
  5722. capabilities.SetForTests(capabilities.Capabilities{
  5723. AllowPrivileged: false,
  5724. })
  5725. errorCases := map[string][]core.Container{
  5726. "zero-length name": {{Name: "", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  5727. "zero-length-image": {{Name: "abc", Image: "", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  5728. "name > 63 characters": {{Name: strings.Repeat("a", 64), Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  5729. "name not a DNS label": {{Name: "a.b.c", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  5730. "name not unique": {
  5731. {Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
  5732. {Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
  5733. },
  5734. "zero-length image": {{Name: "abc", Image: "", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  5735. "host port not unique": {
  5736. {Name: "abc", Image: "image", Ports: []core.ContainerPort{{ContainerPort: 80, HostPort: 80, Protocol: "TCP"}},
  5737. ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
  5738. {Name: "def", Image: "image", Ports: []core.ContainerPort{{ContainerPort: 81, HostPort: 80, Protocol: "TCP"}},
  5739. ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
  5740. },
  5741. "invalid env var name": {
  5742. {Name: "abc", Image: "image", Env: []core.EnvVar{{Name: "ev!1"}}, ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
  5743. },
  5744. "unknown volume name": {
  5745. {Name: "abc", Image: "image", VolumeMounts: []core.VolumeMount{{Name: "anything", MountPath: "/foo"}},
  5746. ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
  5747. },
  5748. "invalid lifecycle, no exec command.": {
  5749. {
  5750. Name: "life-123",
  5751. Image: "image",
  5752. Lifecycle: &core.Lifecycle{
  5753. PreStop: &core.Handler{
  5754. Exec: &core.ExecAction{},
  5755. },
  5756. },
  5757. ImagePullPolicy: "IfNotPresent",
  5758. TerminationMessagePolicy: "File",
  5759. },
  5760. },
  5761. "invalid lifecycle, no http path.": {
  5762. {
  5763. Name: "life-123",
  5764. Image: "image",
  5765. Lifecycle: &core.Lifecycle{
  5766. PreStop: &core.Handler{
  5767. HTTPGet: &core.HTTPGetAction{},
  5768. },
  5769. },
  5770. ImagePullPolicy: "IfNotPresent",
  5771. TerminationMessagePolicy: "File",
  5772. },
  5773. },
  5774. "invalid lifecycle, no tcp socket port.": {
  5775. {
  5776. Name: "life-123",
  5777. Image: "image",
  5778. Lifecycle: &core.Lifecycle{
  5779. PreStop: &core.Handler{
  5780. TCPSocket: &core.TCPSocketAction{},
  5781. },
  5782. },
  5783. ImagePullPolicy: "IfNotPresent",
  5784. TerminationMessagePolicy: "File",
  5785. },
  5786. },
  5787. "invalid lifecycle, zero tcp socket port.": {
  5788. {
  5789. Name: "life-123",
  5790. Image: "image",
  5791. Lifecycle: &core.Lifecycle{
  5792. PreStop: &core.Handler{
  5793. TCPSocket: &core.TCPSocketAction{
  5794. Port: intstr.FromInt(0),
  5795. },
  5796. },
  5797. },
  5798. ImagePullPolicy: "IfNotPresent",
  5799. TerminationMessagePolicy: "File",
  5800. },
  5801. },
  5802. "invalid lifecycle, no action.": {
  5803. {
  5804. Name: "life-123",
  5805. Image: "image",
  5806. Lifecycle: &core.Lifecycle{
  5807. PreStop: &core.Handler{},
  5808. },
  5809. ImagePullPolicy: "IfNotPresent",
  5810. TerminationMessagePolicy: "File",
  5811. },
  5812. },
  5813. "invalid liveness probe, no tcp socket port.": {
  5814. {
  5815. Name: "life-123",
  5816. Image: "image",
  5817. LivenessProbe: &core.Probe{
  5818. Handler: core.Handler{
  5819. TCPSocket: &core.TCPSocketAction{},
  5820. },
  5821. },
  5822. ImagePullPolicy: "IfNotPresent",
  5823. TerminationMessagePolicy: "File",
  5824. },
  5825. },
  5826. "invalid liveness probe, no action.": {
  5827. {
  5828. Name: "life-123",
  5829. Image: "image",
  5830. LivenessProbe: &core.Probe{
  5831. Handler: core.Handler{},
  5832. },
  5833. ImagePullPolicy: "IfNotPresent",
  5834. TerminationMessagePolicy: "File",
  5835. },
  5836. },
  5837. "invalid message termination policy": {
  5838. {
  5839. Name: "life-123",
  5840. Image: "image",
  5841. ImagePullPolicy: "IfNotPresent",
  5842. TerminationMessagePolicy: "Unknown",
  5843. },
  5844. },
  5845. "empty message termination policy": {
  5846. {
  5847. Name: "life-123",
  5848. Image: "image",
  5849. ImagePullPolicy: "IfNotPresent",
  5850. TerminationMessagePolicy: "",
  5851. },
  5852. },
  5853. "privilege disabled": {
  5854. {Name: "abc", Image: "image", SecurityContext: fakeValidSecurityContext(true)},
  5855. },
  5856. "invalid compute resource": {
  5857. {
  5858. Name: "abc-123",
  5859. Image: "image",
  5860. Resources: core.ResourceRequirements{
  5861. Limits: core.ResourceList{
  5862. "disk": resource.MustParse("10G"),
  5863. },
  5864. },
  5865. ImagePullPolicy: "IfNotPresent",
  5866. TerminationMessagePolicy: "File",
  5867. },
  5868. },
  5869. "Resource CPU invalid": {
  5870. {
  5871. Name: "abc-123",
  5872. Image: "image",
  5873. Resources: core.ResourceRequirements{
  5874. Limits: getResourceLimits("-10", "0"),
  5875. },
  5876. ImagePullPolicy: "IfNotPresent",
  5877. TerminationMessagePolicy: "File",
  5878. },
  5879. },
  5880. "Resource Requests CPU invalid": {
  5881. {
  5882. Name: "abc-123",
  5883. Image: "image",
  5884. Resources: core.ResourceRequirements{
  5885. Requests: getResourceLimits("-10", "0"),
  5886. },
  5887. ImagePullPolicy: "IfNotPresent",
  5888. TerminationMessagePolicy: "File",
  5889. },
  5890. },
  5891. "Resource Memory invalid": {
  5892. {
  5893. Name: "abc-123",
  5894. Image: "image",
  5895. Resources: core.ResourceRequirements{
  5896. Limits: getResourceLimits("0", "-10"),
  5897. },
  5898. ImagePullPolicy: "IfNotPresent",
  5899. TerminationMessagePolicy: "File",
  5900. },
  5901. },
  5902. "Request limit simple invalid": {
  5903. {
  5904. Name: "abc-123",
  5905. Image: "image",
  5906. Resources: core.ResourceRequirements{
  5907. Limits: getResourceLimits("5", "3"),
  5908. Requests: getResourceLimits("6", "3"),
  5909. },
  5910. ImagePullPolicy: "IfNotPresent",
  5911. TerminationMessagePolicy: "File",
  5912. },
  5913. },
  5914. "Invalid storage limit request": {
  5915. {
  5916. Name: "abc-123",
  5917. Image: "image",
  5918. Resources: core.ResourceRequirements{
  5919. Limits: core.ResourceList{
  5920. core.ResourceName("attachable-volumes-aws-ebs"): *resource.NewQuantity(10, resource.DecimalSI),
  5921. },
  5922. },
  5923. ImagePullPolicy: "IfNotPresent",
  5924. TerminationMessagePolicy: "File",
  5925. },
  5926. },
  5927. "Request limit multiple invalid": {
  5928. {
  5929. Name: "abc-123",
  5930. Image: "image",
  5931. Resources: core.ResourceRequirements{
  5932. Limits: getResourceLimits("5", "3"),
  5933. Requests: getResourceLimits("6", "4"),
  5934. },
  5935. ImagePullPolicy: "IfNotPresent",
  5936. TerminationMessagePolicy: "File",
  5937. },
  5938. },
  5939. "Invalid env from": {
  5940. {
  5941. Name: "env-from-source",
  5942. Image: "image",
  5943. ImagePullPolicy: "IfNotPresent",
  5944. TerminationMessagePolicy: "File",
  5945. EnvFrom: []core.EnvFromSource{
  5946. {
  5947. ConfigMapRef: &core.ConfigMapEnvSource{
  5948. LocalObjectReference: core.LocalObjectReference{
  5949. Name: "$%^&*#",
  5950. },
  5951. },
  5952. },
  5953. },
  5954. },
  5955. },
  5956. }
  5957. for k, v := range errorCases {
  5958. if errs := validateContainers(v, false, volumeDevices, field.NewPath("field")); len(errs) == 0 {
  5959. t.Errorf("expected failure for %s", k)
  5960. }
  5961. }
  5962. }
  5963. func TestValidateInitContainers(t *testing.T) {
  5964. volumeDevices := make(map[string]core.VolumeSource)
  5965. capabilities.SetForTests(capabilities.Capabilities{
  5966. AllowPrivileged: true,
  5967. })
  5968. successCase := []core.Container{
  5969. {
  5970. Name: "container-1-same-host-port-different-protocol",
  5971. Image: "image",
  5972. Ports: []core.ContainerPort{
  5973. {ContainerPort: 80, HostPort: 80, Protocol: "TCP"},
  5974. {ContainerPort: 80, HostPort: 80, Protocol: "UDP"},
  5975. },
  5976. ImagePullPolicy: "IfNotPresent",
  5977. TerminationMessagePolicy: "File",
  5978. },
  5979. {
  5980. Name: "container-2-same-host-port-different-protocol",
  5981. Image: "image",
  5982. Ports: []core.ContainerPort{
  5983. {ContainerPort: 80, HostPort: 80, Protocol: "TCP"},
  5984. {ContainerPort: 80, HostPort: 80, Protocol: "UDP"},
  5985. },
  5986. ImagePullPolicy: "IfNotPresent",
  5987. TerminationMessagePolicy: "File",
  5988. },
  5989. }
  5990. if errs := validateContainers(successCase, true, volumeDevices, field.NewPath("field")); len(errs) != 0 {
  5991. t.Errorf("expected success: %v", errs)
  5992. }
  5993. capabilities.SetForTests(capabilities.Capabilities{
  5994. AllowPrivileged: false,
  5995. })
  5996. errorCases := map[string][]core.Container{
  5997. "duplicate ports": {
  5998. {
  5999. Name: "abc",
  6000. Image: "image",
  6001. Ports: []core.ContainerPort{
  6002. {
  6003. ContainerPort: 8080, HostPort: 8080, Protocol: "TCP",
  6004. },
  6005. {
  6006. ContainerPort: 8080, HostPort: 8080, Protocol: "TCP",
  6007. },
  6008. },
  6009. ImagePullPolicy: "IfNotPresent",
  6010. TerminationMessagePolicy: "File",
  6011. },
  6012. },
  6013. }
  6014. for k, v := range errorCases {
  6015. if errs := validateContainers(v, true, volumeDevices, field.NewPath("field")); len(errs) == 0 {
  6016. t.Errorf("expected failure for %s", k)
  6017. }
  6018. }
  6019. }
  6020. func TestValidateRestartPolicy(t *testing.T) {
  6021. successCases := []core.RestartPolicy{
  6022. core.RestartPolicyAlways,
  6023. core.RestartPolicyOnFailure,
  6024. core.RestartPolicyNever,
  6025. }
  6026. for _, policy := range successCases {
  6027. if errs := validateRestartPolicy(&policy, field.NewPath("field")); len(errs) != 0 {
  6028. t.Errorf("expected success: %v", errs)
  6029. }
  6030. }
  6031. errorCases := []core.RestartPolicy{"", "newpolicy"}
  6032. for k, policy := range errorCases {
  6033. if errs := validateRestartPolicy(&policy, field.NewPath("field")); len(errs) == 0 {
  6034. t.Errorf("expected failure for %d", k)
  6035. }
  6036. }
  6037. }
  6038. func TestValidateDNSPolicy(t *testing.T) {
  6039. successCases := []core.DNSPolicy{core.DNSClusterFirst, core.DNSDefault, core.DNSPolicy(core.DNSClusterFirst), core.DNSNone}
  6040. for _, policy := range successCases {
  6041. if errs := validateDNSPolicy(&policy, field.NewPath("field")); len(errs) != 0 {
  6042. t.Errorf("expected success: %v", errs)
  6043. }
  6044. }
  6045. errorCases := []core.DNSPolicy{core.DNSPolicy("invalid")}
  6046. for _, policy := range errorCases {
  6047. if errs := validateDNSPolicy(&policy, field.NewPath("field")); len(errs) == 0 {
  6048. t.Errorf("expected failure for %v", policy)
  6049. }
  6050. }
  6051. }
  6052. func TestValidatePodDNSConfig(t *testing.T) {
  6053. generateTestSearchPathFunc := func(numChars int) string {
  6054. res := ""
  6055. for i := 0; i < numChars; i++ {
  6056. res = res + "a"
  6057. }
  6058. return res
  6059. }
  6060. testOptionValue := "2"
  6061. testDNSNone := core.DNSNone
  6062. testDNSClusterFirst := core.DNSClusterFirst
  6063. testCases := []struct {
  6064. desc string
  6065. dnsConfig *core.PodDNSConfig
  6066. dnsPolicy *core.DNSPolicy
  6067. expectedError bool
  6068. }{
  6069. {
  6070. desc: "valid: empty DNSConfig",
  6071. dnsConfig: &core.PodDNSConfig{},
  6072. expectedError: false,
  6073. },
  6074. {
  6075. desc: "valid: 1 option",
  6076. dnsConfig: &core.PodDNSConfig{
  6077. Options: []core.PodDNSConfigOption{
  6078. {Name: "ndots", Value: &testOptionValue},
  6079. },
  6080. },
  6081. expectedError: false,
  6082. },
  6083. {
  6084. desc: "valid: 1 nameserver",
  6085. dnsConfig: &core.PodDNSConfig{
  6086. Nameservers: []string{"127.0.0.1"},
  6087. },
  6088. expectedError: false,
  6089. },
  6090. {
  6091. desc: "valid: DNSNone with 1 nameserver",
  6092. dnsConfig: &core.PodDNSConfig{
  6093. Nameservers: []string{"127.0.0.1"},
  6094. },
  6095. dnsPolicy: &testDNSNone,
  6096. expectedError: false,
  6097. },
  6098. {
  6099. desc: "valid: 1 search path",
  6100. dnsConfig: &core.PodDNSConfig{
  6101. Searches: []string{"custom"},
  6102. },
  6103. expectedError: false,
  6104. },
  6105. {
  6106. desc: "valid: 1 search path with trailing period",
  6107. dnsConfig: &core.PodDNSConfig{
  6108. Searches: []string{"custom."},
  6109. },
  6110. expectedError: false,
  6111. },
  6112. {
  6113. desc: "valid: 3 nameservers and 6 search paths",
  6114. dnsConfig: &core.PodDNSConfig{
  6115. Nameservers: []string{"127.0.0.1", "10.0.0.10", "8.8.8.8"},
  6116. Searches: []string{"custom", "mydomain.com", "local", "cluster.local", "svc.cluster.local", "default.svc.cluster.local."},
  6117. },
  6118. expectedError: false,
  6119. },
  6120. {
  6121. desc: "valid: 256 characters in search path list",
  6122. dnsConfig: &core.PodDNSConfig{
  6123. // We can have 256 - (6 - 1) = 251 characters in total for 6 search paths.
  6124. Searches: []string{
  6125. generateTestSearchPathFunc(1),
  6126. generateTestSearchPathFunc(50),
  6127. generateTestSearchPathFunc(50),
  6128. generateTestSearchPathFunc(50),
  6129. generateTestSearchPathFunc(50),
  6130. generateTestSearchPathFunc(50),
  6131. },
  6132. },
  6133. expectedError: false,
  6134. },
  6135. {
  6136. desc: "valid: ipv6 nameserver",
  6137. dnsConfig: &core.PodDNSConfig{
  6138. Nameservers: []string{"FE80::0202:B3FF:FE1E:8329"},
  6139. },
  6140. expectedError: false,
  6141. },
  6142. {
  6143. desc: "invalid: 4 nameservers",
  6144. dnsConfig: &core.PodDNSConfig{
  6145. Nameservers: []string{"127.0.0.1", "10.0.0.10", "8.8.8.8", "1.2.3.4"},
  6146. },
  6147. expectedError: true,
  6148. },
  6149. {
  6150. desc: "invalid: 7 search paths",
  6151. dnsConfig: &core.PodDNSConfig{
  6152. Searches: []string{"custom", "mydomain.com", "local", "cluster.local", "svc.cluster.local", "default.svc.cluster.local", "exceeded"},
  6153. },
  6154. expectedError: true,
  6155. },
  6156. {
  6157. desc: "invalid: 257 characters in search path list",
  6158. dnsConfig: &core.PodDNSConfig{
  6159. // We can have 256 - (6 - 1) = 251 characters in total for 6 search paths.
  6160. Searches: []string{
  6161. generateTestSearchPathFunc(2),
  6162. generateTestSearchPathFunc(50),
  6163. generateTestSearchPathFunc(50),
  6164. generateTestSearchPathFunc(50),
  6165. generateTestSearchPathFunc(50),
  6166. generateTestSearchPathFunc(50),
  6167. },
  6168. },
  6169. expectedError: true,
  6170. },
  6171. {
  6172. desc: "invalid search path",
  6173. dnsConfig: &core.PodDNSConfig{
  6174. Searches: []string{"custom?"},
  6175. },
  6176. expectedError: true,
  6177. },
  6178. {
  6179. desc: "invalid nameserver",
  6180. dnsConfig: &core.PodDNSConfig{
  6181. Nameservers: []string{"invalid"},
  6182. },
  6183. expectedError: true,
  6184. },
  6185. {
  6186. desc: "invalid empty option name",
  6187. dnsConfig: &core.PodDNSConfig{
  6188. Options: []core.PodDNSConfigOption{
  6189. {Value: &testOptionValue},
  6190. },
  6191. },
  6192. expectedError: true,
  6193. },
  6194. {
  6195. desc: "invalid: DNSNone with 0 nameserver",
  6196. dnsConfig: &core.PodDNSConfig{
  6197. Searches: []string{"custom"},
  6198. },
  6199. dnsPolicy: &testDNSNone,
  6200. expectedError: true,
  6201. },
  6202. }
  6203. for _, tc := range testCases {
  6204. if tc.dnsPolicy == nil {
  6205. tc.dnsPolicy = &testDNSClusterFirst
  6206. }
  6207. errs := validatePodDNSConfig(tc.dnsConfig, tc.dnsPolicy, field.NewPath("dnsConfig"))
  6208. if len(errs) != 0 && !tc.expectedError {
  6209. t.Errorf("%v: validatePodDNSConfig(%v) = %v, want nil", tc.desc, tc.dnsConfig, errs)
  6210. } else if len(errs) == 0 && tc.expectedError {
  6211. t.Errorf("%v: validatePodDNSConfig(%v) = nil, want error", tc.desc, tc.dnsConfig)
  6212. }
  6213. }
  6214. }
  6215. func TestValidatePodReadinessGates(t *testing.T) {
  6216. successCases := []struct {
  6217. desc string
  6218. readinessGates []core.PodReadinessGate
  6219. }{
  6220. {
  6221. "no gate",
  6222. []core.PodReadinessGate{},
  6223. },
  6224. {
  6225. "one readiness gate",
  6226. []core.PodReadinessGate{
  6227. {
  6228. ConditionType: core.PodConditionType("example.com/condition"),
  6229. },
  6230. },
  6231. },
  6232. {
  6233. "two readiness gates",
  6234. []core.PodReadinessGate{
  6235. {
  6236. ConditionType: core.PodConditionType("example.com/condition1"),
  6237. },
  6238. {
  6239. ConditionType: core.PodConditionType("example.com/condition2"),
  6240. },
  6241. },
  6242. },
  6243. }
  6244. for _, tc := range successCases {
  6245. if errs := validateReadinessGates(tc.readinessGates, field.NewPath("field")); len(errs) != 0 {
  6246. t.Errorf("expect tc %q to success: %v", tc.desc, errs)
  6247. }
  6248. }
  6249. errorCases := []struct {
  6250. desc string
  6251. readinessGates []core.PodReadinessGate
  6252. }{
  6253. {
  6254. "invalid condition type",
  6255. []core.PodReadinessGate{
  6256. {
  6257. ConditionType: core.PodConditionType("invalid/condition/type"),
  6258. },
  6259. },
  6260. },
  6261. }
  6262. for _, tc := range errorCases {
  6263. if errs := validateReadinessGates(tc.readinessGates, field.NewPath("field")); len(errs) == 0 {
  6264. t.Errorf("expected tc %q to fail", tc.desc)
  6265. }
  6266. }
  6267. }
  6268. func TestValidatePodConditions(t *testing.T) {
  6269. successCases := []struct {
  6270. desc string
  6271. podConditions []core.PodCondition
  6272. }{
  6273. {
  6274. "no condition",
  6275. []core.PodCondition{},
  6276. },
  6277. {
  6278. "one system condition",
  6279. []core.PodCondition{
  6280. {
  6281. Type: core.PodReady,
  6282. Status: core.ConditionTrue,
  6283. },
  6284. },
  6285. },
  6286. {
  6287. "one system condition and one custom condition",
  6288. []core.PodCondition{
  6289. {
  6290. Type: core.PodReady,
  6291. Status: core.ConditionTrue,
  6292. },
  6293. {
  6294. Type: core.PodConditionType("example.com/condition"),
  6295. Status: core.ConditionFalse,
  6296. },
  6297. },
  6298. },
  6299. {
  6300. "two custom condition",
  6301. []core.PodCondition{
  6302. {
  6303. Type: core.PodConditionType("foobar"),
  6304. Status: core.ConditionTrue,
  6305. },
  6306. {
  6307. Type: core.PodConditionType("example.com/condition"),
  6308. Status: core.ConditionFalse,
  6309. },
  6310. },
  6311. },
  6312. }
  6313. for _, tc := range successCases {
  6314. if errs := validatePodConditions(tc.podConditions, field.NewPath("field")); len(errs) != 0 {
  6315. t.Errorf("expected tc %q to success, but got: %v", tc.desc, errs)
  6316. }
  6317. }
  6318. errorCases := []struct {
  6319. desc string
  6320. podConditions []core.PodCondition
  6321. }{
  6322. {
  6323. "one system condition and a invalid custom condition",
  6324. []core.PodCondition{
  6325. {
  6326. Type: core.PodReady,
  6327. Status: core.ConditionStatus("True"),
  6328. },
  6329. {
  6330. Type: core.PodConditionType("invalid/custom/condition"),
  6331. Status: core.ConditionStatus("True"),
  6332. },
  6333. },
  6334. },
  6335. }
  6336. for _, tc := range errorCases {
  6337. if errs := validatePodConditions(tc.podConditions, field.NewPath("field")); len(errs) == 0 {
  6338. t.Errorf("expected tc %q to fail", tc.desc)
  6339. }
  6340. }
  6341. }
  6342. func TestValidatePodSpec(t *testing.T) {
  6343. activeDeadlineSeconds := int64(30)
  6344. activeDeadlineSecondsMax := int64(math.MaxInt32)
  6345. minUserID := int64(0)
  6346. maxUserID := int64(2147483647)
  6347. minGroupID := int64(0)
  6348. maxGroupID := int64(2147483647)
  6349. successCases := []core.PodSpec{
  6350. { // Populate basic fields, leave defaults for most.
  6351. Volumes: []core.Volume{{Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}}},
  6352. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6353. RestartPolicy: core.RestartPolicyAlways,
  6354. DNSPolicy: core.DNSClusterFirst,
  6355. },
  6356. { // Populate all fields.
  6357. Volumes: []core.Volume{
  6358. {Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}},
  6359. },
  6360. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6361. InitContainers: []core.Container{{Name: "ictr", Image: "iimage", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6362. RestartPolicy: core.RestartPolicyAlways,
  6363. NodeSelector: map[string]string{
  6364. "key": "value",
  6365. },
  6366. NodeName: "foobar",
  6367. DNSPolicy: core.DNSClusterFirst,
  6368. ActiveDeadlineSeconds: &activeDeadlineSeconds,
  6369. ServiceAccountName: "acct",
  6370. },
  6371. { // Populate all fields with larger active deadline.
  6372. Volumes: []core.Volume{
  6373. {Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}},
  6374. },
  6375. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6376. InitContainers: []core.Container{{Name: "ictr", Image: "iimage", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6377. RestartPolicy: core.RestartPolicyAlways,
  6378. NodeSelector: map[string]string{
  6379. "key": "value",
  6380. },
  6381. NodeName: "foobar",
  6382. DNSPolicy: core.DNSClusterFirst,
  6383. ActiveDeadlineSeconds: &activeDeadlineSecondsMax,
  6384. ServiceAccountName: "acct",
  6385. },
  6386. { // Populate HostNetwork.
  6387. Containers: []core.Container{
  6388. {Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
  6389. Ports: []core.ContainerPort{
  6390. {HostPort: 8080, ContainerPort: 8080, Protocol: "TCP"}},
  6391. },
  6392. },
  6393. SecurityContext: &core.PodSecurityContext{
  6394. HostNetwork: true,
  6395. },
  6396. RestartPolicy: core.RestartPolicyAlways,
  6397. DNSPolicy: core.DNSClusterFirst,
  6398. },
  6399. { // Populate RunAsUser SupplementalGroups FSGroup with minID 0
  6400. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6401. SecurityContext: &core.PodSecurityContext{
  6402. SupplementalGroups: []int64{minGroupID},
  6403. RunAsUser: &minUserID,
  6404. FSGroup: &minGroupID,
  6405. },
  6406. RestartPolicy: core.RestartPolicyAlways,
  6407. DNSPolicy: core.DNSClusterFirst,
  6408. },
  6409. { // Populate RunAsUser SupplementalGroups FSGroup with maxID 2147483647
  6410. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6411. SecurityContext: &core.PodSecurityContext{
  6412. SupplementalGroups: []int64{maxGroupID},
  6413. RunAsUser: &maxUserID,
  6414. FSGroup: &maxGroupID,
  6415. },
  6416. RestartPolicy: core.RestartPolicyAlways,
  6417. DNSPolicy: core.DNSClusterFirst,
  6418. },
  6419. { // Populate HostIPC.
  6420. SecurityContext: &core.PodSecurityContext{
  6421. HostIPC: true,
  6422. },
  6423. Volumes: []core.Volume{{Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}}},
  6424. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6425. RestartPolicy: core.RestartPolicyAlways,
  6426. DNSPolicy: core.DNSClusterFirst,
  6427. },
  6428. { // Populate HostPID.
  6429. SecurityContext: &core.PodSecurityContext{
  6430. HostPID: true,
  6431. },
  6432. Volumes: []core.Volume{{Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}}},
  6433. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6434. RestartPolicy: core.RestartPolicyAlways,
  6435. DNSPolicy: core.DNSClusterFirst,
  6436. },
  6437. { // Populate Affinity.
  6438. Volumes: []core.Volume{{Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}}},
  6439. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6440. RestartPolicy: core.RestartPolicyAlways,
  6441. DNSPolicy: core.DNSClusterFirst,
  6442. },
  6443. { // Populate HostAliases.
  6444. HostAliases: []core.HostAlias{{IP: "12.34.56.78", Hostnames: []string{"host1", "host2"}}},
  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. { // Populate HostAliases with `foo.bar` hostnames.
  6451. HostAliases: []core.HostAlias{{IP: "12.34.56.78", Hostnames: []string{"host1.foo", "host2.bar"}}},
  6452. Volumes: []core.Volume{{Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}}},
  6453. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6454. RestartPolicy: core.RestartPolicyAlways,
  6455. DNSPolicy: core.DNSClusterFirst,
  6456. },
  6457. { // Populate HostAliases with HostNetwork.
  6458. HostAliases: []core.HostAlias{{IP: "12.34.56.78", Hostnames: []string{"host1.foo", "host2.bar"}}},
  6459. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6460. SecurityContext: &core.PodSecurityContext{
  6461. HostNetwork: true,
  6462. },
  6463. RestartPolicy: core.RestartPolicyAlways,
  6464. DNSPolicy: core.DNSClusterFirst,
  6465. },
  6466. { // Populate PriorityClassName.
  6467. Volumes: []core.Volume{{Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}}},
  6468. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6469. RestartPolicy: core.RestartPolicyAlways,
  6470. DNSPolicy: core.DNSClusterFirst,
  6471. PriorityClassName: "valid-name",
  6472. },
  6473. { // Populate ShareProcessNamespace
  6474. Volumes: []core.Volume{{Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}}},
  6475. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6476. RestartPolicy: core.RestartPolicyAlways,
  6477. DNSPolicy: core.DNSClusterFirst,
  6478. SecurityContext: &core.PodSecurityContext{
  6479. ShareProcessNamespace: &[]bool{true}[0],
  6480. },
  6481. },
  6482. { // Populate RuntimeClassName
  6483. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6484. RestartPolicy: core.RestartPolicyAlways,
  6485. DNSPolicy: core.DNSClusterFirst,
  6486. RuntimeClassName: utilpointer.StringPtr("valid-sandbox"),
  6487. },
  6488. { // Populate Overhead
  6489. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6490. RestartPolicy: core.RestartPolicyAlways,
  6491. DNSPolicy: core.DNSClusterFirst,
  6492. RuntimeClassName: utilpointer.StringPtr("valid-sandbox"),
  6493. Overhead: core.ResourceList{},
  6494. },
  6495. }
  6496. for i := range successCases {
  6497. if errs := ValidatePodSpec(&successCases[i], field.NewPath("field")); len(errs) != 0 {
  6498. t.Errorf("expected success: %v", errs)
  6499. }
  6500. }
  6501. activeDeadlineSeconds = int64(0)
  6502. activeDeadlineSecondsTooLarge := int64(math.MaxInt32 + 1)
  6503. minUserID = int64(-1)
  6504. maxUserID = int64(2147483648)
  6505. minGroupID = int64(-1)
  6506. maxGroupID = int64(2147483648)
  6507. failureCases := map[string]core.PodSpec{
  6508. "bad volume": {
  6509. Volumes: []core.Volume{{}},
  6510. RestartPolicy: core.RestartPolicyAlways,
  6511. DNSPolicy: core.DNSClusterFirst,
  6512. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6513. },
  6514. "no containers": {
  6515. RestartPolicy: core.RestartPolicyAlways,
  6516. DNSPolicy: core.DNSClusterFirst,
  6517. },
  6518. "bad container": {
  6519. Containers: []core.Container{{}},
  6520. RestartPolicy: core.RestartPolicyAlways,
  6521. DNSPolicy: core.DNSClusterFirst,
  6522. },
  6523. "bad init container": {
  6524. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6525. InitContainers: []core.Container{{}},
  6526. RestartPolicy: core.RestartPolicyAlways,
  6527. DNSPolicy: core.DNSClusterFirst,
  6528. },
  6529. "bad DNS policy": {
  6530. DNSPolicy: core.DNSPolicy("invalid"),
  6531. RestartPolicy: core.RestartPolicyAlways,
  6532. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6533. },
  6534. "bad service account name": {
  6535. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6536. RestartPolicy: core.RestartPolicyAlways,
  6537. DNSPolicy: core.DNSClusterFirst,
  6538. ServiceAccountName: "invalidName",
  6539. },
  6540. "bad restart policy": {
  6541. RestartPolicy: "UnknowPolicy",
  6542. DNSPolicy: core.DNSClusterFirst,
  6543. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6544. },
  6545. "with hostNetwork hostPort not equal to containerPort": {
  6546. Containers: []core.Container{
  6547. {Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", Ports: []core.ContainerPort{
  6548. {HostPort: 8080, ContainerPort: 2600, Protocol: "TCP"}},
  6549. },
  6550. },
  6551. SecurityContext: &core.PodSecurityContext{
  6552. HostNetwork: true,
  6553. },
  6554. RestartPolicy: core.RestartPolicyAlways,
  6555. DNSPolicy: core.DNSClusterFirst,
  6556. },
  6557. "with hostAliases with invalid IP": {
  6558. SecurityContext: &core.PodSecurityContext{
  6559. HostNetwork: false,
  6560. },
  6561. HostAliases: []core.HostAlias{{IP: "999.999.999.999", Hostnames: []string{"host1", "host2"}}},
  6562. },
  6563. "with hostAliases with invalid hostname": {
  6564. SecurityContext: &core.PodSecurityContext{
  6565. HostNetwork: false,
  6566. },
  6567. HostAliases: []core.HostAlias{{IP: "12.34.56.78", Hostnames: []string{"@#$^#@#$"}}},
  6568. },
  6569. "bad supplementalGroups large than math.MaxInt32": {
  6570. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6571. SecurityContext: &core.PodSecurityContext{
  6572. HostNetwork: false,
  6573. SupplementalGroups: []int64{maxGroupID, 1234},
  6574. },
  6575. RestartPolicy: core.RestartPolicyAlways,
  6576. DNSPolicy: core.DNSClusterFirst,
  6577. },
  6578. "bad supplementalGroups less than 0": {
  6579. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6580. SecurityContext: &core.PodSecurityContext{
  6581. HostNetwork: false,
  6582. SupplementalGroups: []int64{minGroupID, 1234},
  6583. },
  6584. RestartPolicy: core.RestartPolicyAlways,
  6585. DNSPolicy: core.DNSClusterFirst,
  6586. },
  6587. "bad runAsUser large than math.MaxInt32": {
  6588. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6589. SecurityContext: &core.PodSecurityContext{
  6590. HostNetwork: false,
  6591. RunAsUser: &maxUserID,
  6592. },
  6593. RestartPolicy: core.RestartPolicyAlways,
  6594. DNSPolicy: core.DNSClusterFirst,
  6595. },
  6596. "bad runAsUser less than 0": {
  6597. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6598. SecurityContext: &core.PodSecurityContext{
  6599. HostNetwork: false,
  6600. RunAsUser: &minUserID,
  6601. },
  6602. RestartPolicy: core.RestartPolicyAlways,
  6603. DNSPolicy: core.DNSClusterFirst,
  6604. },
  6605. "bad fsGroup large than math.MaxInt32": {
  6606. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6607. SecurityContext: &core.PodSecurityContext{
  6608. HostNetwork: false,
  6609. FSGroup: &maxGroupID,
  6610. },
  6611. RestartPolicy: core.RestartPolicyAlways,
  6612. DNSPolicy: core.DNSClusterFirst,
  6613. },
  6614. "bad fsGroup less than 0": {
  6615. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6616. SecurityContext: &core.PodSecurityContext{
  6617. HostNetwork: false,
  6618. FSGroup: &minGroupID,
  6619. },
  6620. RestartPolicy: core.RestartPolicyAlways,
  6621. DNSPolicy: core.DNSClusterFirst,
  6622. },
  6623. "bad-active-deadline-seconds": {
  6624. Volumes: []core.Volume{
  6625. {Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}},
  6626. },
  6627. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6628. RestartPolicy: core.RestartPolicyAlways,
  6629. NodeSelector: map[string]string{
  6630. "key": "value",
  6631. },
  6632. NodeName: "foobar",
  6633. DNSPolicy: core.DNSClusterFirst,
  6634. ActiveDeadlineSeconds: &activeDeadlineSeconds,
  6635. },
  6636. "active-deadline-seconds-too-large": {
  6637. Volumes: []core.Volume{
  6638. {Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}},
  6639. },
  6640. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6641. RestartPolicy: core.RestartPolicyAlways,
  6642. NodeSelector: map[string]string{
  6643. "key": "value",
  6644. },
  6645. NodeName: "foobar",
  6646. DNSPolicy: core.DNSClusterFirst,
  6647. ActiveDeadlineSeconds: &activeDeadlineSecondsTooLarge,
  6648. },
  6649. "bad nodeName": {
  6650. NodeName: "node name",
  6651. Volumes: []core.Volume{{Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}}},
  6652. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6653. RestartPolicy: core.RestartPolicyAlways,
  6654. DNSPolicy: core.DNSClusterFirst,
  6655. },
  6656. "bad PriorityClassName": {
  6657. Volumes: []core.Volume{{Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}}},
  6658. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6659. RestartPolicy: core.RestartPolicyAlways,
  6660. DNSPolicy: core.DNSClusterFirst,
  6661. PriorityClassName: "InvalidName",
  6662. },
  6663. "ShareProcessNamespace and HostPID both set": {
  6664. Volumes: []core.Volume{{Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}}},
  6665. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6666. RestartPolicy: core.RestartPolicyAlways,
  6667. DNSPolicy: core.DNSClusterFirst,
  6668. SecurityContext: &core.PodSecurityContext{
  6669. HostPID: true,
  6670. ShareProcessNamespace: &[]bool{true}[0],
  6671. },
  6672. },
  6673. "bad RuntimeClassName": {
  6674. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6675. RestartPolicy: core.RestartPolicyAlways,
  6676. DNSPolicy: core.DNSClusterFirst,
  6677. RuntimeClassName: utilpointer.StringPtr("invalid/sandbox"),
  6678. },
  6679. }
  6680. for k, v := range failureCases {
  6681. if errs := ValidatePodSpec(&v, field.NewPath("field")); len(errs) == 0 {
  6682. t.Errorf("expected failure for %q", k)
  6683. }
  6684. }
  6685. }
  6686. func extendPodSpecwithTolerations(in core.PodSpec, tolerations []core.Toleration) core.PodSpec {
  6687. var out core.PodSpec
  6688. out.Containers = in.Containers
  6689. out.RestartPolicy = in.RestartPolicy
  6690. out.DNSPolicy = in.DNSPolicy
  6691. out.Tolerations = tolerations
  6692. return out
  6693. }
  6694. func TestValidatePod(t *testing.T) {
  6695. validPodSpec := func(affinity *core.Affinity) core.PodSpec {
  6696. spec := core.PodSpec{
  6697. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6698. RestartPolicy: core.RestartPolicyAlways,
  6699. DNSPolicy: core.DNSClusterFirst,
  6700. }
  6701. if affinity != nil {
  6702. spec.Affinity = affinity
  6703. }
  6704. return spec
  6705. }
  6706. successCases := []core.Pod{
  6707. { // Basic fields.
  6708. ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
  6709. Spec: core.PodSpec{
  6710. Volumes: []core.Volume{{Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}}},
  6711. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6712. RestartPolicy: core.RestartPolicyAlways,
  6713. DNSPolicy: core.DNSClusterFirst,
  6714. },
  6715. },
  6716. { // Just about everything.
  6717. ObjectMeta: metav1.ObjectMeta{Name: "abc.123.do-re-mi", Namespace: "ns"},
  6718. Spec: core.PodSpec{
  6719. Volumes: []core.Volume{
  6720. {Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}},
  6721. },
  6722. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  6723. RestartPolicy: core.RestartPolicyAlways,
  6724. DNSPolicy: core.DNSClusterFirst,
  6725. NodeSelector: map[string]string{
  6726. "key": "value",
  6727. },
  6728. NodeName: "foobar",
  6729. },
  6730. },
  6731. { // Serialized node affinity requirements.
  6732. ObjectMeta: metav1.ObjectMeta{
  6733. Name: "123",
  6734. Namespace: "ns",
  6735. },
  6736. Spec: validPodSpec(
  6737. // TODO: Uncomment and move this block and move inside NodeAffinity once
  6738. // RequiredDuringSchedulingRequiredDuringExecution is implemented
  6739. // RequiredDuringSchedulingRequiredDuringExecution: &core.NodeSelector{
  6740. // NodeSelectorTerms: []core.NodeSelectorTerm{
  6741. // {
  6742. // MatchExpressions: []core.NodeSelectorRequirement{
  6743. // {
  6744. // Key: "key1",
  6745. // Operator: core.NodeSelectorOpExists
  6746. // },
  6747. // },
  6748. // },
  6749. // },
  6750. // },
  6751. &core.Affinity{
  6752. NodeAffinity: &core.NodeAffinity{
  6753. RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
  6754. NodeSelectorTerms: []core.NodeSelectorTerm{
  6755. {
  6756. MatchExpressions: []core.NodeSelectorRequirement{
  6757. {
  6758. Key: "key2",
  6759. Operator: core.NodeSelectorOpIn,
  6760. Values: []string{"value1", "value2"},
  6761. },
  6762. },
  6763. MatchFields: []core.NodeSelectorRequirement{
  6764. {
  6765. Key: "metadata.name",
  6766. Operator: core.NodeSelectorOpIn,
  6767. Values: []string{"host1"},
  6768. },
  6769. },
  6770. },
  6771. },
  6772. },
  6773. PreferredDuringSchedulingIgnoredDuringExecution: []core.PreferredSchedulingTerm{
  6774. {
  6775. Weight: 10,
  6776. Preference: core.NodeSelectorTerm{
  6777. MatchExpressions: []core.NodeSelectorRequirement{
  6778. {
  6779. Key: "foo",
  6780. Operator: core.NodeSelectorOpIn,
  6781. Values: []string{"bar"},
  6782. },
  6783. },
  6784. },
  6785. },
  6786. },
  6787. },
  6788. },
  6789. ),
  6790. },
  6791. { // Serialized node affinity requirements.
  6792. ObjectMeta: metav1.ObjectMeta{
  6793. Name: "123",
  6794. Namespace: "ns",
  6795. },
  6796. Spec: validPodSpec(
  6797. // TODO: Uncomment and move this block and move inside NodeAffinity once
  6798. // RequiredDuringSchedulingRequiredDuringExecution is implemented
  6799. // RequiredDuringSchedulingRequiredDuringExecution: &core.NodeSelector{
  6800. // NodeSelectorTerms: []core.NodeSelectorTerm{
  6801. // {
  6802. // MatchExpressions: []core.NodeSelectorRequirement{
  6803. // {
  6804. // Key: "key1",
  6805. // Operator: core.NodeSelectorOpExists
  6806. // },
  6807. // },
  6808. // },
  6809. // },
  6810. // },
  6811. &core.Affinity{
  6812. NodeAffinity: &core.NodeAffinity{
  6813. RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
  6814. NodeSelectorTerms: []core.NodeSelectorTerm{
  6815. {
  6816. MatchExpressions: []core.NodeSelectorRequirement{},
  6817. },
  6818. },
  6819. },
  6820. PreferredDuringSchedulingIgnoredDuringExecution: []core.PreferredSchedulingTerm{
  6821. {
  6822. Weight: 10,
  6823. Preference: core.NodeSelectorTerm{
  6824. MatchExpressions: []core.NodeSelectorRequirement{},
  6825. },
  6826. },
  6827. },
  6828. },
  6829. },
  6830. ),
  6831. },
  6832. { // Serialized pod affinity in affinity requirements in annotations.
  6833. ObjectMeta: metav1.ObjectMeta{
  6834. Name: "123",
  6835. Namespace: "ns",
  6836. // TODO: Uncomment and move this block into Annotations map once
  6837. // RequiredDuringSchedulingRequiredDuringExecution is implemented
  6838. // "requiredDuringSchedulingRequiredDuringExecution": [{
  6839. // "labelSelector": {
  6840. // "matchExpressions": [{
  6841. // "key": "key2",
  6842. // "operator": "In",
  6843. // "values": ["value1", "value2"]
  6844. // }]
  6845. // },
  6846. // "namespaces":["ns"],
  6847. // "topologyKey": "zone"
  6848. // }]
  6849. },
  6850. Spec: validPodSpec(&core.Affinity{
  6851. PodAffinity: &core.PodAffinity{
  6852. RequiredDuringSchedulingIgnoredDuringExecution: []core.PodAffinityTerm{
  6853. {
  6854. LabelSelector: &metav1.LabelSelector{
  6855. MatchExpressions: []metav1.LabelSelectorRequirement{
  6856. {
  6857. Key: "key2",
  6858. Operator: metav1.LabelSelectorOpIn,
  6859. Values: []string{"value1", "value2"},
  6860. },
  6861. },
  6862. },
  6863. TopologyKey: "zone",
  6864. Namespaces: []string{"ns"},
  6865. },
  6866. },
  6867. PreferredDuringSchedulingIgnoredDuringExecution: []core.WeightedPodAffinityTerm{
  6868. {
  6869. Weight: 10,
  6870. PodAffinityTerm: core.PodAffinityTerm{
  6871. LabelSelector: &metav1.LabelSelector{
  6872. MatchExpressions: []metav1.LabelSelectorRequirement{
  6873. {
  6874. Key: "key2",
  6875. Operator: metav1.LabelSelectorOpNotIn,
  6876. Values: []string{"value1", "value2"},
  6877. },
  6878. },
  6879. },
  6880. Namespaces: []string{"ns"},
  6881. TopologyKey: "region",
  6882. },
  6883. },
  6884. },
  6885. },
  6886. }),
  6887. },
  6888. { // Serialized pod anti affinity with different Label Operators in affinity requirements in annotations.
  6889. ObjectMeta: metav1.ObjectMeta{
  6890. Name: "123",
  6891. Namespace: "ns",
  6892. // TODO: Uncomment and move this block into Annotations map once
  6893. // RequiredDuringSchedulingRequiredDuringExecution is implemented
  6894. // "requiredDuringSchedulingRequiredDuringExecution": [{
  6895. // "labelSelector": {
  6896. // "matchExpressions": [{
  6897. // "key": "key2",
  6898. // "operator": "In",
  6899. // "values": ["value1", "value2"]
  6900. // }]
  6901. // },
  6902. // "namespaces":["ns"],
  6903. // "topologyKey": "zone"
  6904. // }]
  6905. },
  6906. Spec: validPodSpec(&core.Affinity{
  6907. PodAntiAffinity: &core.PodAntiAffinity{
  6908. RequiredDuringSchedulingIgnoredDuringExecution: []core.PodAffinityTerm{
  6909. {
  6910. LabelSelector: &metav1.LabelSelector{
  6911. MatchExpressions: []metav1.LabelSelectorRequirement{
  6912. {
  6913. Key: "key2",
  6914. Operator: metav1.LabelSelectorOpExists,
  6915. },
  6916. },
  6917. },
  6918. TopologyKey: "zone",
  6919. Namespaces: []string{"ns"},
  6920. },
  6921. },
  6922. PreferredDuringSchedulingIgnoredDuringExecution: []core.WeightedPodAffinityTerm{
  6923. {
  6924. Weight: 10,
  6925. PodAffinityTerm: core.PodAffinityTerm{
  6926. LabelSelector: &metav1.LabelSelector{
  6927. MatchExpressions: []metav1.LabelSelectorRequirement{
  6928. {
  6929. Key: "key2",
  6930. Operator: metav1.LabelSelectorOpDoesNotExist,
  6931. },
  6932. },
  6933. },
  6934. Namespaces: []string{"ns"},
  6935. TopologyKey: "region",
  6936. },
  6937. },
  6938. },
  6939. },
  6940. }),
  6941. },
  6942. { // populate forgiveness tolerations with exists operator in annotations.
  6943. ObjectMeta: metav1.ObjectMeta{
  6944. Name: "123",
  6945. Namespace: "ns",
  6946. },
  6947. Spec: extendPodSpecwithTolerations(validPodSpec(nil), []core.Toleration{{Key: "foo", Operator: "Exists", Value: "", Effect: "NoExecute", TolerationSeconds: &[]int64{60}[0]}}),
  6948. },
  6949. { // populate forgiveness tolerations with equal operator in annotations.
  6950. ObjectMeta: metav1.ObjectMeta{
  6951. Name: "123",
  6952. Namespace: "ns",
  6953. },
  6954. Spec: extendPodSpecwithTolerations(validPodSpec(nil), []core.Toleration{{Key: "foo", Operator: "Equal", Value: "bar", Effect: "NoExecute", TolerationSeconds: &[]int64{60}[0]}}),
  6955. },
  6956. { // populate tolerations equal operator in annotations.
  6957. ObjectMeta: metav1.ObjectMeta{
  6958. Name: "123",
  6959. Namespace: "ns",
  6960. },
  6961. Spec: extendPodSpecwithTolerations(validPodSpec(nil), []core.Toleration{{Key: "foo", Operator: "Equal", Value: "bar", Effect: "NoSchedule"}}),
  6962. },
  6963. { // populate tolerations exists operator in annotations.
  6964. ObjectMeta: metav1.ObjectMeta{
  6965. Name: "123",
  6966. Namespace: "ns",
  6967. },
  6968. Spec: validPodSpec(nil),
  6969. },
  6970. { // empty key with Exists operator is OK for toleration, empty toleration key means match all taint keys.
  6971. ObjectMeta: metav1.ObjectMeta{
  6972. Name: "123",
  6973. Namespace: "ns",
  6974. },
  6975. Spec: extendPodSpecwithTolerations(validPodSpec(nil), []core.Toleration{{Operator: "Exists", Effect: "NoSchedule"}}),
  6976. },
  6977. { // empty operator is OK for toleration, defaults to Equal.
  6978. ObjectMeta: metav1.ObjectMeta{
  6979. Name: "123",
  6980. Namespace: "ns",
  6981. },
  6982. Spec: extendPodSpecwithTolerations(validPodSpec(nil), []core.Toleration{{Key: "foo", Value: "bar", Effect: "NoSchedule"}}),
  6983. },
  6984. { // empty effect is OK for toleration, empty toleration effect means match all taint effects.
  6985. ObjectMeta: metav1.ObjectMeta{
  6986. Name: "123",
  6987. Namespace: "ns",
  6988. },
  6989. Spec: extendPodSpecwithTolerations(validPodSpec(nil), []core.Toleration{{Key: "foo", Operator: "Equal", Value: "bar"}}),
  6990. },
  6991. { // negative tolerationSeconds is OK for toleration.
  6992. ObjectMeta: metav1.ObjectMeta{
  6993. Name: "pod-forgiveness-invalid",
  6994. Namespace: "ns",
  6995. },
  6996. Spec: extendPodSpecwithTolerations(validPodSpec(nil), []core.Toleration{{Key: "node.kubernetes.io/not-ready", Operator: "Exists", Effect: "NoExecute", TolerationSeconds: &[]int64{-2}[0]}}),
  6997. },
  6998. { // runtime default seccomp profile
  6999. ObjectMeta: metav1.ObjectMeta{
  7000. Name: "123",
  7001. Namespace: "ns",
  7002. Annotations: map[string]string{
  7003. core.SeccompPodAnnotationKey: core.SeccompProfileRuntimeDefault,
  7004. },
  7005. },
  7006. Spec: validPodSpec(nil),
  7007. },
  7008. { // docker default seccomp profile
  7009. ObjectMeta: metav1.ObjectMeta{
  7010. Name: "123",
  7011. Namespace: "ns",
  7012. Annotations: map[string]string{
  7013. core.SeccompPodAnnotationKey: core.DeprecatedSeccompProfileDockerDefault,
  7014. },
  7015. },
  7016. Spec: validPodSpec(nil),
  7017. },
  7018. { // unconfined seccomp profile
  7019. ObjectMeta: metav1.ObjectMeta{
  7020. Name: "123",
  7021. Namespace: "ns",
  7022. Annotations: map[string]string{
  7023. core.SeccompPodAnnotationKey: "unconfined",
  7024. },
  7025. },
  7026. Spec: validPodSpec(nil),
  7027. },
  7028. { // localhost seccomp profile
  7029. ObjectMeta: metav1.ObjectMeta{
  7030. Name: "123",
  7031. Namespace: "ns",
  7032. Annotations: map[string]string{
  7033. core.SeccompPodAnnotationKey: "localhost/foo",
  7034. },
  7035. },
  7036. Spec: validPodSpec(nil),
  7037. },
  7038. { // localhost seccomp profile for a container
  7039. ObjectMeta: metav1.ObjectMeta{
  7040. Name: "123",
  7041. Namespace: "ns",
  7042. Annotations: map[string]string{
  7043. core.SeccompContainerAnnotationKeyPrefix + "foo": "localhost/foo",
  7044. },
  7045. },
  7046. Spec: validPodSpec(nil),
  7047. },
  7048. { // default AppArmor profile for a container
  7049. ObjectMeta: metav1.ObjectMeta{
  7050. Name: "123",
  7051. Namespace: "ns",
  7052. Annotations: map[string]string{
  7053. apparmor.ContainerAnnotationKeyPrefix + "ctr": apparmor.ProfileRuntimeDefault,
  7054. },
  7055. },
  7056. Spec: validPodSpec(nil),
  7057. },
  7058. { // default AppArmor profile for an init container
  7059. ObjectMeta: metav1.ObjectMeta{
  7060. Name: "123",
  7061. Namespace: "ns",
  7062. Annotations: map[string]string{
  7063. apparmor.ContainerAnnotationKeyPrefix + "init-ctr": apparmor.ProfileRuntimeDefault,
  7064. },
  7065. },
  7066. Spec: core.PodSpec{
  7067. InitContainers: []core.Container{{Name: "init-ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  7068. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  7069. RestartPolicy: core.RestartPolicyAlways,
  7070. DNSPolicy: core.DNSClusterFirst,
  7071. },
  7072. },
  7073. { // localhost AppArmor profile for a container
  7074. ObjectMeta: metav1.ObjectMeta{
  7075. Name: "123",
  7076. Namespace: "ns",
  7077. Annotations: map[string]string{
  7078. apparmor.ContainerAnnotationKeyPrefix + "ctr": apparmor.ProfileNamePrefix + "foo",
  7079. },
  7080. },
  7081. Spec: validPodSpec(nil),
  7082. },
  7083. { // syntactically valid sysctls
  7084. ObjectMeta: metav1.ObjectMeta{
  7085. Name: "123",
  7086. Namespace: "ns",
  7087. },
  7088. Spec: core.PodSpec{
  7089. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  7090. RestartPolicy: core.RestartPolicyAlways,
  7091. DNSPolicy: core.DNSClusterFirst,
  7092. SecurityContext: &core.PodSecurityContext{
  7093. Sysctls: []core.Sysctl{
  7094. {
  7095. Name: "kernel.shmmni",
  7096. Value: "32768",
  7097. },
  7098. {
  7099. Name: "kernel.shmmax",
  7100. Value: "1000000000",
  7101. },
  7102. {
  7103. Name: "knet.ipv4.route.min_pmtu",
  7104. Value: "1000",
  7105. },
  7106. },
  7107. },
  7108. },
  7109. },
  7110. { // valid extended resources for init container
  7111. ObjectMeta: metav1.ObjectMeta{Name: "valid-extended", Namespace: "ns"},
  7112. Spec: core.PodSpec{
  7113. InitContainers: []core.Container{
  7114. {
  7115. Name: "valid-extended",
  7116. Image: "image",
  7117. ImagePullPolicy: "IfNotPresent",
  7118. Resources: core.ResourceRequirements{
  7119. Requests: core.ResourceList{
  7120. core.ResourceName("example.com/a"): resource.MustParse("10"),
  7121. },
  7122. Limits: core.ResourceList{
  7123. core.ResourceName("example.com/a"): resource.MustParse("10"),
  7124. },
  7125. },
  7126. TerminationMessagePolicy: "File",
  7127. },
  7128. },
  7129. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  7130. RestartPolicy: core.RestartPolicyAlways,
  7131. DNSPolicy: core.DNSClusterFirst,
  7132. },
  7133. },
  7134. { // valid extended resources for regular container
  7135. ObjectMeta: metav1.ObjectMeta{Name: "valid-extended", Namespace: "ns"},
  7136. Spec: core.PodSpec{
  7137. InitContainers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  7138. Containers: []core.Container{
  7139. {
  7140. Name: "valid-extended",
  7141. Image: "image",
  7142. ImagePullPolicy: "IfNotPresent",
  7143. Resources: core.ResourceRequirements{
  7144. Requests: core.ResourceList{
  7145. core.ResourceName("example.com/a"): resource.MustParse("10"),
  7146. },
  7147. Limits: core.ResourceList{
  7148. core.ResourceName("example.com/a"): resource.MustParse("10"),
  7149. },
  7150. },
  7151. TerminationMessagePolicy: "File",
  7152. },
  7153. },
  7154. RestartPolicy: core.RestartPolicyAlways,
  7155. DNSPolicy: core.DNSClusterFirst,
  7156. },
  7157. },
  7158. { // valid serviceaccount token projected volume with serviceaccount name specified
  7159. ObjectMeta: metav1.ObjectMeta{Name: "valid-extended", Namespace: "ns"},
  7160. Spec: core.PodSpec{
  7161. ServiceAccountName: "some-service-account",
  7162. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  7163. RestartPolicy: core.RestartPolicyAlways,
  7164. DNSPolicy: core.DNSClusterFirst,
  7165. Volumes: []core.Volume{
  7166. {
  7167. Name: "projected-volume",
  7168. VolumeSource: core.VolumeSource{
  7169. Projected: &core.ProjectedVolumeSource{
  7170. Sources: []core.VolumeProjection{
  7171. {
  7172. ServiceAccountToken: &core.ServiceAccountTokenProjection{
  7173. Audience: "foo-audience",
  7174. ExpirationSeconds: 6000,
  7175. Path: "foo-path",
  7176. },
  7177. },
  7178. },
  7179. },
  7180. },
  7181. },
  7182. },
  7183. },
  7184. },
  7185. }
  7186. for _, pod := range successCases {
  7187. if errs := ValidatePod(&pod); len(errs) != 0 {
  7188. t.Errorf("expected success: %v", errs)
  7189. }
  7190. }
  7191. errorCases := map[string]struct {
  7192. spec core.Pod
  7193. expectedError string
  7194. }{
  7195. "bad name": {
  7196. expectedError: "metadata.name",
  7197. spec: core.Pod{
  7198. ObjectMeta: metav1.ObjectMeta{Name: "", Namespace: "ns"},
  7199. Spec: core.PodSpec{
  7200. RestartPolicy: core.RestartPolicyAlways,
  7201. DNSPolicy: core.DNSClusterFirst,
  7202. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  7203. },
  7204. },
  7205. },
  7206. "image whitespace": {
  7207. expectedError: "spec.containers[0].image",
  7208. spec: core.Pod{
  7209. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "ns"},
  7210. Spec: core.PodSpec{
  7211. RestartPolicy: core.RestartPolicyAlways,
  7212. DNSPolicy: core.DNSClusterFirst,
  7213. Containers: []core.Container{{Name: "ctr", Image: " ", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  7214. },
  7215. },
  7216. },
  7217. "image leading and trailing whitespace": {
  7218. expectedError: "spec.containers[0].image",
  7219. spec: core.Pod{
  7220. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "ns"},
  7221. Spec: core.PodSpec{
  7222. RestartPolicy: core.RestartPolicyAlways,
  7223. DNSPolicy: core.DNSClusterFirst,
  7224. Containers: []core.Container{{Name: "ctr", Image: " something ", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  7225. },
  7226. },
  7227. },
  7228. "bad namespace": {
  7229. expectedError: "metadata.namespace",
  7230. spec: core.Pod{
  7231. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: ""},
  7232. Spec: core.PodSpec{
  7233. RestartPolicy: core.RestartPolicyAlways,
  7234. DNSPolicy: core.DNSClusterFirst,
  7235. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  7236. },
  7237. },
  7238. },
  7239. "bad spec": {
  7240. expectedError: "spec.containers[0].name",
  7241. spec: core.Pod{
  7242. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "ns"},
  7243. Spec: core.PodSpec{
  7244. Containers: []core.Container{{}},
  7245. },
  7246. },
  7247. },
  7248. "bad label": {
  7249. expectedError: "NoUppercaseOrSpecialCharsLike=Equals",
  7250. spec: core.Pod{
  7251. ObjectMeta: metav1.ObjectMeta{
  7252. Name: "abc",
  7253. Namespace: "ns",
  7254. Labels: map[string]string{
  7255. "NoUppercaseOrSpecialCharsLike=Equals": "bar",
  7256. },
  7257. },
  7258. Spec: core.PodSpec{
  7259. RestartPolicy: core.RestartPolicyAlways,
  7260. DNSPolicy: core.DNSClusterFirst,
  7261. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  7262. },
  7263. },
  7264. },
  7265. "invalid node selector requirement in node affinity, operator can't be null": {
  7266. expectedError: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].operator",
  7267. spec: core.Pod{
  7268. ObjectMeta: metav1.ObjectMeta{
  7269. Name: "123",
  7270. Namespace: "ns",
  7271. },
  7272. Spec: validPodSpec(&core.Affinity{
  7273. NodeAffinity: &core.NodeAffinity{
  7274. RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
  7275. NodeSelectorTerms: []core.NodeSelectorTerm{
  7276. {
  7277. MatchExpressions: []core.NodeSelectorRequirement{
  7278. {
  7279. Key: "key1",
  7280. },
  7281. },
  7282. },
  7283. },
  7284. },
  7285. },
  7286. }),
  7287. },
  7288. },
  7289. "invalid node selector requirement in node affinity, key is invalid": {
  7290. expectedError: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].key",
  7291. spec: core.Pod{
  7292. ObjectMeta: metav1.ObjectMeta{
  7293. Name: "123",
  7294. Namespace: "ns",
  7295. },
  7296. Spec: validPodSpec(&core.Affinity{
  7297. NodeAffinity: &core.NodeAffinity{
  7298. RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
  7299. NodeSelectorTerms: []core.NodeSelectorTerm{
  7300. {
  7301. MatchExpressions: []core.NodeSelectorRequirement{
  7302. {
  7303. Key: "invalid key ___@#",
  7304. Operator: core.NodeSelectorOpExists,
  7305. },
  7306. },
  7307. },
  7308. },
  7309. },
  7310. },
  7311. }),
  7312. },
  7313. },
  7314. "invalid node field selector requirement in node affinity, more values for field selector": {
  7315. expectedError: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchFields[0].values",
  7316. spec: core.Pod{
  7317. ObjectMeta: metav1.ObjectMeta{
  7318. Name: "123",
  7319. Namespace: "ns",
  7320. },
  7321. Spec: validPodSpec(&core.Affinity{
  7322. NodeAffinity: &core.NodeAffinity{
  7323. RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
  7324. NodeSelectorTerms: []core.NodeSelectorTerm{
  7325. {
  7326. MatchFields: []core.NodeSelectorRequirement{
  7327. {
  7328. Key: "metadata.name",
  7329. Operator: core.NodeSelectorOpIn,
  7330. Values: []string{"host1", "host2"},
  7331. },
  7332. },
  7333. },
  7334. },
  7335. },
  7336. },
  7337. }),
  7338. },
  7339. },
  7340. "invalid node field selector requirement in node affinity, invalid operator": {
  7341. expectedError: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchFields[0].operator",
  7342. spec: core.Pod{
  7343. ObjectMeta: metav1.ObjectMeta{
  7344. Name: "123",
  7345. Namespace: "ns",
  7346. },
  7347. Spec: validPodSpec(&core.Affinity{
  7348. NodeAffinity: &core.NodeAffinity{
  7349. RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
  7350. NodeSelectorTerms: []core.NodeSelectorTerm{
  7351. {
  7352. MatchFields: []core.NodeSelectorRequirement{
  7353. {
  7354. Key: "metadata.name",
  7355. Operator: core.NodeSelectorOpExists,
  7356. },
  7357. },
  7358. },
  7359. },
  7360. },
  7361. },
  7362. }),
  7363. },
  7364. },
  7365. "invalid node field selector requirement in node affinity, invalid key": {
  7366. expectedError: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchFields[0].key",
  7367. spec: core.Pod{
  7368. ObjectMeta: metav1.ObjectMeta{
  7369. Name: "123",
  7370. Namespace: "ns",
  7371. },
  7372. Spec: validPodSpec(&core.Affinity{
  7373. NodeAffinity: &core.NodeAffinity{
  7374. RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
  7375. NodeSelectorTerms: []core.NodeSelectorTerm{
  7376. {
  7377. MatchFields: []core.NodeSelectorRequirement{
  7378. {
  7379. Key: "metadata.namespace",
  7380. Operator: core.NodeSelectorOpIn,
  7381. Values: []string{"ns1"},
  7382. },
  7383. },
  7384. },
  7385. },
  7386. },
  7387. },
  7388. }),
  7389. },
  7390. },
  7391. "invalid preferredSchedulingTerm in node affinity, weight should be in range 1-100": {
  7392. expectedError: "must be in the range 1-100",
  7393. spec: core.Pod{
  7394. ObjectMeta: metav1.ObjectMeta{
  7395. Name: "123",
  7396. Namespace: "ns",
  7397. },
  7398. Spec: validPodSpec(&core.Affinity{
  7399. NodeAffinity: &core.NodeAffinity{
  7400. PreferredDuringSchedulingIgnoredDuringExecution: []core.PreferredSchedulingTerm{
  7401. {
  7402. Weight: 199,
  7403. Preference: core.NodeSelectorTerm{
  7404. MatchExpressions: []core.NodeSelectorRequirement{
  7405. {
  7406. Key: "foo",
  7407. Operator: core.NodeSelectorOpIn,
  7408. Values: []string{"bar"},
  7409. },
  7410. },
  7411. },
  7412. },
  7413. },
  7414. },
  7415. }),
  7416. },
  7417. },
  7418. "invalid requiredDuringSchedulingIgnoredDuringExecution node selector, nodeSelectorTerms must have at least one term": {
  7419. expectedError: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms",
  7420. spec: core.Pod{
  7421. ObjectMeta: metav1.ObjectMeta{
  7422. Name: "123",
  7423. Namespace: "ns",
  7424. },
  7425. Spec: validPodSpec(&core.Affinity{
  7426. NodeAffinity: &core.NodeAffinity{
  7427. RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
  7428. NodeSelectorTerms: []core.NodeSelectorTerm{},
  7429. },
  7430. },
  7431. }),
  7432. },
  7433. },
  7434. "invalid weight in preferredDuringSchedulingIgnoredDuringExecution in pod affinity annotations, weight should be in range 1-100": {
  7435. expectedError: "must be in the range 1-100",
  7436. spec: core.Pod{
  7437. ObjectMeta: metav1.ObjectMeta{
  7438. Name: "123",
  7439. Namespace: "ns",
  7440. },
  7441. Spec: validPodSpec(&core.Affinity{
  7442. PodAffinity: &core.PodAffinity{
  7443. PreferredDuringSchedulingIgnoredDuringExecution: []core.WeightedPodAffinityTerm{
  7444. {
  7445. Weight: 109,
  7446. PodAffinityTerm: core.PodAffinityTerm{
  7447. LabelSelector: &metav1.LabelSelector{
  7448. MatchExpressions: []metav1.LabelSelectorRequirement{
  7449. {
  7450. Key: "key2",
  7451. Operator: metav1.LabelSelectorOpNotIn,
  7452. Values: []string{"value1", "value2"},
  7453. },
  7454. },
  7455. },
  7456. Namespaces: []string{"ns"},
  7457. TopologyKey: "region",
  7458. },
  7459. },
  7460. },
  7461. },
  7462. }),
  7463. },
  7464. },
  7465. "invalid labelSelector in preferredDuringSchedulingIgnoredDuringExecution in podaffinity annotations, values should be empty if the operator is Exists": {
  7466. expectedError: "spec.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution[0].podAffinityTerm.matchExpressions.matchExpressions[0].values",
  7467. spec: core.Pod{
  7468. ObjectMeta: metav1.ObjectMeta{
  7469. Name: "123",
  7470. Namespace: "ns",
  7471. },
  7472. Spec: validPodSpec(&core.Affinity{
  7473. PodAntiAffinity: &core.PodAntiAffinity{
  7474. PreferredDuringSchedulingIgnoredDuringExecution: []core.WeightedPodAffinityTerm{
  7475. {
  7476. Weight: 10,
  7477. PodAffinityTerm: core.PodAffinityTerm{
  7478. LabelSelector: &metav1.LabelSelector{
  7479. MatchExpressions: []metav1.LabelSelectorRequirement{
  7480. {
  7481. Key: "key2",
  7482. Operator: metav1.LabelSelectorOpExists,
  7483. Values: []string{"value1", "value2"},
  7484. },
  7485. },
  7486. },
  7487. Namespaces: []string{"ns"},
  7488. TopologyKey: "region",
  7489. },
  7490. },
  7491. },
  7492. },
  7493. }),
  7494. },
  7495. },
  7496. "invalid name space in preferredDuringSchedulingIgnoredDuringExecution in podaffinity annotations, name space shouldbe valid": {
  7497. expectedError: "spec.affinity.podAffinity.preferredDuringSchedulingIgnoredDuringExecution[0].podAffinityTerm.namespace",
  7498. spec: core.Pod{
  7499. ObjectMeta: metav1.ObjectMeta{
  7500. Name: "123",
  7501. Namespace: "ns",
  7502. },
  7503. Spec: validPodSpec(&core.Affinity{
  7504. PodAffinity: &core.PodAffinity{
  7505. PreferredDuringSchedulingIgnoredDuringExecution: []core.WeightedPodAffinityTerm{
  7506. {
  7507. Weight: 10,
  7508. PodAffinityTerm: core.PodAffinityTerm{
  7509. LabelSelector: &metav1.LabelSelector{
  7510. MatchExpressions: []metav1.LabelSelectorRequirement{
  7511. {
  7512. Key: "key2",
  7513. Operator: metav1.LabelSelectorOpExists,
  7514. },
  7515. },
  7516. },
  7517. Namespaces: []string{"INVALID_NAMESPACE"},
  7518. TopologyKey: "region",
  7519. },
  7520. },
  7521. },
  7522. },
  7523. }),
  7524. },
  7525. },
  7526. "invalid hard pod affinity, empty topologyKey is not allowed for hard pod affinity": {
  7527. expectedError: "can not be empty",
  7528. spec: core.Pod{
  7529. ObjectMeta: metav1.ObjectMeta{
  7530. Name: "123",
  7531. Namespace: "ns",
  7532. },
  7533. Spec: validPodSpec(&core.Affinity{
  7534. PodAffinity: &core.PodAffinity{
  7535. RequiredDuringSchedulingIgnoredDuringExecution: []core.PodAffinityTerm{
  7536. {
  7537. LabelSelector: &metav1.LabelSelector{
  7538. MatchExpressions: []metav1.LabelSelectorRequirement{
  7539. {
  7540. Key: "key2",
  7541. Operator: metav1.LabelSelectorOpIn,
  7542. Values: []string{"value1", "value2"},
  7543. },
  7544. },
  7545. },
  7546. Namespaces: []string{"ns"},
  7547. },
  7548. },
  7549. },
  7550. }),
  7551. },
  7552. },
  7553. "invalid hard pod anti-affinity, empty topologyKey is not allowed for hard pod anti-affinity": {
  7554. expectedError: "can not be empty",
  7555. spec: core.Pod{
  7556. ObjectMeta: metav1.ObjectMeta{
  7557. Name: "123",
  7558. Namespace: "ns",
  7559. },
  7560. Spec: validPodSpec(&core.Affinity{
  7561. PodAntiAffinity: &core.PodAntiAffinity{
  7562. RequiredDuringSchedulingIgnoredDuringExecution: []core.PodAffinityTerm{
  7563. {
  7564. LabelSelector: &metav1.LabelSelector{
  7565. MatchExpressions: []metav1.LabelSelectorRequirement{
  7566. {
  7567. Key: "key2",
  7568. Operator: metav1.LabelSelectorOpIn,
  7569. Values: []string{"value1", "value2"},
  7570. },
  7571. },
  7572. },
  7573. Namespaces: []string{"ns"},
  7574. },
  7575. },
  7576. },
  7577. }),
  7578. },
  7579. },
  7580. "invalid soft pod affinity, empty topologyKey is not allowed for soft pod affinity": {
  7581. expectedError: "can not be empty",
  7582. spec: core.Pod{
  7583. ObjectMeta: metav1.ObjectMeta{
  7584. Name: "123",
  7585. Namespace: "ns",
  7586. },
  7587. Spec: validPodSpec(&core.Affinity{
  7588. PodAffinity: &core.PodAffinity{
  7589. PreferredDuringSchedulingIgnoredDuringExecution: []core.WeightedPodAffinityTerm{
  7590. {
  7591. Weight: 10,
  7592. PodAffinityTerm: core.PodAffinityTerm{
  7593. LabelSelector: &metav1.LabelSelector{
  7594. MatchExpressions: []metav1.LabelSelectorRequirement{
  7595. {
  7596. Key: "key2",
  7597. Operator: metav1.LabelSelectorOpNotIn,
  7598. Values: []string{"value1", "value2"},
  7599. },
  7600. },
  7601. },
  7602. Namespaces: []string{"ns"},
  7603. },
  7604. },
  7605. },
  7606. },
  7607. }),
  7608. },
  7609. },
  7610. "invalid soft pod anti-affinity, empty topologyKey is not allowed for soft pod anti-affinity": {
  7611. expectedError: "can not be empty",
  7612. spec: core.Pod{
  7613. ObjectMeta: metav1.ObjectMeta{
  7614. Name: "123",
  7615. Namespace: "ns",
  7616. },
  7617. Spec: validPodSpec(&core.Affinity{
  7618. PodAntiAffinity: &core.PodAntiAffinity{
  7619. PreferredDuringSchedulingIgnoredDuringExecution: []core.WeightedPodAffinityTerm{
  7620. {
  7621. Weight: 10,
  7622. PodAffinityTerm: core.PodAffinityTerm{
  7623. LabelSelector: &metav1.LabelSelector{
  7624. MatchExpressions: []metav1.LabelSelectorRequirement{
  7625. {
  7626. Key: "key2",
  7627. Operator: metav1.LabelSelectorOpNotIn,
  7628. Values: []string{"value1", "value2"},
  7629. },
  7630. },
  7631. },
  7632. Namespaces: []string{"ns"},
  7633. },
  7634. },
  7635. },
  7636. },
  7637. }),
  7638. },
  7639. },
  7640. "invalid toleration key": {
  7641. expectedError: "spec.tolerations[0].key",
  7642. spec: core.Pod{
  7643. ObjectMeta: metav1.ObjectMeta{
  7644. Name: "123",
  7645. Namespace: "ns",
  7646. },
  7647. Spec: extendPodSpecwithTolerations(validPodSpec(nil), []core.Toleration{{Key: "nospecialchars^=@", Operator: "Equal", Value: "bar", Effect: "NoSchedule"}}),
  7648. },
  7649. },
  7650. "invalid toleration operator": {
  7651. expectedError: "spec.tolerations[0].operator",
  7652. spec: core.Pod{
  7653. ObjectMeta: metav1.ObjectMeta{
  7654. Name: "123",
  7655. Namespace: "ns",
  7656. },
  7657. Spec: extendPodSpecwithTolerations(validPodSpec(nil), []core.Toleration{{Key: "foo", Operator: "In", Value: "bar", Effect: "NoSchedule"}}),
  7658. },
  7659. },
  7660. "value must be empty when `operator` is 'Exists'": {
  7661. expectedError: "spec.tolerations[0].operator",
  7662. spec: core.Pod{
  7663. ObjectMeta: metav1.ObjectMeta{
  7664. Name: "123",
  7665. Namespace: "ns",
  7666. },
  7667. Spec: extendPodSpecwithTolerations(validPodSpec(nil), []core.Toleration{{Key: "foo", Operator: "Exists", Value: "bar", Effect: "NoSchedule"}}),
  7668. },
  7669. },
  7670. "operator must be 'Exists' when `key` is empty": {
  7671. expectedError: "spec.tolerations[0].operator",
  7672. spec: core.Pod{
  7673. ObjectMeta: metav1.ObjectMeta{
  7674. Name: "123",
  7675. Namespace: "ns",
  7676. },
  7677. Spec: extendPodSpecwithTolerations(validPodSpec(nil), []core.Toleration{{Operator: "Equal", Value: "bar", Effect: "NoSchedule"}}),
  7678. },
  7679. },
  7680. "effect must be 'NoExecute' when `TolerationSeconds` is set": {
  7681. expectedError: "spec.tolerations[0].effect",
  7682. spec: core.Pod{
  7683. ObjectMeta: metav1.ObjectMeta{
  7684. Name: "pod-forgiveness-invalid",
  7685. Namespace: "ns",
  7686. },
  7687. Spec: extendPodSpecwithTolerations(validPodSpec(nil), []core.Toleration{{Key: "node.kubernetes.io/not-ready", Operator: "Exists", Effect: "NoSchedule", TolerationSeconds: &[]int64{20}[0]}}),
  7688. },
  7689. },
  7690. "must be a valid pod seccomp profile": {
  7691. expectedError: "must be a valid seccomp profile",
  7692. spec: core.Pod{
  7693. ObjectMeta: metav1.ObjectMeta{
  7694. Name: "123",
  7695. Namespace: "ns",
  7696. Annotations: map[string]string{
  7697. core.SeccompPodAnnotationKey: "foo",
  7698. },
  7699. },
  7700. Spec: validPodSpec(nil),
  7701. },
  7702. },
  7703. "must be a valid container seccomp profile": {
  7704. expectedError: "must be a valid seccomp profile",
  7705. spec: core.Pod{
  7706. ObjectMeta: metav1.ObjectMeta{
  7707. Name: "123",
  7708. Namespace: "ns",
  7709. Annotations: map[string]string{
  7710. core.SeccompContainerAnnotationKeyPrefix + "foo": "foo",
  7711. },
  7712. },
  7713. Spec: validPodSpec(nil),
  7714. },
  7715. },
  7716. "must be a non-empty container name in seccomp annotation": {
  7717. expectedError: "name part must be non-empty",
  7718. spec: core.Pod{
  7719. ObjectMeta: metav1.ObjectMeta{
  7720. Name: "123",
  7721. Namespace: "ns",
  7722. Annotations: map[string]string{
  7723. core.SeccompContainerAnnotationKeyPrefix: "foo",
  7724. },
  7725. },
  7726. Spec: validPodSpec(nil),
  7727. },
  7728. },
  7729. "must be a non-empty container profile in seccomp annotation": {
  7730. expectedError: "must be a valid seccomp profile",
  7731. spec: core.Pod{
  7732. ObjectMeta: metav1.ObjectMeta{
  7733. Name: "123",
  7734. Namespace: "ns",
  7735. Annotations: map[string]string{
  7736. core.SeccompContainerAnnotationKeyPrefix + "foo": "",
  7737. },
  7738. },
  7739. Spec: validPodSpec(nil),
  7740. },
  7741. },
  7742. "must be a relative path in a node-local seccomp profile annotation": {
  7743. expectedError: "must be a relative path",
  7744. spec: core.Pod{
  7745. ObjectMeta: metav1.ObjectMeta{
  7746. Name: "123",
  7747. Namespace: "ns",
  7748. Annotations: map[string]string{
  7749. core.SeccompPodAnnotationKey: "localhost//foo",
  7750. },
  7751. },
  7752. Spec: validPodSpec(nil),
  7753. },
  7754. },
  7755. "must not start with '../'": {
  7756. expectedError: "must not contain '..'",
  7757. spec: core.Pod{
  7758. ObjectMeta: metav1.ObjectMeta{
  7759. Name: "123",
  7760. Namespace: "ns",
  7761. Annotations: map[string]string{
  7762. core.SeccompPodAnnotationKey: "localhost/../foo",
  7763. },
  7764. },
  7765. Spec: validPodSpec(nil),
  7766. },
  7767. },
  7768. "AppArmor profile must apply to a container": {
  7769. expectedError: "metadata.annotations[container.apparmor.security.beta.kubernetes.io/fake-ctr]",
  7770. spec: core.Pod{
  7771. ObjectMeta: metav1.ObjectMeta{
  7772. Name: "123",
  7773. Namespace: "ns",
  7774. Annotations: map[string]string{
  7775. apparmor.ContainerAnnotationKeyPrefix + "ctr": apparmor.ProfileRuntimeDefault,
  7776. apparmor.ContainerAnnotationKeyPrefix + "init-ctr": apparmor.ProfileRuntimeDefault,
  7777. apparmor.ContainerAnnotationKeyPrefix + "fake-ctr": apparmor.ProfileRuntimeDefault,
  7778. },
  7779. },
  7780. Spec: core.PodSpec{
  7781. InitContainers: []core.Container{{Name: "init-ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  7782. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  7783. RestartPolicy: core.RestartPolicyAlways,
  7784. DNSPolicy: core.DNSClusterFirst,
  7785. },
  7786. },
  7787. },
  7788. "AppArmor profile format must be valid": {
  7789. expectedError: "invalid AppArmor profile name",
  7790. spec: core.Pod{
  7791. ObjectMeta: metav1.ObjectMeta{
  7792. Name: "123",
  7793. Namespace: "ns",
  7794. Annotations: map[string]string{
  7795. apparmor.ContainerAnnotationKeyPrefix + "ctr": "bad-name",
  7796. },
  7797. },
  7798. Spec: validPodSpec(nil),
  7799. },
  7800. },
  7801. "only default AppArmor profile may start with runtime/": {
  7802. expectedError: "invalid AppArmor profile name",
  7803. spec: core.Pod{
  7804. ObjectMeta: metav1.ObjectMeta{
  7805. Name: "123",
  7806. Namespace: "ns",
  7807. Annotations: map[string]string{
  7808. apparmor.ContainerAnnotationKeyPrefix + "ctr": "runtime/foo",
  7809. },
  7810. },
  7811. Spec: validPodSpec(nil),
  7812. },
  7813. },
  7814. "invalid extended resource name in container request": {
  7815. expectedError: "must be a standard resource for containers",
  7816. spec: core.Pod{
  7817. ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
  7818. Spec: core.PodSpec{
  7819. Containers: []core.Container{
  7820. {
  7821. Name: "invalid",
  7822. Image: "image",
  7823. ImagePullPolicy: "IfNotPresent",
  7824. Resources: core.ResourceRequirements{
  7825. Requests: core.ResourceList{
  7826. core.ResourceName("invalid-name"): resource.MustParse("2"),
  7827. },
  7828. Limits: core.ResourceList{
  7829. core.ResourceName("invalid-name"): resource.MustParse("2"),
  7830. },
  7831. },
  7832. },
  7833. },
  7834. RestartPolicy: core.RestartPolicyAlways,
  7835. DNSPolicy: core.DNSClusterFirst,
  7836. },
  7837. },
  7838. },
  7839. "invalid extended resource requirement: request must be == limit": {
  7840. expectedError: "must be equal to example.com/a",
  7841. spec: core.Pod{
  7842. ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
  7843. Spec: core.PodSpec{
  7844. Containers: []core.Container{
  7845. {
  7846. Name: "invalid",
  7847. Image: "image",
  7848. ImagePullPolicy: "IfNotPresent",
  7849. Resources: core.ResourceRequirements{
  7850. Requests: core.ResourceList{
  7851. core.ResourceName("example.com/a"): resource.MustParse("2"),
  7852. },
  7853. Limits: core.ResourceList{
  7854. core.ResourceName("example.com/a"): resource.MustParse("1"),
  7855. },
  7856. },
  7857. },
  7858. },
  7859. RestartPolicy: core.RestartPolicyAlways,
  7860. DNSPolicy: core.DNSClusterFirst,
  7861. },
  7862. },
  7863. },
  7864. "invalid extended resource requirement without limit": {
  7865. expectedError: "Limit must be set",
  7866. spec: core.Pod{
  7867. ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
  7868. Spec: core.PodSpec{
  7869. Containers: []core.Container{
  7870. {
  7871. Name: "invalid",
  7872. Image: "image",
  7873. ImagePullPolicy: "IfNotPresent",
  7874. Resources: core.ResourceRequirements{
  7875. Requests: core.ResourceList{
  7876. core.ResourceName("example.com/a"): resource.MustParse("2"),
  7877. },
  7878. },
  7879. },
  7880. },
  7881. RestartPolicy: core.RestartPolicyAlways,
  7882. DNSPolicy: core.DNSClusterFirst,
  7883. },
  7884. },
  7885. },
  7886. "invalid fractional extended resource in container request": {
  7887. expectedError: "must be an integer",
  7888. spec: core.Pod{
  7889. ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
  7890. Spec: core.PodSpec{
  7891. Containers: []core.Container{
  7892. {
  7893. Name: "invalid",
  7894. Image: "image",
  7895. ImagePullPolicy: "IfNotPresent",
  7896. Resources: core.ResourceRequirements{
  7897. Requests: core.ResourceList{
  7898. core.ResourceName("example.com/a"): resource.MustParse("500m"),
  7899. },
  7900. },
  7901. },
  7902. },
  7903. RestartPolicy: core.RestartPolicyAlways,
  7904. DNSPolicy: core.DNSClusterFirst,
  7905. },
  7906. },
  7907. },
  7908. "invalid fractional extended resource in init container request": {
  7909. expectedError: "must be an integer",
  7910. spec: core.Pod{
  7911. ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
  7912. Spec: core.PodSpec{
  7913. InitContainers: []core.Container{
  7914. {
  7915. Name: "invalid",
  7916. Image: "image",
  7917. ImagePullPolicy: "IfNotPresent",
  7918. Resources: core.ResourceRequirements{
  7919. Requests: core.ResourceList{
  7920. core.ResourceName("example.com/a"): resource.MustParse("500m"),
  7921. },
  7922. },
  7923. },
  7924. },
  7925. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  7926. RestartPolicy: core.RestartPolicyAlways,
  7927. DNSPolicy: core.DNSClusterFirst,
  7928. },
  7929. },
  7930. },
  7931. "invalid fractional extended resource in container limit": {
  7932. expectedError: "must be an integer",
  7933. spec: core.Pod{
  7934. ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
  7935. Spec: core.PodSpec{
  7936. Containers: []core.Container{
  7937. {
  7938. Name: "invalid",
  7939. Image: "image",
  7940. ImagePullPolicy: "IfNotPresent",
  7941. Resources: core.ResourceRequirements{
  7942. Requests: core.ResourceList{
  7943. core.ResourceName("example.com/a"): resource.MustParse("5"),
  7944. },
  7945. Limits: core.ResourceList{
  7946. core.ResourceName("example.com/a"): resource.MustParse("2.5"),
  7947. },
  7948. },
  7949. },
  7950. },
  7951. RestartPolicy: core.RestartPolicyAlways,
  7952. DNSPolicy: core.DNSClusterFirst,
  7953. },
  7954. },
  7955. },
  7956. "invalid fractional extended resource in init container limit": {
  7957. expectedError: "must be an integer",
  7958. spec: core.Pod{
  7959. ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
  7960. Spec: core.PodSpec{
  7961. InitContainers: []core.Container{
  7962. {
  7963. Name: "invalid",
  7964. Image: "image",
  7965. ImagePullPolicy: "IfNotPresent",
  7966. Resources: core.ResourceRequirements{
  7967. Requests: core.ResourceList{
  7968. core.ResourceName("example.com/a"): resource.MustParse("2.5"),
  7969. },
  7970. Limits: core.ResourceList{
  7971. core.ResourceName("example.com/a"): resource.MustParse("2.5"),
  7972. },
  7973. },
  7974. },
  7975. },
  7976. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  7977. RestartPolicy: core.RestartPolicyAlways,
  7978. DNSPolicy: core.DNSClusterFirst,
  7979. },
  7980. },
  7981. },
  7982. "mirror-pod present without nodeName": {
  7983. expectedError: "mirror",
  7984. spec: core.Pod{
  7985. ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns", Annotations: map[string]string{core.MirrorPodAnnotationKey: ""}},
  7986. Spec: core.PodSpec{
  7987. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  7988. RestartPolicy: core.RestartPolicyAlways,
  7989. DNSPolicy: core.DNSClusterFirst,
  7990. },
  7991. },
  7992. },
  7993. "mirror-pod populated without nodeName": {
  7994. expectedError: "mirror",
  7995. spec: core.Pod{
  7996. ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns", Annotations: map[string]string{core.MirrorPodAnnotationKey: "foo"}},
  7997. Spec: core.PodSpec{
  7998. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  7999. RestartPolicy: core.RestartPolicyAlways,
  8000. DNSPolicy: core.DNSClusterFirst,
  8001. },
  8002. },
  8003. },
  8004. "serviceaccount token projected volume with no serviceaccount name specified": {
  8005. expectedError: "must not be specified when serviceAccountName is not set",
  8006. spec: core.Pod{
  8007. ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
  8008. Spec: core.PodSpec{
  8009. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  8010. RestartPolicy: core.RestartPolicyAlways,
  8011. DNSPolicy: core.DNSClusterFirst,
  8012. Volumes: []core.Volume{
  8013. {
  8014. Name: "projected-volume",
  8015. VolumeSource: core.VolumeSource{
  8016. Projected: &core.ProjectedVolumeSource{
  8017. Sources: []core.VolumeProjection{
  8018. {
  8019. ServiceAccountToken: &core.ServiceAccountTokenProjection{
  8020. Audience: "foo-audience",
  8021. ExpirationSeconds: 6000,
  8022. Path: "foo-path",
  8023. },
  8024. },
  8025. },
  8026. },
  8027. },
  8028. },
  8029. },
  8030. },
  8031. },
  8032. },
  8033. }
  8034. for k, v := range errorCases {
  8035. if errs := ValidatePod(&v.spec); len(errs) == 0 {
  8036. t.Errorf("expected failure for %q", k)
  8037. } else if v.expectedError == "" {
  8038. t.Errorf("missing expectedError for %q, got %q", k, errs.ToAggregate().Error())
  8039. } else if actualError := errs.ToAggregate().Error(); !strings.Contains(actualError, v.expectedError) {
  8040. t.Errorf("expected error for %q to contain %q, got %q", k, v.expectedError, actualError)
  8041. }
  8042. }
  8043. }
  8044. func TestValidatePodUpdate(t *testing.T) {
  8045. var (
  8046. activeDeadlineSecondsZero = int64(0)
  8047. activeDeadlineSecondsNegative = int64(-30)
  8048. activeDeadlineSecondsPositive = int64(30)
  8049. activeDeadlineSecondsLarger = int64(31)
  8050. now = metav1.Now()
  8051. grace = int64(30)
  8052. grace2 = int64(31)
  8053. )
  8054. tests := []struct {
  8055. new core.Pod
  8056. old core.Pod
  8057. err string
  8058. test string
  8059. }{
  8060. {core.Pod{}, core.Pod{}, "", "nothing"},
  8061. {
  8062. core.Pod{
  8063. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  8064. },
  8065. core.Pod{
  8066. ObjectMeta: metav1.ObjectMeta{Name: "bar"},
  8067. },
  8068. "metadata.name",
  8069. "ids",
  8070. },
  8071. {
  8072. core.Pod{
  8073. ObjectMeta: metav1.ObjectMeta{
  8074. Name: "foo",
  8075. Labels: map[string]string{
  8076. "foo": "bar",
  8077. },
  8078. },
  8079. },
  8080. core.Pod{
  8081. ObjectMeta: metav1.ObjectMeta{
  8082. Name: "foo",
  8083. Labels: map[string]string{
  8084. "bar": "foo",
  8085. },
  8086. },
  8087. },
  8088. "",
  8089. "labels",
  8090. },
  8091. {
  8092. core.Pod{
  8093. ObjectMeta: metav1.ObjectMeta{
  8094. Name: "foo",
  8095. Annotations: map[string]string{
  8096. "foo": "bar",
  8097. },
  8098. },
  8099. },
  8100. core.Pod{
  8101. ObjectMeta: metav1.ObjectMeta{
  8102. Name: "foo",
  8103. Annotations: map[string]string{
  8104. "bar": "foo",
  8105. },
  8106. },
  8107. },
  8108. "",
  8109. "annotations",
  8110. },
  8111. {
  8112. core.Pod{
  8113. ObjectMeta: metav1.ObjectMeta{
  8114. Name: "foo",
  8115. },
  8116. Spec: core.PodSpec{
  8117. Containers: []core.Container{
  8118. {
  8119. Image: "foo:V1",
  8120. },
  8121. },
  8122. },
  8123. },
  8124. core.Pod{
  8125. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  8126. Spec: core.PodSpec{
  8127. Containers: []core.Container{
  8128. {
  8129. Image: "foo:V2",
  8130. },
  8131. {
  8132. Image: "bar:V2",
  8133. },
  8134. },
  8135. },
  8136. },
  8137. "may not add or remove containers",
  8138. "less containers",
  8139. },
  8140. {
  8141. core.Pod{
  8142. ObjectMeta: metav1.ObjectMeta{
  8143. Name: "foo",
  8144. },
  8145. Spec: core.PodSpec{
  8146. Containers: []core.Container{
  8147. {
  8148. Image: "foo:V1",
  8149. },
  8150. {
  8151. Image: "bar:V2",
  8152. },
  8153. },
  8154. },
  8155. },
  8156. core.Pod{
  8157. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  8158. Spec: core.PodSpec{
  8159. Containers: []core.Container{
  8160. {
  8161. Image: "foo:V2",
  8162. },
  8163. },
  8164. },
  8165. },
  8166. "may not add or remove containers",
  8167. "more containers",
  8168. },
  8169. {
  8170. core.Pod{
  8171. ObjectMeta: metav1.ObjectMeta{
  8172. Name: "foo",
  8173. },
  8174. Spec: core.PodSpec{
  8175. InitContainers: []core.Container{
  8176. {
  8177. Image: "foo:V1",
  8178. },
  8179. },
  8180. },
  8181. },
  8182. core.Pod{
  8183. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  8184. Spec: core.PodSpec{
  8185. InitContainers: []core.Container{
  8186. {
  8187. Image: "foo:V2",
  8188. },
  8189. {
  8190. Image: "bar:V2",
  8191. },
  8192. },
  8193. },
  8194. },
  8195. "may not add or remove containers",
  8196. "more init containers",
  8197. },
  8198. {
  8199. core.Pod{
  8200. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  8201. Spec: core.PodSpec{Containers: []core.Container{{Image: "foo:V1"}}},
  8202. },
  8203. core.Pod{
  8204. ObjectMeta: metav1.ObjectMeta{Name: "foo", DeletionTimestamp: &now},
  8205. Spec: core.PodSpec{Containers: []core.Container{{Image: "foo:V1"}}},
  8206. },
  8207. "metadata.deletionTimestamp",
  8208. "deletion timestamp removed",
  8209. },
  8210. {
  8211. core.Pod{
  8212. ObjectMeta: metav1.ObjectMeta{Name: "foo", DeletionTimestamp: &now},
  8213. Spec: core.PodSpec{Containers: []core.Container{{Image: "foo:V1"}}},
  8214. },
  8215. core.Pod{
  8216. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  8217. Spec: core.PodSpec{Containers: []core.Container{{Image: "foo:V1"}}},
  8218. },
  8219. "metadata.deletionTimestamp",
  8220. "deletion timestamp added",
  8221. },
  8222. {
  8223. core.Pod{
  8224. ObjectMeta: metav1.ObjectMeta{Name: "foo", DeletionTimestamp: &now, DeletionGracePeriodSeconds: &grace},
  8225. Spec: core.PodSpec{Containers: []core.Container{{Image: "foo:V1"}}},
  8226. },
  8227. core.Pod{
  8228. ObjectMeta: metav1.ObjectMeta{Name: "foo", DeletionTimestamp: &now, DeletionGracePeriodSeconds: &grace2},
  8229. Spec: core.PodSpec{Containers: []core.Container{{Image: "foo:V1"}}},
  8230. },
  8231. "metadata.deletionGracePeriodSeconds",
  8232. "deletion grace period seconds changed",
  8233. },
  8234. {
  8235. core.Pod{
  8236. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  8237. Spec: core.PodSpec{
  8238. Containers: []core.Container{
  8239. {
  8240. Image: "foo:V1",
  8241. },
  8242. },
  8243. },
  8244. },
  8245. core.Pod{
  8246. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  8247. Spec: core.PodSpec{
  8248. Containers: []core.Container{
  8249. {
  8250. Image: "foo:V2",
  8251. },
  8252. },
  8253. },
  8254. },
  8255. "",
  8256. "image change",
  8257. },
  8258. {
  8259. core.Pod{
  8260. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  8261. Spec: core.PodSpec{
  8262. InitContainers: []core.Container{
  8263. {
  8264. Image: "foo:V1",
  8265. },
  8266. },
  8267. },
  8268. },
  8269. core.Pod{
  8270. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  8271. Spec: core.PodSpec{
  8272. InitContainers: []core.Container{
  8273. {
  8274. Image: "foo:V2",
  8275. },
  8276. },
  8277. },
  8278. },
  8279. "",
  8280. "init container image change",
  8281. },
  8282. {
  8283. core.Pod{
  8284. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  8285. Spec: core.PodSpec{
  8286. Containers: []core.Container{
  8287. {},
  8288. },
  8289. },
  8290. },
  8291. core.Pod{
  8292. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  8293. Spec: core.PodSpec{
  8294. Containers: []core.Container{
  8295. {
  8296. Image: "foo:V2",
  8297. },
  8298. },
  8299. },
  8300. },
  8301. "spec.containers[0].image",
  8302. "image change to empty",
  8303. },
  8304. {
  8305. core.Pod{
  8306. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  8307. Spec: core.PodSpec{
  8308. InitContainers: []core.Container{
  8309. {},
  8310. },
  8311. },
  8312. },
  8313. core.Pod{
  8314. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  8315. Spec: core.PodSpec{
  8316. InitContainers: []core.Container{
  8317. {
  8318. Image: "foo:V2",
  8319. },
  8320. },
  8321. },
  8322. },
  8323. "spec.initContainers[0].image",
  8324. "init container image change to empty",
  8325. },
  8326. {
  8327. core.Pod{
  8328. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  8329. Spec: core.PodSpec{
  8330. EphemeralContainers: []core.EphemeralContainer{
  8331. {
  8332. EphemeralContainerCommon: core.EphemeralContainerCommon{
  8333. Name: "ephemeral",
  8334. Image: "busybox",
  8335. },
  8336. },
  8337. },
  8338. },
  8339. },
  8340. core.Pod{
  8341. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  8342. Spec: core.PodSpec{},
  8343. },
  8344. "Forbidden: pod updates may not change fields other than",
  8345. "ephemeralContainer changes are not allowed via normal pod update",
  8346. },
  8347. {
  8348. core.Pod{
  8349. Spec: core.PodSpec{},
  8350. },
  8351. core.Pod{
  8352. Spec: core.PodSpec{},
  8353. },
  8354. "",
  8355. "activeDeadlineSeconds no change, nil",
  8356. },
  8357. {
  8358. core.Pod{
  8359. Spec: core.PodSpec{
  8360. ActiveDeadlineSeconds: &activeDeadlineSecondsPositive,
  8361. },
  8362. },
  8363. core.Pod{
  8364. Spec: core.PodSpec{
  8365. ActiveDeadlineSeconds: &activeDeadlineSecondsPositive,
  8366. },
  8367. },
  8368. "",
  8369. "activeDeadlineSeconds no change, set",
  8370. },
  8371. {
  8372. core.Pod{
  8373. Spec: core.PodSpec{
  8374. ActiveDeadlineSeconds: &activeDeadlineSecondsPositive,
  8375. },
  8376. },
  8377. core.Pod{},
  8378. "",
  8379. "activeDeadlineSeconds change to positive from nil",
  8380. },
  8381. {
  8382. core.Pod{
  8383. Spec: core.PodSpec{
  8384. ActiveDeadlineSeconds: &activeDeadlineSecondsPositive,
  8385. },
  8386. },
  8387. core.Pod{
  8388. Spec: core.PodSpec{
  8389. ActiveDeadlineSeconds: &activeDeadlineSecondsLarger,
  8390. },
  8391. },
  8392. "",
  8393. "activeDeadlineSeconds change to smaller positive",
  8394. },
  8395. {
  8396. core.Pod{
  8397. Spec: core.PodSpec{
  8398. ActiveDeadlineSeconds: &activeDeadlineSecondsLarger,
  8399. },
  8400. },
  8401. core.Pod{
  8402. Spec: core.PodSpec{
  8403. ActiveDeadlineSeconds: &activeDeadlineSecondsPositive,
  8404. },
  8405. },
  8406. "spec.activeDeadlineSeconds",
  8407. "activeDeadlineSeconds change to larger positive",
  8408. },
  8409. {
  8410. core.Pod{
  8411. Spec: core.PodSpec{
  8412. ActiveDeadlineSeconds: &activeDeadlineSecondsNegative,
  8413. },
  8414. },
  8415. core.Pod{},
  8416. "spec.activeDeadlineSeconds",
  8417. "activeDeadlineSeconds change to negative from nil",
  8418. },
  8419. {
  8420. core.Pod{
  8421. Spec: core.PodSpec{
  8422. ActiveDeadlineSeconds: &activeDeadlineSecondsNegative,
  8423. },
  8424. },
  8425. core.Pod{
  8426. Spec: core.PodSpec{
  8427. ActiveDeadlineSeconds: &activeDeadlineSecondsPositive,
  8428. },
  8429. },
  8430. "spec.activeDeadlineSeconds",
  8431. "activeDeadlineSeconds change to negative from positive",
  8432. },
  8433. {
  8434. core.Pod{
  8435. Spec: core.PodSpec{
  8436. ActiveDeadlineSeconds: &activeDeadlineSecondsZero,
  8437. },
  8438. },
  8439. core.Pod{
  8440. Spec: core.PodSpec{
  8441. ActiveDeadlineSeconds: &activeDeadlineSecondsPositive,
  8442. },
  8443. },
  8444. "",
  8445. "activeDeadlineSeconds change to zero from positive",
  8446. },
  8447. {
  8448. core.Pod{
  8449. Spec: core.PodSpec{
  8450. ActiveDeadlineSeconds: &activeDeadlineSecondsZero,
  8451. },
  8452. },
  8453. core.Pod{},
  8454. "",
  8455. "activeDeadlineSeconds change to zero from nil",
  8456. },
  8457. {
  8458. core.Pod{},
  8459. core.Pod{
  8460. Spec: core.PodSpec{
  8461. ActiveDeadlineSeconds: &activeDeadlineSecondsPositive,
  8462. },
  8463. },
  8464. "spec.activeDeadlineSeconds",
  8465. "activeDeadlineSeconds change to nil from positive",
  8466. },
  8467. {
  8468. core.Pod{
  8469. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  8470. Spec: core.PodSpec{
  8471. Containers: []core.Container{
  8472. {
  8473. Image: "foo:V1",
  8474. Resources: core.ResourceRequirements{
  8475. Limits: getResourceLimits("100m", "0"),
  8476. },
  8477. },
  8478. },
  8479. },
  8480. },
  8481. core.Pod{
  8482. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  8483. Spec: core.PodSpec{
  8484. Containers: []core.Container{
  8485. {
  8486. Image: "foo:V2",
  8487. Resources: core.ResourceRequirements{
  8488. Limits: getResourceLimits("1000m", "0"),
  8489. },
  8490. },
  8491. },
  8492. },
  8493. },
  8494. "spec: Forbidden: pod updates may not change fields",
  8495. "cpu change",
  8496. },
  8497. {
  8498. core.Pod{
  8499. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  8500. Spec: core.PodSpec{
  8501. Containers: []core.Container{
  8502. {
  8503. Image: "foo:V1",
  8504. Ports: []core.ContainerPort{
  8505. {HostPort: 8080, ContainerPort: 80},
  8506. },
  8507. },
  8508. },
  8509. },
  8510. },
  8511. core.Pod{
  8512. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  8513. Spec: core.PodSpec{
  8514. Containers: []core.Container{
  8515. {
  8516. Image: "foo:V2",
  8517. Ports: []core.ContainerPort{
  8518. {HostPort: 8000, ContainerPort: 80},
  8519. },
  8520. },
  8521. },
  8522. },
  8523. },
  8524. "spec: Forbidden: pod updates may not change fields",
  8525. "port change",
  8526. },
  8527. {
  8528. core.Pod{
  8529. ObjectMeta: metav1.ObjectMeta{
  8530. Name: "foo",
  8531. Labels: map[string]string{
  8532. "foo": "bar",
  8533. },
  8534. },
  8535. },
  8536. core.Pod{
  8537. ObjectMeta: metav1.ObjectMeta{
  8538. Name: "foo",
  8539. Labels: map[string]string{
  8540. "Bar": "foo",
  8541. },
  8542. },
  8543. },
  8544. "",
  8545. "bad label change",
  8546. },
  8547. {
  8548. core.Pod{
  8549. ObjectMeta: metav1.ObjectMeta{
  8550. Name: "foo",
  8551. },
  8552. Spec: core.PodSpec{
  8553. NodeName: "node1",
  8554. Tolerations: []core.Toleration{{Key: "key1", Value: "value2"}},
  8555. },
  8556. },
  8557. core.Pod{
  8558. ObjectMeta: metav1.ObjectMeta{
  8559. Name: "foo",
  8560. },
  8561. Spec: core.PodSpec{
  8562. NodeName: "node1",
  8563. Tolerations: []core.Toleration{{Key: "key1", Value: "value1"}},
  8564. },
  8565. },
  8566. "spec.tolerations: Forbidden",
  8567. "existing toleration value modified in pod spec updates",
  8568. },
  8569. {
  8570. core.Pod{
  8571. ObjectMeta: metav1.ObjectMeta{
  8572. Name: "foo",
  8573. },
  8574. Spec: core.PodSpec{
  8575. NodeName: "node1",
  8576. Tolerations: []core.Toleration{{Key: "key1", Value: "value2", Operator: "Equal", Effect: "NoExecute", TolerationSeconds: nil}},
  8577. },
  8578. },
  8579. core.Pod{
  8580. ObjectMeta: metav1.ObjectMeta{
  8581. Name: "foo",
  8582. },
  8583. Spec: core.PodSpec{
  8584. NodeName: "node1",
  8585. Tolerations: []core.Toleration{{Key: "key1", Value: "value1", Operator: "Equal", Effect: "NoExecute", TolerationSeconds: &[]int64{10}[0]}},
  8586. },
  8587. },
  8588. "spec.tolerations: Forbidden",
  8589. "existing toleration value modified in pod spec updates with modified tolerationSeconds",
  8590. },
  8591. {
  8592. core.Pod{
  8593. ObjectMeta: metav1.ObjectMeta{
  8594. Name: "foo",
  8595. },
  8596. Spec: core.PodSpec{
  8597. NodeName: "node1",
  8598. Tolerations: []core.Toleration{{Key: "key1", Value: "value1", Operator: "Equal", Effect: "NoExecute", TolerationSeconds: &[]int64{10}[0]}},
  8599. },
  8600. },
  8601. core.Pod{
  8602. ObjectMeta: metav1.ObjectMeta{
  8603. Name: "foo",
  8604. },
  8605. Spec: core.PodSpec{
  8606. NodeName: "node1",
  8607. Tolerations: []core.Toleration{{Key: "key1", Value: "value1", Operator: "Equal", Effect: "NoExecute", TolerationSeconds: &[]int64{20}[0]}},
  8608. }},
  8609. "",
  8610. "modified tolerationSeconds in existing toleration value in pod spec updates",
  8611. },
  8612. {
  8613. core.Pod{
  8614. ObjectMeta: metav1.ObjectMeta{
  8615. Name: "foo",
  8616. },
  8617. Spec: core.PodSpec{
  8618. Tolerations: []core.Toleration{{Key: "key1", Value: "value2"}},
  8619. },
  8620. },
  8621. core.Pod{
  8622. ObjectMeta: metav1.ObjectMeta{
  8623. Name: "foo",
  8624. },
  8625. Spec: core.PodSpec{
  8626. NodeName: "",
  8627. Tolerations: []core.Toleration{{Key: "key1", Value: "value1"}},
  8628. },
  8629. },
  8630. "spec.tolerations: Forbidden",
  8631. "toleration modified in updates to an unscheduled pod",
  8632. },
  8633. {
  8634. core.Pod{
  8635. ObjectMeta: metav1.ObjectMeta{
  8636. Name: "foo",
  8637. },
  8638. Spec: core.PodSpec{
  8639. NodeName: "node1",
  8640. Tolerations: []core.Toleration{{Key: "key1", Value: "value1"}},
  8641. },
  8642. },
  8643. core.Pod{
  8644. ObjectMeta: metav1.ObjectMeta{
  8645. Name: "foo",
  8646. },
  8647. Spec: core.PodSpec{
  8648. NodeName: "node1",
  8649. Tolerations: []core.Toleration{{Key: "key1", Value: "value1"}},
  8650. },
  8651. },
  8652. "",
  8653. "tolerations unmodified in updates to a scheduled pod",
  8654. },
  8655. {
  8656. core.Pod{
  8657. ObjectMeta: metav1.ObjectMeta{
  8658. Name: "foo",
  8659. },
  8660. Spec: core.PodSpec{
  8661. NodeName: "node1",
  8662. Tolerations: []core.Toleration{
  8663. {Key: "key1", Value: "value1", Operator: "Equal", Effect: "NoExecute", TolerationSeconds: &[]int64{20}[0]},
  8664. {Key: "key2", Value: "value2", Operator: "Equal", Effect: "NoExecute", TolerationSeconds: &[]int64{30}[0]},
  8665. },
  8666. }},
  8667. core.Pod{
  8668. ObjectMeta: metav1.ObjectMeta{
  8669. Name: "foo",
  8670. },
  8671. Spec: core.PodSpec{
  8672. NodeName: "node1",
  8673. Tolerations: []core.Toleration{{Key: "key1", Value: "value1", Operator: "Equal", Effect: "NoExecute", TolerationSeconds: &[]int64{10}[0]}},
  8674. },
  8675. },
  8676. "",
  8677. "added valid new toleration to existing tolerations in pod spec updates",
  8678. },
  8679. {
  8680. core.Pod{
  8681. ObjectMeta: metav1.ObjectMeta{Name: "foo"}, Spec: core.PodSpec{
  8682. NodeName: "node1",
  8683. Tolerations: []core.Toleration{
  8684. {Key: "key1", Value: "value1", Operator: "Equal", Effect: "NoExecute", TolerationSeconds: &[]int64{20}[0]},
  8685. {Key: "key2", Value: "value2", Operator: "Equal", Effect: "NoSchedule", TolerationSeconds: &[]int64{30}[0]},
  8686. },
  8687. }},
  8688. core.Pod{
  8689. ObjectMeta: metav1.ObjectMeta{
  8690. Name: "foo",
  8691. },
  8692. Spec: core.PodSpec{
  8693. NodeName: "node1", Tolerations: []core.Toleration{{Key: "key1", Value: "value1", Operator: "Equal", Effect: "NoExecute", TolerationSeconds: &[]int64{10}[0]}},
  8694. }},
  8695. "spec.tolerations[1].effect",
  8696. "added invalid new toleration to existing tolerations in pod spec updates",
  8697. },
  8698. {
  8699. core.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}, Spec: core.PodSpec{NodeName: "foo"}},
  8700. core.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}},
  8701. "spec: Forbidden: pod updates may not change fields",
  8702. "removed nodeName from pod spec",
  8703. },
  8704. {
  8705. core.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Annotations: map[string]string{core.MirrorPodAnnotationKey: ""}}, Spec: core.PodSpec{NodeName: "foo"}},
  8706. core.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}, Spec: core.PodSpec{NodeName: "foo"}},
  8707. "metadata.annotations[kubernetes.io/config.mirror]",
  8708. "added mirror pod annotation",
  8709. },
  8710. {
  8711. core.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}, Spec: core.PodSpec{NodeName: "foo"}},
  8712. core.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Annotations: map[string]string{core.MirrorPodAnnotationKey: ""}}, Spec: core.PodSpec{NodeName: "foo"}},
  8713. "metadata.annotations[kubernetes.io/config.mirror]",
  8714. "removed mirror pod annotation",
  8715. },
  8716. {
  8717. core.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Annotations: map[string]string{core.MirrorPodAnnotationKey: "foo"}}, Spec: core.PodSpec{NodeName: "foo"}},
  8718. core.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Annotations: map[string]string{core.MirrorPodAnnotationKey: "bar"}}, Spec: core.PodSpec{NodeName: "foo"}},
  8719. "metadata.annotations[kubernetes.io/config.mirror]",
  8720. "changed mirror pod annotation",
  8721. },
  8722. {
  8723. core.Pod{
  8724. ObjectMeta: metav1.ObjectMeta{
  8725. Name: "foo",
  8726. },
  8727. Spec: core.PodSpec{
  8728. NodeName: "node1",
  8729. PriorityClassName: "bar-priority",
  8730. },
  8731. },
  8732. core.Pod{
  8733. ObjectMeta: metav1.ObjectMeta{
  8734. Name: "foo",
  8735. },
  8736. Spec: core.PodSpec{
  8737. NodeName: "node1",
  8738. PriorityClassName: "foo-priority",
  8739. },
  8740. },
  8741. "spec: Forbidden: pod updates",
  8742. "changed priority class name",
  8743. },
  8744. {
  8745. core.Pod{
  8746. ObjectMeta: metav1.ObjectMeta{
  8747. Name: "foo",
  8748. },
  8749. Spec: core.PodSpec{
  8750. NodeName: "node1",
  8751. PriorityClassName: "",
  8752. },
  8753. },
  8754. core.Pod{
  8755. ObjectMeta: metav1.ObjectMeta{
  8756. Name: "foo",
  8757. },
  8758. Spec: core.PodSpec{
  8759. NodeName: "node1",
  8760. PriorityClassName: "foo-priority",
  8761. },
  8762. },
  8763. "spec: Forbidden: pod updates",
  8764. "removed priority class name",
  8765. },
  8766. }
  8767. for _, test := range tests {
  8768. test.new.ObjectMeta.ResourceVersion = "1"
  8769. test.old.ObjectMeta.ResourceVersion = "1"
  8770. errs := ValidatePodUpdate(&test.new, &test.old)
  8771. if test.err == "" {
  8772. if len(errs) != 0 {
  8773. t.Errorf("unexpected invalid: %s (%+v)\nA: %+v\nB: %+v", test.test, errs, test.new, test.old)
  8774. }
  8775. } else {
  8776. if len(errs) == 0 {
  8777. t.Errorf("unexpected valid: %s\nA: %+v\nB: %+v", test.test, test.new, test.old)
  8778. } else if actualErr := errs.ToAggregate().Error(); !strings.Contains(actualErr, test.err) {
  8779. t.Errorf("unexpected error message: %s\nExpected error: %s\nActual error: %s", test.test, test.err, actualErr)
  8780. }
  8781. }
  8782. }
  8783. }
  8784. func TestValidatePodStatusUpdate(t *testing.T) {
  8785. tests := []struct {
  8786. new core.Pod
  8787. old core.Pod
  8788. err string
  8789. test string
  8790. }{
  8791. {
  8792. core.Pod{
  8793. ObjectMeta: metav1.ObjectMeta{
  8794. Name: "foo",
  8795. },
  8796. Spec: core.PodSpec{
  8797. NodeName: "node1",
  8798. },
  8799. Status: core.PodStatus{
  8800. NominatedNodeName: "node1",
  8801. },
  8802. },
  8803. core.Pod{
  8804. ObjectMeta: metav1.ObjectMeta{
  8805. Name: "foo",
  8806. },
  8807. Spec: core.PodSpec{
  8808. NodeName: "node1",
  8809. },
  8810. Status: core.PodStatus{},
  8811. },
  8812. "",
  8813. "removed nominatedNodeName",
  8814. },
  8815. {
  8816. core.Pod{
  8817. ObjectMeta: metav1.ObjectMeta{
  8818. Name: "foo",
  8819. },
  8820. Spec: core.PodSpec{
  8821. NodeName: "node1",
  8822. },
  8823. },
  8824. core.Pod{
  8825. ObjectMeta: metav1.ObjectMeta{
  8826. Name: "foo",
  8827. },
  8828. Spec: core.PodSpec{
  8829. NodeName: "node1",
  8830. },
  8831. Status: core.PodStatus{
  8832. NominatedNodeName: "node1",
  8833. },
  8834. },
  8835. "",
  8836. "add valid nominatedNodeName",
  8837. },
  8838. {
  8839. core.Pod{
  8840. ObjectMeta: metav1.ObjectMeta{
  8841. Name: "foo",
  8842. },
  8843. Spec: core.PodSpec{
  8844. NodeName: "node1",
  8845. },
  8846. Status: core.PodStatus{
  8847. NominatedNodeName: "Node1",
  8848. },
  8849. },
  8850. core.Pod{
  8851. ObjectMeta: metav1.ObjectMeta{
  8852. Name: "foo",
  8853. },
  8854. Spec: core.PodSpec{
  8855. NodeName: "node1",
  8856. },
  8857. },
  8858. "nominatedNodeName",
  8859. "Add invalid nominatedNodeName",
  8860. },
  8861. {
  8862. core.Pod{
  8863. ObjectMeta: metav1.ObjectMeta{
  8864. Name: "foo",
  8865. },
  8866. Spec: core.PodSpec{
  8867. NodeName: "node1",
  8868. },
  8869. Status: core.PodStatus{
  8870. NominatedNodeName: "node1",
  8871. },
  8872. },
  8873. core.Pod{
  8874. ObjectMeta: metav1.ObjectMeta{
  8875. Name: "foo",
  8876. },
  8877. Spec: core.PodSpec{
  8878. NodeName: "node1",
  8879. },
  8880. Status: core.PodStatus{
  8881. NominatedNodeName: "node2",
  8882. },
  8883. },
  8884. "",
  8885. "Update nominatedNodeName",
  8886. },
  8887. }
  8888. for _, test := range tests {
  8889. test.new.ObjectMeta.ResourceVersion = "1"
  8890. test.old.ObjectMeta.ResourceVersion = "1"
  8891. errs := ValidatePodStatusUpdate(&test.new, &test.old)
  8892. if test.err == "" {
  8893. if len(errs) != 0 {
  8894. t.Errorf("unexpected invalid: %s (%+v)\nA: %+v\nB: %+v", test.test, errs, test.new, test.old)
  8895. }
  8896. } else {
  8897. if len(errs) == 0 {
  8898. t.Errorf("unexpected valid: %s\nA: %+v\nB: %+v", test.test, test.new, test.old)
  8899. } else if actualErr := errs.ToAggregate().Error(); !strings.Contains(actualErr, test.err) {
  8900. t.Errorf("unexpected error message: %s\nExpected error: %s\nActual error: %s", test.test, test.err, actualErr)
  8901. }
  8902. }
  8903. }
  8904. }
  8905. func makeValidService() core.Service {
  8906. serviceIPFamily := core.IPv4Protocol
  8907. return core.Service{
  8908. ObjectMeta: metav1.ObjectMeta{
  8909. Name: "valid",
  8910. Namespace: "valid",
  8911. Labels: map[string]string{},
  8912. Annotations: map[string]string{},
  8913. ResourceVersion: "1",
  8914. },
  8915. Spec: core.ServiceSpec{
  8916. Selector: map[string]string{"key": "val"},
  8917. SessionAffinity: "None",
  8918. Type: core.ServiceTypeClusterIP,
  8919. Ports: []core.ServicePort{{Name: "p", Protocol: "TCP", Port: 8675, TargetPort: intstr.FromInt(8675)}},
  8920. IPFamily: &serviceIPFamily,
  8921. },
  8922. }
  8923. }
  8924. func TestValidatePodEphemeralContainersUpdate(t *testing.T) {
  8925. tests := []struct {
  8926. new []core.EphemeralContainer
  8927. old []core.EphemeralContainer
  8928. err string
  8929. test string
  8930. }{
  8931. {[]core.EphemeralContainer{}, []core.EphemeralContainer{}, "", "nothing"},
  8932. {
  8933. []core.EphemeralContainer{{
  8934. EphemeralContainerCommon: core.EphemeralContainerCommon{
  8935. Name: "debugger",
  8936. Image: "busybox",
  8937. ImagePullPolicy: "IfNotPresent",
  8938. TerminationMessagePolicy: "File",
  8939. },
  8940. }, {
  8941. EphemeralContainerCommon: core.EphemeralContainerCommon{
  8942. Name: "debugger2",
  8943. Image: "busybox",
  8944. ImagePullPolicy: "IfNotPresent",
  8945. TerminationMessagePolicy: "File",
  8946. },
  8947. }},
  8948. []core.EphemeralContainer{{
  8949. EphemeralContainerCommon: core.EphemeralContainerCommon{
  8950. Name: "debugger",
  8951. Image: "busybox",
  8952. ImagePullPolicy: "IfNotPresent",
  8953. TerminationMessagePolicy: "File",
  8954. },
  8955. }, {
  8956. EphemeralContainerCommon: core.EphemeralContainerCommon{
  8957. Name: "debugger2",
  8958. Image: "busybox",
  8959. ImagePullPolicy: "IfNotPresent",
  8960. TerminationMessagePolicy: "File",
  8961. },
  8962. }},
  8963. "",
  8964. "No change in Ephemeral Containers",
  8965. },
  8966. {
  8967. []core.EphemeralContainer{{
  8968. EphemeralContainerCommon: core.EphemeralContainerCommon{
  8969. Name: "debugger",
  8970. Image: "busybox",
  8971. ImagePullPolicy: "IfNotPresent",
  8972. TerminationMessagePolicy: "File",
  8973. },
  8974. }, {
  8975. EphemeralContainerCommon: core.EphemeralContainerCommon{
  8976. Name: "debugger2",
  8977. Image: "busybox",
  8978. ImagePullPolicy: "IfNotPresent",
  8979. TerminationMessagePolicy: "File",
  8980. },
  8981. }},
  8982. []core.EphemeralContainer{{
  8983. EphemeralContainerCommon: core.EphemeralContainerCommon{
  8984. Name: "debugger2",
  8985. Image: "busybox",
  8986. ImagePullPolicy: "IfNotPresent",
  8987. TerminationMessagePolicy: "File",
  8988. },
  8989. }, {
  8990. EphemeralContainerCommon: core.EphemeralContainerCommon{
  8991. Name: "debugger",
  8992. Image: "busybox",
  8993. ImagePullPolicy: "IfNotPresent",
  8994. TerminationMessagePolicy: "File",
  8995. },
  8996. }},
  8997. "",
  8998. "Ephemeral Container list order changes",
  8999. },
  9000. {
  9001. []core.EphemeralContainer{{
  9002. EphemeralContainerCommon: core.EphemeralContainerCommon{
  9003. Name: "debugger",
  9004. Image: "busybox",
  9005. ImagePullPolicy: "IfNotPresent",
  9006. TerminationMessagePolicy: "File",
  9007. },
  9008. }},
  9009. []core.EphemeralContainer{},
  9010. "",
  9011. "Add an Ephemeral Container",
  9012. },
  9013. {
  9014. []core.EphemeralContainer{{
  9015. EphemeralContainerCommon: core.EphemeralContainerCommon{
  9016. Name: "debugger1",
  9017. Image: "busybox",
  9018. ImagePullPolicy: "IfNotPresent",
  9019. TerminationMessagePolicy: "File",
  9020. },
  9021. }, {
  9022. EphemeralContainerCommon: core.EphemeralContainerCommon{
  9023. Name: "debugger2",
  9024. Image: "busybox",
  9025. ImagePullPolicy: "IfNotPresent",
  9026. TerminationMessagePolicy: "File",
  9027. },
  9028. }},
  9029. []core.EphemeralContainer{},
  9030. "",
  9031. "Add two Ephemeral Containers",
  9032. },
  9033. {
  9034. []core.EphemeralContainer{{
  9035. EphemeralContainerCommon: core.EphemeralContainerCommon{
  9036. Name: "debugger",
  9037. Image: "busybox",
  9038. ImagePullPolicy: "IfNotPresent",
  9039. TerminationMessagePolicy: "File",
  9040. },
  9041. }, {
  9042. EphemeralContainerCommon: core.EphemeralContainerCommon{
  9043. Name: "debugger2",
  9044. Image: "busybox",
  9045. ImagePullPolicy: "IfNotPresent",
  9046. TerminationMessagePolicy: "File",
  9047. },
  9048. }},
  9049. []core.EphemeralContainer{{
  9050. EphemeralContainerCommon: core.EphemeralContainerCommon{
  9051. Name: "debugger",
  9052. Image: "busybox",
  9053. ImagePullPolicy: "IfNotPresent",
  9054. TerminationMessagePolicy: "File",
  9055. },
  9056. }},
  9057. "",
  9058. "Add to an existing Ephemeral Containers",
  9059. },
  9060. {
  9061. []core.EphemeralContainer{{
  9062. EphemeralContainerCommon: core.EphemeralContainerCommon{
  9063. Name: "debugger3",
  9064. Image: "busybox",
  9065. ImagePullPolicy: "IfNotPresent",
  9066. TerminationMessagePolicy: "File",
  9067. },
  9068. }, {
  9069. EphemeralContainerCommon: core.EphemeralContainerCommon{
  9070. Name: "debugger2",
  9071. Image: "busybox",
  9072. ImagePullPolicy: "IfNotPresent",
  9073. TerminationMessagePolicy: "File",
  9074. },
  9075. }, {
  9076. EphemeralContainerCommon: core.EphemeralContainerCommon{
  9077. Name: "debugger",
  9078. Image: "busybox",
  9079. ImagePullPolicy: "IfNotPresent",
  9080. TerminationMessagePolicy: "File",
  9081. },
  9082. }},
  9083. []core.EphemeralContainer{{
  9084. EphemeralContainerCommon: core.EphemeralContainerCommon{
  9085. Name: "debugger",
  9086. Image: "busybox",
  9087. ImagePullPolicy: "IfNotPresent",
  9088. TerminationMessagePolicy: "File",
  9089. },
  9090. }, {
  9091. EphemeralContainerCommon: core.EphemeralContainerCommon{
  9092. Name: "debugger2",
  9093. Image: "busybox",
  9094. ImagePullPolicy: "IfNotPresent",
  9095. TerminationMessagePolicy: "File",
  9096. },
  9097. }},
  9098. "",
  9099. "Add to an existing Ephemeral Containers, list order changes",
  9100. },
  9101. {
  9102. []core.EphemeralContainer{},
  9103. []core.EphemeralContainer{{
  9104. EphemeralContainerCommon: core.EphemeralContainerCommon{
  9105. Name: "debugger",
  9106. Image: "busybox",
  9107. ImagePullPolicy: "IfNotPresent",
  9108. TerminationMessagePolicy: "File",
  9109. },
  9110. }},
  9111. "may not be removed",
  9112. "Remove an Ephemeral Container",
  9113. },
  9114. {
  9115. []core.EphemeralContainer{{
  9116. EphemeralContainerCommon: core.EphemeralContainerCommon{
  9117. Name: "firstone",
  9118. Image: "busybox",
  9119. ImagePullPolicy: "IfNotPresent",
  9120. TerminationMessagePolicy: "File",
  9121. },
  9122. }},
  9123. []core.EphemeralContainer{{
  9124. EphemeralContainerCommon: core.EphemeralContainerCommon{
  9125. Name: "thentheother",
  9126. Image: "busybox",
  9127. ImagePullPolicy: "IfNotPresent",
  9128. TerminationMessagePolicy: "File",
  9129. },
  9130. }},
  9131. "may not be removed",
  9132. "Replace an Ephemeral Container",
  9133. },
  9134. {
  9135. []core.EphemeralContainer{{
  9136. EphemeralContainerCommon: core.EphemeralContainerCommon{
  9137. Name: "debugger1",
  9138. Image: "busybox",
  9139. ImagePullPolicy: "IfNotPresent",
  9140. TerminationMessagePolicy: "File",
  9141. },
  9142. }, {
  9143. EphemeralContainerCommon: core.EphemeralContainerCommon{
  9144. Name: "debugger2",
  9145. Image: "busybox",
  9146. ImagePullPolicy: "IfNotPresent",
  9147. TerminationMessagePolicy: "File",
  9148. },
  9149. }},
  9150. []core.EphemeralContainer{{
  9151. EphemeralContainerCommon: core.EphemeralContainerCommon{
  9152. Name: "debugger1",
  9153. Image: "debian",
  9154. ImagePullPolicy: "IfNotPresent",
  9155. TerminationMessagePolicy: "File",
  9156. },
  9157. }, {
  9158. EphemeralContainerCommon: core.EphemeralContainerCommon{
  9159. Name: "debugger2",
  9160. Image: "busybox",
  9161. ImagePullPolicy: "IfNotPresent",
  9162. TerminationMessagePolicy: "File",
  9163. },
  9164. }},
  9165. "may not be changed",
  9166. "Change an Ephemeral Containers",
  9167. },
  9168. }
  9169. for _, test := range tests {
  9170. new := core.Pod{Spec: core.PodSpec{EphemeralContainers: test.new}}
  9171. old := core.Pod{Spec: core.PodSpec{EphemeralContainers: test.old}}
  9172. errs := ValidatePodEphemeralContainersUpdate(&new, &old)
  9173. if test.err == "" {
  9174. if len(errs) != 0 {
  9175. t.Errorf("unexpected invalid: %s (%+v)\nA: %+v\nB: %+v", test.test, errs, test.new, test.old)
  9176. }
  9177. } else {
  9178. if len(errs) == 0 {
  9179. t.Errorf("unexpected valid: %s\nA: %+v\nB: %+v", test.test, test.new, test.old)
  9180. } else if actualErr := errs.ToAggregate().Error(); !strings.Contains(actualErr, test.err) {
  9181. t.Errorf("unexpected error message: %s\nExpected error: %s\nActual error: %s", test.test, test.err, actualErr)
  9182. }
  9183. }
  9184. }
  9185. }
  9186. func TestValidateService(t *testing.T) {
  9187. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SCTPSupport, true)()
  9188. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceTopology, true)()
  9189. testCases := []struct {
  9190. name string
  9191. tweakSvc func(svc *core.Service) // given a basic valid service, each test case can customize it
  9192. numErrs int
  9193. }{
  9194. {
  9195. name: "missing namespace",
  9196. tweakSvc: func(s *core.Service) {
  9197. s.Namespace = ""
  9198. },
  9199. numErrs: 1,
  9200. },
  9201. {
  9202. name: "invalid namespace",
  9203. tweakSvc: func(s *core.Service) {
  9204. s.Namespace = "-123"
  9205. },
  9206. numErrs: 1,
  9207. },
  9208. {
  9209. name: "missing name",
  9210. tweakSvc: func(s *core.Service) {
  9211. s.Name = ""
  9212. },
  9213. numErrs: 1,
  9214. },
  9215. {
  9216. name: "invalid name",
  9217. tweakSvc: func(s *core.Service) {
  9218. s.Name = "-123"
  9219. },
  9220. numErrs: 1,
  9221. },
  9222. {
  9223. name: "too long name",
  9224. tweakSvc: func(s *core.Service) {
  9225. s.Name = strings.Repeat("a", 64)
  9226. },
  9227. numErrs: 1,
  9228. },
  9229. {
  9230. name: "invalid generateName",
  9231. tweakSvc: func(s *core.Service) {
  9232. s.GenerateName = "-123"
  9233. },
  9234. numErrs: 1,
  9235. },
  9236. {
  9237. name: "too long generateName",
  9238. tweakSvc: func(s *core.Service) {
  9239. s.GenerateName = strings.Repeat("a", 64)
  9240. },
  9241. numErrs: 1,
  9242. },
  9243. {
  9244. name: "invalid label",
  9245. tweakSvc: func(s *core.Service) {
  9246. s.Labels["NoUppercaseOrSpecialCharsLike=Equals"] = "bar"
  9247. },
  9248. numErrs: 1,
  9249. },
  9250. {
  9251. name: "invalid annotation",
  9252. tweakSvc: func(s *core.Service) {
  9253. s.Annotations["NoSpecialCharsLike=Equals"] = "bar"
  9254. },
  9255. numErrs: 1,
  9256. },
  9257. {
  9258. name: "nil selector",
  9259. tweakSvc: func(s *core.Service) {
  9260. s.Spec.Selector = nil
  9261. },
  9262. numErrs: 0,
  9263. },
  9264. {
  9265. name: "invalid selector",
  9266. tweakSvc: func(s *core.Service) {
  9267. s.Spec.Selector["NoSpecialCharsLike=Equals"] = "bar"
  9268. },
  9269. numErrs: 1,
  9270. },
  9271. {
  9272. name: "missing session affinity",
  9273. tweakSvc: func(s *core.Service) {
  9274. s.Spec.SessionAffinity = ""
  9275. },
  9276. numErrs: 1,
  9277. },
  9278. {
  9279. name: "missing type",
  9280. tweakSvc: func(s *core.Service) {
  9281. s.Spec.Type = ""
  9282. },
  9283. numErrs: 1,
  9284. },
  9285. {
  9286. name: "missing ports",
  9287. tweakSvc: func(s *core.Service) {
  9288. s.Spec.Ports = nil
  9289. },
  9290. numErrs: 1,
  9291. },
  9292. {
  9293. name: "missing ports but headless",
  9294. tweakSvc: func(s *core.Service) {
  9295. s.Spec.Ports = nil
  9296. s.Spec.ClusterIP = core.ClusterIPNone
  9297. },
  9298. numErrs: 0,
  9299. },
  9300. {
  9301. name: "empty port[0] name",
  9302. tweakSvc: func(s *core.Service) {
  9303. s.Spec.Ports[0].Name = ""
  9304. },
  9305. numErrs: 0,
  9306. },
  9307. {
  9308. name: "empty port[1] name",
  9309. tweakSvc: func(s *core.Service) {
  9310. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "", Protocol: "TCP", Port: 12345, TargetPort: intstr.FromInt(12345)})
  9311. },
  9312. numErrs: 1,
  9313. },
  9314. {
  9315. name: "empty multi-port port[0] name",
  9316. tweakSvc: func(s *core.Service) {
  9317. s.Spec.Ports[0].Name = ""
  9318. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "p", Protocol: "TCP", Port: 12345, TargetPort: intstr.FromInt(12345)})
  9319. },
  9320. numErrs: 1,
  9321. },
  9322. {
  9323. name: "invalid port name",
  9324. tweakSvc: func(s *core.Service) {
  9325. s.Spec.Ports[0].Name = "INVALID"
  9326. },
  9327. numErrs: 1,
  9328. },
  9329. {
  9330. name: "missing protocol",
  9331. tweakSvc: func(s *core.Service) {
  9332. s.Spec.Ports[0].Protocol = ""
  9333. },
  9334. numErrs: 1,
  9335. },
  9336. {
  9337. name: "invalid protocol",
  9338. tweakSvc: func(s *core.Service) {
  9339. s.Spec.Ports[0].Protocol = "INVALID"
  9340. },
  9341. numErrs: 1,
  9342. },
  9343. {
  9344. name: "invalid cluster ip",
  9345. tweakSvc: func(s *core.Service) {
  9346. s.Spec.ClusterIP = "invalid"
  9347. },
  9348. numErrs: 1,
  9349. },
  9350. {
  9351. name: "missing port",
  9352. tweakSvc: func(s *core.Service) {
  9353. s.Spec.Ports[0].Port = 0
  9354. },
  9355. numErrs: 1,
  9356. },
  9357. {
  9358. name: "invalid port",
  9359. tweakSvc: func(s *core.Service) {
  9360. s.Spec.Ports[0].Port = 65536
  9361. },
  9362. numErrs: 1,
  9363. },
  9364. {
  9365. name: "invalid TargetPort int",
  9366. tweakSvc: func(s *core.Service) {
  9367. s.Spec.Ports[0].TargetPort = intstr.FromInt(65536)
  9368. },
  9369. numErrs: 1,
  9370. },
  9371. {
  9372. name: "valid port headless",
  9373. tweakSvc: func(s *core.Service) {
  9374. s.Spec.Ports[0].Port = 11722
  9375. s.Spec.Ports[0].TargetPort = intstr.FromInt(11722)
  9376. s.Spec.ClusterIP = core.ClusterIPNone
  9377. },
  9378. numErrs: 0,
  9379. },
  9380. {
  9381. name: "invalid port headless 1",
  9382. tweakSvc: func(s *core.Service) {
  9383. s.Spec.Ports[0].Port = 11722
  9384. s.Spec.Ports[0].TargetPort = intstr.FromInt(11721)
  9385. s.Spec.ClusterIP = core.ClusterIPNone
  9386. },
  9387. // in the v1 API, targetPorts on headless services were tolerated.
  9388. // once we have version-specific validation, we can reject this on newer API versions, but until then, we have to tolerate it for compatibility.
  9389. // numErrs: 1,
  9390. numErrs: 0,
  9391. },
  9392. {
  9393. name: "invalid port headless 2",
  9394. tweakSvc: func(s *core.Service) {
  9395. s.Spec.Ports[0].Port = 11722
  9396. s.Spec.Ports[0].TargetPort = intstr.FromString("target")
  9397. s.Spec.ClusterIP = core.ClusterIPNone
  9398. },
  9399. // in the v1 API, targetPorts on headless services were tolerated.
  9400. // once we have version-specific validation, we can reject this on newer API versions, but until then, we have to tolerate it for compatibility.
  9401. // numErrs: 1,
  9402. numErrs: 0,
  9403. },
  9404. {
  9405. name: "invalid publicIPs localhost",
  9406. tweakSvc: func(s *core.Service) {
  9407. s.Spec.ExternalIPs = []string{"127.0.0.1"}
  9408. },
  9409. numErrs: 1,
  9410. },
  9411. {
  9412. name: "invalid publicIPs unspecified",
  9413. tweakSvc: func(s *core.Service) {
  9414. s.Spec.ExternalIPs = []string{"0.0.0.0"}
  9415. },
  9416. numErrs: 1,
  9417. },
  9418. {
  9419. name: "invalid publicIPs loopback",
  9420. tweakSvc: func(s *core.Service) {
  9421. s.Spec.ExternalIPs = []string{"127.0.0.1"}
  9422. },
  9423. numErrs: 1,
  9424. },
  9425. {
  9426. name: "invalid publicIPs host",
  9427. tweakSvc: func(s *core.Service) {
  9428. s.Spec.ExternalIPs = []string{"myhost.mydomain"}
  9429. },
  9430. numErrs: 1,
  9431. },
  9432. {
  9433. name: "dup port name",
  9434. tweakSvc: func(s *core.Service) {
  9435. s.Spec.Ports[0].Name = "p"
  9436. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "p", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt(12345)})
  9437. },
  9438. numErrs: 1,
  9439. },
  9440. {
  9441. name: "valid load balancer protocol UDP 1",
  9442. tweakSvc: func(s *core.Service) {
  9443. s.Spec.Type = core.ServiceTypeLoadBalancer
  9444. s.Spec.Ports[0].Protocol = "UDP"
  9445. },
  9446. numErrs: 0,
  9447. },
  9448. {
  9449. name: "valid load balancer protocol UDP 2",
  9450. tweakSvc: func(s *core.Service) {
  9451. s.Spec.Type = core.ServiceTypeLoadBalancer
  9452. s.Spec.Ports[0] = core.ServicePort{Name: "q", Port: 12345, Protocol: "UDP", TargetPort: intstr.FromInt(12345)}
  9453. },
  9454. numErrs: 0,
  9455. },
  9456. {
  9457. name: "invalid load balancer with mix protocol",
  9458. tweakSvc: func(s *core.Service) {
  9459. s.Spec.Type = core.ServiceTypeLoadBalancer
  9460. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "UDP", TargetPort: intstr.FromInt(12345)})
  9461. },
  9462. numErrs: 1,
  9463. },
  9464. {
  9465. name: "valid 1",
  9466. tweakSvc: func(s *core.Service) {
  9467. // do nothing
  9468. },
  9469. numErrs: 0,
  9470. },
  9471. {
  9472. name: "valid 2",
  9473. tweakSvc: func(s *core.Service) {
  9474. s.Spec.Ports[0].Protocol = "UDP"
  9475. s.Spec.Ports[0].TargetPort = intstr.FromInt(12345)
  9476. },
  9477. numErrs: 0,
  9478. },
  9479. {
  9480. name: "valid 3",
  9481. tweakSvc: func(s *core.Service) {
  9482. s.Spec.Ports[0].TargetPort = intstr.FromString("http")
  9483. },
  9484. numErrs: 0,
  9485. },
  9486. {
  9487. name: "valid cluster ip - none ",
  9488. tweakSvc: func(s *core.Service) {
  9489. s.Spec.ClusterIP = "None"
  9490. },
  9491. numErrs: 0,
  9492. },
  9493. {
  9494. name: "valid cluster ip - empty",
  9495. tweakSvc: func(s *core.Service) {
  9496. s.Spec.ClusterIP = ""
  9497. s.Spec.Ports[0].TargetPort = intstr.FromString("http")
  9498. },
  9499. numErrs: 0,
  9500. },
  9501. {
  9502. name: "valid type - cluster",
  9503. tweakSvc: func(s *core.Service) {
  9504. s.Spec.Type = core.ServiceTypeClusterIP
  9505. },
  9506. numErrs: 0,
  9507. },
  9508. {
  9509. name: "valid type - loadbalancer",
  9510. tweakSvc: func(s *core.Service) {
  9511. s.Spec.Type = core.ServiceTypeLoadBalancer
  9512. },
  9513. numErrs: 0,
  9514. },
  9515. {
  9516. name: "valid type loadbalancer 2 ports",
  9517. tweakSvc: func(s *core.Service) {
  9518. s.Spec.Type = core.ServiceTypeLoadBalancer
  9519. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt(12345)})
  9520. },
  9521. numErrs: 0,
  9522. },
  9523. {
  9524. name: "valid external load balancer 2 ports",
  9525. tweakSvc: func(s *core.Service) {
  9526. s.Spec.Type = core.ServiceTypeLoadBalancer
  9527. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt(12345)})
  9528. },
  9529. numErrs: 0,
  9530. },
  9531. {
  9532. name: "duplicate nodeports",
  9533. tweakSvc: func(s *core.Service) {
  9534. s.Spec.Type = core.ServiceTypeNodePort
  9535. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 1, Protocol: "TCP", NodePort: 1, TargetPort: intstr.FromInt(1)})
  9536. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "r", Port: 2, Protocol: "TCP", NodePort: 1, TargetPort: intstr.FromInt(2)})
  9537. },
  9538. numErrs: 1,
  9539. },
  9540. {
  9541. name: "duplicate nodeports (different protocols)",
  9542. tweakSvc: func(s *core.Service) {
  9543. s.Spec.Type = core.ServiceTypeNodePort
  9544. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 1, Protocol: "TCP", NodePort: 1, TargetPort: intstr.FromInt(1)})
  9545. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "r", Port: 2, Protocol: "UDP", NodePort: 1, TargetPort: intstr.FromInt(2)})
  9546. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "s", Port: 3, Protocol: "SCTP", NodePort: 1, TargetPort: intstr.FromInt(3)})
  9547. },
  9548. numErrs: 0,
  9549. },
  9550. {
  9551. name: "invalid duplicate ports (with same protocol)",
  9552. tweakSvc: func(s *core.Service) {
  9553. s.Spec.Type = core.ServiceTypeClusterIP
  9554. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt(8080)})
  9555. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "r", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt(80)})
  9556. },
  9557. numErrs: 1,
  9558. },
  9559. {
  9560. name: "valid duplicate ports (with different protocols)",
  9561. tweakSvc: func(s *core.Service) {
  9562. s.Spec.Type = core.ServiceTypeClusterIP
  9563. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt(8080)})
  9564. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "r", Port: 12345, Protocol: "UDP", TargetPort: intstr.FromInt(80)})
  9565. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "s", Port: 12345, Protocol: "SCTP", TargetPort: intstr.FromInt(8088)})
  9566. },
  9567. numErrs: 0,
  9568. },
  9569. {
  9570. name: "valid type - cluster",
  9571. tweakSvc: func(s *core.Service) {
  9572. s.Spec.Type = core.ServiceTypeClusterIP
  9573. },
  9574. numErrs: 0,
  9575. },
  9576. {
  9577. name: "valid type - nodeport",
  9578. tweakSvc: func(s *core.Service) {
  9579. s.Spec.Type = core.ServiceTypeNodePort
  9580. },
  9581. numErrs: 0,
  9582. },
  9583. {
  9584. name: "valid type - loadbalancer",
  9585. tweakSvc: func(s *core.Service) {
  9586. s.Spec.Type = core.ServiceTypeLoadBalancer
  9587. },
  9588. numErrs: 0,
  9589. },
  9590. {
  9591. name: "valid type loadbalancer 2 ports",
  9592. tweakSvc: func(s *core.Service) {
  9593. s.Spec.Type = core.ServiceTypeLoadBalancer
  9594. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt(12345)})
  9595. },
  9596. numErrs: 0,
  9597. },
  9598. {
  9599. name: "valid type loadbalancer with NodePort",
  9600. tweakSvc: func(s *core.Service) {
  9601. s.Spec.Type = core.ServiceTypeLoadBalancer
  9602. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", NodePort: 12345, TargetPort: intstr.FromInt(12345)})
  9603. },
  9604. numErrs: 0,
  9605. },
  9606. {
  9607. name: "valid type=NodePort service with NodePort",
  9608. tweakSvc: func(s *core.Service) {
  9609. s.Spec.Type = core.ServiceTypeNodePort
  9610. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", NodePort: 12345, TargetPort: intstr.FromInt(12345)})
  9611. },
  9612. numErrs: 0,
  9613. },
  9614. {
  9615. name: "valid type=NodePort service without NodePort",
  9616. tweakSvc: func(s *core.Service) {
  9617. s.Spec.Type = core.ServiceTypeNodePort
  9618. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt(12345)})
  9619. },
  9620. numErrs: 0,
  9621. },
  9622. {
  9623. name: "valid cluster service without NodePort",
  9624. tweakSvc: func(s *core.Service) {
  9625. s.Spec.Type = core.ServiceTypeClusterIP
  9626. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt(12345)})
  9627. },
  9628. numErrs: 0,
  9629. },
  9630. {
  9631. name: "invalid cluster service with NodePort",
  9632. tweakSvc: func(s *core.Service) {
  9633. s.Spec.Type = core.ServiceTypeClusterIP
  9634. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", NodePort: 12345, TargetPort: intstr.FromInt(12345)})
  9635. },
  9636. numErrs: 1,
  9637. },
  9638. {
  9639. name: "invalid public service with duplicate NodePort",
  9640. tweakSvc: func(s *core.Service) {
  9641. s.Spec.Type = core.ServiceTypeNodePort
  9642. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "p1", Port: 1, Protocol: "TCP", NodePort: 1, TargetPort: intstr.FromInt(1)})
  9643. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "p2", Port: 2, Protocol: "TCP", NodePort: 1, TargetPort: intstr.FromInt(2)})
  9644. },
  9645. numErrs: 1,
  9646. },
  9647. {
  9648. name: "valid type=LoadBalancer",
  9649. tweakSvc: func(s *core.Service) {
  9650. s.Spec.Type = core.ServiceTypeLoadBalancer
  9651. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt(12345)})
  9652. },
  9653. numErrs: 0,
  9654. },
  9655. {
  9656. // For now we open firewalls, and its insecure if we open 10250, remove this
  9657. // when we have better protections in place.
  9658. name: "invalid port type=LoadBalancer",
  9659. tweakSvc: func(s *core.Service) {
  9660. s.Spec.Type = core.ServiceTypeLoadBalancer
  9661. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "kubelet", Port: 10250, Protocol: "TCP", TargetPort: intstr.FromInt(12345)})
  9662. },
  9663. numErrs: 1,
  9664. },
  9665. {
  9666. name: "valid LoadBalancer source range annotation",
  9667. tweakSvc: func(s *core.Service) {
  9668. s.Spec.Type = core.ServiceTypeLoadBalancer
  9669. s.Annotations[core.AnnotationLoadBalancerSourceRangesKey] = "1.2.3.4/8, 5.6.7.8/16"
  9670. },
  9671. numErrs: 0,
  9672. },
  9673. {
  9674. name: "empty LoadBalancer source range annotation",
  9675. tweakSvc: func(s *core.Service) {
  9676. s.Spec.Type = core.ServiceTypeLoadBalancer
  9677. s.Annotations[core.AnnotationLoadBalancerSourceRangesKey] = ""
  9678. },
  9679. numErrs: 0,
  9680. },
  9681. {
  9682. name: "invalid LoadBalancer source range annotation (hostname)",
  9683. tweakSvc: func(s *core.Service) {
  9684. s.Annotations[core.AnnotationLoadBalancerSourceRangesKey] = "foo.bar"
  9685. },
  9686. numErrs: 2,
  9687. },
  9688. {
  9689. name: "invalid LoadBalancer source range annotation (invalid CIDR)",
  9690. tweakSvc: func(s *core.Service) {
  9691. s.Spec.Type = core.ServiceTypeLoadBalancer
  9692. s.Annotations[core.AnnotationLoadBalancerSourceRangesKey] = "1.2.3.4/33"
  9693. },
  9694. numErrs: 1,
  9695. },
  9696. {
  9697. name: "invalid source range for non LoadBalancer type service",
  9698. tweakSvc: func(s *core.Service) {
  9699. s.Spec.LoadBalancerSourceRanges = []string{"1.2.3.4/8", "5.6.7.8/16"}
  9700. },
  9701. numErrs: 1,
  9702. },
  9703. {
  9704. name: "valid LoadBalancer source range",
  9705. tweakSvc: func(s *core.Service) {
  9706. s.Spec.Type = core.ServiceTypeLoadBalancer
  9707. s.Spec.LoadBalancerSourceRanges = []string{"1.2.3.4/8", "5.6.7.8/16"}
  9708. },
  9709. numErrs: 0,
  9710. },
  9711. {
  9712. name: "empty LoadBalancer source range",
  9713. tweakSvc: func(s *core.Service) {
  9714. s.Spec.Type = core.ServiceTypeLoadBalancer
  9715. s.Spec.LoadBalancerSourceRanges = []string{" "}
  9716. },
  9717. numErrs: 1,
  9718. },
  9719. {
  9720. name: "invalid LoadBalancer source range",
  9721. tweakSvc: func(s *core.Service) {
  9722. s.Spec.Type = core.ServiceTypeLoadBalancer
  9723. s.Spec.LoadBalancerSourceRanges = []string{"foo.bar"}
  9724. },
  9725. numErrs: 1,
  9726. },
  9727. {
  9728. name: "valid ExternalName",
  9729. tweakSvc: func(s *core.Service) {
  9730. s.Spec.Type = core.ServiceTypeExternalName
  9731. s.Spec.ClusterIP = ""
  9732. s.Spec.ExternalName = "foo.bar.example.com"
  9733. },
  9734. numErrs: 0,
  9735. },
  9736. {
  9737. name: "valid ExternalName (trailing dot)",
  9738. tweakSvc: func(s *core.Service) {
  9739. s.Spec.Type = core.ServiceTypeExternalName
  9740. s.Spec.ClusterIP = ""
  9741. s.Spec.ExternalName = "foo.bar.example.com."
  9742. },
  9743. numErrs: 0,
  9744. },
  9745. {
  9746. name: "invalid ExternalName clusterIP (valid IP)",
  9747. tweakSvc: func(s *core.Service) {
  9748. s.Spec.Type = core.ServiceTypeExternalName
  9749. s.Spec.ClusterIP = "1.2.3.4"
  9750. s.Spec.ExternalName = "foo.bar.example.com"
  9751. },
  9752. numErrs: 1,
  9753. },
  9754. {
  9755. name: "invalid ExternalName clusterIP (None)",
  9756. tweakSvc: func(s *core.Service) {
  9757. s.Spec.Type = core.ServiceTypeExternalName
  9758. s.Spec.ClusterIP = "None"
  9759. s.Spec.ExternalName = "foo.bar.example.com"
  9760. },
  9761. numErrs: 1,
  9762. },
  9763. {
  9764. name: "invalid ExternalName (not a DNS name)",
  9765. tweakSvc: func(s *core.Service) {
  9766. s.Spec.Type = core.ServiceTypeExternalName
  9767. s.Spec.ClusterIP = ""
  9768. s.Spec.ExternalName = "-123"
  9769. },
  9770. numErrs: 1,
  9771. },
  9772. {
  9773. name: "LoadBalancer type cannot have None ClusterIP",
  9774. tweakSvc: func(s *core.Service) {
  9775. s.Spec.ClusterIP = "None"
  9776. s.Spec.Type = core.ServiceTypeLoadBalancer
  9777. },
  9778. numErrs: 1,
  9779. },
  9780. {
  9781. name: "invalid node port with clusterIP None",
  9782. tweakSvc: func(s *core.Service) {
  9783. s.Spec.Type = core.ServiceTypeNodePort
  9784. s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 1, Protocol: "TCP", NodePort: 1, TargetPort: intstr.FromInt(1)})
  9785. s.Spec.ClusterIP = "None"
  9786. },
  9787. numErrs: 1,
  9788. },
  9789. // ESIPP section begins.
  9790. {
  9791. name: "invalid externalTraffic field",
  9792. tweakSvc: func(s *core.Service) {
  9793. s.Spec.Type = core.ServiceTypeLoadBalancer
  9794. s.Spec.ExternalTrafficPolicy = "invalid"
  9795. },
  9796. numErrs: 1,
  9797. },
  9798. {
  9799. name: "nagative healthCheckNodePort field",
  9800. tweakSvc: func(s *core.Service) {
  9801. s.Spec.Type = core.ServiceTypeLoadBalancer
  9802. s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyTypeLocal
  9803. s.Spec.HealthCheckNodePort = -1
  9804. },
  9805. numErrs: 1,
  9806. },
  9807. {
  9808. name: "nagative healthCheckNodePort field",
  9809. tweakSvc: func(s *core.Service) {
  9810. s.Spec.Type = core.ServiceTypeLoadBalancer
  9811. s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyTypeLocal
  9812. s.Spec.HealthCheckNodePort = 31100
  9813. },
  9814. numErrs: 0,
  9815. },
  9816. // ESIPP section ends.
  9817. {
  9818. name: "invalid timeoutSeconds field",
  9819. tweakSvc: func(s *core.Service) {
  9820. s.Spec.Type = core.ServiceTypeClusterIP
  9821. s.Spec.SessionAffinity = core.ServiceAffinityClientIP
  9822. s.Spec.SessionAffinityConfig = &core.SessionAffinityConfig{
  9823. ClientIP: &core.ClientIPConfig{
  9824. TimeoutSeconds: utilpointer.Int32Ptr(-1),
  9825. },
  9826. }
  9827. },
  9828. numErrs: 1,
  9829. },
  9830. {
  9831. name: "sessionAffinityConfig can't be set when session affinity is None",
  9832. tweakSvc: func(s *core.Service) {
  9833. s.Spec.Type = core.ServiceTypeLoadBalancer
  9834. s.Spec.SessionAffinity = core.ServiceAffinityNone
  9835. s.Spec.SessionAffinityConfig = &core.SessionAffinityConfig{
  9836. ClientIP: &core.ClientIPConfig{
  9837. TimeoutSeconds: utilpointer.Int32Ptr(90),
  9838. },
  9839. }
  9840. },
  9841. numErrs: 1,
  9842. },
  9843. {
  9844. name: "valid, nil service IPFamily",
  9845. tweakSvc: func(s *core.Service) {
  9846. s.Spec.IPFamily = nil
  9847. },
  9848. numErrs: 0,
  9849. },
  9850. {
  9851. name: "valid, service with valid IPFamily",
  9852. tweakSvc: func(s *core.Service) {
  9853. ipv4Service := core.IPv4Protocol
  9854. s.Spec.IPFamily = &ipv4Service
  9855. },
  9856. numErrs: 0,
  9857. },
  9858. {
  9859. name: "invalid, service with invalid IPFamily",
  9860. tweakSvc: func(s *core.Service) {
  9861. invalidServiceIPFamily := core.IPFamily("not-a-valid-ip-family")
  9862. s.Spec.IPFamily = &invalidServiceIPFamily
  9863. },
  9864. numErrs: 1,
  9865. },
  9866. {
  9867. name: "valid topology keys",
  9868. tweakSvc: func(s *core.Service) {
  9869. s.Spec.TopologyKeys = []string{
  9870. "kubernetes.io/hostname",
  9871. "failure-domain.beta.kubernetes.io/zone",
  9872. "failure-domain.beta.kubernetes.io/region",
  9873. v1.TopologyKeyAny,
  9874. }
  9875. },
  9876. numErrs: 0,
  9877. },
  9878. {
  9879. name: "invalid topology key",
  9880. tweakSvc: func(s *core.Service) {
  9881. s.Spec.TopologyKeys = []string{"NoUppercaseOrSpecialCharsLike=Equals"}
  9882. },
  9883. numErrs: 1,
  9884. },
  9885. {
  9886. name: "too many topology keys",
  9887. tweakSvc: func(s *core.Service) {
  9888. for i := 0; i < core.MaxServiceTopologyKeys+1; i++ {
  9889. s.Spec.TopologyKeys = append(s.Spec.TopologyKeys, fmt.Sprintf("topologykey-%d", i))
  9890. }
  9891. },
  9892. numErrs: 1,
  9893. },
  9894. {
  9895. name: `"Any" was not the last key`,
  9896. tweakSvc: func(s *core.Service) {
  9897. s.Spec.TopologyKeys = []string{
  9898. "kubernetes.io/hostname",
  9899. v1.TopologyKeyAny,
  9900. "failure-domain.beta.kubernetes.io/zone",
  9901. }
  9902. },
  9903. numErrs: 1,
  9904. },
  9905. {
  9906. name: `duplicate topology key`,
  9907. tweakSvc: func(s *core.Service) {
  9908. s.Spec.TopologyKeys = []string{
  9909. "kubernetes.io/hostname",
  9910. "kubernetes.io/hostname",
  9911. "failure-domain.beta.kubernetes.io/zone",
  9912. }
  9913. },
  9914. numErrs: 1,
  9915. },
  9916. {
  9917. name: `use topology keys with externalTrafficPolicy=Local`,
  9918. tweakSvc: func(s *core.Service) {
  9919. s.Spec.ExternalTrafficPolicy = "Local"
  9920. s.Spec.TopologyKeys = []string{
  9921. "kubernetes.io/hostname",
  9922. }
  9923. },
  9924. numErrs: 1,
  9925. },
  9926. }
  9927. for _, tc := range testCases {
  9928. svc := makeValidService()
  9929. tc.tweakSvc(&svc)
  9930. errs := ValidateService(&svc)
  9931. if len(errs) != tc.numErrs {
  9932. t.Errorf("Unexpected error list for case %q: %v", tc.name, errs.ToAggregate())
  9933. }
  9934. }
  9935. }
  9936. func TestValidateServiceExternalTrafficFieldsCombination(t *testing.T) {
  9937. testCases := []struct {
  9938. name string
  9939. tweakSvc func(svc *core.Service) // Given a basic valid service, each test case can customize it.
  9940. numErrs int
  9941. }{
  9942. {
  9943. name: "valid loadBalancer service with externalTrafficPolicy and healthCheckNodePort set",
  9944. tweakSvc: func(s *core.Service) {
  9945. s.Spec.Type = core.ServiceTypeLoadBalancer
  9946. s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyTypeLocal
  9947. s.Spec.HealthCheckNodePort = 34567
  9948. },
  9949. numErrs: 0,
  9950. },
  9951. {
  9952. name: "valid nodePort service with externalTrafficPolicy set",
  9953. tweakSvc: func(s *core.Service) {
  9954. s.Spec.Type = core.ServiceTypeNodePort
  9955. s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyTypeLocal
  9956. },
  9957. numErrs: 0,
  9958. },
  9959. {
  9960. name: "valid clusterIP service with none of externalTrafficPolicy and healthCheckNodePort set",
  9961. tweakSvc: func(s *core.Service) {
  9962. s.Spec.Type = core.ServiceTypeClusterIP
  9963. },
  9964. numErrs: 0,
  9965. },
  9966. {
  9967. name: "cannot set healthCheckNodePort field on loadBalancer service with externalTrafficPolicy!=Local",
  9968. tweakSvc: func(s *core.Service) {
  9969. s.Spec.Type = core.ServiceTypeLoadBalancer
  9970. s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyTypeCluster
  9971. s.Spec.HealthCheckNodePort = 34567
  9972. },
  9973. numErrs: 1,
  9974. },
  9975. {
  9976. name: "cannot set healthCheckNodePort field on nodePort service",
  9977. tweakSvc: func(s *core.Service) {
  9978. s.Spec.Type = core.ServiceTypeNodePort
  9979. s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyTypeLocal
  9980. s.Spec.HealthCheckNodePort = 34567
  9981. },
  9982. numErrs: 1,
  9983. },
  9984. {
  9985. name: "cannot set externalTrafficPolicy or healthCheckNodePort fields on clusterIP service",
  9986. tweakSvc: func(s *core.Service) {
  9987. s.Spec.Type = core.ServiceTypeClusterIP
  9988. s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyTypeLocal
  9989. s.Spec.HealthCheckNodePort = 34567
  9990. },
  9991. numErrs: 2,
  9992. },
  9993. }
  9994. for _, tc := range testCases {
  9995. svc := makeValidService()
  9996. tc.tweakSvc(&svc)
  9997. errs := ValidateServiceExternalTrafficFieldsCombination(&svc)
  9998. if len(errs) != tc.numErrs {
  9999. t.Errorf("Unexpected error list for case %q: %v", tc.name, errs.ToAggregate())
  10000. }
  10001. }
  10002. }
  10003. func TestValidateReplicationControllerStatus(t *testing.T) {
  10004. tests := []struct {
  10005. name string
  10006. replicas int32
  10007. fullyLabeledReplicas int32
  10008. readyReplicas int32
  10009. availableReplicas int32
  10010. observedGeneration int64
  10011. expectedErr bool
  10012. }{
  10013. {
  10014. name: "valid status",
  10015. replicas: 3,
  10016. fullyLabeledReplicas: 3,
  10017. readyReplicas: 2,
  10018. availableReplicas: 1,
  10019. observedGeneration: 2,
  10020. expectedErr: false,
  10021. },
  10022. {
  10023. name: "invalid replicas",
  10024. replicas: -1,
  10025. fullyLabeledReplicas: 3,
  10026. readyReplicas: 2,
  10027. availableReplicas: 1,
  10028. observedGeneration: 2,
  10029. expectedErr: true,
  10030. },
  10031. {
  10032. name: "invalid fullyLabeledReplicas",
  10033. replicas: 3,
  10034. fullyLabeledReplicas: -1,
  10035. readyReplicas: 2,
  10036. availableReplicas: 1,
  10037. observedGeneration: 2,
  10038. expectedErr: true,
  10039. },
  10040. {
  10041. name: "invalid readyReplicas",
  10042. replicas: 3,
  10043. fullyLabeledReplicas: 3,
  10044. readyReplicas: -1,
  10045. availableReplicas: 1,
  10046. observedGeneration: 2,
  10047. expectedErr: true,
  10048. },
  10049. {
  10050. name: "invalid availableReplicas",
  10051. replicas: 3,
  10052. fullyLabeledReplicas: 3,
  10053. readyReplicas: 3,
  10054. availableReplicas: -1,
  10055. observedGeneration: 2,
  10056. expectedErr: true,
  10057. },
  10058. {
  10059. name: "invalid observedGeneration",
  10060. replicas: 3,
  10061. fullyLabeledReplicas: 3,
  10062. readyReplicas: 3,
  10063. availableReplicas: 3,
  10064. observedGeneration: -1,
  10065. expectedErr: true,
  10066. },
  10067. {
  10068. name: "fullyLabeledReplicas greater than replicas",
  10069. replicas: 3,
  10070. fullyLabeledReplicas: 4,
  10071. readyReplicas: 3,
  10072. availableReplicas: 3,
  10073. observedGeneration: 1,
  10074. expectedErr: true,
  10075. },
  10076. {
  10077. name: "readyReplicas greater than replicas",
  10078. replicas: 3,
  10079. fullyLabeledReplicas: 3,
  10080. readyReplicas: 4,
  10081. availableReplicas: 3,
  10082. observedGeneration: 1,
  10083. expectedErr: true,
  10084. },
  10085. {
  10086. name: "availableReplicas greater than replicas",
  10087. replicas: 3,
  10088. fullyLabeledReplicas: 3,
  10089. readyReplicas: 3,
  10090. availableReplicas: 4,
  10091. observedGeneration: 1,
  10092. expectedErr: true,
  10093. },
  10094. {
  10095. name: "availableReplicas greater than readyReplicas",
  10096. replicas: 3,
  10097. fullyLabeledReplicas: 3,
  10098. readyReplicas: 2,
  10099. availableReplicas: 3,
  10100. observedGeneration: 1,
  10101. expectedErr: true,
  10102. },
  10103. }
  10104. for _, test := range tests {
  10105. status := core.ReplicationControllerStatus{
  10106. Replicas: test.replicas,
  10107. FullyLabeledReplicas: test.fullyLabeledReplicas,
  10108. ReadyReplicas: test.readyReplicas,
  10109. AvailableReplicas: test.availableReplicas,
  10110. ObservedGeneration: test.observedGeneration,
  10111. }
  10112. if hasErr := len(ValidateReplicationControllerStatus(status, field.NewPath("status"))) > 0; hasErr != test.expectedErr {
  10113. t.Errorf("%s: expected error: %t, got error: %t", test.name, test.expectedErr, hasErr)
  10114. }
  10115. }
  10116. }
  10117. func TestValidateReplicationControllerStatusUpdate(t *testing.T) {
  10118. validSelector := map[string]string{"a": "b"}
  10119. validPodTemplate := core.PodTemplate{
  10120. Template: core.PodTemplateSpec{
  10121. ObjectMeta: metav1.ObjectMeta{
  10122. Labels: validSelector,
  10123. },
  10124. Spec: core.PodSpec{
  10125. RestartPolicy: core.RestartPolicyAlways,
  10126. DNSPolicy: core.DNSClusterFirst,
  10127. Containers: []core.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  10128. },
  10129. },
  10130. }
  10131. type rcUpdateTest struct {
  10132. old core.ReplicationController
  10133. update core.ReplicationController
  10134. }
  10135. successCases := []rcUpdateTest{
  10136. {
  10137. old: core.ReplicationController{
  10138. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  10139. Spec: core.ReplicationControllerSpec{
  10140. Selector: validSelector,
  10141. Template: &validPodTemplate.Template,
  10142. },
  10143. Status: core.ReplicationControllerStatus{
  10144. Replicas: 2,
  10145. },
  10146. },
  10147. update: core.ReplicationController{
  10148. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  10149. Spec: core.ReplicationControllerSpec{
  10150. Replicas: 3,
  10151. Selector: validSelector,
  10152. Template: &validPodTemplate.Template,
  10153. },
  10154. Status: core.ReplicationControllerStatus{
  10155. Replicas: 4,
  10156. },
  10157. },
  10158. },
  10159. }
  10160. for _, successCase := range successCases {
  10161. successCase.old.ObjectMeta.ResourceVersion = "1"
  10162. successCase.update.ObjectMeta.ResourceVersion = "1"
  10163. if errs := ValidateReplicationControllerStatusUpdate(&successCase.update, &successCase.old); len(errs) != 0 {
  10164. t.Errorf("expected success: %v", errs)
  10165. }
  10166. }
  10167. errorCases := map[string]rcUpdateTest{
  10168. "negative replicas": {
  10169. old: core.ReplicationController{
  10170. ObjectMeta: metav1.ObjectMeta{Name: "", Namespace: metav1.NamespaceDefault},
  10171. Spec: core.ReplicationControllerSpec{
  10172. Selector: validSelector,
  10173. Template: &validPodTemplate.Template,
  10174. },
  10175. Status: core.ReplicationControllerStatus{
  10176. Replicas: 3,
  10177. },
  10178. },
  10179. update: core.ReplicationController{
  10180. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  10181. Spec: core.ReplicationControllerSpec{
  10182. Replicas: 2,
  10183. Selector: validSelector,
  10184. Template: &validPodTemplate.Template,
  10185. },
  10186. Status: core.ReplicationControllerStatus{
  10187. Replicas: -3,
  10188. },
  10189. },
  10190. },
  10191. }
  10192. for testName, errorCase := range errorCases {
  10193. if errs := ValidateReplicationControllerStatusUpdate(&errorCase.update, &errorCase.old); len(errs) == 0 {
  10194. t.Errorf("expected failure: %s", testName)
  10195. }
  10196. }
  10197. }
  10198. func TestValidateReplicationControllerUpdate(t *testing.T) {
  10199. validSelector := map[string]string{"a": "b"}
  10200. validPodTemplate := core.PodTemplate{
  10201. Template: core.PodTemplateSpec{
  10202. ObjectMeta: metav1.ObjectMeta{
  10203. Labels: validSelector,
  10204. },
  10205. Spec: core.PodSpec{
  10206. RestartPolicy: core.RestartPolicyAlways,
  10207. DNSPolicy: core.DNSClusterFirst,
  10208. Containers: []core.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  10209. },
  10210. },
  10211. }
  10212. readWriteVolumePodTemplate := core.PodTemplate{
  10213. Template: core.PodTemplateSpec{
  10214. ObjectMeta: metav1.ObjectMeta{
  10215. Labels: validSelector,
  10216. },
  10217. Spec: core.PodSpec{
  10218. RestartPolicy: core.RestartPolicyAlways,
  10219. DNSPolicy: core.DNSClusterFirst,
  10220. Containers: []core.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  10221. Volumes: []core.Volume{{Name: "gcepd", VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{PDName: "my-PD", FSType: "ext4", Partition: 1, ReadOnly: false}}}},
  10222. },
  10223. },
  10224. }
  10225. invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
  10226. invalidPodTemplate := core.PodTemplate{
  10227. Template: core.PodTemplateSpec{
  10228. Spec: core.PodSpec{
  10229. RestartPolicy: core.RestartPolicyAlways,
  10230. DNSPolicy: core.DNSClusterFirst,
  10231. },
  10232. ObjectMeta: metav1.ObjectMeta{
  10233. Labels: invalidSelector,
  10234. },
  10235. },
  10236. }
  10237. type rcUpdateTest struct {
  10238. old core.ReplicationController
  10239. update core.ReplicationController
  10240. }
  10241. successCases := []rcUpdateTest{
  10242. {
  10243. old: core.ReplicationController{
  10244. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  10245. Spec: core.ReplicationControllerSpec{
  10246. Selector: validSelector,
  10247. Template: &validPodTemplate.Template,
  10248. },
  10249. },
  10250. update: core.ReplicationController{
  10251. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  10252. Spec: core.ReplicationControllerSpec{
  10253. Replicas: 3,
  10254. Selector: validSelector,
  10255. Template: &validPodTemplate.Template,
  10256. },
  10257. },
  10258. },
  10259. {
  10260. old: core.ReplicationController{
  10261. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  10262. Spec: core.ReplicationControllerSpec{
  10263. Selector: validSelector,
  10264. Template: &validPodTemplate.Template,
  10265. },
  10266. },
  10267. update: core.ReplicationController{
  10268. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  10269. Spec: core.ReplicationControllerSpec{
  10270. Replicas: 1,
  10271. Selector: validSelector,
  10272. Template: &readWriteVolumePodTemplate.Template,
  10273. },
  10274. },
  10275. },
  10276. }
  10277. for _, successCase := range successCases {
  10278. successCase.old.ObjectMeta.ResourceVersion = "1"
  10279. successCase.update.ObjectMeta.ResourceVersion = "1"
  10280. if errs := ValidateReplicationControllerUpdate(&successCase.update, &successCase.old); len(errs) != 0 {
  10281. t.Errorf("expected success: %v", errs)
  10282. }
  10283. }
  10284. errorCases := map[string]rcUpdateTest{
  10285. "more than one read/write": {
  10286. old: core.ReplicationController{
  10287. ObjectMeta: metav1.ObjectMeta{Name: "", Namespace: metav1.NamespaceDefault},
  10288. Spec: core.ReplicationControllerSpec{
  10289. Selector: validSelector,
  10290. Template: &validPodTemplate.Template,
  10291. },
  10292. },
  10293. update: core.ReplicationController{
  10294. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  10295. Spec: core.ReplicationControllerSpec{
  10296. Replicas: 2,
  10297. Selector: validSelector,
  10298. Template: &readWriteVolumePodTemplate.Template,
  10299. },
  10300. },
  10301. },
  10302. "invalid selector": {
  10303. old: core.ReplicationController{
  10304. ObjectMeta: metav1.ObjectMeta{Name: "", Namespace: metav1.NamespaceDefault},
  10305. Spec: core.ReplicationControllerSpec{
  10306. Selector: validSelector,
  10307. Template: &validPodTemplate.Template,
  10308. },
  10309. },
  10310. update: core.ReplicationController{
  10311. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  10312. Spec: core.ReplicationControllerSpec{
  10313. Replicas: 2,
  10314. Selector: invalidSelector,
  10315. Template: &validPodTemplate.Template,
  10316. },
  10317. },
  10318. },
  10319. "invalid pod": {
  10320. old: core.ReplicationController{
  10321. ObjectMeta: metav1.ObjectMeta{Name: "", Namespace: metav1.NamespaceDefault},
  10322. Spec: core.ReplicationControllerSpec{
  10323. Selector: validSelector,
  10324. Template: &validPodTemplate.Template,
  10325. },
  10326. },
  10327. update: core.ReplicationController{
  10328. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  10329. Spec: core.ReplicationControllerSpec{
  10330. Replicas: 2,
  10331. Selector: validSelector,
  10332. Template: &invalidPodTemplate.Template,
  10333. },
  10334. },
  10335. },
  10336. "negative replicas": {
  10337. old: core.ReplicationController{
  10338. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  10339. Spec: core.ReplicationControllerSpec{
  10340. Selector: validSelector,
  10341. Template: &validPodTemplate.Template,
  10342. },
  10343. },
  10344. update: core.ReplicationController{
  10345. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  10346. Spec: core.ReplicationControllerSpec{
  10347. Replicas: -1,
  10348. Selector: validSelector,
  10349. Template: &validPodTemplate.Template,
  10350. },
  10351. },
  10352. },
  10353. }
  10354. for testName, errorCase := range errorCases {
  10355. if errs := ValidateReplicationControllerUpdate(&errorCase.update, &errorCase.old); len(errs) == 0 {
  10356. t.Errorf("expected failure: %s", testName)
  10357. }
  10358. }
  10359. }
  10360. func TestValidateReplicationController(t *testing.T) {
  10361. validSelector := map[string]string{"a": "b"}
  10362. validPodTemplate := core.PodTemplate{
  10363. Template: core.PodTemplateSpec{
  10364. ObjectMeta: metav1.ObjectMeta{
  10365. Labels: validSelector,
  10366. },
  10367. Spec: core.PodSpec{
  10368. RestartPolicy: core.RestartPolicyAlways,
  10369. DNSPolicy: core.DNSClusterFirst,
  10370. Containers: []core.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  10371. },
  10372. },
  10373. }
  10374. readWriteVolumePodTemplate := core.PodTemplate{
  10375. Template: core.PodTemplateSpec{
  10376. ObjectMeta: metav1.ObjectMeta{
  10377. Labels: validSelector,
  10378. },
  10379. Spec: core.PodSpec{
  10380. Volumes: []core.Volume{{Name: "gcepd", VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{PDName: "my-PD", FSType: "ext4", Partition: 1, ReadOnly: false}}}},
  10381. RestartPolicy: core.RestartPolicyAlways,
  10382. DNSPolicy: core.DNSClusterFirst,
  10383. Containers: []core.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  10384. },
  10385. },
  10386. }
  10387. invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
  10388. invalidPodTemplate := core.PodTemplate{
  10389. Template: core.PodTemplateSpec{
  10390. Spec: core.PodSpec{
  10391. RestartPolicy: core.RestartPolicyAlways,
  10392. DNSPolicy: core.DNSClusterFirst,
  10393. },
  10394. ObjectMeta: metav1.ObjectMeta{
  10395. Labels: invalidSelector,
  10396. },
  10397. },
  10398. }
  10399. successCases := []core.ReplicationController{
  10400. {
  10401. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  10402. Spec: core.ReplicationControllerSpec{
  10403. Selector: validSelector,
  10404. Template: &validPodTemplate.Template,
  10405. },
  10406. },
  10407. {
  10408. ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault},
  10409. Spec: core.ReplicationControllerSpec{
  10410. Selector: validSelector,
  10411. Template: &validPodTemplate.Template,
  10412. },
  10413. },
  10414. {
  10415. ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault},
  10416. Spec: core.ReplicationControllerSpec{
  10417. Replicas: 1,
  10418. Selector: validSelector,
  10419. Template: &readWriteVolumePodTemplate.Template,
  10420. },
  10421. },
  10422. }
  10423. for _, successCase := range successCases {
  10424. if errs := ValidateReplicationController(&successCase); len(errs) != 0 {
  10425. t.Errorf("expected success: %v", errs)
  10426. }
  10427. }
  10428. errorCases := map[string]core.ReplicationController{
  10429. "zero-length ID": {
  10430. ObjectMeta: metav1.ObjectMeta{Name: "", Namespace: metav1.NamespaceDefault},
  10431. Spec: core.ReplicationControllerSpec{
  10432. Selector: validSelector,
  10433. Template: &validPodTemplate.Template,
  10434. },
  10435. },
  10436. "missing-namespace": {
  10437. ObjectMeta: metav1.ObjectMeta{Name: "abc-123"},
  10438. Spec: core.ReplicationControllerSpec{
  10439. Selector: validSelector,
  10440. Template: &validPodTemplate.Template,
  10441. },
  10442. },
  10443. "empty selector": {
  10444. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  10445. Spec: core.ReplicationControllerSpec{
  10446. Template: &validPodTemplate.Template,
  10447. },
  10448. },
  10449. "selector_doesnt_match": {
  10450. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  10451. Spec: core.ReplicationControllerSpec{
  10452. Selector: map[string]string{"foo": "bar"},
  10453. Template: &validPodTemplate.Template,
  10454. },
  10455. },
  10456. "invalid manifest": {
  10457. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  10458. Spec: core.ReplicationControllerSpec{
  10459. Selector: validSelector,
  10460. },
  10461. },
  10462. "read-write persistent disk with > 1 pod": {
  10463. ObjectMeta: metav1.ObjectMeta{Name: "abc"},
  10464. Spec: core.ReplicationControllerSpec{
  10465. Replicas: 2,
  10466. Selector: validSelector,
  10467. Template: &readWriteVolumePodTemplate.Template,
  10468. },
  10469. },
  10470. "negative_replicas": {
  10471. ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
  10472. Spec: core.ReplicationControllerSpec{
  10473. Replicas: -1,
  10474. Selector: validSelector,
  10475. },
  10476. },
  10477. "invalid_label": {
  10478. ObjectMeta: metav1.ObjectMeta{
  10479. Name: "abc-123",
  10480. Namespace: metav1.NamespaceDefault,
  10481. Labels: map[string]string{
  10482. "NoUppercaseOrSpecialCharsLike=Equals": "bar",
  10483. },
  10484. },
  10485. Spec: core.ReplicationControllerSpec{
  10486. Selector: validSelector,
  10487. Template: &validPodTemplate.Template,
  10488. },
  10489. },
  10490. "invalid_label 2": {
  10491. ObjectMeta: metav1.ObjectMeta{
  10492. Name: "abc-123",
  10493. Namespace: metav1.NamespaceDefault,
  10494. Labels: map[string]string{
  10495. "NoUppercaseOrSpecialCharsLike=Equals": "bar",
  10496. },
  10497. },
  10498. Spec: core.ReplicationControllerSpec{
  10499. Template: &invalidPodTemplate.Template,
  10500. },
  10501. },
  10502. "invalid_annotation": {
  10503. ObjectMeta: metav1.ObjectMeta{
  10504. Name: "abc-123",
  10505. Namespace: metav1.NamespaceDefault,
  10506. Annotations: map[string]string{
  10507. "NoUppercaseOrSpecialCharsLike=Equals": "bar",
  10508. },
  10509. },
  10510. Spec: core.ReplicationControllerSpec{
  10511. Selector: validSelector,
  10512. Template: &validPodTemplate.Template,
  10513. },
  10514. },
  10515. "invalid restart policy 1": {
  10516. ObjectMeta: metav1.ObjectMeta{
  10517. Name: "abc-123",
  10518. Namespace: metav1.NamespaceDefault,
  10519. },
  10520. Spec: core.ReplicationControllerSpec{
  10521. Selector: validSelector,
  10522. Template: &core.PodTemplateSpec{
  10523. Spec: core.PodSpec{
  10524. RestartPolicy: core.RestartPolicyOnFailure,
  10525. DNSPolicy: core.DNSClusterFirst,
  10526. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  10527. },
  10528. ObjectMeta: metav1.ObjectMeta{
  10529. Labels: validSelector,
  10530. },
  10531. },
  10532. },
  10533. },
  10534. "invalid restart policy 2": {
  10535. ObjectMeta: metav1.ObjectMeta{
  10536. Name: "abc-123",
  10537. Namespace: metav1.NamespaceDefault,
  10538. },
  10539. Spec: core.ReplicationControllerSpec{
  10540. Selector: validSelector,
  10541. Template: &core.PodTemplateSpec{
  10542. Spec: core.PodSpec{
  10543. RestartPolicy: core.RestartPolicyNever,
  10544. DNSPolicy: core.DNSClusterFirst,
  10545. Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  10546. },
  10547. ObjectMeta: metav1.ObjectMeta{
  10548. Labels: validSelector,
  10549. },
  10550. },
  10551. },
  10552. },
  10553. "template may not contain ephemeral containers": {
  10554. ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault},
  10555. Spec: core.ReplicationControllerSpec{
  10556. Replicas: 1,
  10557. Selector: validSelector,
  10558. Template: &core.PodTemplateSpec{
  10559. ObjectMeta: metav1.ObjectMeta{
  10560. Labels: validSelector,
  10561. },
  10562. Spec: core.PodSpec{
  10563. RestartPolicy: core.RestartPolicyAlways,
  10564. DNSPolicy: core.DNSClusterFirst,
  10565. Containers: []core.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
  10566. EphemeralContainers: []core.EphemeralContainer{{EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "debug", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}}},
  10567. },
  10568. },
  10569. },
  10570. },
  10571. }
  10572. for k, v := range errorCases {
  10573. errs := ValidateReplicationController(&v)
  10574. if len(errs) == 0 {
  10575. t.Errorf("expected failure for %s", k)
  10576. }
  10577. for i := range errs {
  10578. field := errs[i].Field
  10579. if !strings.HasPrefix(field, "spec.template.") &&
  10580. field != "metadata.name" &&
  10581. field != "metadata.namespace" &&
  10582. field != "spec.selector" &&
  10583. field != "spec.template" &&
  10584. field != "GCEPersistentDisk.ReadOnly" &&
  10585. field != "spec.replicas" &&
  10586. field != "spec.template.labels" &&
  10587. field != "metadata.annotations" &&
  10588. field != "metadata.labels" &&
  10589. field != "status.replicas" {
  10590. t.Errorf("%s: missing prefix for: %v", k, errs[i])
  10591. }
  10592. }
  10593. }
  10594. }
  10595. func TestValidateNode(t *testing.T) {
  10596. opts := NodeValidationOptions{
  10597. ValidateSingleHugePageResource: true,
  10598. }
  10599. validSelector := map[string]string{"a": "b"}
  10600. invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
  10601. successCases := []core.Node{
  10602. {
  10603. ObjectMeta: metav1.ObjectMeta{
  10604. Name: "abc",
  10605. Labels: validSelector,
  10606. },
  10607. Status: core.NodeStatus{
  10608. Addresses: []core.NodeAddress{
  10609. {Type: core.NodeExternalIP, Address: "something"},
  10610. },
  10611. Capacity: core.ResourceList{
  10612. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  10613. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  10614. core.ResourceName("my.org/gpu"): resource.MustParse("10"),
  10615. core.ResourceName("hugepages-2Mi"): resource.MustParse("10Gi"),
  10616. core.ResourceName("hugepages-1Gi"): resource.MustParse("0"),
  10617. },
  10618. },
  10619. },
  10620. {
  10621. ObjectMeta: metav1.ObjectMeta{
  10622. Name: "abc",
  10623. },
  10624. Status: core.NodeStatus{
  10625. Addresses: []core.NodeAddress{
  10626. {Type: core.NodeExternalIP, Address: "something"},
  10627. },
  10628. Capacity: core.ResourceList{
  10629. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  10630. core.ResourceName(core.ResourceMemory): resource.MustParse("0"),
  10631. },
  10632. },
  10633. },
  10634. {
  10635. ObjectMeta: metav1.ObjectMeta{
  10636. Name: "dedicated-node1",
  10637. },
  10638. Status: core.NodeStatus{
  10639. Addresses: []core.NodeAddress{
  10640. {Type: core.NodeExternalIP, Address: "something"},
  10641. },
  10642. Capacity: core.ResourceList{
  10643. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  10644. core.ResourceName(core.ResourceMemory): resource.MustParse("0"),
  10645. },
  10646. },
  10647. Spec: core.NodeSpec{
  10648. // Add a valid taint to a node
  10649. Taints: []core.Taint{{Key: "GPU", Value: "true", Effect: "NoSchedule"}},
  10650. },
  10651. },
  10652. {
  10653. ObjectMeta: metav1.ObjectMeta{
  10654. Name: "abc",
  10655. Annotations: map[string]string{
  10656. core.PreferAvoidPodsAnnotationKey: `
  10657. {
  10658. "preferAvoidPods": [
  10659. {
  10660. "podSignature": {
  10661. "podController": {
  10662. "apiVersion": "v1",
  10663. "kind": "ReplicationController",
  10664. "name": "foo",
  10665. "uid": "abcdef123456",
  10666. "controller": true
  10667. }
  10668. },
  10669. "reason": "some reason",
  10670. "message": "some message"
  10671. }
  10672. ]
  10673. }`,
  10674. },
  10675. },
  10676. Status: core.NodeStatus{
  10677. Addresses: []core.NodeAddress{
  10678. {Type: core.NodeExternalIP, Address: "something"},
  10679. },
  10680. Capacity: core.ResourceList{
  10681. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  10682. core.ResourceName(core.ResourceMemory): resource.MustParse("0"),
  10683. },
  10684. },
  10685. },
  10686. {
  10687. ObjectMeta: metav1.ObjectMeta{
  10688. Name: "abc",
  10689. },
  10690. Status: core.NodeStatus{
  10691. Addresses: []core.NodeAddress{
  10692. {Type: core.NodeExternalIP, Address: "something"},
  10693. },
  10694. Capacity: core.ResourceList{
  10695. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  10696. core.ResourceName(core.ResourceMemory): resource.MustParse("0"),
  10697. },
  10698. },
  10699. Spec: core.NodeSpec{
  10700. PodCIDRs: []string{"192.168.0.0/16"},
  10701. },
  10702. },
  10703. }
  10704. for _, successCase := range successCases {
  10705. if errs := ValidateNode(&successCase, opts); len(errs) != 0 {
  10706. t.Errorf("expected success: %v", errs)
  10707. }
  10708. }
  10709. errorCases := map[string]core.Node{
  10710. "zero-length Name": {
  10711. ObjectMeta: metav1.ObjectMeta{
  10712. Name: "",
  10713. Labels: validSelector,
  10714. },
  10715. Status: core.NodeStatus{
  10716. Addresses: []core.NodeAddress{},
  10717. Capacity: core.ResourceList{
  10718. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  10719. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  10720. },
  10721. },
  10722. },
  10723. "invalid-labels": {
  10724. ObjectMeta: metav1.ObjectMeta{
  10725. Name: "abc-123",
  10726. Labels: invalidSelector,
  10727. },
  10728. Status: core.NodeStatus{
  10729. Capacity: core.ResourceList{
  10730. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  10731. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  10732. },
  10733. },
  10734. },
  10735. "missing-taint-key": {
  10736. ObjectMeta: metav1.ObjectMeta{
  10737. Name: "dedicated-node1",
  10738. },
  10739. Spec: core.NodeSpec{
  10740. // Add a taint with an empty key to a node
  10741. Taints: []core.Taint{{Key: "", Value: "special-user-1", Effect: "NoSchedule"}},
  10742. },
  10743. },
  10744. "bad-taint-key": {
  10745. ObjectMeta: metav1.ObjectMeta{
  10746. Name: "dedicated-node1",
  10747. },
  10748. Spec: core.NodeSpec{
  10749. // Add a taint with an invalid key to a node
  10750. Taints: []core.Taint{{Key: "NoUppercaseOrSpecialCharsLike=Equals", Value: "special-user-1", Effect: "NoSchedule"}},
  10751. },
  10752. },
  10753. "bad-taint-value": {
  10754. ObjectMeta: metav1.ObjectMeta{
  10755. Name: "dedicated-node2",
  10756. },
  10757. Status: core.NodeStatus{
  10758. Addresses: []core.NodeAddress{
  10759. {Type: core.NodeExternalIP, Address: "something"},
  10760. },
  10761. Capacity: core.ResourceList{
  10762. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  10763. core.ResourceName(core.ResourceMemory): resource.MustParse("0"),
  10764. },
  10765. },
  10766. Spec: core.NodeSpec{
  10767. // Add a taint with a bad value to a node
  10768. Taints: []core.Taint{{Key: "dedicated", Value: "some\\bad\\value", Effect: "NoSchedule"}},
  10769. },
  10770. },
  10771. "missing-taint-effect": {
  10772. ObjectMeta: metav1.ObjectMeta{
  10773. Name: "dedicated-node3",
  10774. },
  10775. Status: core.NodeStatus{
  10776. Addresses: []core.NodeAddress{
  10777. {Type: core.NodeExternalIP, Address: "something"},
  10778. },
  10779. Capacity: core.ResourceList{
  10780. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  10781. core.ResourceName(core.ResourceMemory): resource.MustParse("0"),
  10782. },
  10783. },
  10784. Spec: core.NodeSpec{
  10785. // Add a taint with an empty effect to a node
  10786. Taints: []core.Taint{{Key: "dedicated", Value: "special-user-3", Effect: ""}},
  10787. },
  10788. },
  10789. "invalid-taint-effect": {
  10790. ObjectMeta: metav1.ObjectMeta{
  10791. Name: "dedicated-node3",
  10792. },
  10793. Status: core.NodeStatus{
  10794. Addresses: []core.NodeAddress{
  10795. {Type: core.NodeExternalIP, Address: "something"},
  10796. },
  10797. Capacity: core.ResourceList{
  10798. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  10799. core.ResourceName(core.ResourceMemory): resource.MustParse("0"),
  10800. },
  10801. },
  10802. Spec: core.NodeSpec{
  10803. // Add a taint with NoExecute effect to a node
  10804. Taints: []core.Taint{{Key: "dedicated", Value: "special-user-3", Effect: "NoScheduleNoAdmit"}},
  10805. },
  10806. },
  10807. "duplicated-taints-with-same-key-effect": {
  10808. ObjectMeta: metav1.ObjectMeta{
  10809. Name: "dedicated-node1",
  10810. },
  10811. Spec: core.NodeSpec{
  10812. // Add two taints to the node with the same key and effect; should be rejected.
  10813. Taints: []core.Taint{
  10814. {Key: "dedicated", Value: "special-user-1", Effect: "NoSchedule"},
  10815. {Key: "dedicated", Value: "special-user-2", Effect: "NoSchedule"},
  10816. },
  10817. },
  10818. },
  10819. "missing-podSignature": {
  10820. ObjectMeta: metav1.ObjectMeta{
  10821. Name: "abc-123",
  10822. Annotations: map[string]string{
  10823. core.PreferAvoidPodsAnnotationKey: `
  10824. {
  10825. "preferAvoidPods": [
  10826. {
  10827. "reason": "some reason",
  10828. "message": "some message"
  10829. }
  10830. ]
  10831. }`,
  10832. },
  10833. },
  10834. Status: core.NodeStatus{
  10835. Addresses: []core.NodeAddress{},
  10836. Capacity: core.ResourceList{
  10837. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  10838. core.ResourceName(core.ResourceMemory): resource.MustParse("0"),
  10839. },
  10840. },
  10841. },
  10842. "invalid-podController": {
  10843. ObjectMeta: metav1.ObjectMeta{
  10844. Name: "abc-123",
  10845. Annotations: map[string]string{
  10846. core.PreferAvoidPodsAnnotationKey: `
  10847. {
  10848. "preferAvoidPods": [
  10849. {
  10850. "podSignature": {
  10851. "podController": {
  10852. "apiVersion": "v1",
  10853. "kind": "ReplicationController",
  10854. "name": "foo",
  10855. "uid": "abcdef123456",
  10856. "controller": false
  10857. }
  10858. },
  10859. "reason": "some reason",
  10860. "message": "some message"
  10861. }
  10862. ]
  10863. }`,
  10864. },
  10865. },
  10866. Status: core.NodeStatus{
  10867. Addresses: []core.NodeAddress{},
  10868. Capacity: core.ResourceList{
  10869. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  10870. core.ResourceName(core.ResourceMemory): resource.MustParse("0"),
  10871. },
  10872. },
  10873. },
  10874. "multiple-pre-allocated-hugepages": {
  10875. ObjectMeta: metav1.ObjectMeta{
  10876. Name: "abc",
  10877. Labels: validSelector,
  10878. },
  10879. Status: core.NodeStatus{
  10880. Addresses: []core.NodeAddress{
  10881. {Type: core.NodeExternalIP, Address: "something"},
  10882. },
  10883. Capacity: core.ResourceList{
  10884. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  10885. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  10886. core.ResourceName("my.org/gpu"): resource.MustParse("10"),
  10887. core.ResourceName("hugepages-2Mi"): resource.MustParse("10Gi"),
  10888. core.ResourceName("hugepages-1Gi"): resource.MustParse("10Gi"),
  10889. },
  10890. },
  10891. },
  10892. "invalid-pod-cidr": {
  10893. ObjectMeta: metav1.ObjectMeta{
  10894. Name: "abc",
  10895. },
  10896. Status: core.NodeStatus{
  10897. Addresses: []core.NodeAddress{
  10898. {Type: core.NodeExternalIP, Address: "something"},
  10899. },
  10900. Capacity: core.ResourceList{
  10901. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  10902. core.ResourceName(core.ResourceMemory): resource.MustParse("0"),
  10903. },
  10904. },
  10905. Spec: core.NodeSpec{
  10906. PodCIDRs: []string{"192.168.0.0"},
  10907. },
  10908. },
  10909. "duplicate-pod-cidr": {
  10910. ObjectMeta: metav1.ObjectMeta{
  10911. Name: "abc",
  10912. },
  10913. Status: core.NodeStatus{
  10914. Addresses: []core.NodeAddress{
  10915. {Type: core.NodeExternalIP, Address: "something"},
  10916. },
  10917. Capacity: core.ResourceList{
  10918. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  10919. core.ResourceName(core.ResourceMemory): resource.MustParse("0"),
  10920. },
  10921. },
  10922. Spec: core.NodeSpec{
  10923. PodCIDRs: []string{"10.0.0.1/16", "10.0.0.1/16"},
  10924. },
  10925. },
  10926. }
  10927. for k, v := range errorCases {
  10928. errs := ValidateNode(&v, opts)
  10929. if len(errs) == 0 {
  10930. t.Errorf("expected failure for %s", k)
  10931. }
  10932. for i := range errs {
  10933. field := errs[i].Field
  10934. expectedFields := map[string]bool{
  10935. "metadata.name": true,
  10936. "metadata.labels": true,
  10937. "metadata.annotations": true,
  10938. "metadata.namespace": true,
  10939. "spec.externalID": true,
  10940. "spec.taints[0].key": true,
  10941. "spec.taints[0].value": true,
  10942. "spec.taints[0].effect": true,
  10943. "metadata.annotations.scheduler.alpha.kubernetes.io/preferAvoidPods[0].PodSignature": true,
  10944. "metadata.annotations.scheduler.alpha.kubernetes.io/preferAvoidPods[0].PodSignature.PodController.Controller": true,
  10945. }
  10946. if val, ok := expectedFields[field]; ok {
  10947. if !val {
  10948. t.Errorf("%s: missing prefix for: %v", k, errs[i])
  10949. }
  10950. }
  10951. }
  10952. }
  10953. }
  10954. func TestNodeValidationOptions(t *testing.T) {
  10955. updateTests := []struct {
  10956. oldNode core.Node
  10957. node core.Node
  10958. opts NodeValidationOptions
  10959. valid bool
  10960. }{
  10961. {core.Node{
  10962. ObjectMeta: metav1.ObjectMeta{
  10963. Name: "validate-single-hugepages",
  10964. },
  10965. Status: core.NodeStatus{
  10966. Capacity: core.ResourceList{
  10967. core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
  10968. core.ResourceName("hugepages-1Gi"): resource.MustParse("0"),
  10969. },
  10970. },
  10971. }, core.Node{
  10972. ObjectMeta: metav1.ObjectMeta{
  10973. Name: "validate-single-hugepages",
  10974. },
  10975. Status: core.NodeStatus{
  10976. Capacity: core.ResourceList{
  10977. core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
  10978. core.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
  10979. },
  10980. },
  10981. }, NodeValidationOptions{ValidateSingleHugePageResource: true}, false},
  10982. {core.Node{
  10983. ObjectMeta: metav1.ObjectMeta{
  10984. Name: "validate-single-hugepages",
  10985. },
  10986. Status: core.NodeStatus{
  10987. Capacity: core.ResourceList{
  10988. core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
  10989. core.ResourceName("hugepages-1Gi"): resource.MustParse("1Gi"),
  10990. },
  10991. },
  10992. }, core.Node{
  10993. ObjectMeta: metav1.ObjectMeta{
  10994. Name: "validate-single-hugepages",
  10995. },
  10996. Status: core.NodeStatus{
  10997. Capacity: core.ResourceList{
  10998. core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
  10999. core.ResourceName("hugepages-1Gi"): resource.MustParse("1Gi"),
  11000. },
  11001. },
  11002. }, NodeValidationOptions{ValidateSingleHugePageResource: true}, false},
  11003. {core.Node{
  11004. ObjectMeta: metav1.ObjectMeta{
  11005. Name: "not-validate-single-hugepages",
  11006. },
  11007. Status: core.NodeStatus{
  11008. Capacity: core.ResourceList{
  11009. core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
  11010. core.ResourceName("hugepages-1Gi"): resource.MustParse("0"),
  11011. },
  11012. },
  11013. }, core.Node{
  11014. ObjectMeta: metav1.ObjectMeta{
  11015. Name: "not-validate-single-hugepages",
  11016. },
  11017. Status: core.NodeStatus{
  11018. Capacity: core.ResourceList{
  11019. core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
  11020. core.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
  11021. },
  11022. },
  11023. }, NodeValidationOptions{ValidateSingleHugePageResource: false}, true},
  11024. }
  11025. for i, test := range updateTests {
  11026. test.oldNode.ObjectMeta.ResourceVersion = "1"
  11027. test.node.ObjectMeta.ResourceVersion = "1"
  11028. errs := ValidateNodeUpdate(&test.node, &test.oldNode, test.opts)
  11029. if test.valid && len(errs) > 0 {
  11030. t.Errorf("%d: Unexpected error: %v", i, errs)
  11031. t.Logf("%#v vs %#v", test.oldNode.ObjectMeta, test.node.ObjectMeta)
  11032. }
  11033. if !test.valid && len(errs) == 0 {
  11034. t.Errorf("%d: Unexpected non-error", i)
  11035. t.Logf("%#v vs %#v", test.oldNode.ObjectMeta, test.node.ObjectMeta)
  11036. }
  11037. }
  11038. nodeTests := []struct {
  11039. node core.Node
  11040. opts NodeValidationOptions
  11041. valid bool
  11042. }{
  11043. {core.Node{
  11044. ObjectMeta: metav1.ObjectMeta{
  11045. Name: "validate-single-hugepages",
  11046. },
  11047. Status: core.NodeStatus{
  11048. Capacity: core.ResourceList{
  11049. core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
  11050. core.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
  11051. },
  11052. },
  11053. }, NodeValidationOptions{ValidateSingleHugePageResource: true}, false},
  11054. {core.Node{
  11055. ObjectMeta: metav1.ObjectMeta{
  11056. Name: "not-validate-single-hugepages",
  11057. },
  11058. Status: core.NodeStatus{
  11059. Capacity: core.ResourceList{
  11060. core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
  11061. core.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
  11062. core.ResourceName("hugepages-64Ki"): resource.MustParse("2Gi"),
  11063. },
  11064. },
  11065. }, NodeValidationOptions{ValidateSingleHugePageResource: false}, true},
  11066. }
  11067. for i, test := range nodeTests {
  11068. test.node.ObjectMeta.ResourceVersion = "1"
  11069. errs := ValidateNode(&test.node, test.opts)
  11070. if test.valid && len(errs) > 0 {
  11071. t.Errorf("%d: Unexpected error: %v", i, errs)
  11072. }
  11073. if !test.valid && len(errs) == 0 {
  11074. t.Errorf("%d: Unexpected non-error", i)
  11075. }
  11076. }
  11077. }
  11078. func TestValidateNodeUpdate(t *testing.T) {
  11079. opts := NodeValidationOptions{
  11080. ValidateSingleHugePageResource: true,
  11081. }
  11082. tests := []struct {
  11083. oldNode core.Node
  11084. node core.Node
  11085. valid bool
  11086. }{
  11087. {core.Node{}, core.Node{}, true},
  11088. {core.Node{
  11089. ObjectMeta: metav1.ObjectMeta{
  11090. Name: "foo"}},
  11091. core.Node{
  11092. ObjectMeta: metav1.ObjectMeta{
  11093. Name: "bar"},
  11094. }, false},
  11095. {core.Node{
  11096. ObjectMeta: metav1.ObjectMeta{
  11097. Name: "foo",
  11098. Labels: map[string]string{"foo": "bar"},
  11099. },
  11100. }, core.Node{
  11101. ObjectMeta: metav1.ObjectMeta{
  11102. Name: "foo",
  11103. Labels: map[string]string{"foo": "baz"},
  11104. },
  11105. }, true},
  11106. {core.Node{
  11107. ObjectMeta: metav1.ObjectMeta{
  11108. Name: "foo",
  11109. },
  11110. }, core.Node{
  11111. ObjectMeta: metav1.ObjectMeta{
  11112. Name: "foo",
  11113. Labels: map[string]string{"foo": "baz"},
  11114. },
  11115. }, true},
  11116. {core.Node{
  11117. ObjectMeta: metav1.ObjectMeta{
  11118. Name: "foo",
  11119. Labels: map[string]string{"bar": "foo"},
  11120. },
  11121. }, core.Node{
  11122. ObjectMeta: metav1.ObjectMeta{
  11123. Name: "foo",
  11124. Labels: map[string]string{"foo": "baz"},
  11125. },
  11126. }, true},
  11127. {core.Node{
  11128. ObjectMeta: metav1.ObjectMeta{
  11129. Name: "foo",
  11130. },
  11131. Spec: core.NodeSpec{
  11132. PodCIDRs: []string{},
  11133. },
  11134. }, core.Node{
  11135. ObjectMeta: metav1.ObjectMeta{
  11136. Name: "foo",
  11137. },
  11138. Spec: core.NodeSpec{
  11139. PodCIDRs: []string{"192.168.0.0/16"},
  11140. },
  11141. }, true},
  11142. {core.Node{
  11143. ObjectMeta: metav1.ObjectMeta{
  11144. Name: "foo",
  11145. },
  11146. Spec: core.NodeSpec{
  11147. PodCIDRs: []string{"192.123.0.0/16"},
  11148. },
  11149. }, core.Node{
  11150. ObjectMeta: metav1.ObjectMeta{
  11151. Name: "foo",
  11152. },
  11153. Spec: core.NodeSpec{
  11154. PodCIDRs: []string{"192.168.0.0/16"},
  11155. },
  11156. }, false},
  11157. {core.Node{
  11158. ObjectMeta: metav1.ObjectMeta{
  11159. Name: "foo",
  11160. },
  11161. Status: core.NodeStatus{
  11162. Capacity: core.ResourceList{
  11163. core.ResourceCPU: resource.MustParse("10000"),
  11164. core.ResourceMemory: resource.MustParse("100"),
  11165. },
  11166. },
  11167. }, core.Node{
  11168. ObjectMeta: metav1.ObjectMeta{
  11169. Name: "foo",
  11170. },
  11171. Status: core.NodeStatus{
  11172. Capacity: core.ResourceList{
  11173. core.ResourceCPU: resource.MustParse("100"),
  11174. core.ResourceMemory: resource.MustParse("10000"),
  11175. },
  11176. },
  11177. }, true},
  11178. {core.Node{
  11179. ObjectMeta: metav1.ObjectMeta{
  11180. Name: "foo",
  11181. Labels: map[string]string{"bar": "foo"},
  11182. },
  11183. Status: core.NodeStatus{
  11184. Capacity: core.ResourceList{
  11185. core.ResourceCPU: resource.MustParse("10000"),
  11186. core.ResourceMemory: resource.MustParse("100"),
  11187. },
  11188. },
  11189. }, core.Node{
  11190. ObjectMeta: metav1.ObjectMeta{
  11191. Name: "foo",
  11192. Labels: map[string]string{"bar": "fooobaz"},
  11193. },
  11194. Status: core.NodeStatus{
  11195. Capacity: core.ResourceList{
  11196. core.ResourceCPU: resource.MustParse("100"),
  11197. core.ResourceMemory: resource.MustParse("10000"),
  11198. },
  11199. },
  11200. }, true},
  11201. {core.Node{
  11202. ObjectMeta: metav1.ObjectMeta{
  11203. Name: "foo",
  11204. Labels: map[string]string{"bar": "foo"},
  11205. },
  11206. Status: core.NodeStatus{
  11207. Addresses: []core.NodeAddress{
  11208. {Type: core.NodeExternalIP, Address: "1.2.3.4"},
  11209. },
  11210. },
  11211. }, core.Node{
  11212. ObjectMeta: metav1.ObjectMeta{
  11213. Name: "foo",
  11214. Labels: map[string]string{"bar": "fooobaz"},
  11215. },
  11216. }, true},
  11217. {core.Node{
  11218. ObjectMeta: metav1.ObjectMeta{
  11219. Name: "foo",
  11220. Labels: map[string]string{"foo": "baz"},
  11221. },
  11222. }, core.Node{
  11223. ObjectMeta: metav1.ObjectMeta{
  11224. Name: "foo",
  11225. Labels: map[string]string{"Foo": "baz"},
  11226. },
  11227. }, true},
  11228. {core.Node{
  11229. ObjectMeta: metav1.ObjectMeta{
  11230. Name: "foo",
  11231. },
  11232. Spec: core.NodeSpec{
  11233. Unschedulable: false,
  11234. },
  11235. }, core.Node{
  11236. ObjectMeta: metav1.ObjectMeta{
  11237. Name: "foo",
  11238. },
  11239. Spec: core.NodeSpec{
  11240. Unschedulable: true,
  11241. },
  11242. }, true},
  11243. {core.Node{
  11244. ObjectMeta: metav1.ObjectMeta{
  11245. Name: "foo",
  11246. },
  11247. Spec: core.NodeSpec{
  11248. Unschedulable: false,
  11249. },
  11250. }, core.Node{
  11251. ObjectMeta: metav1.ObjectMeta{
  11252. Name: "foo",
  11253. },
  11254. Status: core.NodeStatus{
  11255. Addresses: []core.NodeAddress{
  11256. {Type: core.NodeExternalIP, Address: "1.1.1.1"},
  11257. {Type: core.NodeExternalIP, Address: "1.1.1.1"},
  11258. },
  11259. },
  11260. }, false},
  11261. {core.Node{
  11262. ObjectMeta: metav1.ObjectMeta{
  11263. Name: "foo",
  11264. },
  11265. Spec: core.NodeSpec{
  11266. Unschedulable: false,
  11267. },
  11268. }, core.Node{
  11269. ObjectMeta: metav1.ObjectMeta{
  11270. Name: "foo",
  11271. },
  11272. Status: core.NodeStatus{
  11273. Addresses: []core.NodeAddress{
  11274. {Type: core.NodeExternalIP, Address: "1.1.1.1"},
  11275. {Type: core.NodeInternalIP, Address: "10.1.1.1"},
  11276. },
  11277. },
  11278. }, true},
  11279. {core.Node{
  11280. ObjectMeta: metav1.ObjectMeta{
  11281. Name: "foo",
  11282. },
  11283. }, core.Node{
  11284. ObjectMeta: metav1.ObjectMeta{
  11285. Name: "foo",
  11286. Annotations: map[string]string{
  11287. core.PreferAvoidPodsAnnotationKey: `
  11288. {
  11289. "preferAvoidPods": [
  11290. {
  11291. "podSignature": {
  11292. "podController": {
  11293. "apiVersion": "v1",
  11294. "kind": "ReplicationController",
  11295. "name": "foo",
  11296. "uid": "abcdef123456",
  11297. "controller": true
  11298. }
  11299. },
  11300. "reason": "some reason",
  11301. "message": "some message"
  11302. }
  11303. ]
  11304. }`,
  11305. },
  11306. },
  11307. Spec: core.NodeSpec{
  11308. Unschedulable: false,
  11309. },
  11310. }, true},
  11311. {core.Node{
  11312. ObjectMeta: metav1.ObjectMeta{
  11313. Name: "foo",
  11314. },
  11315. }, core.Node{
  11316. ObjectMeta: metav1.ObjectMeta{
  11317. Name: "foo",
  11318. Annotations: map[string]string{
  11319. core.PreferAvoidPodsAnnotationKey: `
  11320. {
  11321. "preferAvoidPods": [
  11322. {
  11323. "reason": "some reason",
  11324. "message": "some message"
  11325. }
  11326. ]
  11327. }`,
  11328. },
  11329. },
  11330. }, false},
  11331. {core.Node{
  11332. ObjectMeta: metav1.ObjectMeta{
  11333. Name: "foo",
  11334. },
  11335. }, core.Node{
  11336. ObjectMeta: metav1.ObjectMeta{
  11337. Name: "foo",
  11338. Annotations: map[string]string{
  11339. core.PreferAvoidPodsAnnotationKey: `
  11340. {
  11341. "preferAvoidPods": [
  11342. {
  11343. "podSignature": {
  11344. "podController": {
  11345. "apiVersion": "v1",
  11346. "kind": "ReplicationController",
  11347. "name": "foo",
  11348. "uid": "abcdef123456",
  11349. "controller": false
  11350. }
  11351. },
  11352. "reason": "some reason",
  11353. "message": "some message"
  11354. }
  11355. ]
  11356. }`,
  11357. },
  11358. },
  11359. }, false},
  11360. {core.Node{
  11361. ObjectMeta: metav1.ObjectMeta{
  11362. Name: "valid-extended-resources",
  11363. },
  11364. }, core.Node{
  11365. ObjectMeta: metav1.ObjectMeta{
  11366. Name: "valid-extended-resources",
  11367. },
  11368. Status: core.NodeStatus{
  11369. Capacity: core.ResourceList{
  11370. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  11371. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  11372. core.ResourceName("example.com/a"): resource.MustParse("5"),
  11373. core.ResourceName("example.com/b"): resource.MustParse("10"),
  11374. },
  11375. },
  11376. }, true},
  11377. {core.Node{
  11378. ObjectMeta: metav1.ObjectMeta{
  11379. Name: "invalid-fractional-extended-capacity",
  11380. },
  11381. }, core.Node{
  11382. ObjectMeta: metav1.ObjectMeta{
  11383. Name: "invalid-fractional-extended-capacity",
  11384. },
  11385. Status: core.NodeStatus{
  11386. Capacity: core.ResourceList{
  11387. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  11388. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  11389. core.ResourceName("example.com/a"): resource.MustParse("500m"),
  11390. },
  11391. },
  11392. }, false},
  11393. {core.Node{
  11394. ObjectMeta: metav1.ObjectMeta{
  11395. Name: "invalid-fractional-extended-allocatable",
  11396. },
  11397. }, core.Node{
  11398. ObjectMeta: metav1.ObjectMeta{
  11399. Name: "invalid-fractional-extended-allocatable",
  11400. },
  11401. Status: core.NodeStatus{
  11402. Capacity: core.ResourceList{
  11403. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  11404. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  11405. core.ResourceName("example.com/a"): resource.MustParse("5"),
  11406. },
  11407. Allocatable: core.ResourceList{
  11408. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  11409. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  11410. core.ResourceName("example.com/a"): resource.MustParse("4.5"),
  11411. },
  11412. },
  11413. }, false},
  11414. {core.Node{
  11415. ObjectMeta: metav1.ObjectMeta{
  11416. Name: "update-provider-id-when-not-set",
  11417. },
  11418. }, core.Node{
  11419. ObjectMeta: metav1.ObjectMeta{
  11420. Name: "update-provider-id-when-not-set",
  11421. },
  11422. Spec: core.NodeSpec{
  11423. ProviderID: "provider:///new",
  11424. },
  11425. }, true},
  11426. {core.Node{
  11427. ObjectMeta: metav1.ObjectMeta{
  11428. Name: "update-provider-id-when-set",
  11429. },
  11430. Spec: core.NodeSpec{
  11431. ProviderID: "provider:///old",
  11432. },
  11433. }, core.Node{
  11434. ObjectMeta: metav1.ObjectMeta{
  11435. Name: "update-provider-id-when-set",
  11436. },
  11437. Spec: core.NodeSpec{
  11438. ProviderID: "provider:///new",
  11439. },
  11440. }, false},
  11441. {core.Node{
  11442. ObjectMeta: metav1.ObjectMeta{
  11443. Name: "pod-cidrs-as-is",
  11444. },
  11445. Spec: core.NodeSpec{
  11446. PodCIDRs: []string{"192.168.0.0/16"},
  11447. },
  11448. }, core.Node{
  11449. ObjectMeta: metav1.ObjectMeta{
  11450. Name: "pod-cidrs-as-is",
  11451. },
  11452. Spec: core.NodeSpec{
  11453. PodCIDRs: []string{"192.168.0.0/16"},
  11454. },
  11455. }, true},
  11456. {core.Node{
  11457. ObjectMeta: metav1.ObjectMeta{
  11458. Name: "pod-cidrs-as-is-2",
  11459. },
  11460. Spec: core.NodeSpec{
  11461. PodCIDRs: []string{"192.168.0.0/16", "2000::/10"},
  11462. },
  11463. }, core.Node{
  11464. ObjectMeta: metav1.ObjectMeta{
  11465. Name: "pod-cidrs-as-is-2",
  11466. },
  11467. Spec: core.NodeSpec{
  11468. PodCIDRs: []string{"192.168.0.0/16", "2000::/10"},
  11469. },
  11470. }, true},
  11471. {core.Node{
  11472. ObjectMeta: metav1.ObjectMeta{
  11473. Name: "pod-cidrs-not-same-length",
  11474. },
  11475. Spec: core.NodeSpec{
  11476. PodCIDRs: []string{"192.168.0.0/16", "192.167.0.0/16", "2000::/10"},
  11477. },
  11478. }, core.Node{
  11479. ObjectMeta: metav1.ObjectMeta{
  11480. Name: "pod-cidrs-not-same-length",
  11481. },
  11482. Spec: core.NodeSpec{
  11483. PodCIDRs: []string{"192.168.0.0/16", "2000::/10"},
  11484. },
  11485. }, false},
  11486. {core.Node{
  11487. ObjectMeta: metav1.ObjectMeta{
  11488. Name: "pod-cidrs-not-same",
  11489. },
  11490. Spec: core.NodeSpec{
  11491. PodCIDRs: []string{"192.168.0.0/16", "2000::/10"},
  11492. },
  11493. }, core.Node{
  11494. ObjectMeta: metav1.ObjectMeta{
  11495. Name: "pod-cidrs-not-same",
  11496. },
  11497. Spec: core.NodeSpec{
  11498. PodCIDRs: []string{"2000::/10", "192.168.0.0/16"},
  11499. },
  11500. }, false},
  11501. }
  11502. for i, test := range tests {
  11503. test.oldNode.ObjectMeta.ResourceVersion = "1"
  11504. test.node.ObjectMeta.ResourceVersion = "1"
  11505. errs := ValidateNodeUpdate(&test.node, &test.oldNode, opts)
  11506. if test.valid && len(errs) > 0 {
  11507. t.Errorf("%d: Unexpected error: %v", i, errs)
  11508. t.Logf("%#v vs %#v", test.oldNode.ObjectMeta, test.node.ObjectMeta)
  11509. }
  11510. if !test.valid && len(errs) == 0 {
  11511. t.Errorf("%d: Unexpected non-error", i)
  11512. }
  11513. }
  11514. }
  11515. func TestValidateServiceUpdate(t *testing.T) {
  11516. testCases := []struct {
  11517. name string
  11518. tweakSvc func(oldSvc, newSvc *core.Service) // given basic valid services, each test case can customize them
  11519. numErrs int
  11520. }{
  11521. {
  11522. name: "no change",
  11523. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11524. // do nothing
  11525. },
  11526. numErrs: 0,
  11527. },
  11528. {
  11529. name: "change name",
  11530. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11531. newSvc.Name += "2"
  11532. },
  11533. numErrs: 1,
  11534. },
  11535. {
  11536. name: "change namespace",
  11537. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11538. newSvc.Namespace += "2"
  11539. },
  11540. numErrs: 1,
  11541. },
  11542. {
  11543. name: "change label valid",
  11544. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11545. newSvc.Labels["key"] = "other-value"
  11546. },
  11547. numErrs: 0,
  11548. },
  11549. {
  11550. name: "add label",
  11551. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11552. newSvc.Labels["key2"] = "value2"
  11553. },
  11554. numErrs: 0,
  11555. },
  11556. {
  11557. name: "change cluster IP",
  11558. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11559. oldSvc.Spec.ClusterIP = "1.2.3.4"
  11560. newSvc.Spec.ClusterIP = "8.6.7.5"
  11561. },
  11562. numErrs: 1,
  11563. },
  11564. {
  11565. name: "remove cluster IP",
  11566. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11567. oldSvc.Spec.ClusterIP = "1.2.3.4"
  11568. newSvc.Spec.ClusterIP = ""
  11569. },
  11570. numErrs: 1,
  11571. },
  11572. {
  11573. name: "change affinity",
  11574. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11575. newSvc.Spec.SessionAffinity = "ClientIP"
  11576. newSvc.Spec.SessionAffinityConfig = &core.SessionAffinityConfig{
  11577. ClientIP: &core.ClientIPConfig{
  11578. TimeoutSeconds: utilpointer.Int32Ptr(90),
  11579. },
  11580. }
  11581. },
  11582. numErrs: 0,
  11583. },
  11584. {
  11585. name: "remove affinity",
  11586. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11587. newSvc.Spec.SessionAffinity = ""
  11588. },
  11589. numErrs: 1,
  11590. },
  11591. {
  11592. name: "change type",
  11593. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11594. newSvc.Spec.Type = core.ServiceTypeLoadBalancer
  11595. },
  11596. numErrs: 0,
  11597. },
  11598. {
  11599. name: "remove type",
  11600. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11601. newSvc.Spec.Type = ""
  11602. },
  11603. numErrs: 1,
  11604. },
  11605. {
  11606. name: "change type -> nodeport",
  11607. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11608. newSvc.Spec.Type = core.ServiceTypeNodePort
  11609. },
  11610. numErrs: 0,
  11611. },
  11612. {
  11613. name: "add loadBalancerSourceRanges",
  11614. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11615. oldSvc.Spec.Type = core.ServiceTypeLoadBalancer
  11616. newSvc.Spec.Type = core.ServiceTypeLoadBalancer
  11617. newSvc.Spec.LoadBalancerSourceRanges = []string{"10.0.0.0/8"}
  11618. },
  11619. numErrs: 0,
  11620. },
  11621. {
  11622. name: "update loadBalancerSourceRanges",
  11623. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11624. oldSvc.Spec.Type = core.ServiceTypeLoadBalancer
  11625. oldSvc.Spec.LoadBalancerSourceRanges = []string{"10.0.0.0/8"}
  11626. newSvc.Spec.Type = core.ServiceTypeLoadBalancer
  11627. newSvc.Spec.LoadBalancerSourceRanges = []string{"10.100.0.0/16"}
  11628. },
  11629. numErrs: 0,
  11630. },
  11631. {
  11632. name: "LoadBalancer type cannot have None ClusterIP",
  11633. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11634. newSvc.Spec.ClusterIP = "None"
  11635. newSvc.Spec.Type = core.ServiceTypeLoadBalancer
  11636. },
  11637. numErrs: 1,
  11638. },
  11639. {
  11640. name: "`None` ClusterIP cannot be changed",
  11641. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11642. oldSvc.Spec.ClusterIP = "None"
  11643. newSvc.Spec.ClusterIP = "1.2.3.4"
  11644. },
  11645. numErrs: 1,
  11646. },
  11647. {
  11648. name: "`None` ClusterIP cannot be removed",
  11649. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11650. oldSvc.Spec.ClusterIP = "None"
  11651. newSvc.Spec.ClusterIP = ""
  11652. },
  11653. numErrs: 1,
  11654. },
  11655. {
  11656. name: "Service with ClusterIP type cannot change its set ClusterIP",
  11657. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11658. oldSvc.Spec.Type = core.ServiceTypeClusterIP
  11659. newSvc.Spec.Type = core.ServiceTypeClusterIP
  11660. oldSvc.Spec.ClusterIP = "1.2.3.4"
  11661. newSvc.Spec.ClusterIP = "1.2.3.5"
  11662. },
  11663. numErrs: 1,
  11664. },
  11665. {
  11666. name: "Service with ClusterIP type can change its empty ClusterIP",
  11667. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11668. oldSvc.Spec.Type = core.ServiceTypeClusterIP
  11669. newSvc.Spec.Type = core.ServiceTypeClusterIP
  11670. oldSvc.Spec.ClusterIP = ""
  11671. newSvc.Spec.ClusterIP = "1.2.3.5"
  11672. },
  11673. numErrs: 0,
  11674. },
  11675. {
  11676. name: "Service with ClusterIP type cannot change its set ClusterIP when changing type to NodePort",
  11677. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11678. oldSvc.Spec.Type = core.ServiceTypeClusterIP
  11679. newSvc.Spec.Type = core.ServiceTypeNodePort
  11680. oldSvc.Spec.ClusterIP = "1.2.3.4"
  11681. newSvc.Spec.ClusterIP = "1.2.3.5"
  11682. },
  11683. numErrs: 1,
  11684. },
  11685. {
  11686. name: "Service with ClusterIP type can change its empty ClusterIP when changing type to NodePort",
  11687. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11688. oldSvc.Spec.Type = core.ServiceTypeClusterIP
  11689. newSvc.Spec.Type = core.ServiceTypeNodePort
  11690. oldSvc.Spec.ClusterIP = ""
  11691. newSvc.Spec.ClusterIP = "1.2.3.5"
  11692. },
  11693. numErrs: 0,
  11694. },
  11695. {
  11696. name: "Service with ClusterIP type cannot change its ClusterIP when changing type to LoadBalancer",
  11697. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11698. oldSvc.Spec.Type = core.ServiceTypeClusterIP
  11699. newSvc.Spec.Type = core.ServiceTypeLoadBalancer
  11700. oldSvc.Spec.ClusterIP = "1.2.3.4"
  11701. newSvc.Spec.ClusterIP = "1.2.3.5"
  11702. },
  11703. numErrs: 1,
  11704. },
  11705. {
  11706. name: "Service with ClusterIP type can change its empty ClusterIP when changing type to LoadBalancer",
  11707. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11708. oldSvc.Spec.Type = core.ServiceTypeClusterIP
  11709. newSvc.Spec.Type = core.ServiceTypeLoadBalancer
  11710. oldSvc.Spec.ClusterIP = ""
  11711. newSvc.Spec.ClusterIP = "1.2.3.5"
  11712. },
  11713. numErrs: 0,
  11714. },
  11715. {
  11716. name: "Service with NodePort type cannot change its set ClusterIP",
  11717. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11718. oldSvc.Spec.Type = core.ServiceTypeNodePort
  11719. newSvc.Spec.Type = core.ServiceTypeNodePort
  11720. oldSvc.Spec.ClusterIP = "1.2.3.4"
  11721. newSvc.Spec.ClusterIP = "1.2.3.5"
  11722. },
  11723. numErrs: 1,
  11724. },
  11725. {
  11726. name: "Service with NodePort type can change its empty ClusterIP",
  11727. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11728. oldSvc.Spec.Type = core.ServiceTypeNodePort
  11729. newSvc.Spec.Type = core.ServiceTypeNodePort
  11730. oldSvc.Spec.ClusterIP = ""
  11731. newSvc.Spec.ClusterIP = "1.2.3.5"
  11732. },
  11733. numErrs: 0,
  11734. },
  11735. {
  11736. name: "Service with NodePort type cannot change its set ClusterIP when changing type to ClusterIP",
  11737. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11738. oldSvc.Spec.Type = core.ServiceTypeNodePort
  11739. newSvc.Spec.Type = core.ServiceTypeClusterIP
  11740. oldSvc.Spec.ClusterIP = "1.2.3.4"
  11741. newSvc.Spec.ClusterIP = "1.2.3.5"
  11742. },
  11743. numErrs: 1,
  11744. },
  11745. {
  11746. name: "Service with NodePort type can change its empty ClusterIP when changing type to ClusterIP",
  11747. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11748. oldSvc.Spec.Type = core.ServiceTypeNodePort
  11749. newSvc.Spec.Type = core.ServiceTypeClusterIP
  11750. oldSvc.Spec.ClusterIP = ""
  11751. newSvc.Spec.ClusterIP = "1.2.3.5"
  11752. },
  11753. numErrs: 0,
  11754. },
  11755. {
  11756. name: "Service with NodePort type cannot change its set ClusterIP when changing type to LoadBalancer",
  11757. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11758. oldSvc.Spec.Type = core.ServiceTypeNodePort
  11759. newSvc.Spec.Type = core.ServiceTypeLoadBalancer
  11760. oldSvc.Spec.ClusterIP = "1.2.3.4"
  11761. newSvc.Spec.ClusterIP = "1.2.3.5"
  11762. },
  11763. numErrs: 1,
  11764. },
  11765. {
  11766. name: "Service with NodePort type can change its empty ClusterIP when changing type to LoadBalancer",
  11767. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11768. oldSvc.Spec.Type = core.ServiceTypeNodePort
  11769. newSvc.Spec.Type = core.ServiceTypeLoadBalancer
  11770. oldSvc.Spec.ClusterIP = ""
  11771. newSvc.Spec.ClusterIP = "1.2.3.5"
  11772. },
  11773. numErrs: 0,
  11774. },
  11775. {
  11776. name: "Service with LoadBalancer type cannot change its set ClusterIP",
  11777. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11778. oldSvc.Spec.Type = core.ServiceTypeLoadBalancer
  11779. newSvc.Spec.Type = core.ServiceTypeLoadBalancer
  11780. oldSvc.Spec.ClusterIP = "1.2.3.4"
  11781. newSvc.Spec.ClusterIP = "1.2.3.5"
  11782. },
  11783. numErrs: 1,
  11784. },
  11785. {
  11786. name: "Service with LoadBalancer type can change its empty ClusterIP",
  11787. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11788. oldSvc.Spec.Type = core.ServiceTypeLoadBalancer
  11789. newSvc.Spec.Type = core.ServiceTypeLoadBalancer
  11790. oldSvc.Spec.ClusterIP = ""
  11791. newSvc.Spec.ClusterIP = "1.2.3.5"
  11792. },
  11793. numErrs: 0,
  11794. },
  11795. {
  11796. name: "Service with LoadBalancer type cannot change its set ClusterIP when changing type to ClusterIP",
  11797. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11798. oldSvc.Spec.Type = core.ServiceTypeLoadBalancer
  11799. newSvc.Spec.Type = core.ServiceTypeClusterIP
  11800. oldSvc.Spec.ClusterIP = "1.2.3.4"
  11801. newSvc.Spec.ClusterIP = "1.2.3.5"
  11802. },
  11803. numErrs: 1,
  11804. },
  11805. {
  11806. name: "Service with LoadBalancer type can change its empty ClusterIP when changing type to ClusterIP",
  11807. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11808. oldSvc.Spec.Type = core.ServiceTypeLoadBalancer
  11809. newSvc.Spec.Type = core.ServiceTypeClusterIP
  11810. oldSvc.Spec.ClusterIP = ""
  11811. newSvc.Spec.ClusterIP = "1.2.3.5"
  11812. },
  11813. numErrs: 0,
  11814. },
  11815. {
  11816. name: "Service with LoadBalancer type cannot change its set ClusterIP when changing type to NodePort",
  11817. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11818. oldSvc.Spec.Type = core.ServiceTypeLoadBalancer
  11819. newSvc.Spec.Type = core.ServiceTypeNodePort
  11820. oldSvc.Spec.ClusterIP = "1.2.3.4"
  11821. newSvc.Spec.ClusterIP = "1.2.3.5"
  11822. },
  11823. numErrs: 1,
  11824. },
  11825. {
  11826. name: "Service with LoadBalancer type can change its empty ClusterIP when changing type to NodePort",
  11827. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11828. oldSvc.Spec.Type = core.ServiceTypeLoadBalancer
  11829. newSvc.Spec.Type = core.ServiceTypeNodePort
  11830. oldSvc.Spec.ClusterIP = ""
  11831. newSvc.Spec.ClusterIP = "1.2.3.5"
  11832. },
  11833. numErrs: 0,
  11834. },
  11835. {
  11836. name: "Service with ExternalName type can change its empty ClusterIP when changing type to ClusterIP",
  11837. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11838. oldSvc.Spec.Type = core.ServiceTypeExternalName
  11839. newSvc.Spec.Type = core.ServiceTypeClusterIP
  11840. oldSvc.Spec.ClusterIP = ""
  11841. newSvc.Spec.ClusterIP = "1.2.3.5"
  11842. },
  11843. numErrs: 0,
  11844. },
  11845. {
  11846. name: "Service with ExternalName type can change its set ClusterIP when changing type to ClusterIP",
  11847. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11848. oldSvc.Spec.Type = core.ServiceTypeExternalName
  11849. newSvc.Spec.Type = core.ServiceTypeClusterIP
  11850. oldSvc.Spec.ClusterIP = "1.2.3.4"
  11851. newSvc.Spec.ClusterIP = "1.2.3.5"
  11852. },
  11853. numErrs: 0,
  11854. },
  11855. {
  11856. name: "invalid node port with clusterIP None",
  11857. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11858. oldSvc.Spec.Type = core.ServiceTypeNodePort
  11859. newSvc.Spec.Type = core.ServiceTypeNodePort
  11860. oldSvc.Spec.Ports = append(oldSvc.Spec.Ports, core.ServicePort{Name: "q", Port: 1, Protocol: "TCP", NodePort: 1, TargetPort: intstr.FromInt(1)})
  11861. newSvc.Spec.Ports = append(newSvc.Spec.Ports, core.ServicePort{Name: "q", Port: 1, Protocol: "TCP", NodePort: 1, TargetPort: intstr.FromInt(1)})
  11862. oldSvc.Spec.ClusterIP = ""
  11863. newSvc.Spec.ClusterIP = "None"
  11864. },
  11865. numErrs: 1,
  11866. },
  11867. /* Service IP Family */
  11868. {
  11869. name: "same ServiceIPFamily",
  11870. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11871. ipv4Service := core.IPv4Protocol
  11872. oldSvc.Spec.Type = core.ServiceTypeClusterIP
  11873. oldSvc.Spec.IPFamily = &ipv4Service
  11874. newSvc.Spec.Type = core.ServiceTypeClusterIP
  11875. newSvc.Spec.IPFamily = &ipv4Service
  11876. },
  11877. numErrs: 0,
  11878. },
  11879. {
  11880. name: "ExternalName while changing Service IPFamily",
  11881. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11882. ipv4Service := core.IPv4Protocol
  11883. oldSvc.Spec.ExternalName = "somename"
  11884. oldSvc.Spec.Type = core.ServiceTypeExternalName
  11885. oldSvc.Spec.IPFamily = &ipv4Service
  11886. ipv6Service := core.IPv6Protocol
  11887. newSvc.Spec.ExternalName = "somename"
  11888. newSvc.Spec.Type = core.ServiceTypeExternalName
  11889. newSvc.Spec.IPFamily = &ipv6Service
  11890. },
  11891. numErrs: 0,
  11892. },
  11893. {
  11894. name: "setting ipfamily from nil to v4",
  11895. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11896. oldSvc.Spec.IPFamily = nil
  11897. ipv4Service := core.IPv4Protocol
  11898. newSvc.Spec.ExternalName = "somename"
  11899. newSvc.Spec.IPFamily = &ipv4Service
  11900. },
  11901. numErrs: 0,
  11902. },
  11903. {
  11904. name: "setting ipfamily from nil to v6",
  11905. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11906. oldSvc.Spec.IPFamily = nil
  11907. ipv6Service := core.IPv6Protocol
  11908. newSvc.Spec.ExternalName = "somename"
  11909. newSvc.Spec.IPFamily = &ipv6Service
  11910. },
  11911. numErrs: 0,
  11912. },
  11913. {
  11914. name: "remove ipfamily",
  11915. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11916. ipv6Service := core.IPv6Protocol
  11917. oldSvc.Spec.IPFamily = &ipv6Service
  11918. newSvc.Spec.IPFamily = nil
  11919. },
  11920. numErrs: 1,
  11921. },
  11922. {
  11923. name: "change ServiceIPFamily",
  11924. tweakSvc: func(oldSvc, newSvc *core.Service) {
  11925. ipv4Service := core.IPv4Protocol
  11926. oldSvc.Spec.Type = core.ServiceTypeClusterIP
  11927. oldSvc.Spec.IPFamily = &ipv4Service
  11928. ipv6Service := core.IPv6Protocol
  11929. newSvc.Spec.Type = core.ServiceTypeClusterIP
  11930. newSvc.Spec.IPFamily = &ipv6Service
  11931. },
  11932. numErrs: 1,
  11933. },
  11934. }
  11935. for _, tc := range testCases {
  11936. oldSvc := makeValidService()
  11937. newSvc := makeValidService()
  11938. tc.tweakSvc(&oldSvc, &newSvc)
  11939. errs := ValidateServiceUpdate(&newSvc, &oldSvc)
  11940. if len(errs) != tc.numErrs {
  11941. t.Errorf("Unexpected error list for case %q: %v", tc.name, errs.ToAggregate())
  11942. }
  11943. }
  11944. }
  11945. func TestValidateResourceNames(t *testing.T) {
  11946. table := []struct {
  11947. input string
  11948. success bool
  11949. expect string
  11950. }{
  11951. {"memory", true, ""},
  11952. {"cpu", true, ""},
  11953. {"storage", true, ""},
  11954. {"requests.cpu", true, ""},
  11955. {"requests.memory", true, ""},
  11956. {"requests.storage", true, ""},
  11957. {"limits.cpu", true, ""},
  11958. {"limits.memory", true, ""},
  11959. {"network", false, ""},
  11960. {"disk", false, ""},
  11961. {"", false, ""},
  11962. {".", false, ""},
  11963. {"..", false, ""},
  11964. {"my.favorite.app.co/12345", true, ""},
  11965. {"my.favorite.app.co/_12345", false, ""},
  11966. {"my.favorite.app.co/12345_", false, ""},
  11967. {"kubernetes.io/..", false, ""},
  11968. {"kubernetes.io/" + strings.Repeat("a", 63), true, ""},
  11969. {"kubernetes.io/" + strings.Repeat("a", 64), false, ""},
  11970. {"kubernetes.io//", false, ""},
  11971. {"kubernetes.io", false, ""},
  11972. {"kubernetes.io/will/not/work/", false, ""},
  11973. }
  11974. for k, item := range table {
  11975. err := validateResourceName(item.input, field.NewPath("field"))
  11976. if len(err) != 0 && item.success {
  11977. t.Errorf("expected no failure for input %q", item.input)
  11978. } else if len(err) == 0 && !item.success {
  11979. t.Errorf("expected failure for input %q", item.input)
  11980. for i := range err {
  11981. detail := err[i].Detail
  11982. if detail != "" && !strings.Contains(detail, item.expect) {
  11983. t.Errorf("%d: expected error detail either empty or %s, got %s", k, item.expect, detail)
  11984. }
  11985. }
  11986. }
  11987. }
  11988. }
  11989. func getResourceList(cpu, memory string) core.ResourceList {
  11990. res := core.ResourceList{}
  11991. if cpu != "" {
  11992. res[core.ResourceCPU] = resource.MustParse(cpu)
  11993. }
  11994. if memory != "" {
  11995. res[core.ResourceMemory] = resource.MustParse(memory)
  11996. }
  11997. return res
  11998. }
  11999. func getStorageResourceList(storage string) core.ResourceList {
  12000. res := core.ResourceList{}
  12001. if storage != "" {
  12002. res[core.ResourceStorage] = resource.MustParse(storage)
  12003. }
  12004. return res
  12005. }
  12006. func getLocalStorageResourceList(ephemeralStorage string) core.ResourceList {
  12007. res := core.ResourceList{}
  12008. if ephemeralStorage != "" {
  12009. res[core.ResourceEphemeralStorage] = resource.MustParse(ephemeralStorage)
  12010. }
  12011. return res
  12012. }
  12013. func TestValidateLimitRangeForLocalStorage(t *testing.T) {
  12014. testCases := []struct {
  12015. name string
  12016. spec core.LimitRangeSpec
  12017. }{
  12018. {
  12019. name: "all-fields-valid",
  12020. spec: core.LimitRangeSpec{
  12021. Limits: []core.LimitRangeItem{
  12022. {
  12023. Type: core.LimitTypePod,
  12024. Max: getLocalStorageResourceList("10000Mi"),
  12025. Min: getLocalStorageResourceList("100Mi"),
  12026. MaxLimitRequestRatio: getLocalStorageResourceList(""),
  12027. },
  12028. {
  12029. Type: core.LimitTypeContainer,
  12030. Max: getLocalStorageResourceList("10000Mi"),
  12031. Min: getLocalStorageResourceList("100Mi"),
  12032. Default: getLocalStorageResourceList("500Mi"),
  12033. DefaultRequest: getLocalStorageResourceList("200Mi"),
  12034. MaxLimitRequestRatio: getLocalStorageResourceList(""),
  12035. },
  12036. },
  12037. },
  12038. },
  12039. }
  12040. for _, testCase := range testCases {
  12041. limitRange := &core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: testCase.name, Namespace: "foo"}, Spec: testCase.spec}
  12042. if errs := ValidateLimitRange(limitRange); len(errs) != 0 {
  12043. t.Errorf("Case %v, unexpected error: %v", testCase.name, errs)
  12044. }
  12045. }
  12046. }
  12047. func TestValidateLimitRange(t *testing.T) {
  12048. successCases := []struct {
  12049. name string
  12050. spec core.LimitRangeSpec
  12051. }{
  12052. {
  12053. name: "all-fields-valid",
  12054. spec: core.LimitRangeSpec{
  12055. Limits: []core.LimitRangeItem{
  12056. {
  12057. Type: core.LimitTypePod,
  12058. Max: getResourceList("100m", "10000Mi"),
  12059. Min: getResourceList("5m", "100Mi"),
  12060. MaxLimitRequestRatio: getResourceList("10", ""),
  12061. },
  12062. {
  12063. Type: core.LimitTypeContainer,
  12064. Max: getResourceList("100m", "10000Mi"),
  12065. Min: getResourceList("5m", "100Mi"),
  12066. Default: getResourceList("50m", "500Mi"),
  12067. DefaultRequest: getResourceList("10m", "200Mi"),
  12068. MaxLimitRequestRatio: getResourceList("10", ""),
  12069. },
  12070. {
  12071. Type: core.LimitTypePersistentVolumeClaim,
  12072. Max: getStorageResourceList("10Gi"),
  12073. Min: getStorageResourceList("5Gi"),
  12074. },
  12075. },
  12076. },
  12077. },
  12078. {
  12079. name: "pvc-min-only",
  12080. spec: core.LimitRangeSpec{
  12081. Limits: []core.LimitRangeItem{
  12082. {
  12083. Type: core.LimitTypePersistentVolumeClaim,
  12084. Min: getStorageResourceList("5Gi"),
  12085. },
  12086. },
  12087. },
  12088. },
  12089. {
  12090. name: "pvc-max-only",
  12091. spec: core.LimitRangeSpec{
  12092. Limits: []core.LimitRangeItem{
  12093. {
  12094. Type: core.LimitTypePersistentVolumeClaim,
  12095. Max: getStorageResourceList("10Gi"),
  12096. },
  12097. },
  12098. },
  12099. },
  12100. {
  12101. name: "all-fields-valid-big-numbers",
  12102. spec: core.LimitRangeSpec{
  12103. Limits: []core.LimitRangeItem{
  12104. {
  12105. Type: core.LimitTypeContainer,
  12106. Max: getResourceList("100m", "10000T"),
  12107. Min: getResourceList("5m", "100Mi"),
  12108. Default: getResourceList("50m", "500Mi"),
  12109. DefaultRequest: getResourceList("10m", "200Mi"),
  12110. MaxLimitRequestRatio: getResourceList("10", ""),
  12111. },
  12112. },
  12113. },
  12114. },
  12115. {
  12116. name: "thirdparty-fields-all-valid-standard-container-resources",
  12117. spec: core.LimitRangeSpec{
  12118. Limits: []core.LimitRangeItem{
  12119. {
  12120. Type: "thirdparty.com/foo",
  12121. Max: getResourceList("100m", "10000T"),
  12122. Min: getResourceList("5m", "100Mi"),
  12123. Default: getResourceList("50m", "500Mi"),
  12124. DefaultRequest: getResourceList("10m", "200Mi"),
  12125. MaxLimitRequestRatio: getResourceList("10", ""),
  12126. },
  12127. },
  12128. },
  12129. },
  12130. {
  12131. name: "thirdparty-fields-all-valid-storage-resources",
  12132. spec: core.LimitRangeSpec{
  12133. Limits: []core.LimitRangeItem{
  12134. {
  12135. Type: "thirdparty.com/foo",
  12136. Max: getStorageResourceList("10000T"),
  12137. Min: getStorageResourceList("100Mi"),
  12138. Default: getStorageResourceList("500Mi"),
  12139. DefaultRequest: getStorageResourceList("200Mi"),
  12140. MaxLimitRequestRatio: getStorageResourceList(""),
  12141. },
  12142. },
  12143. },
  12144. },
  12145. }
  12146. for _, successCase := range successCases {
  12147. limitRange := &core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: successCase.name, Namespace: "foo"}, Spec: successCase.spec}
  12148. if errs := ValidateLimitRange(limitRange); len(errs) != 0 {
  12149. t.Errorf("Case %v, unexpected error: %v", successCase.name, errs)
  12150. }
  12151. }
  12152. errorCases := map[string]struct {
  12153. R core.LimitRange
  12154. D string
  12155. }{
  12156. "zero-length-name": {
  12157. core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "", Namespace: "foo"}, Spec: core.LimitRangeSpec{}},
  12158. "name or generateName is required",
  12159. },
  12160. "zero-length-namespace": {
  12161. core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: ""}, Spec: core.LimitRangeSpec{}},
  12162. "",
  12163. },
  12164. "invalid-name": {
  12165. core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "^Invalid", Namespace: "foo"}, Spec: core.LimitRangeSpec{}},
  12166. dnsSubdomainLabelErrMsg,
  12167. },
  12168. "invalid-namespace": {
  12169. core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "^Invalid"}, Spec: core.LimitRangeSpec{}},
  12170. dnsLabelErrMsg,
  12171. },
  12172. "duplicate-limit-type": {
  12173. core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: core.LimitRangeSpec{
  12174. Limits: []core.LimitRangeItem{
  12175. {
  12176. Type: core.LimitTypePod,
  12177. Max: getResourceList("100m", "10000m"),
  12178. Min: getResourceList("0m", "100m"),
  12179. },
  12180. {
  12181. Type: core.LimitTypePod,
  12182. Min: getResourceList("0m", "100m"),
  12183. },
  12184. },
  12185. }},
  12186. "",
  12187. },
  12188. "default-limit-type-pod": {
  12189. core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: core.LimitRangeSpec{
  12190. Limits: []core.LimitRangeItem{
  12191. {
  12192. Type: core.LimitTypePod,
  12193. Max: getResourceList("100m", "10000m"),
  12194. Min: getResourceList("0m", "100m"),
  12195. Default: getResourceList("10m", "100m"),
  12196. },
  12197. },
  12198. }},
  12199. "may not be specified when `type` is 'Pod'",
  12200. },
  12201. "default-request-limit-type-pod": {
  12202. core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: core.LimitRangeSpec{
  12203. Limits: []core.LimitRangeItem{
  12204. {
  12205. Type: core.LimitTypePod,
  12206. Max: getResourceList("100m", "10000m"),
  12207. Min: getResourceList("0m", "100m"),
  12208. DefaultRequest: getResourceList("10m", "100m"),
  12209. },
  12210. },
  12211. }},
  12212. "may not be specified when `type` is 'Pod'",
  12213. },
  12214. "min value 100m is greater than max value 10m": {
  12215. core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: core.LimitRangeSpec{
  12216. Limits: []core.LimitRangeItem{
  12217. {
  12218. Type: core.LimitTypePod,
  12219. Max: getResourceList("10m", ""),
  12220. Min: getResourceList("100m", ""),
  12221. },
  12222. },
  12223. }},
  12224. "min value 100m is greater than max value 10m",
  12225. },
  12226. "invalid spec default outside range": {
  12227. core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: core.LimitRangeSpec{
  12228. Limits: []core.LimitRangeItem{
  12229. {
  12230. Type: core.LimitTypeContainer,
  12231. Max: getResourceList("1", ""),
  12232. Min: getResourceList("100m", ""),
  12233. Default: getResourceList("2000m", ""),
  12234. },
  12235. },
  12236. }},
  12237. "default value 2 is greater than max value 1",
  12238. },
  12239. "invalid spec default request outside range": {
  12240. core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: core.LimitRangeSpec{
  12241. Limits: []core.LimitRangeItem{
  12242. {
  12243. Type: core.LimitTypeContainer,
  12244. Max: getResourceList("1", ""),
  12245. Min: getResourceList("100m", ""),
  12246. DefaultRequest: getResourceList("2000m", ""),
  12247. },
  12248. },
  12249. }},
  12250. "default request value 2 is greater than max value 1",
  12251. },
  12252. "invalid spec default request more than default": {
  12253. core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: core.LimitRangeSpec{
  12254. Limits: []core.LimitRangeItem{
  12255. {
  12256. Type: core.LimitTypeContainer,
  12257. Max: getResourceList("2", ""),
  12258. Min: getResourceList("100m", ""),
  12259. Default: getResourceList("500m", ""),
  12260. DefaultRequest: getResourceList("800m", ""),
  12261. },
  12262. },
  12263. }},
  12264. "default request value 800m is greater than default limit value 500m",
  12265. },
  12266. "invalid spec maxLimitRequestRatio less than 1": {
  12267. core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: core.LimitRangeSpec{
  12268. Limits: []core.LimitRangeItem{
  12269. {
  12270. Type: core.LimitTypePod,
  12271. MaxLimitRequestRatio: getResourceList("800m", ""),
  12272. },
  12273. },
  12274. }},
  12275. "ratio 800m is less than 1",
  12276. },
  12277. "invalid spec maxLimitRequestRatio greater than max/min": {
  12278. core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: core.LimitRangeSpec{
  12279. Limits: []core.LimitRangeItem{
  12280. {
  12281. Type: core.LimitTypeContainer,
  12282. Max: getResourceList("", "2Gi"),
  12283. Min: getResourceList("", "512Mi"),
  12284. MaxLimitRequestRatio: getResourceList("", "10"),
  12285. },
  12286. },
  12287. }},
  12288. "ratio 10 is greater than max/min = 4.000000",
  12289. },
  12290. "invalid non standard limit type": {
  12291. core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: core.LimitRangeSpec{
  12292. Limits: []core.LimitRangeItem{
  12293. {
  12294. Type: "foo",
  12295. Max: getStorageResourceList("10000T"),
  12296. Min: getStorageResourceList("100Mi"),
  12297. Default: getStorageResourceList("500Mi"),
  12298. DefaultRequest: getStorageResourceList("200Mi"),
  12299. MaxLimitRequestRatio: getStorageResourceList(""),
  12300. },
  12301. },
  12302. }},
  12303. "must be a standard limit type or fully qualified",
  12304. },
  12305. "min and max values missing, one required": {
  12306. core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: core.LimitRangeSpec{
  12307. Limits: []core.LimitRangeItem{
  12308. {
  12309. Type: core.LimitTypePersistentVolumeClaim,
  12310. },
  12311. },
  12312. }},
  12313. "either minimum or maximum storage value is required, but neither was provided",
  12314. },
  12315. "invalid min greater than max": {
  12316. core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: core.LimitRangeSpec{
  12317. Limits: []core.LimitRangeItem{
  12318. {
  12319. Type: core.LimitTypePersistentVolumeClaim,
  12320. Min: getStorageResourceList("10Gi"),
  12321. Max: getStorageResourceList("1Gi"),
  12322. },
  12323. },
  12324. }},
  12325. "min value 10Gi is greater than max value 1Gi",
  12326. },
  12327. }
  12328. for k, v := range errorCases {
  12329. errs := ValidateLimitRange(&v.R)
  12330. if len(errs) == 0 {
  12331. t.Errorf("expected failure for %s", k)
  12332. }
  12333. for i := range errs {
  12334. detail := errs[i].Detail
  12335. if !strings.Contains(detail, v.D) {
  12336. t.Errorf("[%s]: expected error detail either empty or %q, got %q", k, v.D, detail)
  12337. }
  12338. }
  12339. }
  12340. }
  12341. func TestValidatePersistentVolumeClaimStatusUpdate(t *testing.T) {
  12342. validClaim := testVolumeClaim("foo", "ns", core.PersistentVolumeClaimSpec{
  12343. AccessModes: []core.PersistentVolumeAccessMode{
  12344. core.ReadWriteOnce,
  12345. core.ReadOnlyMany,
  12346. },
  12347. Resources: core.ResourceRequirements{
  12348. Requests: core.ResourceList{
  12349. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  12350. },
  12351. },
  12352. })
  12353. validConditionUpdate := testVolumeClaimWithStatus("foo", "ns", core.PersistentVolumeClaimSpec{
  12354. AccessModes: []core.PersistentVolumeAccessMode{
  12355. core.ReadWriteOnce,
  12356. core.ReadOnlyMany,
  12357. },
  12358. Resources: core.ResourceRequirements{
  12359. Requests: core.ResourceList{
  12360. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  12361. },
  12362. },
  12363. }, core.PersistentVolumeClaimStatus{
  12364. Phase: core.ClaimPending,
  12365. Conditions: []core.PersistentVolumeClaimCondition{
  12366. {Type: core.PersistentVolumeClaimResizing, Status: core.ConditionTrue},
  12367. },
  12368. })
  12369. scenarios := map[string]struct {
  12370. isExpectedFailure bool
  12371. oldClaim *core.PersistentVolumeClaim
  12372. newClaim *core.PersistentVolumeClaim
  12373. enableResize bool
  12374. }{
  12375. "condition-update-with-enabled-feature-gate": {
  12376. isExpectedFailure: false,
  12377. oldClaim: validClaim,
  12378. newClaim: validConditionUpdate,
  12379. enableResize: true,
  12380. },
  12381. }
  12382. for name, scenario := range scenarios {
  12383. t.Run(name, func(t *testing.T) {
  12384. // ensure we have a resource version specified for updates
  12385. scenario.oldClaim.ResourceVersion = "1"
  12386. scenario.newClaim.ResourceVersion = "1"
  12387. errs := ValidatePersistentVolumeClaimStatusUpdate(scenario.newClaim, scenario.oldClaim)
  12388. if len(errs) == 0 && scenario.isExpectedFailure {
  12389. t.Errorf("Unexpected success for scenario: %s", name)
  12390. }
  12391. if len(errs) > 0 && !scenario.isExpectedFailure {
  12392. t.Errorf("Unexpected failure for scenario: %s - %+v", name, errs)
  12393. }
  12394. })
  12395. }
  12396. }
  12397. func TestValidateResourceQuota(t *testing.T) {
  12398. spec := core.ResourceQuotaSpec{
  12399. Hard: core.ResourceList{
  12400. core.ResourceCPU: resource.MustParse("100"),
  12401. core.ResourceMemory: resource.MustParse("10000"),
  12402. core.ResourceRequestsCPU: resource.MustParse("100"),
  12403. core.ResourceRequestsMemory: resource.MustParse("10000"),
  12404. core.ResourceLimitsCPU: resource.MustParse("100"),
  12405. core.ResourceLimitsMemory: resource.MustParse("10000"),
  12406. core.ResourcePods: resource.MustParse("10"),
  12407. core.ResourceServices: resource.MustParse("0"),
  12408. core.ResourceReplicationControllers: resource.MustParse("10"),
  12409. core.ResourceQuotas: resource.MustParse("10"),
  12410. core.ResourceConfigMaps: resource.MustParse("10"),
  12411. core.ResourceSecrets: resource.MustParse("10"),
  12412. },
  12413. }
  12414. terminatingSpec := core.ResourceQuotaSpec{
  12415. Hard: core.ResourceList{
  12416. core.ResourceCPU: resource.MustParse("100"),
  12417. core.ResourceLimitsCPU: resource.MustParse("200"),
  12418. },
  12419. Scopes: []core.ResourceQuotaScope{core.ResourceQuotaScopeTerminating},
  12420. }
  12421. nonTerminatingSpec := core.ResourceQuotaSpec{
  12422. Hard: core.ResourceList{
  12423. core.ResourceCPU: resource.MustParse("100"),
  12424. },
  12425. Scopes: []core.ResourceQuotaScope{core.ResourceQuotaScopeNotTerminating},
  12426. }
  12427. bestEffortSpec := core.ResourceQuotaSpec{
  12428. Hard: core.ResourceList{
  12429. core.ResourcePods: resource.MustParse("100"),
  12430. },
  12431. Scopes: []core.ResourceQuotaScope{core.ResourceQuotaScopeBestEffort},
  12432. }
  12433. nonBestEffortSpec := core.ResourceQuotaSpec{
  12434. Hard: core.ResourceList{
  12435. core.ResourceCPU: resource.MustParse("100"),
  12436. },
  12437. Scopes: []core.ResourceQuotaScope{core.ResourceQuotaScopeNotBestEffort},
  12438. }
  12439. scopeSelectorSpec := core.ResourceQuotaSpec{
  12440. ScopeSelector: &core.ScopeSelector{
  12441. MatchExpressions: []core.ScopedResourceSelectorRequirement{
  12442. {
  12443. ScopeName: core.ResourceQuotaScopePriorityClass,
  12444. Operator: core.ScopeSelectorOpIn,
  12445. Values: []string{"cluster-services"},
  12446. },
  12447. },
  12448. },
  12449. }
  12450. // storage is not yet supported as a quota tracked resource
  12451. invalidQuotaResourceSpec := core.ResourceQuotaSpec{
  12452. Hard: core.ResourceList{
  12453. core.ResourceStorage: resource.MustParse("10"),
  12454. },
  12455. }
  12456. negativeSpec := core.ResourceQuotaSpec{
  12457. Hard: core.ResourceList{
  12458. core.ResourceCPU: resource.MustParse("-100"),
  12459. core.ResourceMemory: resource.MustParse("-10000"),
  12460. core.ResourcePods: resource.MustParse("-10"),
  12461. core.ResourceServices: resource.MustParse("-10"),
  12462. core.ResourceReplicationControllers: resource.MustParse("-10"),
  12463. core.ResourceQuotas: resource.MustParse("-10"),
  12464. core.ResourceConfigMaps: resource.MustParse("-10"),
  12465. core.ResourceSecrets: resource.MustParse("-10"),
  12466. },
  12467. }
  12468. fractionalComputeSpec := core.ResourceQuotaSpec{
  12469. Hard: core.ResourceList{
  12470. core.ResourceCPU: resource.MustParse("100m"),
  12471. },
  12472. }
  12473. fractionalPodSpec := core.ResourceQuotaSpec{
  12474. Hard: core.ResourceList{
  12475. core.ResourcePods: resource.MustParse(".1"),
  12476. core.ResourceServices: resource.MustParse(".5"),
  12477. core.ResourceReplicationControllers: resource.MustParse("1.25"),
  12478. core.ResourceQuotas: resource.MustParse("2.5"),
  12479. },
  12480. }
  12481. invalidTerminatingScopePairsSpec := core.ResourceQuotaSpec{
  12482. Hard: core.ResourceList{
  12483. core.ResourceCPU: resource.MustParse("100"),
  12484. },
  12485. Scopes: []core.ResourceQuotaScope{core.ResourceQuotaScopeTerminating, core.ResourceQuotaScopeNotTerminating},
  12486. }
  12487. invalidBestEffortScopePairsSpec := core.ResourceQuotaSpec{
  12488. Hard: core.ResourceList{
  12489. core.ResourcePods: resource.MustParse("100"),
  12490. },
  12491. Scopes: []core.ResourceQuotaScope{core.ResourceQuotaScopeBestEffort, core.ResourceQuotaScopeNotBestEffort},
  12492. }
  12493. invalidScopeNameSpec := core.ResourceQuotaSpec{
  12494. Hard: core.ResourceList{
  12495. core.ResourceCPU: resource.MustParse("100"),
  12496. },
  12497. Scopes: []core.ResourceQuotaScope{core.ResourceQuotaScope("foo")},
  12498. }
  12499. successCases := []core.ResourceQuota{
  12500. {
  12501. ObjectMeta: metav1.ObjectMeta{
  12502. Name: "abc",
  12503. Namespace: "foo",
  12504. },
  12505. Spec: spec,
  12506. },
  12507. {
  12508. ObjectMeta: metav1.ObjectMeta{
  12509. Name: "abc",
  12510. Namespace: "foo",
  12511. },
  12512. Spec: fractionalComputeSpec,
  12513. },
  12514. {
  12515. ObjectMeta: metav1.ObjectMeta{
  12516. Name: "abc",
  12517. Namespace: "foo",
  12518. },
  12519. Spec: terminatingSpec,
  12520. },
  12521. {
  12522. ObjectMeta: metav1.ObjectMeta{
  12523. Name: "abc",
  12524. Namespace: "foo",
  12525. },
  12526. Spec: nonTerminatingSpec,
  12527. },
  12528. {
  12529. ObjectMeta: metav1.ObjectMeta{
  12530. Name: "abc",
  12531. Namespace: "foo",
  12532. },
  12533. Spec: bestEffortSpec,
  12534. },
  12535. {
  12536. ObjectMeta: metav1.ObjectMeta{
  12537. Name: "abc",
  12538. Namespace: "foo",
  12539. },
  12540. Spec: scopeSelectorSpec,
  12541. },
  12542. {
  12543. ObjectMeta: metav1.ObjectMeta{
  12544. Name: "abc",
  12545. Namespace: "foo",
  12546. },
  12547. Spec: nonBestEffortSpec,
  12548. },
  12549. }
  12550. for _, successCase := range successCases {
  12551. if errs := ValidateResourceQuota(&successCase); len(errs) != 0 {
  12552. t.Errorf("expected success: %v", errs)
  12553. }
  12554. }
  12555. errorCases := map[string]struct {
  12556. R core.ResourceQuota
  12557. D string
  12558. }{
  12559. "zero-length Name": {
  12560. core.ResourceQuota{ObjectMeta: metav1.ObjectMeta{Name: "", Namespace: "foo"}, Spec: spec},
  12561. "name or generateName is required",
  12562. },
  12563. "zero-length Namespace": {
  12564. core.ResourceQuota{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: ""}, Spec: spec},
  12565. "",
  12566. },
  12567. "invalid Name": {
  12568. core.ResourceQuota{ObjectMeta: metav1.ObjectMeta{Name: "^Invalid", Namespace: "foo"}, Spec: spec},
  12569. dnsSubdomainLabelErrMsg,
  12570. },
  12571. "invalid Namespace": {
  12572. core.ResourceQuota{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "^Invalid"}, Spec: spec},
  12573. dnsLabelErrMsg,
  12574. },
  12575. "negative-limits": {
  12576. core.ResourceQuota{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: negativeSpec},
  12577. isNegativeErrorMsg,
  12578. },
  12579. "fractional-api-resource": {
  12580. core.ResourceQuota{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: fractionalPodSpec},
  12581. isNotIntegerErrorMsg,
  12582. },
  12583. "invalid-quota-resource": {
  12584. core.ResourceQuota{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: invalidQuotaResourceSpec},
  12585. isInvalidQuotaResource,
  12586. },
  12587. "invalid-quota-terminating-pair": {
  12588. core.ResourceQuota{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: invalidTerminatingScopePairsSpec},
  12589. "conflicting scopes",
  12590. },
  12591. "invalid-quota-besteffort-pair": {
  12592. core.ResourceQuota{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: invalidBestEffortScopePairsSpec},
  12593. "conflicting scopes",
  12594. },
  12595. "invalid-quota-scope-name": {
  12596. core.ResourceQuota{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: invalidScopeNameSpec},
  12597. "unsupported scope",
  12598. },
  12599. }
  12600. for k, v := range errorCases {
  12601. errs := ValidateResourceQuota(&v.R)
  12602. if len(errs) == 0 {
  12603. t.Errorf("expected failure for %s", k)
  12604. }
  12605. for i := range errs {
  12606. if !strings.Contains(errs[i].Detail, v.D) {
  12607. t.Errorf("[%s]: expected error detail either empty or %s, got %s", k, v.D, errs[i].Detail)
  12608. }
  12609. }
  12610. }
  12611. }
  12612. func TestValidateNamespace(t *testing.T) {
  12613. validLabels := map[string]string{"a": "b"}
  12614. invalidLabels := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
  12615. successCases := []core.Namespace{
  12616. {
  12617. ObjectMeta: metav1.ObjectMeta{Name: "abc", Labels: validLabels},
  12618. },
  12619. {
  12620. ObjectMeta: metav1.ObjectMeta{Name: "abc-123"},
  12621. Spec: core.NamespaceSpec{
  12622. Finalizers: []core.FinalizerName{"example.com/something", "example.com/other"},
  12623. },
  12624. },
  12625. }
  12626. for _, successCase := range successCases {
  12627. if errs := ValidateNamespace(&successCase); len(errs) != 0 {
  12628. t.Errorf("expected success: %v", errs)
  12629. }
  12630. }
  12631. errorCases := map[string]struct {
  12632. R core.Namespace
  12633. D string
  12634. }{
  12635. "zero-length name": {
  12636. core.Namespace{ObjectMeta: metav1.ObjectMeta{Name: ""}},
  12637. "",
  12638. },
  12639. "defined-namespace": {
  12640. core.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: "makesnosense"}},
  12641. "",
  12642. },
  12643. "invalid-labels": {
  12644. core.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "abc", Labels: invalidLabels}},
  12645. "",
  12646. },
  12647. }
  12648. for k, v := range errorCases {
  12649. errs := ValidateNamespace(&v.R)
  12650. if len(errs) == 0 {
  12651. t.Errorf("expected failure for %s", k)
  12652. }
  12653. }
  12654. }
  12655. func TestValidateNamespaceFinalizeUpdate(t *testing.T) {
  12656. tests := []struct {
  12657. oldNamespace core.Namespace
  12658. namespace core.Namespace
  12659. valid bool
  12660. }{
  12661. {core.Namespace{}, core.Namespace{}, true},
  12662. {core.Namespace{
  12663. ObjectMeta: metav1.ObjectMeta{
  12664. Name: "foo"}},
  12665. core.Namespace{
  12666. ObjectMeta: metav1.ObjectMeta{
  12667. Name: "foo"},
  12668. Spec: core.NamespaceSpec{
  12669. Finalizers: []core.FinalizerName{"Foo"},
  12670. },
  12671. }, false},
  12672. {core.Namespace{
  12673. ObjectMeta: metav1.ObjectMeta{
  12674. Name: "foo"},
  12675. Spec: core.NamespaceSpec{
  12676. Finalizers: []core.FinalizerName{"foo.com/bar"},
  12677. },
  12678. },
  12679. core.Namespace{
  12680. ObjectMeta: metav1.ObjectMeta{
  12681. Name: "foo"},
  12682. Spec: core.NamespaceSpec{
  12683. Finalizers: []core.FinalizerName{"foo.com/bar", "what.com/bar"},
  12684. },
  12685. }, true},
  12686. {core.Namespace{
  12687. ObjectMeta: metav1.ObjectMeta{
  12688. Name: "fooemptyfinalizer"},
  12689. Spec: core.NamespaceSpec{
  12690. Finalizers: []core.FinalizerName{"foo.com/bar"},
  12691. },
  12692. },
  12693. core.Namespace{
  12694. ObjectMeta: metav1.ObjectMeta{
  12695. Name: "fooemptyfinalizer"},
  12696. Spec: core.NamespaceSpec{
  12697. Finalizers: []core.FinalizerName{"", "foo.com/bar", "what.com/bar"},
  12698. },
  12699. }, false},
  12700. }
  12701. for i, test := range tests {
  12702. test.namespace.ObjectMeta.ResourceVersion = "1"
  12703. test.oldNamespace.ObjectMeta.ResourceVersion = "1"
  12704. errs := ValidateNamespaceFinalizeUpdate(&test.namespace, &test.oldNamespace)
  12705. if test.valid && len(errs) > 0 {
  12706. t.Errorf("%d: Unexpected error: %v", i, errs)
  12707. t.Logf("%#v vs %#v", test.oldNamespace, test.namespace)
  12708. }
  12709. if !test.valid && len(errs) == 0 {
  12710. t.Errorf("%d: Unexpected non-error", i)
  12711. }
  12712. }
  12713. }
  12714. func TestValidateNamespaceStatusUpdate(t *testing.T) {
  12715. now := metav1.Now()
  12716. tests := []struct {
  12717. oldNamespace core.Namespace
  12718. namespace core.Namespace
  12719. valid bool
  12720. }{
  12721. {core.Namespace{}, core.Namespace{
  12722. Status: core.NamespaceStatus{
  12723. Phase: core.NamespaceActive,
  12724. },
  12725. }, true},
  12726. // Cannot set deletionTimestamp via status update
  12727. {core.Namespace{
  12728. ObjectMeta: metav1.ObjectMeta{
  12729. Name: "foo"}},
  12730. core.Namespace{
  12731. ObjectMeta: metav1.ObjectMeta{
  12732. Name: "foo",
  12733. DeletionTimestamp: &now},
  12734. Status: core.NamespaceStatus{
  12735. Phase: core.NamespaceTerminating,
  12736. },
  12737. }, false},
  12738. // Can update phase via status update
  12739. {core.Namespace{
  12740. ObjectMeta: metav1.ObjectMeta{
  12741. Name: "foo",
  12742. DeletionTimestamp: &now}},
  12743. core.Namespace{
  12744. ObjectMeta: metav1.ObjectMeta{
  12745. Name: "foo",
  12746. DeletionTimestamp: &now},
  12747. Status: core.NamespaceStatus{
  12748. Phase: core.NamespaceTerminating,
  12749. },
  12750. }, true},
  12751. {core.Namespace{
  12752. ObjectMeta: metav1.ObjectMeta{
  12753. Name: "foo"}},
  12754. core.Namespace{
  12755. ObjectMeta: metav1.ObjectMeta{
  12756. Name: "foo"},
  12757. Status: core.NamespaceStatus{
  12758. Phase: core.NamespaceTerminating,
  12759. },
  12760. }, false},
  12761. {core.Namespace{
  12762. ObjectMeta: metav1.ObjectMeta{
  12763. Name: "foo"}},
  12764. core.Namespace{
  12765. ObjectMeta: metav1.ObjectMeta{
  12766. Name: "bar"},
  12767. Status: core.NamespaceStatus{
  12768. Phase: core.NamespaceTerminating,
  12769. },
  12770. }, false},
  12771. }
  12772. for i, test := range tests {
  12773. test.namespace.ObjectMeta.ResourceVersion = "1"
  12774. test.oldNamespace.ObjectMeta.ResourceVersion = "1"
  12775. errs := ValidateNamespaceStatusUpdate(&test.namespace, &test.oldNamespace)
  12776. if test.valid && len(errs) > 0 {
  12777. t.Errorf("%d: Unexpected error: %v", i, errs)
  12778. t.Logf("%#v vs %#v", test.oldNamespace.ObjectMeta, test.namespace.ObjectMeta)
  12779. }
  12780. if !test.valid && len(errs) == 0 {
  12781. t.Errorf("%d: Unexpected non-error", i)
  12782. }
  12783. }
  12784. }
  12785. func TestValidateNamespaceUpdate(t *testing.T) {
  12786. tests := []struct {
  12787. oldNamespace core.Namespace
  12788. namespace core.Namespace
  12789. valid bool
  12790. }{
  12791. {core.Namespace{}, core.Namespace{}, true},
  12792. {core.Namespace{
  12793. ObjectMeta: metav1.ObjectMeta{
  12794. Name: "foo1"}},
  12795. core.Namespace{
  12796. ObjectMeta: metav1.ObjectMeta{
  12797. Name: "bar1"},
  12798. }, false},
  12799. {core.Namespace{
  12800. ObjectMeta: metav1.ObjectMeta{
  12801. Name: "foo2",
  12802. Labels: map[string]string{"foo": "bar"},
  12803. },
  12804. }, core.Namespace{
  12805. ObjectMeta: metav1.ObjectMeta{
  12806. Name: "foo2",
  12807. Labels: map[string]string{"foo": "baz"},
  12808. },
  12809. }, true},
  12810. {core.Namespace{
  12811. ObjectMeta: metav1.ObjectMeta{
  12812. Name: "foo3",
  12813. },
  12814. }, core.Namespace{
  12815. ObjectMeta: metav1.ObjectMeta{
  12816. Name: "foo3",
  12817. Labels: map[string]string{"foo": "baz"},
  12818. },
  12819. }, true},
  12820. {core.Namespace{
  12821. ObjectMeta: metav1.ObjectMeta{
  12822. Name: "foo4",
  12823. Labels: map[string]string{"bar": "foo"},
  12824. },
  12825. }, core.Namespace{
  12826. ObjectMeta: metav1.ObjectMeta{
  12827. Name: "foo4",
  12828. Labels: map[string]string{"foo": "baz"},
  12829. },
  12830. }, true},
  12831. {core.Namespace{
  12832. ObjectMeta: metav1.ObjectMeta{
  12833. Name: "foo5",
  12834. Labels: map[string]string{"foo": "baz"},
  12835. },
  12836. }, core.Namespace{
  12837. ObjectMeta: metav1.ObjectMeta{
  12838. Name: "foo5",
  12839. Labels: map[string]string{"Foo": "baz"},
  12840. },
  12841. }, true},
  12842. {core.Namespace{
  12843. ObjectMeta: metav1.ObjectMeta{
  12844. Name: "foo6",
  12845. Labels: map[string]string{"foo": "baz"},
  12846. },
  12847. }, core.Namespace{
  12848. ObjectMeta: metav1.ObjectMeta{
  12849. Name: "foo6",
  12850. Labels: map[string]string{"Foo": "baz"},
  12851. },
  12852. Spec: core.NamespaceSpec{
  12853. Finalizers: []core.FinalizerName{"kubernetes"},
  12854. },
  12855. Status: core.NamespaceStatus{
  12856. Phase: core.NamespaceTerminating,
  12857. },
  12858. }, true},
  12859. }
  12860. for i, test := range tests {
  12861. test.namespace.ObjectMeta.ResourceVersion = "1"
  12862. test.oldNamespace.ObjectMeta.ResourceVersion = "1"
  12863. errs := ValidateNamespaceUpdate(&test.namespace, &test.oldNamespace)
  12864. if test.valid && len(errs) > 0 {
  12865. t.Errorf("%d: Unexpected error: %v", i, errs)
  12866. t.Logf("%#v vs %#v", test.oldNamespace.ObjectMeta, test.namespace.ObjectMeta)
  12867. }
  12868. if !test.valid && len(errs) == 0 {
  12869. t.Errorf("%d: Unexpected non-error", i)
  12870. }
  12871. }
  12872. }
  12873. func TestValidateSecret(t *testing.T) {
  12874. // Opaque secret validation
  12875. validSecret := func() core.Secret {
  12876. return core.Secret{
  12877. ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"},
  12878. Data: map[string][]byte{
  12879. "data-1": []byte("bar"),
  12880. },
  12881. }
  12882. }
  12883. var (
  12884. emptyName = validSecret()
  12885. invalidName = validSecret()
  12886. emptyNs = validSecret()
  12887. invalidNs = validSecret()
  12888. overMaxSize = validSecret()
  12889. invalidKey = validSecret()
  12890. leadingDotKey = validSecret()
  12891. dotKey = validSecret()
  12892. doubleDotKey = validSecret()
  12893. )
  12894. emptyName.Name = ""
  12895. invalidName.Name = "NoUppercaseOrSpecialCharsLike=Equals"
  12896. emptyNs.Namespace = ""
  12897. invalidNs.Namespace = "NoUppercaseOrSpecialCharsLike=Equals"
  12898. overMaxSize.Data = map[string][]byte{
  12899. "over": make([]byte, core.MaxSecretSize+1),
  12900. }
  12901. invalidKey.Data["a*b"] = []byte("whoops")
  12902. leadingDotKey.Data[".key"] = []byte("bar")
  12903. dotKey.Data["."] = []byte("bar")
  12904. doubleDotKey.Data[".."] = []byte("bar")
  12905. // kubernetes.io/service-account-token secret validation
  12906. validServiceAccountTokenSecret := func() core.Secret {
  12907. return core.Secret{
  12908. ObjectMeta: metav1.ObjectMeta{
  12909. Name: "foo",
  12910. Namespace: "bar",
  12911. Annotations: map[string]string{
  12912. core.ServiceAccountNameKey: "foo",
  12913. },
  12914. },
  12915. Type: core.SecretTypeServiceAccountToken,
  12916. Data: map[string][]byte{
  12917. "data-1": []byte("bar"),
  12918. },
  12919. }
  12920. }
  12921. var (
  12922. emptyTokenAnnotation = validServiceAccountTokenSecret()
  12923. missingTokenAnnotation = validServiceAccountTokenSecret()
  12924. missingTokenAnnotations = validServiceAccountTokenSecret()
  12925. )
  12926. emptyTokenAnnotation.Annotations[core.ServiceAccountNameKey] = ""
  12927. delete(missingTokenAnnotation.Annotations, core.ServiceAccountNameKey)
  12928. missingTokenAnnotations.Annotations = nil
  12929. tests := map[string]struct {
  12930. secret core.Secret
  12931. valid bool
  12932. }{
  12933. "valid": {validSecret(), true},
  12934. "empty name": {emptyName, false},
  12935. "invalid name": {invalidName, false},
  12936. "empty namespace": {emptyNs, false},
  12937. "invalid namespace": {invalidNs, false},
  12938. "over max size": {overMaxSize, false},
  12939. "invalid key": {invalidKey, false},
  12940. "valid service-account-token secret": {validServiceAccountTokenSecret(), true},
  12941. "empty service-account-token annotation": {emptyTokenAnnotation, false},
  12942. "missing service-account-token annotation": {missingTokenAnnotation, false},
  12943. "missing service-account-token annotations": {missingTokenAnnotations, false},
  12944. "leading dot key": {leadingDotKey, true},
  12945. "dot key": {dotKey, false},
  12946. "double dot key": {doubleDotKey, false},
  12947. }
  12948. for name, tc := range tests {
  12949. errs := ValidateSecret(&tc.secret)
  12950. if tc.valid && len(errs) > 0 {
  12951. t.Errorf("%v: Unexpected error: %v", name, errs)
  12952. }
  12953. if !tc.valid && len(errs) == 0 {
  12954. t.Errorf("%v: Unexpected non-error", name)
  12955. }
  12956. }
  12957. }
  12958. func TestValidateSecretUpdate(t *testing.T) {
  12959. validSecret := func() core.Secret {
  12960. return core.Secret{
  12961. ObjectMeta: metav1.ObjectMeta{
  12962. Name: "foo",
  12963. Namespace: "bar",
  12964. ResourceVersion: "20",
  12965. },
  12966. Data: map[string][]byte{
  12967. "data-1": []byte("bar"),
  12968. },
  12969. }
  12970. }
  12971. falseVal := false
  12972. trueVal := true
  12973. secret := validSecret()
  12974. immutableSecret := validSecret()
  12975. immutableSecret.Immutable = &trueVal
  12976. mutableSecret := validSecret()
  12977. mutableSecret.Immutable = &falseVal
  12978. secretWithData := validSecret()
  12979. secretWithData.Data["data-2"] = []byte("baz")
  12980. immutableSecretWithData := validSecret()
  12981. immutableSecretWithData.Immutable = &trueVal
  12982. immutableSecretWithData.Data["data-2"] = []byte("baz")
  12983. secretWithChangedData := validSecret()
  12984. secretWithChangedData.Data["data-1"] = []byte("foo")
  12985. immutableSecretWithChangedData := validSecret()
  12986. immutableSecretWithChangedData.Immutable = &trueVal
  12987. immutableSecretWithChangedData.Data["data-1"] = []byte("foo")
  12988. tests := []struct {
  12989. name string
  12990. oldSecret core.Secret
  12991. newSecret core.Secret
  12992. valid bool
  12993. }{
  12994. {
  12995. name: "mark secret immutable",
  12996. oldSecret: secret,
  12997. newSecret: immutableSecret,
  12998. valid: true,
  12999. },
  13000. {
  13001. name: "revert immutable secret",
  13002. oldSecret: immutableSecret,
  13003. newSecret: secret,
  13004. valid: false,
  13005. },
  13006. {
  13007. name: "makr immutable secret mutable",
  13008. oldSecret: immutableSecret,
  13009. newSecret: mutableSecret,
  13010. valid: false,
  13011. },
  13012. {
  13013. name: "add data in secret",
  13014. oldSecret: secret,
  13015. newSecret: secretWithData,
  13016. valid: true,
  13017. },
  13018. {
  13019. name: "add data in immutable secret",
  13020. oldSecret: immutableSecret,
  13021. newSecret: immutableSecretWithData,
  13022. valid: false,
  13023. },
  13024. {
  13025. name: "change data in secret",
  13026. oldSecret: secret,
  13027. newSecret: secretWithChangedData,
  13028. valid: true,
  13029. },
  13030. {
  13031. name: "change data in immutable secret",
  13032. oldSecret: immutableSecret,
  13033. newSecret: immutableSecretWithChangedData,
  13034. valid: false,
  13035. },
  13036. }
  13037. for _, tc := range tests {
  13038. t.Run(tc.name, func(t *testing.T) {
  13039. errs := ValidateSecretUpdate(&tc.newSecret, &tc.oldSecret)
  13040. if tc.valid && len(errs) > 0 {
  13041. t.Errorf("Unexpected error: %v", errs)
  13042. }
  13043. if !tc.valid && len(errs) == 0 {
  13044. t.Errorf("Unexpected lack of error")
  13045. }
  13046. })
  13047. }
  13048. }
  13049. func TestValidateDockerConfigSecret(t *testing.T) {
  13050. validDockerSecret := func() core.Secret {
  13051. return core.Secret{
  13052. ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"},
  13053. Type: core.SecretTypeDockercfg,
  13054. Data: map[string][]byte{
  13055. core.DockerConfigKey: []byte(`{"https://index.docker.io/v1/": {"auth": "Y2x1ZWRyb29sZXIwMDAxOnBhc3N3b3Jk","email": "fake@example.com"}}`),
  13056. },
  13057. }
  13058. }
  13059. validDockerSecret2 := func() core.Secret {
  13060. return core.Secret{
  13061. ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"},
  13062. Type: core.SecretTypeDockerConfigJSON,
  13063. Data: map[string][]byte{
  13064. core.DockerConfigJSONKey: []byte(`{"auths":{"https://index.docker.io/v1/": {"auth": "Y2x1ZWRyb29sZXIwMDAxOnBhc3N3b3Jk","email": "fake@example.com"}}}`),
  13065. },
  13066. }
  13067. }
  13068. var (
  13069. missingDockerConfigKey = validDockerSecret()
  13070. emptyDockerConfigKey = validDockerSecret()
  13071. invalidDockerConfigKey = validDockerSecret()
  13072. missingDockerConfigKey2 = validDockerSecret2()
  13073. emptyDockerConfigKey2 = validDockerSecret2()
  13074. invalidDockerConfigKey2 = validDockerSecret2()
  13075. )
  13076. delete(missingDockerConfigKey.Data, core.DockerConfigKey)
  13077. emptyDockerConfigKey.Data[core.DockerConfigKey] = []byte("")
  13078. invalidDockerConfigKey.Data[core.DockerConfigKey] = []byte("bad")
  13079. delete(missingDockerConfigKey2.Data, core.DockerConfigJSONKey)
  13080. emptyDockerConfigKey2.Data[core.DockerConfigJSONKey] = []byte("")
  13081. invalidDockerConfigKey2.Data[core.DockerConfigJSONKey] = []byte("bad")
  13082. tests := map[string]struct {
  13083. secret core.Secret
  13084. valid bool
  13085. }{
  13086. "valid dockercfg": {validDockerSecret(), true},
  13087. "missing dockercfg": {missingDockerConfigKey, false},
  13088. "empty dockercfg": {emptyDockerConfigKey, false},
  13089. "invalid dockercfg": {invalidDockerConfigKey, false},
  13090. "valid config.json": {validDockerSecret2(), true},
  13091. "missing config.json": {missingDockerConfigKey2, false},
  13092. "empty config.json": {emptyDockerConfigKey2, false},
  13093. "invalid config.json": {invalidDockerConfigKey2, false},
  13094. }
  13095. for name, tc := range tests {
  13096. errs := ValidateSecret(&tc.secret)
  13097. if tc.valid && len(errs) > 0 {
  13098. t.Errorf("%v: Unexpected error: %v", name, errs)
  13099. }
  13100. if !tc.valid && len(errs) == 0 {
  13101. t.Errorf("%v: Unexpected non-error", name)
  13102. }
  13103. }
  13104. }
  13105. func TestValidateBasicAuthSecret(t *testing.T) {
  13106. validBasicAuthSecret := func() core.Secret {
  13107. return core.Secret{
  13108. ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"},
  13109. Type: core.SecretTypeBasicAuth,
  13110. Data: map[string][]byte{
  13111. core.BasicAuthUsernameKey: []byte("username"),
  13112. core.BasicAuthPasswordKey: []byte("password"),
  13113. },
  13114. }
  13115. }
  13116. var (
  13117. missingBasicAuthUsernamePasswordKeys = validBasicAuthSecret()
  13118. )
  13119. delete(missingBasicAuthUsernamePasswordKeys.Data, core.BasicAuthUsernameKey)
  13120. delete(missingBasicAuthUsernamePasswordKeys.Data, core.BasicAuthPasswordKey)
  13121. tests := map[string]struct {
  13122. secret core.Secret
  13123. valid bool
  13124. }{
  13125. "valid": {validBasicAuthSecret(), true},
  13126. "missing username and password": {missingBasicAuthUsernamePasswordKeys, false},
  13127. }
  13128. for name, tc := range tests {
  13129. errs := ValidateSecret(&tc.secret)
  13130. if tc.valid && len(errs) > 0 {
  13131. t.Errorf("%v: Unexpected error: %v", name, errs)
  13132. }
  13133. if !tc.valid && len(errs) == 0 {
  13134. t.Errorf("%v: Unexpected non-error", name)
  13135. }
  13136. }
  13137. }
  13138. func TestValidateSSHAuthSecret(t *testing.T) {
  13139. validSSHAuthSecret := func() core.Secret {
  13140. return core.Secret{
  13141. ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"},
  13142. Type: core.SecretTypeSSHAuth,
  13143. Data: map[string][]byte{
  13144. core.SSHAuthPrivateKey: []byte("foo-bar-baz"),
  13145. },
  13146. }
  13147. }
  13148. missingSSHAuthPrivateKey := validSSHAuthSecret()
  13149. delete(missingSSHAuthPrivateKey.Data, core.SSHAuthPrivateKey)
  13150. tests := map[string]struct {
  13151. secret core.Secret
  13152. valid bool
  13153. }{
  13154. "valid": {validSSHAuthSecret(), true},
  13155. "missing private key": {missingSSHAuthPrivateKey, false},
  13156. }
  13157. for name, tc := range tests {
  13158. errs := ValidateSecret(&tc.secret)
  13159. if tc.valid && len(errs) > 0 {
  13160. t.Errorf("%v: Unexpected error: %v", name, errs)
  13161. }
  13162. if !tc.valid && len(errs) == 0 {
  13163. t.Errorf("%v: Unexpected non-error", name)
  13164. }
  13165. }
  13166. }
  13167. func TestValidateEndpoints(t *testing.T) {
  13168. successCases := map[string]core.Endpoints{
  13169. "simple endpoint": {
  13170. ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
  13171. Subsets: []core.EndpointSubset{
  13172. {
  13173. Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}, {IP: "10.10.2.2"}},
  13174. Ports: []core.EndpointPort{{Name: "a", Port: 8675, Protocol: "TCP"}, {Name: "b", Port: 309, Protocol: "TCP"}},
  13175. },
  13176. {
  13177. Addresses: []core.EndpointAddress{{IP: "10.10.3.3"}},
  13178. Ports: []core.EndpointPort{{Name: "a", Port: 93, Protocol: "TCP"}, {Name: "b", Port: 76, Protocol: "TCP"}},
  13179. },
  13180. },
  13181. },
  13182. "empty subsets": {
  13183. ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
  13184. },
  13185. "no name required for singleton port": {
  13186. ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
  13187. Subsets: []core.EndpointSubset{
  13188. {
  13189. Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}},
  13190. Ports: []core.EndpointPort{{Port: 8675, Protocol: "TCP"}},
  13191. },
  13192. },
  13193. },
  13194. "empty ports": {
  13195. ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
  13196. Subsets: []core.EndpointSubset{
  13197. {
  13198. Addresses: []core.EndpointAddress{{IP: "10.10.3.3"}},
  13199. },
  13200. },
  13201. },
  13202. }
  13203. for k, v := range successCases {
  13204. if errs := ValidateEndpoints(&v); len(errs) != 0 {
  13205. t.Errorf("Expected success for %s, got %v", k, errs)
  13206. }
  13207. }
  13208. errorCases := map[string]struct {
  13209. endpoints core.Endpoints
  13210. errorType field.ErrorType
  13211. errorDetail string
  13212. }{
  13213. "missing namespace": {
  13214. endpoints: core.Endpoints{ObjectMeta: metav1.ObjectMeta{Name: "mysvc"}},
  13215. errorType: "FieldValueRequired",
  13216. },
  13217. "missing name": {
  13218. endpoints: core.Endpoints{ObjectMeta: metav1.ObjectMeta{Namespace: "namespace"}},
  13219. errorType: "FieldValueRequired",
  13220. },
  13221. "invalid namespace": {
  13222. endpoints: core.Endpoints{ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "no@#invalid.;chars\"allowed"}},
  13223. errorType: "FieldValueInvalid",
  13224. errorDetail: dnsLabelErrMsg,
  13225. },
  13226. "invalid name": {
  13227. endpoints: core.Endpoints{ObjectMeta: metav1.ObjectMeta{Name: "-_Invliad^&Characters", Namespace: "namespace"}},
  13228. errorType: "FieldValueInvalid",
  13229. errorDetail: dnsSubdomainLabelErrMsg,
  13230. },
  13231. "empty addresses": {
  13232. endpoints: core.Endpoints{
  13233. ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
  13234. Subsets: []core.EndpointSubset{
  13235. {
  13236. Ports: []core.EndpointPort{{Name: "a", Port: 93, Protocol: "TCP"}},
  13237. },
  13238. },
  13239. },
  13240. errorType: "FieldValueRequired",
  13241. },
  13242. "invalid IP": {
  13243. endpoints: core.Endpoints{
  13244. ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
  13245. Subsets: []core.EndpointSubset{
  13246. {
  13247. Addresses: []core.EndpointAddress{{IP: "[2001:0db8:85a3:0042:1000:8a2e:0370:7334]"}},
  13248. Ports: []core.EndpointPort{{Name: "a", Port: 93, Protocol: "TCP"}},
  13249. },
  13250. },
  13251. },
  13252. errorType: "FieldValueInvalid",
  13253. errorDetail: "must be a valid IP address",
  13254. },
  13255. "Multiple ports, one without name": {
  13256. endpoints: core.Endpoints{
  13257. ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
  13258. Subsets: []core.EndpointSubset{
  13259. {
  13260. Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}},
  13261. Ports: []core.EndpointPort{{Port: 8675, Protocol: "TCP"}, {Name: "b", Port: 309, Protocol: "TCP"}},
  13262. },
  13263. },
  13264. },
  13265. errorType: "FieldValueRequired",
  13266. },
  13267. "Invalid port number": {
  13268. endpoints: core.Endpoints{
  13269. ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
  13270. Subsets: []core.EndpointSubset{
  13271. {
  13272. Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}},
  13273. Ports: []core.EndpointPort{{Name: "a", Port: 66000, Protocol: "TCP"}},
  13274. },
  13275. },
  13276. },
  13277. errorType: "FieldValueInvalid",
  13278. errorDetail: "between",
  13279. },
  13280. "Invalid protocol": {
  13281. endpoints: core.Endpoints{
  13282. ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
  13283. Subsets: []core.EndpointSubset{
  13284. {
  13285. Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}},
  13286. Ports: []core.EndpointPort{{Name: "a", Port: 93, Protocol: "Protocol"}},
  13287. },
  13288. },
  13289. },
  13290. errorType: "FieldValueNotSupported",
  13291. },
  13292. "Address missing IP": {
  13293. endpoints: core.Endpoints{
  13294. ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
  13295. Subsets: []core.EndpointSubset{
  13296. {
  13297. Addresses: []core.EndpointAddress{{}},
  13298. Ports: []core.EndpointPort{{Name: "a", Port: 93, Protocol: "TCP"}},
  13299. },
  13300. },
  13301. },
  13302. errorType: "FieldValueInvalid",
  13303. errorDetail: "must be a valid IP address",
  13304. },
  13305. "Port missing number": {
  13306. endpoints: core.Endpoints{
  13307. ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
  13308. Subsets: []core.EndpointSubset{
  13309. {
  13310. Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}},
  13311. Ports: []core.EndpointPort{{Name: "a", Protocol: "TCP"}},
  13312. },
  13313. },
  13314. },
  13315. errorType: "FieldValueInvalid",
  13316. errorDetail: "between",
  13317. },
  13318. "Port missing protocol": {
  13319. endpoints: core.Endpoints{
  13320. ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
  13321. Subsets: []core.EndpointSubset{
  13322. {
  13323. Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}},
  13324. Ports: []core.EndpointPort{{Name: "a", Port: 93}},
  13325. },
  13326. },
  13327. },
  13328. errorType: "FieldValueRequired",
  13329. },
  13330. "Address is loopback": {
  13331. endpoints: core.Endpoints{
  13332. ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
  13333. Subsets: []core.EndpointSubset{
  13334. {
  13335. Addresses: []core.EndpointAddress{{IP: "127.0.0.1"}},
  13336. Ports: []core.EndpointPort{{Name: "p", Port: 93, Protocol: "TCP"}},
  13337. },
  13338. },
  13339. },
  13340. errorType: "FieldValueInvalid",
  13341. errorDetail: "loopback",
  13342. },
  13343. "Address is link-local": {
  13344. endpoints: core.Endpoints{
  13345. ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
  13346. Subsets: []core.EndpointSubset{
  13347. {
  13348. Addresses: []core.EndpointAddress{{IP: "169.254.169.254"}},
  13349. Ports: []core.EndpointPort{{Name: "p", Port: 93, Protocol: "TCP"}},
  13350. },
  13351. },
  13352. },
  13353. errorType: "FieldValueInvalid",
  13354. errorDetail: "link-local",
  13355. },
  13356. "Address is link-local multicast": {
  13357. endpoints: core.Endpoints{
  13358. ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
  13359. Subsets: []core.EndpointSubset{
  13360. {
  13361. Addresses: []core.EndpointAddress{{IP: "224.0.0.1"}},
  13362. Ports: []core.EndpointPort{{Name: "p", Port: 93, Protocol: "TCP"}},
  13363. },
  13364. },
  13365. },
  13366. errorType: "FieldValueInvalid",
  13367. errorDetail: "link-local multicast",
  13368. },
  13369. }
  13370. for k, v := range errorCases {
  13371. if errs := ValidateEndpoints(&v.endpoints); len(errs) == 0 || errs[0].Type != v.errorType || !strings.Contains(errs[0].Detail, v.errorDetail) {
  13372. t.Errorf("[%s] Expected error type %s with detail %q, got %v", k, v.errorType, v.errorDetail, errs)
  13373. }
  13374. }
  13375. }
  13376. func TestValidateTLSSecret(t *testing.T) {
  13377. successCases := map[string]core.Secret{
  13378. "empty certificate chain": {
  13379. ObjectMeta: metav1.ObjectMeta{Name: "tls-cert", Namespace: "namespace"},
  13380. Data: map[string][]byte{
  13381. core.TLSCertKey: []byte("public key"),
  13382. core.TLSPrivateKeyKey: []byte("private key"),
  13383. },
  13384. },
  13385. }
  13386. for k, v := range successCases {
  13387. if errs := ValidateSecret(&v); len(errs) != 0 {
  13388. t.Errorf("Expected success for %s, got %v", k, errs)
  13389. }
  13390. }
  13391. errorCases := map[string]struct {
  13392. secrets core.Secret
  13393. errorType field.ErrorType
  13394. errorDetail string
  13395. }{
  13396. "missing public key": {
  13397. secrets: core.Secret{
  13398. ObjectMeta: metav1.ObjectMeta{Name: "tls-cert"},
  13399. Data: map[string][]byte{
  13400. core.TLSCertKey: []byte("public key"),
  13401. },
  13402. },
  13403. errorType: "FieldValueRequired",
  13404. },
  13405. "missing private key": {
  13406. secrets: core.Secret{
  13407. ObjectMeta: metav1.ObjectMeta{Name: "tls-cert"},
  13408. Data: map[string][]byte{
  13409. core.TLSCertKey: []byte("public key"),
  13410. },
  13411. },
  13412. errorType: "FieldValueRequired",
  13413. },
  13414. }
  13415. for k, v := range errorCases {
  13416. if errs := ValidateSecret(&v.secrets); len(errs) == 0 || errs[0].Type != v.errorType || !strings.Contains(errs[0].Detail, v.errorDetail) {
  13417. t.Errorf("[%s] Expected error type %s with detail %q, got %v", k, v.errorType, v.errorDetail, errs)
  13418. }
  13419. }
  13420. }
  13421. func TestValidateSecurityContext(t *testing.T) {
  13422. runAsUser := int64(1)
  13423. fullValidSC := func() *core.SecurityContext {
  13424. return &core.SecurityContext{
  13425. Privileged: utilpointer.BoolPtr(false),
  13426. Capabilities: &core.Capabilities{
  13427. Add: []core.Capability{"foo"},
  13428. Drop: []core.Capability{"bar"},
  13429. },
  13430. SELinuxOptions: &core.SELinuxOptions{
  13431. User: "user",
  13432. Role: "role",
  13433. Type: "type",
  13434. Level: "level",
  13435. },
  13436. RunAsUser: &runAsUser,
  13437. }
  13438. }
  13439. //setup data
  13440. allSettings := fullValidSC()
  13441. noCaps := fullValidSC()
  13442. noCaps.Capabilities = nil
  13443. noSELinux := fullValidSC()
  13444. noSELinux.SELinuxOptions = nil
  13445. noPrivRequest := fullValidSC()
  13446. noPrivRequest.Privileged = nil
  13447. noRunAsUser := fullValidSC()
  13448. noRunAsUser.RunAsUser = nil
  13449. successCases := map[string]struct {
  13450. sc *core.SecurityContext
  13451. }{
  13452. "all settings": {allSettings},
  13453. "no capabilities": {noCaps},
  13454. "no selinux": {noSELinux},
  13455. "no priv request": {noPrivRequest},
  13456. "no run as user": {noRunAsUser},
  13457. }
  13458. for k, v := range successCases {
  13459. if errs := ValidateSecurityContext(v.sc, field.NewPath("field")); len(errs) != 0 {
  13460. t.Errorf("[%s] Expected success, got %v", k, errs)
  13461. }
  13462. }
  13463. privRequestWithGlobalDeny := fullValidSC()
  13464. privRequestWithGlobalDeny.Privileged = utilpointer.BoolPtr(true)
  13465. negativeRunAsUser := fullValidSC()
  13466. negativeUser := int64(-1)
  13467. negativeRunAsUser.RunAsUser = &negativeUser
  13468. privWithoutEscalation := fullValidSC()
  13469. privWithoutEscalation.Privileged = utilpointer.BoolPtr(true)
  13470. privWithoutEscalation.AllowPrivilegeEscalation = utilpointer.BoolPtr(false)
  13471. capSysAdminWithoutEscalation := fullValidSC()
  13472. capSysAdminWithoutEscalation.Capabilities.Add = []core.Capability{"CAP_SYS_ADMIN"}
  13473. capSysAdminWithoutEscalation.AllowPrivilegeEscalation = utilpointer.BoolPtr(false)
  13474. errorCases := map[string]struct {
  13475. sc *core.SecurityContext
  13476. errorType field.ErrorType
  13477. errorDetail string
  13478. capAllowPriv bool
  13479. }{
  13480. "request privileged when capabilities forbids": {
  13481. sc: privRequestWithGlobalDeny,
  13482. errorType: "FieldValueForbidden",
  13483. errorDetail: "disallowed by cluster policy",
  13484. },
  13485. "negative RunAsUser": {
  13486. sc: negativeRunAsUser,
  13487. errorType: "FieldValueInvalid",
  13488. errorDetail: "must be between",
  13489. },
  13490. "with CAP_SYS_ADMIN and allowPrivilegeEscalation false": {
  13491. sc: capSysAdminWithoutEscalation,
  13492. errorType: "FieldValueInvalid",
  13493. errorDetail: "cannot set `allowPrivilegeEscalation` to false and `capabilities.Add` CAP_SYS_ADMIN",
  13494. },
  13495. "with privileged and allowPrivilegeEscalation false": {
  13496. sc: privWithoutEscalation,
  13497. errorType: "FieldValueInvalid",
  13498. errorDetail: "cannot set `allowPrivilegeEscalation` to false and `privileged` to true",
  13499. capAllowPriv: true,
  13500. },
  13501. }
  13502. for k, v := range errorCases {
  13503. capabilities.SetForTests(capabilities.Capabilities{
  13504. AllowPrivileged: v.capAllowPriv,
  13505. })
  13506. if errs := ValidateSecurityContext(v.sc, field.NewPath("field")); len(errs) == 0 || errs[0].Type != v.errorType || !strings.Contains(errs[0].Detail, v.errorDetail) {
  13507. t.Errorf("[%s] Expected error type %q with detail %q, got %v", k, v.errorType, v.errorDetail, errs)
  13508. }
  13509. }
  13510. }
  13511. func fakeValidSecurityContext(priv bool) *core.SecurityContext {
  13512. return &core.SecurityContext{
  13513. Privileged: &priv,
  13514. }
  13515. }
  13516. func TestValidPodLogOptions(t *testing.T) {
  13517. now := metav1.Now()
  13518. negative := int64(-1)
  13519. zero := int64(0)
  13520. positive := int64(1)
  13521. tests := []struct {
  13522. opt core.PodLogOptions
  13523. errs int
  13524. }{
  13525. {core.PodLogOptions{}, 0},
  13526. {core.PodLogOptions{Previous: true}, 0},
  13527. {core.PodLogOptions{Follow: true}, 0},
  13528. {core.PodLogOptions{TailLines: &zero}, 0},
  13529. {core.PodLogOptions{TailLines: &negative}, 1},
  13530. {core.PodLogOptions{TailLines: &positive}, 0},
  13531. {core.PodLogOptions{LimitBytes: &zero}, 1},
  13532. {core.PodLogOptions{LimitBytes: &negative}, 1},
  13533. {core.PodLogOptions{LimitBytes: &positive}, 0},
  13534. {core.PodLogOptions{SinceSeconds: &negative}, 1},
  13535. {core.PodLogOptions{SinceSeconds: &positive}, 0},
  13536. {core.PodLogOptions{SinceSeconds: &zero}, 1},
  13537. {core.PodLogOptions{SinceTime: &now}, 0},
  13538. }
  13539. for i, test := range tests {
  13540. errs := ValidatePodLogOptions(&test.opt)
  13541. if test.errs != len(errs) {
  13542. t.Errorf("%d: Unexpected errors: %v", i, errs)
  13543. }
  13544. }
  13545. }
  13546. func TestValidateConfigMap(t *testing.T) {
  13547. newConfigMap := func(name, namespace string, data map[string]string, binaryData map[string][]byte) core.ConfigMap {
  13548. return core.ConfigMap{
  13549. ObjectMeta: metav1.ObjectMeta{
  13550. Name: name,
  13551. Namespace: namespace,
  13552. },
  13553. Data: data,
  13554. BinaryData: binaryData,
  13555. }
  13556. }
  13557. var (
  13558. validConfigMap = newConfigMap("validname", "validns", map[string]string{"key": "value"}, map[string][]byte{"bin": []byte("value")})
  13559. maxKeyLength = newConfigMap("validname", "validns", map[string]string{strings.Repeat("a", 253): "value"}, nil)
  13560. emptyName = newConfigMap("", "validns", nil, nil)
  13561. invalidName = newConfigMap("NoUppercaseOrSpecialCharsLike=Equals", "validns", nil, nil)
  13562. emptyNs = newConfigMap("validname", "", nil, nil)
  13563. invalidNs = newConfigMap("validname", "NoUppercaseOrSpecialCharsLike=Equals", nil, nil)
  13564. invalidKey = newConfigMap("validname", "validns", map[string]string{"a*b": "value"}, nil)
  13565. leadingDotKey = newConfigMap("validname", "validns", map[string]string{".ab": "value"}, nil)
  13566. dotKey = newConfigMap("validname", "validns", map[string]string{".": "value"}, nil)
  13567. doubleDotKey = newConfigMap("validname", "validns", map[string]string{"..": "value"}, nil)
  13568. overMaxKeyLength = newConfigMap("validname", "validns", map[string]string{strings.Repeat("a", 254): "value"}, nil)
  13569. overMaxSize = newConfigMap("validname", "validns", map[string]string{"key": strings.Repeat("a", v1.MaxSecretSize+1)}, nil)
  13570. duplicatedKey = newConfigMap("validname", "validns", map[string]string{"key": "value1"}, map[string][]byte{"key": []byte("value2")})
  13571. binDataInvalidKey = newConfigMap("validname", "validns", nil, map[string][]byte{"a*b": []byte("value")})
  13572. binDataLeadingDotKey = newConfigMap("validname", "validns", nil, map[string][]byte{".ab": []byte("value")})
  13573. binDataDotKey = newConfigMap("validname", "validns", nil, map[string][]byte{".": []byte("value")})
  13574. binDataDoubleDotKey = newConfigMap("validname", "validns", nil, map[string][]byte{"..": []byte("value")})
  13575. binDataOverMaxKeyLength = newConfigMap("validname", "validns", nil, map[string][]byte{strings.Repeat("a", 254): []byte("value")})
  13576. binDataOverMaxSize = newConfigMap("validname", "validns", nil, map[string][]byte{"bin": bytes.Repeat([]byte("a"), v1.MaxSecretSize+1)})
  13577. binNonUtf8Value = newConfigMap("validname", "validns", nil, map[string][]byte{"key": {0, 0xFE, 0, 0xFF}})
  13578. )
  13579. tests := map[string]struct {
  13580. cfg core.ConfigMap
  13581. isValid bool
  13582. }{
  13583. "valid": {validConfigMap, true},
  13584. "max key length": {maxKeyLength, true},
  13585. "leading dot key": {leadingDotKey, true},
  13586. "empty name": {emptyName, false},
  13587. "invalid name": {invalidName, false},
  13588. "invalid key": {invalidKey, false},
  13589. "empty namespace": {emptyNs, false},
  13590. "invalid namespace": {invalidNs, false},
  13591. "dot key": {dotKey, false},
  13592. "double dot key": {doubleDotKey, false},
  13593. "over max key length": {overMaxKeyLength, false},
  13594. "over max size": {overMaxSize, false},
  13595. "duplicated key": {duplicatedKey, false},
  13596. "binary data invalid key": {binDataInvalidKey, false},
  13597. "binary data leading dot key": {binDataLeadingDotKey, true},
  13598. "binary data dot key": {binDataDotKey, false},
  13599. "binary data double dot key": {binDataDoubleDotKey, false},
  13600. "binary data over max key length": {binDataOverMaxKeyLength, false},
  13601. "binary data max size": {binDataOverMaxSize, false},
  13602. "binary data non utf-8 bytes": {binNonUtf8Value, true},
  13603. }
  13604. for name, tc := range tests {
  13605. errs := ValidateConfigMap(&tc.cfg)
  13606. if tc.isValid && len(errs) > 0 {
  13607. t.Errorf("%v: unexpected error: %v", name, errs)
  13608. }
  13609. if !tc.isValid && len(errs) == 0 {
  13610. t.Errorf("%v: unexpected non-error", name)
  13611. }
  13612. }
  13613. }
  13614. func TestValidateConfigMapUpdate(t *testing.T) {
  13615. newConfigMap := func(version, name, namespace string, data map[string]string) core.ConfigMap {
  13616. return core.ConfigMap{
  13617. ObjectMeta: metav1.ObjectMeta{
  13618. Name: name,
  13619. Namespace: namespace,
  13620. ResourceVersion: version,
  13621. },
  13622. Data: data,
  13623. }
  13624. }
  13625. validConfigMap := func() core.ConfigMap {
  13626. return newConfigMap("1", "validname", "validdns", map[string]string{"key": "value"})
  13627. }
  13628. falseVal := false
  13629. trueVal := true
  13630. configMap := validConfigMap()
  13631. immutableConfigMap := validConfigMap()
  13632. immutableConfigMap.Immutable = &trueVal
  13633. mutableConfigMap := validConfigMap()
  13634. mutableConfigMap.Immutable = &falseVal
  13635. configMapWithData := validConfigMap()
  13636. configMapWithData.Data["key-2"] = "value-2"
  13637. immutableConfigMapWithData := validConfigMap()
  13638. immutableConfigMapWithData.Immutable = &trueVal
  13639. immutableConfigMapWithData.Data["key-2"] = "value-2"
  13640. configMapWithChangedData := validConfigMap()
  13641. configMapWithChangedData.Data["key"] = "foo"
  13642. immutableConfigMapWithChangedData := validConfigMap()
  13643. immutableConfigMapWithChangedData.Immutable = &trueVal
  13644. immutableConfigMapWithChangedData.Data["key"] = "foo"
  13645. noVersion := newConfigMap("", "validname", "validns", map[string]string{"key": "value"})
  13646. cases := []struct {
  13647. name string
  13648. newCfg core.ConfigMap
  13649. oldCfg core.ConfigMap
  13650. valid bool
  13651. }{
  13652. {
  13653. name: "valid",
  13654. newCfg: configMap,
  13655. oldCfg: configMap,
  13656. valid: true,
  13657. },
  13658. {
  13659. name: "invalid",
  13660. newCfg: noVersion,
  13661. oldCfg: configMap,
  13662. valid: false,
  13663. },
  13664. {
  13665. name: "mark configmap immutable",
  13666. oldCfg: configMap,
  13667. newCfg: immutableConfigMap,
  13668. valid: true,
  13669. },
  13670. {
  13671. name: "revert immutable configmap",
  13672. oldCfg: immutableConfigMap,
  13673. newCfg: configMap,
  13674. valid: false,
  13675. },
  13676. {
  13677. name: "mark immutable configmap mutable",
  13678. oldCfg: immutableConfigMap,
  13679. newCfg: mutableConfigMap,
  13680. valid: false,
  13681. },
  13682. {
  13683. name: "add data in configmap",
  13684. oldCfg: configMap,
  13685. newCfg: configMapWithData,
  13686. valid: true,
  13687. },
  13688. {
  13689. name: "add data in immutable configmap",
  13690. oldCfg: immutableConfigMap,
  13691. newCfg: immutableConfigMapWithData,
  13692. valid: false,
  13693. },
  13694. {
  13695. name: "change data in configmap",
  13696. oldCfg: configMap,
  13697. newCfg: configMapWithChangedData,
  13698. valid: true,
  13699. },
  13700. {
  13701. name: "change data in immutable configmap",
  13702. oldCfg: immutableConfigMap,
  13703. newCfg: immutableConfigMapWithChangedData,
  13704. valid: false,
  13705. },
  13706. }
  13707. for _, tc := range cases {
  13708. t.Run(tc.name, func(t *testing.T) {
  13709. errs := ValidateConfigMapUpdate(&tc.newCfg, &tc.oldCfg)
  13710. if tc.valid && len(errs) > 0 {
  13711. t.Errorf("Unexpected error: %v", errs)
  13712. }
  13713. if !tc.valid && len(errs) == 0 {
  13714. t.Errorf("Unexpected lack of error")
  13715. }
  13716. })
  13717. }
  13718. }
  13719. func TestValidateHasLabel(t *testing.T) {
  13720. successCase := metav1.ObjectMeta{
  13721. Name: "123",
  13722. Namespace: "ns",
  13723. Labels: map[string]string{
  13724. "other": "blah",
  13725. "foo": "bar",
  13726. },
  13727. }
  13728. if errs := ValidateHasLabel(successCase, field.NewPath("field"), "foo", "bar"); len(errs) != 0 {
  13729. t.Errorf("expected success: %v", errs)
  13730. }
  13731. missingCase := metav1.ObjectMeta{
  13732. Name: "123",
  13733. Namespace: "ns",
  13734. Labels: map[string]string{
  13735. "other": "blah",
  13736. },
  13737. }
  13738. if errs := ValidateHasLabel(missingCase, field.NewPath("field"), "foo", "bar"); len(errs) == 0 {
  13739. t.Errorf("expected failure")
  13740. }
  13741. wrongValueCase := metav1.ObjectMeta{
  13742. Name: "123",
  13743. Namespace: "ns",
  13744. Labels: map[string]string{
  13745. "other": "blah",
  13746. "foo": "notbar",
  13747. },
  13748. }
  13749. if errs := ValidateHasLabel(wrongValueCase, field.NewPath("field"), "foo", "bar"); len(errs) == 0 {
  13750. t.Errorf("expected failure")
  13751. }
  13752. }
  13753. func TestIsValidSysctlName(t *testing.T) {
  13754. valid := []string{
  13755. "a.b.c.d",
  13756. "a",
  13757. "a_b",
  13758. "a-b",
  13759. "abc",
  13760. "abc.def",
  13761. }
  13762. invalid := []string{
  13763. "",
  13764. "*",
  13765. "ä",
  13766. "a_",
  13767. "_",
  13768. "__",
  13769. "_a",
  13770. "_a._b",
  13771. "-",
  13772. ".",
  13773. "a.",
  13774. ".a",
  13775. "a.b.",
  13776. "a*.b",
  13777. "a*b",
  13778. "*a",
  13779. "a.*",
  13780. "*",
  13781. "abc*",
  13782. "a.abc*",
  13783. "a.b.*",
  13784. "Abc",
  13785. func(n int) string {
  13786. x := make([]byte, n)
  13787. for i := range x {
  13788. x[i] = byte('a')
  13789. }
  13790. return string(x)
  13791. }(256),
  13792. }
  13793. for _, s := range valid {
  13794. if !IsValidSysctlName(s) {
  13795. t.Errorf("%q expected to be a valid sysctl name", s)
  13796. }
  13797. }
  13798. for _, s := range invalid {
  13799. if IsValidSysctlName(s) {
  13800. t.Errorf("%q expected to be an invalid sysctl name", s)
  13801. }
  13802. }
  13803. }
  13804. func TestValidateSysctls(t *testing.T) {
  13805. valid := []string{
  13806. "net.foo.bar",
  13807. "kernel.shmmax",
  13808. }
  13809. invalid := []string{
  13810. "i..nvalid",
  13811. "_invalid",
  13812. }
  13813. duplicates := []string{
  13814. "kernel.shmmax",
  13815. "kernel.shmmax",
  13816. }
  13817. sysctls := make([]core.Sysctl, len(valid))
  13818. for i, sysctl := range valid {
  13819. sysctls[i].Name = sysctl
  13820. }
  13821. errs := validateSysctls(sysctls, field.NewPath("foo"))
  13822. if len(errs) != 0 {
  13823. t.Errorf("unexpected validation errors: %v", errs)
  13824. }
  13825. sysctls = make([]core.Sysctl, len(invalid))
  13826. for i, sysctl := range invalid {
  13827. sysctls[i].Name = sysctl
  13828. }
  13829. errs = validateSysctls(sysctls, field.NewPath("foo"))
  13830. if len(errs) != 2 {
  13831. t.Errorf("expected 2 validation errors. Got: %v", errs)
  13832. } else {
  13833. if got, expected := errs[0].Error(), "foo"; !strings.Contains(got, expected) {
  13834. t.Errorf("unexpected errors: expected=%q, got=%q", expected, got)
  13835. }
  13836. if got, expected := errs[1].Error(), "foo"; !strings.Contains(got, expected) {
  13837. t.Errorf("unexpected errors: expected=%q, got=%q", expected, got)
  13838. }
  13839. }
  13840. sysctls = make([]core.Sysctl, len(duplicates))
  13841. for i, sysctl := range duplicates {
  13842. sysctls[i].Name = sysctl
  13843. }
  13844. errs = validateSysctls(sysctls, field.NewPath("foo"))
  13845. if len(errs) != 1 {
  13846. t.Errorf("unexpected validation errors: %v", errs)
  13847. } else if errs[0].Type != field.ErrorTypeDuplicate {
  13848. t.Errorf("expected error type %v, got %v", field.ErrorTypeDuplicate, errs[0].Type)
  13849. }
  13850. }
  13851. func newNodeNameEndpoint(nodeName string) *core.Endpoints {
  13852. ep := &core.Endpoints{
  13853. ObjectMeta: metav1.ObjectMeta{
  13854. Name: "foo",
  13855. Namespace: metav1.NamespaceDefault,
  13856. ResourceVersion: "1",
  13857. },
  13858. Subsets: []core.EndpointSubset{
  13859. {
  13860. NotReadyAddresses: []core.EndpointAddress{},
  13861. Ports: []core.EndpointPort{{Name: "https", Port: 443, Protocol: "TCP"}},
  13862. Addresses: []core.EndpointAddress{
  13863. {
  13864. IP: "8.8.8.8",
  13865. Hostname: "zookeeper1",
  13866. NodeName: &nodeName}}}}}
  13867. return ep
  13868. }
  13869. func TestEndpointAddressNodeNameUpdateRestrictions(t *testing.T) {
  13870. oldEndpoint := newNodeNameEndpoint("kubernetes-node-setup-by-backend")
  13871. updatedEndpoint := newNodeNameEndpoint("kubernetes-changed-nodename")
  13872. // Check that NodeName can be changed during update, this is to accommodate the case where nodeIP or PodCIDR is reused.
  13873. // The same ip will now have a different nodeName.
  13874. errList := ValidateEndpoints(updatedEndpoint)
  13875. errList = append(errList, ValidateEndpointsUpdate(updatedEndpoint, oldEndpoint)...)
  13876. if len(errList) != 0 {
  13877. t.Error("Endpoint should allow changing of Subset.Addresses.NodeName on update")
  13878. }
  13879. }
  13880. func TestEndpointAddressNodeNameInvalidDNSSubdomain(t *testing.T) {
  13881. // Check NodeName DNS validation
  13882. endpoint := newNodeNameEndpoint("illegal*.nodename")
  13883. errList := ValidateEndpoints(endpoint)
  13884. if len(errList) == 0 {
  13885. t.Error("Endpoint should reject invalid NodeName")
  13886. }
  13887. }
  13888. func TestEndpointAddressNodeNameCanBeAnIPAddress(t *testing.T) {
  13889. endpoint := newNodeNameEndpoint("10.10.1.1")
  13890. errList := ValidateEndpoints(endpoint)
  13891. if len(errList) != 0 {
  13892. t.Error("Endpoint should accept a NodeName that is an IP address")
  13893. }
  13894. }
  13895. func TestValidateFlexVolumeSource(t *testing.T) {
  13896. testcases := map[string]struct {
  13897. source *core.FlexVolumeSource
  13898. expectedErrs map[string]string
  13899. }{
  13900. "valid": {
  13901. source: &core.FlexVolumeSource{Driver: "foo"},
  13902. expectedErrs: map[string]string{},
  13903. },
  13904. "valid with options": {
  13905. source: &core.FlexVolumeSource{Driver: "foo", Options: map[string]string{"foo": "bar"}},
  13906. expectedErrs: map[string]string{},
  13907. },
  13908. "no driver": {
  13909. source: &core.FlexVolumeSource{Driver: ""},
  13910. expectedErrs: map[string]string{"driver": "Required value"},
  13911. },
  13912. "reserved option keys": {
  13913. source: &core.FlexVolumeSource{
  13914. Driver: "foo",
  13915. Options: map[string]string{
  13916. // valid options
  13917. "myns.io": "A",
  13918. "myns.io/bar": "A",
  13919. "myns.io/kubernetes.io": "A",
  13920. // invalid options
  13921. "KUBERNETES.IO": "A",
  13922. "kubernetes.io": "A",
  13923. "kubernetes.io/": "A",
  13924. "kubernetes.io/foo": "A",
  13925. "alpha.kubernetes.io": "A",
  13926. "alpha.kubernetes.io/": "A",
  13927. "alpha.kubernetes.io/foo": "A",
  13928. "k8s.io": "A",
  13929. "k8s.io/": "A",
  13930. "k8s.io/foo": "A",
  13931. "alpha.k8s.io": "A",
  13932. "alpha.k8s.io/": "A",
  13933. "alpha.k8s.io/foo": "A",
  13934. },
  13935. },
  13936. expectedErrs: map[string]string{
  13937. "options[KUBERNETES.IO]": "reserved",
  13938. "options[kubernetes.io]": "reserved",
  13939. "options[kubernetes.io/]": "reserved",
  13940. "options[kubernetes.io/foo]": "reserved",
  13941. "options[alpha.kubernetes.io]": "reserved",
  13942. "options[alpha.kubernetes.io/]": "reserved",
  13943. "options[alpha.kubernetes.io/foo]": "reserved",
  13944. "options[k8s.io]": "reserved",
  13945. "options[k8s.io/]": "reserved",
  13946. "options[k8s.io/foo]": "reserved",
  13947. "options[alpha.k8s.io]": "reserved",
  13948. "options[alpha.k8s.io/]": "reserved",
  13949. "options[alpha.k8s.io/foo]": "reserved",
  13950. },
  13951. },
  13952. }
  13953. for k, tc := range testcases {
  13954. errs := validateFlexVolumeSource(tc.source, nil)
  13955. for _, err := range errs {
  13956. expectedErr, ok := tc.expectedErrs[err.Field]
  13957. if !ok {
  13958. t.Errorf("%s: unexpected err on field %s: %v", k, err.Field, err)
  13959. continue
  13960. }
  13961. if !strings.Contains(err.Error(), expectedErr) {
  13962. t.Errorf("%s: expected err on field %s to contain '%s', was %v", k, err.Field, expectedErr, err.Error())
  13963. continue
  13964. }
  13965. }
  13966. if len(errs) != len(tc.expectedErrs) {
  13967. t.Errorf("%s: expected errs %#v, got %#v", k, tc.expectedErrs, errs)
  13968. continue
  13969. }
  13970. }
  13971. }
  13972. func TestValidateOrSetClientIPAffinityConfig(t *testing.T) {
  13973. successCases := map[string]*core.SessionAffinityConfig{
  13974. "non-empty config, valid timeout: 1": {
  13975. ClientIP: &core.ClientIPConfig{
  13976. TimeoutSeconds: utilpointer.Int32Ptr(1),
  13977. },
  13978. },
  13979. "non-empty config, valid timeout: core.MaxClientIPServiceAffinitySeconds-1": {
  13980. ClientIP: &core.ClientIPConfig{
  13981. TimeoutSeconds: utilpointer.Int32Ptr(core.MaxClientIPServiceAffinitySeconds - 1),
  13982. },
  13983. },
  13984. "non-empty config, valid timeout: core.MaxClientIPServiceAffinitySeconds": {
  13985. ClientIP: &core.ClientIPConfig{
  13986. TimeoutSeconds: utilpointer.Int32Ptr(core.MaxClientIPServiceAffinitySeconds),
  13987. },
  13988. },
  13989. }
  13990. for name, test := range successCases {
  13991. if errs := validateClientIPAffinityConfig(test, field.NewPath("field")); len(errs) != 0 {
  13992. t.Errorf("case: %s, expected success: %v", name, errs)
  13993. }
  13994. }
  13995. errorCases := map[string]*core.SessionAffinityConfig{
  13996. "empty session affinity config": nil,
  13997. "empty client IP config": {
  13998. ClientIP: nil,
  13999. },
  14000. "empty timeoutSeconds": {
  14001. ClientIP: &core.ClientIPConfig{
  14002. TimeoutSeconds: nil,
  14003. },
  14004. },
  14005. "non-empty config, invalid timeout: core.MaxClientIPServiceAffinitySeconds+1": {
  14006. ClientIP: &core.ClientIPConfig{
  14007. TimeoutSeconds: utilpointer.Int32Ptr(core.MaxClientIPServiceAffinitySeconds + 1),
  14008. },
  14009. },
  14010. "non-empty config, invalid timeout: -1": {
  14011. ClientIP: &core.ClientIPConfig{
  14012. TimeoutSeconds: utilpointer.Int32Ptr(-1),
  14013. },
  14014. },
  14015. "non-empty config, invalid timeout: 0": {
  14016. ClientIP: &core.ClientIPConfig{
  14017. TimeoutSeconds: utilpointer.Int32Ptr(0),
  14018. },
  14019. },
  14020. }
  14021. for name, test := range errorCases {
  14022. if errs := validateClientIPAffinityConfig(test, field.NewPath("field")); len(errs) == 0 {
  14023. t.Errorf("case: %v, expected failures: %v", name, errs)
  14024. }
  14025. }
  14026. }
  14027. func TestValidateWindowsSecurityContextOptions(t *testing.T) {
  14028. toPtr := func(s string) *string {
  14029. return &s
  14030. }
  14031. testCases := []struct {
  14032. testName string
  14033. windowsOptions *core.WindowsSecurityContextOptions
  14034. expectedErrorSubstring string
  14035. }{
  14036. {
  14037. testName: "a nil pointer",
  14038. },
  14039. {
  14040. testName: "an empty struct",
  14041. windowsOptions: &core.WindowsSecurityContextOptions{},
  14042. },
  14043. {
  14044. testName: "a valid input",
  14045. windowsOptions: &core.WindowsSecurityContextOptions{
  14046. GMSACredentialSpecName: toPtr("dummy-gmsa-crep-spec-name"),
  14047. GMSACredentialSpec: toPtr("dummy-gmsa-crep-spec-contents"),
  14048. },
  14049. },
  14050. {
  14051. testName: "a GMSA cred spec name that is not a valid resource name",
  14052. windowsOptions: &core.WindowsSecurityContextOptions{
  14053. // invalid because of the underscore
  14054. GMSACredentialSpecName: toPtr("not_a-valid-gmsa-crep-spec-name"),
  14055. },
  14056. expectedErrorSubstring: dnsSubdomainLabelErrMsg,
  14057. },
  14058. {
  14059. testName: "empty GMSA cred spec contents",
  14060. windowsOptions: &core.WindowsSecurityContextOptions{
  14061. GMSACredentialSpec: toPtr(""),
  14062. },
  14063. expectedErrorSubstring: "gmsaCredentialSpec cannot be an empty string",
  14064. },
  14065. {
  14066. testName: "GMSA cred spec contents that are too long",
  14067. windowsOptions: &core.WindowsSecurityContextOptions{
  14068. GMSACredentialSpec: toPtr(strings.Repeat("a", maxGMSACredentialSpecLength+1)),
  14069. },
  14070. expectedErrorSubstring: "gmsaCredentialSpec size must be under",
  14071. },
  14072. {
  14073. testName: "RunAsUserName is nil",
  14074. windowsOptions: &core.WindowsSecurityContextOptions{
  14075. RunAsUserName: nil,
  14076. },
  14077. },
  14078. {
  14079. testName: "a valid RunAsUserName",
  14080. windowsOptions: &core.WindowsSecurityContextOptions{
  14081. RunAsUserName: toPtr("Container. User"),
  14082. },
  14083. },
  14084. {
  14085. testName: "a valid RunAsUserName with NetBios Domain",
  14086. windowsOptions: &core.WindowsSecurityContextOptions{
  14087. RunAsUserName: toPtr("Network Service\\Container. User"),
  14088. },
  14089. },
  14090. {
  14091. testName: "a valid RunAsUserName with DNS Domain",
  14092. windowsOptions: &core.WindowsSecurityContextOptions{
  14093. RunAsUserName: toPtr(strings.Repeat("fOo", 20) + ".liSH\\Container. User"),
  14094. },
  14095. },
  14096. {
  14097. testName: "a valid RunAsUserName with DNS Domain with a single character segment",
  14098. windowsOptions: &core.WindowsSecurityContextOptions{
  14099. RunAsUserName: toPtr(strings.Repeat("fOo", 20) + ".l\\Container. User"),
  14100. },
  14101. },
  14102. {
  14103. testName: "a valid RunAsUserName with a long single segment DNS Domain",
  14104. windowsOptions: &core.WindowsSecurityContextOptions{
  14105. RunAsUserName: toPtr(strings.Repeat("a", 42) + "\\Container. User"),
  14106. },
  14107. },
  14108. {
  14109. testName: "an empty RunAsUserName",
  14110. windowsOptions: &core.WindowsSecurityContextOptions{
  14111. RunAsUserName: toPtr(""),
  14112. },
  14113. expectedErrorSubstring: "runAsUserName cannot be an empty string",
  14114. },
  14115. {
  14116. testName: "RunAsUserName containing a control character",
  14117. windowsOptions: &core.WindowsSecurityContextOptions{
  14118. RunAsUserName: toPtr("Container\tUser"),
  14119. },
  14120. expectedErrorSubstring: "runAsUserName cannot contain control characters",
  14121. },
  14122. {
  14123. testName: "RunAsUserName containing too many backslashes",
  14124. windowsOptions: &core.WindowsSecurityContextOptions{
  14125. RunAsUserName: toPtr("Container\\Foo\\Lish"),
  14126. },
  14127. expectedErrorSubstring: "runAsUserName cannot contain more than one backslash",
  14128. },
  14129. {
  14130. testName: "RunAsUserName containing backslash but empty Domain",
  14131. windowsOptions: &core.WindowsSecurityContextOptions{
  14132. RunAsUserName: toPtr("\\User"),
  14133. },
  14134. expectedErrorSubstring: "runAsUserName's Domain doesn't match the NetBios nor the DNS format",
  14135. },
  14136. {
  14137. testName: "RunAsUserName containing backslash but empty User",
  14138. windowsOptions: &core.WindowsSecurityContextOptions{
  14139. RunAsUserName: toPtr("Container\\"),
  14140. },
  14141. expectedErrorSubstring: "runAsUserName's User cannot be empty",
  14142. },
  14143. {
  14144. testName: "RunAsUserName's NetBios Domain is too long",
  14145. windowsOptions: &core.WindowsSecurityContextOptions{
  14146. RunAsUserName: toPtr("NetBios " + strings.Repeat("a", 8) + "\\user"),
  14147. },
  14148. expectedErrorSubstring: "runAsUserName's Domain doesn't match the NetBios",
  14149. },
  14150. {
  14151. testName: "RunAsUserName's DNS Domain is too long",
  14152. windowsOptions: &core.WindowsSecurityContextOptions{
  14153. // even if this tests the max Domain length, the Domain should still be "valid".
  14154. RunAsUserName: toPtr(strings.Repeat(strings.Repeat("a", 63)+".", 4)[:253] + ".com\\user"),
  14155. },
  14156. expectedErrorSubstring: "runAsUserName's Domain length must be under",
  14157. },
  14158. {
  14159. testName: "RunAsUserName's User is too long",
  14160. windowsOptions: &core.WindowsSecurityContextOptions{
  14161. RunAsUserName: toPtr(strings.Repeat("a", maxRunAsUserNameUserLength+1)),
  14162. },
  14163. expectedErrorSubstring: "runAsUserName's User length must not be longer than",
  14164. },
  14165. {
  14166. testName: "RunAsUserName's User cannot contain only spaces or periods",
  14167. windowsOptions: &core.WindowsSecurityContextOptions{
  14168. RunAsUserName: toPtr("... ..."),
  14169. },
  14170. expectedErrorSubstring: "runAsUserName's User cannot contain only periods or spaces",
  14171. },
  14172. {
  14173. testName: "RunAsUserName's NetBios Domain cannot start with a dot",
  14174. windowsOptions: &core.WindowsSecurityContextOptions{
  14175. RunAsUserName: toPtr(".FooLish\\User"),
  14176. },
  14177. expectedErrorSubstring: "runAsUserName's Domain doesn't match the NetBios",
  14178. },
  14179. {
  14180. testName: "RunAsUserName's NetBios Domain cannot contain invalid characters",
  14181. windowsOptions: &core.WindowsSecurityContextOptions{
  14182. RunAsUserName: toPtr("Foo? Lish?\\User"),
  14183. },
  14184. expectedErrorSubstring: "runAsUserName's Domain doesn't match the NetBios",
  14185. },
  14186. {
  14187. testName: "RunAsUserName's DNS Domain cannot contain invalid characters",
  14188. windowsOptions: &core.WindowsSecurityContextOptions{
  14189. RunAsUserName: toPtr(strings.Repeat("a", 32) + ".com-\\user"),
  14190. },
  14191. expectedErrorSubstring: "runAsUserName's Domain doesn't match the NetBios nor the DNS format",
  14192. },
  14193. {
  14194. testName: "RunAsUserName's User cannot contain invalid characters",
  14195. windowsOptions: &core.WindowsSecurityContextOptions{
  14196. RunAsUserName: toPtr("Container/User"),
  14197. },
  14198. expectedErrorSubstring: "runAsUserName's User cannot contain the following characters",
  14199. },
  14200. }
  14201. for _, testCase := range testCases {
  14202. t.Run("validateWindowsSecurityContextOptions with"+testCase.testName, func(t *testing.T) {
  14203. errs := validateWindowsSecurityContextOptions(testCase.windowsOptions, field.NewPath("field"))
  14204. switch len(errs) {
  14205. case 0:
  14206. if testCase.expectedErrorSubstring != "" {
  14207. t.Errorf("expected a failure containing the substring: %q", testCase.expectedErrorSubstring)
  14208. }
  14209. case 1:
  14210. if testCase.expectedErrorSubstring == "" {
  14211. t.Errorf("didn't expect a failure, got: %q", errs[0].Error())
  14212. } else if !strings.Contains(errs[0].Error(), testCase.expectedErrorSubstring) {
  14213. t.Errorf("expected a failure with the substring %q, got %q instead", testCase.expectedErrorSubstring, errs[0].Error())
  14214. }
  14215. default:
  14216. t.Errorf("got %d failures", len(errs))
  14217. for i, err := range errs {
  14218. t.Errorf("error %d: %q", i, err.Error())
  14219. }
  14220. }
  14221. })
  14222. }
  14223. }
  14224. func testDataSourceInSpec(name string, kind string, apiGroup string) *core.PersistentVolumeClaimSpec {
  14225. scName := "csi-plugin"
  14226. dataSourceInSpec := core.PersistentVolumeClaimSpec{
  14227. AccessModes: []core.PersistentVolumeAccessMode{
  14228. core.ReadOnlyMany,
  14229. },
  14230. Resources: core.ResourceRequirements{
  14231. Requests: core.ResourceList{
  14232. core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
  14233. },
  14234. },
  14235. StorageClassName: &scName,
  14236. DataSource: &core.TypedLocalObjectReference{
  14237. APIGroup: &apiGroup,
  14238. Kind: kind,
  14239. Name: name,
  14240. },
  14241. }
  14242. return &dataSourceInSpec
  14243. }
  14244. func TestAlphaVolumePVCDataSource(t *testing.T) {
  14245. testCases := []struct {
  14246. testName string
  14247. claimSpec core.PersistentVolumeClaimSpec
  14248. expectedFail bool
  14249. }{
  14250. {
  14251. testName: "test create from valid snapshot source",
  14252. claimSpec: *testDataSourceInSpec("test_snapshot", "VolumeSnapshot", "snapshot.storage.k8s.io"),
  14253. },
  14254. {
  14255. testName: "test create from valid pvc source",
  14256. claimSpec: *testDataSourceInSpec("test_pvc", "PersistentVolumeClaim", ""),
  14257. },
  14258. {
  14259. testName: "test missing name in snapshot datasource should fail",
  14260. claimSpec: *testDataSourceInSpec("", "VolumeSnapshot", "snapshot.storage.k8s.io"),
  14261. expectedFail: true,
  14262. },
  14263. {
  14264. testName: "test specifying pvc with snapshot api group should fail",
  14265. claimSpec: *testDataSourceInSpec("test_snapshot", "PersistentVolumeClaim", "snapshot.storage.k8s.io"),
  14266. expectedFail: true,
  14267. },
  14268. {
  14269. testName: "test invalid group name in snapshot datasource should fail",
  14270. claimSpec: *testDataSourceInSpec("test_snapshot", "VolumeSnapshot", "storage.k8s.io"),
  14271. expectedFail: true,
  14272. },
  14273. }
  14274. for _, tc := range testCases {
  14275. if tc.expectedFail {
  14276. if errs := ValidatePersistentVolumeClaimSpec(&tc.claimSpec, field.NewPath("spec")); len(errs) == 0 {
  14277. t.Errorf("expected failure: %v", errs)
  14278. }
  14279. } else {
  14280. if errs := ValidatePersistentVolumeClaimSpec(&tc.claimSpec, field.NewPath("spec")); len(errs) != 0 {
  14281. t.Errorf("expected success: %v", errs)
  14282. }
  14283. }
  14284. }
  14285. }
  14286. func TestValidateTopologySpreadConstraints(t *testing.T) {
  14287. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EvenPodsSpread, true)()
  14288. testCases := []struct {
  14289. name string
  14290. constraints []core.TopologySpreadConstraint
  14291. errtype field.ErrorType
  14292. errfield string
  14293. }{
  14294. {
  14295. name: "all required fields ok",
  14296. constraints: []core.TopologySpreadConstraint{
  14297. {MaxSkew: 1, TopologyKey: "k8s.io/zone", WhenUnsatisfiable: core.DoNotSchedule},
  14298. },
  14299. },
  14300. {
  14301. name: "missing MaxSkew",
  14302. constraints: []core.TopologySpreadConstraint{
  14303. {TopologyKey: "k8s.io/zone", WhenUnsatisfiable: core.DoNotSchedule},
  14304. },
  14305. errtype: field.ErrorTypeInvalid,
  14306. errfield: "maxSkew",
  14307. },
  14308. {
  14309. name: "invalid MaxSkew",
  14310. constraints: []core.TopologySpreadConstraint{
  14311. {MaxSkew: 0, TopologyKey: "k8s.io/zone", WhenUnsatisfiable: core.DoNotSchedule},
  14312. },
  14313. errtype: field.ErrorTypeInvalid,
  14314. errfield: "maxSkew",
  14315. },
  14316. {
  14317. name: "missing TopologyKey",
  14318. constraints: []core.TopologySpreadConstraint{
  14319. {MaxSkew: 1, WhenUnsatisfiable: core.DoNotSchedule},
  14320. },
  14321. errtype: field.ErrorTypeRequired,
  14322. errfield: "topologyKey",
  14323. },
  14324. {
  14325. name: "missing scheduling mode",
  14326. constraints: []core.TopologySpreadConstraint{
  14327. {MaxSkew: 1, TopologyKey: "k8s.io/zone"},
  14328. },
  14329. errtype: field.ErrorTypeNotSupported,
  14330. errfield: "whenUnsatisfiable",
  14331. },
  14332. {
  14333. name: "unsupported scheduling mode",
  14334. constraints: []core.TopologySpreadConstraint{
  14335. {MaxSkew: 1, TopologyKey: "k8s.io/zone", WhenUnsatisfiable: core.UnsatisfiableConstraintAction("N/A")},
  14336. },
  14337. errtype: field.ErrorTypeNotSupported,
  14338. errfield: "whenUnsatisfiable",
  14339. },
  14340. {
  14341. name: "multiple constraints ok with all required fields",
  14342. constraints: []core.TopologySpreadConstraint{
  14343. {MaxSkew: 1, TopologyKey: "k8s.io/zone", WhenUnsatisfiable: core.DoNotSchedule},
  14344. {MaxSkew: 2, TopologyKey: "k8s.io/node", WhenUnsatisfiable: core.ScheduleAnyway},
  14345. },
  14346. },
  14347. {
  14348. name: "multiple constraints missing TopologyKey on partial ones",
  14349. constraints: []core.TopologySpreadConstraint{
  14350. {MaxSkew: 1, TopologyKey: "k8s.io/zone", WhenUnsatisfiable: core.DoNotSchedule},
  14351. {MaxSkew: 2, WhenUnsatisfiable: core.ScheduleAnyway},
  14352. },
  14353. errtype: field.ErrorTypeRequired,
  14354. errfield: "topologyKey",
  14355. },
  14356. {
  14357. name: "duplicate constraints",
  14358. constraints: []core.TopologySpreadConstraint{
  14359. {MaxSkew: 1, TopologyKey: "k8s.io/zone", WhenUnsatisfiable: core.DoNotSchedule},
  14360. {MaxSkew: 2, TopologyKey: "k8s.io/zone", WhenUnsatisfiable: core.DoNotSchedule},
  14361. },
  14362. errtype: field.ErrorTypeDuplicate,
  14363. errfield: "{topologyKey, whenUnsatisfiable}",
  14364. },
  14365. }
  14366. for i, tc := range testCases {
  14367. errs := validateTopologySpreadConstraints(tc.constraints, field.NewPath("field"))
  14368. if len(errs) > 0 && tc.errtype == "" {
  14369. t.Errorf("[%d: %q] unexpected error(s): %v", i, tc.name, errs)
  14370. } else if len(errs) == 0 && tc.errtype != "" {
  14371. t.Errorf("[%d: %q] expected error type %v", i, tc.name, tc.errtype)
  14372. } else if len(errs) >= 1 {
  14373. if errs[0].Type != tc.errtype {
  14374. t.Errorf("[%d: %q] expected error type %v, got %v", i, tc.name, tc.errtype, errs[0].Type)
  14375. } else if !strings.HasSuffix(errs[0].Field, "."+tc.errfield) {
  14376. t.Errorf("[%d: %q] expected error on field %q, got %q", i, tc.name, tc.errfield, errs[0].Field)
  14377. }
  14378. }
  14379. }
  14380. }
  14381. func TestValidateOverhead(t *testing.T) {
  14382. successCase := []struct {
  14383. Name string
  14384. overhead core.ResourceList
  14385. }{
  14386. {
  14387. Name: "Valid Overhead for CPU + Memory",
  14388. overhead: core.ResourceList{
  14389. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  14390. core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
  14391. },
  14392. },
  14393. }
  14394. for _, tc := range successCase {
  14395. if errs := validateOverhead(tc.overhead, field.NewPath("overheads")); len(errs) != 0 {
  14396. t.Errorf("%q unexpected error: %v", tc.Name, errs)
  14397. }
  14398. }
  14399. errorCase := []struct {
  14400. Name string
  14401. overhead core.ResourceList
  14402. }{
  14403. {
  14404. Name: "Invalid Overhead Resources",
  14405. overhead: core.ResourceList{
  14406. core.ResourceName("my.org"): resource.MustParse("10m"),
  14407. },
  14408. },
  14409. }
  14410. for _, tc := range errorCase {
  14411. if errs := validateOverhead(tc.overhead, field.NewPath("resources")); len(errs) == 0 {
  14412. t.Errorf("%q expected error", tc.Name)
  14413. }
  14414. }
  14415. }
  14416. // helper creates a pod with name, namespace and IPs
  14417. func makePod(podName string, podNamespace string, podIPs []core.PodIP) core.Pod {
  14418. return core.Pod{
  14419. ObjectMeta: metav1.ObjectMeta{Name: podName, Namespace: podNamespace},
  14420. Spec: core.PodSpec{
  14421. Containers: []core.Container{
  14422. {
  14423. Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
  14424. },
  14425. },
  14426. RestartPolicy: core.RestartPolicyAlways,
  14427. DNSPolicy: core.DNSClusterFirst,
  14428. },
  14429. Status: core.PodStatus{
  14430. PodIPs: podIPs,
  14431. },
  14432. }
  14433. }
  14434. func TestPodIPsValidation(t *testing.T) {
  14435. testCases := []struct {
  14436. pod core.Pod
  14437. expectError bool
  14438. }{
  14439. {
  14440. expectError: false,
  14441. pod: makePod("nil-ips", "ns", nil),
  14442. },
  14443. {
  14444. expectError: false,
  14445. pod: makePod("empty-podips-list", "ns", []core.PodIP{}),
  14446. },
  14447. {
  14448. expectError: false,
  14449. pod: makePod("single-ip-family-6", "ns", []core.PodIP{{IP: "::1"}}),
  14450. },
  14451. {
  14452. expectError: false,
  14453. pod: makePod("single-ip-family-4", "ns", []core.PodIP{{IP: "1.1.1.1"}}),
  14454. },
  14455. {
  14456. expectError: false,
  14457. pod: makePod("dual-stack-4-6", "ns", []core.PodIP{{IP: "1.1.1.1"}, {IP: "::1"}}),
  14458. },
  14459. {
  14460. expectError: false,
  14461. pod: makePod("dual-stack-6-4", "ns", []core.PodIP{{IP: "::1"}, {IP: "1.1.1.1"}}),
  14462. },
  14463. /* failure cases start here */
  14464. {
  14465. expectError: true,
  14466. pod: makePod("invalid-pod-ip", "ns", []core.PodIP{{IP: "this-is-not-an-ip"}}),
  14467. },
  14468. {
  14469. expectError: true,
  14470. pod: makePod("dualstack-same-ip-family-6", "ns", []core.PodIP{{IP: "::1"}, {IP: "::2"}}),
  14471. },
  14472. {
  14473. expectError: true,
  14474. pod: makePod("dualstack-same-ip-family-4", "ns", []core.PodIP{{IP: "1.1.1.1"}, {IP: "2.2.2.2"}}),
  14475. },
  14476. {
  14477. expectError: true,
  14478. pod: makePod("dualstack-repeated-ip-family-6", "ns", []core.PodIP{{IP: "1.1.1.1"}, {IP: "::1"}, {IP: "::2"}}),
  14479. },
  14480. {
  14481. expectError: true,
  14482. pod: makePod("dualstack-repeated-ip-family-4", "ns", []core.PodIP{{IP: "1.1.1.1"}, {IP: "::1"}, {IP: "2.2.2.2"}}),
  14483. },
  14484. {
  14485. expectError: true,
  14486. pod: makePod("dualstack-duplicate-ip-family-4", "ns", []core.PodIP{{IP: "1.1.1.1"}, {IP: "1.1.1.1"}, {IP: "::1"}}),
  14487. },
  14488. {
  14489. expectError: true,
  14490. pod: makePod("dualstack-duplicate-ip-family-6", "ns", []core.PodIP{{IP: "1.1.1.1"}, {IP: "::1"}, {IP: "::1"}}),
  14491. },
  14492. }
  14493. for _, testCase := range testCases {
  14494. errs := ValidatePod(&testCase.pod)
  14495. if len(errs) == 0 && testCase.expectError {
  14496. t.Errorf("expected failure for %s, but there were none", testCase.pod.Name)
  14497. return
  14498. }
  14499. if len(errs) != 0 && !testCase.expectError {
  14500. t.Errorf("expected success for %s, but there were errors: %v", testCase.pod.Name, errs)
  14501. return
  14502. }
  14503. }
  14504. }
  14505. // makes a node with pod cidr and a name
  14506. func makeNode(nodeName string, podCIDRs []string) core.Node {
  14507. return core.Node{
  14508. ObjectMeta: metav1.ObjectMeta{
  14509. Name: nodeName,
  14510. },
  14511. Status: core.NodeStatus{
  14512. Addresses: []core.NodeAddress{
  14513. {Type: core.NodeExternalIP, Address: "something"},
  14514. },
  14515. Capacity: core.ResourceList{
  14516. core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
  14517. core.ResourceName(core.ResourceMemory): resource.MustParse("0"),
  14518. },
  14519. },
  14520. Spec: core.NodeSpec{
  14521. PodCIDRs: podCIDRs,
  14522. },
  14523. }
  14524. }
  14525. func TestValidateNodeCIDRs(t *testing.T) {
  14526. opts := NodeValidationOptions{
  14527. ValidateSingleHugePageResource: true,
  14528. }
  14529. testCases := []struct {
  14530. expectError bool
  14531. node core.Node
  14532. }{
  14533. {
  14534. expectError: false,
  14535. node: makeNode("nil-pod-cidr", nil),
  14536. },
  14537. {
  14538. expectError: false,
  14539. node: makeNode("empty-pod-cidr", []string{}),
  14540. },
  14541. {
  14542. expectError: false,
  14543. node: makeNode("single-pod-cidr-4", []string{"192.168.0.0/16"}),
  14544. },
  14545. {
  14546. expectError: false,
  14547. node: makeNode("single-pod-cidr-6", []string{"2000::/10"}),
  14548. },
  14549. {
  14550. expectError: false,
  14551. node: makeNode("multi-pod-cidr-6-4", []string{"2000::/10", "192.168.0.0/16"}),
  14552. },
  14553. {
  14554. expectError: false,
  14555. node: makeNode("multi-pod-cidr-4-6", []string{"192.168.0.0/16", "2000::/10"}),
  14556. },
  14557. // error cases starts here
  14558. {
  14559. expectError: true,
  14560. node: makeNode("invalid-pod-cidr", []string{"this-is-not-a-valid-cidr"}),
  14561. },
  14562. {
  14563. expectError: true,
  14564. node: makeNode("duplicate-pod-cidr-4", []string{"10.0.0.1/16", "10.0.0.1/16"}),
  14565. },
  14566. {
  14567. expectError: true,
  14568. node: makeNode("duplicate-pod-cidr-6", []string{"2000::/10", "2000::/10"}),
  14569. },
  14570. {
  14571. expectError: true,
  14572. node: makeNode("not-a-dualstack-no-v4", []string{"2000::/10", "3000::/10"}),
  14573. },
  14574. {
  14575. expectError: true,
  14576. node: makeNode("not-a-dualstack-no-v6", []string{"10.0.0.0/16", "10.1.0.0/16"}),
  14577. },
  14578. {
  14579. expectError: true,
  14580. node: makeNode("not-a-dualstack-repeated-v6", []string{"2000::/10", "10.0.0.0/16", "3000::/10"}),
  14581. },
  14582. {
  14583. expectError: true,
  14584. node: makeNode("not-a-dualstack-repeated-v4", []string{"10.0.0.0/16", "3000::/10", "10.1.0.0/16"}),
  14585. },
  14586. }
  14587. for _, testCase := range testCases {
  14588. errs := ValidateNode(&testCase.node, opts)
  14589. if len(errs) == 0 && testCase.expectError {
  14590. t.Errorf("expected failure for %s, but there were none", testCase.node.Name)
  14591. return
  14592. }
  14593. if len(errs) != 0 && !testCase.expectError {
  14594. t.Errorf("expected success for %s, but there were errors: %v", testCase.node.Name, errs)
  14595. return
  14596. }
  14597. }
  14598. }