123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906 |
- /*
- Copyright 2014 The Kubernetes Authors.
- 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 kubelet
- import (
- "bytes"
- "errors"
- "fmt"
- "io"
- "io/ioutil"
- "net"
- "os"
- "reflect"
- "sort"
- "testing"
- "time"
- cadvisorapi "github.com/google/cadvisor/info/v1"
- cadvisorapiv2 "github.com/google/cadvisor/info/v2"
- "github.com/stretchr/testify/assert"
- "k8s.io/kubernetes/pkg/api"
- "k8s.io/kubernetes/pkg/api/resource"
- "k8s.io/kubernetes/pkg/api/testapi"
- "k8s.io/kubernetes/pkg/api/unversioned"
- "k8s.io/kubernetes/pkg/apis/componentconfig"
- "k8s.io/kubernetes/pkg/capabilities"
- "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
- "k8s.io/kubernetes/pkg/client/record"
- "k8s.io/kubernetes/pkg/client/testing/core"
- cadvisortest "k8s.io/kubernetes/pkg/kubelet/cadvisor/testing"
- "k8s.io/kubernetes/pkg/kubelet/cm"
- "k8s.io/kubernetes/pkg/kubelet/config"
- kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
- containertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
- "k8s.io/kubernetes/pkg/kubelet/eviction"
- "k8s.io/kubernetes/pkg/kubelet/images"
- "k8s.io/kubernetes/pkg/kubelet/lifecycle"
- "k8s.io/kubernetes/pkg/kubelet/network"
- nettest "k8s.io/kubernetes/pkg/kubelet/network/testing"
- "k8s.io/kubernetes/pkg/kubelet/pleg"
- kubepod "k8s.io/kubernetes/pkg/kubelet/pod"
- podtest "k8s.io/kubernetes/pkg/kubelet/pod/testing"
- proberesults "k8s.io/kubernetes/pkg/kubelet/prober/results"
- probetest "k8s.io/kubernetes/pkg/kubelet/prober/testing"
- "k8s.io/kubernetes/pkg/kubelet/server/stats"
- "k8s.io/kubernetes/pkg/kubelet/status"
- kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
- "k8s.io/kubernetes/pkg/kubelet/util/queue"
- kubeletvolume "k8s.io/kubernetes/pkg/kubelet/volumemanager"
- "k8s.io/kubernetes/pkg/runtime"
- "k8s.io/kubernetes/pkg/types"
- "k8s.io/kubernetes/pkg/util/clock"
- "k8s.io/kubernetes/pkg/util/diff"
- "k8s.io/kubernetes/pkg/util/flowcontrol"
- "k8s.io/kubernetes/pkg/util/mount"
- utilruntime "k8s.io/kubernetes/pkg/util/runtime"
- "k8s.io/kubernetes/pkg/util/sets"
- "k8s.io/kubernetes/pkg/util/term"
- "k8s.io/kubernetes/pkg/util/wait"
- "k8s.io/kubernetes/pkg/volume"
- _ "k8s.io/kubernetes/pkg/volume/host_path"
- volumetest "k8s.io/kubernetes/pkg/volume/testing"
- "k8s.io/kubernetes/pkg/volume/util/volumehelper"
- )
- func init() {
- utilruntime.ReallyCrash = true
- }
- const (
- testKubeletHostname = "127.0.0.1"
- testReservationCPU = "200m"
- testReservationMemory = "100M"
- maxImageTagsForTest = 3
- // TODO(harry) any global place for these two?
- // Reasonable size range of all container images. 90%ile of images on dockerhub drops into this range.
- minImgSize int64 = 23 * 1024 * 1024
- maxImgSize int64 = 1000 * 1024 * 1024
- )
- type TestKubelet struct {
- kubelet *Kubelet
- fakeRuntime *containertest.FakeRuntime
- fakeCadvisor *cadvisortest.Mock
- fakeKubeClient *fake.Clientset
- fakeMirrorClient *podtest.FakeMirrorClient
- fakeClock *clock.FakeClock
- mounter mount.Interface
- volumePlugin *volumetest.FakeVolumePlugin
- }
- // newTestKubelet returns test kubelet with two images.
- func newTestKubelet(t *testing.T, controllerAttachDetachEnabled bool) *TestKubelet {
- imageList := []kubecontainer.Image{
- {
- ID: "abc",
- RepoTags: []string{"gcr.io/google_containers:v1", "gcr.io/google_containers:v2"},
- Size: 123,
- },
- {
- ID: "efg",
- RepoTags: []string{"gcr.io/google_containers:v3", "gcr.io/google_containers:v4"},
- Size: 456,
- },
- }
- return newTestKubeletWithImageList(t, imageList, controllerAttachDetachEnabled)
- }
- func newTestKubeletWithImageList(
- t *testing.T,
- imageList []kubecontainer.Image,
- controllerAttachDetachEnabled bool) *TestKubelet {
- fakeRuntime := &containertest.FakeRuntime{}
- fakeRuntime.RuntimeType = "test"
- fakeRuntime.VersionInfo = "1.5.0"
- fakeRuntime.ImageList = imageList
- fakeRecorder := &record.FakeRecorder{}
- fakeKubeClient := &fake.Clientset{}
- kubelet := &Kubelet{}
- kubelet.recorder = fakeRecorder
- kubelet.kubeClient = fakeKubeClient
- kubelet.os = &containertest.FakeOS{}
- kubelet.hostname = testKubeletHostname
- kubelet.nodeName = testKubeletHostname
- kubelet.runtimeState = newRuntimeState(maxWaitForContainerRuntime)
- kubelet.runtimeState.setNetworkState(nil)
- kubelet.networkPlugin, _ = network.InitNetworkPlugin([]network.NetworkPlugin{}, "", nettest.NewFakeHost(nil), componentconfig.HairpinNone, kubelet.nonMasqueradeCIDR, 1440)
- if tempDir, err := ioutil.TempDir("/tmp", "kubelet_test."); err != nil {
- t.Fatalf("can't make a temp rootdir: %v", err)
- } else {
- kubelet.rootDirectory = tempDir
- }
- if err := os.MkdirAll(kubelet.rootDirectory, 0750); err != nil {
- t.Fatalf("can't mkdir(%q): %v", kubelet.rootDirectory, err)
- }
- kubelet.sourcesReady = config.NewSourcesReady(func(_ sets.String) bool { return true })
- kubelet.masterServiceNamespace = api.NamespaceDefault
- kubelet.serviceLister = testServiceLister{}
- kubelet.nodeLister = testNodeLister{}
- kubelet.nodeInfo = testNodeInfo{}
- kubelet.recorder = fakeRecorder
- if err := kubelet.setupDataDirs(); err != nil {
- t.Fatalf("can't initialize kubelet data dirs: %v", err)
- }
- kubelet.daemonEndpoints = &api.NodeDaemonEndpoints{}
- mockCadvisor := &cadvisortest.Mock{}
- kubelet.cadvisor = mockCadvisor
- fakeMirrorClient := podtest.NewFakeMirrorClient()
- kubelet.podManager = kubepod.NewBasicPodManager(fakeMirrorClient)
- kubelet.statusManager = status.NewManager(fakeKubeClient, kubelet.podManager)
- kubelet.containerRefManager = kubecontainer.NewRefManager()
- diskSpaceManager, err := newDiskSpaceManager(mockCadvisor, DiskSpacePolicy{})
- if err != nil {
- t.Fatalf("can't initialize disk space manager: %v", err)
- }
- kubelet.diskSpaceManager = diskSpaceManager
- kubelet.containerRuntime = fakeRuntime
- kubelet.runtimeCache = containertest.NewFakeRuntimeCache(kubelet.containerRuntime)
- kubelet.reasonCache = NewReasonCache()
- kubelet.podCache = containertest.NewFakeCache(kubelet.containerRuntime)
- kubelet.podWorkers = &fakePodWorkers{
- syncPodFn: kubelet.syncPod,
- cache: kubelet.podCache,
- t: t,
- }
- kubelet.probeManager = probetest.FakeManager{}
- kubelet.livenessManager = proberesults.NewManager()
- kubelet.containerManager = cm.NewStubContainerManager()
- fakeNodeRef := &api.ObjectReference{
- Kind: "Node",
- Name: testKubeletHostname,
- UID: types.UID(testKubeletHostname),
- Namespace: "",
- }
- fakeImageGCPolicy := images.ImageGCPolicy{
- HighThresholdPercent: 90,
- LowThresholdPercent: 80,
- }
- kubelet.imageManager, err = images.NewImageGCManager(fakeRuntime, mockCadvisor, fakeRecorder, fakeNodeRef, fakeImageGCPolicy)
- fakeClock := clock.NewFakeClock(time.Now())
- kubelet.backOff = flowcontrol.NewBackOff(time.Second, time.Minute)
- kubelet.backOff.Clock = fakeClock
- kubelet.podKillingCh = make(chan *kubecontainer.PodPair, 20)
- kubelet.resyncInterval = 10 * time.Second
- kubelet.reservation = kubetypes.Reservation{
- Kubernetes: api.ResourceList{
- api.ResourceCPU: resource.MustParse(testReservationCPU),
- api.ResourceMemory: resource.MustParse(testReservationMemory),
- },
- }
- kubelet.workQueue = queue.NewBasicWorkQueue(fakeClock)
- // Relist period does not affect the tests.
- kubelet.pleg = pleg.NewGenericPLEG(fakeRuntime, 100, time.Hour, nil, clock.RealClock{})
- kubelet.clock = fakeClock
- kubelet.setNodeStatusFuncs = kubelet.defaultNodeStatusFuncs()
- // TODO: Factor out "StatsProvider" from Kubelet so we don't have a cyclic dependency
- volumeStatsAggPeriod := time.Second * 10
- kubelet.resourceAnalyzer = stats.NewResourceAnalyzer(kubelet, volumeStatsAggPeriod, kubelet.containerRuntime)
- nodeRef := &api.ObjectReference{
- Kind: "Node",
- Name: kubelet.nodeName,
- UID: types.UID(kubelet.nodeName),
- Namespace: "",
- }
- // setup eviction manager
- evictionManager, evictionAdmitHandler, err := eviction.NewManager(kubelet.resourceAnalyzer, eviction.Config{}, killPodNow(kubelet.podWorkers), kubelet.imageManager, fakeRecorder, nodeRef, kubelet.clock)
- if err != nil {
- t.Fatalf("failed to initialize eviction manager: %v", err)
- }
- kubelet.evictionManager = evictionManager
- kubelet.AddPodAdmitHandler(evictionAdmitHandler)
- plug := &volumetest.FakeVolumePlugin{PluginName: "fake", Host: nil}
- kubelet.volumePluginMgr, err =
- NewInitializedVolumePluginMgr(kubelet, []volume.VolumePlugin{plug})
- if err != nil {
- t.Fatalf("failed to initialize VolumePluginMgr: %v", err)
- }
- kubelet.mounter = &mount.FakeMounter{}
- kubelet.volumeManager, err = kubeletvolume.NewVolumeManager(
- controllerAttachDetachEnabled,
- kubelet.hostname,
- kubelet.podManager,
- fakeKubeClient,
- kubelet.volumePluginMgr,
- fakeRuntime,
- kubelet.mounter,
- kubelet.getPodsDir(),
- kubelet.recorder)
- if err != nil {
- t.Fatalf("failed to initialize volume manager: %v", err)
- }
- // enable active deadline handler
- activeDeadlineHandler, err := newActiveDeadlineHandler(kubelet.statusManager, kubelet.recorder, kubelet.clock)
- if err != nil {
- t.Fatalf("can't initialize active deadline handler: %v", err)
- }
- kubelet.AddPodSyncLoopHandler(activeDeadlineHandler)
- kubelet.AddPodSyncHandler(activeDeadlineHandler)
- return &TestKubelet{kubelet, fakeRuntime, mockCadvisor, fakeKubeClient, fakeMirrorClient, fakeClock, nil, plug}
- }
- func newTestPods(count int) []*api.Pod {
- pods := make([]*api.Pod, count)
- for i := 0; i < count; i++ {
- pods[i] = &api.Pod{
- Spec: api.PodSpec{
- SecurityContext: &api.PodSecurityContext{
- HostNetwork: true,
- },
- },
- ObjectMeta: api.ObjectMeta{
- UID: types.UID(10000 + i),
- Name: fmt.Sprintf("pod%d", i),
- },
- }
- }
- return pods
- }
- var emptyPodUIDs map[types.UID]kubetypes.SyncPodType
- func TestSyncLoopTimeUpdate(t *testing.T) {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
- kubelet := testKubelet.kubelet
- loopTime1 := kubelet.LatestLoopEntryTime()
- if !loopTime1.IsZero() {
- t.Errorf("Unexpected sync loop time: %s, expected 0", loopTime1)
- }
- // Start sync ticker.
- syncCh := make(chan time.Time, 1)
- housekeepingCh := make(chan time.Time, 1)
- plegCh := make(chan *pleg.PodLifecycleEvent)
- syncCh <- time.Now()
- kubelet.syncLoopIteration(make(chan kubetypes.PodUpdate), kubelet, syncCh, housekeepingCh, plegCh)
- loopTime2 := kubelet.LatestLoopEntryTime()
- if loopTime2.IsZero() {
- t.Errorf("Unexpected sync loop time: 0, expected non-zero value.")
- }
- syncCh <- time.Now()
- kubelet.syncLoopIteration(make(chan kubetypes.PodUpdate), kubelet, syncCh, housekeepingCh, plegCh)
- loopTime3 := kubelet.LatestLoopEntryTime()
- if !loopTime3.After(loopTime1) {
- t.Errorf("Sync Loop Time was not updated correctly. Second update timestamp should be greater than first update timestamp")
- }
- }
- func TestSyncLoopAbort(t *testing.T) {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
- kubelet := testKubelet.kubelet
- kubelet.runtimeState.setRuntimeSync(time.Now())
- // The syncLoop waits on time.After(resyncInterval), set it really big so that we don't race for
- // the channel close
- kubelet.resyncInterval = time.Second * 30
- ch := make(chan kubetypes.PodUpdate)
- close(ch)
- // sanity check (also prevent this test from hanging in the next step)
- ok := kubelet.syncLoopIteration(ch, kubelet, make(chan time.Time), make(chan time.Time), make(chan *pleg.PodLifecycleEvent, 1))
- if ok {
- t.Fatalf("expected syncLoopIteration to return !ok since update chan was closed")
- }
- // this should terminate immediately; if it hangs then the syncLoopIteration isn't aborting properly
- kubelet.syncLoop(ch, kubelet)
- }
- func TestSyncPodsStartPod(t *testing.T) {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- testKubelet.fakeCadvisor.On("VersionInfo").Return(&cadvisorapi.VersionInfo{}, nil)
- testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
- testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- kubelet := testKubelet.kubelet
- fakeRuntime := testKubelet.fakeRuntime
- pods := []*api.Pod{
- podWithUidNameNsSpec("12345678", "foo", "new", api.PodSpec{
- Containers: []api.Container{
- {Name: "bar"},
- },
- }),
- }
- kubelet.podManager.SetPods(pods)
- kubelet.HandlePodSyncs(pods)
- fakeRuntime.AssertStartedPods([]string{string(pods[0].UID)})
- }
- func TestSyncPodsDeletesWhenSourcesAreReady(t *testing.T) {
- ready := false
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- fakeRuntime := testKubelet.fakeRuntime
- testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
- testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- kubelet := testKubelet.kubelet
- kubelet.sourcesReady = config.NewSourcesReady(func(_ sets.String) bool { return ready })
- fakeRuntime.PodList = []*containertest.FakePod{
- {Pod: &kubecontainer.Pod{
- ID: "12345678",
- Name: "foo",
- Namespace: "new",
- Containers: []*kubecontainer.Container{
- {Name: "bar"},
- },
- }},
- }
- kubelet.HandlePodCleanups()
- // Sources are not ready yet. Don't remove any pods.
- fakeRuntime.AssertKilledPods([]string{})
- ready = true
- kubelet.HandlePodCleanups()
- // Sources are ready. Remove unwanted pods.
- fakeRuntime.AssertKilledPods([]string{"12345678"})
- }
- func TestVolumeAttachAndMountControllerDisabled(t *testing.T) {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- kubelet := testKubelet.kubelet
- pod := podWithUidNameNsSpec("12345678", "foo", "test", api.PodSpec{
- Volumes: []api.Volume{
- {
- Name: "vol1",
- VolumeSource: api.VolumeSource{
- GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{
- PDName: "fake-device",
- },
- },
- },
- },
- })
- stopCh := runVolumeManager(kubelet)
- defer func() {
- close(stopCh)
- }()
- kubelet.podManager.SetPods([]*api.Pod{pod})
- err := kubelet.volumeManager.WaitForAttachAndMount(pod)
- if err != nil {
- t.Errorf("Expected success: %v", err)
- }
- podVolumes := kubelet.volumeManager.GetMountedVolumesForPod(
- volumehelper.GetUniquePodName(pod))
- expectedPodVolumes := []string{"vol1"}
- if len(expectedPodVolumes) != len(podVolumes) {
- t.Errorf("Unexpected volumes. Expected %#v got %#v. Manifest was: %#v", expectedPodVolumes, podVolumes, pod)
- }
- for _, name := range expectedPodVolumes {
- if _, ok := podVolumes[name]; !ok {
- t.Errorf("api.Pod volumes map is missing key: %s. %#v", name, podVolumes)
- }
- }
- if testKubelet.volumePlugin.GetNewAttacherCallCount() < 1 {
- t.Errorf("Expected plugin NewAttacher to be called at least once")
- }
- err = volumetest.VerifyWaitForAttachCallCount(
- 1 /* expectedWaitForAttachCallCount */, testKubelet.volumePlugin)
- if err != nil {
- t.Error(err)
- }
- err = volumetest.VerifyAttachCallCount(
- 1 /* expectedAttachCallCount */, testKubelet.volumePlugin)
- if err != nil {
- t.Error(err)
- }
- err = volumetest.VerifyMountDeviceCallCount(
- 1 /* expectedMountDeviceCallCount */, testKubelet.volumePlugin)
- if err != nil {
- t.Error(err)
- }
- err = volumetest.VerifySetUpCallCount(
- 1 /* expectedSetUpCallCount */, testKubelet.volumePlugin)
- if err != nil {
- t.Error(err)
- }
- }
- func TestVolumeUnmountAndDetachControllerDisabled(t *testing.T) {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- kubelet := testKubelet.kubelet
- pod := podWithUidNameNsSpec("12345678", "foo", "test", api.PodSpec{
- Volumes: []api.Volume{
- {
- Name: "vol1",
- VolumeSource: api.VolumeSource{
- GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{
- PDName: "fake-device",
- },
- },
- },
- },
- })
- stopCh := runVolumeManager(kubelet)
- defer func() {
- close(stopCh)
- }()
- // Add pod
- kubelet.podManager.SetPods([]*api.Pod{pod})
- // Verify volumes attached
- err := kubelet.volumeManager.WaitForAttachAndMount(pod)
- if err != nil {
- t.Errorf("Expected success: %v", err)
- }
- podVolumes := kubelet.volumeManager.GetMountedVolumesForPod(
- volumehelper.GetUniquePodName(pod))
- expectedPodVolumes := []string{"vol1"}
- if len(expectedPodVolumes) != len(podVolumes) {
- t.Errorf("Unexpected volumes. Expected %#v got %#v. Manifest was: %#v", expectedPodVolumes, podVolumes, pod)
- }
- for _, name := range expectedPodVolumes {
- if _, ok := podVolumes[name]; !ok {
- t.Errorf("api.Pod volumes map is missing key: %s. %#v", name, podVolumes)
- }
- }
- if testKubelet.volumePlugin.GetNewAttacherCallCount() < 1 {
- t.Errorf("Expected plugin NewAttacher to be called at least once")
- }
- err = volumetest.VerifyWaitForAttachCallCount(
- 1 /* expectedWaitForAttachCallCount */, testKubelet.volumePlugin)
- if err != nil {
- t.Error(err)
- }
- err = volumetest.VerifyAttachCallCount(
- 1 /* expectedAttachCallCount */, testKubelet.volumePlugin)
- if err != nil {
- t.Error(err)
- }
- err = volumetest.VerifyMountDeviceCallCount(
- 1 /* expectedMountDeviceCallCount */, testKubelet.volumePlugin)
- if err != nil {
- t.Error(err)
- }
- err = volumetest.VerifySetUpCallCount(
- 1 /* expectedSetUpCallCount */, testKubelet.volumePlugin)
- if err != nil {
- t.Error(err)
- }
- // Remove pod
- kubelet.podManager.SetPods([]*api.Pod{})
- err = waitForVolumeUnmount(kubelet.volumeManager, pod)
- if err != nil {
- t.Error(err)
- }
- // Verify volumes unmounted
- podVolumes = kubelet.volumeManager.GetMountedVolumesForPod(
- volumehelper.GetUniquePodName(pod))
- if len(podVolumes) != 0 {
- t.Errorf("Expected volumes to be unmounted and detached. But some volumes are still mounted: %#v", podVolumes)
- }
- err = volumetest.VerifyTearDownCallCount(
- 1 /* expectedTearDownCallCount */, testKubelet.volumePlugin)
- if err != nil {
- t.Error(err)
- }
- // Verify volumes detached and no longer reported as in use
- err = waitForVolumeDetach(api.UniqueVolumeName("fake/vol1"), kubelet.volumeManager)
- if err != nil {
- t.Error(err)
- }
- if testKubelet.volumePlugin.GetNewDetacherCallCount() < 1 {
- t.Errorf("Expected plugin NewDetacher to be called at least once")
- }
- err = volumetest.VerifyDetachCallCount(
- 1 /* expectedDetachCallCount */, testKubelet.volumePlugin)
- if err != nil {
- t.Error(err)
- }
- }
- func TestVolumeAttachAndMountControllerEnabled(t *testing.T) {
- testKubelet := newTestKubelet(t, true /* controllerAttachDetachEnabled */)
- kubelet := testKubelet.kubelet
- kubeClient := testKubelet.fakeKubeClient
- kubeClient.AddReactor("get", "nodes",
- func(action core.Action) (bool, runtime.Object, error) {
- return true, &api.Node{
- ObjectMeta: api.ObjectMeta{Name: testKubeletHostname},
- Status: api.NodeStatus{
- VolumesAttached: []api.AttachedVolume{
- {
- Name: "fake/vol1",
- DevicePath: "fake/path",
- },
- }},
- Spec: api.NodeSpec{ExternalID: testKubeletHostname},
- }, nil
- })
- kubeClient.AddReactor("*", "*", func(action core.Action) (bool, runtime.Object, error) {
- return true, nil, fmt.Errorf("no reaction implemented for %s", action)
- })
- pod := podWithUidNameNsSpec("12345678", "foo", "test", api.PodSpec{
- Volumes: []api.Volume{
- {
- Name: "vol1",
- VolumeSource: api.VolumeSource{
- GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{
- PDName: "fake-device",
- },
- },
- },
- },
- })
- stopCh := runVolumeManager(kubelet)
- defer func() {
- close(stopCh)
- }()
- kubelet.podManager.SetPods([]*api.Pod{pod})
- // Fake node status update
- go simulateVolumeInUseUpdate(
- api.UniqueVolumeName("fake/vol1"),
- stopCh,
- kubelet.volumeManager)
- err := kubelet.volumeManager.WaitForAttachAndMount(pod)
- if err != nil {
- t.Errorf("Expected success: %v", err)
- }
- podVolumes := kubelet.volumeManager.GetMountedVolumesForPod(
- volumehelper.GetUniquePodName(pod))
- expectedPodVolumes := []string{"vol1"}
- if len(expectedPodVolumes) != len(podVolumes) {
- t.Errorf("Unexpected volumes. Expected %#v got %#v. Manifest was: %#v", expectedPodVolumes, podVolumes, pod)
- }
- for _, name := range expectedPodVolumes {
- if _, ok := podVolumes[name]; !ok {
- t.Errorf("api.Pod volumes map is missing key: %s. %#v", name, podVolumes)
- }
- }
- if testKubelet.volumePlugin.GetNewAttacherCallCount() < 1 {
- t.Errorf("Expected plugin NewAttacher to be called at least once")
- }
- err = volumetest.VerifyWaitForAttachCallCount(
- 1 /* expectedWaitForAttachCallCount */, testKubelet.volumePlugin)
- if err != nil {
- t.Error(err)
- }
- err = volumetest.VerifyZeroAttachCalls(testKubelet.volumePlugin)
- if err != nil {
- t.Error(err)
- }
- err = volumetest.VerifyMountDeviceCallCount(
- 1 /* expectedMountDeviceCallCount */, testKubelet.volumePlugin)
- if err != nil {
- t.Error(err)
- }
- err = volumetest.VerifySetUpCallCount(
- 1 /* expectedSetUpCallCount */, testKubelet.volumePlugin)
- if err != nil {
- t.Error(err)
- }
- }
- func TestVolumeUnmountAndDetachControllerEnabled(t *testing.T) {
- testKubelet := newTestKubelet(t, true /* controllerAttachDetachEnabled */)
- kubelet := testKubelet.kubelet
- kubeClient := testKubelet.fakeKubeClient
- kubeClient.AddReactor("get", "nodes",
- func(action core.Action) (bool, runtime.Object, error) {
- return true, &api.Node{
- ObjectMeta: api.ObjectMeta{Name: testKubeletHostname},
- Status: api.NodeStatus{
- VolumesAttached: []api.AttachedVolume{
- {
- Name: "fake/vol1",
- DevicePath: "fake/path",
- },
- }},
- Spec: api.NodeSpec{ExternalID: testKubeletHostname},
- }, nil
- })
- kubeClient.AddReactor("*", "*", func(action core.Action) (bool, runtime.Object, error) {
- return true, nil, fmt.Errorf("no reaction implemented for %s", action)
- })
- pod := podWithUidNameNsSpec("12345678", "foo", "test", api.PodSpec{
- Volumes: []api.Volume{
- {
- Name: "vol1",
- VolumeSource: api.VolumeSource{
- GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{
- PDName: "fake-device",
- },
- },
- },
- },
- })
- stopCh := runVolumeManager(kubelet)
- defer func() {
- close(stopCh)
- }()
- // Add pod
- kubelet.podManager.SetPods([]*api.Pod{pod})
- // Fake node status update
- go simulateVolumeInUseUpdate(
- api.UniqueVolumeName("fake/vol1"),
- stopCh,
- kubelet.volumeManager)
- // Verify volumes attached
- err := kubelet.volumeManager.WaitForAttachAndMount(pod)
- if err != nil {
- t.Errorf("Expected success: %v", err)
- }
- podVolumes := kubelet.volumeManager.GetMountedVolumesForPod(
- volumehelper.GetUniquePodName(pod))
- expectedPodVolumes := []string{"vol1"}
- if len(expectedPodVolumes) != len(podVolumes) {
- t.Errorf("Unexpected volumes. Expected %#v got %#v. Manifest was: %#v", expectedPodVolumes, podVolumes, pod)
- }
- for _, name := range expectedPodVolumes {
- if _, ok := podVolumes[name]; !ok {
- t.Errorf("api.Pod volumes map is missing key: %s. %#v", name, podVolumes)
- }
- }
- if testKubelet.volumePlugin.GetNewAttacherCallCount() < 1 {
- t.Errorf("Expected plugin NewAttacher to be called at least once")
- }
- err = volumetest.VerifyWaitForAttachCallCount(
- 1 /* expectedWaitForAttachCallCount */, testKubelet.volumePlugin)
- if err != nil {
- t.Error(err)
- }
- err = volumetest.VerifyZeroAttachCalls(testKubelet.volumePlugin)
- if err != nil {
- t.Error(err)
- }
- err = volumetest.VerifyMountDeviceCallCount(
- 1 /* expectedMountDeviceCallCount */, testKubelet.volumePlugin)
- if err != nil {
- t.Error(err)
- }
- err = volumetest.VerifySetUpCallCount(
- 1 /* expectedSetUpCallCount */, testKubelet.volumePlugin)
- if err != nil {
- t.Error(err)
- }
- // Remove pod
- kubelet.podManager.SetPods([]*api.Pod{})
- err = waitForVolumeUnmount(kubelet.volumeManager, pod)
- if err != nil {
- t.Error(err)
- }
- // Verify volumes unmounted
- podVolumes = kubelet.volumeManager.GetMountedVolumesForPod(
- volumehelper.GetUniquePodName(pod))
- if len(podVolumes) != 0 {
- t.Errorf("Expected volumes to be unmounted and detached. But some volumes are still mounted: %#v", podVolumes)
- }
- err = volumetest.VerifyTearDownCallCount(
- 1 /* expectedTearDownCallCount */, testKubelet.volumePlugin)
- if err != nil {
- t.Error(err)
- }
- // Verify volumes detached and no longer reported as in use
- err = waitForVolumeDetach(api.UniqueVolumeName("fake/vol1"), kubelet.volumeManager)
- if err != nil {
- t.Error(err)
- }
- if testKubelet.volumePlugin.GetNewDetacherCallCount() < 1 {
- t.Errorf("Expected plugin NewDetacher to be called at least once")
- }
- err = volumetest.VerifyZeroDetachCallCount(testKubelet.volumePlugin)
- if err != nil {
- t.Error(err)
- }
- }
- type stubVolume struct {
- path string
- volume.MetricsNil
- }
- func (f *stubVolume) GetPath() string {
- return f.path
- }
- func (f *stubVolume) GetAttributes() volume.Attributes {
- return volume.Attributes{}
- }
- func (f *stubVolume) SetUp(fsGroup *int64) error {
- return nil
- }
- func (f *stubVolume) SetUpAt(dir string, fsGroup *int64) error {
- return nil
- }
- func TestMakeVolumeMounts(t *testing.T) {
- container := api.Container{
- VolumeMounts: []api.VolumeMount{
- {
- MountPath: "/etc/hosts",
- Name: "disk",
- ReadOnly: false,
- },
- {
- MountPath: "/mnt/path3",
- Name: "disk",
- ReadOnly: true,
- },
- {
- MountPath: "/mnt/path4",
- Name: "disk4",
- ReadOnly: false,
- },
- {
- MountPath: "/mnt/path5",
- Name: "disk5",
- ReadOnly: false,
- },
- },
- }
- podVolumes := kubecontainer.VolumeMap{
- "disk": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/mnt/disk"}},
- "disk4": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/mnt/host"}},
- "disk5": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/var/lib/kubelet/podID/volumes/empty/disk5"}},
- }
- pod := api.Pod{
- Spec: api.PodSpec{
- SecurityContext: &api.PodSecurityContext{
- HostNetwork: true,
- },
- },
- }
- mounts, _ := makeMounts(&pod, "/pod", &container, "fakepodname", "", "", podVolumes)
- expectedMounts := []kubecontainer.Mount{
- {
- Name: "disk",
- ContainerPath: "/etc/hosts",
- HostPath: "/mnt/disk",
- ReadOnly: false,
- SELinuxRelabel: false,
- },
- {
- Name: "disk",
- ContainerPath: "/mnt/path3",
- HostPath: "/mnt/disk",
- ReadOnly: true,
- SELinuxRelabel: false,
- },
- {
- Name: "disk4",
- ContainerPath: "/mnt/path4",
- HostPath: "/mnt/host",
- ReadOnly: false,
- SELinuxRelabel: false,
- },
- {
- Name: "disk5",
- ContainerPath: "/mnt/path5",
- HostPath: "/var/lib/kubelet/podID/volumes/empty/disk5",
- ReadOnly: false,
- SELinuxRelabel: false,
- },
- }
- if !reflect.DeepEqual(mounts, expectedMounts) {
- t.Errorf("Unexpected mounts: Expected %#v got %#v. Container was: %#v", expectedMounts, mounts, container)
- }
- }
- type fakeContainerCommandRunner struct {
- // what was passed in
- Cmd []string
- ID kubecontainer.ContainerID
- PodID types.UID
- E error
- Stdin io.Reader
- Stdout io.WriteCloser
- Stderr io.WriteCloser
- TTY bool
- Port uint16
- Stream io.ReadWriteCloser
- // what to return
- StdoutData string
- StderrData string
- }
- func (f *fakeContainerCommandRunner) ExecInContainer(id kubecontainer.ContainerID, cmd []string, in io.Reader, out, err io.WriteCloser, tty bool, resize <-chan term.Size) error {
- // record params
- f.Cmd = cmd
- f.ID = id
- f.Stdin = in
- f.Stdout = out
- f.Stderr = err
- f.TTY = tty
- // Copy stdout/stderr data
- fmt.Fprint(out, f.StdoutData)
- fmt.Fprint(out, f.StderrData)
- return f.E
- }
- func (f *fakeContainerCommandRunner) PortForward(pod *kubecontainer.Pod, port uint16, stream io.ReadWriteCloser) error {
- f.PodID = pod.ID
- f.Port = port
- f.Stream = stream
- return nil
- }
- func TestRunInContainerNoSuchPod(t *testing.T) {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- kubelet := testKubelet.kubelet
- fakeRuntime := testKubelet.fakeRuntime
- fakeRuntime.PodList = []*containertest.FakePod{}
- podName := "podFoo"
- podNamespace := "nsFoo"
- containerName := "containerFoo"
- output, err := kubelet.RunInContainer(
- kubecontainer.GetPodFullName(&api.Pod{ObjectMeta: api.ObjectMeta{Name: podName, Namespace: podNamespace}}),
- "",
- containerName,
- []string{"ls"})
- if output != nil {
- t.Errorf("unexpected non-nil command: %v", output)
- }
- if err == nil {
- t.Error("unexpected non-error")
- }
- }
- func TestRunInContainer(t *testing.T) {
- for _, testError := range []error{nil, errors.New("foo")} {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- kubelet := testKubelet.kubelet
- fakeRuntime := testKubelet.fakeRuntime
- fakeCommandRunner := fakeContainerCommandRunner{
- E: testError,
- StdoutData: "foo",
- StderrData: "bar",
- }
- kubelet.runner = &fakeCommandRunner
- containerID := kubecontainer.ContainerID{Type: "test", ID: "abc1234"}
- fakeRuntime.PodList = []*containertest.FakePod{
- {Pod: &kubecontainer.Pod{
- ID: "12345678",
- Name: "podFoo",
- Namespace: "nsFoo",
- Containers: []*kubecontainer.Container{
- {Name: "containerFoo",
- ID: containerID,
- },
- },
- }},
- }
- cmd := []string{"ls"}
- actualOutput, err := kubelet.RunInContainer("podFoo_nsFoo", "", "containerFoo", cmd)
- if fakeCommandRunner.ID != containerID {
- t.Errorf("(testError=%v) unexpected Name: %s", testError, fakeCommandRunner.ID)
- }
- if !reflect.DeepEqual(fakeCommandRunner.Cmd, cmd) {
- t.Errorf("(testError=%v) unexpected command: %s", testError, fakeCommandRunner.Cmd)
- }
- // this isn't 100% foolproof as a bug in a real ContainerCommandRunner where it fails to copy to stdout/stderr wouldn't be caught by this test
- if "foobar" != string(actualOutput) {
- t.Errorf("(testError=%v) unexpected output %q", testError, actualOutput)
- }
- if e, a := fmt.Sprintf("%v", testError), fmt.Sprintf("%v", err); e != a {
- t.Errorf("(testError=%v) error: expected %s, got %s", testError, e, a)
- }
- }
- }
- func TestDNSConfigurationParams(t *testing.T) {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- kubelet := testKubelet.kubelet
- clusterNS := "203.0.113.1"
- kubelet.clusterDomain = "kubernetes.io"
- kubelet.clusterDNS = net.ParseIP(clusterNS)
- pods := newTestPods(2)
- pods[0].Spec.DNSPolicy = api.DNSClusterFirst
- pods[1].Spec.DNSPolicy = api.DNSDefault
- options := make([]*kubecontainer.RunContainerOptions, 2)
- for i, pod := range pods {
- var err error
- options[i], err = kubelet.GenerateRunContainerOptions(pod, &api.Container{}, "")
- if err != nil {
- t.Fatalf("failed to generate container options: %v", err)
- }
- }
- if len(options[0].DNS) != 1 || options[0].DNS[0] != clusterNS {
- t.Errorf("expected nameserver %s, got %+v", clusterNS, options[0].DNS)
- }
- if len(options[0].DNSSearch) == 0 || options[0].DNSSearch[0] != ".svc."+kubelet.clusterDomain {
- t.Errorf("expected search %s, got %+v", ".svc."+kubelet.clusterDomain, options[0].DNSSearch)
- }
- if len(options[1].DNS) != 1 || options[1].DNS[0] != "127.0.0.1" {
- t.Errorf("expected nameserver 127.0.0.1, got %+v", options[1].DNS)
- }
- if len(options[1].DNSSearch) != 1 || options[1].DNSSearch[0] != "." {
- t.Errorf("expected search \".\", got %+v", options[1].DNSSearch)
- }
- kubelet.resolverConfig = "/etc/resolv.conf"
- for i, pod := range pods {
- var err error
- options[i], err = kubelet.GenerateRunContainerOptions(pod, &api.Container{}, "")
- if err != nil {
- t.Fatalf("failed to generate container options: %v", err)
- }
- }
- t.Logf("nameservers %+v", options[1].DNS)
- if len(options[0].DNS) != 1 {
- t.Errorf("expected cluster nameserver only, got %+v", options[0].DNS)
- } else if options[0].DNS[0] != clusterNS {
- t.Errorf("expected nameserver %s, got %v", clusterNS, options[0].DNS[0])
- }
- if len(options[0].DNSSearch) != len(options[1].DNSSearch)+3 {
- t.Errorf("expected prepend of cluster domain, got %+v", options[0].DNSSearch)
- } else if options[0].DNSSearch[0] != ".svc."+kubelet.clusterDomain {
- t.Errorf("expected domain %s, got %s", ".svc."+kubelet.clusterDomain, options[0].DNSSearch)
- }
- }
- type testServiceLister struct {
- services []api.Service
- }
- func (ls testServiceLister) List() (api.ServiceList, error) {
- return api.ServiceList{
- Items: ls.services,
- }, nil
- }
- type testNodeLister struct {
- nodes []api.Node
- }
- type testNodeInfo struct {
- nodes []api.Node
- }
- func (ls testNodeInfo) GetNodeInfo(id string) (*api.Node, error) {
- for _, node := range ls.nodes {
- if node.Name == id {
- return &node, nil
- }
- }
- return nil, fmt.Errorf("Node with name: %s does not exist", id)
- }
- func (ls testNodeLister) List() (api.NodeList, error) {
- return api.NodeList{
- Items: ls.nodes,
- }, nil
- }
- type envs []kubecontainer.EnvVar
- func (e envs) Len() int {
- return len(e)
- }
- func (e envs) Swap(i, j int) { e[i], e[j] = e[j], e[i] }
- func (e envs) Less(i, j int) bool { return e[i].Name < e[j].Name }
- func buildService(name, namespace, clusterIP, protocol string, port int) api.Service {
- return api.Service{
- ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespace},
- Spec: api.ServiceSpec{
- Ports: []api.ServicePort{{
- Protocol: api.Protocol(protocol),
- Port: int32(port),
- }},
- ClusterIP: clusterIP,
- },
- }
- }
- func TestMakeEnvironmentVariables(t *testing.T) {
- services := []api.Service{
- buildService("kubernetes", api.NamespaceDefault, "1.2.3.1", "TCP", 8081),
- buildService("test", "test1", "1.2.3.3", "TCP", 8083),
- buildService("kubernetes", "test2", "1.2.3.4", "TCP", 8084),
- buildService("test", "test2", "1.2.3.5", "TCP", 8085),
- buildService("test", "test2", "None", "TCP", 8085),
- buildService("test", "test2", "", "TCP", 8085),
- buildService("kubernetes", "kubernetes", "1.2.3.6", "TCP", 8086),
- buildService("not-special", "kubernetes", "1.2.3.8", "TCP", 8088),
- buildService("not-special", "kubernetes", "None", "TCP", 8088),
- buildService("not-special", "kubernetes", "", "TCP", 8088),
- }
- testCases := []struct {
- name string // the name of the test case
- ns string // the namespace to generate environment for
- container *api.Container // the container to use
- masterServiceNs string // the namespace to read master service info from
- nilLister bool // whether the lister should be nil
- expectedEnvs []kubecontainer.EnvVar // a set of expected environment vars
- }{
- {
- name: "api server = Y, kubelet = Y",
- ns: "test1",
- container: &api.Container{
- Env: []api.EnvVar{
- {Name: "FOO", Value: "BAR"},
- {Name: "TEST_SERVICE_HOST", Value: "1.2.3.3"},
- {Name: "TEST_SERVICE_PORT", Value: "8083"},
- {Name: "TEST_PORT", Value: "tcp://1.2.3.3:8083"},
- {Name: "TEST_PORT_8083_TCP", Value: "tcp://1.2.3.3:8083"},
- {Name: "TEST_PORT_8083_TCP_PROTO", Value: "tcp"},
- {Name: "TEST_PORT_8083_TCP_PORT", Value: "8083"},
- {Name: "TEST_PORT_8083_TCP_ADDR", Value: "1.2.3.3"},
- },
- },
- masterServiceNs: api.NamespaceDefault,
- nilLister: false,
- expectedEnvs: []kubecontainer.EnvVar{
- {Name: "FOO", Value: "BAR"},
- {Name: "TEST_SERVICE_HOST", Value: "1.2.3.3"},
- {Name: "TEST_SERVICE_PORT", Value: "8083"},
- {Name: "TEST_PORT", Value: "tcp://1.2.3.3:8083"},
- {Name: "TEST_PORT_8083_TCP", Value: "tcp://1.2.3.3:8083"},
- {Name: "TEST_PORT_8083_TCP_PROTO", Value: "tcp"},
- {Name: "TEST_PORT_8083_TCP_PORT", Value: "8083"},
- {Name: "TEST_PORT_8083_TCP_ADDR", Value: "1.2.3.3"},
- {Name: "KUBERNETES_SERVICE_PORT", Value: "8081"},
- {Name: "KUBERNETES_SERVICE_HOST", Value: "1.2.3.1"},
- {Name: "KUBERNETES_PORT", Value: "tcp://1.2.3.1:8081"},
- {Name: "KUBERNETES_PORT_8081_TCP", Value: "tcp://1.2.3.1:8081"},
- {Name: "KUBERNETES_PORT_8081_TCP_PROTO", Value: "tcp"},
- {Name: "KUBERNETES_PORT_8081_TCP_PORT", Value: "8081"},
- {Name: "KUBERNETES_PORT_8081_TCP_ADDR", Value: "1.2.3.1"},
- },
- },
- {
- name: "api server = Y, kubelet = N",
- ns: "test1",
- container: &api.Container{
- Env: []api.EnvVar{
- {Name: "FOO", Value: "BAR"},
- {Name: "TEST_SERVICE_HOST", Value: "1.2.3.3"},
- {Name: "TEST_SERVICE_PORT", Value: "8083"},
- {Name: "TEST_PORT", Value: "tcp://1.2.3.3:8083"},
- {Name: "TEST_PORT_8083_TCP", Value: "tcp://1.2.3.3:8083"},
- {Name: "TEST_PORT_8083_TCP_PROTO", Value: "tcp"},
- {Name: "TEST_PORT_8083_TCP_PORT", Value: "8083"},
- {Name: "TEST_PORT_8083_TCP_ADDR", Value: "1.2.3.3"},
- },
- },
- masterServiceNs: api.NamespaceDefault,
- nilLister: true,
- expectedEnvs: []kubecontainer.EnvVar{
- {Name: "FOO", Value: "BAR"},
- {Name: "TEST_SERVICE_HOST", Value: "1.2.3.3"},
- {Name: "TEST_SERVICE_PORT", Value: "8083"},
- {Name: "TEST_PORT", Value: "tcp://1.2.3.3:8083"},
- {Name: "TEST_PORT_8083_TCP", Value: "tcp://1.2.3.3:8083"},
- {Name: "TEST_PORT_8083_TCP_PROTO", Value: "tcp"},
- {Name: "TEST_PORT_8083_TCP_PORT", Value: "8083"},
- {Name: "TEST_PORT_8083_TCP_ADDR", Value: "1.2.3.3"},
- },
- },
- {
- name: "api server = N; kubelet = Y",
- ns: "test1",
- container: &api.Container{
- Env: []api.EnvVar{
- {Name: "FOO", Value: "BAZ"},
- },
- },
- masterServiceNs: api.NamespaceDefault,
- nilLister: false,
- expectedEnvs: []kubecontainer.EnvVar{
- {Name: "FOO", Value: "BAZ"},
- {Name: "TEST_SERVICE_HOST", Value: "1.2.3.3"},
- {Name: "TEST_SERVICE_PORT", Value: "8083"},
- {Name: "TEST_PORT", Value: "tcp://1.2.3.3:8083"},
- {Name: "TEST_PORT_8083_TCP", Value: "tcp://1.2.3.3:8083"},
- {Name: "TEST_PORT_8083_TCP_PROTO", Value: "tcp"},
- {Name: "TEST_PORT_8083_TCP_PORT", Value: "8083"},
- {Name: "TEST_PORT_8083_TCP_ADDR", Value: "1.2.3.3"},
- {Name: "KUBERNETES_SERVICE_HOST", Value: "1.2.3.1"},
- {Name: "KUBERNETES_SERVICE_PORT", Value: "8081"},
- {Name: "KUBERNETES_PORT", Value: "tcp://1.2.3.1:8081"},
- {Name: "KUBERNETES_PORT_8081_TCP", Value: "tcp://1.2.3.1:8081"},
- {Name: "KUBERNETES_PORT_8081_TCP_PROTO", Value: "tcp"},
- {Name: "KUBERNETES_PORT_8081_TCP_PORT", Value: "8081"},
- {Name: "KUBERNETES_PORT_8081_TCP_ADDR", Value: "1.2.3.1"},
- },
- },
- {
- name: "master service in pod ns",
- ns: "test2",
- container: &api.Container{
- Env: []api.EnvVar{
- {Name: "FOO", Value: "ZAP"},
- },
- },
- masterServiceNs: "kubernetes",
- nilLister: false,
- expectedEnvs: []kubecontainer.EnvVar{
- {Name: "FOO", Value: "ZAP"},
- {Name: "TEST_SERVICE_HOST", Value: "1.2.3.5"},
- {Name: "TEST_SERVICE_PORT", Value: "8085"},
- {Name: "TEST_PORT", Value: "tcp://1.2.3.5:8085"},
- {Name: "TEST_PORT_8085_TCP", Value: "tcp://1.2.3.5:8085"},
- {Name: "TEST_PORT_8085_TCP_PROTO", Value: "tcp"},
- {Name: "TEST_PORT_8085_TCP_PORT", Value: "8085"},
- {Name: "TEST_PORT_8085_TCP_ADDR", Value: "1.2.3.5"},
- {Name: "KUBERNETES_SERVICE_HOST", Value: "1.2.3.4"},
- {Name: "KUBERNETES_SERVICE_PORT", Value: "8084"},
- {Name: "KUBERNETES_PORT", Value: "tcp://1.2.3.4:8084"},
- {Name: "KUBERNETES_PORT_8084_TCP", Value: "tcp://1.2.3.4:8084"},
- {Name: "KUBERNETES_PORT_8084_TCP_PROTO", Value: "tcp"},
- {Name: "KUBERNETES_PORT_8084_TCP_PORT", Value: "8084"},
- {Name: "KUBERNETES_PORT_8084_TCP_ADDR", Value: "1.2.3.4"},
- },
- },
- {
- name: "pod in master service ns",
- ns: "kubernetes",
- container: &api.Container{},
- masterServiceNs: "kubernetes",
- nilLister: false,
- expectedEnvs: []kubecontainer.EnvVar{
- {Name: "NOT_SPECIAL_SERVICE_HOST", Value: "1.2.3.8"},
- {Name: "NOT_SPECIAL_SERVICE_PORT", Value: "8088"},
- {Name: "NOT_SPECIAL_PORT", Value: "tcp://1.2.3.8:8088"},
- {Name: "NOT_SPECIAL_PORT_8088_TCP", Value: "tcp://1.2.3.8:8088"},
- {Name: "NOT_SPECIAL_PORT_8088_TCP_PROTO", Value: "tcp"},
- {Name: "NOT_SPECIAL_PORT_8088_TCP_PORT", Value: "8088"},
- {Name: "NOT_SPECIAL_PORT_8088_TCP_ADDR", Value: "1.2.3.8"},
- {Name: "KUBERNETES_SERVICE_HOST", Value: "1.2.3.6"},
- {Name: "KUBERNETES_SERVICE_PORT", Value: "8086"},
- {Name: "KUBERNETES_PORT", Value: "tcp://1.2.3.6:8086"},
- {Name: "KUBERNETES_PORT_8086_TCP", Value: "tcp://1.2.3.6:8086"},
- {Name: "KUBERNETES_PORT_8086_TCP_PROTO", Value: "tcp"},
- {Name: "KUBERNETES_PORT_8086_TCP_PORT", Value: "8086"},
- {Name: "KUBERNETES_PORT_8086_TCP_ADDR", Value: "1.2.3.6"},
- },
- },
- {
- name: "downward api pod",
- ns: "downward-api",
- container: &api.Container{
- Env: []api.EnvVar{
- {
- Name: "POD_NAME",
- ValueFrom: &api.EnvVarSource{
- FieldRef: &api.ObjectFieldSelector{
- APIVersion: testapi.Default.GroupVersion().String(),
- FieldPath: "metadata.name",
- },
- },
- },
- {
- Name: "POD_NAMESPACE",
- ValueFrom: &api.EnvVarSource{
- FieldRef: &api.ObjectFieldSelector{
- APIVersion: testapi.Default.GroupVersion().String(),
- FieldPath: "metadata.namespace",
- },
- },
- },
- {
- Name: "POD_NODE_NAME",
- ValueFrom: &api.EnvVarSource{
- FieldRef: &api.ObjectFieldSelector{
- APIVersion: testapi.Default.GroupVersion().String(),
- FieldPath: "spec.nodeName",
- },
- },
- },
- {
- Name: "POD_SERVICE_ACCOUNT_NAME",
- ValueFrom: &api.EnvVarSource{
- FieldRef: &api.ObjectFieldSelector{
- APIVersion: testapi.Default.GroupVersion().String(),
- FieldPath: "spec.serviceAccountName",
- },
- },
- },
- {
- Name: "POD_IP",
- ValueFrom: &api.EnvVarSource{
- FieldRef: &api.ObjectFieldSelector{
- APIVersion: testapi.Default.GroupVersion().String(),
- FieldPath: "status.podIP",
- },
- },
- },
- },
- },
- masterServiceNs: "nothing",
- nilLister: true,
- expectedEnvs: []kubecontainer.EnvVar{
- {Name: "POD_NAME", Value: "dapi-test-pod-name"},
- {Name: "POD_NAMESPACE", Value: "downward-api"},
- {Name: "POD_NODE_NAME", Value: "node-name"},
- {Name: "POD_SERVICE_ACCOUNT_NAME", Value: "special"},
- {Name: "POD_IP", Value: "1.2.3.4"},
- },
- },
- {
- name: "env expansion",
- ns: "test1",
- container: &api.Container{
- Env: []api.EnvVar{
- {
- Name: "TEST_LITERAL",
- Value: "test-test-test",
- },
- {
- Name: "POD_NAME",
- ValueFrom: &api.EnvVarSource{
- FieldRef: &api.ObjectFieldSelector{
- APIVersion: testapi.Default.GroupVersion().String(),
- FieldPath: "metadata.name",
- },
- },
- },
- {
- Name: "OUT_OF_ORDER_TEST",
- Value: "$(OUT_OF_ORDER_TARGET)",
- },
- {
- Name: "OUT_OF_ORDER_TARGET",
- Value: "FOO",
- },
- {
- Name: "EMPTY_VAR",
- },
- {
- Name: "EMPTY_TEST",
- Value: "foo-$(EMPTY_VAR)",
- },
- {
- Name: "POD_NAME_TEST2",
- Value: "test2-$(POD_NAME)",
- },
- {
- Name: "POD_NAME_TEST3",
- Value: "$(POD_NAME_TEST2)-3",
- },
- {
- Name: "LITERAL_TEST",
- Value: "literal-$(TEST_LITERAL)",
- },
- {
- Name: "SERVICE_VAR_TEST",
- Value: "$(TEST_SERVICE_HOST):$(TEST_SERVICE_PORT)",
- },
- {
- Name: "TEST_UNDEFINED",
- Value: "$(UNDEFINED_VAR)",
- },
- },
- },
- masterServiceNs: "nothing",
- nilLister: false,
- expectedEnvs: []kubecontainer.EnvVar{
- {
- Name: "TEST_LITERAL",
- Value: "test-test-test",
- },
- {
- Name: "POD_NAME",
- Value: "dapi-test-pod-name",
- },
- {
- Name: "POD_NAME_TEST2",
- Value: "test2-dapi-test-pod-name",
- },
- {
- Name: "POD_NAME_TEST3",
- Value: "test2-dapi-test-pod-name-3",
- },
- {
- Name: "LITERAL_TEST",
- Value: "literal-test-test-test",
- },
- {
- Name: "TEST_SERVICE_HOST",
- Value: "1.2.3.3",
- },
- {
- Name: "TEST_SERVICE_PORT",
- Value: "8083",
- },
- {
- Name: "TEST_PORT",
- Value: "tcp://1.2.3.3:8083",
- },
- {
- Name: "TEST_PORT_8083_TCP",
- Value: "tcp://1.2.3.3:8083",
- },
- {
- Name: "TEST_PORT_8083_TCP_PROTO",
- Value: "tcp",
- },
- {
- Name: "TEST_PORT_8083_TCP_PORT",
- Value: "8083",
- },
- {
- Name: "TEST_PORT_8083_TCP_ADDR",
- Value: "1.2.3.3",
- },
- {
- Name: "SERVICE_VAR_TEST",
- Value: "1.2.3.3:8083",
- },
- {
- Name: "OUT_OF_ORDER_TEST",
- Value: "$(OUT_OF_ORDER_TARGET)",
- },
- {
- Name: "OUT_OF_ORDER_TARGET",
- Value: "FOO",
- },
- {
- Name: "TEST_UNDEFINED",
- Value: "$(UNDEFINED_VAR)",
- },
- {
- Name: "EMPTY_VAR",
- },
- {
- Name: "EMPTY_TEST",
- Value: "foo-",
- },
- },
- },
- }
- for i, tc := range testCases {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- kl := testKubelet.kubelet
- kl.masterServiceNamespace = tc.masterServiceNs
- if tc.nilLister {
- kl.serviceLister = nil
- } else {
- kl.serviceLister = testServiceLister{services}
- }
- testPod := &api.Pod{
- ObjectMeta: api.ObjectMeta{
- Namespace: tc.ns,
- Name: "dapi-test-pod-name",
- },
- Spec: api.PodSpec{
- ServiceAccountName: "special",
- NodeName: "node-name",
- },
- }
- podIP := "1.2.3.4"
- result, err := kl.makeEnvironmentVariables(testPod, tc.container, podIP)
- if err != nil {
- t.Errorf("[%v] Unexpected error: %v", tc.name, err)
- }
- sort.Sort(envs(result))
- sort.Sort(envs(tc.expectedEnvs))
- if !reflect.DeepEqual(result, tc.expectedEnvs) {
- t.Errorf("%d: [%v] Unexpected env entries; expected {%v}, got {%v}", i, tc.name, tc.expectedEnvs, result)
- }
- }
- }
- func waitingState(cName string) api.ContainerStatus {
- return api.ContainerStatus{
- Name: cName,
- State: api.ContainerState{
- Waiting: &api.ContainerStateWaiting{},
- },
- }
- }
- func waitingStateWithLastTermination(cName string) api.ContainerStatus {
- return api.ContainerStatus{
- Name: cName,
- State: api.ContainerState{
- Waiting: &api.ContainerStateWaiting{},
- },
- LastTerminationState: api.ContainerState{
- Terminated: &api.ContainerStateTerminated{
- ExitCode: 0,
- },
- },
- }
- }
- func runningState(cName string) api.ContainerStatus {
- return api.ContainerStatus{
- Name: cName,
- State: api.ContainerState{
- Running: &api.ContainerStateRunning{},
- },
- }
- }
- func stoppedState(cName string) api.ContainerStatus {
- return api.ContainerStatus{
- Name: cName,
- State: api.ContainerState{
- Terminated: &api.ContainerStateTerminated{},
- },
- }
- }
- func succeededState(cName string) api.ContainerStatus {
- return api.ContainerStatus{
- Name: cName,
- State: api.ContainerState{
- Terminated: &api.ContainerStateTerminated{
- ExitCode: 0,
- },
- },
- }
- }
- func failedState(cName string) api.ContainerStatus {
- return api.ContainerStatus{
- Name: cName,
- State: api.ContainerState{
- Terminated: &api.ContainerStateTerminated{
- ExitCode: -1,
- },
- },
- }
- }
- func TestPodPhaseWithRestartAlways(t *testing.T) {
- desiredState := api.PodSpec{
- NodeName: "machine",
- Containers: []api.Container{
- {Name: "containerA"},
- {Name: "containerB"},
- },
- RestartPolicy: api.RestartPolicyAlways,
- }
- tests := []struct {
- pod *api.Pod
- status api.PodPhase
- test string
- }{
- {&api.Pod{Spec: desiredState, Status: api.PodStatus{}}, api.PodPending, "waiting"},
- {
- &api.Pod{
- Spec: desiredState,
- Status: api.PodStatus{
- ContainerStatuses: []api.ContainerStatus{
- runningState("containerA"),
- runningState("containerB"),
- },
- },
- },
- api.PodRunning,
- "all running",
- },
- {
- &api.Pod{
- Spec: desiredState,
- Status: api.PodStatus{
- ContainerStatuses: []api.ContainerStatus{
- stoppedState("containerA"),
- stoppedState("containerB"),
- },
- },
- },
- api.PodRunning,
- "all stopped with restart always",
- },
- {
- &api.Pod{
- Spec: desiredState,
- Status: api.PodStatus{
- ContainerStatuses: []api.ContainerStatus{
- runningState("containerA"),
- stoppedState("containerB"),
- },
- },
- },
- api.PodRunning,
- "mixed state #1 with restart always",
- },
- {
- &api.Pod{
- Spec: desiredState,
- Status: api.PodStatus{
- ContainerStatuses: []api.ContainerStatus{
- runningState("containerA"),
- },
- },
- },
- api.PodPending,
- "mixed state #2 with restart always",
- },
- {
- &api.Pod{
- Spec: desiredState,
- Status: api.PodStatus{
- ContainerStatuses: []api.ContainerStatus{
- runningState("containerA"),
- waitingState("containerB"),
- },
- },
- },
- api.PodPending,
- "mixed state #3 with restart always",
- },
- {
- &api.Pod{
- Spec: desiredState,
- Status: api.PodStatus{
- ContainerStatuses: []api.ContainerStatus{
- runningState("containerA"),
- waitingStateWithLastTermination("containerB"),
- },
- },
- },
- api.PodRunning,
- "backoff crashloop container with restart always",
- },
- }
- for _, test := range tests {
- if status := GetPhase(&test.pod.Spec, test.pod.Status.ContainerStatuses); status != test.status {
- t.Errorf("In test %s, expected %v, got %v", test.test, test.status, status)
- }
- }
- }
- func TestPodPhaseWithRestartNever(t *testing.T) {
- desiredState := api.PodSpec{
- NodeName: "machine",
- Containers: []api.Container{
- {Name: "containerA"},
- {Name: "containerB"},
- },
- RestartPolicy: api.RestartPolicyNever,
- }
- tests := []struct {
- pod *api.Pod
- status api.PodPhase
- test string
- }{
- {&api.Pod{Spec: desiredState, Status: api.PodStatus{}}, api.PodPending, "waiting"},
- {
- &api.Pod{
- Spec: desiredState,
- Status: api.PodStatus{
- ContainerStatuses: []api.ContainerStatus{
- runningState("containerA"),
- runningState("containerB"),
- },
- },
- },
- api.PodRunning,
- "all running with restart never",
- },
- {
- &api.Pod{
- Spec: desiredState,
- Status: api.PodStatus{
- ContainerStatuses: []api.ContainerStatus{
- succeededState("containerA"),
- succeededState("containerB"),
- },
- },
- },
- api.PodSucceeded,
- "all succeeded with restart never",
- },
- {
- &api.Pod{
- Spec: desiredState,
- Status: api.PodStatus{
- ContainerStatuses: []api.ContainerStatus{
- failedState("containerA"),
- failedState("containerB"),
- },
- },
- },
- api.PodFailed,
- "all failed with restart never",
- },
- {
- &api.Pod{
- Spec: desiredState,
- Status: api.PodStatus{
- ContainerStatuses: []api.ContainerStatus{
- runningState("containerA"),
- succeededState("containerB"),
- },
- },
- },
- api.PodRunning,
- "mixed state #1 with restart never",
- },
- {
- &api.Pod{
- Spec: desiredState,
- Status: api.PodStatus{
- ContainerStatuses: []api.ContainerStatus{
- runningState("containerA"),
- },
- },
- },
- api.PodPending,
- "mixed state #2 with restart never",
- },
- {
- &api.Pod{
- Spec: desiredState,
- Status: api.PodStatus{
- ContainerStatuses: []api.ContainerStatus{
- runningState("containerA"),
- waitingState("containerB"),
- },
- },
- },
- api.PodPending,
- "mixed state #3 with restart never",
- },
- }
- for _, test := range tests {
- if status := GetPhase(&test.pod.Spec, test.pod.Status.ContainerStatuses); status != test.status {
- t.Errorf("In test %s, expected %v, got %v", test.test, test.status, status)
- }
- }
- }
- func TestPodPhaseWithRestartOnFailure(t *testing.T) {
- desiredState := api.PodSpec{
- NodeName: "machine",
- Containers: []api.Container{
- {Name: "containerA"},
- {Name: "containerB"},
- },
- RestartPolicy: api.RestartPolicyOnFailure,
- }
- tests := []struct {
- pod *api.Pod
- status api.PodPhase
- test string
- }{
- {&api.Pod{Spec: desiredState, Status: api.PodStatus{}}, api.PodPending, "waiting"},
- {
- &api.Pod{
- Spec: desiredState,
- Status: api.PodStatus{
- ContainerStatuses: []api.ContainerStatus{
- runningState("containerA"),
- runningState("containerB"),
- },
- },
- },
- api.PodRunning,
- "all running with restart onfailure",
- },
- {
- &api.Pod{
- Spec: desiredState,
- Status: api.PodStatus{
- ContainerStatuses: []api.ContainerStatus{
- succeededState("containerA"),
- succeededState("containerB"),
- },
- },
- },
- api.PodSucceeded,
- "all succeeded with restart onfailure",
- },
- {
- &api.Pod{
- Spec: desiredState,
- Status: api.PodStatus{
- ContainerStatuses: []api.ContainerStatus{
- failedState("containerA"),
- failedState("containerB"),
- },
- },
- },
- api.PodRunning,
- "all failed with restart never",
- },
- {
- &api.Pod{
- Spec: desiredState,
- Status: api.PodStatus{
- ContainerStatuses: []api.ContainerStatus{
- runningState("containerA"),
- succeededState("containerB"),
- },
- },
- },
- api.PodRunning,
- "mixed state #1 with restart onfailure",
- },
- {
- &api.Pod{
- Spec: desiredState,
- Status: api.PodStatus{
- ContainerStatuses: []api.ContainerStatus{
- runningState("containerA"),
- },
- },
- },
- api.PodPending,
- "mixed state #2 with restart onfailure",
- },
- {
- &api.Pod{
- Spec: desiredState,
- Status: api.PodStatus{
- ContainerStatuses: []api.ContainerStatus{
- runningState("containerA"),
- waitingState("containerB"),
- },
- },
- },
- api.PodPending,
- "mixed state #3 with restart onfailure",
- },
- {
- &api.Pod{
- Spec: desiredState,
- Status: api.PodStatus{
- ContainerStatuses: []api.ContainerStatus{
- runningState("containerA"),
- waitingStateWithLastTermination("containerB"),
- },
- },
- },
- api.PodRunning,
- "backoff crashloop container with restart onfailure",
- },
- }
- for _, test := range tests {
- if status := GetPhase(&test.pod.Spec, test.pod.Status.ContainerStatuses); status != test.status {
- t.Errorf("In test %s, expected %v, got %v", test.test, test.status, status)
- }
- }
- }
- func TestExecInContainerNoSuchPod(t *testing.T) {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- kubelet := testKubelet.kubelet
- fakeRuntime := testKubelet.fakeRuntime
- fakeCommandRunner := fakeContainerCommandRunner{}
- kubelet.runner = &fakeCommandRunner
- fakeRuntime.PodList = []*containertest.FakePod{}
- podName := "podFoo"
- podNamespace := "nsFoo"
- containerID := "containerFoo"
- err := kubelet.ExecInContainer(
- kubecontainer.GetPodFullName(&api.Pod{ObjectMeta: api.ObjectMeta{Name: podName, Namespace: podNamespace}}),
- "",
- containerID,
- []string{"ls"},
- nil,
- nil,
- nil,
- false,
- nil,
- )
- if err == nil {
- t.Fatal("unexpected non-error")
- }
- if !fakeCommandRunner.ID.IsEmpty() {
- t.Fatal("unexpected invocation of runner.ExecInContainer")
- }
- }
- func TestExecInContainerNoSuchContainer(t *testing.T) {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- kubelet := testKubelet.kubelet
- fakeRuntime := testKubelet.fakeRuntime
- fakeCommandRunner := fakeContainerCommandRunner{}
- kubelet.runner = &fakeCommandRunner
- podName := "podFoo"
- podNamespace := "nsFoo"
- containerID := "containerFoo"
- fakeRuntime.PodList = []*containertest.FakePod{
- {Pod: &kubecontainer.Pod{
- ID: "12345678",
- Name: podName,
- Namespace: podNamespace,
- Containers: []*kubecontainer.Container{
- {Name: "bar",
- ID: kubecontainer.ContainerID{Type: "test", ID: "barID"}},
- },
- }},
- }
- err := kubelet.ExecInContainer(
- kubecontainer.GetPodFullName(&api.Pod{ObjectMeta: api.ObjectMeta{
- UID: "12345678",
- Name: podName,
- Namespace: podNamespace,
- }}),
- "",
- containerID,
- []string{"ls"},
- nil,
- nil,
- nil,
- false,
- nil,
- )
- if err == nil {
- t.Fatal("unexpected non-error")
- }
- if !fakeCommandRunner.ID.IsEmpty() {
- t.Fatal("unexpected invocation of runner.ExecInContainer")
- }
- }
- type fakeReadWriteCloser struct{}
- func (f *fakeReadWriteCloser) Write(data []byte) (int, error) {
- return 0, nil
- }
- func (f *fakeReadWriteCloser) Read(data []byte) (int, error) {
- return 0, nil
- }
- func (f *fakeReadWriteCloser) Close() error {
- return nil
- }
- func TestExecInContainer(t *testing.T) {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- kubelet := testKubelet.kubelet
- fakeRuntime := testKubelet.fakeRuntime
- fakeCommandRunner := fakeContainerCommandRunner{}
- kubelet.runner = &fakeCommandRunner
- podName := "podFoo"
- podNamespace := "nsFoo"
- containerID := "containerFoo"
- command := []string{"ls"}
- stdin := &bytes.Buffer{}
- stdout := &fakeReadWriteCloser{}
- stderr := &fakeReadWriteCloser{}
- tty := true
- fakeRuntime.PodList = []*containertest.FakePod{
- {Pod: &kubecontainer.Pod{
- ID: "12345678",
- Name: podName,
- Namespace: podNamespace,
- Containers: []*kubecontainer.Container{
- {Name: containerID,
- ID: kubecontainer.ContainerID{Type: "test", ID: containerID},
- },
- },
- }},
- }
- err := kubelet.ExecInContainer(
- kubecontainer.GetPodFullName(podWithUidNameNs("12345678", podName, podNamespace)),
- "",
- containerID,
- []string{"ls"},
- stdin,
- stdout,
- stderr,
- tty,
- nil,
- )
- if err != nil {
- t.Fatalf("unexpected error: %s", err)
- }
- if e, a := containerID, fakeCommandRunner.ID.ID; e != a {
- t.Fatalf("container name: expected %q, got %q", e, a)
- }
- if e, a := command, fakeCommandRunner.Cmd; !reflect.DeepEqual(e, a) {
- t.Fatalf("command: expected '%v', got '%v'", e, a)
- }
- if e, a := stdin, fakeCommandRunner.Stdin; e != a {
- t.Fatalf("stdin: expected %#v, got %#v", e, a)
- }
- if e, a := stdout, fakeCommandRunner.Stdout; e != a {
- t.Fatalf("stdout: expected %#v, got %#v", e, a)
- }
- if e, a := stderr, fakeCommandRunner.Stderr; e != a {
- t.Fatalf("stderr: expected %#v, got %#v", e, a)
- }
- if e, a := tty, fakeCommandRunner.TTY; e != a {
- t.Fatalf("tty: expected %t, got %t", e, a)
- }
- }
- func TestPortForwardNoSuchPod(t *testing.T) {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- kubelet := testKubelet.kubelet
- fakeRuntime := testKubelet.fakeRuntime
- fakeRuntime.PodList = []*containertest.FakePod{}
- fakeCommandRunner := fakeContainerCommandRunner{}
- kubelet.runner = &fakeCommandRunner
- podName := "podFoo"
- podNamespace := "nsFoo"
- var port uint16 = 5000
- err := kubelet.PortForward(
- kubecontainer.GetPodFullName(&api.Pod{ObjectMeta: api.ObjectMeta{Name: podName, Namespace: podNamespace}}),
- "",
- port,
- nil,
- )
- if err == nil {
- t.Fatal("unexpected non-error")
- }
- if !fakeCommandRunner.ID.IsEmpty() {
- t.Fatal("unexpected invocation of runner.PortForward")
- }
- }
- func TestPortForward(t *testing.T) {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- kubelet := testKubelet.kubelet
- fakeRuntime := testKubelet.fakeRuntime
- podName := "podFoo"
- podNamespace := "nsFoo"
- podID := types.UID("12345678")
- fakeRuntime.PodList = []*containertest.FakePod{
- {Pod: &kubecontainer.Pod{
- ID: podID,
- Name: podName,
- Namespace: podNamespace,
- Containers: []*kubecontainer.Container{
- {
- Name: "foo",
- ID: kubecontainer.ContainerID{Type: "test", ID: "containerFoo"},
- },
- },
- }},
- }
- fakeCommandRunner := fakeContainerCommandRunner{}
- kubelet.runner = &fakeCommandRunner
- var port uint16 = 5000
- stream := &fakeReadWriteCloser{}
- err := kubelet.PortForward(
- kubecontainer.GetPodFullName(&api.Pod{ObjectMeta: api.ObjectMeta{
- UID: "12345678",
- Name: podName,
- Namespace: podNamespace,
- }}),
- "",
- port,
- stream,
- )
- if err != nil {
- t.Fatalf("unexpected error: %s", err)
- }
- if e, a := podID, fakeCommandRunner.PodID; e != a {
- t.Fatalf("container id: expected %q, got %q", e, a)
- }
- if e, a := port, fakeCommandRunner.Port; e != a {
- t.Fatalf("port: expected %v, got %v", e, a)
- }
- if e, a := stream, fakeCommandRunner.Stream; e != a {
- t.Fatalf("stream: expected %v, got %v", e, a)
- }
- }
- // Tests that identify the host port conflicts are detected correctly.
- func TestGetHostPortConflicts(t *testing.T) {
- pods := []*api.Pod{
- {Spec: api.PodSpec{Containers: []api.Container{{Ports: []api.ContainerPort{{HostPort: 80}}}}}},
- {Spec: api.PodSpec{Containers: []api.Container{{Ports: []api.ContainerPort{{HostPort: 81}}}}}},
- {Spec: api.PodSpec{Containers: []api.Container{{Ports: []api.ContainerPort{{HostPort: 82}}}}}},
- {Spec: api.PodSpec{Containers: []api.Container{{Ports: []api.ContainerPort{{HostPort: 83}}}}}},
- }
- // Pods should not cause any conflict.
- if hasHostPortConflicts(pods) {
- t.Errorf("expected no conflicts, Got conflicts")
- }
- expected := &api.Pod{
- Spec: api.PodSpec{Containers: []api.Container{{Ports: []api.ContainerPort{{HostPort: 81}}}}},
- }
- // The new pod should cause conflict and be reported.
- pods = append(pods, expected)
- if !hasHostPortConflicts(pods) {
- t.Errorf("expected conflict, Got no conflicts")
- }
- }
- // Tests that we handle port conflicts correctly by setting the failed status in status map.
- func TestHandlePortConflicts(t *testing.T) {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- kl := testKubelet.kubelet
- testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
- testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- kl.nodeLister = testNodeLister{nodes: []api.Node{
- {
- ObjectMeta: api.ObjectMeta{Name: kl.nodeName},
- Status: api.NodeStatus{
- Allocatable: api.ResourceList{
- api.ResourcePods: *resource.NewQuantity(110, resource.DecimalSI),
- },
- },
- },
- }}
- kl.nodeInfo = testNodeInfo{nodes: []api.Node{
- {
- ObjectMeta: api.ObjectMeta{Name: kl.nodeName},
- Status: api.NodeStatus{
- Allocatable: api.ResourceList{
- api.ResourcePods: *resource.NewQuantity(110, resource.DecimalSI),
- },
- },
- },
- }}
- spec := api.PodSpec{NodeName: kl.nodeName, Containers: []api.Container{{Ports: []api.ContainerPort{{HostPort: 80}}}}}
- pods := []*api.Pod{
- podWithUidNameNsSpec("123456789", "newpod", "foo", spec),
- podWithUidNameNsSpec("987654321", "oldpod", "foo", spec),
- }
- // Make sure the Pods are in the reverse order of creation time.
- pods[1].CreationTimestamp = unversioned.NewTime(time.Now())
- pods[0].CreationTimestamp = unversioned.NewTime(time.Now().Add(1 * time.Second))
- // The newer pod should be rejected.
- notfittingPod := pods[0]
- fittingPod := pods[1]
- kl.HandlePodAdditions(pods)
- // Check pod status stored in the status map.
- // notfittingPod should be Failed
- status, found := kl.statusManager.GetPodStatus(notfittingPod.UID)
- if !found {
- t.Fatalf("status of pod %q is not found in the status map", notfittingPod.UID)
- }
- if status.Phase != api.PodFailed {
- t.Fatalf("expected pod status %q. Got %q.", api.PodFailed, status.Phase)
- }
- // fittingPod should be Pending
- status, found = kl.statusManager.GetPodStatus(fittingPod.UID)
- if !found {
- t.Fatalf("status of pod %q is not found in the status map", fittingPod.UID)
- }
- if status.Phase != api.PodPending {
- t.Fatalf("expected pod status %q. Got %q.", api.PodPending, status.Phase)
- }
- }
- // Tests that we handle host name conflicts correctly by setting the failed status in status map.
- func TestHandleHostNameConflicts(t *testing.T) {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- kl := testKubelet.kubelet
- testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
- testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- kl.nodeLister = testNodeLister{nodes: []api.Node{
- {
- ObjectMeta: api.ObjectMeta{Name: "127.0.0.1"},
- Status: api.NodeStatus{
- Allocatable: api.ResourceList{
- api.ResourcePods: *resource.NewQuantity(110, resource.DecimalSI),
- },
- },
- },
- }}
- kl.nodeInfo = testNodeInfo{nodes: []api.Node{
- {
- ObjectMeta: api.ObjectMeta{Name: "127.0.0.1"},
- Status: api.NodeStatus{
- Allocatable: api.ResourceList{
- api.ResourcePods: *resource.NewQuantity(110, resource.DecimalSI),
- },
- },
- },
- }}
- // default NodeName in test is 127.0.0.1
- pods := []*api.Pod{
- podWithUidNameNsSpec("123456789", "notfittingpod", "foo", api.PodSpec{NodeName: "127.0.0.2"}),
- podWithUidNameNsSpec("987654321", "fittingpod", "foo", api.PodSpec{NodeName: "127.0.0.1"}),
- }
- notfittingPod := pods[0]
- fittingPod := pods[1]
- kl.HandlePodAdditions(pods)
- // Check pod status stored in the status map.
- // notfittingPod should be Failed
- status, found := kl.statusManager.GetPodStatus(notfittingPod.UID)
- if !found {
- t.Fatalf("status of pod %q is not found in the status map", notfittingPod.UID)
- }
- if status.Phase != api.PodFailed {
- t.Fatalf("expected pod status %q. Got %q.", api.PodFailed, status.Phase)
- }
- // fittingPod should be Pending
- status, found = kl.statusManager.GetPodStatus(fittingPod.UID)
- if !found {
- t.Fatalf("status of pod %q is not found in the status map", fittingPod.UID)
- }
- if status.Phase != api.PodPending {
- t.Fatalf("expected pod status %q. Got %q.", api.PodPending, status.Phase)
- }
- }
- // Tests that we handle not matching labels selector correctly by setting the failed status in status map.
- func TestHandleNodeSelector(t *testing.T) {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- kl := testKubelet.kubelet
- nodes := []api.Node{
- {
- ObjectMeta: api.ObjectMeta{Name: testKubeletHostname, Labels: map[string]string{"key": "B"}},
- Status: api.NodeStatus{
- Allocatable: api.ResourceList{
- api.ResourcePods: *resource.NewQuantity(110, resource.DecimalSI),
- },
- },
- },
- }
- kl.nodeLister = testNodeLister{nodes: nodes}
- kl.nodeInfo = testNodeInfo{nodes: nodes}
- testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
- testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- pods := []*api.Pod{
- podWithUidNameNsSpec("123456789", "podA", "foo", api.PodSpec{NodeSelector: map[string]string{"key": "A"}}),
- podWithUidNameNsSpec("987654321", "podB", "foo", api.PodSpec{NodeSelector: map[string]string{"key": "B"}}),
- }
- // The first pod should be rejected.
- notfittingPod := pods[0]
- fittingPod := pods[1]
- kl.HandlePodAdditions(pods)
- // Check pod status stored in the status map.
- // notfittingPod should be Failed
- status, found := kl.statusManager.GetPodStatus(notfittingPod.UID)
- if !found {
- t.Fatalf("status of pod %q is not found in the status map", notfittingPod.UID)
- }
- if status.Phase != api.PodFailed {
- t.Fatalf("expected pod status %q. Got %q.", api.PodFailed, status.Phase)
- }
- // fittingPod should be Pending
- status, found = kl.statusManager.GetPodStatus(fittingPod.UID)
- if !found {
- t.Fatalf("status of pod %q is not found in the status map", fittingPod.UID)
- }
- if status.Phase != api.PodPending {
- t.Fatalf("expected pod status %q. Got %q.", api.PodPending, status.Phase)
- }
- }
- // Tests that we handle exceeded resources correctly by setting the failed status in status map.
- func TestHandleMemExceeded(t *testing.T) {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- kl := testKubelet.kubelet
- nodes := []api.Node{
- {ObjectMeta: api.ObjectMeta{Name: testKubeletHostname},
- Status: api.NodeStatus{Capacity: api.ResourceList{}, Allocatable: api.ResourceList{
- api.ResourceCPU: *resource.NewMilliQuantity(10, resource.DecimalSI),
- api.ResourceMemory: *resource.NewQuantity(100, resource.BinarySI),
- api.ResourcePods: *resource.NewQuantity(40, resource.DecimalSI),
- }}},
- }
- kl.nodeLister = testNodeLister{nodes: nodes}
- kl.nodeInfo = testNodeInfo{nodes: nodes}
- testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
- testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- spec := api.PodSpec{NodeName: kl.nodeName,
- Containers: []api.Container{{Resources: api.ResourceRequirements{
- Requests: api.ResourceList{
- "memory": resource.MustParse("90"),
- },
- }}}}
- pods := []*api.Pod{
- podWithUidNameNsSpec("123456789", "newpod", "foo", spec),
- podWithUidNameNsSpec("987654321", "oldpod", "foo", spec),
- }
- // Make sure the Pods are in the reverse order of creation time.
- pods[1].CreationTimestamp = unversioned.NewTime(time.Now())
- pods[0].CreationTimestamp = unversioned.NewTime(time.Now().Add(1 * time.Second))
- // The newer pod should be rejected.
- notfittingPod := pods[0]
- fittingPod := pods[1]
- kl.HandlePodAdditions(pods)
- // Check pod status stored in the status map.
- // notfittingPod should be Failed
- status, found := kl.statusManager.GetPodStatus(notfittingPod.UID)
- if !found {
- t.Fatalf("status of pod %q is not found in the status map", notfittingPod.UID)
- }
- if status.Phase != api.PodFailed {
- t.Fatalf("expected pod status %q. Got %q.", api.PodFailed, status.Phase)
- }
- // fittingPod should be Pending
- status, found = kl.statusManager.GetPodStatus(fittingPod.UID)
- if !found {
- t.Fatalf("status of pod %q is not found in the status map", fittingPod.UID)
- }
- if status.Phase != api.PodPending {
- t.Fatalf("expected pod status %q. Got %q.", api.PodPending, status.Phase)
- }
- }
- // TODO(filipg): This test should be removed once StatusSyncer can do garbage collection without external signal.
- func TestPurgingObsoleteStatusMapEntries(t *testing.T) {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
- testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- versionInfo := &cadvisorapi.VersionInfo{
- KernelVersion: "3.16.0-0.bpo.4-amd64",
- ContainerOsVersion: "Debian GNU/Linux 7 (wheezy)",
- DockerVersion: "1.5.0",
- }
- testKubelet.fakeCadvisor.On("VersionInfo").Return(versionInfo, nil)
- kl := testKubelet.kubelet
- pods := []*api.Pod{
- {ObjectMeta: api.ObjectMeta{Name: "pod1", UID: "1234"}, Spec: api.PodSpec{Containers: []api.Container{{Ports: []api.ContainerPort{{HostPort: 80}}}}}},
- {ObjectMeta: api.ObjectMeta{Name: "pod2", UID: "4567"}, Spec: api.PodSpec{Containers: []api.Container{{Ports: []api.ContainerPort{{HostPort: 80}}}}}},
- }
- podToTest := pods[1]
- // Run once to populate the status map.
- kl.HandlePodAdditions(pods)
- if _, found := kl.statusManager.GetPodStatus(podToTest.UID); !found {
- t.Fatalf("expected to have status cached for pod2")
- }
- // Sync with empty pods so that the entry in status map will be removed.
- kl.podManager.SetPods([]*api.Pod{})
- kl.HandlePodCleanups()
- if _, found := kl.statusManager.GetPodStatus(podToTest.UID); found {
- t.Fatalf("expected to not have status cached for pod2")
- }
- }
- func TestValidateContainerLogStatus(t *testing.T) {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- kubelet := testKubelet.kubelet
- containerName := "x"
- testCases := []struct {
- statuses []api.ContainerStatus
- success bool
- }{
- {
- statuses: []api.ContainerStatus{
- {
- Name: containerName,
- State: api.ContainerState{
- Running: &api.ContainerStateRunning{},
- },
- LastTerminationState: api.ContainerState{
- Terminated: &api.ContainerStateTerminated{},
- },
- },
- },
- success: true,
- },
- {
- statuses: []api.ContainerStatus{
- {
- Name: containerName,
- State: api.ContainerState{
- Running: &api.ContainerStateRunning{},
- },
- },
- },
- success: true,
- },
- {
- statuses: []api.ContainerStatus{
- {
- Name: containerName,
- State: api.ContainerState{
- Terminated: &api.ContainerStateTerminated{},
- },
- },
- },
- success: true,
- },
- {
- statuses: []api.ContainerStatus{
- {
- Name: containerName,
- State: api.ContainerState{
- Waiting: &api.ContainerStateWaiting{},
- },
- },
- },
- success: false,
- },
- {
- statuses: []api.ContainerStatus{
- {
- Name: containerName,
- State: api.ContainerState{Waiting: &api.ContainerStateWaiting{Reason: "ErrImagePull"}},
- },
- },
- success: false,
- },
- {
- statuses: []api.ContainerStatus{
- {
- Name: containerName,
- State: api.ContainerState{Waiting: &api.ContainerStateWaiting{Reason: "ErrImagePullBackOff"}},
- },
- },
- success: false,
- },
- }
- for i, tc := range testCases {
- _, err := kubelet.validateContainerLogStatus("podName", &api.PodStatus{
- ContainerStatuses: tc.statuses,
- }, containerName, false)
- if tc.success {
- if err != nil {
- t.Errorf("[case %d]: unexpected failure - %v", i, err)
- }
- } else if err == nil {
- t.Errorf("[case %d]: unexpected success", i)
- }
- }
- if _, err := kubelet.validateContainerLogStatus("podName", &api.PodStatus{
- ContainerStatuses: testCases[0].statuses,
- }, "blah", false); err == nil {
- t.Errorf("expected error with invalid container name")
- }
- if _, err := kubelet.validateContainerLogStatus("podName", &api.PodStatus{
- ContainerStatuses: testCases[0].statuses,
- }, containerName, true); err != nil {
- t.Errorf("unexpected error with for previous terminated container - %v", err)
- }
- if _, err := kubelet.validateContainerLogStatus("podName", &api.PodStatus{
- ContainerStatuses: testCases[0].statuses,
- }, containerName, false); err != nil {
- t.Errorf("unexpected error with for most recent container - %v", err)
- }
- if _, err := kubelet.validateContainerLogStatus("podName", &api.PodStatus{
- ContainerStatuses: testCases[1].statuses,
- }, containerName, true); err == nil {
- t.Errorf("expected error with for previous terminated container")
- }
- if _, err := kubelet.validateContainerLogStatus("podName", &api.PodStatus{
- ContainerStatuses: testCases[1].statuses,
- }, containerName, false); err != nil {
- t.Errorf("unexpected error with for most recent container")
- }
- }
- // updateDiskSpacePolicy creates a new DiskSpaceManager with a new policy. This new manager along
- // with the mock FsInfo values added to Cadvisor should make the kubelet report that it has
- // sufficient disk space or it is out of disk, depending on the capacity, availability and
- // threshold values.
- func updateDiskSpacePolicy(kubelet *Kubelet, mockCadvisor *cadvisortest.Mock, rootCap, dockerCap, rootAvail, dockerAvail uint64, rootThreshold, dockerThreshold int) error {
- dockerimagesFsInfo := cadvisorapiv2.FsInfo{Capacity: rootCap * mb, Available: rootAvail * mb}
- rootFsInfo := cadvisorapiv2.FsInfo{Capacity: dockerCap * mb, Available: dockerAvail * mb}
- mockCadvisor.On("ImagesFsInfo").Return(dockerimagesFsInfo, nil)
- mockCadvisor.On("RootFsInfo").Return(rootFsInfo, nil)
- dsp := DiskSpacePolicy{DockerFreeDiskMB: rootThreshold, RootFreeDiskMB: dockerThreshold}
- diskSpaceManager, err := newDiskSpaceManager(mockCadvisor, dsp)
- if err != nil {
- return err
- }
- kubelet.diskSpaceManager = diskSpaceManager
- return nil
- }
- func TestCreateMirrorPod(t *testing.T) {
- for _, updateType := range []kubetypes.SyncPodType{kubetypes.SyncPodCreate, kubetypes.SyncPodUpdate} {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- testKubelet.fakeCadvisor.On("Start").Return(nil)
- testKubelet.fakeCadvisor.On("VersionInfo").Return(&cadvisorapi.VersionInfo{}, nil)
- testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
- testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- kl := testKubelet.kubelet
- manager := testKubelet.fakeMirrorClient
- pod := podWithUidNameNs("12345678", "bar", "foo")
- pod.Annotations[kubetypes.ConfigSourceAnnotationKey] = "file"
- pods := []*api.Pod{pod}
- kl.podManager.SetPods(pods)
- err := kl.syncPod(syncPodOptions{
- pod: pod,
- podStatus: &kubecontainer.PodStatus{},
- updateType: updateType,
- })
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- podFullName := kubecontainer.GetPodFullName(pod)
- if !manager.HasPod(podFullName) {
- t.Errorf("expected mirror pod %q to be created", podFullName)
- }
- if manager.NumOfPods() != 1 || !manager.HasPod(podFullName) {
- t.Errorf("expected one mirror pod %q, got %v", podFullName, manager.GetPods())
- }
- }
- }
- func TestDeleteOutdatedMirrorPod(t *testing.T) {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- testKubelet.fakeCadvisor.On("Start").Return(nil)
- testKubelet.fakeCadvisor.On("VersionInfo").Return(&cadvisorapi.VersionInfo{}, nil)
- testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
- testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- kl := testKubelet.kubelet
- manager := testKubelet.fakeMirrorClient
- pod := podWithUidNameNsSpec("12345678", "foo", "ns", api.PodSpec{
- Containers: []api.Container{
- {Name: "1234", Image: "foo"},
- },
- })
- pod.Annotations[kubetypes.ConfigSourceAnnotationKey] = "file"
- // Mirror pod has an outdated spec.
- mirrorPod := podWithUidNameNsSpec("11111111", "foo", "ns", api.PodSpec{
- Containers: []api.Container{
- {Name: "1234", Image: "bar"},
- },
- })
- mirrorPod.Annotations[kubetypes.ConfigSourceAnnotationKey] = "api"
- mirrorPod.Annotations[kubetypes.ConfigMirrorAnnotationKey] = "mirror"
- pods := []*api.Pod{pod, mirrorPod}
- kl.podManager.SetPods(pods)
- err := kl.syncPod(syncPodOptions{
- pod: pod,
- mirrorPod: mirrorPod,
- podStatus: &kubecontainer.PodStatus{},
- updateType: kubetypes.SyncPodUpdate,
- })
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- name := kubecontainer.GetPodFullName(pod)
- creates, deletes := manager.GetCounts(name)
- if creates != 1 || deletes != 1 {
- t.Errorf("expected 1 creation and 1 deletion of %q, got %d, %d", name, creates, deletes)
- }
- }
- func TestDeleteOrphanedMirrorPods(t *testing.T) {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- testKubelet.fakeCadvisor.On("Start").Return(nil)
- testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
- testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- kl := testKubelet.kubelet
- manager := testKubelet.fakeMirrorClient
- orphanPods := []*api.Pod{
- {
- ObjectMeta: api.ObjectMeta{
- UID: "12345678",
- Name: "pod1",
- Namespace: "ns",
- Annotations: map[string]string{
- kubetypes.ConfigSourceAnnotationKey: "api",
- kubetypes.ConfigMirrorAnnotationKey: "mirror",
- },
- },
- },
- {
- ObjectMeta: api.ObjectMeta{
- UID: "12345679",
- Name: "pod2",
- Namespace: "ns",
- Annotations: map[string]string{
- kubetypes.ConfigSourceAnnotationKey: "api",
- kubetypes.ConfigMirrorAnnotationKey: "mirror",
- },
- },
- },
- }
- kl.podManager.SetPods(orphanPods)
- // Sync with an empty pod list to delete all mirror pods.
- kl.HandlePodCleanups()
- if manager.NumOfPods() != 0 {
- t.Errorf("expected zero mirror pods, got %v", manager.GetPods())
- }
- for _, pod := range orphanPods {
- name := kubecontainer.GetPodFullName(pod)
- creates, deletes := manager.GetCounts(name)
- if creates != 0 || deletes != 1 {
- t.Errorf("expected 0 creation and one deletion of %q, got %d, %d", name, creates, deletes)
- }
- }
- }
- func TestGetContainerInfoForMirrorPods(t *testing.T) {
- // pods contain one static and one mirror pod with the same name but
- // different UIDs.
- pods := []*api.Pod{
- {
- ObjectMeta: api.ObjectMeta{
- UID: "1234",
- Name: "qux",
- Namespace: "ns",
- Annotations: map[string]string{
- kubetypes.ConfigSourceAnnotationKey: "file",
- },
- },
- Spec: api.PodSpec{
- Containers: []api.Container{
- {Name: "foo"},
- },
- },
- },
- {
- ObjectMeta: api.ObjectMeta{
- UID: "5678",
- Name: "qux",
- Namespace: "ns",
- Annotations: map[string]string{
- kubetypes.ConfigSourceAnnotationKey: "api",
- kubetypes.ConfigMirrorAnnotationKey: "mirror",
- },
- },
- Spec: api.PodSpec{
- Containers: []api.Container{
- {Name: "foo"},
- },
- },
- },
- }
- containerID := "ab2cdf"
- containerPath := fmt.Sprintf("/docker/%v", containerID)
- containerInfo := cadvisorapi.ContainerInfo{
- ContainerReference: cadvisorapi.ContainerReference{
- Name: containerPath,
- },
- }
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- fakeRuntime := testKubelet.fakeRuntime
- mockCadvisor := testKubelet.fakeCadvisor
- cadvisorReq := &cadvisorapi.ContainerInfoRequest{}
- mockCadvisor.On("DockerContainer", containerID, cadvisorReq).Return(containerInfo, nil)
- kubelet := testKubelet.kubelet
- fakeRuntime.PodList = []*containertest.FakePod{
- {Pod: &kubecontainer.Pod{
- ID: "1234",
- Name: "qux",
- Namespace: "ns",
- Containers: []*kubecontainer.Container{
- {
- Name: "foo",
- ID: kubecontainer.ContainerID{Type: "test", ID: containerID},
- },
- },
- }},
- }
- kubelet.podManager.SetPods(pods)
- // Use the mirror pod UID to retrieve the stats.
- stats, err := kubelet.GetContainerInfo("qux_ns", "5678", "foo", cadvisorReq)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if stats == nil {
- t.Fatalf("stats should not be nil")
- }
- mockCadvisor.AssertExpectations(t)
- }
- func TestHostNetworkAllowed(t *testing.T) {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- testKubelet.fakeCadvisor.On("Start").Return(nil)
- testKubelet.fakeCadvisor.On("VersionInfo").Return(&cadvisorapi.VersionInfo{}, nil)
- testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
- testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- kubelet := testKubelet.kubelet
- capabilities.SetForTests(capabilities.Capabilities{
- PrivilegedSources: capabilities.PrivilegedSources{
- HostNetworkSources: []string{kubetypes.ApiserverSource, kubetypes.FileSource},
- },
- })
- pod := podWithUidNameNsSpec("12345678", "foo", "new", api.PodSpec{
- Containers: []api.Container{
- {Name: "foo"},
- },
- SecurityContext: &api.PodSecurityContext{
- HostNetwork: true,
- },
- })
- pod.Annotations[kubetypes.ConfigSourceAnnotationKey] = kubetypes.FileSource
- kubelet.podManager.SetPods([]*api.Pod{pod})
- err := kubelet.syncPod(syncPodOptions{
- pod: pod,
- podStatus: &kubecontainer.PodStatus{},
- updateType: kubetypes.SyncPodUpdate,
- })
- if err != nil {
- t.Errorf("expected pod infra creation to succeed: %v", err)
- }
- }
- func TestHostNetworkDisallowed(t *testing.T) {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- testKubelet.fakeCadvisor.On("Start").Return(nil)
- testKubelet.fakeCadvisor.On("VersionInfo").Return(&cadvisorapi.VersionInfo{}, nil)
- testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
- testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- kubelet := testKubelet.kubelet
- capabilities.SetForTests(capabilities.Capabilities{
- PrivilegedSources: capabilities.PrivilegedSources{
- HostNetworkSources: []string{},
- },
- })
- pod := podWithUidNameNsSpec("12345678", "foo", "new", api.PodSpec{
- Containers: []api.Container{
- {Name: "foo"},
- },
- SecurityContext: &api.PodSecurityContext{
- HostNetwork: true,
- },
- })
- pod.Annotations[kubetypes.ConfigSourceAnnotationKey] = kubetypes.FileSource
- err := kubelet.syncPod(syncPodOptions{
- pod: pod,
- podStatus: &kubecontainer.PodStatus{},
- updateType: kubetypes.SyncPodUpdate,
- })
- if err == nil {
- t.Errorf("expected pod infra creation to fail")
- }
- }
- func TestPrivilegeContainerAllowed(t *testing.T) {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- testKubelet.fakeCadvisor.On("Start").Return(nil)
- testKubelet.fakeCadvisor.On("VersionInfo").Return(&cadvisorapi.VersionInfo{}, nil)
- testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
- testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- kubelet := testKubelet.kubelet
- capabilities.SetForTests(capabilities.Capabilities{
- AllowPrivileged: true,
- })
- privileged := true
- pod := podWithUidNameNsSpec("12345678", "foo", "new", api.PodSpec{
- Containers: []api.Container{
- {Name: "foo", SecurityContext: &api.SecurityContext{Privileged: &privileged}},
- },
- })
- kubelet.podManager.SetPods([]*api.Pod{pod})
- err := kubelet.syncPod(syncPodOptions{
- pod: pod,
- podStatus: &kubecontainer.PodStatus{},
- updateType: kubetypes.SyncPodUpdate,
- })
- if err != nil {
- t.Errorf("expected pod infra creation to succeed: %v", err)
- }
- }
- func TestPrivilegedContainerDisallowed(t *testing.T) {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- testKubelet.fakeCadvisor.On("VersionInfo").Return(&cadvisorapi.VersionInfo{}, nil)
- testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
- testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- kubelet := testKubelet.kubelet
- capabilities.SetForTests(capabilities.Capabilities{
- AllowPrivileged: false,
- })
- privileged := true
- pod := podWithUidNameNsSpec("12345678", "foo", "new", api.PodSpec{
- Containers: []api.Container{
- {Name: "foo", SecurityContext: &api.SecurityContext{Privileged: &privileged}},
- },
- })
- err := kubelet.syncPod(syncPodOptions{
- pod: pod,
- podStatus: &kubecontainer.PodStatus{},
- updateType: kubetypes.SyncPodUpdate,
- })
- if err == nil {
- t.Errorf("expected pod infra creation to fail")
- }
- }
- func TestFilterOutTerminatedPods(t *testing.T) {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- kubelet := testKubelet.kubelet
- pods := newTestPods(5)
- pods[0].Status.Phase = api.PodFailed
- pods[1].Status.Phase = api.PodSucceeded
- pods[2].Status.Phase = api.PodRunning
- pods[3].Status.Phase = api.PodPending
- expected := []*api.Pod{pods[2], pods[3], pods[4]}
- kubelet.podManager.SetPods(pods)
- actual := kubelet.filterOutTerminatedPods(pods)
- if !reflect.DeepEqual(expected, actual) {
- t.Errorf("expected %#v, got %#v", expected, actual)
- }
- }
- func TestMakePortMappings(t *testing.T) {
- port := func(name string, protocol api.Protocol, containerPort, hostPort int32, ip string) api.ContainerPort {
- return api.ContainerPort{
- Name: name,
- Protocol: protocol,
- ContainerPort: containerPort,
- HostPort: hostPort,
- HostIP: ip,
- }
- }
- portMapping := func(name string, protocol api.Protocol, containerPort, hostPort int, ip string) kubecontainer.PortMapping {
- return kubecontainer.PortMapping{
- Name: name,
- Protocol: protocol,
- ContainerPort: containerPort,
- HostPort: hostPort,
- HostIP: ip,
- }
- }
- tests := []struct {
- container *api.Container
- expectedPortMappings []kubecontainer.PortMapping
- }{
- {
- &api.Container{
- Name: "fooContainer",
- Ports: []api.ContainerPort{
- port("", api.ProtocolTCP, 80, 8080, "127.0.0.1"),
- port("", api.ProtocolTCP, 443, 4343, "192.168.0.1"),
- port("foo", api.ProtocolUDP, 555, 5555, ""),
- // Duplicated, should be ignored.
- port("foo", api.ProtocolUDP, 888, 8888, ""),
- // Duplicated, should be ignored.
- port("", api.ProtocolTCP, 80, 8888, ""),
- },
- },
- []kubecontainer.PortMapping{
- portMapping("fooContainer-TCP:80", api.ProtocolTCP, 80, 8080, "127.0.0.1"),
- portMapping("fooContainer-TCP:443", api.ProtocolTCP, 443, 4343, "192.168.0.1"),
- portMapping("fooContainer-foo", api.ProtocolUDP, 555, 5555, ""),
- },
- },
- }
- for i, tt := range tests {
- actual := makePortMappings(tt.container)
- if !reflect.DeepEqual(tt.expectedPortMappings, actual) {
- t.Errorf("%d: Expected: %#v, saw: %#v", i, tt.expectedPortMappings, actual)
- }
- }
- }
- func TestSyncPodsSetStatusToFailedForPodsThatRunTooLong(t *testing.T) {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- fakeRuntime := testKubelet.fakeRuntime
- testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
- kubelet := testKubelet.kubelet
- now := unversioned.Now()
- startTime := unversioned.NewTime(now.Time.Add(-1 * time.Minute))
- exceededActiveDeadlineSeconds := int64(30)
- pods := []*api.Pod{
- {
- ObjectMeta: api.ObjectMeta{
- UID: "12345678",
- Name: "bar",
- Namespace: "new",
- },
- Spec: api.PodSpec{
- Containers: []api.Container{
- {Name: "foo"},
- },
- ActiveDeadlineSeconds: &exceededActiveDeadlineSeconds,
- },
- Status: api.PodStatus{
- StartTime: &startTime,
- },
- },
- }
- fakeRuntime.PodList = []*containertest.FakePod{
- {Pod: &kubecontainer.Pod{
- ID: "12345678",
- Name: "bar",
- Namespace: "new",
- Containers: []*kubecontainer.Container{
- {Name: "foo"},
- },
- }},
- }
- // Let the pod worker sets the status to fail after this sync.
- kubelet.HandlePodUpdates(pods)
- status, found := kubelet.statusManager.GetPodStatus(pods[0].UID)
- if !found {
- t.Errorf("expected to found status for pod %q", pods[0].UID)
- }
- if status.Phase != api.PodFailed {
- t.Fatalf("expected pod status %q, got %q.", api.PodFailed, status.Phase)
- }
- }
- func TestSyncPodsDoesNotSetPodsThatDidNotRunTooLongToFailed(t *testing.T) {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- fakeRuntime := testKubelet.fakeRuntime
- testKubelet.fakeCadvisor.On("Start").Return(nil)
- testKubelet.fakeCadvisor.On("VersionInfo").Return(&cadvisorapi.VersionInfo{}, nil)
- testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
- testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- kubelet := testKubelet.kubelet
- now := unversioned.Now()
- startTime := unversioned.NewTime(now.Time.Add(-1 * time.Minute))
- exceededActiveDeadlineSeconds := int64(300)
- pods := []*api.Pod{
- {
- ObjectMeta: api.ObjectMeta{
- UID: "12345678",
- Name: "bar",
- Namespace: "new",
- },
- Spec: api.PodSpec{
- Containers: []api.Container{
- {Name: "foo"},
- },
- ActiveDeadlineSeconds: &exceededActiveDeadlineSeconds,
- },
- Status: api.PodStatus{
- StartTime: &startTime,
- },
- },
- }
- fakeRuntime.PodList = []*containertest.FakePod{
- {Pod: &kubecontainer.Pod{
- ID: "12345678",
- Name: "bar",
- Namespace: "new",
- Containers: []*kubecontainer.Container{
- {Name: "foo"},
- },
- }},
- }
- kubelet.podManager.SetPods(pods)
- kubelet.HandlePodUpdates(pods)
- status, found := kubelet.statusManager.GetPodStatus(pods[0].UID)
- if !found {
- t.Errorf("expected to found status for pod %q", pods[0].UID)
- }
- if status.Phase == api.PodFailed {
- t.Fatalf("expected pod status to not be %q", status.Phase)
- }
- }
- func podWithUidNameNs(uid types.UID, name, namespace string) *api.Pod {
- return &api.Pod{
- ObjectMeta: api.ObjectMeta{
- UID: uid,
- Name: name,
- Namespace: namespace,
- Annotations: map[string]string{},
- },
- }
- }
- func podWithUidNameNsSpec(uid types.UID, name, namespace string, spec api.PodSpec) *api.Pod {
- pod := podWithUidNameNs(uid, name, namespace)
- pod.Spec = spec
- return pod
- }
- func TestDeletePodDirsForDeletedPods(t *testing.T) {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- testKubelet.fakeCadvisor.On("Start").Return(nil)
- testKubelet.fakeCadvisor.On("VersionInfo").Return(&cadvisorapi.VersionInfo{}, nil)
- testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
- testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- kl := testKubelet.kubelet
- pods := []*api.Pod{
- podWithUidNameNs("12345678", "pod1", "ns"),
- podWithUidNameNs("12345679", "pod2", "ns"),
- }
- kl.podManager.SetPods(pods)
- // Sync to create pod directories.
- kl.HandlePodSyncs(kl.podManager.GetPods())
- for i := range pods {
- if !dirExists(kl.getPodDir(pods[i].UID)) {
- t.Errorf("expected directory to exist for pod %d", i)
- }
- }
- // Pod 1 has been deleted and no longer exists.
- kl.podManager.SetPods([]*api.Pod{pods[0]})
- kl.HandlePodCleanups()
- if !dirExists(kl.getPodDir(pods[0].UID)) {
- t.Errorf("expected directory to exist for pod 0")
- }
- if dirExists(kl.getPodDir(pods[1].UID)) {
- t.Errorf("expected directory to be deleted for pod 1")
- }
- }
- func syncAndVerifyPodDir(t *testing.T, testKubelet *TestKubelet, pods []*api.Pod, podsToCheck []*api.Pod, shouldExist bool) {
- kl := testKubelet.kubelet
- kl.podManager.SetPods(pods)
- kl.HandlePodSyncs(pods)
- kl.HandlePodCleanups()
- for i, pod := range podsToCheck {
- exist := dirExists(kl.getPodDir(pod.UID))
- if shouldExist && !exist {
- t.Errorf("expected directory to exist for pod %d", i)
- } else if !shouldExist && exist {
- t.Errorf("expected directory to be removed for pod %d", i)
- }
- }
- }
- func TestDoesNotDeletePodDirsForTerminatedPods(t *testing.T) {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- testKubelet.fakeCadvisor.On("Start").Return(nil)
- testKubelet.fakeCadvisor.On("VersionInfo").Return(&cadvisorapi.VersionInfo{}, nil)
- testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
- testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- kl := testKubelet.kubelet
- pods := []*api.Pod{
- podWithUidNameNs("12345678", "pod1", "ns"),
- podWithUidNameNs("12345679", "pod2", "ns"),
- podWithUidNameNs("12345680", "pod3", "ns"),
- }
- syncAndVerifyPodDir(t, testKubelet, pods, pods, true)
- // Pod 1 failed, and pod 2 succeeded. None of the pod directories should be
- // deleted.
- kl.statusManager.SetPodStatus(pods[1], api.PodStatus{Phase: api.PodFailed})
- kl.statusManager.SetPodStatus(pods[2], api.PodStatus{Phase: api.PodSucceeded})
- syncAndVerifyPodDir(t, testKubelet, pods, pods, true)
- }
- func TestDoesNotDeletePodDirsIfContainerIsRunning(t *testing.T) {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- testKubelet.fakeCadvisor.On("Start").Return(nil)
- testKubelet.fakeCadvisor.On("VersionInfo").Return(&cadvisorapi.VersionInfo{}, nil)
- testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
- testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- runningPod := &kubecontainer.Pod{
- ID: "12345678",
- Name: "pod1",
- Namespace: "ns",
- }
- apiPod := podWithUidNameNs(runningPod.ID, runningPod.Name, runningPod.Namespace)
- // Sync once to create pod directory; confirm that the pod directory has
- // already been created.
- pods := []*api.Pod{apiPod}
- syncAndVerifyPodDir(t, testKubelet, pods, []*api.Pod{apiPod}, true)
- // Pretend the pod is deleted from apiserver, but is still active on the node.
- // The pod directory should not be removed.
- pods = []*api.Pod{}
- testKubelet.fakeRuntime.PodList = []*containertest.FakePod{{runningPod, ""}}
- syncAndVerifyPodDir(t, testKubelet, pods, []*api.Pod{apiPod}, true)
- // The pod is deleted and also not active on the node. The pod directory
- // should be removed.
- pods = []*api.Pod{}
- testKubelet.fakeRuntime.PodList = []*containertest.FakePod{}
- syncAndVerifyPodDir(t, testKubelet, pods, []*api.Pod{apiPod}, false)
- }
- func TestGetPodsToSync(t *testing.T) {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- kubelet := testKubelet.kubelet
- clock := testKubelet.fakeClock
- pods := newTestPods(5)
- exceededActiveDeadlineSeconds := int64(30)
- notYetActiveDeadlineSeconds := int64(120)
- startTime := unversioned.NewTime(clock.Now())
- pods[0].Status.StartTime = &startTime
- pods[0].Spec.ActiveDeadlineSeconds = &exceededActiveDeadlineSeconds
- pods[1].Status.StartTime = &startTime
- pods[1].Spec.ActiveDeadlineSeconds = ¬YetActiveDeadlineSeconds
- pods[2].Status.StartTime = &startTime
- pods[2].Spec.ActiveDeadlineSeconds = &exceededActiveDeadlineSeconds
- kubelet.podManager.SetPods(pods)
- kubelet.workQueue.Enqueue(pods[2].UID, 0)
- kubelet.workQueue.Enqueue(pods[3].UID, 30*time.Second)
- kubelet.workQueue.Enqueue(pods[4].UID, 2*time.Minute)
- clock.Step(1 * time.Minute)
- expectedPods := []*api.Pod{pods[0], pods[2], pods[3]}
- podsToSync := kubelet.getPodsToSync()
- if len(podsToSync) == len(expectedPods) {
- for _, expect := range expectedPods {
- var found bool
- for _, got := range podsToSync {
- if expect.UID == got.UID {
- found = true
- break
- }
- }
- if !found {
- t.Errorf("expected pod not found: %#v", expect)
- }
- }
- } else {
- t.Errorf("expected %d pods to sync, got %d", len(expectedPods), len(podsToSync))
- }
- }
- func TestGenerateAPIPodStatusWithSortedContainers(t *testing.T) {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- testKubelet.fakeCadvisor.On("VersionInfo").Return(&cadvisorapi.VersionInfo{}, nil)
- testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
- testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- kubelet := testKubelet.kubelet
- numContainers := 10
- expectedOrder := []string{}
- cStatuses := []*kubecontainer.ContainerStatus{}
- specContainerList := []api.Container{}
- for i := 0; i < numContainers; i++ {
- id := fmt.Sprintf("%v", i)
- containerName := fmt.Sprintf("%vcontainer", id)
- expectedOrder = append(expectedOrder, containerName)
- cStatus := &kubecontainer.ContainerStatus{
- ID: kubecontainer.BuildContainerID("test", id),
- Name: containerName,
- }
- // Rearrange container statuses
- if i%2 == 0 {
- cStatuses = append(cStatuses, cStatus)
- } else {
- cStatuses = append([]*kubecontainer.ContainerStatus{cStatus}, cStatuses...)
- }
- specContainerList = append(specContainerList, api.Container{Name: containerName})
- }
- pod := podWithUidNameNs("uid1", "foo", "test")
- pod.Spec = api.PodSpec{
- Containers: specContainerList,
- }
- status := &kubecontainer.PodStatus{
- ID: pod.UID,
- Name: pod.Name,
- Namespace: pod.Namespace,
- ContainerStatuses: cStatuses,
- }
- for i := 0; i < 5; i++ {
- apiStatus := kubelet.generateAPIPodStatus(pod, status)
- for i, c := range apiStatus.ContainerStatuses {
- if expectedOrder[i] != c.Name {
- t.Fatalf("Container status not sorted, expected %v at index %d, but found %v", expectedOrder[i], i, c.Name)
- }
- }
- }
- }
- func verifyContainerStatuses(statuses []api.ContainerStatus, state, lastTerminationState map[string]api.ContainerState) error {
- for _, s := range statuses {
- if !reflect.DeepEqual(s.State, state[s.Name]) {
- return fmt.Errorf("unexpected state: %s", diff.ObjectDiff(state[s.Name], s.State))
- }
- if !reflect.DeepEqual(s.LastTerminationState, lastTerminationState[s.Name]) {
- return fmt.Errorf("unexpected last termination state %s", diff.ObjectDiff(
- lastTerminationState[s.Name], s.LastTerminationState))
- }
- }
- return nil
- }
- // Test generateAPIPodStatus with different reason cache and old api pod status.
- func TestGenerateAPIPodStatusWithReasonCache(t *testing.T) {
- // The following waiting reason and message are generated in convertStatusToAPIStatus()
- startWaitingReason := "ContainerCreating"
- initWaitingReason := "PodInitializing"
- testTimestamp := time.Unix(123456789, 987654321)
- testErrorReason := fmt.Errorf("test-error")
- emptyContainerID := (&kubecontainer.ContainerID{}).String()
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- testKubelet.fakeCadvisor.On("VersionInfo").Return(&cadvisorapi.VersionInfo{}, nil)
- testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
- testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- kubelet := testKubelet.kubelet
- pod := podWithUidNameNs("12345678", "foo", "new")
- pod.Spec = api.PodSpec{RestartPolicy: api.RestartPolicyOnFailure}
- podStatus := &kubecontainer.PodStatus{
- ID: pod.UID,
- Name: pod.Name,
- Namespace: pod.Namespace,
- }
- tests := []struct {
- containers []api.Container
- statuses []*kubecontainer.ContainerStatus
- reasons map[string]error
- oldStatuses []api.ContainerStatus
- expectedState map[string]api.ContainerState
- // Only set expectedInitState when it is different from expectedState
- expectedInitState map[string]api.ContainerState
- expectedLastTerminationState map[string]api.ContainerState
- }{
- // For container with no historical record, State should be Waiting, LastTerminationState should be retrieved from
- // old status from apiserver.
- {
- containers: []api.Container{{Name: "without-old-record"}, {Name: "with-old-record"}},
- statuses: []*kubecontainer.ContainerStatus{},
- reasons: map[string]error{},
- oldStatuses: []api.ContainerStatus{{
- Name: "with-old-record",
- LastTerminationState: api.ContainerState{Terminated: &api.ContainerStateTerminated{}},
- }},
- expectedState: map[string]api.ContainerState{
- "without-old-record": {Waiting: &api.ContainerStateWaiting{
- Reason: startWaitingReason,
- }},
- "with-old-record": {Waiting: &api.ContainerStateWaiting{
- Reason: startWaitingReason,
- }},
- },
- expectedInitState: map[string]api.ContainerState{
- "without-old-record": {Waiting: &api.ContainerStateWaiting{
- Reason: initWaitingReason,
- }},
- "with-old-record": {Waiting: &api.ContainerStateWaiting{
- Reason: initWaitingReason,
- }},
- },
- expectedLastTerminationState: map[string]api.ContainerState{
- "with-old-record": {Terminated: &api.ContainerStateTerminated{}},
- },
- },
- // For running container, State should be Running, LastTerminationState should be retrieved from latest terminated status.
- {
- containers: []api.Container{{Name: "running"}},
- statuses: []*kubecontainer.ContainerStatus{
- {
- Name: "running",
- State: kubecontainer.ContainerStateRunning,
- StartedAt: testTimestamp,
- },
- {
- Name: "running",
- State: kubecontainer.ContainerStateExited,
- ExitCode: 1,
- },
- },
- reasons: map[string]error{},
- oldStatuses: []api.ContainerStatus{},
- expectedState: map[string]api.ContainerState{
- "running": {Running: &api.ContainerStateRunning{
- StartedAt: unversioned.NewTime(testTimestamp),
- }},
- },
- expectedLastTerminationState: map[string]api.ContainerState{
- "running": {Terminated: &api.ContainerStateTerminated{
- ExitCode: 1,
- ContainerID: emptyContainerID,
- }},
- },
- },
- // For terminated container:
- // * If there is no recent start error record, State should be Terminated, LastTerminationState should be retrieved from
- // second latest terminated status;
- // * If there is recent start error record, State should be Waiting, LastTerminationState should be retrieved from latest
- // terminated status;
- // * If ExitCode = 0, restart policy is RestartPolicyOnFailure, the container shouldn't be restarted. No matter there is
- // recent start error or not, State should be Terminated, LastTerminationState should be retrieved from second latest
- // terminated status.
- {
- containers: []api.Container{{Name: "without-reason"}, {Name: "with-reason"}},
- statuses: []*kubecontainer.ContainerStatus{
- {
- Name: "without-reason",
- State: kubecontainer.ContainerStateExited,
- ExitCode: 1,
- },
- {
- Name: "with-reason",
- State: kubecontainer.ContainerStateExited,
- ExitCode: 2,
- },
- {
- Name: "without-reason",
- State: kubecontainer.ContainerStateExited,
- ExitCode: 3,
- },
- {
- Name: "with-reason",
- State: kubecontainer.ContainerStateExited,
- ExitCode: 4,
- },
- {
- Name: "succeed",
- State: kubecontainer.ContainerStateExited,
- ExitCode: 0,
- },
- {
- Name: "succeed",
- State: kubecontainer.ContainerStateExited,
- ExitCode: 5,
- },
- },
- reasons: map[string]error{"with-reason": testErrorReason, "succeed": testErrorReason},
- oldStatuses: []api.ContainerStatus{},
- expectedState: map[string]api.ContainerState{
- "without-reason": {Terminated: &api.ContainerStateTerminated{
- ExitCode: 1,
- ContainerID: emptyContainerID,
- }},
- "with-reason": {Waiting: &api.ContainerStateWaiting{Reason: testErrorReason.Error()}},
- "succeed": {Terminated: &api.ContainerStateTerminated{
- ExitCode: 0,
- ContainerID: emptyContainerID,
- }},
- },
- expectedLastTerminationState: map[string]api.ContainerState{
- "without-reason": {Terminated: &api.ContainerStateTerminated{
- ExitCode: 3,
- ContainerID: emptyContainerID,
- }},
- "with-reason": {Terminated: &api.ContainerStateTerminated{
- ExitCode: 2,
- ContainerID: emptyContainerID,
- }},
- "succeed": {Terminated: &api.ContainerStateTerminated{
- ExitCode: 5,
- ContainerID: emptyContainerID,
- }},
- },
- },
- }
- for i, test := range tests {
- kubelet.reasonCache = NewReasonCache()
- for n, e := range test.reasons {
- kubelet.reasonCache.add(pod.UID, n, e, "")
- }
- pod.Spec.Containers = test.containers
- pod.Status.ContainerStatuses = test.oldStatuses
- podStatus.ContainerStatuses = test.statuses
- apiStatus := kubelet.generateAPIPodStatus(pod, podStatus)
- assert.NoError(t, verifyContainerStatuses(apiStatus.ContainerStatuses, test.expectedState, test.expectedLastTerminationState), "case %d", i)
- }
- // Everything should be the same for init containers
- for i, test := range tests {
- kubelet.reasonCache = NewReasonCache()
- for n, e := range test.reasons {
- kubelet.reasonCache.add(pod.UID, n, e, "")
- }
- pod.Spec.InitContainers = test.containers
- pod.Status.InitContainerStatuses = test.oldStatuses
- podStatus.ContainerStatuses = test.statuses
- apiStatus := kubelet.generateAPIPodStatus(pod, podStatus)
- expectedState := test.expectedState
- if test.expectedInitState != nil {
- expectedState = test.expectedInitState
- }
- assert.NoError(t, verifyContainerStatuses(apiStatus.InitContainerStatuses, expectedState, test.expectedLastTerminationState), "case %d", i)
- }
- }
- // Test generateAPIPodStatus with different restart policies.
- func TestGenerateAPIPodStatusWithDifferentRestartPolicies(t *testing.T) {
- testErrorReason := fmt.Errorf("test-error")
- emptyContainerID := (&kubecontainer.ContainerID{}).String()
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- testKubelet.fakeCadvisor.On("VersionInfo").Return(&cadvisorapi.VersionInfo{}, nil)
- testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
- testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- kubelet := testKubelet.kubelet
- pod := podWithUidNameNs("12345678", "foo", "new")
- containers := []api.Container{{Name: "succeed"}, {Name: "failed"}}
- podStatus := &kubecontainer.PodStatus{
- ID: pod.UID,
- Name: pod.Name,
- Namespace: pod.Namespace,
- ContainerStatuses: []*kubecontainer.ContainerStatus{
- {
- Name: "succeed",
- State: kubecontainer.ContainerStateExited,
- ExitCode: 0,
- },
- {
- Name: "failed",
- State: kubecontainer.ContainerStateExited,
- ExitCode: 1,
- },
- {
- Name: "succeed",
- State: kubecontainer.ContainerStateExited,
- ExitCode: 2,
- },
- {
- Name: "failed",
- State: kubecontainer.ContainerStateExited,
- ExitCode: 3,
- },
- },
- }
- kubelet.reasonCache.add(pod.UID, "succeed", testErrorReason, "")
- kubelet.reasonCache.add(pod.UID, "failed", testErrorReason, "")
- for c, test := range []struct {
- restartPolicy api.RestartPolicy
- expectedState map[string]api.ContainerState
- expectedLastTerminationState map[string]api.ContainerState
- // Only set expectedInitState when it is different from expectedState
- expectedInitState map[string]api.ContainerState
- // Only set expectedInitLastTerminationState when it is different from expectedLastTerminationState
- expectedInitLastTerminationState map[string]api.ContainerState
- }{
- {
- restartPolicy: api.RestartPolicyNever,
- expectedState: map[string]api.ContainerState{
- "succeed": {Terminated: &api.ContainerStateTerminated{
- ExitCode: 0,
- ContainerID: emptyContainerID,
- }},
- "failed": {Terminated: &api.ContainerStateTerminated{
- ExitCode: 1,
- ContainerID: emptyContainerID,
- }},
- },
- expectedLastTerminationState: map[string]api.ContainerState{
- "succeed": {Terminated: &api.ContainerStateTerminated{
- ExitCode: 2,
- ContainerID: emptyContainerID,
- }},
- "failed": {Terminated: &api.ContainerStateTerminated{
- ExitCode: 3,
- ContainerID: emptyContainerID,
- }},
- },
- },
- {
- restartPolicy: api.RestartPolicyOnFailure,
- expectedState: map[string]api.ContainerState{
- "succeed": {Terminated: &api.ContainerStateTerminated{
- ExitCode: 0,
- ContainerID: emptyContainerID,
- }},
- "failed": {Waiting: &api.ContainerStateWaiting{Reason: testErrorReason.Error()}},
- },
- expectedLastTerminationState: map[string]api.ContainerState{
- "succeed": {Terminated: &api.ContainerStateTerminated{
- ExitCode: 2,
- ContainerID: emptyContainerID,
- }},
- "failed": {Terminated: &api.ContainerStateTerminated{
- ExitCode: 1,
- ContainerID: emptyContainerID,
- }},
- },
- },
- {
- restartPolicy: api.RestartPolicyAlways,
- expectedState: map[string]api.ContainerState{
- "succeed": {Waiting: &api.ContainerStateWaiting{Reason: testErrorReason.Error()}},
- "failed": {Waiting: &api.ContainerStateWaiting{Reason: testErrorReason.Error()}},
- },
- expectedLastTerminationState: map[string]api.ContainerState{
- "succeed": {Terminated: &api.ContainerStateTerminated{
- ExitCode: 0,
- ContainerID: emptyContainerID,
- }},
- "failed": {Terminated: &api.ContainerStateTerminated{
- ExitCode: 1,
- ContainerID: emptyContainerID,
- }},
- },
- // If the init container is terminated with exit code 0, it won't be restarted even when the
- // restart policy is RestartAlways.
- expectedInitState: map[string]api.ContainerState{
- "succeed": {Terminated: &api.ContainerStateTerminated{
- ExitCode: 0,
- ContainerID: emptyContainerID,
- }},
- "failed": {Waiting: &api.ContainerStateWaiting{Reason: testErrorReason.Error()}},
- },
- expectedInitLastTerminationState: map[string]api.ContainerState{
- "succeed": {Terminated: &api.ContainerStateTerminated{
- ExitCode: 2,
- ContainerID: emptyContainerID,
- }},
- "failed": {Terminated: &api.ContainerStateTerminated{
- ExitCode: 1,
- ContainerID: emptyContainerID,
- }},
- },
- },
- } {
- pod.Spec.RestartPolicy = test.restartPolicy
- // Test normal containers
- pod.Spec.Containers = containers
- apiStatus := kubelet.generateAPIPodStatus(pod, podStatus)
- expectedState, expectedLastTerminationState := test.expectedState, test.expectedLastTerminationState
- assert.NoError(t, verifyContainerStatuses(apiStatus.ContainerStatuses, expectedState, expectedLastTerminationState), "case %d", c)
- pod.Spec.Containers = nil
- // Test init containers
- pod.Spec.InitContainers = containers
- apiStatus = kubelet.generateAPIPodStatus(pod, podStatus)
- if test.expectedInitState != nil {
- expectedState = test.expectedInitState
- }
- if test.expectedInitLastTerminationState != nil {
- expectedLastTerminationState = test.expectedInitLastTerminationState
- }
- assert.NoError(t, verifyContainerStatuses(apiStatus.InitContainerStatuses, expectedState, expectedLastTerminationState), "case %d", c)
- pod.Spec.InitContainers = nil
- }
- }
- // testPodAdmitHandler is a lifecycle.PodAdmitHandler for testing.
- type testPodAdmitHandler struct {
- // list of pods to reject.
- podsToReject []*api.Pod
- }
- // Admit rejects all pods in the podsToReject list with a matching UID.
- func (a *testPodAdmitHandler) Admit(attrs *lifecycle.PodAdmitAttributes) lifecycle.PodAdmitResult {
- for _, podToReject := range a.podsToReject {
- if podToReject.UID == attrs.Pod.UID {
- return lifecycle.PodAdmitResult{Admit: false, Reason: "Rejected", Message: "Pod is rejected"}
- }
- }
- return lifecycle.PodAdmitResult{Admit: true}
- }
- // Test verifies that the kubelet invokes an admission handler during HandlePodAdditions.
- func TestHandlePodAdditionsInvokesPodAdmitHandlers(t *testing.T) {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- kl := testKubelet.kubelet
- kl.nodeLister = testNodeLister{nodes: []api.Node{
- {
- ObjectMeta: api.ObjectMeta{Name: kl.nodeName},
- Status: api.NodeStatus{
- Allocatable: api.ResourceList{
- api.ResourcePods: *resource.NewQuantity(110, resource.DecimalSI),
- },
- },
- },
- }}
- kl.nodeInfo = testNodeInfo{nodes: []api.Node{
- {
- ObjectMeta: api.ObjectMeta{Name: kl.nodeName},
- Status: api.NodeStatus{
- Allocatable: api.ResourceList{
- api.ResourcePods: *resource.NewQuantity(110, resource.DecimalSI),
- },
- },
- },
- }}
- testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
- testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
- pods := []*api.Pod{
- {
- ObjectMeta: api.ObjectMeta{
- UID: "123456789",
- Name: "podA",
- Namespace: "foo",
- },
- },
- {
- ObjectMeta: api.ObjectMeta{
- UID: "987654321",
- Name: "podB",
- Namespace: "foo",
- },
- },
- }
- podToReject := pods[0]
- podToAdmit := pods[1]
- podsToReject := []*api.Pod{podToReject}
- kl.AddPodAdmitHandler(&testPodAdmitHandler{podsToReject: podsToReject})
- kl.HandlePodAdditions(pods)
- // Check pod status stored in the status map.
- // podToReject should be Failed
- status, found := kl.statusManager.GetPodStatus(podToReject.UID)
- if !found {
- t.Fatalf("status of pod %q is not found in the status map", podToReject.UID)
- }
- if status.Phase != api.PodFailed {
- t.Fatalf("expected pod status %q. Got %q.", api.PodFailed, status.Phase)
- }
- // podToAdmit should be Pending
- status, found = kl.statusManager.GetPodStatus(podToAdmit.UID)
- if !found {
- t.Fatalf("status of pod %q is not found in the status map", podToAdmit.UID)
- }
- if status.Phase != api.PodPending {
- t.Fatalf("expected pod status %q. Got %q.", api.PodPending, status.Phase)
- }
- }
- // testPodSyncLoopHandler is a lifecycle.PodSyncLoopHandler that is used for testing.
- type testPodSyncLoopHandler struct {
- // list of pods to sync
- podsToSync []*api.Pod
- }
- // ShouldSync evaluates if the pod should be synced from the kubelet.
- func (a *testPodSyncLoopHandler) ShouldSync(pod *api.Pod) bool {
- for _, podToSync := range a.podsToSync {
- if podToSync.UID == pod.UID {
- return true
- }
- }
- return false
- }
- // TestGetPodsToSyncInvokesPodSyncLoopHandlers ensures that the get pods to sync routine invokes the handler.
- func TestGetPodsToSyncInvokesPodSyncLoopHandlers(t *testing.T) {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- kubelet := testKubelet.kubelet
- pods := newTestPods(5)
- podUIDs := []types.UID{}
- for _, pod := range pods {
- podUIDs = append(podUIDs, pod.UID)
- }
- podsToSync := []*api.Pod{pods[0]}
- kubelet.AddPodSyncLoopHandler(&testPodSyncLoopHandler{podsToSync})
- kubelet.podManager.SetPods(pods)
- expectedPodsUID := []types.UID{pods[0].UID}
- podsToSync = kubelet.getPodsToSync()
- if len(podsToSync) == len(expectedPodsUID) {
- var rightNum int
- for _, podUID := range expectedPodsUID {
- for _, podToSync := range podsToSync {
- if podToSync.UID == podUID {
- rightNum++
- break
- }
- }
- }
- if rightNum != len(expectedPodsUID) {
- // Just for report error
- podsToSyncUID := []types.UID{}
- for _, podToSync := range podsToSync {
- podsToSyncUID = append(podsToSyncUID, podToSync.UID)
- }
- t.Errorf("expected pods %v to sync, got %v", expectedPodsUID, podsToSyncUID)
- }
- } else {
- t.Errorf("expected %d pods to sync, got %d", 3, len(podsToSync))
- }
- }
- // testPodSyncHandler is a lifecycle.PodSyncHandler that is used for testing.
- type testPodSyncHandler struct {
- // list of pods to evict.
- podsToEvict []*api.Pod
- // the reason for the eviction
- reason string
- // the message for the eviction
- message string
- }
- // ShouldEvict evaluates if the pod should be evicted from the kubelet.
- func (a *testPodSyncHandler) ShouldEvict(pod *api.Pod) lifecycle.ShouldEvictResponse {
- for _, podToEvict := range a.podsToEvict {
- if podToEvict.UID == pod.UID {
- return lifecycle.ShouldEvictResponse{Evict: true, Reason: a.reason, Message: a.message}
- }
- }
- return lifecycle.ShouldEvictResponse{Evict: false}
- }
- // TestGenerateAPIPodStatusInvokesPodSyncHandlers invokes the handlers and reports the proper status
- func TestGenerateAPIPodStatusInvokesPodSyncHandlers(t *testing.T) {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- kubelet := testKubelet.kubelet
- pod := newTestPods(1)[0]
- podsToEvict := []*api.Pod{pod}
- kubelet.AddPodSyncHandler(&testPodSyncHandler{podsToEvict, "Evicted", "because"})
- status := &kubecontainer.PodStatus{
- ID: pod.UID,
- Name: pod.Name,
- Namespace: pod.Namespace,
- }
- apiStatus := kubelet.generateAPIPodStatus(pod, status)
- if apiStatus.Phase != api.PodFailed {
- t.Fatalf("Expected phase %v, but got %v", api.PodFailed, apiStatus.Phase)
- }
- if apiStatus.Reason != "Evicted" {
- t.Fatalf("Expected reason %v, but got %v", "Evicted", apiStatus.Reason)
- }
- if apiStatus.Message != "because" {
- t.Fatalf("Expected message %v, but got %v", "because", apiStatus.Message)
- }
- }
- func TestSyncPodKillPod(t *testing.T) {
- testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
- kl := testKubelet.kubelet
- pod := &api.Pod{
- ObjectMeta: api.ObjectMeta{
- UID: "12345678",
- Name: "bar",
- Namespace: "foo",
- },
- }
- pods := []*api.Pod{pod}
- kl.podManager.SetPods(pods)
- gracePeriodOverride := int64(0)
- err := kl.syncPod(syncPodOptions{
- pod: pod,
- podStatus: &kubecontainer.PodStatus{},
- updateType: kubetypes.SyncPodKill,
- killPodOptions: &KillPodOptions{
- PodStatusFunc: func(p *api.Pod, podStatus *kubecontainer.PodStatus) api.PodStatus {
- return api.PodStatus{
- Phase: api.PodFailed,
- Reason: "reason",
- Message: "message",
- }
- },
- PodTerminationGracePeriodSecondsOverride: &gracePeriodOverride,
- },
- })
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- // Check pod status stored in the status map.
- status, found := kl.statusManager.GetPodStatus(pod.UID)
- if !found {
- t.Fatalf("status of pod %q is not found in the status map", pod.UID)
- }
- if status.Phase != api.PodFailed {
- t.Fatalf("expected pod status %q. Got %q.", api.PodFailed, status.Phase)
- }
- }
- func waitForVolumeUnmount(
- volumeManager kubeletvolume.VolumeManager,
- pod *api.Pod) error {
- var podVolumes kubecontainer.VolumeMap
- err := retryWithExponentialBackOff(
- time.Duration(50*time.Millisecond),
- func() (bool, error) {
- // Verify volumes detached
- podVolumes = volumeManager.GetMountedVolumesForPod(
- volumehelper.GetUniquePodName(pod))
- if len(podVolumes) != 0 {
- return false, nil
- }
- return true, nil
- },
- )
- if err != nil {
- return fmt.Errorf(
- "Expected volumes to be unmounted. But some volumes are still mounted: %#v", podVolumes)
- }
- return nil
- }
- func waitForVolumeDetach(
- volumeName api.UniqueVolumeName,
- volumeManager kubeletvolume.VolumeManager) error {
- attachedVolumes := []api.UniqueVolumeName{}
- err := retryWithExponentialBackOff(
- time.Duration(50*time.Millisecond),
- func() (bool, error) {
- // Verify volumes detached
- volumeAttached := volumeManager.VolumeIsAttached(volumeName)
- return !volumeAttached, nil
- },
- )
- if err != nil {
- return fmt.Errorf(
- "Expected volumes to be detached. But some volumes are still attached: %#v", attachedVolumes)
- }
- return nil
- }
- func retryWithExponentialBackOff(initialDuration time.Duration, fn wait.ConditionFunc) error {
- backoff := wait.Backoff{
- Duration: initialDuration,
- Factor: 3,
- Jitter: 0,
- Steps: 6,
- }
- return wait.ExponentialBackoff(backoff, fn)
- }
- func simulateVolumeInUseUpdate(
- volumeName api.UniqueVolumeName,
- stopCh <-chan struct{},
- volumeManager kubeletvolume.VolumeManager) {
- ticker := time.NewTicker(100 * time.Millisecond)
- defer ticker.Stop()
- for {
- select {
- case <-ticker.C:
- volumeManager.MarkVolumesAsReportedInUse(
- []api.UniqueVolumeName{volumeName})
- case <-stopCh:
- return
- }
- }
- }
- func runVolumeManager(kubelet *Kubelet) chan struct{} {
- stopCh := make(chan struct{})
- go kubelet.volumeManager.Run(kubelet.sourcesReady, stopCh)
- return stopCh
- }
|