1 Star 0 Fork 58

gnaygnil/docker

forked from src-openEuler/docker 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
0067-pause-fix-build-missing-dep-packages.patch 172.31 KB
一键复制 编辑 原始数据 按行查看 历史
gnaygnil 提交于 2020-02-13 15:36 . docker: Fixed build error and URL
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759
From 962e669ab1b1d3545faca5b668ca9d1be0d3b786 Mon Sep 17 00:00:00 2001
From: jingrui <jingrui@huawei.com>
Date: Mon, 21 Jan 2019 21:25:33 +0800
Subject: [PATCH 067/111] pause: fix build missing dep packages
reason: update vendor for libcontainer/cgroup dependency.
checkout from runc-1.0.0:
eff62015 runc: support specify umask
- coreos/go-systemd/util
- opencontainers/runc/libcontainer/cgroups/fs
- opencontainers/runc/libcontainer/cgroups/systemd
- opencontainers/runc/libcontainer/utils
files modified support FilesLimit:
- opencontainers/runc/libcontainer/cgroups/stats.go
- opencontainers/runc/libcontainer/configs/cgroup_linux.go
Change-Id: I794f0bf9be87c6068788f866555c346ca2372c02
Signed-off-by: jingrui <jingrui@huawei.com>
---
.../github.com/coreos/go-systemd/Checklist | 2 +
.../github.com/coreos/go-systemd/util/util.go | 33 +
.../runc/libcontainer/Checklist | 4 +
.../runc/libcontainer/cgroups/fs/apply_raw.go | 361 ++++++++++
.../libcontainer/cgroups/fs/apply_raw_test.go | 272 ++++++++
.../runc/libcontainer/cgroups/fs/blkio.go | 237 +++++++
.../libcontainer/cgroups/fs/blkio_test.go | 636 ++++++++++++++++++
.../runc/libcontainer/cgroups/fs/cpu.go | 125 ++++
.../runc/libcontainer/cgroups/fs/cpu_test.go | 209 ++++++
.../runc/libcontainer/cgroups/fs/cpuacct.go | 121 ++++
.../runc/libcontainer/cgroups/fs/cpuset.go | 183 +++++
.../libcontainer/cgroups/fs/cpuset_test.go | 65 ++
.../runc/libcontainer/cgroups/fs/devices.go | 80 +++
.../libcontainer/cgroups/fs/devices_test.go | 98 +++
.../runc/libcontainer/cgroups/fs/files.go | 72 ++
.../runc/libcontainer/cgroups/fs/freezer.go | 61 ++
.../libcontainer/cgroups/fs/freezer_test.go | 47 ++
.../libcontainer/cgroups/fs/fs_unsupported.go | 3 +
.../runc/libcontainer/cgroups/fs/hugetlb.go | 71 ++
.../libcontainer/cgroups/fs/hugetlb_test.go | 154 +++++
.../runc/libcontainer/cgroups/fs/memory.go | 301 +++++++++
.../libcontainer/cgroups/fs/memory_test.go | 453 +++++++++++++
.../runc/libcontainer/cgroups/fs/name.go | 40 ++
.../runc/libcontainer/cgroups/fs/net_cls.go | 43 ++
.../libcontainer/cgroups/fs/net_cls_test.go | 39 ++
.../runc/libcontainer/cgroups/fs/net_prio.go | 41 ++
.../libcontainer/cgroups/fs/net_prio_test.go | 38 ++
.../libcontainer/cgroups/fs/perf_event.go | 35 +
.../runc/libcontainer/cgroups/fs/pids.go | 73 ++
.../runc/libcontainer/cgroups/fs/pids_test.go | 111 +++
.../cgroups/fs/stats_util_test.go | 117 ++++
.../runc/libcontainer/cgroups/fs/util_test.go | 67 ++
.../runc/libcontainer/cgroups/fs/utils.go | 78 +++
.../libcontainer/cgroups/fs/utils_test.go | 97 +++
.../libcontainer/cgroups/rootless/rootless.go | 128 ++++
.../runc/libcontainer/cgroups/stats.go | 8 +
.../cgroups/systemd/apply_nosystemd.go | 55 ++
.../cgroups/systemd/apply_systemd.go | 556 +++++++++++++++
.../runc/libcontainer/configs/cgroup_linux.go | 3 +
.../runc/libcontainer/utils/cmsg.go | 95 +++
.../runc/libcontainer/utils/utils.go | 126 ++++
.../runc/libcontainer/utils/utils_unix.go | 43 ++
42 files changed, 5381 insertions(+)
create mode 100644 components/engine/vendor/github.com/coreos/go-systemd/Checklist
create mode 100644 components/engine/vendor/github.com/coreos/go-systemd/util/util.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/Checklist
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/apply_raw.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/apply_raw_test.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/blkio.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/blkio_test.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpu.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpu_test.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuacct.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuset.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuset_test.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/devices.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/devices_test.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/files.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/freezer.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/freezer_test.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/fs_unsupported.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/hugetlb.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/hugetlb_test.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/memory.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/memory_test.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/name.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/net_cls.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/net_cls_test.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/net_prio.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/net_prio_test.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/perf_event.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/pids.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/pids_test.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/stats_util_test.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/util_test.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/utils.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/utils_test.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/rootless/rootless.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/systemd/apply_nosystemd.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/systemd/apply_systemd.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/utils/cmsg.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/utils/utils.go
create mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/utils/utils_unix.go
diff --git a/components/engine/vendor/github.com/coreos/go-systemd/Checklist b/components/engine/vendor/github.com/coreos/go-systemd/Checklist
new file mode 100644
index 0000000000..c231fc1636
--- /dev/null
+++ b/components/engine/vendor/github.com/coreos/go-systemd/Checklist
@@ -0,0 +1,2 @@
+Add these packages from go-systemd v4 for moving Pause handling from runc to dockerd
+- github.com/coreos/go-systemd/util
\ No newline at end of file
diff --git a/components/engine/vendor/github.com/coreos/go-systemd/util/util.go b/components/engine/vendor/github.com/coreos/go-systemd/util/util.go
new file mode 100644
index 0000000000..33832a1ed4
--- /dev/null
+++ b/components/engine/vendor/github.com/coreos/go-systemd/util/util.go
@@ -0,0 +1,33 @@
+// Copyright 2015 CoreOS, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package util contains utility functions related to systemd that applications
+// can use to check things like whether systemd is running.
+package util
+
+import (
+ "os"
+)
+
+// IsRunningSystemd checks whether the host was booted with systemd as its init
+// system. This functions similar to systemd's `sd_booted(3)`: internally, it
+// checks whether /run/systemd/system/ exists and is a directory.
+// http://www.freedesktop.org/software/systemd/man/sd_booted.html
+func IsRunningSystemd() bool {
+ fi, err := os.Lstat("/run/systemd/system")
+ if err != nil {
+ return false
+ }
+ return fi.IsDir()
+}
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/Checklist b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/Checklist
new file mode 100644
index 0000000000..d0900fc618
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/Checklist
@@ -0,0 +1,4 @@
+Add these packages for moving Pause handling from runc to dockerd
+- github.com/opencontainers/runc/libcontainer/cgroups/fs
+- github.com/opencontainers/runc/libcontainer/cgroups/systemd
+- github.com/opencontainers/runc/libcontainer/utils
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/apply_raw.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/apply_raw.go
new file mode 100644
index 0000000000..1bf59a47be
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/apply_raw.go
@@ -0,0 +1,361 @@
+// +build linux
+
+package fs
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "sync"
+
+ "github.com/opencontainers/runc/libcontainer/cgroups"
+ "github.com/opencontainers/runc/libcontainer/configs"
+ libcontainerUtils "github.com/opencontainers/runc/libcontainer/utils"
+)
+
+var (
+ subsystems = subsystemSet{
+ &CpusetGroup{},
+ &DevicesGroup{},
+ &MemoryGroup{},
+ &CpuGroup{},
+ &CpuacctGroup{},
+ &PidsGroup{},
+ &FilesGroup{},
+ &BlkioGroup{},
+ &HugetlbGroup{},
+ &NetClsGroup{},
+ &NetPrioGroup{},
+ &PerfEventGroup{},
+ &FreezerGroup{},
+ &NameGroup{GroupName: "name=systemd", Join: true},
+ }
+ HugePageSizes, _ = cgroups.GetHugePageSize()
+)
+
+var errSubsystemDoesNotExist = errors.New("cgroup: subsystem does not exist")
+
+type subsystemSet []subsystem
+
+func (s subsystemSet) Get(name string) (subsystem, error) {
+ for _, ss := range s {
+ if ss.Name() == name {
+ return ss, nil
+ }
+ }
+ return nil, errSubsystemDoesNotExist
+}
+
+type subsystem interface {
+ // Name returns the name of the subsystem.
+ Name() string
+ // Returns the stats, as 'stats', corresponding to the cgroup under 'path'.
+ GetStats(path string, stats *cgroups.Stats) error
+ // Removes the cgroup represented by 'cgroupData'.
+ Remove(*cgroupData) error
+ // Creates and joins the cgroup represented by 'cgroupData'.
+ Apply(*cgroupData) error
+ // Set the cgroup represented by cgroup.
+ Set(path string, cgroup *configs.Cgroup) error
+}
+
+type Manager struct {
+ mu sync.Mutex
+ Cgroups *configs.Cgroup
+ Paths map[string]string
+}
+
+// The absolute path to the root of the cgroup hierarchies.
+var cgroupRootLock sync.Mutex
+var cgroupRoot string
+
+// Gets the cgroupRoot.
+func getCgroupRoot() (string, error) {
+ cgroupRootLock.Lock()
+ defer cgroupRootLock.Unlock()
+
+ if cgroupRoot != "" {
+ return cgroupRoot, nil
+ }
+
+ root, err := cgroups.FindCgroupMountpointDir()
+ if err != nil {
+ return "", err
+ }
+
+ if _, err := os.Stat(root); err != nil {
+ return "", err
+ }
+
+ cgroupRoot = root
+ return cgroupRoot, nil
+}
+
+type cgroupData struct {
+ root string
+ innerPath string
+ config *configs.Cgroup
+ pid int
+}
+
+func (m *Manager) Apply(pid int) (err error) {
+ if m.Cgroups == nil {
+ return nil
+ }
+ m.mu.Lock()
+ defer m.mu.Unlock()
+
+ var c = m.Cgroups
+
+ d, err := getCgroupData(m.Cgroups, pid)
+ if err != nil {
+ return err
+ }
+
+ m.Paths = make(map[string]string)
+ if c.Paths != nil {
+ for name, path := range c.Paths {
+ _, err := d.path(name)
+ if err != nil {
+ if cgroups.IsNotFound(err) {
+ continue
+ }
+ return err
+ }
+ m.Paths[name] = path
+ }
+ return cgroups.EnterPid(m.Paths, pid)
+ }
+
+ for _, sys := range subsystems {
+ // TODO: Apply should, ideally, be reentrant or be broken up into a separate
+ // create and join phase so that the cgroup hierarchy for a container can be
+ // created then join consists of writing the process pids to cgroup.procs
+ p, err := d.path(sys.Name())
+ if err != nil {
+ // The non-presence of the devices subsystem is
+ // considered fatal for security reasons.
+ if cgroups.IsNotFound(err) && sys.Name() != "devices" {
+ continue
+ }
+ return err
+ }
+ m.Paths[sys.Name()] = p
+
+ if err := sys.Apply(d); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (m *Manager) Destroy() error {
+ if m.Cgroups == nil || m.Cgroups.Paths != nil {
+ return nil
+ }
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ if err := cgroups.RemovePaths(m.Paths); err != nil {
+ return err
+ }
+ m.Paths = make(map[string]string)
+ return nil
+}
+
+func (m *Manager) GetPaths() map[string]string {
+ m.mu.Lock()
+ paths := m.Paths
+ m.mu.Unlock()
+ return paths
+}
+
+func (m *Manager) GetStats() (*cgroups.Stats, error) {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ stats := cgroups.NewStats()
+ for name, path := range m.Paths {
+ sys, err := subsystems.Get(name)
+ if err == errSubsystemDoesNotExist || !cgroups.PathExists(path) {
+ continue
+ }
+ if err := sys.GetStats(path, stats); err != nil {
+ return nil, err
+ }
+ }
+ return stats, nil
+}
+
+func (m *Manager) Set(container *configs.Config) error {
+ // If Paths are set, then we are just joining cgroups paths
+ // and there is no need to set any values.
+ if m.Cgroups.Paths != nil {
+ return nil
+ }
+
+ paths := m.GetPaths()
+ for _, sys := range subsystems {
+ path := paths[sys.Name()]
+ if err := sys.Set(path, container.Cgroups); err != nil {
+ return err
+ }
+ }
+
+ if m.Paths["cpu"] != "" {
+ if err := CheckCpushares(m.Paths["cpu"], container.Cgroups.Resources.CpuShares); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// Freeze toggles the container's freezer cgroup depending on the state
+// provided
+func (m *Manager) Freeze(state configs.FreezerState) error {
+ paths := m.GetPaths()
+ dir := paths["freezer"]
+ prevState := m.Cgroups.Resources.Freezer
+ m.Cgroups.Resources.Freezer = state
+ freezer, err := subsystems.Get("freezer")
+ if err != nil {
+ return err
+ }
+ err = freezer.Set(dir, m.Cgroups)
+ if err != nil {
+ m.Cgroups.Resources.Freezer = prevState
+ return err
+ }
+ return nil
+}
+
+func (m *Manager) GetPids() ([]int, error) {
+ paths := m.GetPaths()
+ return cgroups.GetPids(paths["devices"])
+}
+
+func (m *Manager) GetAllPids() ([]int, error) {
+ paths := m.GetPaths()
+ return cgroups.GetAllPids(paths["devices"])
+}
+
+func getCgroupData(c *configs.Cgroup, pid int) (*cgroupData, error) {
+ root, err := getCgroupRoot()
+ if err != nil {
+ return nil, err
+ }
+
+ if (c.Name != "" || c.Parent != "") && c.Path != "" {
+ return nil, fmt.Errorf("cgroup: either Path or Name and Parent should be used")
+ }
+
+ // XXX: Do not remove this code. Path safety is important! -- cyphar
+ cgPath := libcontainerUtils.CleanPath(c.Path)
+ cgParent := libcontainerUtils.CleanPath(c.Parent)
+ cgName := libcontainerUtils.CleanPath(c.Name)
+
+ innerPath := cgPath
+ if innerPath == "" {
+ innerPath = filepath.Join(cgParent, cgName)
+ }
+
+ return &cgroupData{
+ root: root,
+ innerPath: innerPath,
+ config: c,
+ pid: pid,
+ }, nil
+}
+
+func (raw *cgroupData) path(subsystem string) (string, error) {
+ mnt, err := cgroups.FindCgroupMountpoint(subsystem)
+ // If we didn't mount the subsystem, there is no point we make the path.
+ if err != nil {
+ return "", err
+ }
+
+ // If the cgroup name/path is absolute do not look relative to the cgroup of the init process.
+ if filepath.IsAbs(raw.innerPath) {
+ // Sometimes subsystems can be mounted together as 'cpu,cpuacct'.
+ return filepath.Join(raw.root, filepath.Base(mnt), raw.innerPath), nil
+ }
+
+ // Use GetOwnCgroupPath instead of GetInitCgroupPath, because the creating
+ // process could in container and shared pid namespace with host, and
+ // /proc/1/cgroup could point to whole other world of cgroups.
+ parentPath, err := cgroups.GetOwnCgroupPath(subsystem)
+ if err != nil {
+ return "", err
+ }
+
+ return filepath.Join(parentPath, raw.innerPath), nil
+}
+
+func (raw *cgroupData) join(subsystem string) (string, error) {
+ path, err := raw.path(subsystem)
+ if err != nil {
+ return "", err
+ }
+ if err := os.MkdirAll(path, 0755); err != nil {
+ return "", err
+ }
+ if err := cgroups.WriteCgroupProc(path, raw.pid); err != nil {
+ return "", err
+ }
+ return path, nil
+}
+
+func writeFile(dir, file, data string) error {
+ // Normally dir should not be empty, one case is that cgroup subsystem
+ // is not mounted, we will get empty dir, and we want it fail here.
+ if dir == "" {
+ return fmt.Errorf("no such directory for %s", file)
+ }
+ if err := ioutil.WriteFile(filepath.Join(dir, file), []byte(data), 0700); err != nil {
+ return fmt.Errorf("failed to write %v to %v: %v", data, file, err)
+ }
+ return nil
+}
+
+func readFile(dir, file string) (string, error) {
+ data, err := ioutil.ReadFile(filepath.Join(dir, file))
+ return string(data), err
+}
+
+func removePath(p string, err error) error {
+ if err != nil {
+ return err
+ }
+ if p != "" {
+ return os.RemoveAll(p)
+ }
+ return nil
+}
+
+func CheckCpushares(path string, c uint64) error {
+ var cpuShares uint64
+
+ if c == 0 {
+ return nil
+ }
+
+ fd, err := os.Open(filepath.Join(path, "cpu.shares"))
+ if err != nil {
+ return err
+ }
+ defer fd.Close()
+
+ _, err = fmt.Fscanf(fd, "%d", &cpuShares)
+ if err != nil && err != io.EOF {
+ return err
+ }
+
+ if c > cpuShares {
+ return fmt.Errorf("The maximum allowed cpu-shares is %d", cpuShares)
+ } else if c < cpuShares {
+ return fmt.Errorf("The minimum allowed cpu-shares is %d", cpuShares)
+ }
+
+ return nil
+}
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/apply_raw_test.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/apply_raw_test.go
new file mode 100644
index 0000000000..ba4e9e543c
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/apply_raw_test.go
@@ -0,0 +1,272 @@
+// +build linux
+
+package fs
+
+import (
+ "path/filepath"
+ "strings"
+ "testing"
+
+ "github.com/opencontainers/runc/libcontainer/configs"
+)
+
+func TestInvalidCgroupPath(t *testing.T) {
+ root, err := getCgroupRoot()
+ if err != nil {
+ t.Errorf("couldn't get cgroup root: %v", err)
+ }
+
+ config := &configs.Cgroup{
+ Path: "../../../../../../../../../../some/path",
+ }
+
+ data, err := getCgroupData(config, 0)
+ if err != nil {
+ t.Errorf("couldn't get cgroup data: %v", err)
+ }
+
+ // Make sure the final innerPath doesn't go outside the cgroup mountpoint.
+ if strings.HasPrefix(data.innerPath, "..") {
+ t.Errorf("SECURITY: cgroup innerPath is outside cgroup mountpoint!")
+ }
+
+ // Double-check, using an actual cgroup.
+ deviceRoot := filepath.Join(root, "devices")
+ devicePath, err := data.path("devices")
+ if err != nil {
+ t.Errorf("couldn't get cgroup path: %v", err)
+ }
+ if !strings.HasPrefix(devicePath, deviceRoot) {
+ t.Errorf("SECURITY: cgroup path() is outside cgroup mountpoint!")
+ }
+}
+
+func TestInvalidAbsoluteCgroupPath(t *testing.T) {
+ root, err := getCgroupRoot()
+ if err != nil {
+ t.Errorf("couldn't get cgroup root: %v", err)
+ }
+
+ config := &configs.Cgroup{
+ Path: "/../../../../../../../../../../some/path",
+ }
+
+ data, err := getCgroupData(config, 0)
+ if err != nil {
+ t.Errorf("couldn't get cgroup data: %v", err)
+ }
+
+ // Make sure the final innerPath doesn't go outside the cgroup mountpoint.
+ if strings.HasPrefix(data.innerPath, "..") {
+ t.Errorf("SECURITY: cgroup innerPath is outside cgroup mountpoint!")
+ }
+
+ // Double-check, using an actual cgroup.
+ deviceRoot := filepath.Join(root, "devices")
+ devicePath, err := data.path("devices")
+ if err != nil {
+ t.Errorf("couldn't get cgroup path: %v", err)
+ }
+ if !strings.HasPrefix(devicePath, deviceRoot) {
+ t.Errorf("SECURITY: cgroup path() is outside cgroup mountpoint!")
+ }
+}
+
+// XXX: Remove me after we get rid of configs.Cgroup.Name and configs.Cgroup.Parent.
+func TestInvalidCgroupParent(t *testing.T) {
+ root, err := getCgroupRoot()
+ if err != nil {
+ t.Errorf("couldn't get cgroup root: %v", err)
+ }
+
+ config := &configs.Cgroup{
+ Parent: "../../../../../../../../../../some/path",
+ Name: "name",
+ }
+
+ data, err := getCgroupData(config, 0)
+ if err != nil {
+ t.Errorf("couldn't get cgroup data: %v", err)
+ }
+
+ // Make sure the final innerPath doesn't go outside the cgroup mountpoint.
+ if strings.HasPrefix(data.innerPath, "..") {
+ t.Errorf("SECURITY: cgroup innerPath is outside cgroup mountpoint!")
+ }
+
+ // Double-check, using an actual cgroup.
+ deviceRoot := filepath.Join(root, "devices")
+ devicePath, err := data.path("devices")
+ if err != nil {
+ t.Errorf("couldn't get cgroup path: %v", err)
+ }
+ if !strings.HasPrefix(devicePath, deviceRoot) {
+ t.Errorf("SECURITY: cgroup path() is outside cgroup mountpoint!")
+ }
+}
+
+// XXX: Remove me after we get rid of configs.Cgroup.Name and configs.Cgroup.Parent.
+func TestInvalidAbsoluteCgroupParent(t *testing.T) {
+ root, err := getCgroupRoot()
+ if err != nil {
+ t.Errorf("couldn't get cgroup root: %v", err)
+ }
+
+ config := &configs.Cgroup{
+ Parent: "/../../../../../../../../../../some/path",
+ Name: "name",
+ }
+
+ data, err := getCgroupData(config, 0)
+ if err != nil {
+ t.Errorf("couldn't get cgroup data: %v", err)
+ }
+
+ // Make sure the final innerPath doesn't go outside the cgroup mountpoint.
+ if strings.HasPrefix(data.innerPath, "..") {
+ t.Errorf("SECURITY: cgroup innerPath is outside cgroup mountpoint!")
+ }
+
+ // Double-check, using an actual cgroup.
+ deviceRoot := filepath.Join(root, "devices")
+ devicePath, err := data.path("devices")
+ if err != nil {
+ t.Errorf("couldn't get cgroup path: %v", err)
+ }
+ if !strings.HasPrefix(devicePath, deviceRoot) {
+ t.Errorf("SECURITY: cgroup path() is outside cgroup mountpoint!")
+ }
+}
+
+// XXX: Remove me after we get rid of configs.Cgroup.Name and configs.Cgroup.Parent.
+func TestInvalidCgroupName(t *testing.T) {
+ root, err := getCgroupRoot()
+ if err != nil {
+ t.Errorf("couldn't get cgroup root: %v", err)
+ }
+
+ config := &configs.Cgroup{
+ Parent: "parent",
+ Name: "../../../../../../../../../../some/path",
+ }
+
+ data, err := getCgroupData(config, 0)
+ if err != nil {
+ t.Errorf("couldn't get cgroup data: %v", err)
+ }
+
+ // Make sure the final innerPath doesn't go outside the cgroup mountpoint.
+ if strings.HasPrefix(data.innerPath, "..") {
+ t.Errorf("SECURITY: cgroup innerPath is outside cgroup mountpoint!")
+ }
+
+ // Double-check, using an actual cgroup.
+ deviceRoot := filepath.Join(root, "devices")
+ devicePath, err := data.path("devices")
+ if err != nil {
+ t.Errorf("couldn't get cgroup path: %v", err)
+ }
+ if !strings.HasPrefix(devicePath, deviceRoot) {
+ t.Errorf("SECURITY: cgroup path() is outside cgroup mountpoint!")
+ }
+
+}
+
+// XXX: Remove me after we get rid of configs.Cgroup.Name and configs.Cgroup.Parent.
+func TestInvalidAbsoluteCgroupName(t *testing.T) {
+ root, err := getCgroupRoot()
+ if err != nil {
+ t.Errorf("couldn't get cgroup root: %v", err)
+ }
+
+ config := &configs.Cgroup{
+ Parent: "parent",
+ Name: "/../../../../../../../../../../some/path",
+ }
+
+ data, err := getCgroupData(config, 0)
+ if err != nil {
+ t.Errorf("couldn't get cgroup data: %v", err)
+ }
+
+ // Make sure the final innerPath doesn't go outside the cgroup mountpoint.
+ if strings.HasPrefix(data.innerPath, "..") {
+ t.Errorf("SECURITY: cgroup innerPath is outside cgroup mountpoint!")
+ }
+
+ // Double-check, using an actual cgroup.
+ deviceRoot := filepath.Join(root, "devices")
+ devicePath, err := data.path("devices")
+ if err != nil {
+ t.Errorf("couldn't get cgroup path: %v", err)
+ }
+ if !strings.HasPrefix(devicePath, deviceRoot) {
+ t.Errorf("SECURITY: cgroup path() is outside cgroup mountpoint!")
+ }
+}
+
+// XXX: Remove me after we get rid of configs.Cgroup.Name and configs.Cgroup.Parent.
+func TestInvalidCgroupNameAndParent(t *testing.T) {
+ root, err := getCgroupRoot()
+ if err != nil {
+ t.Errorf("couldn't get cgroup root: %v", err)
+ }
+
+ config := &configs.Cgroup{
+ Parent: "../../../../../../../../../../some/path",
+ Name: "../../../../../../../../../../some/path",
+ }
+
+ data, err := getCgroupData(config, 0)
+ if err != nil {
+ t.Errorf("couldn't get cgroup data: %v", err)
+ }
+
+ // Make sure the final innerPath doesn't go outside the cgroup mountpoint.
+ if strings.HasPrefix(data.innerPath, "..") {
+ t.Errorf("SECURITY: cgroup innerPath is outside cgroup mountpoint!")
+ }
+
+ // Double-check, using an actual cgroup.
+ deviceRoot := filepath.Join(root, "devices")
+ devicePath, err := data.path("devices")
+ if err != nil {
+ t.Errorf("couldn't get cgroup path: %v", err)
+ }
+ if !strings.HasPrefix(devicePath, deviceRoot) {
+ t.Errorf("SECURITY: cgroup path() is outside cgroup mountpoint!")
+ }
+}
+
+// XXX: Remove me after we get rid of configs.Cgroup.Name and configs.Cgroup.Parent.
+func TestInvalidAbsoluteCgroupNameAndParent(t *testing.T) {
+ root, err := getCgroupRoot()
+ if err != nil {
+ t.Errorf("couldn't get cgroup root: %v", err)
+ }
+
+ config := &configs.Cgroup{
+ Parent: "/../../../../../../../../../../some/path",
+ Name: "/../../../../../../../../../../some/path",
+ }
+
+ data, err := getCgroupData(config, 0)
+ if err != nil {
+ t.Errorf("couldn't get cgroup data: %v", err)
+ }
+
+ // Make sure the final innerPath doesn't go outside the cgroup mountpoint.
+ if strings.HasPrefix(data.innerPath, "..") {
+ t.Errorf("SECURITY: cgroup innerPath is outside cgroup mountpoint!")
+ }
+
+ // Double-check, using an actual cgroup.
+ deviceRoot := filepath.Join(root, "devices")
+ devicePath, err := data.path("devices")
+ if err != nil {
+ t.Errorf("couldn't get cgroup path: %v", err)
+ }
+ if !strings.HasPrefix(devicePath, deviceRoot) {
+ t.Errorf("SECURITY: cgroup path() is outside cgroup mountpoint!")
+ }
+}
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/blkio.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/blkio.go
new file mode 100644
index 0000000000..a142cb991d
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/blkio.go
@@ -0,0 +1,237 @@
+// +build linux
+
+package fs
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+ "path/filepath"
+ "strconv"
+ "strings"
+
+ "github.com/opencontainers/runc/libcontainer/cgroups"
+ "github.com/opencontainers/runc/libcontainer/configs"
+)
+
+type BlkioGroup struct {
+}
+
+func (s *BlkioGroup) Name() string {
+ return "blkio"
+}
+
+func (s *BlkioGroup) Apply(d *cgroupData) error {
+ _, err := d.join("blkio")
+ if err != nil && !cgroups.IsNotFound(err) {
+ return err
+ }
+ return nil
+}
+
+func (s *BlkioGroup) Set(path string, cgroup *configs.Cgroup) error {
+ if cgroup.Resources.BlkioWeight != 0 {
+ if err := writeFile(path, "blkio.weight", strconv.FormatUint(uint64(cgroup.Resources.BlkioWeight), 10)); err != nil {
+ return err
+ }
+ }
+
+ if cgroup.Resources.BlkioLeafWeight != 0 {
+ if err := writeFile(path, "blkio.leaf_weight", strconv.FormatUint(uint64(cgroup.Resources.BlkioLeafWeight), 10)); err != nil {
+ return err
+ }
+ }
+ for _, wd := range cgroup.Resources.BlkioWeightDevice {
+ if err := writeFile(path, "blkio.weight_device", wd.WeightString()); err != nil {
+ return err
+ }
+ if err := writeFile(path, "blkio.leaf_weight_device", wd.LeafWeightString()); err != nil {
+ return err
+ }
+ }
+ for _, td := range cgroup.Resources.BlkioThrottleReadBpsDevice {
+ if err := writeFile(path, "blkio.throttle.read_bps_device", td.String()); err != nil {
+ return err
+ }
+ }
+ for _, td := range cgroup.Resources.BlkioThrottleWriteBpsDevice {
+ if err := writeFile(path, "blkio.throttle.write_bps_device", td.String()); err != nil {
+ return err
+ }
+ }
+ for _, td := range cgroup.Resources.BlkioThrottleReadIOPSDevice {
+ if err := writeFile(path, "blkio.throttle.read_iops_device", td.String()); err != nil {
+ return err
+ }
+ }
+ for _, td := range cgroup.Resources.BlkioThrottleWriteIOPSDevice {
+ if err := writeFile(path, "blkio.throttle.write_iops_device", td.String()); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (s *BlkioGroup) Remove(d *cgroupData) error {
+ return removePath(d.path("blkio"))
+}
+
+/*
+examples:
+
+ blkio.sectors
+ 8:0 6792
+
+ blkio.io_service_bytes
+ 8:0 Read 1282048
+ 8:0 Write 2195456
+ 8:0 Sync 2195456
+ 8:0 Async 1282048
+ 8:0 Total 3477504
+ Total 3477504
+
+ blkio.io_serviced
+ 8:0 Read 124
+ 8:0 Write 104
+ 8:0 Sync 104
+ 8:0 Async 124
+ 8:0 Total 228
+ Total 228
+
+ blkio.io_queued
+ 8:0 Read 0
+ 8:0 Write 0
+ 8:0 Sync 0
+ 8:0 Async 0
+ 8:0 Total 0
+ Total 0
+*/
+
+func splitBlkioStatLine(r rune) bool {
+ return r == ' ' || r == ':'
+}
+
+func getBlkioStat(path string) ([]cgroups.BlkioStatEntry, error) {
+ var blkioStats []cgroups.BlkioStatEntry
+ f, err := os.Open(path)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return blkioStats, nil
+ }
+ return nil, err
+ }
+ defer f.Close()
+
+ sc := bufio.NewScanner(f)
+ for sc.Scan() {
+ // format: dev type amount
+ fields := strings.FieldsFunc(sc.Text(), splitBlkioStatLine)
+ if len(fields) < 3 {
+ if len(fields) == 2 && fields[0] == "Total" {
+ // skip total line
+ continue
+ } else {
+ return nil, fmt.Errorf("Invalid line found while parsing %s: %s", path, sc.Text())
+ }
+ }
+
+ v, err := strconv.ParseUint(fields[0], 10, 64)
+ if err != nil {
+ return nil, err
+ }
+ major := v
+
+ v, err = strconv.ParseUint(fields[1], 10, 64)
+ if err != nil {
+ return nil, err
+ }
+ minor := v
+
+ op := ""
+ valueField := 2
+ if len(fields) == 4 {
+ op = fields[2]
+ valueField = 3
+ }
+ v, err = strconv.ParseUint(fields[valueField], 10, 64)
+ if err != nil {
+ return nil, err
+ }
+ blkioStats = append(blkioStats, cgroups.BlkioStatEntry{Major: major, Minor: minor, Op: op, Value: v})
+ }
+
+ return blkioStats, nil
+}
+
+func (s *BlkioGroup) GetStats(path string, stats *cgroups.Stats) error {
+ // Try to read CFQ stats available on all CFQ enabled kernels first
+ if blkioStats, err := getBlkioStat(filepath.Join(path, "blkio.io_serviced_recursive")); err == nil && blkioStats != nil {
+ return getCFQStats(path, stats)
+ }
+ return getStats(path, stats) // Use generic stats as fallback
+}
+
+func getCFQStats(path string, stats *cgroups.Stats) error {
+ var blkioStats []cgroups.BlkioStatEntry
+ var err error
+
+ if blkioStats, err = getBlkioStat(filepath.Join(path, "blkio.sectors_recursive")); err != nil {
+ return err
+ }
+ stats.BlkioStats.SectorsRecursive = blkioStats
+
+ if blkioStats, err = getBlkioStat(filepath.Join(path, "blkio.io_service_bytes_recursive")); err != nil {
+ return err
+ }
+ stats.BlkioStats.IoServiceBytesRecursive = blkioStats
+
+ if blkioStats, err = getBlkioStat(filepath.Join(path, "blkio.io_serviced_recursive")); err != nil {
+ return err
+ }
+ stats.BlkioStats.IoServicedRecursive = blkioStats
+
+ if blkioStats, err = getBlkioStat(filepath.Join(path, "blkio.io_queued_recursive")); err != nil {
+ return err
+ }
+ stats.BlkioStats.IoQueuedRecursive = blkioStats
+
+ if blkioStats, err = getBlkioStat(filepath.Join(path, "blkio.io_service_time_recursive")); err != nil {
+ return err
+ }
+ stats.BlkioStats.IoServiceTimeRecursive = blkioStats
+
+ if blkioStats, err = getBlkioStat(filepath.Join(path, "blkio.io_wait_time_recursive")); err != nil {
+ return err
+ }
+ stats.BlkioStats.IoWaitTimeRecursive = blkioStats
+
+ if blkioStats, err = getBlkioStat(filepath.Join(path, "blkio.io_merged_recursive")); err != nil {
+ return err
+ }
+ stats.BlkioStats.IoMergedRecursive = blkioStats
+
+ if blkioStats, err = getBlkioStat(filepath.Join(path, "blkio.time_recursive")); err != nil {
+ return err
+ }
+ stats.BlkioStats.IoTimeRecursive = blkioStats
+
+ return nil
+}
+
+func getStats(path string, stats *cgroups.Stats) error {
+ var blkioStats []cgroups.BlkioStatEntry
+ var err error
+
+ if blkioStats, err = getBlkioStat(filepath.Join(path, "blkio.throttle.io_service_bytes")); err != nil {
+ return err
+ }
+ stats.BlkioStats.IoServiceBytesRecursive = blkioStats
+
+ if blkioStats, err = getBlkioStat(filepath.Join(path, "blkio.throttle.io_serviced")); err != nil {
+ return err
+ }
+ stats.BlkioStats.IoServicedRecursive = blkioStats
+
+ return nil
+}
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/blkio_test.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/blkio_test.go
new file mode 100644
index 0000000000..6957392048
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/blkio_test.go
@@ -0,0 +1,636 @@
+// +build linux
+
+package fs
+
+import (
+ "strconv"
+ "testing"
+
+ "github.com/opencontainers/runc/libcontainer/cgroups"
+ "github.com/opencontainers/runc/libcontainer/configs"
+)
+
+const (
+ sectorsRecursiveContents = `8:0 1024`
+ serviceBytesRecursiveContents = `8:0 Read 100
+8:0 Write 200
+8:0 Sync 300
+8:0 Async 500
+8:0 Total 500
+Total 500`
+ servicedRecursiveContents = `8:0 Read 10
+8:0 Write 40
+8:0 Sync 20
+8:0 Async 30
+8:0 Total 50
+Total 50`
+ queuedRecursiveContents = `8:0 Read 1
+8:0 Write 4
+8:0 Sync 2
+8:0 Async 3
+8:0 Total 5
+Total 5`
+ serviceTimeRecursiveContents = `8:0 Read 173959
+8:0 Write 0
+8:0 Sync 0
+8:0 Async 173959
+8:0 Total 17395
+Total 17395`
+ waitTimeRecursiveContents = `8:0 Read 15571
+8:0 Write 0
+8:0 Sync 0
+8:0 Async 15571
+8:0 Total 15571`
+ mergedRecursiveContents = `8:0 Read 5
+8:0 Write 10
+8:0 Sync 0
+8:0 Async 0
+8:0 Total 15
+Total 15`
+ timeRecursiveContents = `8:0 8`
+ throttleServiceBytes = `8:0 Read 11030528
+8:0 Write 23
+8:0 Sync 42
+8:0 Async 11030528
+8:0 Total 11030528
+252:0 Read 11030528
+252:0 Write 23
+252:0 Sync 42
+252:0 Async 11030528
+252:0 Total 11030528
+Total 22061056`
+ throttleServiced = `8:0 Read 164
+8:0 Write 23
+8:0 Sync 42
+8:0 Async 164
+8:0 Total 164
+252:0 Read 164
+252:0 Write 23
+252:0 Sync 42
+252:0 Async 164
+252:0 Total 164
+Total 328`
+)
+
+func appendBlkioStatEntry(blkioStatEntries *[]cgroups.BlkioStatEntry, major, minor, value uint64, op string) {
+ *blkioStatEntries = append(*blkioStatEntries, cgroups.BlkioStatEntry{Major: major, Minor: minor, Value: value, Op: op})
+}
+
+func TestBlkioSetWeight(t *testing.T) {
+ helper := NewCgroupTestUtil("blkio", t)
+ defer helper.cleanup()
+
+ const (
+ weightBefore = 100
+ weightAfter = 200
+ )
+
+ helper.writeFileContents(map[string]string{
+ "blkio.weight": strconv.Itoa(weightBefore),
+ })
+
+ helper.CgroupData.config.Resources.BlkioWeight = weightAfter
+ blkio := &BlkioGroup{}
+ if err := blkio.Set(helper.CgroupPath, helper.CgroupData.config); err != nil {
+ t.Fatal(err)
+ }
+
+ value, err := getCgroupParamUint(helper.CgroupPath, "blkio.weight")
+ if err != nil {
+ t.Fatalf("Failed to parse blkio.weight - %s", err)
+ }
+
+ if value != weightAfter {
+ t.Fatal("Got the wrong value, set blkio.weight failed.")
+ }
+}
+
+func TestBlkioSetWeightDevice(t *testing.T) {
+ helper := NewCgroupTestUtil("blkio", t)
+ defer helper.cleanup()
+
+ const (
+ weightDeviceBefore = "8:0 400"
+ )
+
+ wd := configs.NewWeightDevice(8, 0, 500, 0)
+ weightDeviceAfter := wd.WeightString()
+
+ helper.writeFileContents(map[string]string{
+ "blkio.weight_device": weightDeviceBefore,
+ })
+
+ helper.CgroupData.config.Resources.BlkioWeightDevice = []*configs.WeightDevice{wd}
+ blkio := &BlkioGroup{}
+ if err := blkio.Set(helper.CgroupPath, helper.CgroupData.config); err != nil {
+ t.Fatal(err)
+ }
+
+ value, err := getCgroupParamString(helper.CgroupPath, "blkio.weight_device")
+ if err != nil {
+ t.Fatalf("Failed to parse blkio.weight_device - %s", err)
+ }
+
+ if value != weightDeviceAfter {
+ t.Fatal("Got the wrong value, set blkio.weight_device failed.")
+ }
+}
+
+// regression #274
+func TestBlkioSetMultipleWeightDevice(t *testing.T) {
+ helper := NewCgroupTestUtil("blkio", t)
+ defer helper.cleanup()
+
+ const (
+ weightDeviceBefore = "8:0 400"
+ )
+
+ wd1 := configs.NewWeightDevice(8, 0, 500, 0)
+ wd2 := configs.NewWeightDevice(8, 16, 500, 0)
+ // we cannot actually set and check both because normal ioutil.WriteFile
+ // when writing to cgroup file will overwrite the whole file content instead
+ // of updating it as the kernel is doing. Just check the second device
+ // is present will suffice for the test to ensure multiple writes are done.
+ weightDeviceAfter := wd2.WeightString()
+
+ helper.writeFileContents(map[string]string{
+ "blkio.weight_device": weightDeviceBefore,
+ })
+
+ helper.CgroupData.config.Resources.BlkioWeightDevice = []*configs.WeightDevice{wd1, wd2}
+ blkio := &BlkioGroup{}
+ if err := blkio.Set(helper.CgroupPath, helper.CgroupData.config); err != nil {
+ t.Fatal(err)
+ }
+
+ value, err := getCgroupParamString(helper.CgroupPath, "blkio.weight_device")
+ if err != nil {
+ t.Fatalf("Failed to parse blkio.weight_device - %s", err)
+ }
+
+ if value != weightDeviceAfter {
+ t.Fatal("Got the wrong value, set blkio.weight_device failed.")
+ }
+}
+
+func TestBlkioStats(t *testing.T) {
+ helper := NewCgroupTestUtil("blkio", t)
+ defer helper.cleanup()
+ helper.writeFileContents(map[string]string{
+ "blkio.io_service_bytes_recursive": serviceBytesRecursiveContents,
+ "blkio.io_serviced_recursive": servicedRecursiveContents,
+ "blkio.io_queued_recursive": queuedRecursiveContents,
+ "blkio.io_service_time_recursive": serviceTimeRecursiveContents,
+ "blkio.io_wait_time_recursive": waitTimeRecursiveContents,
+ "blkio.io_merged_recursive": mergedRecursiveContents,
+ "blkio.time_recursive": timeRecursiveContents,
+ "blkio.sectors_recursive": sectorsRecursiveContents,
+ })
+
+ blkio := &BlkioGroup{}
+ actualStats := *cgroups.NewStats()
+ err := blkio.GetStats(helper.CgroupPath, &actualStats)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Verify expected stats.
+ expectedStats := cgroups.BlkioStats{}
+ appendBlkioStatEntry(&expectedStats.SectorsRecursive, 8, 0, 1024, "")
+
+ appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 100, "Read")
+ appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 200, "Write")
+ appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 300, "Sync")
+ appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 500, "Async")
+ appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 500, "Total")
+
+ appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 10, "Read")
+ appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 40, "Write")
+ appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 20, "Sync")
+ appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 30, "Async")
+ appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 50, "Total")
+
+ appendBlkioStatEntry(&expectedStats.IoQueuedRecursive, 8, 0, 1, "Read")
+ appendBlkioStatEntry(&expectedStats.IoQueuedRecursive, 8, 0, 4, "Write")
+ appendBlkioStatEntry(&expectedStats.IoQueuedRecursive, 8, 0, 2, "Sync")
+ appendBlkioStatEntry(&expectedStats.IoQueuedRecursive, 8, 0, 3, "Async")
+ appendBlkioStatEntry(&expectedStats.IoQueuedRecursive, 8, 0, 5, "Total")
+
+ appendBlkioStatEntry(&expectedStats.IoServiceTimeRecursive, 8, 0, 173959, "Read")
+ appendBlkioStatEntry(&expectedStats.IoServiceTimeRecursive, 8, 0, 0, "Write")
+ appendBlkioStatEntry(&expectedStats.IoServiceTimeRecursive, 8, 0, 0, "Sync")
+ appendBlkioStatEntry(&expectedStats.IoServiceTimeRecursive, 8, 0, 173959, "Async")
+ appendBlkioStatEntry(&expectedStats.IoServiceTimeRecursive, 8, 0, 17395, "Total")
+
+ appendBlkioStatEntry(&expectedStats.IoWaitTimeRecursive, 8, 0, 15571, "Read")
+ appendBlkioStatEntry(&expectedStats.IoWaitTimeRecursive, 8, 0, 0, "Write")
+ appendBlkioStatEntry(&expectedStats.IoWaitTimeRecursive, 8, 0, 0, "Sync")
+ appendBlkioStatEntry(&expectedStats.IoWaitTimeRecursive, 8, 0, 15571, "Async")
+ appendBlkioStatEntry(&expectedStats.IoWaitTimeRecursive, 8, 0, 15571, "Total")
+
+ appendBlkioStatEntry(&expectedStats.IoMergedRecursive, 8, 0, 5, "Read")
+ appendBlkioStatEntry(&expectedStats.IoMergedRecursive, 8, 0, 10, "Write")
+ appendBlkioStatEntry(&expectedStats.IoMergedRecursive, 8, 0, 0, "Sync")
+ appendBlkioStatEntry(&expectedStats.IoMergedRecursive, 8, 0, 0, "Async")
+ appendBlkioStatEntry(&expectedStats.IoMergedRecursive, 8, 0, 15, "Total")
+
+ appendBlkioStatEntry(&expectedStats.IoTimeRecursive, 8, 0, 8, "")
+
+ expectBlkioStatsEquals(t, expectedStats, actualStats.BlkioStats)
+}
+
+func TestBlkioStatsNoSectorsFile(t *testing.T) {
+ helper := NewCgroupTestUtil("blkio", t)
+ defer helper.cleanup()
+ helper.writeFileContents(map[string]string{
+ "blkio.io_service_bytes_recursive": serviceBytesRecursiveContents,
+ "blkio.io_serviced_recursive": servicedRecursiveContents,
+ "blkio.io_queued_recursive": queuedRecursiveContents,
+ "blkio.io_service_time_recursive": serviceTimeRecursiveContents,
+ "blkio.io_wait_time_recursive": waitTimeRecursiveContents,
+ "blkio.io_merged_recursive": mergedRecursiveContents,
+ "blkio.time_recursive": timeRecursiveContents,
+ })
+
+ blkio := &BlkioGroup{}
+ actualStats := *cgroups.NewStats()
+ err := blkio.GetStats(helper.CgroupPath, &actualStats)
+ if err != nil {
+ t.Fatalf("Failed unexpectedly: %s", err)
+ }
+}
+
+func TestBlkioStatsNoServiceBytesFile(t *testing.T) {
+ helper := NewCgroupTestUtil("blkio", t)
+ defer helper.cleanup()
+ helper.writeFileContents(map[string]string{
+ "blkio.io_serviced_recursive": servicedRecursiveContents,
+ "blkio.io_queued_recursive": queuedRecursiveContents,
+ "blkio.sectors_recursive": sectorsRecursiveContents,
+ "blkio.io_service_time_recursive": serviceTimeRecursiveContents,
+ "blkio.io_wait_time_recursive": waitTimeRecursiveContents,
+ "blkio.io_merged_recursive": mergedRecursiveContents,
+ "blkio.time_recursive": timeRecursiveContents,
+ })
+
+ blkio := &BlkioGroup{}
+ actualStats := *cgroups.NewStats()
+ err := blkio.GetStats(helper.CgroupPath, &actualStats)
+ if err != nil {
+ t.Fatalf("Failed unexpectedly: %s", err)
+ }
+}
+
+func TestBlkioStatsNoServicedFile(t *testing.T) {
+ helper := NewCgroupTestUtil("blkio", t)
+ defer helper.cleanup()
+ helper.writeFileContents(map[string]string{
+ "blkio.io_service_bytes_recursive": serviceBytesRecursiveContents,
+ "blkio.io_queued_recursive": queuedRecursiveContents,
+ "blkio.sectors_recursive": sectorsRecursiveContents,
+ "blkio.io_service_time_recursive": serviceTimeRecursiveContents,
+ "blkio.io_wait_time_recursive": waitTimeRecursiveContents,
+ "blkio.io_merged_recursive": mergedRecursiveContents,
+ "blkio.time_recursive": timeRecursiveContents,
+ })
+
+ blkio := &BlkioGroup{}
+ actualStats := *cgroups.NewStats()
+ err := blkio.GetStats(helper.CgroupPath, &actualStats)
+ if err != nil {
+ t.Fatalf("Failed unexpectedly: %s", err)
+ }
+}
+
+func TestBlkioStatsNoQueuedFile(t *testing.T) {
+ helper := NewCgroupTestUtil("blkio", t)
+ defer helper.cleanup()
+ helper.writeFileContents(map[string]string{
+ "blkio.io_service_bytes_recursive": serviceBytesRecursiveContents,
+ "blkio.io_serviced_recursive": servicedRecursiveContents,
+ "blkio.sectors_recursive": sectorsRecursiveContents,
+ "blkio.io_service_time_recursive": serviceTimeRecursiveContents,
+ "blkio.io_wait_time_recursive": waitTimeRecursiveContents,
+ "blkio.io_merged_recursive": mergedRecursiveContents,
+ "blkio.time_recursive": timeRecursiveContents,
+ })
+
+ blkio := &BlkioGroup{}
+ actualStats := *cgroups.NewStats()
+ err := blkio.GetStats(helper.CgroupPath, &actualStats)
+ if err != nil {
+ t.Fatalf("Failed unexpectedly: %s", err)
+ }
+}
+
+func TestBlkioStatsNoServiceTimeFile(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping test in short mode.")
+ }
+ helper := NewCgroupTestUtil("blkio", t)
+ defer helper.cleanup()
+ helper.writeFileContents(map[string]string{
+ "blkio.io_service_bytes_recursive": serviceBytesRecursiveContents,
+ "blkio.io_serviced_recursive": servicedRecursiveContents,
+ "blkio.io_queued_recursive": queuedRecursiveContents,
+ "blkio.io_wait_time_recursive": waitTimeRecursiveContents,
+ "blkio.io_merged_recursive": mergedRecursiveContents,
+ "blkio.time_recursive": timeRecursiveContents,
+ "blkio.sectors_recursive": sectorsRecursiveContents,
+ })
+
+ blkio := &BlkioGroup{}
+ actualStats := *cgroups.NewStats()
+ err := blkio.GetStats(helper.CgroupPath, &actualStats)
+ if err != nil {
+ t.Fatalf("Failed unexpectedly: %s", err)
+ }
+}
+
+func TestBlkioStatsNoWaitTimeFile(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping test in short mode.")
+ }
+ helper := NewCgroupTestUtil("blkio", t)
+ defer helper.cleanup()
+ helper.writeFileContents(map[string]string{
+ "blkio.io_service_bytes_recursive": serviceBytesRecursiveContents,
+ "blkio.io_serviced_recursive": servicedRecursiveContents,
+ "blkio.io_queued_recursive": queuedRecursiveContents,
+ "blkio.io_service_time_recursive": serviceTimeRecursiveContents,
+ "blkio.io_merged_recursive": mergedRecursiveContents,
+ "blkio.time_recursive": timeRecursiveContents,
+ "blkio.sectors_recursive": sectorsRecursiveContents,
+ })
+
+ blkio := &BlkioGroup{}
+ actualStats := *cgroups.NewStats()
+ err := blkio.GetStats(helper.CgroupPath, &actualStats)
+ if err != nil {
+ t.Fatalf("Failed unexpectedly: %s", err)
+ }
+}
+
+func TestBlkioStatsNoMergedFile(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping test in short mode.")
+ }
+ helper := NewCgroupTestUtil("blkio", t)
+ defer helper.cleanup()
+ helper.writeFileContents(map[string]string{
+ "blkio.io_service_bytes_recursive": serviceBytesRecursiveContents,
+ "blkio.io_serviced_recursive": servicedRecursiveContents,
+ "blkio.io_queued_recursive": queuedRecursiveContents,
+ "blkio.io_service_time_recursive": serviceTimeRecursiveContents,
+ "blkio.io_wait_time_recursive": waitTimeRecursiveContents,
+ "blkio.time_recursive": timeRecursiveContents,
+ "blkio.sectors_recursive": sectorsRecursiveContents,
+ })
+
+ blkio := &BlkioGroup{}
+ actualStats := *cgroups.NewStats()
+ err := blkio.GetStats(helper.CgroupPath, &actualStats)
+ if err != nil {
+ t.Fatalf("Failed unexpectedly: %s", err)
+ }
+}
+
+func TestBlkioStatsNoTimeFile(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping test in short mode.")
+ }
+ helper := NewCgroupTestUtil("blkio", t)
+ defer helper.cleanup()
+ helper.writeFileContents(map[string]string{
+ "blkio.io_service_bytes_recursive": serviceBytesRecursiveContents,
+ "blkio.io_serviced_recursive": servicedRecursiveContents,
+ "blkio.io_queued_recursive": queuedRecursiveContents,
+ "blkio.io_service_time_recursive": serviceTimeRecursiveContents,
+ "blkio.io_wait_time_recursive": waitTimeRecursiveContents,
+ "blkio.io_merged_recursive": mergedRecursiveContents,
+ "blkio.sectors_recursive": sectorsRecursiveContents,
+ })
+
+ blkio := &BlkioGroup{}
+ actualStats := *cgroups.NewStats()
+ err := blkio.GetStats(helper.CgroupPath, &actualStats)
+ if err != nil {
+ t.Fatalf("Failed unexpectedly: %s", err)
+ }
+}
+
+func TestBlkioStatsUnexpectedNumberOfFields(t *testing.T) {
+ helper := NewCgroupTestUtil("blkio", t)
+ defer helper.cleanup()
+ helper.writeFileContents(map[string]string{
+ "blkio.io_service_bytes_recursive": "8:0 Read 100 100",
+ "blkio.io_serviced_recursive": servicedRecursiveContents,
+ "blkio.io_queued_recursive": queuedRecursiveContents,
+ "blkio.sectors_recursive": sectorsRecursiveContents,
+ "blkio.io_service_time_recursive": serviceTimeRecursiveContents,
+ "blkio.io_wait_time_recursive": waitTimeRecursiveContents,
+ "blkio.io_merged_recursive": mergedRecursiveContents,
+ "blkio.time_recursive": timeRecursiveContents,
+ })
+
+ blkio := &BlkioGroup{}
+ actualStats := *cgroups.NewStats()
+ err := blkio.GetStats(helper.CgroupPath, &actualStats)
+ if err == nil {
+ t.Fatal("Expected to fail, but did not")
+ }
+}
+
+func TestBlkioStatsUnexpectedFieldType(t *testing.T) {
+ helper := NewCgroupTestUtil("blkio", t)
+ defer helper.cleanup()
+ helper.writeFileContents(map[string]string{
+ "blkio.io_service_bytes_recursive": "8:0 Read Write",
+ "blkio.io_serviced_recursive": servicedRecursiveContents,
+ "blkio.io_queued_recursive": queuedRecursiveContents,
+ "blkio.sectors_recursive": sectorsRecursiveContents,
+ "blkio.io_service_time_recursive": serviceTimeRecursiveContents,
+ "blkio.io_wait_time_recursive": waitTimeRecursiveContents,
+ "blkio.io_merged_recursive": mergedRecursiveContents,
+ "blkio.time_recursive": timeRecursiveContents,
+ })
+
+ blkio := &BlkioGroup{}
+ actualStats := *cgroups.NewStats()
+ err := blkio.GetStats(helper.CgroupPath, &actualStats)
+ if err == nil {
+ t.Fatal("Expected to fail, but did not")
+ }
+}
+
+func TestNonCFQBlkioStats(t *testing.T) {
+ helper := NewCgroupTestUtil("blkio", t)
+ defer helper.cleanup()
+ helper.writeFileContents(map[string]string{
+ "blkio.io_service_bytes_recursive": "",
+ "blkio.io_serviced_recursive": "",
+ "blkio.io_queued_recursive": "",
+ "blkio.sectors_recursive": "",
+ "blkio.io_service_time_recursive": "",
+ "blkio.io_wait_time_recursive": "",
+ "blkio.io_merged_recursive": "",
+ "blkio.time_recursive": "",
+ "blkio.throttle.io_service_bytes": throttleServiceBytes,
+ "blkio.throttle.io_serviced": throttleServiced,
+ })
+
+ blkio := &BlkioGroup{}
+ actualStats := *cgroups.NewStats()
+ err := blkio.GetStats(helper.CgroupPath, &actualStats)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Verify expected stats.
+ expectedStats := cgroups.BlkioStats{}
+
+ appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 11030528, "Read")
+ appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 23, "Write")
+ appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 42, "Sync")
+ appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 11030528, "Async")
+ appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 11030528, "Total")
+ appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 252, 0, 11030528, "Read")
+ appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 252, 0, 23, "Write")
+ appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 252, 0, 42, "Sync")
+ appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 252, 0, 11030528, "Async")
+ appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 252, 0, 11030528, "Total")
+
+ appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 164, "Read")
+ appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 23, "Write")
+ appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 42, "Sync")
+ appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 164, "Async")
+ appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 164, "Total")
+ appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 252, 0, 164, "Read")
+ appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 252, 0, 23, "Write")
+ appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 252, 0, 42, "Sync")
+ appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 252, 0, 164, "Async")
+ appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 252, 0, 164, "Total")
+
+ expectBlkioStatsEquals(t, expectedStats, actualStats.BlkioStats)
+}
+
+func TestBlkioSetThrottleReadBpsDevice(t *testing.T) {
+ helper := NewCgroupTestUtil("blkio", t)
+ defer helper.cleanup()
+
+ const (
+ throttleBefore = `8:0 1024`
+ )
+
+ td := configs.NewThrottleDevice(8, 0, 2048)
+ throttleAfter := td.String()
+
+ helper.writeFileContents(map[string]string{
+ "blkio.throttle.read_bps_device": throttleBefore,
+ })
+
+ helper.CgroupData.config.Resources.BlkioThrottleReadBpsDevice = []*configs.ThrottleDevice{td}
+ blkio := &BlkioGroup{}
+ if err := blkio.Set(helper.CgroupPath, helper.CgroupData.config); err != nil {
+ t.Fatal(err)
+ }
+
+ value, err := getCgroupParamString(helper.CgroupPath, "blkio.throttle.read_bps_device")
+ if err != nil {
+ t.Fatalf("Failed to parse blkio.throttle.read_bps_device - %s", err)
+ }
+
+ if value != throttleAfter {
+ t.Fatal("Got the wrong value, set blkio.throttle.read_bps_device failed.")
+ }
+}
+func TestBlkioSetThrottleWriteBpsDevice(t *testing.T) {
+ helper := NewCgroupTestUtil("blkio", t)
+ defer helper.cleanup()
+
+ const (
+ throttleBefore = `8:0 1024`
+ )
+
+ td := configs.NewThrottleDevice(8, 0, 2048)
+ throttleAfter := td.String()
+
+ helper.writeFileContents(map[string]string{
+ "blkio.throttle.write_bps_device": throttleBefore,
+ })
+
+ helper.CgroupData.config.Resources.BlkioThrottleWriteBpsDevice = []*configs.ThrottleDevice{td}
+ blkio := &BlkioGroup{}
+ if err := blkio.Set(helper.CgroupPath, helper.CgroupData.config); err != nil {
+ t.Fatal(err)
+ }
+
+ value, err := getCgroupParamString(helper.CgroupPath, "blkio.throttle.write_bps_device")
+ if err != nil {
+ t.Fatalf("Failed to parse blkio.throttle.write_bps_device - %s", err)
+ }
+
+ if value != throttleAfter {
+ t.Fatal("Got the wrong value, set blkio.throttle.write_bps_device failed.")
+ }
+}
+func TestBlkioSetThrottleReadIOpsDevice(t *testing.T) {
+ helper := NewCgroupTestUtil("blkio", t)
+ defer helper.cleanup()
+
+ const (
+ throttleBefore = `8:0 1024`
+ )
+
+ td := configs.NewThrottleDevice(8, 0, 2048)
+ throttleAfter := td.String()
+
+ helper.writeFileContents(map[string]string{
+ "blkio.throttle.read_iops_device": throttleBefore,
+ })
+
+ helper.CgroupData.config.Resources.BlkioThrottleReadIOPSDevice = []*configs.ThrottleDevice{td}
+ blkio := &BlkioGroup{}
+ if err := blkio.Set(helper.CgroupPath, helper.CgroupData.config); err != nil {
+ t.Fatal(err)
+ }
+
+ value, err := getCgroupParamString(helper.CgroupPath, "blkio.throttle.read_iops_device")
+ if err != nil {
+ t.Fatalf("Failed to parse blkio.throttle.read_iops_device - %s", err)
+ }
+
+ if value != throttleAfter {
+ t.Fatal("Got the wrong value, set blkio.throttle.read_iops_device failed.")
+ }
+}
+func TestBlkioSetThrottleWriteIOpsDevice(t *testing.T) {
+ helper := NewCgroupTestUtil("blkio", t)
+ defer helper.cleanup()
+
+ const (
+ throttleBefore = `8:0 1024`
+ )
+
+ td := configs.NewThrottleDevice(8, 0, 2048)
+ throttleAfter := td.String()
+
+ helper.writeFileContents(map[string]string{
+ "blkio.throttle.write_iops_device": throttleBefore,
+ })
+
+ helper.CgroupData.config.Resources.BlkioThrottleWriteIOPSDevice = []*configs.ThrottleDevice{td}
+ blkio := &BlkioGroup{}
+ if err := blkio.Set(helper.CgroupPath, helper.CgroupData.config); err != nil {
+ t.Fatal(err)
+ }
+
+ value, err := getCgroupParamString(helper.CgroupPath, "blkio.throttle.write_iops_device")
+ if err != nil {
+ t.Fatalf("Failed to parse blkio.throttle.write_iops_device - %s", err)
+ }
+
+ if value != throttleAfter {
+ t.Fatal("Got the wrong value, set blkio.throttle.write_iops_device failed.")
+ }
+}
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpu.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpu.go
new file mode 100644
index 0000000000..b712bd0b1e
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpu.go
@@ -0,0 +1,125 @@
+// +build linux
+
+package fs
+
+import (
+ "bufio"
+ "os"
+ "path/filepath"
+ "strconv"
+
+ "github.com/opencontainers/runc/libcontainer/cgroups"
+ "github.com/opencontainers/runc/libcontainer/configs"
+)
+
+type CpuGroup struct {
+}
+
+func (s *CpuGroup) Name() string {
+ return "cpu"
+}
+
+func (s *CpuGroup) Apply(d *cgroupData) error {
+ // We always want to join the cpu group, to allow fair cpu scheduling
+ // on a container basis
+ path, err := d.path("cpu")
+ if err != nil && !cgroups.IsNotFound(err) {
+ return err
+ }
+ return s.ApplyDir(path, d.config, d.pid)
+}
+
+func (s *CpuGroup) ApplyDir(path string, cgroup *configs.Cgroup, pid int) error {
+ // This might happen if we have no cpu cgroup mounted.
+ // Just do nothing and don't fail.
+ if path == "" {
+ return nil
+ }
+ if err := os.MkdirAll(path, 0755); err != nil {
+ return err
+ }
+ // We should set the real-Time group scheduling settings before moving
+ // in the process because if the process is already in SCHED_RR mode
+ // and no RT bandwidth is set, adding it will fail.
+ if err := s.SetRtSched(path, cgroup); err != nil {
+ return err
+ }
+ // because we are not using d.join we need to place the pid into the procs file
+ // unlike the other subsystems
+ if err := cgroups.WriteCgroupProc(path, pid); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (s *CpuGroup) SetRtSched(path string, cgroup *configs.Cgroup) error {
+ if cgroup.Resources.CpuRtPeriod != 0 {
+ if err := writeFile(path, "cpu.rt_period_us", strconv.FormatUint(cgroup.Resources.CpuRtPeriod, 10)); err != nil {
+ return err
+ }
+ }
+ if cgroup.Resources.CpuRtRuntime != 0 {
+ if err := writeFile(path, "cpu.rt_runtime_us", strconv.FormatInt(cgroup.Resources.CpuRtRuntime, 10)); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (s *CpuGroup) Set(path string, cgroup *configs.Cgroup) error {
+ if cgroup.Resources.CpuShares != 0 {
+ if err := writeFile(path, "cpu.shares", strconv.FormatUint(cgroup.Resources.CpuShares, 10)); err != nil {
+ return err
+ }
+ }
+ if cgroup.Resources.CpuPeriod != 0 {
+ if err := writeFile(path, "cpu.cfs_period_us", strconv.FormatUint(cgroup.Resources.CpuPeriod, 10)); err != nil {
+ return err
+ }
+ }
+ if cgroup.Resources.CpuQuota != 0 {
+ if err := writeFile(path, "cpu.cfs_quota_us", strconv.FormatInt(cgroup.Resources.CpuQuota, 10)); err != nil {
+ return err
+ }
+ }
+ if err := s.SetRtSched(path, cgroup); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (s *CpuGroup) Remove(d *cgroupData) error {
+ return removePath(d.path("cpu"))
+}
+
+func (s *CpuGroup) GetStats(path string, stats *cgroups.Stats) error {
+ f, err := os.Open(filepath.Join(path, "cpu.stat"))
+ if err != nil {
+ if os.IsNotExist(err) {
+ return nil
+ }
+ return err
+ }
+ defer f.Close()
+
+ sc := bufio.NewScanner(f)
+ for sc.Scan() {
+ t, v, err := getCgroupParamKeyValue(sc.Text())
+ if err != nil {
+ return err
+ }
+ switch t {
+ case "nr_periods":
+ stats.CpuStats.ThrottlingData.Periods = v
+
+ case "nr_throttled":
+ stats.CpuStats.ThrottlingData.ThrottledPeriods = v
+
+ case "throttled_time":
+ stats.CpuStats.ThrottlingData.ThrottledTime = v
+ }
+ }
+ return nil
+}
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpu_test.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpu_test.go
new file mode 100644
index 0000000000..6369c91ad6
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpu_test.go
@@ -0,0 +1,209 @@
+// +build linux
+
+package fs
+
+import (
+ "fmt"
+ "strconv"
+ "testing"
+
+ "github.com/opencontainers/runc/libcontainer/cgroups"
+)
+
+func TestCpuSetShares(t *testing.T) {
+ helper := NewCgroupTestUtil("cpu", t)
+ defer helper.cleanup()
+
+ const (
+ sharesBefore = 1024
+ sharesAfter = 512
+ )
+
+ helper.writeFileContents(map[string]string{
+ "cpu.shares": strconv.Itoa(sharesBefore),
+ })
+
+ helper.CgroupData.config.Resources.CpuShares = sharesAfter
+ cpu := &CpuGroup{}
+ if err := cpu.Set(helper.CgroupPath, helper.CgroupData.config); err != nil {
+ t.Fatal(err)
+ }
+
+ value, err := getCgroupParamUint(helper.CgroupPath, "cpu.shares")
+ if err != nil {
+ t.Fatalf("Failed to parse cpu.shares - %s", err)
+ }
+
+ if value != sharesAfter {
+ t.Fatal("Got the wrong value, set cpu.shares failed.")
+ }
+}
+
+func TestCpuSetBandWidth(t *testing.T) {
+ helper := NewCgroupTestUtil("cpu", t)
+ defer helper.cleanup()
+
+ const (
+ quotaBefore = 8000
+ quotaAfter = 5000
+ periodBefore = 10000
+ periodAfter = 7000
+ rtRuntimeBefore = 8000
+ rtRuntimeAfter = 5000
+ rtPeriodBefore = 10000
+ rtPeriodAfter = 7000
+ )
+
+ helper.writeFileContents(map[string]string{
+ "cpu.cfs_quota_us": strconv.Itoa(quotaBefore),
+ "cpu.cfs_period_us": strconv.Itoa(periodBefore),
+ "cpu.rt_runtime_us": strconv.Itoa(rtRuntimeBefore),
+ "cpu.rt_period_us": strconv.Itoa(rtPeriodBefore),
+ })
+
+ helper.CgroupData.config.Resources.CpuQuota = quotaAfter
+ helper.CgroupData.config.Resources.CpuPeriod = periodAfter
+ helper.CgroupData.config.Resources.CpuRtRuntime = rtRuntimeAfter
+ helper.CgroupData.config.Resources.CpuRtPeriod = rtPeriodAfter
+ cpu := &CpuGroup{}
+ if err := cpu.Set(helper.CgroupPath, helper.CgroupData.config); err != nil {
+ t.Fatal(err)
+ }
+
+ quota, err := getCgroupParamUint(helper.CgroupPath, "cpu.cfs_quota_us")
+ if err != nil {
+ t.Fatalf("Failed to parse cpu.cfs_quota_us - %s", err)
+ }
+ if quota != quotaAfter {
+ t.Fatal("Got the wrong value, set cpu.cfs_quota_us failed.")
+ }
+
+ period, err := getCgroupParamUint(helper.CgroupPath, "cpu.cfs_period_us")
+ if err != nil {
+ t.Fatalf("Failed to parse cpu.cfs_period_us - %s", err)
+ }
+ if period != periodAfter {
+ t.Fatal("Got the wrong value, set cpu.cfs_period_us failed.")
+ }
+ rtRuntime, err := getCgroupParamUint(helper.CgroupPath, "cpu.rt_runtime_us")
+ if err != nil {
+ t.Fatalf("Failed to parse cpu.rt_runtime_us - %s", err)
+ }
+ if rtRuntime != rtRuntimeAfter {
+ t.Fatal("Got the wrong value, set cpu.rt_runtime_us failed.")
+ }
+ rtPeriod, err := getCgroupParamUint(helper.CgroupPath, "cpu.rt_period_us")
+ if err != nil {
+ t.Fatalf("Failed to parse cpu.rt_period_us - %s", err)
+ }
+ if rtPeriod != rtPeriodAfter {
+ t.Fatal("Got the wrong value, set cpu.rt_period_us failed.")
+ }
+}
+
+func TestCpuStats(t *testing.T) {
+ helper := NewCgroupTestUtil("cpu", t)
+ defer helper.cleanup()
+
+ const (
+ nrPeriods = 2000
+ nrThrottled = 200
+ throttledTime = uint64(18446744073709551615)
+ )
+
+ cpuStatContent := fmt.Sprintf("nr_periods %d\n nr_throttled %d\n throttled_time %d\n",
+ nrPeriods, nrThrottled, throttledTime)
+ helper.writeFileContents(map[string]string{
+ "cpu.stat": cpuStatContent,
+ })
+
+ cpu := &CpuGroup{}
+ actualStats := *cgroups.NewStats()
+ err := cpu.GetStats(helper.CgroupPath, &actualStats)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ expectedStats := cgroups.ThrottlingData{
+ Periods: nrPeriods,
+ ThrottledPeriods: nrThrottled,
+ ThrottledTime: throttledTime}
+
+ expectThrottlingDataEquals(t, expectedStats, actualStats.CpuStats.ThrottlingData)
+}
+
+func TestNoCpuStatFile(t *testing.T) {
+ helper := NewCgroupTestUtil("cpu", t)
+ defer helper.cleanup()
+
+ cpu := &CpuGroup{}
+ actualStats := *cgroups.NewStats()
+ err := cpu.GetStats(helper.CgroupPath, &actualStats)
+ if err != nil {
+ t.Fatal("Expected not to fail, but did")
+ }
+}
+
+func TestInvalidCpuStat(t *testing.T) {
+ helper := NewCgroupTestUtil("cpu", t)
+ defer helper.cleanup()
+ cpuStatContent := `nr_periods 2000
+ nr_throttled 200
+ throttled_time fortytwo`
+ helper.writeFileContents(map[string]string{
+ "cpu.stat": cpuStatContent,
+ })
+
+ cpu := &CpuGroup{}
+ actualStats := *cgroups.NewStats()
+ err := cpu.GetStats(helper.CgroupPath, &actualStats)
+ if err == nil {
+ t.Fatal("Expected failed stat parsing.")
+ }
+}
+
+func TestCpuSetRtSchedAtApply(t *testing.T) {
+ helper := NewCgroupTestUtil("cpu", t)
+ defer helper.cleanup()
+
+ const (
+ rtRuntimeBefore = 0
+ rtRuntimeAfter = 5000
+ rtPeriodBefore = 0
+ rtPeriodAfter = 7000
+ )
+
+ helper.writeFileContents(map[string]string{
+ "cpu.rt_runtime_us": strconv.Itoa(rtRuntimeBefore),
+ "cpu.rt_period_us": strconv.Itoa(rtPeriodBefore),
+ })
+
+ helper.CgroupData.config.Resources.CpuRtRuntime = rtRuntimeAfter
+ helper.CgroupData.config.Resources.CpuRtPeriod = rtPeriodAfter
+ cpu := &CpuGroup{}
+ if err := cpu.ApplyDir(helper.CgroupPath, helper.CgroupData.config, 1234); err != nil {
+ t.Fatal(err)
+ }
+
+ rtRuntime, err := getCgroupParamUint(helper.CgroupPath, "cpu.rt_runtime_us")
+ if err != nil {
+ t.Fatalf("Failed to parse cpu.rt_runtime_us - %s", err)
+ }
+ if rtRuntime != rtRuntimeAfter {
+ t.Fatal("Got the wrong value, set cpu.rt_runtime_us failed.")
+ }
+ rtPeriod, err := getCgroupParamUint(helper.CgroupPath, "cpu.rt_period_us")
+ if err != nil {
+ t.Fatalf("Failed to parse cpu.rt_period_us - %s", err)
+ }
+ if rtPeriod != rtPeriodAfter {
+ t.Fatal("Got the wrong value, set cpu.rt_period_us failed.")
+ }
+ pid, err := getCgroupParamUint(helper.CgroupPath, "cgroup.procs")
+ if err != nil {
+ t.Fatalf("Failed to parse cgroup.procs - %s", err)
+ }
+ if pid != 1234 {
+ t.Fatal("Got the wrong value, set cgroup.procs failed.")
+ }
+}
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuacct.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuacct.go
new file mode 100644
index 0000000000..53afbaddf1
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuacct.go
@@ -0,0 +1,121 @@
+// +build linux
+
+package fs
+
+import (
+ "fmt"
+ "io/ioutil"
+ "path/filepath"
+ "strconv"
+ "strings"
+
+ "github.com/opencontainers/runc/libcontainer/cgroups"
+ "github.com/opencontainers/runc/libcontainer/configs"
+ "github.com/opencontainers/runc/libcontainer/system"
+)
+
+const (
+ cgroupCpuacctStat = "cpuacct.stat"
+ nanosecondsInSecond = 1000000000
+)
+
+var clockTicks = uint64(system.GetClockTicks())
+
+type CpuacctGroup struct {
+}
+
+func (s *CpuacctGroup) Name() string {
+ return "cpuacct"
+}
+
+func (s *CpuacctGroup) Apply(d *cgroupData) error {
+ // we just want to join this group even though we don't set anything
+ if _, err := d.join("cpuacct"); err != nil && !cgroups.IsNotFound(err) {
+ return err
+ }
+
+ return nil
+}
+
+func (s *CpuacctGroup) Set(path string, cgroup *configs.Cgroup) error {
+ return nil
+}
+
+func (s *CpuacctGroup) Remove(d *cgroupData) error {
+ return removePath(d.path("cpuacct"))
+}
+
+func (s *CpuacctGroup) GetStats(path string, stats *cgroups.Stats) error {
+ userModeUsage, kernelModeUsage, err := getCpuUsageBreakdown(path)
+ if err != nil {
+ return err
+ }
+
+ totalUsage, err := getCgroupParamUint(path, "cpuacct.usage")
+ if err != nil {
+ return err
+ }
+
+ percpuUsage, err := getPercpuUsage(path)
+ if err != nil {
+ return err
+ }
+
+ stats.CpuStats.CpuUsage.TotalUsage = totalUsage
+ stats.CpuStats.CpuUsage.PercpuUsage = percpuUsage
+ stats.CpuStats.CpuUsage.UsageInUsermode = userModeUsage
+ stats.CpuStats.CpuUsage.UsageInKernelmode = kernelModeUsage
+ return nil
+}
+
+// Returns user and kernel usage breakdown in nanoseconds.
+func getCpuUsageBreakdown(path string) (uint64, uint64, error) {
+ userModeUsage := uint64(0)
+ kernelModeUsage := uint64(0)
+ const (
+ userField = "user"
+ systemField = "system"
+ )
+
+ // Expected format:
+ // user <usage in ticks>
+ // system <usage in ticks>
+ data, err := ioutil.ReadFile(filepath.Join(path, cgroupCpuacctStat))
+ if err != nil {
+ return 0, 0, err
+ }
+ fields := strings.Fields(string(data))
+ if len(fields) != 4 {
+ return 0, 0, fmt.Errorf("failure - %s is expected to have 4 fields", filepath.Join(path, cgroupCpuacctStat))
+ }
+ if fields[0] != userField {
+ return 0, 0, fmt.Errorf("unexpected field %q in %q, expected %q", fields[0], cgroupCpuacctStat, userField)
+ }
+ if fields[2] != systemField {
+ return 0, 0, fmt.Errorf("unexpected field %q in %q, expected %q", fields[2], cgroupCpuacctStat, systemField)
+ }
+ if userModeUsage, err = strconv.ParseUint(fields[1], 10, 64); err != nil {
+ return 0, 0, err
+ }
+ if kernelModeUsage, err = strconv.ParseUint(fields[3], 10, 64); err != nil {
+ return 0, 0, err
+ }
+
+ return (userModeUsage * nanosecondsInSecond) / clockTicks, (kernelModeUsage * nanosecondsInSecond) / clockTicks, nil
+}
+
+func getPercpuUsage(path string) ([]uint64, error) {
+ percpuUsage := []uint64{}
+ data, err := ioutil.ReadFile(filepath.Join(path, "cpuacct.usage_percpu"))
+ if err != nil {
+ return percpuUsage, err
+ }
+ for _, value := range strings.Fields(string(data)) {
+ value, err := strconv.ParseUint(value, 10, 64)
+ if err != nil {
+ return percpuUsage, fmt.Errorf("Unable to convert param value to uint64: %s", err)
+ }
+ percpuUsage = append(percpuUsage, value)
+ }
+ return percpuUsage, nil
+}
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuset.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuset.go
new file mode 100644
index 0000000000..e61994fc3c
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuset.go
@@ -0,0 +1,183 @@
+// +build linux
+
+package fs
+
+import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+
+ "github.com/sirupsen/logrus"
+ "github.com/opencontainers/runc/libcontainer/cgroups"
+ "github.com/opencontainers/runc/libcontainer/configs"
+ libcontainerUtils "github.com/opencontainers/runc/libcontainer/utils"
+)
+
+type CpusetGroup struct {
+}
+
+func (s *CpusetGroup) Name() string {
+ return "cpuset"
+}
+
+func (s *CpusetGroup) Apply(d *cgroupData) error {
+ dir, err := d.path("cpuset")
+ if err != nil && !cgroups.IsNotFound(err) {
+ return err
+ }
+ return s.ApplyDir(dir, d.config, d.pid)
+}
+
+func (s *CpusetGroup) Set(path string, cgroup *configs.Cgroup) error {
+ if cgroup.Resources.CpusetCpus != "" {
+ if err := writeFile(path, "cpuset.cpus", cgroup.Resources.CpusetCpus); err != nil {
+ return err
+ }
+ }
+ if cgroup.Resources.CpusetMems != "" {
+ if err := writeFile(path, "cpuset.mems", cgroup.Resources.CpusetMems); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (s *CpusetGroup) Remove(d *cgroupData) error {
+ return removePath(d.path("cpuset"))
+}
+
+func (s *CpusetGroup) GetStats(path string, stats *cgroups.Stats) error {
+ return nil
+}
+
+func (s *CpusetGroup) ApplyDir(dir string, cgroup *configs.Cgroup, pid int) error {
+ // This might happen if we have no cpuset cgroup mounted.
+ // Just do nothing and don't fail.
+ if dir == "" {
+ return nil
+ }
+ root, err := getCgroupRoot()
+ if err != nil {
+ return err
+ }
+ // 'ensureParent' start with parent because we don't want to
+ // explicitly inherit from parent, it could conflict with
+ // 'cpuset.cpu_exclusive'.
+ if err := s.ensureParent(filepath.Dir(dir), root); err != nil {
+ return err
+ }
+ if err := os.MkdirAll(dir, 0755); err != nil {
+ return err
+ }
+ // We didn't inherit cpuset configs from parent, but we have
+ // to ensure cpuset configs are set before moving task into the
+ // cgroup.
+ // The logic is, if user specified cpuset configs, use these
+ // specified configs, otherwise, inherit from parent. This makes
+ // cpuset configs work correctly with 'cpuset.cpu_exclusive', and
+ // keep backward compatbility.
+ if err := s.ensureCpusAndMems(dir, cgroup); err != nil {
+ return err
+ }
+
+ // because we are not using d.join we need to place the pid into the procs file
+ // unlike the other subsystems
+ if err := cgroups.WriteCgroupProc(dir, pid); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (s *CpusetGroup) getSubsystemSettings(parent string) (cpus []byte, mems []byte, err error) {
+ defer func() {
+ if err != nil {
+ minfo, err1 := ioutil.ReadFile("/proc/self/mountinfo")
+ if err1 != nil {
+ logrus.Errorf("Failed to read mountinfo when getSubsystemSettings get an error")
+ }
+
+ dirInfo := ""
+ fs, err2 := ioutil.ReadDir(parent)
+ if err2 != nil {
+ logrus.Errorf("Failed to read mountinfo when getSubsystemSettings get an error")
+ }
+ for _, f := range fs {
+ dirInfo = dirInfo + " " + f.Name()
+ }
+
+ logrus.Errorf("Read cpuset cgroup failed, print mountinfo and cgroup info here"+
+ "path: %s, mountinfo: [%s], dirinfo: [%s]", parent, string(minfo), dirInfo)
+ }
+ }()
+ if cpus, err = ioutil.ReadFile(filepath.Join(parent, "cpuset.cpus")); err != nil {
+ return
+ }
+ if mems, err = ioutil.ReadFile(filepath.Join(parent, "cpuset.mems")); err != nil {
+ return
+ }
+ return cpus, mems, nil
+}
+
+// ensureParent makes sure that the parent directory of current is created
+// and populated with the proper cpus and mems files copied from
+// it's parent.
+func (s *CpusetGroup) ensureParent(current, root string) error {
+ parent := filepath.Dir(current)
+ if libcontainerUtils.CleanPath(parent) == root {
+ return nil
+ }
+ // Avoid infinite recursion.
+ if parent == current {
+ return fmt.Errorf("cpuset: cgroup parent path outside cgroup root")
+ }
+ if err := s.ensureParent(parent, root); err != nil {
+ return err
+ }
+ if err := os.MkdirAll(current, 0755); err != nil {
+ return err
+ }
+ return s.copyIfNeeded(current, parent)
+}
+
+// copyIfNeeded copies the cpuset.cpus and cpuset.mems from the parent
+// directory to the current directory if the file's contents are 0
+func (s *CpusetGroup) copyIfNeeded(current, parent string) error {
+ var (
+ err error
+ currentCpus, currentMems []byte
+ parentCpus, parentMems []byte
+ )
+
+ if currentCpus, currentMems, err = s.getSubsystemSettings(current); err != nil {
+ return err
+ }
+ if parentCpus, parentMems, err = s.getSubsystemSettings(parent); err != nil {
+ return err
+ }
+
+ if s.isEmpty(currentCpus) {
+ if err := writeFile(current, "cpuset.cpus", string(parentCpus)); err != nil {
+ return err
+ }
+ }
+ if s.isEmpty(currentMems) {
+ if err := writeFile(current, "cpuset.mems", string(parentMems)); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (s *CpusetGroup) isEmpty(b []byte) bool {
+ return len(bytes.Trim(b, "\n")) == 0
+}
+
+func (s *CpusetGroup) ensureCpusAndMems(path string, cgroup *configs.Cgroup) error {
+ if err := s.Set(path, cgroup); err != nil {
+ return err
+ }
+ return s.copyIfNeeded(path, filepath.Dir(path))
+}
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuset_test.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuset_test.go
new file mode 100644
index 0000000000..0f929151fc
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuset_test.go
@@ -0,0 +1,65 @@
+// +build linux
+
+package fs
+
+import (
+ "testing"
+)
+
+func TestCpusetSetCpus(t *testing.T) {
+ helper := NewCgroupTestUtil("cpuset", t)
+ defer helper.cleanup()
+
+ const (
+ cpusBefore = "0"
+ cpusAfter = "1-3"
+ )
+
+ helper.writeFileContents(map[string]string{
+ "cpuset.cpus": cpusBefore,
+ })
+
+ helper.CgroupData.config.Resources.CpusetCpus = cpusAfter
+ cpuset := &CpusetGroup{}
+ if err := cpuset.Set(helper.CgroupPath, helper.CgroupData.config); err != nil {
+ t.Fatal(err)
+ }
+
+ value, err := getCgroupParamString(helper.CgroupPath, "cpuset.cpus")
+ if err != nil {
+ t.Fatalf("Failed to parse cpuset.cpus - %s", err)
+ }
+
+ if value != cpusAfter {
+ t.Fatal("Got the wrong value, set cpuset.cpus failed.")
+ }
+}
+
+func TestCpusetSetMems(t *testing.T) {
+ helper := NewCgroupTestUtil("cpuset", t)
+ defer helper.cleanup()
+
+ const (
+ memsBefore = "0"
+ memsAfter = "1"
+ )
+
+ helper.writeFileContents(map[string]string{
+ "cpuset.mems": memsBefore,
+ })
+
+ helper.CgroupData.config.Resources.CpusetMems = memsAfter
+ cpuset := &CpusetGroup{}
+ if err := cpuset.Set(helper.CgroupPath, helper.CgroupData.config); err != nil {
+ t.Fatal(err)
+ }
+
+ value, err := getCgroupParamString(helper.CgroupPath, "cpuset.mems")
+ if err != nil {
+ t.Fatalf("Failed to parse cpuset.mems - %s", err)
+ }
+
+ if value != memsAfter {
+ t.Fatal("Got the wrong value, set cpuset.mems failed.")
+ }
+}
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/devices.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/devices.go
new file mode 100644
index 0000000000..0ac5b4ed70
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/devices.go
@@ -0,0 +1,80 @@
+// +build linux
+
+package fs
+
+import (
+ "github.com/opencontainers/runc/libcontainer/cgroups"
+ "github.com/opencontainers/runc/libcontainer/configs"
+ "github.com/opencontainers/runc/libcontainer/system"
+)
+
+type DevicesGroup struct {
+}
+
+func (s *DevicesGroup) Name() string {
+ return "devices"
+}
+
+func (s *DevicesGroup) Apply(d *cgroupData) error {
+ _, err := d.join("devices")
+ if err != nil {
+ // We will return error even it's `not found` error, devices
+ // cgroup is hard requirement for container's security.
+ return err
+ }
+ return nil
+}
+
+func (s *DevicesGroup) Set(path string, cgroup *configs.Cgroup) error {
+ if system.RunningInUserNS() {
+ return nil
+ }
+
+ devices := cgroup.Resources.Devices
+ if len(devices) > 0 {
+ for _, dev := range devices {
+ file := "devices.deny"
+ if dev.Allow {
+ file = "devices.allow"
+ }
+ if err := writeFile(path, file, dev.CgroupString()); err != nil {
+ return err
+ }
+ }
+ return nil
+ }
+ if cgroup.Resources.AllowAllDevices != nil {
+ if *cgroup.Resources.AllowAllDevices == false {
+ if err := writeFile(path, "devices.deny", "a"); err != nil {
+ return err
+ }
+
+ for _, dev := range cgroup.Resources.AllowedDevices {
+ if err := writeFile(path, "devices.allow", dev.CgroupString()); err != nil {
+ return err
+ }
+ }
+ return nil
+ }
+
+ if err := writeFile(path, "devices.allow", "a"); err != nil {
+ return err
+ }
+ }
+
+ for _, dev := range cgroup.Resources.DeniedDevices {
+ if err := writeFile(path, "devices.deny", dev.CgroupString()); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (s *DevicesGroup) Remove(d *cgroupData) error {
+ return removePath(d.path("devices"))
+}
+
+func (s *DevicesGroup) GetStats(path string, stats *cgroups.Stats) error {
+ return nil
+}
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/devices_test.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/devices_test.go
new file mode 100644
index 0000000000..fc635b990f
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/devices_test.go
@@ -0,0 +1,98 @@
+// +build linux
+
+package fs
+
+import (
+ "testing"
+
+ "github.com/opencontainers/runc/libcontainer/configs"
+)
+
+var (
+ allowedDevices = []*configs.Device{
+ {
+ Path: "/dev/zero",
+ Type: 'c',
+ Major: 1,
+ Minor: 5,
+ Permissions: "rwm",
+ FileMode: 0666,
+ },
+ }
+ allowedList = "c 1:5 rwm"
+ deniedDevices = []*configs.Device{
+ {
+ Path: "/dev/null",
+ Type: 'c',
+ Major: 1,
+ Minor: 3,
+ Permissions: "rwm",
+ FileMode: 0666,
+ },
+ }
+ deniedList = "c 1:3 rwm"
+)
+
+func TestDevicesSetAllow(t *testing.T) {
+ helper := NewCgroupTestUtil("devices", t)
+ defer helper.cleanup()
+
+ helper.writeFileContents(map[string]string{
+ "devices.deny": "a",
+ })
+ allowAllDevices := false
+ helper.CgroupData.config.Resources.AllowAllDevices = &allowAllDevices
+ helper.CgroupData.config.Resources.AllowedDevices = allowedDevices
+ devices := &DevicesGroup{}
+ if err := devices.Set(helper.CgroupPath, helper.CgroupData.config); err != nil {
+ t.Fatal(err)
+ }
+
+ value, err := getCgroupParamString(helper.CgroupPath, "devices.allow")
+ if err != nil {
+ t.Fatalf("Failed to parse devices.allow - %s", err)
+ }
+
+ if value != allowedList {
+ t.Fatal("Got the wrong value, set devices.allow failed.")
+ }
+
+ // When AllowAllDevices is nil, devices.allow file should not be modified.
+ helper.CgroupData.config.Resources.AllowAllDevices = nil
+ if err := devices.Set(helper.CgroupPath, helper.CgroupData.config); err != nil {
+ t.Fatal(err)
+ }
+ value, err = getCgroupParamString(helper.CgroupPath, "devices.allow")
+ if err != nil {
+ t.Fatalf("Failed to parse devices.allow - %s", err)
+ }
+ if value != allowedList {
+ t.Fatal("devices policy shouldn't have changed on AllowedAllDevices=nil.")
+ }
+}
+
+func TestDevicesSetDeny(t *testing.T) {
+ helper := NewCgroupTestUtil("devices", t)
+ defer helper.cleanup()
+
+ helper.writeFileContents(map[string]string{
+ "devices.allow": "a",
+ })
+
+ allowAllDevices := true
+ helper.CgroupData.config.Resources.AllowAllDevices = &allowAllDevices
+ helper.CgroupData.config.Resources.DeniedDevices = deniedDevices
+ devices := &DevicesGroup{}
+ if err := devices.Set(helper.CgroupPath, helper.CgroupData.config); err != nil {
+ t.Fatal(err)
+ }
+
+ value, err := getCgroupParamString(helper.CgroupPath, "devices.deny")
+ if err != nil {
+ t.Fatalf("Failed to parse devices.deny - %s", err)
+ }
+
+ if value != deniedList {
+ t.Fatal("Got the wrong value, set devices.deny failed.")
+ }
+}
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/files.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/files.go
new file mode 100644
index 0000000000..70e95240c8
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/files.go
@@ -0,0 +1,72 @@
+// +build linux
+
+package fs
+
+import (
+ "fmt"
+ "strconv"
+
+ "github.com/opencontainers/runc/libcontainer/cgroups"
+ "github.com/opencontainers/runc/libcontainer/configs"
+ "path/filepath"
+)
+
+type FilesGroup struct {
+}
+
+func (s *FilesGroup) Name() string {
+ return "files"
+}
+
+func (s *FilesGroup) Apply(d *cgroupData) error {
+ _, err := d.join("files")
+ if err != nil && !cgroups.IsNotFound(err) {
+ return err
+ }
+ return nil
+}
+
+func (s *FilesGroup) Set(path string, cgroup *configs.Cgroup) error {
+ if cgroup.Resources.FilesLimit != 0 {
+ // "max" is the fallback value.
+ limit := "max"
+ if cgroup.Resources.FilesLimit > 0 {
+ limit = strconv.FormatInt(cgroup.Resources.FilesLimit, 10)
+ }
+
+ if err := writeFile(path, "files.limit", limit); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (s *FilesGroup) Remove(d *cgroupData) error {
+ return removePath(d.path("files"))
+}
+
+func (s *FilesGroup) GetStats(path string, stats *cgroups.Stats) error {
+ usage, err := getCgroupParamUint(path, "files.usage")
+ if err != nil {
+ return fmt.Errorf("failed to parse files.usage - %s", err)
+ }
+
+ maxString, err := getCgroupParamString(path, "files.limit")
+ if err != nil {
+ return fmt.Errorf("failed to parse files.limit - %s", err)
+ }
+
+ // Default if files.limit == "max" is 0 -- which represents "no limit".
+ var max uint64
+ if maxString != "max" {
+ max, err = parseUint(maxString, 10, 64)
+ if err != nil {
+ return fmt.Errorf("failed to parse files.limit -- unable to parse %q as a uint from Cgroup file %q", maxString, filepath.Join(path, "file.limits"))
+ }
+ }
+
+ stats.FilesStats.Usage = usage
+ stats.FilesStats.Limit = max
+ return nil
+}
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/freezer.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/freezer.go
new file mode 100644
index 0000000000..e70dfe3b95
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/freezer.go
@@ -0,0 +1,61 @@
+// +build linux
+
+package fs
+
+import (
+ "fmt"
+ "strings"
+ "time"
+
+ "github.com/opencontainers/runc/libcontainer/cgroups"
+ "github.com/opencontainers/runc/libcontainer/configs"
+)
+
+type FreezerGroup struct {
+}
+
+func (s *FreezerGroup) Name() string {
+ return "freezer"
+}
+
+func (s *FreezerGroup) Apply(d *cgroupData) error {
+ _, err := d.join("freezer")
+ if err != nil && !cgroups.IsNotFound(err) {
+ return err
+ }
+ return nil
+}
+
+func (s *FreezerGroup) Set(path string, cgroup *configs.Cgroup) error {
+ switch cgroup.Resources.Freezer {
+ case configs.Frozen, configs.Thawed:
+ if err := writeFile(path, "freezer.state", string(cgroup.Resources.Freezer)); err != nil {
+ return err
+ }
+
+ for {
+ state, err := readFile(path, "freezer.state")
+ if err != nil {
+ return err
+ }
+ if strings.TrimSpace(state) == string(cgroup.Resources.Freezer) {
+ break
+ }
+ time.Sleep(1 * time.Millisecond)
+ }
+ case configs.Undefined:
+ return nil
+ default:
+ return fmt.Errorf("Invalid argument '%s' to freezer.state", string(cgroup.Resources.Freezer))
+ }
+
+ return nil
+}
+
+func (s *FreezerGroup) Remove(d *cgroupData) error {
+ return removePath(d.path("freezer"))
+}
+
+func (s *FreezerGroup) GetStats(path string, stats *cgroups.Stats) error {
+ return nil
+}
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/freezer_test.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/freezer_test.go
new file mode 100644
index 0000000000..77708db9a6
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/freezer_test.go
@@ -0,0 +1,47 @@
+// +build linux
+
+package fs
+
+import (
+ "testing"
+
+ "github.com/opencontainers/runc/libcontainer/configs"
+)
+
+func TestFreezerSetState(t *testing.T) {
+ helper := NewCgroupTestUtil("freezer", t)
+ defer helper.cleanup()
+
+ helper.writeFileContents(map[string]string{
+ "freezer.state": string(configs.Frozen),
+ })
+
+ helper.CgroupData.config.Resources.Freezer = configs.Thawed
+ freezer := &FreezerGroup{}
+ if err := freezer.Set(helper.CgroupPath, helper.CgroupData.config); err != nil {
+ t.Fatal(err)
+ }
+
+ value, err := getCgroupParamString(helper.CgroupPath, "freezer.state")
+ if err != nil {
+ t.Fatalf("Failed to parse freezer.state - %s", err)
+ }
+ if value != string(configs.Thawed) {
+ t.Fatal("Got the wrong value, set freezer.state failed.")
+ }
+}
+
+func TestFreezerSetInvalidState(t *testing.T) {
+ helper := NewCgroupTestUtil("freezer", t)
+ defer helper.cleanup()
+
+ const (
+ invalidArg configs.FreezerState = "Invalid"
+ )
+
+ helper.CgroupData.config.Resources.Freezer = invalidArg
+ freezer := &FreezerGroup{}
+ if err := freezer.Set(helper.CgroupPath, helper.CgroupData.config); err == nil {
+ t.Fatal("Failed to return invalid argument error")
+ }
+}
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/fs_unsupported.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/fs_unsupported.go
new file mode 100644
index 0000000000..3ef9e03158
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/fs_unsupported.go
@@ -0,0 +1,3 @@
+// +build !linux
+
+package fs
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/hugetlb.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/hugetlb.go
new file mode 100644
index 0000000000..2f9727719d
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/hugetlb.go
@@ -0,0 +1,71 @@
+// +build linux
+
+package fs
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+
+ "github.com/opencontainers/runc/libcontainer/cgroups"
+ "github.com/opencontainers/runc/libcontainer/configs"
+)
+
+type HugetlbGroup struct {
+}
+
+func (s *HugetlbGroup) Name() string {
+ return "hugetlb"
+}
+
+func (s *HugetlbGroup) Apply(d *cgroupData) error {
+ _, err := d.join("hugetlb")
+ if err != nil && !cgroups.IsNotFound(err) {
+ return err
+ }
+ return nil
+}
+
+func (s *HugetlbGroup) Set(path string, cgroup *configs.Cgroup) error {
+ for _, hugetlb := range cgroup.Resources.HugetlbLimit {
+ if err := writeFile(path, strings.Join([]string{"hugetlb", hugetlb.Pagesize, "limit_in_bytes"}, "."), strconv.FormatUint(hugetlb.Limit, 10)); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (s *HugetlbGroup) Remove(d *cgroupData) error {
+ return removePath(d.path("hugetlb"))
+}
+
+func (s *HugetlbGroup) GetStats(path string, stats *cgroups.Stats) error {
+ hugetlbStats := cgroups.HugetlbStats{}
+ for _, pageSize := range HugePageSizes {
+ usage := strings.Join([]string{"hugetlb", pageSize, "usage_in_bytes"}, ".")
+ value, err := getCgroupParamUint(path, usage)
+ if err != nil {
+ return fmt.Errorf("failed to parse %s - %v", usage, err)
+ }
+ hugetlbStats.Usage = value
+
+ maxUsage := strings.Join([]string{"hugetlb", pageSize, "max_usage_in_bytes"}, ".")
+ value, err = getCgroupParamUint(path, maxUsage)
+ if err != nil {
+ return fmt.Errorf("failed to parse %s - %v", maxUsage, err)
+ }
+ hugetlbStats.MaxUsage = value
+
+ failcnt := strings.Join([]string{"hugetlb", pageSize, "failcnt"}, ".")
+ value, err = getCgroupParamUint(path, failcnt)
+ if err != nil {
+ return fmt.Errorf("failed to parse %s - %v", failcnt, err)
+ }
+ hugetlbStats.Failcnt = value
+
+ stats.HugetlbStats[pageSize] = hugetlbStats
+ }
+
+ return nil
+}
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/hugetlb_test.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/hugetlb_test.go
new file mode 100644
index 0000000000..2d41c4eb20
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/hugetlb_test.go
@@ -0,0 +1,154 @@
+// +build linux
+
+package fs
+
+import (
+ "fmt"
+ "strconv"
+ "testing"
+
+ "github.com/opencontainers/runc/libcontainer/cgroups"
+ "github.com/opencontainers/runc/libcontainer/configs"
+)
+
+const (
+ hugetlbUsageContents = "128\n"
+ hugetlbMaxUsageContents = "256\n"
+ hugetlbFailcnt = "100\n"
+)
+
+var (
+ usage = "hugetlb.%s.usage_in_bytes"
+ limit = "hugetlb.%s.limit_in_bytes"
+ maxUsage = "hugetlb.%s.max_usage_in_bytes"
+ failcnt = "hugetlb.%s.failcnt"
+)
+
+func TestHugetlbSetHugetlb(t *testing.T) {
+ helper := NewCgroupTestUtil("hugetlb", t)
+ defer helper.cleanup()
+
+ const (
+ hugetlbBefore = 256
+ hugetlbAfter = 512
+ )
+
+ for _, pageSize := range HugePageSizes {
+ helper.writeFileContents(map[string]string{
+ fmt.Sprintf(limit, pageSize): strconv.Itoa(hugetlbBefore),
+ })
+ }
+
+ for _, pageSize := range HugePageSizes {
+ helper.CgroupData.config.Resources.HugetlbLimit = []*configs.HugepageLimit{
+ {
+ Pagesize: pageSize,
+ Limit: hugetlbAfter,
+ },
+ }
+ hugetlb := &HugetlbGroup{}
+ if err := hugetlb.Set(helper.CgroupPath, helper.CgroupData.config); err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ for _, pageSize := range HugePageSizes {
+ limit := fmt.Sprintf(limit, pageSize)
+ value, err := getCgroupParamUint(helper.CgroupPath, limit)
+ if err != nil {
+ t.Fatalf("Failed to parse %s - %s", limit, err)
+ }
+ if value != hugetlbAfter {
+ t.Fatalf("Set hugetlb.limit_in_bytes failed. Expected: %v, Got: %v", hugetlbAfter, value)
+ }
+ }
+}
+
+func TestHugetlbStats(t *testing.T) {
+ helper := NewCgroupTestUtil("hugetlb", t)
+ defer helper.cleanup()
+ for _, pageSize := range HugePageSizes {
+ helper.writeFileContents(map[string]string{
+ fmt.Sprintf(usage, pageSize): hugetlbUsageContents,
+ fmt.Sprintf(maxUsage, pageSize): hugetlbMaxUsageContents,
+ fmt.Sprintf(failcnt, pageSize): hugetlbFailcnt,
+ })
+ }
+
+ hugetlb := &HugetlbGroup{}
+ actualStats := *cgroups.NewStats()
+ err := hugetlb.GetStats(helper.CgroupPath, &actualStats)
+ if err != nil {
+ t.Fatal(err)
+ }
+ expectedStats := cgroups.HugetlbStats{Usage: 128, MaxUsage: 256, Failcnt: 100}
+ for _, pageSize := range HugePageSizes {
+ expectHugetlbStatEquals(t, expectedStats, actualStats.HugetlbStats[pageSize])
+ }
+}
+
+func TestHugetlbStatsNoUsageFile(t *testing.T) {
+ helper := NewCgroupTestUtil("hugetlb", t)
+ defer helper.cleanup()
+ helper.writeFileContents(map[string]string{
+ maxUsage: hugetlbMaxUsageContents,
+ })
+
+ hugetlb := &HugetlbGroup{}
+ actualStats := *cgroups.NewStats()
+ err := hugetlb.GetStats(helper.CgroupPath, &actualStats)
+ if err == nil {
+ t.Fatal("Expected failure")
+ }
+}
+
+func TestHugetlbStatsNoMaxUsageFile(t *testing.T) {
+ helper := NewCgroupTestUtil("hugetlb", t)
+ defer helper.cleanup()
+ for _, pageSize := range HugePageSizes {
+ helper.writeFileContents(map[string]string{
+ fmt.Sprintf(usage, pageSize): hugetlbUsageContents,
+ })
+ }
+
+ hugetlb := &HugetlbGroup{}
+ actualStats := *cgroups.NewStats()
+ err := hugetlb.GetStats(helper.CgroupPath, &actualStats)
+ if err == nil {
+ t.Fatal("Expected failure")
+ }
+}
+
+func TestHugetlbStatsBadUsageFile(t *testing.T) {
+ helper := NewCgroupTestUtil("hugetlb", t)
+ defer helper.cleanup()
+ for _, pageSize := range HugePageSizes {
+ helper.writeFileContents(map[string]string{
+ fmt.Sprintf(usage, pageSize): "bad",
+ maxUsage: hugetlbMaxUsageContents,
+ })
+ }
+
+ hugetlb := &HugetlbGroup{}
+ actualStats := *cgroups.NewStats()
+ err := hugetlb.GetStats(helper.CgroupPath, &actualStats)
+ if err == nil {
+ t.Fatal("Expected failure")
+ }
+}
+
+func TestHugetlbStatsBadMaxUsageFile(t *testing.T) {
+ helper := NewCgroupTestUtil("hugetlb", t)
+ defer helper.cleanup()
+ helper.writeFileContents(map[string]string{
+ usage: hugetlbUsageContents,
+ maxUsage: "bad",
+ })
+
+ hugetlb := &HugetlbGroup{}
+ actualStats := *cgroups.NewStats()
+ err := hugetlb.GetStats(helper.CgroupPath, &actualStats)
+ if err == nil {
+ t.Fatal("Expected failure")
+ }
+}
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/memory.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/memory.go
new file mode 100644
index 0000000000..118cce8f9a
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/memory.go
@@ -0,0 +1,301 @@
+// +build linux
+
+package fs
+
+import (
+ "bufio"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strconv"
+ "strings"
+ "syscall"
+
+ "github.com/opencontainers/runc/libcontainer/cgroups"
+ "github.com/opencontainers/runc/libcontainer/configs"
+)
+
+const (
+ cgroupKernelMemoryLimit = "memory.kmem.limit_in_bytes"
+ cgroupMemorySwapLimit = "memory.memsw.limit_in_bytes"
+ cgroupMemoryLimit = "memory.limit_in_bytes"
+)
+
+type MemoryGroup struct {
+}
+
+func (s *MemoryGroup) Name() string {
+ return "memory"
+}
+
+func (s *MemoryGroup) Apply(d *cgroupData) (err error) {
+ path, err := d.path("memory")
+ if err != nil && !cgroups.IsNotFound(err) {
+ return err
+ } else if path == "" {
+ return nil
+ }
+ if memoryAssigned(d.config) {
+ if _, err := os.Stat(path); os.IsNotExist(err) {
+ if err := os.MkdirAll(path, 0755); err != nil {
+ return err
+ }
+ }
+ if d.config.KernelMemory != 0 {
+ if err := EnableKernelMemoryAccounting(path); err != nil {
+ return err
+ }
+ }
+ }
+ defer func() {
+ if err != nil {
+ os.RemoveAll(path)
+ }
+ }()
+
+ // We need to join memory cgroup after set memory limits, because
+ // kmem.limit_in_bytes can only be set when the cgroup is empty.
+ _, err = d.join("memory")
+ if err != nil && !cgroups.IsNotFound(err) {
+ return err
+ }
+ return nil
+}
+
+func EnableKernelMemoryAccounting(path string) error {
+ // Check if kernel memory is enabled
+ // We have to limit the kernel memory here as it won't be accounted at all
+ // until a limit is set on the cgroup and limit cannot be set once the
+ // cgroup has children, or if there are already tasks in the cgroup.
+ for _, i := range []int64{1, -1} {
+ if err := setKernelMemory(path, i); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func setKernelMemory(path string, kernelMemoryLimit int64) error {
+ if path == "" {
+ return fmt.Errorf("no such directory for %s", cgroupKernelMemoryLimit)
+ }
+ if !cgroups.PathExists(filepath.Join(path, cgroupKernelMemoryLimit)) {
+ // kernel memory is not enabled on the system so we should do nothing
+ return nil
+ }
+ if err := ioutil.WriteFile(filepath.Join(path, cgroupKernelMemoryLimit), []byte(strconv.FormatInt(kernelMemoryLimit, 10)), 0700); err != nil {
+ // Check if the error number returned by the syscall is "EBUSY"
+ // The EBUSY signal is returned on attempts to write to the
+ // memory.kmem.limit_in_bytes file if the cgroup has children or
+ // once tasks have been attached to the cgroup
+ if pathErr, ok := err.(*os.PathError); ok {
+ if errNo, ok := pathErr.Err.(syscall.Errno); ok {
+ if errNo == syscall.EBUSY {
+ return fmt.Errorf("failed to set %s, because either tasks have already joined this cgroup or it has children", cgroupKernelMemoryLimit)
+ }
+ }
+ }
+ return fmt.Errorf("failed to write %v to %v: %v", kernelMemoryLimit, cgroupKernelMemoryLimit, err)
+ }
+ return nil
+}
+
+func setMemoryAndSwap(path string, cgroup *configs.Cgroup) error {
+ // If the memory update is set to -1 we should also
+ // set swap to -1, it means unlimited memory.
+ if cgroup.Resources.Memory == -1 {
+ // Only set swap if it's enabled in kernel
+ if cgroups.PathExists(filepath.Join(path, cgroupMemorySwapLimit)) {
+ cgroup.Resources.MemorySwap = -1
+ }
+ }
+
+ // When memory and swap memory are both set, we need to handle the cases
+ // for updating container.
+ if cgroup.Resources.Memory != 0 && cgroup.Resources.MemorySwap != 0 {
+ memoryUsage, err := getMemoryData(path, "")
+ if err != nil {
+ return err
+ }
+
+ // When update memory limit, we should adapt the write sequence
+ // for memory and swap memory, so it won't fail because the new
+ // value and the old value don't fit kernel's validation.
+ if cgroup.Resources.MemorySwap == -1 || memoryUsage.Limit < uint64(cgroup.Resources.MemorySwap) {
+ if err := writeFile(path, cgroupMemorySwapLimit, strconv.FormatInt(cgroup.Resources.MemorySwap, 10)); err != nil {
+ return err
+ }
+ if err := writeFile(path, cgroupMemoryLimit, strconv.FormatInt(cgroup.Resources.Memory, 10)); err != nil {
+ return err
+ }
+ } else {
+ if err := writeFile(path, cgroupMemoryLimit, strconv.FormatInt(cgroup.Resources.Memory, 10)); err != nil {
+ return err
+ }
+ if err := writeFile(path, cgroupMemorySwapLimit, strconv.FormatInt(cgroup.Resources.MemorySwap, 10)); err != nil {
+ return err
+ }
+ }
+ } else {
+ if cgroup.Resources.Memory != 0 {
+ if err := writeFile(path, cgroupMemoryLimit, strconv.FormatInt(cgroup.Resources.Memory, 10)); err != nil {
+ return err
+ }
+ }
+ if cgroup.Resources.MemorySwap != 0 {
+ if err := writeFile(path, cgroupMemorySwapLimit, strconv.FormatInt(cgroup.Resources.MemorySwap, 10)); err != nil {
+ return err
+ }
+ }
+ }
+
+ return nil
+}
+
+func (s *MemoryGroup) Set(path string, cgroup *configs.Cgroup) error {
+ if err := setMemoryAndSwap(path, cgroup); err != nil {
+ return err
+ }
+
+ if cgroup.Resources.KernelMemory != 0 {
+ if err := setKernelMemory(path, cgroup.Resources.KernelMemory); err != nil {
+ return err
+ }
+ }
+
+ if cgroup.Resources.MemoryReservation != 0 {
+ if err := writeFile(path, "memory.soft_limit_in_bytes", strconv.FormatInt(cgroup.Resources.MemoryReservation, 10)); err != nil {
+ return err
+ }
+ }
+
+ if cgroup.Resources.KernelMemoryTCP != 0 {
+ if err := writeFile(path, "memory.kmem.tcp.limit_in_bytes", strconv.FormatInt(cgroup.Resources.KernelMemoryTCP, 10)); err != nil {
+ return err
+ }
+ }
+ if cgroup.Resources.OomKillDisable {
+ if err := writeFile(path, "memory.oom_control", "1"); err != nil {
+ return err
+ }
+ }
+ if cgroup.Resources.MemorySwappiness == nil || int64(*cgroup.Resources.MemorySwappiness) == -1 {
+ return nil
+ } else if *cgroup.Resources.MemorySwappiness <= 100 {
+ if err := writeFile(path, "memory.swappiness", strconv.FormatUint(*cgroup.Resources.MemorySwappiness, 10)); err != nil {
+ return err
+ }
+ } else {
+ return fmt.Errorf("invalid value:%d. valid memory swappiness range is 0-100", *cgroup.Resources.MemorySwappiness)
+ }
+
+ return nil
+}
+
+func (s *MemoryGroup) Remove(d *cgroupData) error {
+ return removePath(d.path("memory"))
+}
+
+func (s *MemoryGroup) GetStats(path string, stats *cgroups.Stats) error {
+ // Set stats from memory.stat.
+ statsFile, err := os.Open(filepath.Join(path, "memory.stat"))
+ if err != nil {
+ if os.IsNotExist(err) {
+ return nil
+ }
+ return err
+ }
+ defer statsFile.Close()
+
+ sc := bufio.NewScanner(statsFile)
+ for sc.Scan() {
+ t, v, err := getCgroupParamKeyValue(sc.Text())
+ if err != nil {
+ return fmt.Errorf("failed to parse memory.stat (%q) - %v", sc.Text(), err)
+ }
+ stats.MemoryStats.Stats[t] = v
+ }
+ stats.MemoryStats.Cache = stats.MemoryStats.Stats["cache"]
+
+ memoryUsage, err := getMemoryData(path, "")
+ if err != nil {
+ return err
+ }
+ stats.MemoryStats.Usage = memoryUsage
+ swapUsage, err := getMemoryData(path, "memsw")
+ if err != nil {
+ return err
+ }
+ stats.MemoryStats.SwapUsage = swapUsage
+ kernelUsage, err := getMemoryData(path, "kmem")
+ if err != nil {
+ return err
+ }
+ stats.MemoryStats.KernelUsage = kernelUsage
+ kernelTCPUsage, err := getMemoryData(path, "kmem.tcp")
+ if err != nil {
+ return err
+ }
+ stats.MemoryStats.KernelTCPUsage = kernelTCPUsage
+
+ return nil
+}
+
+func memoryAssigned(cgroup *configs.Cgroup) bool {
+ return cgroup.Resources.Memory != 0 ||
+ cgroup.Resources.MemoryReservation != 0 ||
+ cgroup.Resources.MemorySwap > 0 ||
+ cgroup.Resources.KernelMemory > 0 ||
+ cgroup.Resources.KernelMemoryTCP > 0 ||
+ cgroup.Resources.OomKillDisable ||
+ (cgroup.Resources.MemorySwappiness != nil && int64(*cgroup.Resources.MemorySwappiness) != -1)
+}
+
+func getMemoryData(path, name string) (cgroups.MemoryData, error) {
+ memoryData := cgroups.MemoryData{}
+
+ moduleName := "memory"
+ if name != "" {
+ moduleName = strings.Join([]string{"memory", name}, ".")
+ }
+ usage := strings.Join([]string{moduleName, "usage_in_bytes"}, ".")
+ maxUsage := strings.Join([]string{moduleName, "max_usage_in_bytes"}, ".")
+ failcnt := strings.Join([]string{moduleName, "failcnt"}, ".")
+ limit := strings.Join([]string{moduleName, "limit_in_bytes"}, ".")
+
+ value, err := getCgroupParamUint(path, usage)
+ if err != nil {
+ if moduleName != "memory" && os.IsNotExist(err) {
+ return cgroups.MemoryData{}, nil
+ }
+ return cgroups.MemoryData{}, fmt.Errorf("failed to parse %s - %v", usage, err)
+ }
+ memoryData.Usage = value
+ value, err = getCgroupParamUint(path, maxUsage)
+ if err != nil {
+ if moduleName != "memory" && os.IsNotExist(err) {
+ return cgroups.MemoryData{}, nil
+ }
+ return cgroups.MemoryData{}, fmt.Errorf("failed to parse %s - %v", maxUsage, err)
+ }
+ memoryData.MaxUsage = value
+ value, err = getCgroupParamUint(path, failcnt)
+ if err != nil {
+ if moduleName != "memory" && os.IsNotExist(err) {
+ return cgroups.MemoryData{}, nil
+ }
+ return cgroups.MemoryData{}, fmt.Errorf("failed to parse %s - %v", failcnt, err)
+ }
+ memoryData.Failcnt = value
+ value, err = getCgroupParamUint(path, limit)
+ if err != nil {
+ if moduleName != "memory" && os.IsNotExist(err) {
+ return cgroups.MemoryData{}, nil
+ }
+ return cgroups.MemoryData{}, fmt.Errorf("failed to parse %s - %v", limit, err)
+ }
+ memoryData.Limit = value
+
+ return memoryData, nil
+}
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/memory_test.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/memory_test.go
new file mode 100644
index 0000000000..4b656dc628
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/memory_test.go
@@ -0,0 +1,453 @@
+// +build linux
+
+package fs
+
+import (
+ "strconv"
+ "testing"
+
+ "github.com/opencontainers/runc/libcontainer/cgroups"
+)
+
+const (
+ memoryStatContents = `cache 512
+rss 1024`
+ memoryUsageContents = "2048\n"
+ memoryMaxUsageContents = "4096\n"
+ memoryFailcnt = "100\n"
+ memoryLimitContents = "8192\n"
+)
+
+func TestMemorySetMemory(t *testing.T) {
+ helper := NewCgroupTestUtil("memory", t)
+ defer helper.cleanup()
+
+ const (
+ memoryBefore = 314572800 // 300M
+ memoryAfter = 524288000 // 500M
+ reservationBefore = 209715200 // 200M
+ reservationAfter = 314572800 // 300M
+ )
+
+ helper.writeFileContents(map[string]string{
+ "memory.limit_in_bytes": strconv.Itoa(memoryBefore),
+ "memory.soft_limit_in_bytes": strconv.Itoa(reservationBefore),
+ })
+
+ helper.CgroupData.config.Resources.Memory = memoryAfter
+ helper.CgroupData.config.Resources.MemoryReservation = reservationAfter
+ memory := &MemoryGroup{}
+ if err := memory.Set(helper.CgroupPath, helper.CgroupData.config); err != nil {
+ t.Fatal(err)
+ }
+
+ value, err := getCgroupParamUint(helper.CgroupPath, "memory.limit_in_bytes")
+ if err != nil {
+ t.Fatalf("Failed to parse memory.limit_in_bytes - %s", err)
+ }
+ if value != memoryAfter {
+ t.Fatal("Got the wrong value, set memory.limit_in_bytes failed.")
+ }
+
+ value, err = getCgroupParamUint(helper.CgroupPath, "memory.soft_limit_in_bytes")
+ if err != nil {
+ t.Fatalf("Failed to parse memory.soft_limit_in_bytes - %s", err)
+ }
+ if value != reservationAfter {
+ t.Fatal("Got the wrong value, set memory.soft_limit_in_bytes failed.")
+ }
+}
+
+func TestMemorySetMemoryswap(t *testing.T) {
+ helper := NewCgroupTestUtil("memory", t)
+ defer helper.cleanup()
+
+ const (
+ memoryswapBefore = 314572800 // 300M
+ memoryswapAfter = 524288000 // 500M
+ )
+
+ helper.writeFileContents(map[string]string{
+ "memory.memsw.limit_in_bytes": strconv.Itoa(memoryswapBefore),
+ })
+
+ helper.CgroupData.config.Resources.MemorySwap = memoryswapAfter
+ memory := &MemoryGroup{}
+ if err := memory.Set(helper.CgroupPath, helper.CgroupData.config); err != nil {
+ t.Fatal(err)
+ }
+
+ value, err := getCgroupParamUint(helper.CgroupPath, "memory.memsw.limit_in_bytes")
+ if err != nil {
+ t.Fatalf("Failed to parse memory.memsw.limit_in_bytes - %s", err)
+ }
+ if value != memoryswapAfter {
+ t.Fatal("Got the wrong value, set memory.memsw.limit_in_bytes failed.")
+ }
+}
+
+func TestMemorySetMemoryLargerThanSwap(t *testing.T) {
+ helper := NewCgroupTestUtil("memory", t)
+ defer helper.cleanup()
+
+ const (
+ memoryBefore = 314572800 // 300M
+ memoryswapBefore = 524288000 // 500M
+ memoryAfter = 629145600 // 600M
+ memoryswapAfter = 838860800 // 800M
+ )
+
+ helper.writeFileContents(map[string]string{
+ "memory.limit_in_bytes": strconv.Itoa(memoryBefore),
+ "memory.memsw.limit_in_bytes": strconv.Itoa(memoryswapBefore),
+ // Set will call getMemoryData when memory and swap memory are
+ // both set, fake these fields so we don't get error.
+ "memory.usage_in_bytes": "0",
+ "memory.max_usage_in_bytes": "0",
+ "memory.failcnt": "0",
+ })
+
+ helper.CgroupData.config.Resources.Memory = memoryAfter
+ helper.CgroupData.config.Resources.MemorySwap = memoryswapAfter
+ memory := &MemoryGroup{}
+ if err := memory.Set(helper.CgroupPath, helper.CgroupData.config); err != nil {
+ t.Fatal(err)
+ }
+
+ value, err := getCgroupParamUint(helper.CgroupPath, "memory.limit_in_bytes")
+ if err != nil {
+ t.Fatalf("Failed to parse memory.limit_in_bytes - %s", err)
+ }
+ if value != memoryAfter {
+ t.Fatal("Got the wrong value, set memory.limit_in_bytes failed.")
+ }
+ value, err = getCgroupParamUint(helper.CgroupPath, "memory.memsw.limit_in_bytes")
+ if err != nil {
+ t.Fatalf("Failed to parse memory.memsw.limit_in_bytes - %s", err)
+ }
+ if value != memoryswapAfter {
+ t.Fatal("Got the wrong value, set memory.memsw.limit_in_bytes failed.")
+ }
+}
+
+func TestMemorySetSwapSmallerThanMemory(t *testing.T) {
+ helper := NewCgroupTestUtil("memory", t)
+ defer helper.cleanup()
+
+ const (
+ memoryBefore = 629145600 // 600M
+ memoryswapBefore = 838860800 // 800M
+ memoryAfter = 314572800 // 300M
+ memoryswapAfter = 524288000 // 500M
+ )
+
+ helper.writeFileContents(map[string]string{
+ "memory.limit_in_bytes": strconv.Itoa(memoryBefore),
+ "memory.memsw.limit_in_bytes": strconv.Itoa(memoryswapBefore),
+ // Set will call getMemoryData when memory and swap memory are
+ // both set, fake these fields so we don't get error.
+ "memory.usage_in_bytes": "0",
+ "memory.max_usage_in_bytes": "0",
+ "memory.failcnt": "0",
+ })
+
+ helper.CgroupData.config.Resources.Memory = memoryAfter
+ helper.CgroupData.config.Resources.MemorySwap = memoryswapAfter
+ memory := &MemoryGroup{}
+ if err := memory.Set(helper.CgroupPath, helper.CgroupData.config); err != nil {
+ t.Fatal(err)
+ }
+
+ value, err := getCgroupParamUint(helper.CgroupPath, "memory.limit_in_bytes")
+ if err != nil {
+ t.Fatalf("Failed to parse memory.limit_in_bytes - %s", err)
+ }
+ if value != memoryAfter {
+ t.Fatal("Got the wrong value, set memory.limit_in_bytes failed.")
+ }
+ value, err = getCgroupParamUint(helper.CgroupPath, "memory.memsw.limit_in_bytes")
+ if err != nil {
+ t.Fatalf("Failed to parse memory.memsw.limit_in_bytes - %s", err)
+ }
+ if value != memoryswapAfter {
+ t.Fatal("Got the wrong value, set memory.memsw.limit_in_bytes failed.")
+ }
+}
+
+func TestMemorySetKernelMemory(t *testing.T) {
+ helper := NewCgroupTestUtil("memory", t)
+ defer helper.cleanup()
+
+ const (
+ kernelMemoryBefore = 314572800 // 300M
+ kernelMemoryAfter = 524288000 // 500M
+ )
+
+ helper.writeFileContents(map[string]string{
+ "memory.kmem.limit_in_bytes": strconv.Itoa(kernelMemoryBefore),
+ })
+
+ helper.CgroupData.config.Resources.KernelMemory = kernelMemoryAfter
+ memory := &MemoryGroup{}
+ if err := memory.Set(helper.CgroupPath, helper.CgroupData.config); err != nil {
+ t.Fatal(err)
+ }
+
+ value, err := getCgroupParamUint(helper.CgroupPath, "memory.kmem.limit_in_bytes")
+ if err != nil {
+ t.Fatalf("Failed to parse memory.kmem.limit_in_bytes - %s", err)
+ }
+ if value != kernelMemoryAfter {
+ t.Fatal("Got the wrong value, set memory.kmem.limit_in_bytes failed.")
+ }
+}
+
+func TestMemorySetKernelMemoryTCP(t *testing.T) {
+ helper := NewCgroupTestUtil("memory", t)
+ defer helper.cleanup()
+
+ const (
+ kernelMemoryTCPBefore = 314572800 // 300M
+ kernelMemoryTCPAfter = 524288000 // 500M
+ )
+
+ helper.writeFileContents(map[string]string{
+ "memory.kmem.tcp.limit_in_bytes": strconv.Itoa(kernelMemoryTCPBefore),
+ })
+
+ helper.CgroupData.config.Resources.KernelMemoryTCP = kernelMemoryTCPAfter
+ memory := &MemoryGroup{}
+ if err := memory.Set(helper.CgroupPath, helper.CgroupData.config); err != nil {
+ t.Fatal(err)
+ }
+
+ value, err := getCgroupParamUint(helper.CgroupPath, "memory.kmem.tcp.limit_in_bytes")
+ if err != nil {
+ t.Fatalf("Failed to parse memory.kmem.tcp.limit_in_bytes - %s", err)
+ }
+ if value != kernelMemoryTCPAfter {
+ t.Fatal("Got the wrong value, set memory.kmem.tcp.limit_in_bytes failed.")
+ }
+}
+
+func TestMemorySetMemorySwappinessDefault(t *testing.T) {
+ helper := NewCgroupTestUtil("memory", t)
+ defer helper.cleanup()
+
+ swappinessBefore := 60 // default is 60
+ swappinessAfter := uint64(0)
+
+ helper.writeFileContents(map[string]string{
+ "memory.swappiness": strconv.Itoa(swappinessBefore),
+ })
+
+ helper.CgroupData.config.Resources.MemorySwappiness = &swappinessAfter
+ memory := &MemoryGroup{}
+ if err := memory.Set(helper.CgroupPath, helper.CgroupData.config); err != nil {
+ t.Fatal(err)
+ }
+
+ value, err := getCgroupParamUint(helper.CgroupPath, "memory.swappiness")
+ if err != nil {
+ t.Fatalf("Failed to parse memory.swappiness - %s", err)
+ }
+ if value != swappinessAfter {
+ t.Fatalf("Got the wrong value (%d), set memory.swappiness = %d failed.", value, swappinessAfter)
+ }
+}
+
+func TestMemoryStats(t *testing.T) {
+ helper := NewCgroupTestUtil("memory", t)
+ defer helper.cleanup()
+ helper.writeFileContents(map[string]string{
+ "memory.stat": memoryStatContents,
+ "memory.usage_in_bytes": memoryUsageContents,
+ "memory.limit_in_bytes": memoryLimitContents,
+ "memory.max_usage_in_bytes": memoryMaxUsageContents,
+ "memory.failcnt": memoryFailcnt,
+ "memory.memsw.usage_in_bytes": memoryUsageContents,
+ "memory.memsw.max_usage_in_bytes": memoryMaxUsageContents,
+ "memory.memsw.failcnt": memoryFailcnt,
+ "memory.memsw.limit_in_bytes": memoryLimitContents,
+ "memory.kmem.usage_in_bytes": memoryUsageContents,
+ "memory.kmem.max_usage_in_bytes": memoryMaxUsageContents,
+ "memory.kmem.failcnt": memoryFailcnt,
+ "memory.kmem.limit_in_bytes": memoryLimitContents,
+ })
+
+ memory := &MemoryGroup{}
+ actualStats := *cgroups.NewStats()
+ err := memory.GetStats(helper.CgroupPath, &actualStats)
+ if err != nil {
+ t.Fatal(err)
+ }
+ expectedStats := cgroups.MemoryStats{Cache: 512, Usage: cgroups.MemoryData{Usage: 2048, MaxUsage: 4096, Failcnt: 100, Limit: 8192}, SwapUsage: cgroups.MemoryData{Usage: 2048, MaxUsage: 4096, Failcnt: 100, Limit: 8192}, KernelUsage: cgroups.MemoryData{Usage: 2048, MaxUsage: 4096, Failcnt: 100, Limit: 8192}, Stats: map[string]uint64{"cache": 512, "rss": 1024}}
+ expectMemoryStatEquals(t, expectedStats, actualStats.MemoryStats)
+}
+
+func TestMemoryStatsNoStatFile(t *testing.T) {
+ helper := NewCgroupTestUtil("memory", t)
+ defer helper.cleanup()
+ helper.writeFileContents(map[string]string{
+ "memory.usage_in_bytes": memoryUsageContents,
+ "memory.max_usage_in_bytes": memoryMaxUsageContents,
+ "memory.limit_in_bytes": memoryLimitContents,
+ })
+
+ memory := &MemoryGroup{}
+ actualStats := *cgroups.NewStats()
+ err := memory.GetStats(helper.CgroupPath, &actualStats)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestMemoryStatsNoUsageFile(t *testing.T) {
+ helper := NewCgroupTestUtil("memory", t)
+ defer helper.cleanup()
+ helper.writeFileContents(map[string]string{
+ "memory.stat": memoryStatContents,
+ "memory.max_usage_in_bytes": memoryMaxUsageContents,
+ "memory.limit_in_bytes": memoryLimitContents,
+ })
+
+ memory := &MemoryGroup{}
+ actualStats := *cgroups.NewStats()
+ err := memory.GetStats(helper.CgroupPath, &actualStats)
+ if err == nil {
+ t.Fatal("Expected failure")
+ }
+}
+
+func TestMemoryStatsNoMaxUsageFile(t *testing.T) {
+ helper := NewCgroupTestUtil("memory", t)
+ defer helper.cleanup()
+ helper.writeFileContents(map[string]string{
+ "memory.stat": memoryStatContents,
+ "memory.usage_in_bytes": memoryUsageContents,
+ "memory.limit_in_bytes": memoryLimitContents,
+ })
+
+ memory := &MemoryGroup{}
+ actualStats := *cgroups.NewStats()
+ err := memory.GetStats(helper.CgroupPath, &actualStats)
+ if err == nil {
+ t.Fatal("Expected failure")
+ }
+}
+
+func TestMemoryStatsNoLimitInBytesFile(t *testing.T) {
+ helper := NewCgroupTestUtil("memory", t)
+ defer helper.cleanup()
+ helper.writeFileContents(map[string]string{
+ "memory.stat": memoryStatContents,
+ "memory.usage_in_bytes": memoryUsageContents,
+ "memory.max_usage_in_bytes": memoryMaxUsageContents,
+ })
+
+ memory := &MemoryGroup{}
+ actualStats := *cgroups.NewStats()
+ err := memory.GetStats(helper.CgroupPath, &actualStats)
+ if err == nil {
+ t.Fatal("Expected failure")
+ }
+}
+
+func TestMemoryStatsBadStatFile(t *testing.T) {
+ helper := NewCgroupTestUtil("memory", t)
+ defer helper.cleanup()
+ helper.writeFileContents(map[string]string{
+ "memory.stat": "rss rss",
+ "memory.usage_in_bytes": memoryUsageContents,
+ "memory.max_usage_in_bytes": memoryMaxUsageContents,
+ "memory.limit_in_bytes": memoryLimitContents,
+ })
+
+ memory := &MemoryGroup{}
+ actualStats := *cgroups.NewStats()
+ err := memory.GetStats(helper.CgroupPath, &actualStats)
+ if err == nil {
+ t.Fatal("Expected failure")
+ }
+}
+
+func TestMemoryStatsBadUsageFile(t *testing.T) {
+ helper := NewCgroupTestUtil("memory", t)
+ defer helper.cleanup()
+ helper.writeFileContents(map[string]string{
+ "memory.stat": memoryStatContents,
+ "memory.usage_in_bytes": "bad",
+ "memory.max_usage_in_bytes": memoryMaxUsageContents,
+ "memory.limit_in_bytes": memoryLimitContents,
+ })
+
+ memory := &MemoryGroup{}
+ actualStats := *cgroups.NewStats()
+ err := memory.GetStats(helper.CgroupPath, &actualStats)
+ if err == nil {
+ t.Fatal("Expected failure")
+ }
+}
+
+func TestMemoryStatsBadMaxUsageFile(t *testing.T) {
+ helper := NewCgroupTestUtil("memory", t)
+ defer helper.cleanup()
+ helper.writeFileContents(map[string]string{
+ "memory.stat": memoryStatContents,
+ "memory.usage_in_bytes": memoryUsageContents,
+ "memory.max_usage_in_bytes": "bad",
+ "memory.limit_in_bytes": memoryLimitContents,
+ })
+
+ memory := &MemoryGroup{}
+ actualStats := *cgroups.NewStats()
+ err := memory.GetStats(helper.CgroupPath, &actualStats)
+ if err == nil {
+ t.Fatal("Expected failure")
+ }
+}
+
+func TestMemoryStatsBadLimitInBytesFile(t *testing.T) {
+ helper := NewCgroupTestUtil("memory", t)
+ defer helper.cleanup()
+ helper.writeFileContents(map[string]string{
+ "memory.stat": memoryStatContents,
+ "memory.usage_in_bytes": memoryUsageContents,
+ "memory.max_usage_in_bytes": memoryMaxUsageContents,
+ "memory.limit_in_bytes": "bad",
+ })
+
+ memory := &MemoryGroup{}
+ actualStats := *cgroups.NewStats()
+ err := memory.GetStats(helper.CgroupPath, &actualStats)
+ if err == nil {
+ t.Fatal("Expected failure")
+ }
+}
+
+func TestMemorySetOomControl(t *testing.T) {
+ helper := NewCgroupTestUtil("memory", t)
+ defer helper.cleanup()
+
+ const (
+ oomKillDisable = 1 // disable oom killer, default is 0
+ )
+
+ helper.writeFileContents(map[string]string{
+ "memory.oom_control": strconv.Itoa(oomKillDisable),
+ })
+
+ memory := &MemoryGroup{}
+ if err := memory.Set(helper.CgroupPath, helper.CgroupData.config); err != nil {
+ t.Fatal(err)
+ }
+
+ value, err := getCgroupParamUint(helper.CgroupPath, "memory.oom_control")
+ if err != nil {
+ t.Fatalf("Failed to parse memory.oom_control - %s", err)
+ }
+
+ if value != oomKillDisable {
+ t.Fatalf("Got the wrong value, set memory.oom_control failed.")
+ }
+}
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/name.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/name.go
new file mode 100644
index 0000000000..d8cf1d87c0
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/name.go
@@ -0,0 +1,40 @@
+// +build linux
+
+package fs
+
+import (
+ "github.com/opencontainers/runc/libcontainer/cgroups"
+ "github.com/opencontainers/runc/libcontainer/configs"
+)
+
+type NameGroup struct {
+ GroupName string
+ Join bool
+}
+
+func (s *NameGroup) Name() string {
+ return s.GroupName
+}
+
+func (s *NameGroup) Apply(d *cgroupData) error {
+ if s.Join {
+ // ignore errors if the named cgroup does not exist
+ d.join(s.GroupName)
+ }
+ return nil
+}
+
+func (s *NameGroup) Set(path string, cgroup *configs.Cgroup) error {
+ return nil
+}
+
+func (s *NameGroup) Remove(d *cgroupData) error {
+ if s.Join {
+ removePath(d.path(s.GroupName))
+ }
+ return nil
+}
+
+func (s *NameGroup) GetStats(path string, stats *cgroups.Stats) error {
+ return nil
+}
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/net_cls.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/net_cls.go
new file mode 100644
index 0000000000..8e74b645ea
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/net_cls.go
@@ -0,0 +1,43 @@
+// +build linux
+
+package fs
+
+import (
+ "strconv"
+
+ "github.com/opencontainers/runc/libcontainer/cgroups"
+ "github.com/opencontainers/runc/libcontainer/configs"
+)
+
+type NetClsGroup struct {
+}
+
+func (s *NetClsGroup) Name() string {
+ return "net_cls"
+}
+
+func (s *NetClsGroup) Apply(d *cgroupData) error {
+ _, err := d.join("net_cls")
+ if err != nil && !cgroups.IsNotFound(err) {
+ return err
+ }
+ return nil
+}
+
+func (s *NetClsGroup) Set(path string, cgroup *configs.Cgroup) error {
+ if cgroup.Resources.NetClsClassid != 0 {
+ if err := writeFile(path, "net_cls.classid", strconv.FormatUint(uint64(cgroup.Resources.NetClsClassid), 10)); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (s *NetClsGroup) Remove(d *cgroupData) error {
+ return removePath(d.path("net_cls"))
+}
+
+func (s *NetClsGroup) GetStats(path string, stats *cgroups.Stats) error {
+ return nil
+}
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/net_cls_test.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/net_cls_test.go
new file mode 100644
index 0000000000..c00e8a8d6f
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/net_cls_test.go
@@ -0,0 +1,39 @@
+// +build linux
+
+package fs
+
+import (
+ "strconv"
+ "testing"
+)
+
+const (
+ classidBefore = 0x100002
+ classidAfter = 0x100001
+)
+
+func TestNetClsSetClassid(t *testing.T) {
+ helper := NewCgroupTestUtil("net_cls", t)
+ defer helper.cleanup()
+
+ helper.writeFileContents(map[string]string{
+ "net_cls.classid": strconv.FormatUint(classidBefore, 10),
+ })
+
+ helper.CgroupData.config.Resources.NetClsClassid = classidAfter
+ netcls := &NetClsGroup{}
+ if err := netcls.Set(helper.CgroupPath, helper.CgroupData.config); err != nil {
+ t.Fatal(err)
+ }
+
+ // As we are in mock environment, we can't get correct value of classid from
+ // net_cls.classid.
+ // So. we just judge if we successfully write classid into file
+ value, err := getCgroupParamUint(helper.CgroupPath, "net_cls.classid")
+ if err != nil {
+ t.Fatalf("Failed to parse net_cls.classid - %s", err)
+ }
+ if value != classidAfter {
+ t.Fatal("Got the wrong value, set net_cls.classid failed.")
+ }
+}
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/net_prio.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/net_prio.go
new file mode 100644
index 0000000000..d0ab2af894
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/net_prio.go
@@ -0,0 +1,41 @@
+// +build linux
+
+package fs
+
+import (
+ "github.com/opencontainers/runc/libcontainer/cgroups"
+ "github.com/opencontainers/runc/libcontainer/configs"
+)
+
+type NetPrioGroup struct {
+}
+
+func (s *NetPrioGroup) Name() string {
+ return "net_prio"
+}
+
+func (s *NetPrioGroup) Apply(d *cgroupData) error {
+ _, err := d.join("net_prio")
+ if err != nil && !cgroups.IsNotFound(err) {
+ return err
+ }
+ return nil
+}
+
+func (s *NetPrioGroup) Set(path string, cgroup *configs.Cgroup) error {
+ for _, prioMap := range cgroup.Resources.NetPrioIfpriomap {
+ if err := writeFile(path, "net_prio.ifpriomap", prioMap.CgroupString()); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (s *NetPrioGroup) Remove(d *cgroupData) error {
+ return removePath(d.path("net_prio"))
+}
+
+func (s *NetPrioGroup) GetStats(path string, stats *cgroups.Stats) error {
+ return nil
+}
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/net_prio_test.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/net_prio_test.go
new file mode 100644
index 0000000000..efbf0639a0
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/net_prio_test.go
@@ -0,0 +1,38 @@
+// +build linux
+
+package fs
+
+import (
+ "strings"
+ "testing"
+
+ "github.com/opencontainers/runc/libcontainer/configs"
+)
+
+var (
+ prioMap = []*configs.IfPrioMap{
+ {
+ Interface: "test",
+ Priority: 5,
+ },
+ }
+)
+
+func TestNetPrioSetIfPrio(t *testing.T) {
+ helper := NewCgroupTestUtil("net_prio", t)
+ defer helper.cleanup()
+
+ helper.CgroupData.config.Resources.NetPrioIfpriomap = prioMap
+ netPrio := &NetPrioGroup{}
+ if err := netPrio.Set(helper.CgroupPath, helper.CgroupData.config); err != nil {
+ t.Fatal(err)
+ }
+
+ value, err := getCgroupParamString(helper.CgroupPath, "net_prio.ifpriomap")
+ if err != nil {
+ t.Fatalf("Failed to parse net_prio.ifpriomap - %s", err)
+ }
+ if !strings.Contains(value, "test 5") {
+ t.Fatal("Got the wrong value, set net_prio.ifpriomap failed.")
+ }
+}
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/perf_event.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/perf_event.go
new file mode 100644
index 0000000000..5693676d3a
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/perf_event.go
@@ -0,0 +1,35 @@
+// +build linux
+
+package fs
+
+import (
+ "github.com/opencontainers/runc/libcontainer/cgroups"
+ "github.com/opencontainers/runc/libcontainer/configs"
+)
+
+type PerfEventGroup struct {
+}
+
+func (s *PerfEventGroup) Name() string {
+ return "perf_event"
+}
+
+func (s *PerfEventGroup) Apply(d *cgroupData) error {
+ // we just want to join this group even though we don't set anything
+ if _, err := d.join("perf_event"); err != nil && !cgroups.IsNotFound(err) {
+ return err
+ }
+ return nil
+}
+
+func (s *PerfEventGroup) Set(path string, cgroup *configs.Cgroup) error {
+ return nil
+}
+
+func (s *PerfEventGroup) Remove(d *cgroupData) error {
+ return removePath(d.path("perf_event"))
+}
+
+func (s *PerfEventGroup) GetStats(path string, stats *cgroups.Stats) error {
+ return nil
+}
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/pids.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/pids.go
new file mode 100644
index 0000000000..f1e3720551
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/pids.go
@@ -0,0 +1,73 @@
+// +build linux
+
+package fs
+
+import (
+ "fmt"
+ "path/filepath"
+ "strconv"
+
+ "github.com/opencontainers/runc/libcontainer/cgroups"
+ "github.com/opencontainers/runc/libcontainer/configs"
+)
+
+type PidsGroup struct {
+}
+
+func (s *PidsGroup) Name() string {
+ return "pids"
+}
+
+func (s *PidsGroup) Apply(d *cgroupData) error {
+ _, err := d.join("pids")
+ if err != nil && !cgroups.IsNotFound(err) {
+ return err
+ }
+ return nil
+}
+
+func (s *PidsGroup) Set(path string, cgroup *configs.Cgroup) error {
+ if cgroup.Resources.PidsLimit != 0 {
+ // "max" is the fallback value.
+ limit := "max"
+
+ if cgroup.Resources.PidsLimit > 0 {
+ limit = strconv.FormatInt(cgroup.Resources.PidsLimit, 10)
+ }
+
+ if err := writeFile(path, "pids.max", limit); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (s *PidsGroup) Remove(d *cgroupData) error {
+ return removePath(d.path("pids"))
+}
+
+func (s *PidsGroup) GetStats(path string, stats *cgroups.Stats) error {
+ current, err := getCgroupParamUint(path, "pids.current")
+ if err != nil {
+ return fmt.Errorf("failed to parse pids.current - %s", err)
+ }
+
+ maxString, err := getCgroupParamString(path, "pids.max")
+ if err != nil {
+ return fmt.Errorf("failed to parse pids.max - %s", err)
+ }
+
+ // Default if pids.max == "max" is 0 -- which represents "no limit".
+ var max uint64
+ if maxString != "max" {
+ max, err = parseUint(maxString, 10, 64)
+ if err != nil {
+ return fmt.Errorf("failed to parse pids.max - unable to parse %q as a uint from Cgroup file %q", maxString, filepath.Join(path, "pids.max"))
+ }
+ }
+
+ stats.PidsStats.Current = current
+ stats.PidsStats.Limit = max
+ return nil
+}
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/pids_test.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/pids_test.go
new file mode 100644
index 0000000000..10671247ba
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/pids_test.go
@@ -0,0 +1,111 @@
+// +build linux
+
+package fs
+
+import (
+ "strconv"
+ "testing"
+
+ "github.com/opencontainers/runc/libcontainer/cgroups"
+)
+
+const (
+ maxUnlimited = -1
+ maxLimited = 1024
+)
+
+func TestPidsSetMax(t *testing.T) {
+ helper := NewCgroupTestUtil("pids", t)
+ defer helper.cleanup()
+
+ helper.writeFileContents(map[string]string{
+ "pids.max": "max",
+ })
+
+ helper.CgroupData.config.Resources.PidsLimit = maxLimited
+ pids := &PidsGroup{}
+ if err := pids.Set(helper.CgroupPath, helper.CgroupData.config); err != nil {
+ t.Fatal(err)
+ }
+
+ value, err := getCgroupParamUint(helper.CgroupPath, "pids.max")
+ if err != nil {
+ t.Fatalf("Failed to parse pids.max - %s", err)
+ }
+
+ if value != maxLimited {
+ t.Fatalf("Expected %d, got %d for setting pids.max - limited", maxLimited, value)
+ }
+}
+
+func TestPidsSetUnlimited(t *testing.T) {
+ helper := NewCgroupTestUtil("pids", t)
+ defer helper.cleanup()
+
+ helper.writeFileContents(map[string]string{
+ "pids.max": strconv.Itoa(maxLimited),
+ })
+
+ helper.CgroupData.config.Resources.PidsLimit = maxUnlimited
+ pids := &PidsGroup{}
+ if err := pids.Set(helper.CgroupPath, helper.CgroupData.config); err != nil {
+ t.Fatal(err)
+ }
+
+ value, err := getCgroupParamString(helper.CgroupPath, "pids.max")
+ if err != nil {
+ t.Fatalf("Failed to parse pids.max - %s", err)
+ }
+
+ if value != "max" {
+ t.Fatalf("Expected %s, got %s for setting pids.max - unlimited", "max", value)
+ }
+}
+
+func TestPidsStats(t *testing.T) {
+ helper := NewCgroupTestUtil("pids", t)
+ defer helper.cleanup()
+
+ helper.writeFileContents(map[string]string{
+ "pids.current": strconv.Itoa(1337),
+ "pids.max": strconv.Itoa(maxLimited),
+ })
+
+ pids := &PidsGroup{}
+ stats := *cgroups.NewStats()
+ if err := pids.GetStats(helper.CgroupPath, &stats); err != nil {
+ t.Fatal(err)
+ }
+
+ if stats.PidsStats.Current != 1337 {
+ t.Fatalf("Expected %d, got %d for pids.current", 1337, stats.PidsStats.Current)
+ }
+
+ if stats.PidsStats.Limit != maxLimited {
+ t.Fatalf("Expected %d, got %d for pids.max", maxLimited, stats.PidsStats.Limit)
+ }
+}
+
+func TestPidsStatsUnlimited(t *testing.T) {
+ helper := NewCgroupTestUtil("pids", t)
+ defer helper.cleanup()
+
+ helper.writeFileContents(map[string]string{
+ "pids.current": strconv.Itoa(4096),
+ "pids.max": "max",
+ })
+
+ pids := &PidsGroup{}
+ stats := *cgroups.NewStats()
+ if err := pids.GetStats(helper.CgroupPath, &stats); err != nil {
+ t.Fatal(err)
+ }
+
+ if stats.PidsStats.Current != 4096 {
+ t.Fatalf("Expected %d, got %d for pids.current", 4096, stats.PidsStats.Current)
+ }
+
+ if stats.PidsStats.Limit != 0 {
+ t.Fatalf("Expected %d, got %d for pids.max", 0, stats.PidsStats.Limit)
+ }
+}
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/stats_util_test.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/stats_util_test.go
new file mode 100644
index 0000000000..d0ab047418
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/stats_util_test.go
@@ -0,0 +1,117 @@
+// +build linux
+
+package fs
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/sirupsen/logrus"
+ "github.com/opencontainers/runc/libcontainer/cgroups"
+)
+
+func blkioStatEntryEquals(expected, actual []cgroups.BlkioStatEntry) error {
+ if len(expected) != len(actual) {
+ return fmt.Errorf("blkioStatEntries length do not match")
+ }
+ for i, expValue := range expected {
+ actValue := actual[i]
+ if expValue != actValue {
+ return fmt.Errorf("Expected blkio stat entry %v but found %v", expValue, actValue)
+ }
+ }
+ return nil
+}
+
+func expectBlkioStatsEquals(t *testing.T, expected, actual cgroups.BlkioStats) {
+ if err := blkioStatEntryEquals(expected.IoServiceBytesRecursive, actual.IoServiceBytesRecursive); err != nil {
+ logrus.Printf("blkio IoServiceBytesRecursive do not match - %s\n", err)
+ t.Fail()
+ }
+
+ if err := blkioStatEntryEquals(expected.IoServicedRecursive, actual.IoServicedRecursive); err != nil {
+ logrus.Printf("blkio IoServicedRecursive do not match - %s\n", err)
+ t.Fail()
+ }
+
+ if err := blkioStatEntryEquals(expected.IoQueuedRecursive, actual.IoQueuedRecursive); err != nil {
+ logrus.Printf("blkio IoQueuedRecursive do not match - %s\n", err)
+ t.Fail()
+ }
+
+ if err := blkioStatEntryEquals(expected.SectorsRecursive, actual.SectorsRecursive); err != nil {
+ logrus.Printf("blkio SectorsRecursive do not match - %s\n", err)
+ t.Fail()
+ }
+
+ if err := blkioStatEntryEquals(expected.IoServiceTimeRecursive, actual.IoServiceTimeRecursive); err != nil {
+ logrus.Printf("blkio IoServiceTimeRecursive do not match - %s\n", err)
+ t.Fail()
+ }
+
+ if err := blkioStatEntryEquals(expected.IoWaitTimeRecursive, actual.IoWaitTimeRecursive); err != nil {
+ logrus.Printf("blkio IoWaitTimeRecursive do not match - %s\n", err)
+ t.Fail()
+ }
+
+ if err := blkioStatEntryEquals(expected.IoMergedRecursive, actual.IoMergedRecursive); err != nil {
+ logrus.Printf("blkio IoMergedRecursive do not match - %v vs %v\n", expected.IoMergedRecursive, actual.IoMergedRecursive)
+ t.Fail()
+ }
+
+ if err := blkioStatEntryEquals(expected.IoTimeRecursive, actual.IoTimeRecursive); err != nil {
+ logrus.Printf("blkio IoTimeRecursive do not match - %s\n", err)
+ t.Fail()
+ }
+}
+
+func expectThrottlingDataEquals(t *testing.T, expected, actual cgroups.ThrottlingData) {
+ if expected != actual {
+ logrus.Printf("Expected throttling data %v but found %v\n", expected, actual)
+ t.Fail()
+ }
+}
+
+func expectHugetlbStatEquals(t *testing.T, expected, actual cgroups.HugetlbStats) {
+ if expected != actual {
+ logrus.Printf("Expected hugetlb stats %v but found %v\n", expected, actual)
+ t.Fail()
+ }
+}
+
+func expectMemoryStatEquals(t *testing.T, expected, actual cgroups.MemoryStats) {
+ expectMemoryDataEquals(t, expected.Usage, actual.Usage)
+ expectMemoryDataEquals(t, expected.SwapUsage, actual.SwapUsage)
+ expectMemoryDataEquals(t, expected.KernelUsage, actual.KernelUsage)
+
+ for key, expValue := range expected.Stats {
+ actValue, ok := actual.Stats[key]
+ if !ok {
+ logrus.Printf("Expected memory stat key %s not found\n", key)
+ t.Fail()
+ }
+ if expValue != actValue {
+ logrus.Printf("Expected memory stat value %d but found %d\n", expValue, actValue)
+ t.Fail()
+ }
+ }
+}
+
+func expectMemoryDataEquals(t *testing.T, expected, actual cgroups.MemoryData) {
+ if expected.Usage != actual.Usage {
+ logrus.Printf("Expected memory usage %d but found %d\n", expected.Usage, actual.Usage)
+ t.Fail()
+ }
+ if expected.MaxUsage != actual.MaxUsage {
+ logrus.Printf("Expected memory max usage %d but found %d\n", expected.MaxUsage, actual.MaxUsage)
+ t.Fail()
+ }
+ if expected.Failcnt != actual.Failcnt {
+ logrus.Printf("Expected memory failcnt %d but found %d\n", expected.Failcnt, actual.Failcnt)
+ t.Fail()
+ }
+ if expected.Limit != actual.Limit {
+ logrus.Printf("Expected memory limit %d but found %d\n", expected.Limit, actual.Limit)
+ t.Fail()
+ }
+}
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/util_test.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/util_test.go
new file mode 100644
index 0000000000..7067e799fb
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/util_test.go
@@ -0,0 +1,67 @@
+// +build linux
+
+/*
+Utility for testing cgroup operations.
+
+Creates a mock of the cgroup filesystem for the duration of the test.
+*/
+package fs
+
+import (
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/opencontainers/runc/libcontainer/configs"
+)
+
+type cgroupTestUtil struct {
+ // cgroup data to use in tests.
+ CgroupData *cgroupData
+
+ // Path to the mock cgroup directory.
+ CgroupPath string
+
+ // Temporary directory to store mock cgroup filesystem.
+ tempDir string
+ t *testing.T
+}
+
+// Creates a new test util for the specified subsystem
+func NewCgroupTestUtil(subsystem string, t *testing.T) *cgroupTestUtil {
+ d := &cgroupData{
+ config: &configs.Cgroup{},
+ }
+ d.config.Resources = &configs.Resources{}
+ tempDir, err := ioutil.TempDir("", "cgroup_test")
+ if err != nil {
+ t.Fatal(err)
+ }
+ d.root = tempDir
+ testCgroupPath := filepath.Join(d.root, subsystem)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Ensure the full mock cgroup path exists.
+ err = os.MkdirAll(testCgroupPath, 0755)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return &cgroupTestUtil{CgroupData: d, CgroupPath: testCgroupPath, tempDir: tempDir, t: t}
+}
+
+func (c *cgroupTestUtil) cleanup() {
+ os.RemoveAll(c.tempDir)
+}
+
+// Write the specified contents on the mock of the specified cgroup files.
+func (c *cgroupTestUtil) writeFileContents(fileContents map[string]string) {
+ for file, contents := range fileContents {
+ err := writeFile(c.CgroupPath, file, contents)
+ if err != nil {
+ c.t.Fatal(err)
+ }
+ }
+}
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/utils.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/utils.go
new file mode 100644
index 0000000000..5ff0a16150
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/utils.go
@@ -0,0 +1,78 @@
+// +build linux
+
+package fs
+
+import (
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "path/filepath"
+ "strconv"
+ "strings"
+)
+
+var (
+ ErrNotValidFormat = errors.New("line is not a valid key value format")
+)
+
+// Saturates negative values at zero and returns a uint64.
+// Due to kernel bugs, some of the memory cgroup stats can be negative.
+func parseUint(s string, base, bitSize int) (uint64, error) {
+ value, err := strconv.ParseUint(s, base, bitSize)
+ if err != nil {
+ intValue, intErr := strconv.ParseInt(s, base, bitSize)
+ // 1. Handle negative values greater than MinInt64 (and)
+ // 2. Handle negative values lesser than MinInt64
+ if intErr == nil && intValue < 0 {
+ return 0, nil
+ } else if intErr != nil && intErr.(*strconv.NumError).Err == strconv.ErrRange && intValue < 0 {
+ return 0, nil
+ }
+
+ return value, err
+ }
+
+ return value, nil
+}
+
+// Parses a cgroup param and returns as name, value
+// i.e. "io_service_bytes 1234" will return as io_service_bytes, 1234
+func getCgroupParamKeyValue(t string) (string, uint64, error) {
+ parts := strings.Fields(t)
+ switch len(parts) {
+ case 2:
+ value, err := parseUint(parts[1], 10, 64)
+ if err != nil {
+ return "", 0, fmt.Errorf("unable to convert param value (%q) to uint64: %v", parts[1], err)
+ }
+
+ return parts[0], value, nil
+ default:
+ return "", 0, ErrNotValidFormat
+ }
+}
+
+// Gets a single uint64 value from the specified cgroup file.
+func getCgroupParamUint(cgroupPath, cgroupFile string) (uint64, error) {
+ fileName := filepath.Join(cgroupPath, cgroupFile)
+ contents, err := ioutil.ReadFile(fileName)
+ if err != nil {
+ return 0, err
+ }
+
+ res, err := parseUint(strings.TrimSpace(string(contents)), 10, 64)
+ if err != nil {
+ return res, fmt.Errorf("unable to parse %q as a uint from Cgroup file %q", string(contents), fileName)
+ }
+ return res, nil
+}
+
+// Gets a string value from the specified cgroup file
+func getCgroupParamString(cgroupPath, cgroupFile string) (string, error) {
+ contents, err := ioutil.ReadFile(filepath.Join(cgroupPath, cgroupFile))
+ if err != nil {
+ return "", err
+ }
+
+ return strings.TrimSpace(string(contents)), nil
+}
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/utils_test.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/utils_test.go
new file mode 100644
index 0000000000..99cdc18e07
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/utils_test.go
@@ -0,0 +1,97 @@
+// +build linux
+
+package fs
+
+import (
+ "io/ioutil"
+ "math"
+ "os"
+ "path/filepath"
+ "strconv"
+ "testing"
+)
+
+const (
+ cgroupFile = "cgroup.file"
+ floatValue = 2048.0
+ floatString = "2048"
+)
+
+func TestGetCgroupParamsInt(t *testing.T) {
+ // Setup tempdir.
+ tempDir, err := ioutil.TempDir("", "cgroup_utils_test")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tempDir)
+ tempFile := filepath.Join(tempDir, cgroupFile)
+
+ // Success.
+ err = ioutil.WriteFile(tempFile, []byte(floatString), 0755)
+ if err != nil {
+ t.Fatal(err)
+ }
+ value, err := getCgroupParamUint(tempDir, cgroupFile)
+ if err != nil {
+ t.Fatal(err)
+ } else if value != floatValue {
+ t.Fatalf("Expected %d to equal %f", value, floatValue)
+ }
+
+ // Success with new line.
+ err = ioutil.WriteFile(tempFile, []byte(floatString+"\n"), 0755)
+ if err != nil {
+ t.Fatal(err)
+ }
+ value, err = getCgroupParamUint(tempDir, cgroupFile)
+ if err != nil {
+ t.Fatal(err)
+ } else if value != floatValue {
+ t.Fatalf("Expected %d to equal %f", value, floatValue)
+ }
+
+ // Success with negative values
+ err = ioutil.WriteFile(tempFile, []byte("-12345"), 0755)
+ if err != nil {
+ t.Fatal(err)
+ }
+ value, err = getCgroupParamUint(tempDir, cgroupFile)
+ if err != nil {
+ t.Fatal(err)
+ } else if value != 0 {
+ t.Fatalf("Expected %d to equal %d", value, 0)
+ }
+
+ // Success with negative values lesser than min int64
+ s := strconv.FormatFloat(math.MinInt64, 'f', -1, 64)
+ err = ioutil.WriteFile(tempFile, []byte(s), 0755)
+ if err != nil {
+ t.Fatal(err)
+ }
+ value, err = getCgroupParamUint(tempDir, cgroupFile)
+ if err != nil {
+ t.Fatal(err)
+ } else if value != 0 {
+ t.Fatalf("Expected %d to equal %d", value, 0)
+ }
+
+ // Not a float.
+ err = ioutil.WriteFile(tempFile, []byte("not-a-float"), 0755)
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, err = getCgroupParamUint(tempDir, cgroupFile)
+ if err == nil {
+ t.Fatal("Expecting error, got none")
+ }
+
+ // Unknown file.
+ err = os.Remove(tempFile)
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, err = getCgroupParamUint(tempDir, cgroupFile)
+ if err == nil {
+ t.Fatal("Expecting error, got none")
+ }
+}
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/rootless/rootless.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/rootless/rootless.go
new file mode 100644
index 0000000000..b1efbfd999
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/rootless/rootless.go
@@ -0,0 +1,128 @@
+// +build linux
+
+package rootless
+
+import (
+ "fmt"
+
+ "github.com/opencontainers/runc/libcontainer/cgroups"
+ "github.com/opencontainers/runc/libcontainer/cgroups/fs"
+ "github.com/opencontainers/runc/libcontainer/configs"
+ "github.com/opencontainers/runc/libcontainer/configs/validate"
+)
+
+// TODO: This is copied from libcontainer/cgroups/fs, which duplicates this code
+// needlessly. We should probably export this list.
+
+var subsystems = []subsystem{
+ &fs.CpusetGroup{},
+ &fs.DevicesGroup{},
+ &fs.MemoryGroup{},
+ &fs.CpuGroup{},
+ &fs.CpuacctGroup{},
+ &fs.PidsGroup{},
+ &fs.BlkioGroup{},
+ &fs.HugetlbGroup{},
+ &fs.NetClsGroup{},
+ &fs.NetPrioGroup{},
+ &fs.PerfEventGroup{},
+ &fs.FreezerGroup{},
+ &fs.NameGroup{GroupName: "name=systemd"},
+}
+
+type subsystem interface {
+ // Name returns the name of the subsystem.
+ Name() string
+
+ // Returns the stats, as 'stats', corresponding to the cgroup under 'path'.
+ GetStats(path string, stats *cgroups.Stats) error
+}
+
+// The noop cgroup manager is used for rootless containers, because we currently
+// cannot manage cgroups if we are in a rootless setup. This manager is chosen
+// by factory if we are in rootless mode. We error out if any cgroup options are
+// set in the config -- this may change in the future with upcoming kernel features
+// like the cgroup namespace.
+
+type Manager struct {
+ Cgroups *configs.Cgroup
+ Paths map[string]string
+}
+
+func (m *Manager) Apply(pid int) error {
+ // If there are no cgroup settings, there's nothing to do.
+ if m.Cgroups == nil {
+ return nil
+ }
+
+ // We can't set paths.
+ // TODO(cyphar): Implement the case where the runner of a rootless container
+ // owns their own cgroup, which would allow us to set up a
+ // cgroup for each path.
+ if m.Cgroups.Paths != nil {
+ return fmt.Errorf("cannot change cgroup path in rootless container")
+ }
+
+ // We load the paths into the manager.
+ paths := make(map[string]string)
+ for _, sys := range subsystems {
+ name := sys.Name()
+
+ path, err := cgroups.GetOwnCgroupPath(name)
+ if err != nil {
+ // Ignore paths we couldn't resolve.
+ continue
+ }
+
+ paths[name] = path
+ }
+
+ m.Paths = paths
+ return nil
+}
+
+func (m *Manager) GetPaths() map[string]string {
+ return m.Paths
+}
+
+func (m *Manager) Set(container *configs.Config) error {
+ // We have to re-do the validation here, since someone might decide to
+ // update a rootless container.
+ return validate.New().Validate(container)
+}
+
+func (m *Manager) GetPids() ([]int, error) {
+ dir, err := cgroups.GetOwnCgroupPath("devices")
+ if err != nil {
+ return nil, err
+ }
+ return cgroups.GetPids(dir)
+}
+
+func (m *Manager) GetAllPids() ([]int, error) {
+ dir, err := cgroups.GetOwnCgroupPath("devices")
+ if err != nil {
+ return nil, err
+ }
+ return cgroups.GetAllPids(dir)
+}
+
+func (m *Manager) GetStats() (*cgroups.Stats, error) {
+ // TODO(cyphar): We can make this work if we figure out a way to allow usage
+ // of cgroups with a rootless container. While this doesn't
+ // actually require write access to a cgroup directory, the
+ // statistics are not useful if they can be affected by
+ // non-container processes.
+ return nil, fmt.Errorf("cannot get cgroup stats in rootless container")
+}
+
+func (m *Manager) Freeze(state configs.FreezerState) error {
+ // TODO(cyphar): We can make this work if we figure out a way to allow usage
+ // of cgroups with a rootless container.
+ return fmt.Errorf("cannot use freezer cgroup in rootless container")
+}
+
+func (m *Manager) Destroy() error {
+ // We don't have to do anything here because we didn't do any setup.
+ return nil
+}
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/stats.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/stats.go
index 8eeedc55b0..e11c5c7416 100644
--- a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/stats.go
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/stats.go
@@ -64,6 +64,13 @@ type PidsStats struct {
Limit uint64 `json:"limit,omitempty"`
}
+type FilesStats struct {
+ // number of pids in the cgroup
+ Usage uint64 `json:"usage,omitempty"`
+ // active pids hard limit
+ Limit uint64 `json:"limit,omitempty"`
+}
+
type BlkioStatEntry struct {
Major uint64 `json:"major,omitempty"`
Minor uint64 `json:"minor,omitempty"`
@@ -96,6 +103,7 @@ type Stats struct {
CpuStats CpuStats `json:"cpu_stats,omitempty"`
MemoryStats MemoryStats `json:"memory_stats,omitempty"`
PidsStats PidsStats `json:"pids_stats,omitempty"`
+ FilesStats FilesStats `json:"files_stats,omitempty"`
BlkioStats BlkioStats `json:"blkio_stats,omitempty"`
// the map is in the format "size of hugepage: stats of the hugepage"
HugetlbStats map[string]HugetlbStats `json:"hugetlb_stats,omitempty"`
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/systemd/apply_nosystemd.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/systemd/apply_nosystemd.go
new file mode 100644
index 0000000000..7de9ae6050
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/systemd/apply_nosystemd.go
@@ -0,0 +1,55 @@
+// +build !linux
+
+package systemd
+
+import (
+ "fmt"
+
+ "github.com/opencontainers/runc/libcontainer/cgroups"
+ "github.com/opencontainers/runc/libcontainer/configs"
+)
+
+type Manager struct {
+ Cgroups *configs.Cgroup
+ Paths map[string]string
+}
+
+func UseSystemd() bool {
+ return false
+}
+
+func (m *Manager) Apply(pid int) error {
+ return fmt.Errorf("Systemd not supported")
+}
+
+func (m *Manager) GetPids() ([]int, error) {
+ return nil, fmt.Errorf("Systemd not supported")
+}
+
+func (m *Manager) GetAllPids() ([]int, error) {
+ return nil, fmt.Errorf("Systemd not supported")
+}
+
+func (m *Manager) Destroy() error {
+ return fmt.Errorf("Systemd not supported")
+}
+
+func (m *Manager) GetPaths() map[string]string {
+ return nil
+}
+
+func (m *Manager) GetStats() (*cgroups.Stats, error) {
+ return nil, fmt.Errorf("Systemd not supported")
+}
+
+func (m *Manager) Set(container *configs.Config) error {
+ return nil, fmt.Errorf("Systemd not supported")
+}
+
+func (m *Manager) Freeze(state configs.FreezerState) error {
+ return fmt.Errorf("Systemd not supported")
+}
+
+func Freeze(c *configs.Cgroup, state configs.FreezerState) error {
+ return fmt.Errorf("Systemd not supported")
+}
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/systemd/apply_systemd.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/systemd/apply_systemd.go
new file mode 100644
index 0000000000..0411b72390
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/cgroups/systemd/apply_systemd.go
@@ -0,0 +1,556 @@
+// +build linux
+
+package systemd
+
+import (
+ "errors"
+ "fmt"
+ "os"
+ "path/filepath"
+ "strings"
+ "sync"
+ "time"
+
+ systemdDbus "github.com/coreos/go-systemd/dbus"
+ systemdUtil "github.com/coreos/go-systemd/util"
+ "github.com/godbus/dbus"
+ "github.com/opencontainers/runc/libcontainer/cgroups"
+ "github.com/opencontainers/runc/libcontainer/cgroups/fs"
+ "github.com/opencontainers/runc/libcontainer/configs"
+)
+
+type Manager struct {
+ mu sync.Mutex
+ Cgroups *configs.Cgroup
+ Paths map[string]string
+}
+
+type subsystem interface {
+ // Name returns the name of the subsystem.
+ Name() string
+ // Returns the stats, as 'stats', corresponding to the cgroup under 'path'.
+ GetStats(path string, stats *cgroups.Stats) error
+ // Set the cgroup represented by cgroup.
+ Set(path string, cgroup *configs.Cgroup) error
+}
+
+var errSubsystemDoesNotExist = errors.New("cgroup: subsystem does not exist")
+
+type subsystemSet []subsystem
+
+func (s subsystemSet) Get(name string) (subsystem, error) {
+ for _, ss := range s {
+ if ss.Name() == name {
+ return ss, nil
+ }
+ }
+ return nil, errSubsystemDoesNotExist
+}
+
+var subsystems = subsystemSet{
+ &fs.CpusetGroup{},
+ &fs.DevicesGroup{},
+ &fs.MemoryGroup{},
+ &fs.CpuGroup{},
+ &fs.CpuacctGroup{},
+ &fs.PidsGroup{},
+ &fs.FilesGroup{},
+ &fs.BlkioGroup{},
+ &fs.HugetlbGroup{},
+ &fs.PerfEventGroup{},
+ &fs.FreezerGroup{},
+ &fs.NetPrioGroup{},
+ &fs.NetClsGroup{},
+ &fs.NameGroup{GroupName: "name=systemd"},
+}
+
+const (
+ testScopeWait = 4
+ testSliceWait = 4
+)
+
+var (
+ connLock sync.Mutex
+ theConn *systemdDbus.Conn
+ hasStartTransientUnit bool
+ hasStartTransientSliceUnit bool
+ hasTransientDefaultDependencies bool
+ hasDelegate bool
+)
+
+func newProp(name string, units interface{}) systemdDbus.Property {
+ return systemdDbus.Property{
+ Name: name,
+ Value: dbus.MakeVariant(units),
+ }
+}
+
+func UseSystemd() bool {
+ if !systemdUtil.IsRunningSystemd() {
+ return false
+ }
+
+ connLock.Lock()
+ defer connLock.Unlock()
+
+ if theConn == nil {
+ var err error
+ theConn, err = systemdDbus.New()
+ if err != nil {
+ return false
+ }
+
+ // Assume we have StartTransientUnit
+ hasStartTransientUnit = true
+
+ // But if we get UnknownMethod error we don't
+ if _, err := theConn.StartTransientUnit("test.scope", "invalid", nil, nil); err != nil {
+ if dbusError, ok := err.(dbus.Error); ok {
+ if dbusError.Name == "org.freedesktop.DBus.Error.UnknownMethod" {
+ hasStartTransientUnit = false
+ return hasStartTransientUnit
+ }
+ }
+ }
+
+ // Ensure the scope name we use doesn't exist. Use the Pid to
+ // avoid collisions between multiple libcontainer users on a
+ // single host.
+ scope := fmt.Sprintf("libcontainer-%d-systemd-test-default-dependencies.scope", os.Getpid())
+ testScopeExists := true
+ for i := 0; i <= testScopeWait; i++ {
+ if _, err := theConn.StopUnit(scope, "replace", nil); err != nil {
+ if dbusError, ok := err.(dbus.Error); ok {
+ if strings.Contains(dbusError.Name, "org.freedesktop.systemd1.NoSuchUnit") {
+ testScopeExists = false
+ break
+ }
+ }
+ }
+ time.Sleep(time.Millisecond)
+ }
+
+ // Bail out if we can't kill this scope without testing for DefaultDependencies
+ if testScopeExists {
+ return hasStartTransientUnit
+ }
+
+ // Assume StartTransientUnit on a scope allows DefaultDependencies
+ hasTransientDefaultDependencies = true
+ ddf := newProp("DefaultDependencies", false)
+ if _, err := theConn.StartTransientUnit(scope, "replace", []systemdDbus.Property{ddf}, nil); err != nil {
+ if dbusError, ok := err.(dbus.Error); ok {
+ if strings.Contains(dbusError.Name, "org.freedesktop.DBus.Error.PropertyReadOnly") {
+ hasTransientDefaultDependencies = false
+ }
+ }
+ }
+
+ // Not critical because of the stop unit logic above.
+ theConn.StopUnit(scope, "replace", nil)
+
+ // Assume StartTransientUnit on a scope allows Delegate
+ hasDelegate = true
+ dl := newProp("Delegate", true)
+ if _, err := theConn.StartTransientUnit(scope, "replace", []systemdDbus.Property{dl}, nil); err != nil {
+ if dbusError, ok := err.(dbus.Error); ok {
+ if strings.Contains(dbusError.Name, "org.freedesktop.DBus.Error.PropertyReadOnly") {
+ hasDelegate = false
+ }
+ }
+ }
+
+ // Assume we have the ability to start a transient unit as a slice
+ // This was broken until systemd v229, but has been back-ported on RHEL environments >= 219
+ // For details, see: https://bugzilla.redhat.com/show_bug.cgi?id=1370299
+ hasStartTransientSliceUnit = true
+
+ // To ensure simple clean-up, we create a slice off the root with no hierarchy
+ slice := fmt.Sprintf("libcontainer_%d_systemd_test_default.slice", os.Getpid())
+ if _, err := theConn.StartTransientUnit(slice, "replace", nil, nil); err != nil {
+ if _, ok := err.(dbus.Error); ok {
+ hasStartTransientSliceUnit = false
+ }
+ }
+
+ for i := 0; i <= testSliceWait; i++ {
+ if _, err := theConn.StopUnit(slice, "replace", nil); err != nil {
+ if dbusError, ok := err.(dbus.Error); ok {
+ if strings.Contains(dbusError.Name, "org.freedesktop.systemd1.NoSuchUnit") {
+ hasStartTransientSliceUnit = false
+ break
+ }
+ }
+ } else {
+ break
+ }
+ time.Sleep(time.Millisecond)
+ }
+
+ // Not critical because of the stop unit logic above.
+ theConn.StopUnit(scope, "replace", nil)
+ theConn.StopUnit(slice, "replace", nil)
+ }
+ return hasStartTransientUnit
+}
+
+func (m *Manager) Apply(pid int) error {
+ var (
+ c = m.Cgroups
+ unitName = getUnitName(c)
+ slice = "system.slice"
+ properties []systemdDbus.Property
+ )
+
+ if c.Paths != nil {
+ paths := make(map[string]string)
+ for name, path := range c.Paths {
+ _, err := getSubsystemPath(m.Cgroups, name)
+ if err != nil {
+ // Don't fail if a cgroup hierarchy was not found, just skip this subsystem
+ if cgroups.IsNotFound(err) {
+ continue
+ }
+ return err
+ }
+ paths[name] = path
+ }
+ m.Paths = paths
+ return cgroups.EnterPid(m.Paths, pid)
+ }
+
+ if c.Parent != "" {
+ slice = c.Parent
+ }
+
+ properties = append(properties, systemdDbus.PropDescription("libcontainer container "+c.Name))
+
+ // if we create a slice, the parent is defined via a Wants=
+ if strings.HasSuffix(unitName, ".slice") {
+ // This was broken until systemd v229, but has been back-ported on RHEL environments >= 219
+ if !hasStartTransientSliceUnit {
+ return fmt.Errorf("systemd version does not support ability to start a slice as transient unit")
+ }
+ properties = append(properties, systemdDbus.PropWants(slice))
+ } else {
+ // otherwise, we use Slice=
+ properties = append(properties, systemdDbus.PropSlice(slice))
+ }
+
+ // only add pid if its valid, -1 is used w/ general slice creation.
+ if pid != -1 {
+ properties = append(properties, newProp("PIDs", []uint32{uint32(pid)}))
+ }
+
+ if hasDelegate {
+ // This is only supported on systemd versions 218 and above.
+ properties = append(properties, newProp("Delegate", true))
+ }
+
+ // Always enable accounting, this gets us the same behaviour as the fs implementation,
+ // plus the kernel has some problems with joining the memory cgroup at a later time.
+ properties = append(properties,
+ newProp("MemoryAccounting", true),
+ newProp("CPUAccounting", true),
+ newProp("BlockIOAccounting", true))
+
+ if hasTransientDefaultDependencies {
+ properties = append(properties,
+ newProp("DefaultDependencies", false))
+ }
+
+ if c.Resources.Memory != 0 {
+ properties = append(properties,
+ newProp("MemoryLimit", c.Resources.Memory))
+ }
+
+ if c.Resources.CpuShares != 0 {
+ properties = append(properties,
+ newProp("CPUShares", c.Resources.CpuShares))
+ }
+
+ // cpu.cfs_quota_us and cpu.cfs_period_us are controlled by systemd.
+ if c.Resources.CpuQuota != 0 && c.Resources.CpuPeriod != 0 {
+ cpuQuotaPerSecUSec := uint64(c.Resources.CpuQuota*1000000) / c.Resources.CpuPeriod
+ properties = append(properties,
+ newProp("CPUQuotaPerSecUSec", cpuQuotaPerSecUSec))
+ }
+
+ if c.Resources.BlkioWeight != 0 {
+ properties = append(properties,
+ newProp("BlockIOWeight", uint64(c.Resources.BlkioWeight)))
+ }
+
+ // We have to set kernel memory here, as we can't change it once
+ // processes have been attached to the cgroup.
+ if c.Resources.KernelMemory != 0 {
+ if err := setKernelMemory(c); err != nil {
+ return err
+ }
+ }
+
+ if _, err := theConn.StartTransientUnit(unitName, "replace", properties, nil); err != nil && !isUnitExists(err) {
+ return err
+ }
+
+ if err := joinCgroups(c, pid); err != nil {
+ return err
+ }
+
+ paths := make(map[string]string)
+ for _, s := range subsystems {
+ subsystemPath, err := getSubsystemPath(m.Cgroups, s.Name())
+ if err != nil {
+ // Don't fail if a cgroup hierarchy was not found, just skip this subsystem
+ if cgroups.IsNotFound(err) {
+ continue
+ }
+ return err
+ }
+ paths[s.Name()] = subsystemPath
+ }
+ m.Paths = paths
+ return nil
+}
+
+func (m *Manager) Destroy() error {
+ if m.Cgroups.Paths != nil {
+ return nil
+ }
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ theConn.StopUnit(getUnitName(m.Cgroups), "replace", nil)
+ if err := cgroups.RemovePaths(m.Paths); err != nil {
+ return err
+ }
+ m.Paths = make(map[string]string)
+ return nil
+}
+
+func (m *Manager) GetPaths() map[string]string {
+ m.mu.Lock()
+ paths := m.Paths
+ m.mu.Unlock()
+ return paths
+}
+
+func join(c *configs.Cgroup, subsystem string, pid int) (string, error) {
+ path, err := getSubsystemPath(c, subsystem)
+ if err != nil {
+ return "", err
+ }
+ if err := os.MkdirAll(path, 0755); err != nil {
+ return "", err
+ }
+ if err := cgroups.WriteCgroupProc(path, pid); err != nil {
+ return "", err
+ }
+ return path, nil
+}
+
+func joinCgroups(c *configs.Cgroup, pid int) error {
+ for _, sys := range subsystems {
+ name := sys.Name()
+ switch name {
+ case "name=systemd":
+ // let systemd handle this
+ break
+ case "cpuset":
+ path, err := getSubsystemPath(c, name)
+ if err != nil && !cgroups.IsNotFound(err) {
+ return err
+ }
+ s := &fs.CpusetGroup{}
+ if err := s.ApplyDir(path, c, pid); err != nil {
+ return err
+ }
+ break
+ default:
+ _, err := join(c, name, pid)
+ if err != nil {
+ // Even if it's `not found` error, we'll return err
+ // because devices cgroup is hard requirement for
+ // container security.
+ if name == "devices" {
+ return err
+ }
+ // For other subsystems, omit the `not found` error
+ // because they are optional.
+ if !cgroups.IsNotFound(err) {
+ return err
+ }
+ }
+ }
+ }
+
+ return nil
+}
+
+// systemd represents slice hierarchy using `-`, so we need to follow suit when
+// generating the path of slice. Essentially, test-a-b.slice becomes
+// test.slice/test-a.slice/test-a-b.slice.
+func ExpandSlice(slice string) (string, error) {
+ suffix := ".slice"
+ // Name has to end with ".slice", but can't be just ".slice".
+ if len(slice) < len(suffix) || !strings.HasSuffix(slice, suffix) {
+ return "", fmt.Errorf("invalid slice name: %s", slice)
+ }
+
+ // Path-separators are not allowed.
+ if strings.Contains(slice, "/") {
+ return "", fmt.Errorf("invalid slice name: %s", slice)
+ }
+
+ var path, prefix string
+ sliceName := strings.TrimSuffix(slice, suffix)
+ // if input was -.slice, we should just return root now
+ if sliceName == "-" {
+ return "/", nil
+ }
+ for _, component := range strings.Split(sliceName, "-") {
+ // test--a.slice isn't permitted, nor is -test.slice.
+ if component == "" {
+ return "", fmt.Errorf("invalid slice name: %s", slice)
+ }
+
+ // Append the component to the path and to the prefix.
+ path += prefix + component + suffix + "/"
+ prefix += component + "-"
+ }
+
+ return path, nil
+}
+
+func getSubsystemPath(c *configs.Cgroup, subsystem string) (string, error) {
+ mountpoint, err := cgroups.FindCgroupMountpoint(subsystem)
+ if err != nil {
+ return "", err
+ }
+
+ initPath, err := cgroups.GetInitCgroup(subsystem)
+ if err != nil {
+ return "", err
+ }
+ // if pid 1 is systemd 226 or later, it will be in init.scope, not the root
+ initPath = strings.TrimSuffix(filepath.Clean(initPath), "init.scope")
+
+ slice := "system.slice"
+ if c.Parent != "" {
+ slice = c.Parent
+ }
+
+ slice, err = ExpandSlice(slice)
+ if err != nil {
+ return "", err
+ }
+
+ return filepath.Join(mountpoint, initPath, slice, getUnitName(c)), nil
+}
+
+func (m *Manager) Freeze(state configs.FreezerState) error {
+ path, err := getSubsystemPath(m.Cgroups, "freezer")
+ if err != nil {
+ return err
+ }
+ prevState := m.Cgroups.Resources.Freezer
+ m.Cgroups.Resources.Freezer = state
+ freezer, err := subsystems.Get("freezer")
+ if err != nil {
+ return err
+ }
+ err = freezer.Set(path, m.Cgroups)
+ if err != nil {
+ m.Cgroups.Resources.Freezer = prevState
+ return err
+ }
+ return nil
+}
+
+func (m *Manager) GetPids() ([]int, error) {
+ path, err := getSubsystemPath(m.Cgroups, "devices")
+ if err != nil {
+ return nil, err
+ }
+ return cgroups.GetPids(path)
+}
+
+func (m *Manager) GetAllPids() ([]int, error) {
+ path, err := getSubsystemPath(m.Cgroups, "devices")
+ if err != nil {
+ return nil, err
+ }
+ return cgroups.GetAllPids(path)
+}
+
+func (m *Manager) GetStats() (*cgroups.Stats, error) {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ stats := cgroups.NewStats()
+ for name, path := range m.Paths {
+ sys, err := subsystems.Get(name)
+ if err == errSubsystemDoesNotExist || !cgroups.PathExists(path) {
+ continue
+ }
+ if err := sys.GetStats(path, stats); err != nil {
+ return nil, err
+ }
+ }
+
+ return stats, nil
+}
+
+func (m *Manager) Set(container *configs.Config) error {
+ // If Paths are set, then we are just joining cgroups paths
+ // and there is no need to set any values.
+ if m.Cgroups.Paths != nil {
+ return nil
+ }
+ for _, sys := range subsystems {
+ // Get the subsystem path, but don't error out for not found cgroups.
+ path, err := getSubsystemPath(container.Cgroups, sys.Name())
+ if err != nil && !cgroups.IsNotFound(err) {
+ return err
+ }
+
+ if err := sys.Set(path, container.Cgroups); err != nil {
+ return err
+ }
+ }
+
+ if m.Paths["cpu"] != "" {
+ if err := fs.CheckCpushares(m.Paths["cpu"], container.Cgroups.Resources.CpuShares); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func getUnitName(c *configs.Cgroup) string {
+ // by default, we create a scope unless the user explicitly asks for a slice.
+ if !strings.HasSuffix(c.Name, ".slice") {
+ return fmt.Sprintf("%s-%s.scope", c.ScopePrefix, c.Name)
+ }
+ return c.Name
+}
+
+func setKernelMemory(c *configs.Cgroup) error {
+ path, err := getSubsystemPath(c, "memory")
+ if err != nil && !cgroups.IsNotFound(err) {
+ return err
+ }
+
+ if err := os.MkdirAll(path, 0755); err != nil {
+ return err
+ }
+ return fs.EnableKernelMemoryAccounting(path)
+}
+
+// isUnitExists returns true if the error is that a systemd unit already exists.
+func isUnitExists(err error) bool {
+ if err != nil {
+ if dbusError, ok := err.(dbus.Error); ok {
+ return strings.Contains(dbusError.Name, "org.freedesktop.systemd1.UnitExists")
+ }
+ }
+ return false
+}
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/configs/cgroup_linux.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/configs/cgroup_linux.go
index e15a662f52..c5634f597e 100644
--- a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/configs/cgroup_linux.go
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/configs/cgroup_linux.go
@@ -81,6 +81,9 @@ type Resources struct {
// Process limit; set <= `0' to disable limit.
PidsLimit int64 `json:"pids_limit"`
+ // Process open files limit.
+ FilesLimit int64 `json:"files_limit"`
+
// Specifies per cgroup weight, range is from 10 to 1000.
BlkioWeight uint16 `json:"blkio_weight"`
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/utils/cmsg.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/utils/cmsg.go
new file mode 100644
index 0000000000..2cbb6491a7
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/utils/cmsg.go
@@ -0,0 +1,95 @@
+// +build linux
+
+package utils
+
+/*
+ * Copyright 2016, 2017 SUSE LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import (
+ "fmt"
+ "os"
+
+ "golang.org/x/sys/unix"
+)
+
+// MaxSendfdLen is the maximum length of the name of a file descriptor being
+// sent using SendFd. The name of the file handle returned by RecvFd will never
+// be larger than this value.
+const MaxNameLen = 4096
+
+// oobSpace is the size of the oob slice required to store a single FD. Note
+// that unix.UnixRights appears to make the assumption that fd is always int32,
+// so sizeof(fd) = 4.
+var oobSpace = unix.CmsgSpace(4)
+
+// RecvFd waits for a file descriptor to be sent over the given AF_UNIX
+// socket. The file name of the remote file descriptor will be recreated
+// locally (it is sent as non-auxiliary data in the same payload).
+func RecvFd(socket *os.File) (*os.File, error) {
+ // For some reason, unix.Recvmsg uses the length rather than the capacity
+ // when passing the msg_controllen and other attributes to recvmsg. So we
+ // have to actually set the length.
+ name := make([]byte, MaxNameLen)
+ oob := make([]byte, oobSpace)
+
+ sockfd := socket.Fd()
+ n, oobn, _, _, err := unix.Recvmsg(int(sockfd), name, oob, 0)
+ if err != nil {
+ return nil, err
+ }
+
+ if n >= MaxNameLen || oobn != oobSpace {
+ return nil, fmt.Errorf("recvfd: incorrect number of bytes read (n=%d oobn=%d)", n, oobn)
+ }
+
+ // Truncate.
+ name = name[:n]
+ oob = oob[:oobn]
+
+ scms, err := unix.ParseSocketControlMessage(oob)
+ if err != nil {
+ return nil, err
+ }
+ if len(scms) != 1 {
+ return nil, fmt.Errorf("recvfd: number of SCMs is not 1: %d", len(scms))
+ }
+ scm := scms[0]
+
+ fds, err := unix.ParseUnixRights(&scm)
+ if err != nil {
+ return nil, err
+ }
+ if len(fds) != 1 {
+ return nil, fmt.Errorf("recvfd: number of fds is not 1: %d", len(fds))
+ }
+ fd := uintptr(fds[0])
+
+ return os.NewFile(fd, string(name)), nil
+}
+
+// SendFd sends a file descriptor over the given AF_UNIX socket. In
+// addition, the file.Name() of the given file will also be sent as
+// non-auxiliary data in the same payload (allowing to send contextual
+// information for a file descriptor).
+func SendFd(socket, file *os.File) error {
+ name := []byte(file.Name())
+ if len(name) >= MaxNameLen {
+ return fmt.Errorf("sendfd: filename too long: %s", file.Name())
+ }
+ oob := unix.UnixRights(int(file.Fd()))
+
+ return unix.Sendmsg(int(socket.Fd()), name, oob, nil, 0)
+}
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/utils/utils.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/utils/utils.go
new file mode 100644
index 0000000000..2b35b9a7b6
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/utils/utils.go
@@ -0,0 +1,126 @@
+package utils
+
+import (
+ "crypto/rand"
+ "encoding/hex"
+ "encoding/json"
+ "io"
+ "os"
+ "path/filepath"
+ "strings"
+ "syscall"
+ "unsafe"
+)
+
+const (
+ exitSignalOffset = 128
+)
+
+// GenerateRandomName returns a new name joined with a prefix. This size
+// specified is used to truncate the randomly generated value
+func GenerateRandomName(prefix string, size int) (string, error) {
+ id := make([]byte, 32)
+ if _, err := io.ReadFull(rand.Reader, id); err != nil {
+ return "", err
+ }
+ if size > 64 {
+ size = 64
+ }
+ return prefix + hex.EncodeToString(id)[:size], nil
+}
+
+// ResolveRootfs ensures that the current working directory is
+// not a symlink and returns the absolute path to the rootfs
+func ResolveRootfs(uncleanRootfs string) (string, error) {
+ rootfs, err := filepath.Abs(uncleanRootfs)
+ if err != nil {
+ return "", err
+ }
+ return filepath.EvalSymlinks(rootfs)
+}
+
+// ExitStatus returns the correct exit status for a process based on if it
+// was signaled or exited cleanly
+func ExitStatus(status syscall.WaitStatus) int {
+ if status.Signaled() {
+ return exitSignalOffset + int(status.Signal())
+ }
+ return status.ExitStatus()
+}
+
+// WriteJSON writes the provided struct v to w using standard json marshaling
+func WriteJSON(w io.Writer, v interface{}) error {
+ data, err := json.Marshal(v)
+ if err != nil {
+ return err
+ }
+ _, err = w.Write(data)
+ return err
+}
+
+// CleanPath makes a path safe for use with filepath.Join. This is done by not
+// only cleaning the path, but also (if the path is relative) adding a leading
+// '/' and cleaning it (then removing the leading '/'). This ensures that a
+// path resulting from prepending another path will always resolve to lexically
+// be a subdirectory of the prefixed path. This is all done lexically, so paths
+// that include symlinks won't be safe as a result of using CleanPath.
+func CleanPath(path string) string {
+ // Deal with empty strings nicely.
+ if path == "" {
+ return ""
+ }
+
+ // Ensure that all paths are cleaned (especially problematic ones like
+ // "/../../../../../" which can cause lots of issues).
+ path = filepath.Clean(path)
+
+ // If the path isn't absolute, we need to do more processing to fix paths
+ // such as "../../../../<etc>/some/path". We also shouldn't convert absolute
+ // paths to relative ones.
+ if !filepath.IsAbs(path) {
+ path = filepath.Clean(string(os.PathSeparator) + path)
+ // This can't fail, as (by definition) all paths are relative to root.
+ path, _ = filepath.Rel(string(os.PathSeparator), path)
+ }
+
+ // Clean the path again for good measure.
+ return filepath.Clean(path)
+}
+
+// SearchLabels searches a list of key-value pairs for the provided key and
+// returns the corresponding value. The pairs must be separated with '='.
+func SearchLabels(labels []string, query string) string {
+ for _, l := range labels {
+ parts := strings.SplitN(l, "=", 2)
+ if len(parts) < 2 {
+ continue
+ }
+ if parts[0] == query {
+ return parts[1]
+ }
+ }
+ return ""
+}
+
+// Annotations returns the bundle path and user defined annotations from the
+// libcontainer state. We need to remove the bundle because that is a label
+// added by libcontainer.
+func Annotations(labels []string) (bundle string, userAnnotations map[string]string) {
+ userAnnotations = make(map[string]string)
+ for _, l := range labels {
+ parts := strings.SplitN(l, "=", 2)
+ if len(parts) < 2 {
+ continue
+ }
+ if parts[0] == "bundle" {
+ bundle = parts[1]
+ } else {
+ userAnnotations[parts[0]] = parts[1]
+ }
+ }
+ return
+}
+
+func GetIntSize() int {
+ return int(unsafe.Sizeof(1))
+}
diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/utils/utils_unix.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/utils/utils_unix.go
new file mode 100644
index 0000000000..7b798cc79d
--- /dev/null
+++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/utils/utils_unix.go
@@ -0,0 +1,43 @@
+// +build !windows
+
+package utils
+
+import (
+ "io/ioutil"
+ "os"
+ "strconv"
+ "syscall"
+)
+
+func CloseExecFrom(minFd int) error {
+ fdList, err := ioutil.ReadDir("/proc/self/fd")
+ if err != nil {
+ return err
+ }
+ for _, fi := range fdList {
+ fd, err := strconv.Atoi(fi.Name())
+ if err != nil {
+ // ignore non-numeric file names
+ continue
+ }
+
+ if fd < minFd {
+ // ignore descriptors lower than our specified minimum
+ continue
+ }
+
+ // intentionally ignore errors from syscall.CloseOnExec
+ syscall.CloseOnExec(fd)
+ // the cases where this might fail are basically file descriptors that have already been closed (including and especially the one that was created when ioutil.ReadDir did the "opendir" syscall)
+ }
+ return nil
+}
+
+// NewSockPair returns a new unix socket pair
+func NewSockPair(name string) (parent *os.File, child *os.File, err error) {
+ fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0)
+ if err != nil {
+ return nil, nil, err
+ }
+ return os.NewFile(uintptr(fds[1]), name+"-p"), os.NewFile(uintptr(fds[0]), name+"-c"), nil
+}
--
2.17.1
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/gnaygnil/docker.git
git@gitee.com:gnaygnil/docker.git
gnaygnil
docker
docker
master

搜索帮助