1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607 |
- /*
- Copyright 2017 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 nodelifecycle
- import (
- "context"
- "fmt"
- "strings"
- "testing"
- "time"
- apps "k8s.io/api/apps/v1"
- coordv1 "k8s.io/api/coordination/v1"
- v1 "k8s.io/api/core/v1"
- apiequality "k8s.io/apimachinery/pkg/api/equality"
- "k8s.io/apimachinery/pkg/api/resource"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/fields"
- "k8s.io/apimachinery/pkg/labels"
- "k8s.io/apimachinery/pkg/runtime"
- "k8s.io/apimachinery/pkg/util/diff"
- utilfeature "k8s.io/apiserver/pkg/util/feature"
- "k8s.io/client-go/informers"
- appsinformers "k8s.io/client-go/informers/apps/v1"
- coordinformers "k8s.io/client-go/informers/coordination/v1"
- coreinformers "k8s.io/client-go/informers/core/v1"
- clientset "k8s.io/client-go/kubernetes"
- "k8s.io/client-go/kubernetes/fake"
- testcore "k8s.io/client-go/testing"
- featuregatetesting "k8s.io/component-base/featuregate/testing"
- "k8s.io/kubernetes/pkg/controller"
- "k8s.io/kubernetes/pkg/controller/nodelifecycle/scheduler"
- "k8s.io/kubernetes/pkg/controller/testutil"
- nodeutil "k8s.io/kubernetes/pkg/controller/util/node"
- "k8s.io/kubernetes/pkg/features"
- kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis"
- "k8s.io/kubernetes/pkg/util/node"
- taintutils "k8s.io/kubernetes/pkg/util/taints"
- "k8s.io/utils/pointer"
- )
- const (
- testNodeMonitorGracePeriod = 40 * time.Second
- testNodeStartupGracePeriod = 60 * time.Second
- testNodeMonitorPeriod = 5 * time.Second
- testRateLimiterQPS = float32(10000)
- testLargeClusterThreshold = 20
- testUnhealthyThreshold = float32(0.55)
- )
- func alwaysReady() bool { return true }
- func fakeGetPodsAssignedToNode(c *fake.Clientset) func(string) ([]*v1.Pod, error) {
- return func(nodeName string) ([]*v1.Pod, error) {
- selector := fields.SelectorFromSet(fields.Set{"spec.nodeName": nodeName})
- pods, err := c.CoreV1().Pods(v1.NamespaceAll).List(context.TODO(), metav1.ListOptions{
- FieldSelector: selector.String(),
- LabelSelector: labels.Everything().String(),
- })
- if err != nil {
- return nil, fmt.Errorf("failed to get Pods assigned to node %v", nodeName)
- }
- rPods := make([]*v1.Pod, len(pods.Items))
- for i := range pods.Items {
- rPods[i] = &pods.Items[i]
- }
- return rPods, nil
- }
- }
- type nodeLifecycleController struct {
- *Controller
- leaseInformer coordinformers.LeaseInformer
- nodeInformer coreinformers.NodeInformer
- daemonSetInformer appsinformers.DaemonSetInformer
- }
- // doEviction does the fake eviction and returns the status of eviction operation.
- func (nc *nodeLifecycleController) doEviction(fakeNodeHandler *testutil.FakeNodeHandler) bool {
- nc.evictorLock.Lock()
- defer nc.evictorLock.Unlock()
- zones := testutil.GetZones(fakeNodeHandler)
- for _, zone := range zones {
- nc.zonePodEvictor[zone].Try(func(value scheduler.TimedValue) (bool, time.Duration) {
- uid, _ := value.UID.(string)
- pods, _ := nc.getPodsAssignedToNode(value.Value)
- nodeutil.DeletePods(fakeNodeHandler, pods, nc.recorder, value.Value, uid, nc.daemonSetStore)
- _ = nc.nodeEvictionMap.setStatus(value.Value, evicted)
- return true, 0
- })
- }
- for _, action := range fakeNodeHandler.Actions() {
- if action.GetVerb() == "delete" && action.GetResource().Resource == "pods" {
- return true
- }
- }
- return false
- }
- func createNodeLease(nodeName string, renewTime metav1.MicroTime) *coordv1.Lease {
- return &coordv1.Lease{
- ObjectMeta: metav1.ObjectMeta{
- Name: nodeName,
- Namespace: v1.NamespaceNodeLease,
- },
- Spec: coordv1.LeaseSpec{
- HolderIdentity: pointer.StringPtr(nodeName),
- RenewTime: &renewTime,
- },
- }
- }
- func (nc *nodeLifecycleController) syncLeaseStore(lease *coordv1.Lease) error {
- if lease == nil {
- return nil
- }
- newElems := make([]interface{}, 0, 1)
- newElems = append(newElems, lease)
- return nc.leaseInformer.Informer().GetStore().Replace(newElems, "newRV")
- }
- func (nc *nodeLifecycleController) syncNodeStore(fakeNodeHandler *testutil.FakeNodeHandler) error {
- nodes, err := fakeNodeHandler.List(context.TODO(), metav1.ListOptions{})
- if err != nil {
- return err
- }
- newElems := make([]interface{}, 0, len(nodes.Items))
- for i := range nodes.Items {
- newElems = append(newElems, &nodes.Items[i])
- }
- return nc.nodeInformer.Informer().GetStore().Replace(newElems, "newRV")
- }
- func newNodeLifecycleControllerFromClient(
- kubeClient clientset.Interface,
- podEvictionTimeout time.Duration,
- evictionLimiterQPS float32,
- secondaryEvictionLimiterQPS float32,
- largeClusterThreshold int32,
- unhealthyZoneThreshold float32,
- nodeMonitorGracePeriod time.Duration,
- nodeStartupGracePeriod time.Duration,
- nodeMonitorPeriod time.Duration,
- useTaints bool,
- ) (*nodeLifecycleController, error) {
- factory := informers.NewSharedInformerFactory(kubeClient, controller.NoResyncPeriodFunc())
- leaseInformer := factory.Coordination().V1().Leases()
- nodeInformer := factory.Core().V1().Nodes()
- daemonSetInformer := factory.Apps().V1().DaemonSets()
- nc, err := NewNodeLifecycleController(
- leaseInformer,
- factory.Core().V1().Pods(),
- nodeInformer,
- daemonSetInformer,
- kubeClient,
- nodeMonitorPeriod,
- nodeStartupGracePeriod,
- nodeMonitorGracePeriod,
- podEvictionTimeout,
- evictionLimiterQPS,
- secondaryEvictionLimiterQPS,
- largeClusterThreshold,
- unhealthyZoneThreshold,
- useTaints,
- useTaints,
- )
- if err != nil {
- return nil, err
- }
- nc.leaseInformerSynced = alwaysReady
- nc.podInformerSynced = alwaysReady
- nc.nodeInformerSynced = alwaysReady
- nc.daemonSetInformerSynced = alwaysReady
- return &nodeLifecycleController{nc, leaseInformer, nodeInformer, daemonSetInformer}, nil
- }
- func TestMonitorNodeHealthEvictPods(t *testing.T) {
- fakeNow := metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC)
- evictionTimeout := 10 * time.Minute
- labels := map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- }
- // Because of the logic that prevents NC from evicting anything when all Nodes are NotReady
- // we need second healthy node in tests. Because of how the tests are written we need to update
- // the status of this Node.
- healthyNodeNewStatus := v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- // Node status has just been updated, and is NotReady for 10min.
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 9, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- }
- table := []struct {
- fakeNodeHandler *testutil.FakeNodeHandler
- daemonSets []apps.DaemonSet
- timeToPass time.Duration
- newNodeStatus v1.NodeStatus
- secondNodeNewStatus v1.NodeStatus
- expectedEvictPods bool
- description string
- }{
- // Node created recently, with no status (happens only at cluster startup).
- {
- fakeNodeHandler: &testutil.FakeNodeHandler{
- Existing: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: fakeNow,
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- },
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node1",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- },
- Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
- },
- daemonSets: nil,
- timeToPass: 0,
- newNodeStatus: v1.NodeStatus{},
- secondNodeNewStatus: healthyNodeNewStatus,
- expectedEvictPods: false,
- description: "Node created recently, with no status.",
- },
- // Node created recently without FailureDomain labels which is added back later, with no status (happens only at cluster startup).
- {
- fakeNodeHandler: &testutil.FakeNodeHandler{
- Existing: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: fakeNow,
- },
- },
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node1",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- },
- Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
- },
- daemonSets: nil,
- timeToPass: 0,
- newNodeStatus: v1.NodeStatus{},
- secondNodeNewStatus: healthyNodeNewStatus,
- expectedEvictPods: false,
- description: "Node created recently without FailureDomain labels which is added back later, with no status (happens only at cluster startup).",
- },
- // Node created long time ago, and kubelet posted NotReady for a short period of time.
- {
- fakeNodeHandler: &testutil.FakeNodeHandler{
- Existing: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionFalse,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node1",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- },
- Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
- },
- daemonSets: nil,
- timeToPass: evictionTimeout,
- newNodeStatus: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionFalse,
- // Node status has just been updated, and is NotReady for 10min.
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 9, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- secondNodeNewStatus: healthyNodeNewStatus,
- expectedEvictPods: false,
- description: "Node created long time ago, and kubelet posted NotReady for a short period of time.",
- },
- // Pod is ds-managed, and kubelet posted NotReady for a long period of time.
- {
- fakeNodeHandler: &testutil.FakeNodeHandler{
- Existing: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionFalse,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node1",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- },
- Clientset: fake.NewSimpleClientset(
- &v1.PodList{
- Items: []v1.Pod{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "pod0",
- Namespace: "default",
- Labels: map[string]string{"daemon": "yes"},
- },
- Spec: v1.PodSpec{
- NodeName: "node0",
- },
- },
- },
- },
- ),
- },
- daemonSets: []apps.DaemonSet{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "ds0",
- Namespace: "default",
- },
- Spec: apps.DaemonSetSpec{
- Selector: &metav1.LabelSelector{
- MatchLabels: map[string]string{"daemon": "yes"},
- },
- },
- },
- },
- timeToPass: time.Hour,
- newNodeStatus: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionFalse,
- // Node status has just been updated, and is NotReady for 1hr.
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 59, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- secondNodeNewStatus: healthyNodeNewStatus,
- expectedEvictPods: false,
- description: "Pod is ds-managed, and kubelet posted NotReady for a long period of time.",
- },
- // Node created long time ago, and kubelet posted NotReady for a long period of time.
- {
- fakeNodeHandler: &testutil.FakeNodeHandler{
- Existing: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionFalse,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node1",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- },
- Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
- },
- daemonSets: nil,
- timeToPass: time.Hour,
- newNodeStatus: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionFalse,
- // Node status has just been updated, and is NotReady for 1hr.
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 59, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- secondNodeNewStatus: healthyNodeNewStatus,
- expectedEvictPods: true,
- description: "Node created long time ago, and kubelet posted NotReady for a long period of time.",
- },
- // Node created long time ago, node controller posted Unknown for a short period of time.
- {
- fakeNodeHandler: &testutil.FakeNodeHandler{
- Existing: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionUnknown,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node1",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- },
- Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
- },
- daemonSets: nil,
- timeToPass: evictionTimeout - testNodeMonitorGracePeriod,
- newNodeStatus: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionUnknown,
- // Node status was updated by nodecontroller 10min ago
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- secondNodeNewStatus: healthyNodeNewStatus,
- expectedEvictPods: false,
- description: "Node created long time ago, node controller posted Unknown for a short period of time.",
- },
- // Node created long time ago, node controller posted Unknown for a long period of time.
- {
- fakeNodeHandler: &testutil.FakeNodeHandler{
- Existing: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionUnknown,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node1",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- },
- Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
- },
- daemonSets: nil,
- timeToPass: 60 * time.Minute,
- newNodeStatus: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionUnknown,
- // Node status was updated by nodecontroller 1hr ago
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- secondNodeNewStatus: healthyNodeNewStatus,
- expectedEvictPods: true,
- description: "Node created long time ago, node controller posted Unknown for a long period of time.",
- },
- }
- for _, item := range table {
- nodeController, _ := newNodeLifecycleControllerFromClient(
- item.fakeNodeHandler,
- evictionTimeout,
- testRateLimiterQPS,
- testRateLimiterQPS,
- testLargeClusterThreshold,
- testUnhealthyThreshold,
- testNodeMonitorGracePeriod,
- testNodeStartupGracePeriod,
- testNodeMonitorPeriod,
- false)
- nodeController.now = func() metav1.Time { return fakeNow }
- nodeController.recorder = testutil.NewFakeRecorder()
- nodeController.getPodsAssignedToNode = fakeGetPodsAssignedToNode(item.fakeNodeHandler.Clientset)
- for _, ds := range item.daemonSets {
- nodeController.daemonSetInformer.Informer().GetStore().Add(&ds)
- }
- if err := nodeController.syncNodeStore(item.fakeNodeHandler); err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if err := nodeController.monitorNodeHealth(); err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if item.timeToPass > 0 {
- nodeController.now = func() metav1.Time { return metav1.Time{Time: fakeNow.Add(item.timeToPass)} }
- item.fakeNodeHandler.Existing[0].Status = item.newNodeStatus
- item.fakeNodeHandler.Existing[1].Status = item.secondNodeNewStatus
- }
- if len(item.fakeNodeHandler.Existing[0].Labels) == 0 && len(item.fakeNodeHandler.Existing[1].Labels) == 0 {
- item.fakeNodeHandler.Existing[0].Labels = labels
- item.fakeNodeHandler.Existing[1].Labels = labels
- }
- if err := nodeController.syncNodeStore(item.fakeNodeHandler); err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if err := nodeController.monitorNodeHealth(); err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- zones := testutil.GetZones(item.fakeNodeHandler)
- for _, zone := range zones {
- if _, ok := nodeController.zonePodEvictor[zone]; ok {
- nodeController.zonePodEvictor[zone].Try(func(value scheduler.TimedValue) (bool, time.Duration) {
- nodeUID, _ := value.UID.(string)
- pods, err := nodeController.getPodsAssignedToNode(value.Value)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- t.Logf("listed pods %d for node %v", len(pods), value.Value)
- nodeutil.DeletePods(item.fakeNodeHandler, pods, nodeController.recorder, value.Value, nodeUID, nodeController.daemonSetInformer.Lister())
- return true, 0
- })
- } else {
- t.Fatalf("Zone %v was unitialized!", zone)
- }
- }
- podEvicted := false
- for _, action := range item.fakeNodeHandler.Actions() {
- if action.GetVerb() == "delete" && action.GetResource().Resource == "pods" {
- podEvicted = true
- }
- }
- if item.expectedEvictPods != podEvicted {
- t.Errorf("expected pod eviction: %+v, got %+v for %+v", item.expectedEvictPods,
- podEvicted, item.description)
- }
- }
- }
- func TestPodStatusChange(t *testing.T) {
- fakeNow := metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC)
- evictionTimeout := 10 * time.Minute
- // Because of the logic that prevents NC from evicting anything when all Nodes are NotReady
- // we need second healthy node in tests. Because of how the tests are written we need to update
- // the status of this Node.
- healthyNodeNewStatus := v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- // Node status has just been updated, and is NotReady for 10min.
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 9, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- }
- // Node created long time ago, node controller posted Unknown for a long period of time.
- table := []struct {
- fakeNodeHandler *testutil.FakeNodeHandler
- timeToPass time.Duration
- newNodeStatus v1.NodeStatus
- secondNodeNewStatus v1.NodeStatus
- expectedPodUpdate bool
- expectedReason string
- description string
- }{
- {
- fakeNodeHandler: &testutil.FakeNodeHandler{
- Existing: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionUnknown,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node1",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- },
- Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
- },
- timeToPass: 60 * time.Minute,
- newNodeStatus: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionUnknown,
- // Node status was updated by nodecontroller 1hr ago
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- secondNodeNewStatus: healthyNodeNewStatus,
- expectedPodUpdate: true,
- expectedReason: node.NodeUnreachablePodReason,
- description: "Node created long time ago, node controller posted Unknown for a " +
- "long period of time, the pod status must include reason for termination.",
- },
- }
- for _, item := range table {
- nodeController, _ := newNodeLifecycleControllerFromClient(
- item.fakeNodeHandler,
- evictionTimeout,
- testRateLimiterQPS,
- testRateLimiterQPS,
- testLargeClusterThreshold,
- testUnhealthyThreshold,
- testNodeMonitorGracePeriod,
- testNodeStartupGracePeriod,
- testNodeMonitorPeriod,
- false)
- nodeController.now = func() metav1.Time { return fakeNow }
- nodeController.recorder = testutil.NewFakeRecorder()
- nodeController.getPodsAssignedToNode = fakeGetPodsAssignedToNode(item.fakeNodeHandler.Clientset)
- if err := nodeController.syncNodeStore(item.fakeNodeHandler); err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if err := nodeController.monitorNodeHealth(); err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if item.timeToPass > 0 {
- nodeController.now = func() metav1.Time { return metav1.Time{Time: fakeNow.Add(item.timeToPass)} }
- item.fakeNodeHandler.Existing[0].Status = item.newNodeStatus
- item.fakeNodeHandler.Existing[1].Status = item.secondNodeNewStatus
- }
- if err := nodeController.syncNodeStore(item.fakeNodeHandler); err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if err := nodeController.monitorNodeHealth(); err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- zones := testutil.GetZones(item.fakeNodeHandler)
- for _, zone := range zones {
- nodeController.zonePodEvictor[zone].Try(func(value scheduler.TimedValue) (bool, time.Duration) {
- nodeUID, _ := value.UID.(string)
- pods, err := nodeController.getPodsAssignedToNode(value.Value)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- nodeutil.DeletePods(item.fakeNodeHandler, pods, nodeController.recorder, value.Value, nodeUID, nodeController.daemonSetStore)
- return true, 0
- })
- }
- podReasonUpdate := false
- for _, action := range item.fakeNodeHandler.Actions() {
- if action.GetVerb() == "update" && action.GetResource().Resource == "pods" {
- updateReason := action.(testcore.UpdateActionImpl).GetObject().(*v1.Pod).Status.Reason
- podReasonUpdate = true
- if updateReason != item.expectedReason {
- t.Errorf("expected pod status reason: %+v, got %+v for %+v", item.expectedReason, updateReason, item.description)
- }
- }
- }
- if podReasonUpdate != item.expectedPodUpdate {
- t.Errorf("expected pod update: %+v, got %+v for %+v", item.expectedPodUpdate, podReasonUpdate, item.description)
- }
- }
- }
- func TestMonitorNodeHealthEvictPodsWithDisruption(t *testing.T) {
- fakeNow := metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC)
- evictionTimeout := 10 * time.Minute
- timeToPass := 60 * time.Minute
- // Because of the logic that prevents NC from evicting anything when all Nodes are NotReady
- // we need second healthy node in tests. Because of how the tests are written we need to update
- // the status of this Node.
- healthyNodeNewStatus := v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 13, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- }
- unhealthyNodeNewStatus := v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionUnknown,
- // Node status was updated by nodecontroller 1hr ago
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- }
- table := []struct {
- nodeList []*v1.Node
- podList []v1.Pod
- updatedNodeStatuses []v1.NodeStatus
- expectedInitialStates map[string]ZoneState
- expectedFollowingStates map[string]ZoneState
- expectedEvictPods bool
- description string
- }{
- // NetworkDisruption: Node created long time ago, node controller posted Unknown for a long period of time on both Nodes.
- // Only zone is down - eviction shouldn't take place
- {
- nodeList: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionUnknown,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node1",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionUnknown,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- },
- podList: []v1.Pod{*testutil.NewPod("pod0", "node0")},
- updatedNodeStatuses: []v1.NodeStatus{
- unhealthyNodeNewStatus,
- unhealthyNodeNewStatus,
- },
- expectedInitialStates: map[string]ZoneState{testutil.CreateZoneID("region1", "zone1"): stateFullDisruption},
- expectedFollowingStates: map[string]ZoneState{testutil.CreateZoneID("region1", "zone1"): stateFullDisruption},
- expectedEvictPods: false,
- description: "Network Disruption: Only zone is down - eviction shouldn't take place.",
- },
- // NetworkDisruption: Node created long time ago, node controller posted Unknown for a long period of time on both Nodes.
- // Both zones down - eviction shouldn't take place
- {
- nodeList: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionUnknown,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node1",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region2",
- v1.LabelZoneFailureDomainStable: "zone2",
- v1.LabelZoneRegion: "region2",
- v1.LabelZoneFailureDomain: "zone2",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionUnknown,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- },
- podList: []v1.Pod{*testutil.NewPod("pod0", "node0")},
- updatedNodeStatuses: []v1.NodeStatus{
- unhealthyNodeNewStatus,
- unhealthyNodeNewStatus,
- },
- expectedInitialStates: map[string]ZoneState{
- testutil.CreateZoneID("region1", "zone1"): stateFullDisruption,
- testutil.CreateZoneID("region2", "zone2"): stateFullDisruption,
- },
- expectedFollowingStates: map[string]ZoneState{
- testutil.CreateZoneID("region1", "zone1"): stateFullDisruption,
- testutil.CreateZoneID("region2", "zone2"): stateFullDisruption,
- },
- expectedEvictPods: false,
- description: "Network Disruption: Both zones down - eviction shouldn't take place.",
- },
- // NetworkDisruption: Node created long time ago, node controller posted Unknown for a long period of time on both Nodes.
- // One zone is down - eviction should take place
- {
- nodeList: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionUnknown,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node1",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone2",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone2",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- },
- podList: []v1.Pod{*testutil.NewPod("pod0", "node0")},
- updatedNodeStatuses: []v1.NodeStatus{
- unhealthyNodeNewStatus,
- healthyNodeNewStatus,
- },
- expectedInitialStates: map[string]ZoneState{
- testutil.CreateZoneID("region1", "zone1"): stateFullDisruption,
- testutil.CreateZoneID("region1", "zone2"): stateNormal,
- },
- expectedFollowingStates: map[string]ZoneState{
- testutil.CreateZoneID("region1", "zone1"): stateFullDisruption,
- testutil.CreateZoneID("region1", "zone2"): stateNormal,
- },
- expectedEvictPods: true,
- description: "Network Disruption: One zone is down - eviction should take place.",
- },
- // NetworkDisruption: Node created long time ago, node controller posted Unknown for a long period
- // of on first Node, eviction should stop even though -master Node is healthy.
- {
- nodeList: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionUnknown,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node-master",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- },
- podList: []v1.Pod{*testutil.NewPod("pod0", "node0")},
- updatedNodeStatuses: []v1.NodeStatus{
- unhealthyNodeNewStatus,
- healthyNodeNewStatus,
- },
- expectedInitialStates: map[string]ZoneState{
- testutil.CreateZoneID("region1", "zone1"): stateFullDisruption,
- },
- expectedFollowingStates: map[string]ZoneState{
- testutil.CreateZoneID("region1", "zone1"): stateFullDisruption,
- },
- expectedEvictPods: false,
- description: "NetworkDisruption: eviction should stop, only -master Node is healthy",
- },
- // NetworkDisruption: Node created long time ago, node controller posted Unknown for a long period of time on both Nodes.
- // Initially both zones down, one comes back - eviction should take place
- {
- nodeList: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionUnknown,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node1",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone2",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone2",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionUnknown,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- },
- podList: []v1.Pod{*testutil.NewPod("pod0", "node0")},
- updatedNodeStatuses: []v1.NodeStatus{
- unhealthyNodeNewStatus,
- healthyNodeNewStatus,
- },
- expectedInitialStates: map[string]ZoneState{
- testutil.CreateZoneID("region1", "zone1"): stateFullDisruption,
- testutil.CreateZoneID("region1", "zone2"): stateFullDisruption,
- },
- expectedFollowingStates: map[string]ZoneState{
- testutil.CreateZoneID("region1", "zone1"): stateFullDisruption,
- testutil.CreateZoneID("region1", "zone2"): stateNormal,
- },
- expectedEvictPods: true,
- description: "Initially both zones down, one comes back - eviction should take place",
- },
- // NetworkDisruption: Node created long time ago, node controller posted Unknown for a long period of time on both Nodes.
- // Zone is partially disrupted - eviction should take place
- {
- nodeList: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionUnknown,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node1",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionUnknown,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node2",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionUnknown,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node3",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node4",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- },
- podList: []v1.Pod{*testutil.NewPod("pod0", "node0")},
- updatedNodeStatuses: []v1.NodeStatus{
- unhealthyNodeNewStatus,
- unhealthyNodeNewStatus,
- unhealthyNodeNewStatus,
- healthyNodeNewStatus,
- healthyNodeNewStatus,
- },
- expectedInitialStates: map[string]ZoneState{
- testutil.CreateZoneID("region1", "zone1"): statePartialDisruption,
- },
- expectedFollowingStates: map[string]ZoneState{
- testutil.CreateZoneID("region1", "zone1"): statePartialDisruption,
- },
- expectedEvictPods: true,
- description: "Zone is partially disrupted - eviction should take place.",
- },
- }
- for _, item := range table {
- fakeNodeHandler := &testutil.FakeNodeHandler{
- Existing: item.nodeList,
- Clientset: fake.NewSimpleClientset(&v1.PodList{Items: item.podList}),
- }
- nodeController, _ := newNodeLifecycleControllerFromClient(
- fakeNodeHandler,
- evictionTimeout,
- testRateLimiterQPS,
- testRateLimiterQPS,
- testLargeClusterThreshold,
- testUnhealthyThreshold,
- testNodeMonitorGracePeriod,
- testNodeStartupGracePeriod,
- testNodeMonitorPeriod,
- false)
- nodeController.now = func() metav1.Time { return fakeNow }
- nodeController.recorder = testutil.NewFakeRecorder()
- nodeController.getPodsAssignedToNode = fakeGetPodsAssignedToNode(fakeNodeHandler.Clientset)
- nodeController.enterPartialDisruptionFunc = func(nodeNum int) float32 {
- return testRateLimiterQPS
- }
- nodeController.enterFullDisruptionFunc = func(nodeNum int) float32 {
- return testRateLimiterQPS
- }
- if err := nodeController.syncNodeStore(fakeNodeHandler); err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if err := nodeController.monitorNodeHealth(); err != nil {
- t.Errorf("%v: unexpected error: %v", item.description, err)
- }
- for zone, state := range item.expectedInitialStates {
- if state != nodeController.zoneStates[zone] {
- t.Errorf("%v: Unexpected zone state: %v: %v instead %v", item.description, zone, nodeController.zoneStates[zone], state)
- }
- }
- nodeController.now = func() metav1.Time { return metav1.Time{Time: fakeNow.Add(timeToPass)} }
- for i := range item.updatedNodeStatuses {
- fakeNodeHandler.Existing[i].Status = item.updatedNodeStatuses[i]
- }
- if err := nodeController.syncNodeStore(fakeNodeHandler); err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if err := nodeController.monitorNodeHealth(); err != nil {
- t.Errorf("%v: unexpected error: %v", item.description, err)
- }
- for zone, state := range item.expectedFollowingStates {
- if state != nodeController.zoneStates[zone] {
- t.Errorf("%v: Unexpected zone state: %v: %v instead %v", item.description, zone, nodeController.zoneStates[zone], state)
- }
- }
- var podEvicted bool
- start := time.Now()
- // Infinite loop, used for retrying in case ratelimiter fails to reload for Try function.
- // this breaks when we have the status that we need for test case or when we don't see the
- // intended result after 1 minute.
- for {
- podEvicted = nodeController.doEviction(fakeNodeHandler)
- if podEvicted == item.expectedEvictPods || time.Since(start) > 1*time.Minute {
- break
- }
- }
- if item.expectedEvictPods != podEvicted {
- t.Errorf("%v: expected pod eviction: %+v, got %+v", item.description, item.expectedEvictPods, podEvicted)
- }
- }
- }
- func TestMonitorNodeHealthUpdateStatus(t *testing.T) {
- fakeNow := metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC)
- table := []struct {
- fakeNodeHandler *testutil.FakeNodeHandler
- timeToPass time.Duration
- newNodeStatus v1.NodeStatus
- expectedRequestCount int
- expectedNodes []*v1.Node
- expectedPodStatusUpdate bool
- }{
- // Node created long time ago, without status:
- // Expect Unknown status posted from node controller.
- {
- fakeNodeHandler: &testutil.FakeNodeHandler{
- Existing: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- },
- },
- },
- Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
- },
- expectedRequestCount: 2, // List+Update
- expectedNodes: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionUnknown,
- Reason: "NodeStatusNeverUpdated",
- Message: "Kubelet never posted node status.",
- LastHeartbeatTime: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- LastTransitionTime: fakeNow,
- },
- {
- Type: v1.NodeMemoryPressure,
- Status: v1.ConditionUnknown,
- Reason: "NodeStatusNeverUpdated",
- Message: "Kubelet never posted node status.",
- LastHeartbeatTime: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- LastTransitionTime: fakeNow,
- },
- {
- Type: v1.NodeDiskPressure,
- Status: v1.ConditionUnknown,
- Reason: "NodeStatusNeverUpdated",
- Message: "Kubelet never posted node status.",
- LastHeartbeatTime: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- LastTransitionTime: fakeNow,
- },
- {
- Type: v1.NodePIDPressure,
- Status: v1.ConditionUnknown,
- Reason: "NodeStatusNeverUpdated",
- Message: "Kubelet never posted node status.",
- LastHeartbeatTime: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- LastTransitionTime: fakeNow,
- },
- },
- },
- },
- },
- expectedPodStatusUpdate: false, // Pod was never scheduled
- },
- // Node created recently, without status.
- // Expect no action from node controller (within startup grace period).
- {
- fakeNodeHandler: &testutil.FakeNodeHandler{
- Existing: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: fakeNow,
- },
- },
- },
- Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
- },
- expectedRequestCount: 1, // List
- expectedNodes: nil,
- expectedPodStatusUpdate: false,
- },
- // Node created long time ago, with status updated by kubelet exceeds grace period.
- // Expect Unknown status posted from node controller.
- {
- fakeNodeHandler: &testutil.FakeNodeHandler{
- Existing: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- // Node status hasn't been updated for 1hr.
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- Capacity: v1.ResourceList{
- v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
- v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
- },
- },
- },
- },
- Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
- },
- expectedRequestCount: 3, // (List+)List+Update
- timeToPass: time.Hour,
- newNodeStatus: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- // Node status hasn't been updated for 1hr.
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- Capacity: v1.ResourceList{
- v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
- v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
- },
- },
- expectedNodes: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionUnknown,
- Reason: "NodeStatusUnknown",
- Message: "Kubelet stopped posting node status.",
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Time{Time: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC).Add(time.Hour)},
- },
- {
- Type: v1.NodeMemoryPressure,
- Status: v1.ConditionUnknown,
- Reason: "NodeStatusNeverUpdated",
- Message: "Kubelet never posted node status.",
- LastHeartbeatTime: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), // should default to node creation time if condition was never updated
- LastTransitionTime: metav1.Time{Time: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC).Add(time.Hour)},
- },
- {
- Type: v1.NodeDiskPressure,
- Status: v1.ConditionUnknown,
- Reason: "NodeStatusNeverUpdated",
- Message: "Kubelet never posted node status.",
- LastHeartbeatTime: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), // should default to node creation time if condition was never updated
- LastTransitionTime: metav1.Time{Time: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC).Add(time.Hour)},
- },
- {
- Type: v1.NodePIDPressure,
- Status: v1.ConditionUnknown,
- Reason: "NodeStatusNeverUpdated",
- Message: "Kubelet never posted node status.",
- LastHeartbeatTime: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), // should default to node creation time if condition was never updated
- LastTransitionTime: metav1.Time{Time: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC).Add(time.Hour)},
- },
- },
- Capacity: v1.ResourceList{
- v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
- v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
- },
- },
- },
- },
- expectedPodStatusUpdate: true,
- },
- // Node created long time ago, with status updated recently.
- // Expect no action from node controller (within monitor grace period).
- {
- fakeNodeHandler: &testutil.FakeNodeHandler{
- Existing: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- // Node status has just been updated.
- LastHeartbeatTime: fakeNow,
- LastTransitionTime: fakeNow,
- },
- },
- Capacity: v1.ResourceList{
- v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
- v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
- },
- },
- },
- },
- Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
- },
- expectedRequestCount: 1, // List
- expectedNodes: nil,
- expectedPodStatusUpdate: false,
- },
- }
- for i, item := range table {
- nodeController, _ := newNodeLifecycleControllerFromClient(
- item.fakeNodeHandler,
- 5*time.Minute,
- testRateLimiterQPS,
- testRateLimiterQPS,
- testLargeClusterThreshold,
- testUnhealthyThreshold,
- testNodeMonitorGracePeriod,
- testNodeStartupGracePeriod,
- testNodeMonitorPeriod,
- false)
- nodeController.now = func() metav1.Time { return fakeNow }
- nodeController.recorder = testutil.NewFakeRecorder()
- nodeController.getPodsAssignedToNode = fakeGetPodsAssignedToNode(item.fakeNodeHandler.Clientset)
- if err := nodeController.syncNodeStore(item.fakeNodeHandler); err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if err := nodeController.monitorNodeHealth(); err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if item.timeToPass > 0 {
- nodeController.now = func() metav1.Time { return metav1.Time{Time: fakeNow.Add(item.timeToPass)} }
- item.fakeNodeHandler.Existing[0].Status = item.newNodeStatus
- if err := nodeController.syncNodeStore(item.fakeNodeHandler); err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if err := nodeController.monitorNodeHealth(); err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- }
- if item.expectedRequestCount != item.fakeNodeHandler.RequestCount {
- t.Errorf("expected %v call, but got %v.", item.expectedRequestCount, item.fakeNodeHandler.RequestCount)
- }
- if len(item.fakeNodeHandler.UpdatedNodes) > 0 && !apiequality.Semantic.DeepEqual(item.expectedNodes, item.fakeNodeHandler.UpdatedNodes) {
- t.Errorf("Case[%d] unexpected nodes: %s", i, diff.ObjectDiff(item.expectedNodes[0], item.fakeNodeHandler.UpdatedNodes[0]))
- }
- if len(item.fakeNodeHandler.UpdatedNodeStatuses) > 0 && !apiequality.Semantic.DeepEqual(item.expectedNodes, item.fakeNodeHandler.UpdatedNodeStatuses) {
- t.Errorf("Case[%d] unexpected nodes: %s", i, diff.ObjectDiff(item.expectedNodes[0], item.fakeNodeHandler.UpdatedNodeStatuses[0]))
- }
- podStatusUpdated := false
- for _, action := range item.fakeNodeHandler.Actions() {
- if action.GetVerb() == "update" && action.GetResource().Resource == "pods" && action.GetSubresource() == "status" {
- podStatusUpdated = true
- }
- }
- if podStatusUpdated != item.expectedPodStatusUpdate {
- t.Errorf("Case[%d] expect pod status updated to be %v, but got %v", i, item.expectedPodStatusUpdate, podStatusUpdated)
- }
- }
- }
- func TestMonitorNodeHealthUpdateNodeAndPodStatusWithLease(t *testing.T) {
- nodeCreationTime := metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC)
- fakeNow := metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC)
- testcases := []struct {
- description string
- fakeNodeHandler *testutil.FakeNodeHandler
- lease *coordv1.Lease
- timeToPass time.Duration
- newNodeStatus v1.NodeStatus
- newLease *coordv1.Lease
- expectedRequestCount int
- expectedNodes []*v1.Node
- expectedPodStatusUpdate bool
- }{
- // Node created recently, without status. Node lease is missing.
- // Expect no action from node controller (within startup grace period).
- {
- description: "Node created recently, without status. Node lease is missing.",
- fakeNodeHandler: &testutil.FakeNodeHandler{
- Existing: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: fakeNow,
- },
- },
- },
- Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
- },
- expectedRequestCount: 1, // List
- expectedNodes: nil,
- expectedPodStatusUpdate: false,
- },
- // Node created recently, without status. Node lease is renewed recently.
- // Expect no action from node controller (within startup grace period).
- {
- description: "Node created recently, without status. Node lease is renewed recently.",
- fakeNodeHandler: &testutil.FakeNodeHandler{
- Existing: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: fakeNow,
- },
- },
- },
- Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
- },
- lease: createNodeLease("node0", metav1.NewMicroTime(fakeNow.Time)),
- expectedRequestCount: 1, // List
- expectedNodes: nil,
- expectedPodStatusUpdate: false,
- },
- // Node created long time ago, without status. Node lease is missing.
- // Expect Unknown status posted from node controller.
- {
- description: "Node created long time ago, without status. Node lease is missing.",
- fakeNodeHandler: &testutil.FakeNodeHandler{
- Existing: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: nodeCreationTime,
- },
- },
- },
- Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
- },
- expectedRequestCount: 2, // List+Update
- expectedNodes: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: nodeCreationTime,
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionUnknown,
- Reason: "NodeStatusNeverUpdated",
- Message: "Kubelet never posted node status.",
- LastHeartbeatTime: nodeCreationTime,
- LastTransitionTime: fakeNow,
- },
- {
- Type: v1.NodeMemoryPressure,
- Status: v1.ConditionUnknown,
- Reason: "NodeStatusNeverUpdated",
- Message: "Kubelet never posted node status.",
- LastHeartbeatTime: nodeCreationTime,
- LastTransitionTime: fakeNow,
- },
- {
- Type: v1.NodeDiskPressure,
- Status: v1.ConditionUnknown,
- Reason: "NodeStatusNeverUpdated",
- Message: "Kubelet never posted node status.",
- LastHeartbeatTime: nodeCreationTime,
- LastTransitionTime: fakeNow,
- },
- {
- Type: v1.NodePIDPressure,
- Status: v1.ConditionUnknown,
- Reason: "NodeStatusNeverUpdated",
- Message: "Kubelet never posted node status.",
- LastHeartbeatTime: nodeCreationTime,
- LastTransitionTime: fakeNow,
- },
- },
- },
- },
- },
- expectedPodStatusUpdate: false, // Pod was never scheduled because the node was never ready.
- },
- // Node created long time ago, without status. Node lease is renewed recently.
- // Expect no action from node controller (within monitor grace period).
- {
- description: "Node created long time ago, without status. Node lease is renewed recently.",
- fakeNodeHandler: &testutil.FakeNodeHandler{
- Existing: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: nodeCreationTime,
- },
- },
- },
- Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
- },
- lease: createNodeLease("node0", metav1.NewMicroTime(fakeNow.Time)),
- timeToPass: time.Hour,
- newLease: createNodeLease("node0", metav1.NewMicroTime(fakeNow.Time.Add(time.Hour))), // Lease is renewed after 1 hour.
- expectedRequestCount: 2, // List+List
- expectedNodes: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: nodeCreationTime,
- },
- },
- },
- expectedPodStatusUpdate: false,
- },
- // Node created long time ago, without status. Node lease is expired.
- // Expect Unknown status posted from node controller.
- {
- description: "Node created long time ago, without status. Node lease is expired.",
- fakeNodeHandler: &testutil.FakeNodeHandler{
- Existing: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: nodeCreationTime,
- },
- },
- },
- Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
- },
- lease: createNodeLease("node0", metav1.NewMicroTime(fakeNow.Time)),
- timeToPass: time.Hour,
- newLease: createNodeLease("node0", metav1.NewMicroTime(fakeNow.Time)), // Lease is not renewed after 1 hour.
- expectedRequestCount: 3, // List+List+Update
- expectedNodes: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: nodeCreationTime,
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionUnknown,
- Reason: "NodeStatusNeverUpdated",
- Message: "Kubelet never posted node status.",
- LastHeartbeatTime: nodeCreationTime,
- LastTransitionTime: metav1.Time{Time: fakeNow.Add(time.Hour)},
- },
- {
- Type: v1.NodeMemoryPressure,
- Status: v1.ConditionUnknown,
- Reason: "NodeStatusNeverUpdated",
- Message: "Kubelet never posted node status.",
- LastHeartbeatTime: nodeCreationTime,
- LastTransitionTime: metav1.Time{Time: fakeNow.Add(time.Hour)},
- },
- {
- Type: v1.NodeDiskPressure,
- Status: v1.ConditionUnknown,
- Reason: "NodeStatusNeverUpdated",
- Message: "Kubelet never posted node status.",
- LastHeartbeatTime: nodeCreationTime,
- LastTransitionTime: metav1.Time{Time: fakeNow.Add(time.Hour)},
- },
- {
- Type: v1.NodePIDPressure,
- Status: v1.ConditionUnknown,
- Reason: "NodeStatusNeverUpdated",
- Message: "Kubelet never posted node status.",
- LastHeartbeatTime: nodeCreationTime,
- LastTransitionTime: metav1.Time{Time: fakeNow.Add(time.Hour)},
- },
- },
- },
- },
- },
- expectedPodStatusUpdate: false,
- },
- // Node created long time ago, with status updated by kubelet exceeds grace period. Node lease is renewed.
- // Expect no action from node controller (within monitor grace period).
- {
- description: "Node created long time ago, with status updated by kubelet exceeds grace period. Node lease is renewed.",
- fakeNodeHandler: &testutil.FakeNodeHandler{
- Existing: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: nodeCreationTime,
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- LastHeartbeatTime: fakeNow,
- LastTransitionTime: fakeNow,
- },
- {
- Type: v1.NodeDiskPressure,
- Status: v1.ConditionFalse,
- LastHeartbeatTime: fakeNow,
- LastTransitionTime: fakeNow,
- },
- },
- Capacity: v1.ResourceList{
- v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
- v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
- },
- },
- },
- },
- Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
- },
- lease: createNodeLease("node0", metav1.NewMicroTime(fakeNow.Time)),
- expectedRequestCount: 2, // List+List
- timeToPass: time.Hour,
- newNodeStatus: v1.NodeStatus{
- // Node status hasn't been updated for 1 hour.
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- LastHeartbeatTime: fakeNow,
- LastTransitionTime: fakeNow,
- },
- {
- Type: v1.NodeDiskPressure,
- Status: v1.ConditionFalse,
- LastHeartbeatTime: fakeNow,
- LastTransitionTime: fakeNow,
- },
- },
- Capacity: v1.ResourceList{
- v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
- v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
- },
- },
- newLease: createNodeLease("node0", metav1.NewMicroTime(fakeNow.Time.Add(time.Hour))), // Lease is renewed after 1 hour.
- expectedNodes: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: nodeCreationTime,
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- LastHeartbeatTime: fakeNow,
- LastTransitionTime: fakeNow,
- },
- {
- Type: v1.NodeDiskPressure,
- Status: v1.ConditionFalse,
- LastHeartbeatTime: fakeNow,
- LastTransitionTime: fakeNow,
- },
- },
- Capacity: v1.ResourceList{
- v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
- v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
- },
- },
- },
- },
- expectedPodStatusUpdate: false,
- },
- // Node created long time ago, with status updated by kubelet recently. Node lease is expired.
- // Expect no action from node controller (within monitor grace period).
- {
- description: "Node created long time ago, with status updated by kubelet recently. Node lease is expired.",
- fakeNodeHandler: &testutil.FakeNodeHandler{
- Existing: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: nodeCreationTime,
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- LastHeartbeatTime: fakeNow,
- LastTransitionTime: fakeNow,
- },
- {
- Type: v1.NodeDiskPressure,
- Status: v1.ConditionFalse,
- LastHeartbeatTime: fakeNow,
- LastTransitionTime: fakeNow,
- },
- },
- Capacity: v1.ResourceList{
- v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
- v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
- },
- },
- },
- },
- Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
- },
- lease: createNodeLease("node0", metav1.NewMicroTime(fakeNow.Time)),
- expectedRequestCount: 2, // List+List
- timeToPass: time.Hour,
- newNodeStatus: v1.NodeStatus{
- // Node status is updated after 1 hour.
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- LastHeartbeatTime: metav1.Time{Time: fakeNow.Add(time.Hour)},
- LastTransitionTime: fakeNow,
- },
- {
- Type: v1.NodeDiskPressure,
- Status: v1.ConditionFalse,
- LastHeartbeatTime: metav1.Time{Time: fakeNow.Add(time.Hour)},
- LastTransitionTime: fakeNow,
- },
- },
- Capacity: v1.ResourceList{
- v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
- v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
- },
- },
- newLease: createNodeLease("node0", metav1.NewMicroTime(fakeNow.Time)), // Lease is not renewed after 1 hour.
- expectedNodes: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: nodeCreationTime,
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- LastHeartbeatTime: metav1.Time{Time: fakeNow.Add(time.Hour)},
- LastTransitionTime: fakeNow,
- },
- {
- Type: v1.NodeDiskPressure,
- Status: v1.ConditionFalse,
- LastHeartbeatTime: metav1.Time{Time: fakeNow.Add(time.Hour)},
- LastTransitionTime: fakeNow,
- },
- },
- Capacity: v1.ResourceList{
- v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
- v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
- },
- },
- },
- },
- expectedPodStatusUpdate: false,
- },
- // Node created long time ago, with status updated by kubelet exceeds grace period. Node lease is also expired.
- // Expect Unknown status posted from node controller.
- {
- description: "Node created long time ago, with status updated by kubelet exceeds grace period. Node lease is also expired.",
- fakeNodeHandler: &testutil.FakeNodeHandler{
- Existing: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: nodeCreationTime,
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- LastHeartbeatTime: fakeNow,
- LastTransitionTime: fakeNow,
- },
- },
- Capacity: v1.ResourceList{
- v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
- v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
- },
- },
- },
- },
- Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
- },
- lease: createNodeLease("node0", metav1.NewMicroTime(fakeNow.Time)),
- expectedRequestCount: 3, // List+List+Update
- timeToPass: time.Hour,
- newNodeStatus: v1.NodeStatus{
- // Node status hasn't been updated for 1 hour.
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- LastHeartbeatTime: fakeNow,
- LastTransitionTime: fakeNow,
- },
- },
- Capacity: v1.ResourceList{
- v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
- v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
- },
- },
- newLease: createNodeLease("node0", metav1.NewMicroTime(fakeNow.Time)), // Lease is not renewed after 1 hour.
- expectedNodes: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: nodeCreationTime,
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionUnknown,
- Reason: "NodeStatusUnknown",
- Message: "Kubelet stopped posting node status.",
- LastHeartbeatTime: fakeNow,
- LastTransitionTime: metav1.Time{Time: fakeNow.Add(time.Hour)},
- },
- {
- Type: v1.NodeMemoryPressure,
- Status: v1.ConditionUnknown,
- Reason: "NodeStatusNeverUpdated",
- Message: "Kubelet never posted node status.",
- LastHeartbeatTime: nodeCreationTime, // should default to node creation time if condition was never updated
- LastTransitionTime: metav1.Time{Time: fakeNow.Add(time.Hour)},
- },
- {
- Type: v1.NodeDiskPressure,
- Status: v1.ConditionUnknown,
- Reason: "NodeStatusNeverUpdated",
- Message: "Kubelet never posted node status.",
- LastHeartbeatTime: nodeCreationTime, // should default to node creation time if condition was never updated
- LastTransitionTime: metav1.Time{Time: fakeNow.Add(time.Hour)},
- },
- {
- Type: v1.NodePIDPressure,
- Status: v1.ConditionUnknown,
- Reason: "NodeStatusNeverUpdated",
- Message: "Kubelet never posted node status.",
- LastHeartbeatTime: nodeCreationTime, // should default to node creation time if condition was never updated
- LastTransitionTime: metav1.Time{Time: fakeNow.Add(time.Hour)},
- },
- },
- Capacity: v1.ResourceList{
- v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
- v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
- },
- },
- },
- },
- expectedPodStatusUpdate: true,
- },
- }
- for _, item := range testcases {
- t.Run(item.description, func(t *testing.T) {
- nodeController, _ := newNodeLifecycleControllerFromClient(
- item.fakeNodeHandler,
- 5*time.Minute,
- testRateLimiterQPS,
- testRateLimiterQPS,
- testLargeClusterThreshold,
- testUnhealthyThreshold,
- testNodeMonitorGracePeriod,
- testNodeStartupGracePeriod,
- testNodeMonitorPeriod,
- false)
- nodeController.now = func() metav1.Time { return fakeNow }
- nodeController.recorder = testutil.NewFakeRecorder()
- nodeController.getPodsAssignedToNode = fakeGetPodsAssignedToNode(item.fakeNodeHandler.Clientset)
- if err := nodeController.syncNodeStore(item.fakeNodeHandler); err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if err := nodeController.syncLeaseStore(item.lease); err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if err := nodeController.monitorNodeHealth(); err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if item.timeToPass > 0 {
- nodeController.now = func() metav1.Time { return metav1.Time{Time: fakeNow.Add(item.timeToPass)} }
- item.fakeNodeHandler.Existing[0].Status = item.newNodeStatus
- if err := nodeController.syncNodeStore(item.fakeNodeHandler); err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if err := nodeController.syncLeaseStore(item.newLease); err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if err := nodeController.monitorNodeHealth(); err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- }
- if item.expectedRequestCount != item.fakeNodeHandler.RequestCount {
- t.Errorf("expected %v call, but got %v.", item.expectedRequestCount, item.fakeNodeHandler.RequestCount)
- }
- if len(item.fakeNodeHandler.UpdatedNodes) > 0 && !apiequality.Semantic.DeepEqual(item.expectedNodes, item.fakeNodeHandler.UpdatedNodes) {
- t.Errorf("unexpected nodes: %s", diff.ObjectDiff(item.expectedNodes[0], item.fakeNodeHandler.UpdatedNodes[0]))
- }
- if len(item.fakeNodeHandler.UpdatedNodeStatuses) > 0 && !apiequality.Semantic.DeepEqual(item.expectedNodes, item.fakeNodeHandler.UpdatedNodeStatuses) {
- t.Errorf("unexpected nodes: %s", diff.ObjectDiff(item.expectedNodes[0], item.fakeNodeHandler.UpdatedNodeStatuses[0]))
- }
- podStatusUpdated := false
- for _, action := range item.fakeNodeHandler.Actions() {
- if action.GetVerb() == "update" && action.GetResource().Resource == "pods" && action.GetSubresource() == "status" {
- podStatusUpdated = true
- }
- }
- if podStatusUpdated != item.expectedPodStatusUpdate {
- t.Errorf("expect pod status updated to be %v, but got %v", item.expectedPodStatusUpdate, podStatusUpdated)
- }
- })
- }
- }
- func TestMonitorNodeHealthMarkPodsNotReady(t *testing.T) {
- fakeNow := metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC)
- table := []struct {
- fakeNodeHandler *testutil.FakeNodeHandler
- timeToPass time.Duration
- newNodeStatus v1.NodeStatus
- expectedPodStatusUpdate bool
- }{
- // Node created recently, without status.
- // Expect no action from node controller (within startup grace period).
- {
- fakeNodeHandler: &testutil.FakeNodeHandler{
- Existing: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: fakeNow,
- },
- },
- },
- Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
- },
- expectedPodStatusUpdate: false,
- },
- // Node created long time ago, with status updated recently.
- // Expect no action from node controller (within monitor grace period).
- {
- fakeNodeHandler: &testutil.FakeNodeHandler{
- Existing: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- // Node status has just been updated.
- LastHeartbeatTime: fakeNow,
- LastTransitionTime: fakeNow,
- },
- },
- Capacity: v1.ResourceList{
- v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
- v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
- },
- },
- },
- },
- Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
- },
- expectedPodStatusUpdate: false,
- },
- // Node created long time ago, with status updated by kubelet exceeds grace period.
- // Expect pods status updated and Unknown node status posted from node controller
- {
- fakeNodeHandler: &testutil.FakeNodeHandler{
- Existing: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- // Node status hasn't been updated for 1hr.
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- Capacity: v1.ResourceList{
- v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
- v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
- },
- },
- },
- },
- Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
- },
- timeToPass: 1 * time.Minute,
- newNodeStatus: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- // Node status hasn't been updated for 1hr.
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- Capacity: v1.ResourceList{
- v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
- v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
- },
- },
- expectedPodStatusUpdate: true,
- },
- }
- for i, item := range table {
- nodeController, _ := newNodeLifecycleControllerFromClient(
- item.fakeNodeHandler,
- 5*time.Minute,
- testRateLimiterQPS,
- testRateLimiterQPS,
- testLargeClusterThreshold,
- testUnhealthyThreshold,
- testNodeMonitorGracePeriod,
- testNodeStartupGracePeriod,
- testNodeMonitorPeriod,
- false)
- nodeController.now = func() metav1.Time { return fakeNow }
- nodeController.recorder = testutil.NewFakeRecorder()
- nodeController.getPodsAssignedToNode = fakeGetPodsAssignedToNode(item.fakeNodeHandler.Clientset)
- if err := nodeController.syncNodeStore(item.fakeNodeHandler); err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if err := nodeController.monitorNodeHealth(); err != nil {
- t.Errorf("Case[%d] unexpected error: %v", i, err)
- }
- if item.timeToPass > 0 {
- nodeController.now = func() metav1.Time { return metav1.Time{Time: fakeNow.Add(item.timeToPass)} }
- item.fakeNodeHandler.Existing[0].Status = item.newNodeStatus
- if err := nodeController.syncNodeStore(item.fakeNodeHandler); err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if err := nodeController.monitorNodeHealth(); err != nil {
- t.Errorf("Case[%d] unexpected error: %v", i, err)
- }
- }
- podStatusUpdated := false
- for _, action := range item.fakeNodeHandler.Actions() {
- if action.GetVerb() == "update" && action.GetResource().Resource == "pods" && action.GetSubresource() == "status" {
- podStatusUpdated = true
- }
- }
- if podStatusUpdated != item.expectedPodStatusUpdate {
- t.Errorf("Case[%d] expect pod status updated to be %v, but got %v", i, item.expectedPodStatusUpdate, podStatusUpdated)
- }
- }
- }
- func TestMonitorNodeHealthMarkPodsNotReadyRetry(t *testing.T) {
- type nodeIteration struct {
- timeToPass time.Duration
- newNodes []*v1.Node
- }
- timeNow := metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC)
- timePlusTwoMinutes := metav1.Date(2015, 1, 1, 12, 0, 2, 0, time.UTC)
- makeNodes := func(status v1.ConditionStatus, lastHeartbeatTime, lastTransitionTime metav1.Time) []*v1.Node {
- return []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: timeNow,
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: status,
- LastHeartbeatTime: lastHeartbeatTime,
- LastTransitionTime: lastTransitionTime,
- },
- },
- },
- },
- }
- }
- table := []struct {
- desc string
- fakeNodeHandler *testutil.FakeNodeHandler
- updateReactor func(action testcore.Action) (bool, runtime.Object, error)
- fakeGetPodsAssignedToNode func(c *fake.Clientset) func(string) ([]*v1.Pod, error)
- nodeIterations []nodeIteration
- expectedPodStatusUpdates int
- }{
- // Node created long time ago, with status updated by kubelet exceeds grace period.
- // First monitorNodeHealth check will update pod status to NotReady.
- // Second monitorNodeHealth check will do no updates (no retry).
- {
- desc: "successful pod status update, no retry required",
- fakeNodeHandler: &testutil.FakeNodeHandler{
- Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
- },
- fakeGetPodsAssignedToNode: fakeGetPodsAssignedToNode,
- nodeIterations: []nodeIteration{
- {
- timeToPass: 0,
- newNodes: makeNodes(v1.ConditionTrue, timeNow, timeNow),
- },
- {
- timeToPass: 1 * time.Minute,
- newNodes: makeNodes(v1.ConditionTrue, timeNow, timeNow),
- },
- {
- timeToPass: 1 * time.Minute,
- newNodes: makeNodes(v1.ConditionFalse, timePlusTwoMinutes, timePlusTwoMinutes),
- },
- },
- expectedPodStatusUpdates: 1,
- },
- // Node created long time ago, with status updated by kubelet exceeds grace period.
- // First monitorNodeHealth check will fail to update pod status to NotReady.
- // Second monitorNodeHealth check will update pod status to NotReady (retry).
- {
- desc: "unsuccessful pod status update, retry required",
- fakeNodeHandler: &testutil.FakeNodeHandler{
- Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
- },
- updateReactor: func() func(action testcore.Action) (bool, runtime.Object, error) {
- i := 0
- return func(action testcore.Action) (bool, runtime.Object, error) {
- if action.GetVerb() == "update" && action.GetResource().Resource == "pods" && action.GetSubresource() == "status" {
- i++
- switch i {
- case 1:
- return true, nil, fmt.Errorf("fake error")
- default:
- return true, testutil.NewPod("pod0", "node0"), nil
- }
- }
- return true, nil, fmt.Errorf("unsupported action")
- }
- }(),
- fakeGetPodsAssignedToNode: fakeGetPodsAssignedToNode,
- nodeIterations: []nodeIteration{
- {
- timeToPass: 0,
- newNodes: makeNodes(v1.ConditionTrue, timeNow, timeNow),
- },
- {
- timeToPass: 1 * time.Minute,
- newNodes: makeNodes(v1.ConditionTrue, timeNow, timeNow),
- },
- {
- timeToPass: 1 * time.Minute,
- newNodes: makeNodes(v1.ConditionFalse, timePlusTwoMinutes, timePlusTwoMinutes),
- },
- },
- expectedPodStatusUpdates: 2, // One failed and one retry.
- },
- // Node created long time ago, with status updated by kubelet exceeds grace period.
- // First monitorNodeHealth check will fail to list pods.
- // Second monitorNodeHealth check will update pod status to NotReady (retry).
- {
- desc: "unsuccessful pod list, retry required",
- fakeNodeHandler: &testutil.FakeNodeHandler{
- Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
- },
- fakeGetPodsAssignedToNode: func(c *fake.Clientset) func(string) ([]*v1.Pod, error) {
- i := 0
- f := fakeGetPodsAssignedToNode(c)
- return func(nodeName string) ([]*v1.Pod, error) {
- i++
- if i == 1 {
- return nil, fmt.Errorf("fake error")
- }
- return f(nodeName)
- }
- },
- nodeIterations: []nodeIteration{
- {
- timeToPass: 0,
- newNodes: makeNodes(v1.ConditionTrue, timeNow, timeNow),
- },
- {
- timeToPass: 1 * time.Minute,
- newNodes: makeNodes(v1.ConditionTrue, timeNow, timeNow),
- },
- {
- timeToPass: 1 * time.Minute,
- newNodes: makeNodes(v1.ConditionFalse, timePlusTwoMinutes, timePlusTwoMinutes),
- },
- },
- expectedPodStatusUpdates: 1,
- },
- }
- for _, item := range table {
- t.Run(item.desc, func(t *testing.T) {
- nodeController, _ := newNodeLifecycleControllerFromClient(
- item.fakeNodeHandler,
- 5*time.Minute,
- testRateLimiterQPS,
- testRateLimiterQPS,
- testLargeClusterThreshold,
- testUnhealthyThreshold,
- testNodeMonitorGracePeriod,
- testNodeStartupGracePeriod,
- testNodeMonitorPeriod,
- false)
- if item.updateReactor != nil {
- item.fakeNodeHandler.Clientset.PrependReactor("update", "pods", item.updateReactor)
- }
- nodeController.now = func() metav1.Time { return timeNow }
- nodeController.recorder = testutil.NewFakeRecorder()
- nodeController.getPodsAssignedToNode = item.fakeGetPodsAssignedToNode(item.fakeNodeHandler.Clientset)
- for _, itertion := range item.nodeIterations {
- nodeController.now = func() metav1.Time { return metav1.Time{Time: timeNow.Add(itertion.timeToPass)} }
- item.fakeNodeHandler.Existing = itertion.newNodes
- if err := nodeController.syncNodeStore(item.fakeNodeHandler); err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if err := nodeController.monitorNodeHealth(); err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- }
- podStatusUpdates := 0
- for _, action := range item.fakeNodeHandler.Actions() {
- if action.GetVerb() == "update" && action.GetResource().Resource == "pods" && action.GetSubresource() == "status" {
- podStatusUpdates++
- }
- }
- if podStatusUpdates != item.expectedPodStatusUpdates {
- t.Errorf("expect pod status updated to happen %d times, but got %d", item.expectedPodStatusUpdates, podStatusUpdates)
- }
- })
- }
- }
- // TestApplyNoExecuteTaints, ensures we just have a NoExecute taint applied to node.
- // NodeController is just responsible for enqueuing the node to tainting queue from which taint manager picks up
- // and evicts the pods on the node.
- func TestApplyNoExecuteTaints(t *testing.T) {
- fakeNow := metav1.Date(2017, 1, 1, 12, 0, 0, 0, time.UTC)
- evictionTimeout := 10 * time.Minute
- fakeNodeHandler := &testutil.FakeNodeHandler{
- Existing: []*v1.Node{
- // Unreachable Taint with effect 'NoExecute' should be applied to this node.
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionUnknown,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- // Because of the logic that prevents NC from evicting anything when all Nodes are NotReady
- // we need second healthy node in tests.
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node1",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- LastHeartbeatTime: metav1.Date(2017, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2017, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- // NotReady Taint with NoExecute effect should be applied to this node.
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node2",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionFalse,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- },
- Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
- }
- healthyNodeNewStatus := v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- LastHeartbeatTime: metav1.Date(2017, 1, 1, 12, 10, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2017, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- }
- originalTaint := UnreachableTaintTemplate
- nodeController, _ := newNodeLifecycleControllerFromClient(
- fakeNodeHandler,
- evictionTimeout,
- testRateLimiterQPS,
- testRateLimiterQPS,
- testLargeClusterThreshold,
- testUnhealthyThreshold,
- testNodeMonitorGracePeriod,
- testNodeStartupGracePeriod,
- testNodeMonitorPeriod,
- true)
- nodeController.now = func() metav1.Time { return fakeNow }
- nodeController.recorder = testutil.NewFakeRecorder()
- nodeController.getPodsAssignedToNode = fakeGetPodsAssignedToNode(fakeNodeHandler.Clientset)
- if err := nodeController.syncNodeStore(fakeNodeHandler); err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if err := nodeController.monitorNodeHealth(); err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- nodeController.doNoExecuteTaintingPass()
- node0, err := fakeNodeHandler.Get(context.TODO(), "node0", metav1.GetOptions{})
- if err != nil {
- t.Errorf("Can't get current node0...")
- return
- }
- if !taintutils.TaintExists(node0.Spec.Taints, UnreachableTaintTemplate) {
- t.Errorf("Can't find taint %v in %v", originalTaint, node0.Spec.Taints)
- }
- node2, err := fakeNodeHandler.Get(context.TODO(), "node2", metav1.GetOptions{})
- if err != nil {
- t.Errorf("Can't get current node2...")
- return
- }
- if !taintutils.TaintExists(node2.Spec.Taints, NotReadyTaintTemplate) {
- t.Errorf("Can't find taint %v in %v", NotReadyTaintTemplate, node2.Spec.Taints)
- }
- // Make node3 healthy again.
- node2.Status = healthyNodeNewStatus
- _, err = fakeNodeHandler.UpdateStatus(context.TODO(), node2, metav1.UpdateOptions{})
- if err != nil {
- t.Errorf(err.Error())
- return
- }
- if err := nodeController.syncNodeStore(fakeNodeHandler); err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if err := nodeController.monitorNodeHealth(); err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- nodeController.doNoExecuteTaintingPass()
- node2, err = fakeNodeHandler.Get(context.TODO(), "node2", metav1.GetOptions{})
- if err != nil {
- t.Errorf("Can't get current node2...")
- return
- }
- // We should not see any taint on the node(especially the Not-Ready taint with NoExecute effect).
- if taintutils.TaintExists(node2.Spec.Taints, NotReadyTaintTemplate) || len(node2.Spec.Taints) > 0 {
- t.Errorf("Found taint %v in %v, which should not be present", NotReadyTaintTemplate, node2.Spec.Taints)
- }
- }
- func TestSwapUnreachableNotReadyTaints(t *testing.T) {
- fakeNow := metav1.Date(2017, 1, 1, 12, 0, 0, 0, time.UTC)
- evictionTimeout := 10 * time.Minute
- fakeNodeHandler := &testutil.FakeNodeHandler{
- Existing: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionUnknown,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- // Because of the logic that prevents NC from evicting anything when all Nodes are NotReady
- // we need second healthy node in tests. Because of how the tests are written we need to update
- // the status of this Node.
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node1",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- LastHeartbeatTime: metav1.Date(2017, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2017, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- },
- Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
- }
- timeToPass := evictionTimeout
- newNodeStatus := v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionFalse,
- // Node status has just been updated, and is NotReady for 10min.
- LastHeartbeatTime: metav1.Date(2017, 1, 1, 12, 9, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2017, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- }
- healthyNodeNewStatus := v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- LastHeartbeatTime: metav1.Date(2017, 1, 1, 12, 10, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2017, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- }
- originalTaint := UnreachableTaintTemplate
- updatedTaint := NotReadyTaintTemplate
- nodeController, _ := newNodeLifecycleControllerFromClient(
- fakeNodeHandler,
- evictionTimeout,
- testRateLimiterQPS,
- testRateLimiterQPS,
- testLargeClusterThreshold,
- testUnhealthyThreshold,
- testNodeMonitorGracePeriod,
- testNodeStartupGracePeriod,
- testNodeMonitorPeriod,
- true)
- nodeController.now = func() metav1.Time { return fakeNow }
- nodeController.recorder = testutil.NewFakeRecorder()
- nodeController.getPodsAssignedToNode = fakeGetPodsAssignedToNode(fakeNodeHandler.Clientset)
- if err := nodeController.syncNodeStore(fakeNodeHandler); err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if err := nodeController.monitorNodeHealth(); err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- nodeController.doNoExecuteTaintingPass()
- node0, err := fakeNodeHandler.Get(context.TODO(), "node0", metav1.GetOptions{})
- if err != nil {
- t.Errorf("Can't get current node0...")
- return
- }
- node1, err := fakeNodeHandler.Get(context.TODO(), "node1", metav1.GetOptions{})
- if err != nil {
- t.Errorf("Can't get current node1...")
- return
- }
- if originalTaint != nil && !taintutils.TaintExists(node0.Spec.Taints, originalTaint) {
- t.Errorf("Can't find taint %v in %v", originalTaint, node0.Spec.Taints)
- }
- nodeController.now = func() metav1.Time { return metav1.Time{Time: fakeNow.Add(timeToPass)} }
- node0.Status = newNodeStatus
- node1.Status = healthyNodeNewStatus
- _, err = fakeNodeHandler.UpdateStatus(context.TODO(), node0, metav1.UpdateOptions{})
- if err != nil {
- t.Errorf(err.Error())
- return
- }
- _, err = fakeNodeHandler.UpdateStatus(context.TODO(), node1, metav1.UpdateOptions{})
- if err != nil {
- t.Errorf(err.Error())
- return
- }
- if err := nodeController.syncNodeStore(fakeNodeHandler); err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if err := nodeController.monitorNodeHealth(); err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- nodeController.doNoExecuteTaintingPass()
- node0, err = fakeNodeHandler.Get(context.TODO(), "node0", metav1.GetOptions{})
- if err != nil {
- t.Errorf("Can't get current node0...")
- return
- }
- if updatedTaint != nil {
- if !taintutils.TaintExists(node0.Spec.Taints, updatedTaint) {
- t.Errorf("Can't find taint %v in %v", updatedTaint, node0.Spec.Taints)
- }
- }
- }
- func TestTaintsNodeByCondition(t *testing.T) {
- fakeNow := metav1.Date(2017, 1, 1, 12, 0, 0, 0, time.UTC)
- evictionTimeout := 10 * time.Minute
- fakeNodeHandler := &testutil.FakeNodeHandler{
- Existing: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- },
- Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
- }
- nodeController, _ := newNodeLifecycleControllerFromClient(
- fakeNodeHandler,
- evictionTimeout,
- testRateLimiterQPS,
- testRateLimiterQPS,
- testLargeClusterThreshold,
- testUnhealthyThreshold,
- testNodeMonitorGracePeriod,
- testNodeStartupGracePeriod,
- testNodeMonitorPeriod,
- true)
- nodeController.now = func() metav1.Time { return fakeNow }
- nodeController.recorder = testutil.NewFakeRecorder()
- nodeController.getPodsAssignedToNode = fakeGetPodsAssignedToNode(fakeNodeHandler.Clientset)
- networkUnavailableTaint := &v1.Taint{
- Key: v1.TaintNodeNetworkUnavailable,
- Effect: v1.TaintEffectNoSchedule,
- }
- notReadyTaint := &v1.Taint{
- Key: v1.TaintNodeNotReady,
- Effect: v1.TaintEffectNoSchedule,
- }
- unreachableTaint := &v1.Taint{
- Key: v1.TaintNodeUnreachable,
- Effect: v1.TaintEffectNoSchedule,
- }
- tests := []struct {
- Name string
- Node *v1.Node
- ExpectedTaints []*v1.Taint
- }{
- {
- Name: "NetworkUnavailable is true",
- Node: &v1.Node{
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- {
- Type: v1.NodeNetworkUnavailable,
- Status: v1.ConditionTrue,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- ExpectedTaints: []*v1.Taint{networkUnavailableTaint},
- },
- {
- Name: "NetworkUnavailable is true",
- Node: &v1.Node{
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- {
- Type: v1.NodeNetworkUnavailable,
- Status: v1.ConditionTrue,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- ExpectedTaints: []*v1.Taint{networkUnavailableTaint},
- },
- {
- Name: "Ready is false",
- Node: &v1.Node{
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionFalse,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- ExpectedTaints: []*v1.Taint{notReadyTaint},
- },
- {
- Name: "Ready is unknown",
- Node: &v1.Node{
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionUnknown,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- ExpectedTaints: []*v1.Taint{unreachableTaint},
- },
- }
- for _, test := range tests {
- fakeNodeHandler.Update(context.TODO(), test.Node, metav1.UpdateOptions{})
- if err := nodeController.syncNodeStore(fakeNodeHandler); err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- nodeController.doNoScheduleTaintingPass(test.Node.Name)
- if err := nodeController.syncNodeStore(fakeNodeHandler); err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- node0, err := nodeController.nodeLister.Get("node0")
- if err != nil {
- t.Errorf("Can't get current node0...")
- return
- }
- if len(node0.Spec.Taints) != len(test.ExpectedTaints) {
- t.Errorf("%s: Unexpected number of taints: expected %d, got %d",
- test.Name, len(test.ExpectedTaints), len(node0.Spec.Taints))
- }
- for _, taint := range test.ExpectedTaints {
- if !taintutils.TaintExists(node0.Spec.Taints, taint) {
- t.Errorf("%s: Can't find taint %v in %v", test.Name, taint, node0.Spec.Taints)
- }
- }
- }
- }
- func TestNodeEventGeneration(t *testing.T) {
- fakeNow := metav1.Date(2016, 9, 10, 12, 0, 0, 0, time.UTC)
- fakeNodeHandler := &testutil.FakeNodeHandler{
- Existing: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- UID: "1234567890",
- CreationTimestamp: metav1.Date(2015, 8, 10, 0, 0, 0, 0, time.UTC),
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionUnknown,
- LastHeartbeatTime: metav1.Date(2015, 8, 10, 0, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 8, 10, 0, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- },
- Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
- }
- nodeController, _ := newNodeLifecycleControllerFromClient(
- fakeNodeHandler,
- 5*time.Minute,
- testRateLimiterQPS,
- testRateLimiterQPS,
- testLargeClusterThreshold,
- testUnhealthyThreshold,
- testNodeMonitorGracePeriod,
- testNodeStartupGracePeriod,
- testNodeMonitorPeriod,
- false)
- nodeController.now = func() metav1.Time { return fakeNow }
- fakeRecorder := testutil.NewFakeRecorder()
- nodeController.recorder = fakeRecorder
- nodeController.getPodsAssignedToNode = fakeGetPodsAssignedToNode(fakeNodeHandler.Clientset)
- if err := nodeController.syncNodeStore(fakeNodeHandler); err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if err := nodeController.monitorNodeHealth(); err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if len(fakeRecorder.Events) != 1 {
- t.Fatalf("unexpected events, got %v, expected %v: %+v", len(fakeRecorder.Events), 1, fakeRecorder.Events)
- }
- if fakeRecorder.Events[0].Reason != "RegisteredNode" {
- var reasons []string
- for _, event := range fakeRecorder.Events {
- reasons = append(reasons, event.Reason)
- }
- t.Fatalf("unexpected events generation: %v", strings.Join(reasons, ","))
- }
- for _, event := range fakeRecorder.Events {
- involvedObject := event.InvolvedObject
- actualUID := string(involvedObject.UID)
- if actualUID != "1234567890" {
- t.Fatalf("unexpected event uid: %v", actualUID)
- }
- }
- }
- func TestReconcileNodeLabels(t *testing.T) {
- fakeNow := metav1.Date(2017, 1, 1, 12, 0, 0, 0, time.UTC)
- evictionTimeout := 10 * time.Minute
- fakeNodeHandler := &testutil.FakeNodeHandler{
- Existing: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- v1.LabelZoneFailureDomainStable: "zone1",
- v1.LabelZoneRegion: "region1",
- v1.LabelZoneFailureDomain: "zone1",
- },
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
- },
- },
- },
- },
- },
- Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
- }
- nodeController, _ := newNodeLifecycleControllerFromClient(
- fakeNodeHandler,
- evictionTimeout,
- testRateLimiterQPS,
- testRateLimiterQPS,
- testLargeClusterThreshold,
- testUnhealthyThreshold,
- testNodeMonitorGracePeriod,
- testNodeStartupGracePeriod,
- testNodeMonitorPeriod,
- true)
- nodeController.now = func() metav1.Time { return fakeNow }
- nodeController.recorder = testutil.NewFakeRecorder()
- nodeController.getPodsAssignedToNode = fakeGetPodsAssignedToNode(fakeNodeHandler.Clientset)
- tests := []struct {
- Name string
- Node *v1.Node
- ExpectedLabels map[string]string
- }{
- {
- Name: "No-op if node has no labels",
- Node: &v1.Node{
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- },
- },
- ExpectedLabels: nil,
- },
- {
- Name: "No-op if no target labels present",
- Node: &v1.Node{
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- },
- },
- },
- ExpectedLabels: map[string]string{
- v1.LabelZoneRegionStable: "region1",
- },
- },
- {
- Name: "Create OS/arch stable labels when they don't exist",
- Node: &v1.Node{
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- kubeletapis.LabelOS: "linux",
- kubeletapis.LabelArch: "amd64",
- },
- },
- },
- ExpectedLabels: map[string]string{
- kubeletapis.LabelOS: "linux",
- kubeletapis.LabelArch: "amd64",
- v1.LabelOSStable: "linux",
- v1.LabelArchStable: "amd64",
- },
- },
- {
- Name: "Reconcile OS/arch stable labels to match beta labels",
- Node: &v1.Node{
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
- Labels: map[string]string{
- kubeletapis.LabelOS: "linux",
- kubeletapis.LabelArch: "amd64",
- v1.LabelOSStable: "windows",
- v1.LabelArchStable: "arm",
- },
- },
- },
- ExpectedLabels: map[string]string{
- kubeletapis.LabelOS: "linux",
- kubeletapis.LabelArch: "amd64",
- v1.LabelOSStable: "linux",
- v1.LabelArchStable: "amd64",
- },
- },
- }
- for _, test := range tests {
- fakeNodeHandler.Update(context.TODO(), test.Node, metav1.UpdateOptions{})
- if err := nodeController.syncNodeStore(fakeNodeHandler); err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- nodeController.reconcileNodeLabels(test.Node.Name)
- if err := nodeController.syncNodeStore(fakeNodeHandler); err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- node0, err := nodeController.nodeLister.Get("node0")
- if err != nil {
- t.Fatalf("Can't get current node0...")
- }
- if len(node0.Labels) != len(test.ExpectedLabels) {
- t.Errorf("%s: Unexpected number of taints: expected %d, got %d",
- test.Name, len(test.ExpectedLabels), len(node0.Labels))
- }
- for key, expectedValue := range test.ExpectedLabels {
- actualValue, ok := node0.Labels[key]
- if !ok {
- t.Errorf("%s: Can't find label %v in %v", test.Name, key, node0.Labels)
- }
- if actualValue != expectedValue {
- t.Errorf("%s: label %q: expected value %q, got value %q", test.Name, key, expectedValue, actualValue)
- }
- }
- }
- }
- func TestTryUpdateNodeHealth(t *testing.T) {
- fakeNow := metav1.Date(2017, 1, 1, 12, 0, 0, 0, time.UTC)
- fakeOld := metav1.Date(2016, 1, 1, 12, 0, 0, 0, time.UTC)
- evictionTimeout := 10 * time.Minute
- fakeNodeHandler := &testutil.FakeNodeHandler{
- Existing: []*v1.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: fakeNow,
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- LastHeartbeatTime: fakeNow,
- LastTransitionTime: fakeNow,
- },
- },
- },
- },
- },
- Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
- }
- nodeController, _ := newNodeLifecycleControllerFromClient(
- fakeNodeHandler,
- evictionTimeout,
- testRateLimiterQPS,
- testRateLimiterQPS,
- testLargeClusterThreshold,
- testUnhealthyThreshold,
- testNodeMonitorGracePeriod,
- testNodeStartupGracePeriod,
- testNodeMonitorPeriod,
- true)
- nodeController.now = func() metav1.Time { return fakeNow }
- nodeController.recorder = testutil.NewFakeRecorder()
- nodeController.getPodsAssignedToNode = fakeGetPodsAssignedToNode(fakeNodeHandler.Clientset)
- getStatus := func(cond *v1.NodeCondition) *v1.ConditionStatus {
- if cond == nil {
- return nil
- }
- return &cond.Status
- }
- tests := []struct {
- name string
- node *v1.Node
- }{
- {
- name: "Status true",
- node: &v1.Node{
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: fakeNow,
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- LastHeartbeatTime: fakeNow,
- LastTransitionTime: fakeNow,
- },
- },
- },
- },
- },
- {
- name: "Status false",
- node: &v1.Node{
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: fakeNow,
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionFalse,
- LastHeartbeatTime: fakeNow,
- LastTransitionTime: fakeNow,
- },
- },
- },
- },
- },
- {
- name: "Status unknown",
- node: &v1.Node{
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: fakeNow,
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionUnknown,
- LastHeartbeatTime: fakeNow,
- LastTransitionTime: fakeNow,
- },
- },
- },
- },
- },
- {
- name: "Status nil",
- node: &v1.Node{
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: fakeNow,
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{},
- },
- },
- },
- {
- name: "Status true - after grace period",
- node: &v1.Node{
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: fakeOld,
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionTrue,
- LastHeartbeatTime: fakeOld,
- LastTransitionTime: fakeOld,
- },
- },
- },
- },
- },
- {
- name: "Status false - after grace period",
- node: &v1.Node{
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: fakeOld,
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionFalse,
- LastHeartbeatTime: fakeOld,
- LastTransitionTime: fakeOld,
- },
- },
- },
- },
- },
- {
- name: "Status unknown - after grace period",
- node: &v1.Node{
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: fakeOld,
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{
- {
- Type: v1.NodeReady,
- Status: v1.ConditionUnknown,
- LastHeartbeatTime: fakeOld,
- LastTransitionTime: fakeOld,
- },
- },
- },
- },
- },
- {
- name: "Status nil - after grace period",
- node: &v1.Node{
- ObjectMeta: metav1.ObjectMeta{
- Name: "node0",
- CreationTimestamp: fakeOld,
- },
- Status: v1.NodeStatus{
- Conditions: []v1.NodeCondition{},
- },
- },
- },
- }
- for _, test := range tests {
- t.Run(test.name, func(t *testing.T) {
- nodeController.nodeHealthMap.set(test.node.Name, &nodeHealthData{
- status: &test.node.Status,
- probeTimestamp: test.node.CreationTimestamp,
- readyTransitionTimestamp: test.node.CreationTimestamp,
- })
- _, _, currentReadyCondition, err := nodeController.tryUpdateNodeHealth(test.node)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- _, savedReadyCondition := nodeutil.GetNodeCondition(nodeController.nodeHealthMap.getDeepCopy(test.node.Name).status, v1.NodeReady)
- savedStatus := getStatus(savedReadyCondition)
- currentStatus := getStatus(currentReadyCondition)
- if !apiequality.Semantic.DeepEqual(currentStatus, savedStatus) {
- t.Errorf("expected %v, got %v", savedStatus, currentStatus)
- }
- })
- }
- }
- func Test_isNodeExcludedFromDisruptionChecks(t *testing.T) {
- validNodeStatus := v1.NodeStatus{Conditions: []v1.NodeCondition{{Type: "Test"}}}
- tests := []struct {
- name string
- enableExclusion bool
- enableLegacy bool
- input *v1.Node
- want bool
- }{
- {want: false, input: &v1.Node{Status: validNodeStatus, ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{}}}},
- {want: false, input: &v1.Node{Status: validNodeStatus, ObjectMeta: metav1.ObjectMeta{Name: "master-abc"}}},
- {want: false, input: &v1.Node{Status: validNodeStatus, ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{labelNodeDisruptionExclusion: ""}}}},
- {want: false, enableExclusion: true, input: &v1.Node{Status: validNodeStatus, ObjectMeta: metav1.ObjectMeta{Name: "master-abc"}}},
- {want: false, enableLegacy: true, input: &v1.Node{Status: validNodeStatus, ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{labelNodeDisruptionExclusion: ""}}}},
- {want: true, enableLegacy: true, input: &v1.Node{Status: validNodeStatus, ObjectMeta: metav1.ObjectMeta{Name: "master-abc"}}},
- {want: true, enableExclusion: true, input: &v1.Node{Status: validNodeStatus, ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{labelNodeDisruptionExclusion: ""}}}},
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.NodeDisruptionExclusion, tt.enableExclusion)()
- defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.LegacyNodeRoleBehavior, tt.enableLegacy)()
- if result := isNodeExcludedFromDisruptionChecks(tt.input); result != tt.want {
- t.Errorf("isNodeExcludedFromDisruptionChecks() = %v, want %v", result, tt.want)
- }
- })
- }
- }
|