cluster_size_autoscaling.go 73 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953
  1. /*
  2. Copyright 2016 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package autoscaling
  14. import (
  15. "context"
  16. "fmt"
  17. "io/ioutil"
  18. "math"
  19. "net/http"
  20. "os"
  21. "os/exec"
  22. "regexp"
  23. "strconv"
  24. "strings"
  25. "time"
  26. v1 "k8s.io/api/core/v1"
  27. policyv1beta1 "k8s.io/api/policy/v1beta1"
  28. schedulingv1 "k8s.io/api/scheduling/v1"
  29. apierrors "k8s.io/apimachinery/pkg/api/errors"
  30. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  31. "k8s.io/apimachinery/pkg/fields"
  32. "k8s.io/apimachinery/pkg/labels"
  33. utilerrors "k8s.io/apimachinery/pkg/util/errors"
  34. "k8s.io/apimachinery/pkg/util/intstr"
  35. "k8s.io/apimachinery/pkg/util/sets"
  36. "k8s.io/apimachinery/pkg/util/uuid"
  37. "k8s.io/apimachinery/pkg/util/wait"
  38. clientset "k8s.io/client-go/kubernetes"
  39. "k8s.io/klog"
  40. api "k8s.io/kubernetes/pkg/apis/core"
  41. "k8s.io/kubernetes/test/e2e/framework"
  42. e2enetwork "k8s.io/kubernetes/test/e2e/framework/network"
  43. e2enode "k8s.io/kubernetes/test/e2e/framework/node"
  44. e2epv "k8s.io/kubernetes/test/e2e/framework/pv"
  45. e2erc "k8s.io/kubernetes/test/e2e/framework/rc"
  46. e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
  47. "k8s.io/kubernetes/test/e2e/scheduling"
  48. testutils "k8s.io/kubernetes/test/utils"
  49. imageutils "k8s.io/kubernetes/test/utils/image"
  50. "github.com/onsi/ginkgo"
  51. )
  52. const (
  53. defaultTimeout = 3 * time.Minute
  54. resizeTimeout = 5 * time.Minute
  55. manualResizeTimeout = 6 * time.Minute
  56. scaleUpTimeout = 5 * time.Minute
  57. scaleUpTriggerTimeout = 2 * time.Minute
  58. scaleDownTimeout = 20 * time.Minute
  59. podTimeout = 2 * time.Minute
  60. nodesRecoverTimeout = 5 * time.Minute
  61. rcCreationRetryTimeout = 4 * time.Minute
  62. rcCreationRetryDelay = 20 * time.Second
  63. makeSchedulableTimeout = 10 * time.Minute
  64. makeSchedulableDelay = 20 * time.Second
  65. freshStatusLimit = 20 * time.Second
  66. gkeUpdateTimeout = 15 * time.Minute
  67. gkeNodepoolNameKey = "cloud.google.com/gke-nodepool"
  68. disabledTaint = "DisabledForAutoscalingTest"
  69. criticalAddonsOnlyTaint = "CriticalAddonsOnly"
  70. newNodesForScaledownTests = 2
  71. unhealthyClusterThreshold = 4
  72. caNoScaleUpStatus = "NoActivity"
  73. caOngoingScaleUpStatus = "InProgress"
  74. timestampFormat = "2006-01-02 15:04:05 -0700 MST"
  75. expendablePriorityClassName = "expendable-priority"
  76. highPriorityClassName = "high-priority"
  77. gpuLabel = "cloud.google.com/gke-accelerator"
  78. )
  79. var _ = SIGDescribe("Cluster size autoscaling [Slow]", func() {
  80. f := framework.NewDefaultFramework("autoscaling")
  81. var c clientset.Interface
  82. var nodeCount int
  83. var coreCount int64
  84. var memAllocatableMb int
  85. var originalSizes map[string]int
  86. ginkgo.BeforeEach(func() {
  87. c = f.ClientSet
  88. e2eskipper.SkipUnlessProviderIs("gce", "gke")
  89. originalSizes = make(map[string]int)
  90. sum := 0
  91. for _, mig := range strings.Split(framework.TestContext.CloudConfig.NodeInstanceGroup, ",") {
  92. size, err := framework.GroupSize(mig)
  93. framework.ExpectNoError(err)
  94. ginkgo.By(fmt.Sprintf("Initial size of %s: %d", mig, size))
  95. originalSizes[mig] = size
  96. sum += size
  97. }
  98. // Give instances time to spin up
  99. framework.ExpectNoError(e2enode.WaitForReadyNodes(c, sum, scaleUpTimeout))
  100. nodes, err := e2enode.GetReadySchedulableNodes(f.ClientSet)
  101. framework.ExpectNoError(err)
  102. nodeCount = len(nodes.Items)
  103. coreCount = 0
  104. for _, node := range nodes.Items {
  105. quantity := node.Status.Allocatable[v1.ResourceCPU]
  106. coreCount += quantity.Value()
  107. }
  108. ginkgo.By(fmt.Sprintf("Initial number of schedulable nodes: %v", nodeCount))
  109. framework.ExpectNotEqual(nodeCount, 0)
  110. mem := nodes.Items[0].Status.Allocatable[v1.ResourceMemory]
  111. memAllocatableMb = int((&mem).Value() / 1024 / 1024)
  112. framework.ExpectEqual(nodeCount, sum)
  113. if framework.ProviderIs("gke") {
  114. val, err := isAutoscalerEnabled(5)
  115. framework.ExpectNoError(err)
  116. if !val {
  117. err = enableAutoscaler("default-pool", 3, 5)
  118. framework.ExpectNoError(err)
  119. }
  120. }
  121. })
  122. ginkgo.AfterEach(func() {
  123. e2eskipper.SkipUnlessProviderIs("gce", "gke")
  124. ginkgo.By(fmt.Sprintf("Restoring initial size of the cluster"))
  125. setMigSizes(originalSizes)
  126. expectedNodes := 0
  127. for _, size := range originalSizes {
  128. expectedNodes += size
  129. }
  130. framework.ExpectNoError(e2enode.WaitForReadyNodes(c, expectedNodes, scaleDownTimeout))
  131. nodes, err := c.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
  132. framework.ExpectNoError(err)
  133. s := time.Now()
  134. makeSchedulableLoop:
  135. for start := time.Now(); time.Since(start) < makeSchedulableTimeout; time.Sleep(makeSchedulableDelay) {
  136. for _, n := range nodes.Items {
  137. err = makeNodeSchedulable(c, &n, true)
  138. switch err.(type) {
  139. case CriticalAddonsOnlyError:
  140. continue makeSchedulableLoop
  141. default:
  142. framework.ExpectNoError(err)
  143. }
  144. }
  145. break
  146. }
  147. klog.Infof("Made nodes schedulable again in %v", time.Since(s).String())
  148. })
  149. ginkgo.It("shouldn't increase cluster size if pending pod is too large [Feature:ClusterSizeAutoscalingScaleUp]", func() {
  150. ginkgo.By("Creating unschedulable pod")
  151. ReserveMemory(f, "memory-reservation", 1, int(1.1*float64(memAllocatableMb)), false, defaultTimeout)
  152. defer e2erc.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "memory-reservation")
  153. ginkgo.By("Waiting for scale up hoping it won't happen")
  154. // Verify that the appropriate event was generated
  155. eventFound := false
  156. EventsLoop:
  157. for start := time.Now(); time.Since(start) < scaleUpTimeout; time.Sleep(20 * time.Second) {
  158. ginkgo.By("Waiting for NotTriggerScaleUp event")
  159. events, err := f.ClientSet.CoreV1().Events(f.Namespace.Name).List(context.TODO(), metav1.ListOptions{})
  160. framework.ExpectNoError(err)
  161. for _, e := range events.Items {
  162. if e.InvolvedObject.Kind == "Pod" && e.Reason == "NotTriggerScaleUp" && strings.Contains(e.Message, "it wouldn't fit if a new node is added") {
  163. ginkgo.By("NotTriggerScaleUp event found")
  164. eventFound = true
  165. break EventsLoop
  166. }
  167. }
  168. }
  169. framework.ExpectEqual(eventFound, true)
  170. // Verify that cluster size is not changed
  171. framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet,
  172. func(size int) bool { return size <= nodeCount }, time.Second))
  173. })
  174. simpleScaleUpTest := func(unready int) {
  175. ReserveMemory(f, "memory-reservation", 100, nodeCount*memAllocatableMb, false, 1*time.Second)
  176. defer e2erc.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "memory-reservation")
  177. // Verify that cluster size is increased
  178. framework.ExpectNoError(WaitForClusterSizeFuncWithUnready(f.ClientSet,
  179. func(size int) bool { return size >= nodeCount+1 }, scaleUpTimeout, unready))
  180. framework.ExpectNoError(waitForAllCaPodsReadyInNamespace(f, c))
  181. }
  182. ginkgo.It("should increase cluster size if pending pods are small [Feature:ClusterSizeAutoscalingScaleUp]",
  183. func() { simpleScaleUpTest(0) })
  184. gpuType := os.Getenv("TESTED_GPU_TYPE")
  185. ginkgo.It(fmt.Sprintf("Should scale up GPU pool from 0 [GpuType:%s] [Feature:ClusterSizeAutoscalingGpu]", gpuType), func() {
  186. e2eskipper.SkipUnlessProviderIs("gke")
  187. if gpuType == "" {
  188. framework.Failf("TEST_GPU_TYPE not defined")
  189. return
  190. }
  191. const gpuPoolName = "gpu-pool"
  192. addGpuNodePool(gpuPoolName, gpuType, 1, 0)
  193. defer deleteNodePool(gpuPoolName)
  194. installNvidiaDriversDaemonSet(f.Namespace.Name)
  195. ginkgo.By("Enable autoscaler")
  196. framework.ExpectNoError(enableAutoscaler(gpuPoolName, 0, 1))
  197. defer disableAutoscaler(gpuPoolName, 0, 1)
  198. framework.ExpectEqual(len(getPoolNodes(f, gpuPoolName)), 0)
  199. ginkgo.By("Schedule a pod which requires GPU")
  200. framework.ExpectNoError(ScheduleAnySingleGpuPod(f, "gpu-pod-rc"))
  201. defer e2erc.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "gpu-pod-rc")
  202. framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet,
  203. func(size int) bool { return size == nodeCount+1 }, scaleUpTimeout))
  204. framework.ExpectEqual(len(getPoolNodes(f, gpuPoolName)), 1)
  205. })
  206. ginkgo.It(fmt.Sprintf("Should scale up GPU pool from 1 [GpuType:%s] [Feature:ClusterSizeAutoscalingGpu]", gpuType), func() {
  207. e2eskipper.SkipUnlessProviderIs("gke")
  208. if gpuType == "" {
  209. framework.Failf("TEST_GPU_TYPE not defined")
  210. return
  211. }
  212. const gpuPoolName = "gpu-pool"
  213. addGpuNodePool(gpuPoolName, gpuType, 1, 1)
  214. defer deleteNodePool(gpuPoolName)
  215. installNvidiaDriversDaemonSet(f.Namespace.Name)
  216. ginkgo.By("Schedule a single pod which requires GPU")
  217. framework.ExpectNoError(ScheduleAnySingleGpuPod(f, "gpu-pod-rc"))
  218. defer e2erc.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "gpu-pod-rc")
  219. ginkgo.By("Enable autoscaler")
  220. framework.ExpectNoError(enableAutoscaler(gpuPoolName, 0, 2))
  221. defer disableAutoscaler(gpuPoolName, 0, 2)
  222. framework.ExpectEqual(len(getPoolNodes(f, gpuPoolName)), 1)
  223. ginkgo.By("Scale GPU deployment")
  224. e2erc.ScaleRC(f.ClientSet, f.ScalesGetter, f.Namespace.Name, "gpu-pod-rc", 2, true)
  225. framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet,
  226. func(size int) bool { return size == nodeCount+2 }, scaleUpTimeout))
  227. framework.ExpectEqual(len(getPoolNodes(f, gpuPoolName)), 2)
  228. })
  229. ginkgo.It(fmt.Sprintf("Should not scale GPU pool up if pod does not require GPUs [GpuType:%s] [Feature:ClusterSizeAutoscalingGpu]", gpuType), func() {
  230. e2eskipper.SkipUnlessProviderIs("gke")
  231. if gpuType == "" {
  232. framework.Failf("TEST_GPU_TYPE not defined")
  233. return
  234. }
  235. const gpuPoolName = "gpu-pool"
  236. addGpuNodePool(gpuPoolName, gpuType, 1, 0)
  237. defer deleteNodePool(gpuPoolName)
  238. installNvidiaDriversDaemonSet(f.Namespace.Name)
  239. ginkgo.By("Enable autoscaler")
  240. framework.ExpectNoError(enableAutoscaler(gpuPoolName, 0, 1))
  241. defer disableAutoscaler(gpuPoolName, 0, 1)
  242. framework.ExpectEqual(len(getPoolNodes(f, gpuPoolName)), 0)
  243. ginkgo.By("Schedule bunch of pods beyond point of filling default pool but do not request any GPUs")
  244. ReserveMemory(f, "memory-reservation", 100, nodeCount*memAllocatableMb, false, 1*time.Second)
  245. defer e2erc.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "memory-reservation")
  246. // Verify that cluster size is increased
  247. framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet,
  248. func(size int) bool { return size >= nodeCount+1 }, scaleUpTimeout))
  249. // Expect gpu pool to stay intact
  250. framework.ExpectEqual(len(getPoolNodes(f, gpuPoolName)), 0)
  251. })
  252. ginkgo.It(fmt.Sprintf("Should scale down GPU pool from 1 [GpuType:%s] [Feature:ClusterSizeAutoscalingGpu]", gpuType), func() {
  253. e2eskipper.SkipUnlessProviderIs("gke")
  254. if gpuType == "" {
  255. framework.Failf("TEST_GPU_TYPE not defined")
  256. return
  257. }
  258. const gpuPoolName = "gpu-pool"
  259. addGpuNodePool(gpuPoolName, gpuType, 1, 1)
  260. defer deleteNodePool(gpuPoolName)
  261. installNvidiaDriversDaemonSet(f.Namespace.Name)
  262. ginkgo.By("Schedule a single pod which requires GPU")
  263. framework.ExpectNoError(ScheduleAnySingleGpuPod(f, "gpu-pod-rc"))
  264. defer e2erc.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "gpu-pod-rc")
  265. ginkgo.By("Enable autoscaler")
  266. framework.ExpectNoError(enableAutoscaler(gpuPoolName, 0, 1))
  267. defer disableAutoscaler(gpuPoolName, 0, 1)
  268. framework.ExpectEqual(len(getPoolNodes(f, gpuPoolName)), 1)
  269. ginkgo.By("Remove the only POD requiring GPU")
  270. e2erc.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "gpu-pod-rc")
  271. framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet,
  272. func(size int) bool { return size == nodeCount }, scaleDownTimeout))
  273. framework.ExpectEqual(len(getPoolNodes(f, gpuPoolName)), 0)
  274. })
  275. ginkgo.It("should increase cluster size if pending pods are small and one node is broken [Feature:ClusterSizeAutoscalingScaleUp]",
  276. func() {
  277. e2enetwork.TestUnderTemporaryNetworkFailure(c, "default", getAnyNode(c), func() { simpleScaleUpTest(1) })
  278. })
  279. ginkgo.It("shouldn't trigger additional scale-ups during processing scale-up [Feature:ClusterSizeAutoscalingScaleUp]", func() {
  280. // Wait for the situation to stabilize - CA should be running and have up-to-date node readiness info.
  281. status, err := waitForScaleUpStatus(c, func(s *scaleUpStatus) bool {
  282. return s.ready == s.target && s.ready <= nodeCount
  283. }, scaleUpTriggerTimeout)
  284. framework.ExpectNoError(err)
  285. unmanagedNodes := nodeCount - status.ready
  286. ginkgo.By("Schedule more pods than can fit and wait for cluster to scale-up")
  287. ReserveMemory(f, "memory-reservation", 100, nodeCount*memAllocatableMb, false, 1*time.Second)
  288. defer e2erc.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "memory-reservation")
  289. status, err = waitForScaleUpStatus(c, func(s *scaleUpStatus) bool {
  290. return s.status == caOngoingScaleUpStatus
  291. }, scaleUpTriggerTimeout)
  292. framework.ExpectNoError(err)
  293. target := status.target
  294. framework.ExpectNoError(waitForAllCaPodsReadyInNamespace(f, c))
  295. ginkgo.By("Expect no more scale-up to be happening after all pods are scheduled")
  296. // wait for a while until scale-up finishes; we cannot read CA status immediately
  297. // after pods are scheduled as status config map is updated by CA once every loop iteration
  298. status, err = waitForScaleUpStatus(c, func(s *scaleUpStatus) bool {
  299. return s.status == caNoScaleUpStatus
  300. }, 2*freshStatusLimit)
  301. framework.ExpectNoError(err)
  302. if status.target != target {
  303. klog.Warningf("Final number of nodes (%v) does not match initial scale-up target (%v).", status.target, target)
  304. }
  305. framework.ExpectEqual(status.timestamp.Add(freshStatusLimit).Before(time.Now()), false)
  306. framework.ExpectEqual(status.status, caNoScaleUpStatus)
  307. framework.ExpectEqual(status.ready, status.target)
  308. nodes, err := e2enode.GetReadySchedulableNodes(f.ClientSet)
  309. framework.ExpectNoError(err)
  310. framework.ExpectEqual(len(nodes.Items), status.target+unmanagedNodes)
  311. })
  312. ginkgo.It("should increase cluster size if pending pods are small and there is another node pool that is not autoscaled [Feature:ClusterSizeAutoscalingScaleUp]", func() {
  313. e2eskipper.SkipUnlessProviderIs("gke")
  314. ginkgo.By("Creating new node-pool with n1-standard-4 machines")
  315. const extraPoolName = "extra-pool"
  316. addNodePool(extraPoolName, "n1-standard-4", 1)
  317. defer deleteNodePool(extraPoolName)
  318. extraNodes := getPoolInitialSize(extraPoolName)
  319. framework.ExpectNoError(e2enode.WaitForReadyNodes(c, nodeCount+extraNodes, resizeTimeout))
  320. // We wait for nodes to become schedulable to make sure the new nodes
  321. // will be returned by getPoolNodes below.
  322. framework.ExpectNoError(framework.WaitForAllNodesSchedulable(c, resizeTimeout))
  323. klog.Infof("Not enabling cluster autoscaler for the node pool (on purpose).")
  324. ginkgo.By("Getting memory available on new nodes, so we can account for it when creating RC")
  325. nodes := getPoolNodes(f, extraPoolName)
  326. framework.ExpectEqual(len(nodes), extraNodes)
  327. extraMemMb := 0
  328. for _, node := range nodes {
  329. mem := node.Status.Allocatable[v1.ResourceMemory]
  330. extraMemMb += int((&mem).Value() / 1024 / 1024)
  331. }
  332. ginkgo.By("Reserving 0.1x more memory than the cluster holds to trigger scale up")
  333. totalMemoryReservation := int(1.1 * float64(nodeCount*memAllocatableMb+extraMemMb))
  334. defer e2erc.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "memory-reservation")
  335. ReserveMemory(f, "memory-reservation", 100, totalMemoryReservation, false, defaultTimeout)
  336. // Verify, that cluster size is increased
  337. framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet,
  338. func(size int) bool { return size >= nodeCount+extraNodes+1 }, scaleUpTimeout))
  339. framework.ExpectNoError(waitForAllCaPodsReadyInNamespace(f, c))
  340. })
  341. ginkgo.It("should disable node pool autoscaling [Feature:ClusterSizeAutoscalingScaleUp]", func() {
  342. e2eskipper.SkipUnlessProviderIs("gke")
  343. ginkgo.By("Creating new node-pool with n1-standard-4 machines")
  344. const extraPoolName = "extra-pool"
  345. addNodePool(extraPoolName, "n1-standard-4", 1)
  346. defer deleteNodePool(extraPoolName)
  347. extraNodes := getPoolInitialSize(extraPoolName)
  348. framework.ExpectNoError(e2enode.WaitForReadyNodes(c, nodeCount+extraNodes, resizeTimeout))
  349. framework.ExpectNoError(enableAutoscaler(extraPoolName, 1, 2))
  350. framework.ExpectNoError(disableAutoscaler(extraPoolName, 1, 2))
  351. })
  352. ginkgo.It("should increase cluster size if pods are pending due to host port conflict [Feature:ClusterSizeAutoscalingScaleUp]", func() {
  353. scheduling.CreateHostPortPods(f, "host-port", nodeCount+2, false)
  354. defer e2erc.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "host-port")
  355. framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet,
  356. func(size int) bool { return size >= nodeCount+2 }, scaleUpTimeout))
  357. framework.ExpectNoError(waitForAllCaPodsReadyInNamespace(f, c))
  358. })
  359. ginkgo.It("should increase cluster size if pods are pending due to pod anti-affinity [Feature:ClusterSizeAutoscalingScaleUp]", func() {
  360. pods := nodeCount
  361. newPods := 2
  362. labels := map[string]string{
  363. "anti-affinity": "yes",
  364. }
  365. ginkgo.By("starting a pod with anti-affinity on each node")
  366. framework.ExpectNoError(runAntiAffinityPods(f, f.Namespace.Name, pods, "some-pod", labels, labels))
  367. defer e2erc.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "some-pod")
  368. framework.ExpectNoError(waitForAllCaPodsReadyInNamespace(f, c))
  369. ginkgo.By("scheduling extra pods with anti-affinity to existing ones")
  370. framework.ExpectNoError(runAntiAffinityPods(f, f.Namespace.Name, newPods, "extra-pod", labels, labels))
  371. defer e2erc.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "extra-pod")
  372. framework.ExpectNoError(waitForAllCaPodsReadyInNamespace(f, c))
  373. framework.ExpectNoError(e2enode.WaitForReadyNodes(c, nodeCount+newPods, scaleUpTimeout))
  374. })
  375. ginkgo.It("should increase cluster size if pod requesting EmptyDir volume is pending [Feature:ClusterSizeAutoscalingScaleUp]", func() {
  376. ginkgo.By("creating pods")
  377. pods := nodeCount
  378. newPods := 1
  379. labels := map[string]string{
  380. "anti-affinity": "yes",
  381. }
  382. framework.ExpectNoError(runAntiAffinityPods(f, f.Namespace.Name, pods, "some-pod", labels, labels))
  383. defer e2erc.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "some-pod")
  384. ginkgo.By("waiting for all pods before triggering scale up")
  385. framework.ExpectNoError(waitForAllCaPodsReadyInNamespace(f, c))
  386. ginkgo.By("creating a pod requesting EmptyDir")
  387. framework.ExpectNoError(runVolumeAntiAffinityPods(f, f.Namespace.Name, newPods, "extra-pod", labels, labels, emptyDirVolumes))
  388. defer e2erc.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "extra-pod")
  389. framework.ExpectNoError(waitForAllCaPodsReadyInNamespace(f, c))
  390. framework.ExpectNoError(e2enode.WaitForReadyNodes(c, nodeCount+newPods, scaleUpTimeout))
  391. })
  392. ginkgo.It("should increase cluster size if pod requesting volume is pending [Feature:ClusterSizeAutoscalingScaleUp]", func() {
  393. e2eskipper.SkipUnlessProviderIs("gce", "gke")
  394. volumeLabels := labels.Set{
  395. e2epv.VolumeSelectorKey: f.Namespace.Name,
  396. }
  397. selector := metav1.SetAsLabelSelector(volumeLabels)
  398. ginkgo.By("creating volume & pvc")
  399. diskName, err := e2epv.CreatePDWithRetry()
  400. framework.ExpectNoError(err)
  401. pvConfig := e2epv.PersistentVolumeConfig{
  402. NamePrefix: "gce-",
  403. Labels: volumeLabels,
  404. PVSource: v1.PersistentVolumeSource{
  405. GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
  406. PDName: diskName,
  407. FSType: "ext3",
  408. ReadOnly: false,
  409. },
  410. },
  411. Prebind: nil,
  412. }
  413. emptyStorageClass := ""
  414. pvcConfig := e2epv.PersistentVolumeClaimConfig{
  415. Selector: selector,
  416. StorageClassName: &emptyStorageClass,
  417. }
  418. pv, pvc, err := e2epv.CreatePVPVC(c, pvConfig, pvcConfig, f.Namespace.Name, false)
  419. framework.ExpectNoError(err)
  420. framework.ExpectNoError(e2epv.WaitOnPVandPVC(c, f.Namespace.Name, pv, pvc))
  421. defer func() {
  422. errs := e2epv.PVPVCCleanup(c, f.Namespace.Name, pv, pvc)
  423. if len(errs) > 0 {
  424. framework.Failf("failed to delete PVC and/or PV. Errors: %v", utilerrors.NewAggregate(errs))
  425. }
  426. pv, pvc = nil, nil
  427. if diskName != "" {
  428. framework.ExpectNoError(e2epv.DeletePDWithRetry(diskName))
  429. }
  430. }()
  431. ginkgo.By("creating pods")
  432. pods := nodeCount
  433. labels := map[string]string{
  434. "anti-affinity": "yes",
  435. }
  436. framework.ExpectNoError(runAntiAffinityPods(f, f.Namespace.Name, pods, "some-pod", labels, labels))
  437. defer func() {
  438. e2erc.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "some-pod")
  439. klog.Infof("RC and pods not using volume deleted")
  440. }()
  441. ginkgo.By("waiting for all pods before triggering scale up")
  442. framework.ExpectNoError(waitForAllCaPodsReadyInNamespace(f, c))
  443. ginkgo.By("creating a pod requesting PVC")
  444. pvcPodName := "pvc-pod"
  445. newPods := 1
  446. volumes := buildVolumes(pv, pvc)
  447. framework.ExpectNoError(runVolumeAntiAffinityPods(f, f.Namespace.Name, newPods, pvcPodName, labels, labels, volumes))
  448. defer func() {
  449. e2erc.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, pvcPodName)
  450. framework.ExpectNoError(waitForAllCaPodsReadyInNamespace(f, c))
  451. }()
  452. framework.ExpectNoError(waitForAllCaPodsReadyInNamespace(f, c))
  453. framework.ExpectNoError(e2enode.WaitForReadyNodes(c, nodeCount+newPods, scaleUpTimeout))
  454. })
  455. ginkgo.It("should add node to the particular mig [Feature:ClusterSizeAutoscalingScaleUp]", func() {
  456. labelKey := "cluster-autoscaling-test.special-node"
  457. labelValue := "true"
  458. ginkgo.By("Finding the smallest MIG")
  459. minMig := ""
  460. minSize := nodeCount
  461. for mig, size := range originalSizes {
  462. if size <= minSize {
  463. minMig = mig
  464. minSize = size
  465. }
  466. }
  467. if minSize == 0 {
  468. newSizes := make(map[string]int)
  469. for mig, size := range originalSizes {
  470. newSizes[mig] = size
  471. }
  472. newSizes[minMig] = 1
  473. setMigSizes(newSizes)
  474. }
  475. removeLabels := func(nodesToClean sets.String) {
  476. ginkgo.By("Removing labels from nodes")
  477. for node := range nodesToClean {
  478. framework.RemoveLabelOffNode(c, node, labelKey)
  479. }
  480. }
  481. nodes, err := framework.GetGroupNodes(minMig)
  482. framework.ExpectNoError(err)
  483. nodesSet := sets.NewString(nodes...)
  484. defer removeLabels(nodesSet)
  485. ginkgo.By(fmt.Sprintf("Annotating nodes of the smallest MIG(%s): %v", minMig, nodes))
  486. for node := range nodesSet {
  487. framework.AddOrUpdateLabelOnNode(c, node, labelKey, labelValue)
  488. }
  489. err = scheduling.CreateNodeSelectorPods(f, "node-selector", minSize+1, map[string]string{labelKey: labelValue}, false)
  490. framework.ExpectNoError(err)
  491. ginkgo.By("Waiting for new node to appear and annotating it")
  492. framework.WaitForGroupSize(minMig, int32(minSize+1))
  493. // Verify that cluster size is increased
  494. framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet,
  495. func(size int) bool { return size >= nodeCount+1 }, scaleUpTimeout))
  496. newNodes, err := framework.GetGroupNodes(minMig)
  497. framework.ExpectNoError(err)
  498. newNodesSet := sets.NewString(newNodes...)
  499. newNodesSet.Delete(nodes...)
  500. if len(newNodesSet) > 1 {
  501. ginkgo.By(fmt.Sprintf("Spotted following new nodes in %s: %v", minMig, newNodesSet))
  502. klog.Infof("Usually only 1 new node is expected, investigating")
  503. klog.Infof("Kubectl:%s\n", framework.RunKubectlOrDie(f.Namespace.Name, "get", "nodes", "-o", "json"))
  504. if output, err := exec.Command("gcloud", "compute", "instances", "list",
  505. "--project="+framework.TestContext.CloudConfig.ProjectID,
  506. "--zone="+framework.TestContext.CloudConfig.Zone).Output(); err == nil {
  507. klog.Infof("Gcloud compute instances list: %s", output)
  508. } else {
  509. klog.Errorf("Failed to get instances list: %v", err)
  510. }
  511. for newNode := range newNodesSet {
  512. if output, err := execCmd("gcloud", "compute", "instances", "describe",
  513. newNode,
  514. "--project="+framework.TestContext.CloudConfig.ProjectID,
  515. "--zone="+framework.TestContext.CloudConfig.Zone).Output(); err == nil {
  516. klog.Infof("Gcloud compute instances describe: %s", output)
  517. } else {
  518. klog.Errorf("Failed to get instances describe: %v", err)
  519. }
  520. }
  521. // TODO: possibly remove broken node from newNodesSet to prevent removeLabel from crashing.
  522. // However at this moment we DO WANT it to crash so that we don't check all test runs for the
  523. // rare behavior, but only the broken ones.
  524. }
  525. ginkgo.By(fmt.Sprintf("New nodes: %v\n", newNodesSet))
  526. registeredNodes := sets.NewString()
  527. for nodeName := range newNodesSet {
  528. node, err := f.ClientSet.CoreV1().Nodes().Get(context.TODO(), nodeName, metav1.GetOptions{})
  529. if err == nil && node != nil {
  530. registeredNodes.Insert(nodeName)
  531. } else {
  532. klog.Errorf("Failed to get node %v: %v", nodeName, err)
  533. }
  534. }
  535. ginkgo.By(fmt.Sprintf("Setting labels for registered new nodes: %v", registeredNodes.List()))
  536. for node := range registeredNodes {
  537. framework.AddOrUpdateLabelOnNode(c, node, labelKey, labelValue)
  538. }
  539. defer removeLabels(registeredNodes)
  540. framework.ExpectNoError(waitForAllCaPodsReadyInNamespace(f, c))
  541. framework.ExpectNoError(e2erc.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "node-selector"))
  542. })
  543. ginkgo.It("should scale up correct target pool [Feature:ClusterSizeAutoscalingScaleUp]", func() {
  544. e2eskipper.SkipUnlessProviderIs("gke")
  545. ginkgo.By("Creating new node-pool with n1-standard-4 machines")
  546. const extraPoolName = "extra-pool"
  547. addNodePool(extraPoolName, "n1-standard-4", 1)
  548. defer deleteNodePool(extraPoolName)
  549. extraNodes := getPoolInitialSize(extraPoolName)
  550. framework.ExpectNoError(e2enode.WaitForReadyNodes(c, nodeCount+extraNodes, resizeTimeout))
  551. framework.ExpectNoError(enableAutoscaler(extraPoolName, 1, 2))
  552. defer disableAutoscaler(extraPoolName, 1, 2)
  553. extraPods := extraNodes + 1
  554. totalMemoryReservation := int(float64(extraPods) * 1.5 * float64(memAllocatableMb))
  555. ginkgo.By(fmt.Sprintf("Creating rc with %v pods too big to fit default-pool but fitting extra-pool", extraPods))
  556. defer e2erc.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "memory-reservation")
  557. ReserveMemory(f, "memory-reservation", extraPods, totalMemoryReservation, false, defaultTimeout)
  558. // Apparently GKE master is restarted couple minutes after the node pool is added
  559. // resetting all the timers in scale down code. Adding 5 extra minutes to workaround
  560. // this issue.
  561. // TODO: Remove the extra time when GKE restart is fixed.
  562. framework.ExpectNoError(e2enode.WaitForReadyNodes(c, nodeCount+extraNodes+1, scaleUpTimeout+5*time.Minute))
  563. })
  564. simpleScaleDownTest := func(unready int) {
  565. cleanup, err := addKubeSystemPdbs(f)
  566. defer cleanup()
  567. framework.ExpectNoError(err)
  568. ginkgo.By("Manually increase cluster size")
  569. increasedSize := 0
  570. newSizes := make(map[string]int)
  571. for key, val := range originalSizes {
  572. newSizes[key] = val + 2 + unready
  573. increasedSize += val + 2 + unready
  574. }
  575. setMigSizes(newSizes)
  576. framework.ExpectNoError(WaitForClusterSizeFuncWithUnready(f.ClientSet,
  577. func(size int) bool { return size >= increasedSize }, manualResizeTimeout, unready))
  578. ginkgo.By("Some node should be removed")
  579. framework.ExpectNoError(WaitForClusterSizeFuncWithUnready(f.ClientSet,
  580. func(size int) bool { return size < increasedSize }, scaleDownTimeout, unready))
  581. }
  582. ginkgo.It("should correctly scale down after a node is not needed [Feature:ClusterSizeAutoscalingScaleDown]",
  583. func() { simpleScaleDownTest(0) })
  584. ginkgo.It("should correctly scale down after a node is not needed and one node is broken [Feature:ClusterSizeAutoscalingScaleDown]",
  585. func() {
  586. e2eskipper.SkipUnlessSSHKeyPresent()
  587. e2enetwork.TestUnderTemporaryNetworkFailure(c, "default", getAnyNode(c), func() { simpleScaleDownTest(1) })
  588. })
  589. ginkgo.It("should correctly scale down after a node is not needed when there is non autoscaled pool[Feature:ClusterSizeAutoscalingScaleDown]", func() {
  590. e2eskipper.SkipUnlessProviderIs("gke")
  591. increasedSize := manuallyIncreaseClusterSize(f, originalSizes)
  592. const extraPoolName = "extra-pool"
  593. addNodePool(extraPoolName, "n1-standard-1", 3)
  594. defer deleteNodePool(extraPoolName)
  595. extraNodes := getPoolInitialSize(extraPoolName)
  596. framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet,
  597. func(size int) bool { return size >= increasedSize+extraNodes }, scaleUpTimeout))
  598. ginkgo.By("Some node should be removed")
  599. // Apparently GKE master is restarted couple minutes after the node pool is added
  600. // resetting all the timers in scale down code. Adding 10 extra minutes to workaround
  601. // this issue.
  602. // TODO: Remove the extra time when GKE restart is fixed.
  603. framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet,
  604. func(size int) bool { return size < increasedSize+extraNodes }, scaleDownTimeout+10*time.Minute))
  605. })
  606. ginkgo.It("should be able to scale down when rescheduling a pod is required and pdb allows for it[Feature:ClusterSizeAutoscalingScaleDown]", func() {
  607. runDrainTest(f, originalSizes, f.Namespace.Name, 1, 1, func(increasedSize int) {
  608. ginkgo.By("Some node should be removed")
  609. framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet,
  610. func(size int) bool { return size < increasedSize }, scaleDownTimeout))
  611. })
  612. })
  613. ginkgo.It("shouldn't be able to scale down when rescheduling a pod is required, but pdb doesn't allow drain[Feature:ClusterSizeAutoscalingScaleDown]", func() {
  614. runDrainTest(f, originalSizes, f.Namespace.Name, 1, 0, func(increasedSize int) {
  615. ginkgo.By("No nodes should be removed")
  616. time.Sleep(scaleDownTimeout)
  617. nodes, err := e2enode.GetReadySchedulableNodes(f.ClientSet)
  618. framework.ExpectNoError(err)
  619. framework.ExpectEqual(len(nodes.Items), increasedSize)
  620. })
  621. })
  622. ginkgo.It("should be able to scale down by draining multiple pods one by one as dictated by pdb[Feature:ClusterSizeAutoscalingScaleDown]", func() {
  623. runDrainTest(f, originalSizes, f.Namespace.Name, 2, 1, func(increasedSize int) {
  624. ginkgo.By("Some node should be removed")
  625. framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet,
  626. func(size int) bool { return size < increasedSize }, scaleDownTimeout))
  627. })
  628. })
  629. ginkgo.It("should be able to scale down by draining system pods with pdb[Feature:ClusterSizeAutoscalingScaleDown]", func() {
  630. runDrainTest(f, originalSizes, "kube-system", 2, 1, func(increasedSize int) {
  631. ginkgo.By("Some node should be removed")
  632. framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet,
  633. func(size int) bool { return size < increasedSize }, scaleDownTimeout))
  634. })
  635. })
  636. ginkgo.It("Should be able to scale a node group up from 0[Feature:ClusterSizeAutoscalingScaleUp]", func() {
  637. // Provider-specific setup
  638. if framework.ProviderIs("gke") {
  639. // GKE-specific setup
  640. ginkgo.By("Add a new node pool with 0 nodes and min size 0")
  641. const extraPoolName = "extra-pool"
  642. addNodePool(extraPoolName, "n1-standard-4", 0)
  643. defer deleteNodePool(extraPoolName)
  644. framework.ExpectNoError(enableAutoscaler(extraPoolName, 0, 1))
  645. defer disableAutoscaler(extraPoolName, 0, 1)
  646. } else {
  647. // on GCE, run only if there are already at least 2 node groups
  648. e2eskipper.SkipUnlessAtLeast(len(originalSizes), 2, "At least 2 node groups are needed for scale-to-0 tests")
  649. ginkgo.By("Manually scale smallest node group to 0")
  650. minMig := ""
  651. minSize := nodeCount
  652. for mig, size := range originalSizes {
  653. if size <= minSize {
  654. minMig = mig
  655. minSize = size
  656. }
  657. }
  658. framework.ExpectNoError(framework.ResizeGroup(minMig, int32(0)))
  659. framework.ExpectNoError(e2enode.WaitForReadyNodes(c, nodeCount-minSize, resizeTimeout))
  660. }
  661. ginkgo.By("Make remaining nodes unschedulable")
  662. nodes, err := f.ClientSet.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{FieldSelector: fields.Set{
  663. "spec.unschedulable": "false",
  664. }.AsSelector().String()})
  665. framework.ExpectNoError(err)
  666. for _, node := range nodes.Items {
  667. err = makeNodeUnschedulable(f.ClientSet, &node)
  668. defer func(n v1.Node) {
  669. makeNodeSchedulable(f.ClientSet, &n, false)
  670. }(node)
  671. framework.ExpectNoError(err)
  672. }
  673. ginkgo.By("Run a scale-up test")
  674. ReserveMemory(f, "memory-reservation", 1, 100, false, 1*time.Second)
  675. defer e2erc.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "memory-reservation")
  676. // Verify that cluster size is increased
  677. framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet,
  678. func(size int) bool { return size >= len(nodes.Items)+1 }, scaleUpTimeout))
  679. framework.ExpectNoError(waitForAllCaPodsReadyInNamespace(f, c))
  680. })
  681. // Scale to 0 test is split into two functions (for GKE & GCE.)
  682. // The reason for it is that scenario is exactly the same,
  683. // but setup & verification use different APIs.
  684. //
  685. // Scenario:
  686. // (GKE only) add an extra node pool with size 1 & enable autoscaling for it
  687. // (GCE only) find the smallest MIG & resize it to 1
  688. // manually drain the single node from this node pool/MIG
  689. // wait for cluster size to decrease
  690. // verify the targeted node pool/MIG is of size 0
  691. gkeScaleToZero := func() {
  692. // GKE-specific setup
  693. ginkgo.By("Add a new node pool with size 1 and min size 0")
  694. const extraPoolName = "extra-pool"
  695. addNodePool(extraPoolName, "n1-standard-4", 1)
  696. defer deleteNodePool(extraPoolName)
  697. extraNodes := getPoolInitialSize(extraPoolName)
  698. framework.ExpectNoError(e2enode.WaitForReadyNodes(c, nodeCount+extraNodes, resizeTimeout))
  699. framework.ExpectNoError(enableAutoscaler(extraPoolName, 0, 1))
  700. defer disableAutoscaler(extraPoolName, 0, 1)
  701. ngNodes := getPoolNodes(f, extraPoolName)
  702. framework.ExpectEqual(len(ngNodes), extraNodes)
  703. for _, node := range ngNodes {
  704. ginkgo.By(fmt.Sprintf("Target node for scale-down: %s", node.Name))
  705. }
  706. for _, node := range ngNodes {
  707. drainNode(f, node)
  708. }
  709. framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet,
  710. func(size int) bool { return size <= nodeCount }, scaleDownTimeout))
  711. // GKE-specific check
  712. newSize := getPoolSize(f, extraPoolName)
  713. framework.ExpectEqual(newSize, 0)
  714. }
  715. gceScaleToZero := func() {
  716. // non-GKE only
  717. ginkgo.By("Find smallest node group and manually scale it to a single node")
  718. minMig := ""
  719. minSize := nodeCount
  720. for mig, size := range originalSizes {
  721. if size <= minSize {
  722. minMig = mig
  723. minSize = size
  724. }
  725. }
  726. framework.ExpectNoError(framework.ResizeGroup(minMig, int32(1)))
  727. framework.ExpectNoError(e2enode.WaitForReadyNodes(c, nodeCount-minSize+1, resizeTimeout))
  728. ngNodes, err := framework.GetGroupNodes(minMig)
  729. framework.ExpectNoError(err)
  730. framework.ExpectEqual(len(ngNodes) == 1, true)
  731. node, err := f.ClientSet.CoreV1().Nodes().Get(context.TODO(), ngNodes[0], metav1.GetOptions{})
  732. ginkgo.By(fmt.Sprintf("Target node for scale-down: %s", node.Name))
  733. framework.ExpectNoError(err)
  734. // this part is identical
  735. drainNode(f, node)
  736. framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet,
  737. func(size int) bool { return size < nodeCount-minSize+1 }, scaleDownTimeout))
  738. // non-GKE only
  739. newSize, err := framework.GroupSize(minMig)
  740. framework.ExpectNoError(err)
  741. framework.ExpectEqual(newSize, 0)
  742. }
  743. ginkgo.It("Should be able to scale a node group down to 0[Feature:ClusterSizeAutoscalingScaleDown]", func() {
  744. if framework.ProviderIs("gke") { // In GKE, we can just add a node pool
  745. gkeScaleToZero()
  746. } else if len(originalSizes) >= 2 {
  747. gceScaleToZero()
  748. } else {
  749. e2eskipper.Skipf("At least 2 node groups are needed for scale-to-0 tests")
  750. }
  751. })
  752. ginkgo.It("Shouldn't perform scale up operation and should list unhealthy status if most of the cluster is broken[Feature:ClusterSizeAutoscalingScaleUp]", func() {
  753. e2eskipper.SkipUnlessSSHKeyPresent()
  754. clusterSize := nodeCount
  755. for clusterSize < unhealthyClusterThreshold+1 {
  756. clusterSize = manuallyIncreaseClusterSize(f, originalSizes)
  757. }
  758. // If new nodes are disconnected too soon, they'll be considered not started
  759. // instead of unready, and cluster won't be considered unhealthy.
  760. //
  761. // More precisely, Cluster Autoscaler compares last transition time of
  762. // several readiness conditions to node create time. If it's within
  763. // 2 minutes, it'll assume node is just starting and not unhealthy.
  764. //
  765. // Nodes become ready in less than 1 minute after being created,
  766. // so waiting extra 2 minutes before breaking them (which triggers
  767. // readiness condition transition) should be sufficient, while
  768. // making no assumptions about minimal node startup time.
  769. time.Sleep(2 * time.Minute)
  770. ginkgo.By("Block network connectivity to some nodes to simulate unhealthy cluster")
  771. nodesToBreakCount := int(math.Ceil(math.Max(float64(unhealthyClusterThreshold), 0.5*float64(clusterSize))))
  772. nodes, err := f.ClientSet.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{FieldSelector: fields.Set{
  773. "spec.unschedulable": "false",
  774. }.AsSelector().String()})
  775. framework.ExpectNoError(err)
  776. framework.ExpectEqual(nodesToBreakCount <= len(nodes.Items), true)
  777. nodesToBreak := nodes.Items[:nodesToBreakCount]
  778. // TestUnderTemporaryNetworkFailure only removes connectivity to a single node,
  779. // and accepts func() callback. This is expanding the loop to recursive call
  780. // to avoid duplicating TestUnderTemporaryNetworkFailure
  781. var testFunction func()
  782. testFunction = func() {
  783. if len(nodesToBreak) > 0 {
  784. ntb := &nodesToBreak[0]
  785. nodesToBreak = nodesToBreak[1:]
  786. e2enetwork.TestUnderTemporaryNetworkFailure(c, "default", ntb, testFunction)
  787. } else {
  788. ReserveMemory(f, "memory-reservation", 100, nodeCount*memAllocatableMb, false, defaultTimeout)
  789. defer e2erc.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "memory-reservation")
  790. time.Sleep(scaleUpTimeout)
  791. currentNodes, err := e2enode.GetReadySchedulableNodes(f.ClientSet)
  792. framework.ExpectNoError(err)
  793. framework.Logf("Currently available nodes: %v, nodes available at the start of test: %v, disabled nodes: %v", len(currentNodes.Items), len(nodes.Items), nodesToBreakCount)
  794. framework.ExpectEqual(len(currentNodes.Items), len(nodes.Items)-nodesToBreakCount)
  795. status, err := getClusterwideStatus(c)
  796. framework.Logf("Clusterwide status: %v", status)
  797. framework.ExpectNoError(err)
  798. framework.ExpectEqual(status, "Unhealthy")
  799. }
  800. }
  801. testFunction()
  802. // Give nodes time to recover from network failure
  803. framework.ExpectNoError(e2enode.WaitForReadyNodes(c, len(nodes.Items), nodesRecoverTimeout))
  804. })
  805. ginkgo.It("shouldn't scale up when expendable pod is created [Feature:ClusterSizeAutoscalingScaleUp]", func() {
  806. defer createPriorityClasses(f)()
  807. // Create nodesCountAfterResize+1 pods allocating 0.7 allocatable on present nodes. One more node will have to be created.
  808. cleanupFunc := ReserveMemoryWithPriority(f, "memory-reservation", nodeCount+1, int(float64(nodeCount+1)*float64(0.7)*float64(memAllocatableMb)), false, time.Second, expendablePriorityClassName)
  809. defer cleanupFunc()
  810. ginkgo.By(fmt.Sprintf("Waiting for scale up hoping it won't happen, sleep for %s", scaleUpTimeout.String()))
  811. time.Sleep(scaleUpTimeout)
  812. // Verify that cluster size is not changed
  813. framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet,
  814. func(size int) bool { return size == nodeCount }, time.Second))
  815. })
  816. ginkgo.It("should scale up when non expendable pod is created [Feature:ClusterSizeAutoscalingScaleUp]", func() {
  817. defer createPriorityClasses(f)()
  818. // Create nodesCountAfterResize+1 pods allocating 0.7 allocatable on present nodes. One more node will have to be created.
  819. cleanupFunc := ReserveMemoryWithPriority(f, "memory-reservation", nodeCount+1, int(float64(nodeCount+1)*float64(0.7)*float64(memAllocatableMb)), true, scaleUpTimeout, highPriorityClassName)
  820. defer cleanupFunc()
  821. // Verify that cluster size is not changed
  822. framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet,
  823. func(size int) bool { return size > nodeCount }, time.Second))
  824. })
  825. ginkgo.It("shouldn't scale up when expendable pod is preempted [Feature:ClusterSizeAutoscalingScaleUp]", func() {
  826. defer createPriorityClasses(f)()
  827. // Create nodesCountAfterResize pods allocating 0.7 allocatable on present nodes - one pod per node.
  828. cleanupFunc1 := ReserveMemoryWithPriority(f, "memory-reservation1", nodeCount, int(float64(nodeCount)*float64(0.7)*float64(memAllocatableMb)), true, defaultTimeout, expendablePriorityClassName)
  829. defer cleanupFunc1()
  830. // Create nodesCountAfterResize pods allocating 0.7 allocatable on present nodes - one pod per node. Pods created here should preempt pods created above.
  831. cleanupFunc2 := ReserveMemoryWithPriority(f, "memory-reservation2", nodeCount, int(float64(nodeCount)*float64(0.7)*float64(memAllocatableMb)), true, defaultTimeout, highPriorityClassName)
  832. defer cleanupFunc2()
  833. framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet,
  834. func(size int) bool { return size == nodeCount }, time.Second))
  835. })
  836. ginkgo.It("should scale down when expendable pod is running [Feature:ClusterSizeAutoscalingScaleDown]", func() {
  837. defer createPriorityClasses(f)()
  838. increasedSize := manuallyIncreaseClusterSize(f, originalSizes)
  839. // Create increasedSize pods allocating 0.7 allocatable on present nodes - one pod per node.
  840. cleanupFunc := ReserveMemoryWithPriority(f, "memory-reservation", increasedSize, int(float64(increasedSize)*float64(0.7)*float64(memAllocatableMb)), true, scaleUpTimeout, expendablePriorityClassName)
  841. defer cleanupFunc()
  842. ginkgo.By("Waiting for scale down")
  843. framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet,
  844. func(size int) bool { return size == nodeCount }, scaleDownTimeout))
  845. })
  846. ginkgo.It("shouldn't scale down when non expendable pod is running [Feature:ClusterSizeAutoscalingScaleDown]", func() {
  847. defer createPriorityClasses(f)()
  848. increasedSize := manuallyIncreaseClusterSize(f, originalSizes)
  849. // Create increasedSize pods allocating 0.7 allocatable on present nodes - one pod per node.
  850. cleanupFunc := ReserveMemoryWithPriority(f, "memory-reservation", increasedSize, int(float64(increasedSize)*float64(0.7)*float64(memAllocatableMb)), true, scaleUpTimeout, highPriorityClassName)
  851. defer cleanupFunc()
  852. ginkgo.By(fmt.Sprintf("Waiting for scale down hoping it won't happen, sleep for %s", scaleDownTimeout.String()))
  853. time.Sleep(scaleDownTimeout)
  854. framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet,
  855. func(size int) bool { return size == increasedSize }, time.Second))
  856. })
  857. })
  858. func installNvidiaDriversDaemonSet(namespace string) {
  859. ginkgo.By("Add daemonset which installs nvidia drivers")
  860. // the link differs from one in GKE documentation; discussed with @mindprince this one should be used
  861. framework.RunKubectlOrDie(namespace, "apply", "-f", "https://raw.githubusercontent.com/GoogleCloudPlatform/container-engine-accelerators/master/daemonset.yaml")
  862. }
  863. func execCmd(args ...string) *exec.Cmd {
  864. klog.Infof("Executing: %s", strings.Join(args, " "))
  865. return exec.Command(args[0], args[1:]...)
  866. }
  867. func runDrainTest(f *framework.Framework, migSizes map[string]int, namespace string, podsPerNode, pdbSize int, verifyFunction func(int)) {
  868. increasedSize := manuallyIncreaseClusterSize(f, migSizes)
  869. nodes, err := f.ClientSet.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{FieldSelector: fields.Set{
  870. "spec.unschedulable": "false",
  871. }.AsSelector().String()})
  872. framework.ExpectNoError(err)
  873. numPods := len(nodes.Items) * podsPerNode
  874. testID := string(uuid.NewUUID()) // So that we can label and find pods
  875. labelMap := map[string]string{"test_id": testID}
  876. framework.ExpectNoError(runReplicatedPodOnEachNode(f, nodes.Items, namespace, podsPerNode, "reschedulable-pods", labelMap, 0))
  877. defer e2erc.DeleteRCAndWaitForGC(f.ClientSet, namespace, "reschedulable-pods")
  878. ginkgo.By("Create a PodDisruptionBudget")
  879. minAvailable := intstr.FromInt(numPods - pdbSize)
  880. pdb := &policyv1beta1.PodDisruptionBudget{
  881. ObjectMeta: metav1.ObjectMeta{
  882. Name: "test_pdb",
  883. Namespace: namespace,
  884. },
  885. Spec: policyv1beta1.PodDisruptionBudgetSpec{
  886. Selector: &metav1.LabelSelector{MatchLabels: labelMap},
  887. MinAvailable: &minAvailable,
  888. },
  889. }
  890. _, err = f.ClientSet.PolicyV1beta1().PodDisruptionBudgets(namespace).Create(context.TODO(), pdb, metav1.CreateOptions{})
  891. defer func() {
  892. f.ClientSet.PolicyV1beta1().PodDisruptionBudgets(namespace).Delete(context.TODO(), pdb.Name, &metav1.DeleteOptions{})
  893. }()
  894. framework.ExpectNoError(err)
  895. verifyFunction(increasedSize)
  896. }
  897. func getGkeAPIEndpoint() string {
  898. gkeAPIEndpoint := os.Getenv("CLOUDSDK_API_ENDPOINT_OVERRIDES_CONTAINER")
  899. if gkeAPIEndpoint == "" {
  900. gkeAPIEndpoint = "https://test-container.sandbox.googleapis.com"
  901. }
  902. if strings.HasSuffix(gkeAPIEndpoint, "/") {
  903. gkeAPIEndpoint = gkeAPIEndpoint[:len(gkeAPIEndpoint)-1]
  904. }
  905. return gkeAPIEndpoint
  906. }
  907. func getGKEURL(apiVersion string, suffix string) string {
  908. out, err := execCmd("gcloud", "auth", "print-access-token").Output()
  909. framework.ExpectNoError(err)
  910. token := strings.Replace(string(out), "\n", "", -1)
  911. return fmt.Sprintf("%s/%s/%s?access_token=%s",
  912. getGkeAPIEndpoint(),
  913. apiVersion,
  914. suffix,
  915. token)
  916. }
  917. func getGKEClusterURL(apiVersion string) string {
  918. if isRegionalCluster() {
  919. // TODO(bskiba): Use locations API for all clusters once it's graduated to v1.
  920. return getGKEURL(apiVersion, fmt.Sprintf("projects/%s/locations/%s/clusters/%s",
  921. framework.TestContext.CloudConfig.ProjectID,
  922. framework.TestContext.CloudConfig.Region,
  923. framework.TestContext.CloudConfig.Cluster))
  924. }
  925. return getGKEURL(apiVersion, fmt.Sprintf("projects/%s/zones/%s/clusters/%s",
  926. framework.TestContext.CloudConfig.ProjectID,
  927. framework.TestContext.CloudConfig.Zone,
  928. framework.TestContext.CloudConfig.Cluster))
  929. }
  930. func getCluster(apiVersion string) (string, error) {
  931. resp, err := http.Get(getGKEClusterURL(apiVersion))
  932. if err != nil {
  933. return "", err
  934. }
  935. defer resp.Body.Close()
  936. body, err := ioutil.ReadAll(resp.Body)
  937. if err != nil {
  938. return "", err
  939. }
  940. if resp.StatusCode != http.StatusOK {
  941. return "", fmt.Errorf("error: %s %s", resp.Status, body)
  942. }
  943. return string(body), nil
  944. }
  945. func isAutoscalerEnabled(expectedMaxNodeCountInTargetPool int) (bool, error) {
  946. apiVersion := "v1"
  947. if isRegionalCluster() {
  948. apiVersion = "v1beta1"
  949. }
  950. strBody, err := getCluster(apiVersion)
  951. if err != nil {
  952. return false, err
  953. }
  954. if strings.Contains(strBody, "\"maxNodeCount\": "+strconv.Itoa(expectedMaxNodeCountInTargetPool)) {
  955. return true, nil
  956. }
  957. return false, nil
  958. }
  959. func getClusterLocation() string {
  960. if isRegionalCluster() {
  961. return "--region=" + framework.TestContext.CloudConfig.Region
  962. }
  963. return "--zone=" + framework.TestContext.CloudConfig.Zone
  964. }
  965. func getGcloudCommandFromTrack(commandTrack string, args []string) []string {
  966. command := []string{"gcloud"}
  967. if commandTrack == "beta" || commandTrack == "alpha" {
  968. command = append(command, commandTrack)
  969. }
  970. command = append(command, args...)
  971. command = append(command, getClusterLocation())
  972. command = append(command, "--project="+framework.TestContext.CloudConfig.ProjectID)
  973. return command
  974. }
  975. func getGcloudCommand(args []string) []string {
  976. track := ""
  977. if isRegionalCluster() {
  978. track = "beta"
  979. }
  980. return getGcloudCommandFromTrack(track, args)
  981. }
  982. func isRegionalCluster() bool {
  983. // TODO(bskiba): Use an appropriate indicator that the cluster is regional.
  984. return framework.TestContext.CloudConfig.MultiZone
  985. }
  986. func enableAutoscaler(nodePool string, minCount, maxCount int) error {
  987. klog.Infof("Using gcloud to enable autoscaling for pool %s", nodePool)
  988. args := []string{"container", "clusters", "update", framework.TestContext.CloudConfig.Cluster,
  989. "--enable-autoscaling",
  990. "--min-nodes=" + strconv.Itoa(minCount),
  991. "--max-nodes=" + strconv.Itoa(maxCount),
  992. "--node-pool=" + nodePool}
  993. output, err := execCmd(getGcloudCommand(args)...).CombinedOutput()
  994. if err != nil {
  995. klog.Errorf("Failed config update result: %s", output)
  996. return fmt.Errorf("Failed to enable autoscaling: %v", err)
  997. }
  998. klog.Infof("Config update result: %s", output)
  999. var finalErr error
  1000. for startTime := time.Now(); startTime.Add(gkeUpdateTimeout).After(time.Now()); time.Sleep(30 * time.Second) {
  1001. val, err := isAutoscalerEnabled(maxCount)
  1002. if err == nil && val {
  1003. return nil
  1004. }
  1005. finalErr = err
  1006. }
  1007. return fmt.Errorf("autoscaler not enabled, last error: %v", finalErr)
  1008. }
  1009. func disableAutoscaler(nodePool string, minCount, maxCount int) error {
  1010. klog.Infof("Using gcloud to disable autoscaling for pool %s", nodePool)
  1011. args := []string{"container", "clusters", "update", framework.TestContext.CloudConfig.Cluster,
  1012. "--no-enable-autoscaling",
  1013. "--node-pool=" + nodePool}
  1014. output, err := execCmd(getGcloudCommand(args)...).CombinedOutput()
  1015. if err != nil {
  1016. klog.Errorf("Failed config update result: %s", output)
  1017. return fmt.Errorf("Failed to disable autoscaling: %v", err)
  1018. }
  1019. klog.Infof("Config update result: %s", output)
  1020. var finalErr error
  1021. for startTime := time.Now(); startTime.Add(gkeUpdateTimeout).After(time.Now()); time.Sleep(30 * time.Second) {
  1022. val, err := isAutoscalerEnabled(maxCount)
  1023. if err == nil && !val {
  1024. return nil
  1025. }
  1026. finalErr = err
  1027. }
  1028. return fmt.Errorf("autoscaler still enabled, last error: %v", finalErr)
  1029. }
  1030. func addNodePool(name string, machineType string, numNodes int) {
  1031. args := []string{"container", "node-pools", "create", name, "--quiet",
  1032. "--machine-type=" + machineType,
  1033. "--num-nodes=" + strconv.Itoa(numNodes),
  1034. "--cluster=" + framework.TestContext.CloudConfig.Cluster}
  1035. output, err := execCmd(getGcloudCommand(args)...).CombinedOutput()
  1036. klog.Infof("Creating node-pool %s: %s", name, output)
  1037. framework.ExpectNoError(err, string(output))
  1038. }
  1039. func addGpuNodePool(name string, gpuType string, gpuCount int, numNodes int) {
  1040. args := []string{"beta", "container", "node-pools", "create", name, "--quiet",
  1041. "--accelerator", "type=" + gpuType + ",count=" + strconv.Itoa(gpuCount),
  1042. "--num-nodes=" + strconv.Itoa(numNodes),
  1043. "--cluster=" + framework.TestContext.CloudConfig.Cluster}
  1044. output, err := execCmd(getGcloudCommand(args)...).CombinedOutput()
  1045. klog.Infof("Creating node-pool %s: %s", name, output)
  1046. framework.ExpectNoError(err, string(output))
  1047. }
  1048. func deleteNodePool(name string) {
  1049. klog.Infof("Deleting node pool %s", name)
  1050. args := []string{"container", "node-pools", "delete", name, "--quiet",
  1051. "--cluster=" + framework.TestContext.CloudConfig.Cluster}
  1052. err := wait.ExponentialBackoff(
  1053. wait.Backoff{Duration: 1 * time.Minute, Factor: float64(3), Steps: 3},
  1054. func() (bool, error) {
  1055. output, err := execCmd(getGcloudCommand(args)...).CombinedOutput()
  1056. if err != nil {
  1057. klog.Warningf("Error deleting nodegroup - error:%v, output: %s", err, output)
  1058. return false, nil
  1059. }
  1060. klog.Infof("Node-pool deletion output: %s", output)
  1061. return true, nil
  1062. })
  1063. framework.ExpectNoError(err)
  1064. }
  1065. func getPoolNodes(f *framework.Framework, poolName string) []*v1.Node {
  1066. nodes := make([]*v1.Node, 0, 1)
  1067. nodeList, err := e2enode.GetReadyNodesIncludingTainted(f.ClientSet)
  1068. if err != nil {
  1069. framework.Logf("Unexpected error occurred: %v", err)
  1070. }
  1071. // TODO: write a wrapper for ExpectNoErrorWithOffset()
  1072. framework.ExpectNoErrorWithOffset(0, err)
  1073. for _, node := range nodeList.Items {
  1074. if node.Labels[gkeNodepoolNameKey] == poolName {
  1075. nodes = append(nodes, &node)
  1076. }
  1077. }
  1078. return nodes
  1079. }
  1080. // getPoolInitialSize returns the initial size of the node pool taking into
  1081. // account that it may span multiple zones. In that case, node pool consists of
  1082. // multiple migs all containing initialNodeCount nodes.
  1083. func getPoolInitialSize(poolName string) int {
  1084. // get initial node count
  1085. args := []string{"container", "node-pools", "describe", poolName, "--quiet",
  1086. "--cluster=" + framework.TestContext.CloudConfig.Cluster,
  1087. "--format=value(initialNodeCount)"}
  1088. output, err := execCmd(getGcloudCommand(args)...).CombinedOutput()
  1089. klog.Infof("Node-pool initial size: %s", output)
  1090. framework.ExpectNoError(err, string(output))
  1091. fields := strings.Fields(string(output))
  1092. framework.ExpectEqual(len(fields), 1)
  1093. size, err := strconv.ParseInt(fields[0], 10, 64)
  1094. framework.ExpectNoError(err)
  1095. // get number of node pools
  1096. args = []string{"container", "node-pools", "describe", poolName, "--quiet",
  1097. "--cluster=" + framework.TestContext.CloudConfig.Cluster,
  1098. "--format=value(instanceGroupUrls)"}
  1099. output, err = execCmd(getGcloudCommand(args)...).CombinedOutput()
  1100. framework.ExpectNoError(err, string(output))
  1101. nodeGroupCount := len(strings.Split(string(output), ";"))
  1102. return int(size) * nodeGroupCount
  1103. }
  1104. func getPoolSize(f *framework.Framework, poolName string) int {
  1105. size := 0
  1106. nodeList, err := e2enode.GetReadySchedulableNodes(f.ClientSet)
  1107. framework.ExpectNoError(err)
  1108. for _, node := range nodeList.Items {
  1109. if node.Labels[gkeNodepoolNameKey] == poolName {
  1110. size++
  1111. }
  1112. }
  1113. return size
  1114. }
  1115. func reserveMemory(f *framework.Framework, id string, replicas, megabytes int, expectRunning bool, timeout time.Duration, selector map[string]string, tolerations []v1.Toleration, priorityClassName string) func() error {
  1116. ginkgo.By(fmt.Sprintf("Running RC which reserves %v MB of memory", megabytes))
  1117. request := int64(1024 * 1024 * megabytes / replicas)
  1118. config := &testutils.RCConfig{
  1119. Client: f.ClientSet,
  1120. Name: id,
  1121. Namespace: f.Namespace.Name,
  1122. Timeout: timeout,
  1123. Image: imageutils.GetPauseImageName(),
  1124. Replicas: replicas,
  1125. MemRequest: request,
  1126. NodeSelector: selector,
  1127. Tolerations: tolerations,
  1128. PriorityClassName: priorityClassName,
  1129. }
  1130. for start := time.Now(); time.Since(start) < rcCreationRetryTimeout; time.Sleep(rcCreationRetryDelay) {
  1131. err := e2erc.RunRC(*config)
  1132. if err != nil && strings.Contains(err.Error(), "Error creating replication controller") {
  1133. klog.Warningf("Failed to create memory reservation: %v", err)
  1134. continue
  1135. }
  1136. if expectRunning {
  1137. framework.ExpectNoError(err)
  1138. }
  1139. return func() error {
  1140. return e2erc.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, id)
  1141. }
  1142. }
  1143. framework.Failf("Failed to reserve memory within timeout")
  1144. return nil
  1145. }
  1146. // ReserveMemoryWithPriority creates a replication controller with pods with priority that, in summation,
  1147. // request the specified amount of memory.
  1148. func ReserveMemoryWithPriority(f *framework.Framework, id string, replicas, megabytes int, expectRunning bool, timeout time.Duration, priorityClassName string) func() error {
  1149. return reserveMemory(f, id, replicas, megabytes, expectRunning, timeout, nil, nil, priorityClassName)
  1150. }
  1151. // ReserveMemoryWithSelectorAndTolerations creates a replication controller with pods with node selector that, in summation,
  1152. // request the specified amount of memory.
  1153. func ReserveMemoryWithSelectorAndTolerations(f *framework.Framework, id string, replicas, megabytes int, expectRunning bool, timeout time.Duration, selector map[string]string, tolerations []v1.Toleration) func() error {
  1154. return reserveMemory(f, id, replicas, megabytes, expectRunning, timeout, selector, tolerations, "")
  1155. }
  1156. // ReserveMemory creates a replication controller with pods that, in summation,
  1157. // request the specified amount of memory.
  1158. func ReserveMemory(f *framework.Framework, id string, replicas, megabytes int, expectRunning bool, timeout time.Duration) func() error {
  1159. return reserveMemory(f, id, replicas, megabytes, expectRunning, timeout, nil, nil, "")
  1160. }
  1161. // WaitForClusterSizeFunc waits until the cluster size matches the given function.
  1162. func WaitForClusterSizeFunc(c clientset.Interface, sizeFunc func(int) bool, timeout time.Duration) error {
  1163. return WaitForClusterSizeFuncWithUnready(c, sizeFunc, timeout, 0)
  1164. }
  1165. // WaitForClusterSizeFuncWithUnready waits until the cluster size matches the given function and assumes some unready nodes.
  1166. func WaitForClusterSizeFuncWithUnready(c clientset.Interface, sizeFunc func(int) bool, timeout time.Duration, expectedUnready int) error {
  1167. for start := time.Now(); time.Since(start) < timeout; time.Sleep(20 * time.Second) {
  1168. nodes, err := c.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{FieldSelector: fields.Set{
  1169. "spec.unschedulable": "false",
  1170. }.AsSelector().String()})
  1171. if err != nil {
  1172. klog.Warningf("Failed to list nodes: %v", err)
  1173. continue
  1174. }
  1175. numNodes := len(nodes.Items)
  1176. // Filter out not-ready nodes.
  1177. e2enode.Filter(nodes, func(node v1.Node) bool {
  1178. return e2enode.IsConditionSetAsExpected(&node, v1.NodeReady, true)
  1179. })
  1180. numReady := len(nodes.Items)
  1181. if numNodes == numReady+expectedUnready && sizeFunc(numNodes) {
  1182. klog.Infof("Cluster has reached the desired size")
  1183. return nil
  1184. }
  1185. klog.Infof("Waiting for cluster with func, current size %d, not ready nodes %d", numNodes, numNodes-numReady)
  1186. }
  1187. return fmt.Errorf("timeout waiting %v for appropriate cluster size", timeout)
  1188. }
  1189. func waitForCaPodsReadyInNamespace(f *framework.Framework, c clientset.Interface, tolerateUnreadyCount int) error {
  1190. var notready []string
  1191. for start := time.Now(); time.Now().Before(start.Add(scaleUpTimeout)); time.Sleep(20 * time.Second) {
  1192. pods, err := c.CoreV1().Pods(f.Namespace.Name).List(context.TODO(), metav1.ListOptions{})
  1193. if err != nil {
  1194. return fmt.Errorf("failed to get pods: %v", err)
  1195. }
  1196. notready = make([]string, 0)
  1197. for _, pod := range pods.Items {
  1198. ready := false
  1199. for _, c := range pod.Status.Conditions {
  1200. if c.Type == v1.PodReady && c.Status == v1.ConditionTrue {
  1201. ready = true
  1202. }
  1203. }
  1204. // Failed pods in this context generally mean that they have been
  1205. // double scheduled onto a node, but then failed a constraint check.
  1206. if pod.Status.Phase == v1.PodFailed {
  1207. klog.Warningf("Pod has failed: %v", pod)
  1208. }
  1209. if !ready && pod.Status.Phase != v1.PodFailed {
  1210. notready = append(notready, pod.Name)
  1211. }
  1212. }
  1213. if len(notready) <= tolerateUnreadyCount {
  1214. klog.Infof("sufficient number of pods ready. Tolerating %d unready", tolerateUnreadyCount)
  1215. return nil
  1216. }
  1217. klog.Infof("Too many pods are not ready yet: %v", notready)
  1218. }
  1219. klog.Info("Timeout on waiting for pods being ready")
  1220. klog.Info(framework.RunKubectlOrDie(f.Namespace.Name, "get", "pods", "-o", "json", "--all-namespaces"))
  1221. klog.Info(framework.RunKubectlOrDie(f.Namespace.Name, "get", "nodes", "-o", "json"))
  1222. // Some pods are still not running.
  1223. return fmt.Errorf("Too many pods are still not running: %v", notready)
  1224. }
  1225. func waitForAllCaPodsReadyInNamespace(f *framework.Framework, c clientset.Interface) error {
  1226. return waitForCaPodsReadyInNamespace(f, c, 0)
  1227. }
  1228. func getAnyNode(c clientset.Interface) *v1.Node {
  1229. nodes, err := c.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{FieldSelector: fields.Set{
  1230. "spec.unschedulable": "false",
  1231. }.AsSelector().String()})
  1232. if err != nil {
  1233. klog.Errorf("Failed to get node list: %v", err)
  1234. return nil
  1235. }
  1236. if len(nodes.Items) == 0 {
  1237. klog.Errorf("No nodes")
  1238. return nil
  1239. }
  1240. return &nodes.Items[0]
  1241. }
  1242. func setMigSizes(sizes map[string]int) bool {
  1243. madeChanges := false
  1244. for mig, desiredSize := range sizes {
  1245. currentSize, err := framework.GroupSize(mig)
  1246. framework.ExpectNoError(err)
  1247. if desiredSize != currentSize {
  1248. ginkgo.By(fmt.Sprintf("Setting size of %s to %d", mig, desiredSize))
  1249. err = framework.ResizeGroup(mig, int32(desiredSize))
  1250. framework.ExpectNoError(err)
  1251. madeChanges = true
  1252. }
  1253. }
  1254. return madeChanges
  1255. }
  1256. func drainNode(f *framework.Framework, node *v1.Node) {
  1257. ginkgo.By("Make the single node unschedulable")
  1258. makeNodeUnschedulable(f.ClientSet, node)
  1259. ginkgo.By("Manually drain the single node")
  1260. podOpts := metav1.ListOptions{FieldSelector: fields.OneTermEqualSelector(api.PodHostField, node.Name).String()}
  1261. pods, err := f.ClientSet.CoreV1().Pods(metav1.NamespaceAll).List(context.TODO(), podOpts)
  1262. framework.ExpectNoError(err)
  1263. for _, pod := range pods.Items {
  1264. err = f.ClientSet.CoreV1().Pods(pod.Namespace).Delete(context.TODO(), pod.Name, metav1.NewDeleteOptions(0))
  1265. framework.ExpectNoError(err)
  1266. }
  1267. }
  1268. func makeNodeUnschedulable(c clientset.Interface, node *v1.Node) error {
  1269. ginkgo.By(fmt.Sprintf("Taint node %s", node.Name))
  1270. for j := 0; j < 3; j++ {
  1271. freshNode, err := c.CoreV1().Nodes().Get(context.TODO(), node.Name, metav1.GetOptions{})
  1272. if err != nil {
  1273. return err
  1274. }
  1275. for _, taint := range freshNode.Spec.Taints {
  1276. if taint.Key == disabledTaint {
  1277. return nil
  1278. }
  1279. }
  1280. freshNode.Spec.Taints = append(freshNode.Spec.Taints, v1.Taint{
  1281. Key: disabledTaint,
  1282. Value: "DisabledForTest",
  1283. Effect: v1.TaintEffectNoSchedule,
  1284. })
  1285. _, err = c.CoreV1().Nodes().Update(context.TODO(), freshNode, metav1.UpdateOptions{})
  1286. if err == nil {
  1287. return nil
  1288. }
  1289. if !apierrors.IsConflict(err) {
  1290. return err
  1291. }
  1292. klog.Warningf("Got 409 conflict when trying to taint node, retries left: %v", 3-j)
  1293. }
  1294. return fmt.Errorf("Failed to taint node in allowed number of retries")
  1295. }
  1296. // CriticalAddonsOnlyError implements the `error` interface, and signifies the
  1297. // presence of the `CriticalAddonsOnly` taint on the node.
  1298. type CriticalAddonsOnlyError struct{}
  1299. func (CriticalAddonsOnlyError) Error() string {
  1300. return fmt.Sprintf("CriticalAddonsOnly taint found on node")
  1301. }
  1302. func makeNodeSchedulable(c clientset.Interface, node *v1.Node, failOnCriticalAddonsOnly bool) error {
  1303. ginkgo.By(fmt.Sprintf("Remove taint from node %s", node.Name))
  1304. for j := 0; j < 3; j++ {
  1305. freshNode, err := c.CoreV1().Nodes().Get(context.TODO(), node.Name, metav1.GetOptions{})
  1306. if err != nil {
  1307. return err
  1308. }
  1309. var newTaints []v1.Taint
  1310. for _, taint := range freshNode.Spec.Taints {
  1311. if failOnCriticalAddonsOnly && taint.Key == criticalAddonsOnlyTaint {
  1312. return CriticalAddonsOnlyError{}
  1313. }
  1314. if taint.Key != disabledTaint {
  1315. newTaints = append(newTaints, taint)
  1316. }
  1317. }
  1318. if len(newTaints) == len(freshNode.Spec.Taints) {
  1319. return nil
  1320. }
  1321. freshNode.Spec.Taints = newTaints
  1322. _, err = c.CoreV1().Nodes().Update(context.TODO(), freshNode, metav1.UpdateOptions{})
  1323. if err == nil {
  1324. return nil
  1325. }
  1326. if !apierrors.IsConflict(err) {
  1327. return err
  1328. }
  1329. klog.Warningf("Got 409 conflict when trying to taint node, retries left: %v", 3-j)
  1330. }
  1331. return fmt.Errorf("Failed to remove taint from node in allowed number of retries")
  1332. }
  1333. // ScheduleAnySingleGpuPod schedules a pod which requires single GPU of any type
  1334. func ScheduleAnySingleGpuPod(f *framework.Framework, id string) error {
  1335. return ScheduleGpuPod(f, id, "", 1)
  1336. }
  1337. // ScheduleGpuPod schedules a pod which requires a given number of gpus of given type
  1338. func ScheduleGpuPod(f *framework.Framework, id string, gpuType string, gpuLimit int64) error {
  1339. config := &testutils.RCConfig{
  1340. Client: f.ClientSet,
  1341. Name: id,
  1342. Namespace: f.Namespace.Name,
  1343. Timeout: 3 * scaleUpTimeout, // spinning up GPU node is slow
  1344. Image: imageutils.GetPauseImageName(),
  1345. Replicas: 1,
  1346. GpuLimit: gpuLimit,
  1347. Labels: map[string]string{"requires-gpu": "yes"},
  1348. }
  1349. if gpuType != "" {
  1350. config.NodeSelector = map[string]string{gpuLabel: gpuType}
  1351. }
  1352. err := e2erc.RunRC(*config)
  1353. if err != nil {
  1354. return err
  1355. }
  1356. return nil
  1357. }
  1358. // Create an RC running a given number of pods with anti-affinity
  1359. func runAntiAffinityPods(f *framework.Framework, namespace string, pods int, id string, podLabels, antiAffinityLabels map[string]string) error {
  1360. config := &testutils.RCConfig{
  1361. Affinity: buildAntiAffinity(antiAffinityLabels),
  1362. Client: f.ClientSet,
  1363. Name: id,
  1364. Namespace: namespace,
  1365. Timeout: scaleUpTimeout,
  1366. Image: imageutils.GetPauseImageName(),
  1367. Replicas: pods,
  1368. Labels: podLabels,
  1369. }
  1370. err := e2erc.RunRC(*config)
  1371. if err != nil {
  1372. return err
  1373. }
  1374. _, err = f.ClientSet.CoreV1().ReplicationControllers(namespace).Get(context.TODO(), id, metav1.GetOptions{})
  1375. if err != nil {
  1376. return err
  1377. }
  1378. return nil
  1379. }
  1380. func runVolumeAntiAffinityPods(f *framework.Framework, namespace string, pods int, id string, podLabels, antiAffinityLabels map[string]string, volumes []v1.Volume) error {
  1381. config := &testutils.RCConfig{
  1382. Affinity: buildAntiAffinity(antiAffinityLabels),
  1383. Volumes: volumes,
  1384. Client: f.ClientSet,
  1385. Name: id,
  1386. Namespace: namespace,
  1387. Timeout: scaleUpTimeout,
  1388. Image: imageutils.GetPauseImageName(),
  1389. Replicas: pods,
  1390. Labels: podLabels,
  1391. }
  1392. err := e2erc.RunRC(*config)
  1393. if err != nil {
  1394. return err
  1395. }
  1396. _, err = f.ClientSet.CoreV1().ReplicationControllers(namespace).Get(context.TODO(), id, metav1.GetOptions{})
  1397. if err != nil {
  1398. return err
  1399. }
  1400. return nil
  1401. }
  1402. var emptyDirVolumes = []v1.Volume{
  1403. {
  1404. Name: "empty-volume",
  1405. VolumeSource: v1.VolumeSource{
  1406. EmptyDir: &v1.EmptyDirVolumeSource{},
  1407. },
  1408. },
  1409. }
  1410. func buildVolumes(pv *v1.PersistentVolume, pvc *v1.PersistentVolumeClaim) []v1.Volume {
  1411. return []v1.Volume{
  1412. {
  1413. Name: pv.Name,
  1414. VolumeSource: v1.VolumeSource{
  1415. PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
  1416. ClaimName: pvc.Name,
  1417. ReadOnly: false,
  1418. },
  1419. },
  1420. },
  1421. }
  1422. }
  1423. func buildAntiAffinity(labels map[string]string) *v1.Affinity {
  1424. return &v1.Affinity{
  1425. PodAntiAffinity: &v1.PodAntiAffinity{
  1426. RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
  1427. {
  1428. LabelSelector: &metav1.LabelSelector{
  1429. MatchLabels: labels,
  1430. },
  1431. TopologyKey: "kubernetes.io/hostname",
  1432. },
  1433. },
  1434. },
  1435. }
  1436. }
  1437. // Create an RC running a given number of pods on each node without adding any constraint forcing
  1438. // such pod distribution. This is meant to create a bunch of underutilized (but not unused) nodes
  1439. // with pods that can be rescheduled on different nodes.
  1440. // This is achieved using the following method:
  1441. // 1. disable scheduling on each node
  1442. // 2. create an empty RC
  1443. // 3. for each node:
  1444. // 3a. enable scheduling on that node
  1445. // 3b. increase number of replicas in RC by podsPerNode
  1446. func runReplicatedPodOnEachNode(f *framework.Framework, nodes []v1.Node, namespace string, podsPerNode int, id string, labels map[string]string, memRequest int64) error {
  1447. ginkgo.By("Run a pod on each node")
  1448. for _, node := range nodes {
  1449. err := makeNodeUnschedulable(f.ClientSet, &node)
  1450. defer func(n v1.Node) {
  1451. makeNodeSchedulable(f.ClientSet, &n, false)
  1452. }(node)
  1453. if err != nil {
  1454. return err
  1455. }
  1456. }
  1457. config := &testutils.RCConfig{
  1458. Client: f.ClientSet,
  1459. Name: id,
  1460. Namespace: namespace,
  1461. Timeout: defaultTimeout,
  1462. Image: imageutils.GetPauseImageName(),
  1463. Replicas: 0,
  1464. Labels: labels,
  1465. MemRequest: memRequest,
  1466. }
  1467. err := e2erc.RunRC(*config)
  1468. if err != nil {
  1469. return err
  1470. }
  1471. rc, err := f.ClientSet.CoreV1().ReplicationControllers(namespace).Get(context.TODO(), id, metav1.GetOptions{})
  1472. if err != nil {
  1473. return err
  1474. }
  1475. for i, node := range nodes {
  1476. err = makeNodeSchedulable(f.ClientSet, &node, false)
  1477. if err != nil {
  1478. return err
  1479. }
  1480. // Update replicas count, to create new pods that will be allocated on node
  1481. // (we retry 409 errors in case rc reference got out of sync)
  1482. for j := 0; j < 3; j++ {
  1483. *rc.Spec.Replicas = int32((i + 1) * podsPerNode)
  1484. rc, err = f.ClientSet.CoreV1().ReplicationControllers(namespace).Update(context.TODO(), rc, metav1.UpdateOptions{})
  1485. if err == nil {
  1486. break
  1487. }
  1488. if !apierrors.IsConflict(err) {
  1489. return err
  1490. }
  1491. klog.Warningf("Got 409 conflict when trying to scale RC, retries left: %v", 3-j)
  1492. rc, err = f.ClientSet.CoreV1().ReplicationControllers(namespace).Get(context.TODO(), id, metav1.GetOptions{})
  1493. if err != nil {
  1494. return err
  1495. }
  1496. }
  1497. err = wait.PollImmediate(5*time.Second, podTimeout, func() (bool, error) {
  1498. rc, err = f.ClientSet.CoreV1().ReplicationControllers(namespace).Get(context.TODO(), id, metav1.GetOptions{})
  1499. if err != nil || rc.Status.ReadyReplicas < int32((i+1)*podsPerNode) {
  1500. return false, nil
  1501. }
  1502. return true, nil
  1503. })
  1504. if err != nil {
  1505. return fmt.Errorf("failed to coerce RC into spawning a pod on node %s within timeout", node.Name)
  1506. }
  1507. err = makeNodeUnschedulable(f.ClientSet, &node)
  1508. if err != nil {
  1509. return err
  1510. }
  1511. }
  1512. return nil
  1513. }
  1514. // Increase cluster size by newNodesForScaledownTests to create some unused nodes
  1515. // that can be later removed by cluster autoscaler.
  1516. func manuallyIncreaseClusterSize(f *framework.Framework, originalSizes map[string]int) int {
  1517. ginkgo.By("Manually increase cluster size")
  1518. increasedSize := 0
  1519. newSizes := make(map[string]int)
  1520. for key, val := range originalSizes {
  1521. newSizes[key] = val + newNodesForScaledownTests
  1522. increasedSize += val + newNodesForScaledownTests
  1523. }
  1524. setMigSizes(newSizes)
  1525. checkClusterSize := func(size int) bool {
  1526. if size >= increasedSize {
  1527. return true
  1528. }
  1529. resized := setMigSizes(newSizes)
  1530. if resized {
  1531. klog.Warning("Unexpected node group size while waiting for cluster resize. Setting size to target again.")
  1532. }
  1533. return false
  1534. }
  1535. framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet, checkClusterSize, manualResizeTimeout))
  1536. return increasedSize
  1537. }
  1538. // Try to get clusterwide health from CA status configmap.
  1539. // Status configmap is not parsing-friendly, so evil regexpery follows.
  1540. func getClusterwideStatus(c clientset.Interface) (string, error) {
  1541. configMap, err := c.CoreV1().ConfigMaps("kube-system").Get(context.TODO(), "cluster-autoscaler-status", metav1.GetOptions{})
  1542. if err != nil {
  1543. return "", err
  1544. }
  1545. status, ok := configMap.Data["status"]
  1546. if !ok {
  1547. return "", fmt.Errorf("Status information not found in configmap")
  1548. }
  1549. matcher, err := regexp.Compile("Cluster-wide:\\s*\n\\s*Health:\\s*([A-Za-z]+)")
  1550. if err != nil {
  1551. return "", err
  1552. }
  1553. result := matcher.FindStringSubmatch(status)
  1554. if len(result) < 2 {
  1555. return "", fmt.Errorf("Failed to parse CA status configmap, raw status: %v", status)
  1556. }
  1557. return result[1], nil
  1558. }
  1559. type scaleUpStatus struct {
  1560. status string
  1561. ready int
  1562. target int
  1563. timestamp time.Time
  1564. }
  1565. // Try to get timestamp from status.
  1566. // Status configmap is not parsing-friendly, so evil regexpery follows.
  1567. func getStatusTimestamp(status string) (time.Time, error) {
  1568. timestampMatcher, err := regexp.Compile("Cluster-autoscaler status at \\s*([0-9\\-]+ [0-9]+:[0-9]+:[0-9]+\\.[0-9]+ \\+[0-9]+ [A-Za-z]+)")
  1569. if err != nil {
  1570. return time.Time{}, err
  1571. }
  1572. timestampMatch := timestampMatcher.FindStringSubmatch(status)
  1573. if len(timestampMatch) < 2 {
  1574. return time.Time{}, fmt.Errorf("Failed to parse CA status timestamp, raw status: %v", status)
  1575. }
  1576. timestamp, err := time.Parse(timestampFormat, timestampMatch[1])
  1577. if err != nil {
  1578. return time.Time{}, err
  1579. }
  1580. return timestamp, nil
  1581. }
  1582. // Try to get scaleup statuses of all node groups.
  1583. // Status configmap is not parsing-friendly, so evil regexpery follows.
  1584. func getScaleUpStatus(c clientset.Interface) (*scaleUpStatus, error) {
  1585. configMap, err := c.CoreV1().ConfigMaps("kube-system").Get(context.TODO(), "cluster-autoscaler-status", metav1.GetOptions{})
  1586. if err != nil {
  1587. return nil, err
  1588. }
  1589. status, ok := configMap.Data["status"]
  1590. if !ok {
  1591. return nil, fmt.Errorf("Status information not found in configmap")
  1592. }
  1593. timestamp, err := getStatusTimestamp(status)
  1594. if err != nil {
  1595. return nil, err
  1596. }
  1597. matcher, err := regexp.Compile("s*ScaleUp:\\s*([A-Za-z]+)\\s*\\(ready=([0-9]+)\\s*cloudProviderTarget=([0-9]+)\\s*\\)")
  1598. if err != nil {
  1599. return nil, err
  1600. }
  1601. matches := matcher.FindAllStringSubmatch(status, -1)
  1602. if len(matches) < 1 {
  1603. return nil, fmt.Errorf("Failed to parse CA status configmap, raw status: %v", status)
  1604. }
  1605. result := scaleUpStatus{
  1606. status: caNoScaleUpStatus,
  1607. ready: 0,
  1608. target: 0,
  1609. timestamp: timestamp,
  1610. }
  1611. for _, match := range matches {
  1612. if match[1] == caOngoingScaleUpStatus {
  1613. result.status = caOngoingScaleUpStatus
  1614. }
  1615. newReady, err := strconv.Atoi(match[2])
  1616. if err != nil {
  1617. return nil, err
  1618. }
  1619. result.ready += newReady
  1620. newTarget, err := strconv.Atoi(match[3])
  1621. if err != nil {
  1622. return nil, err
  1623. }
  1624. result.target += newTarget
  1625. }
  1626. klog.Infof("Cluster-Autoscaler scale-up status: %v (%v, %v)", result.status, result.ready, result.target)
  1627. return &result, nil
  1628. }
  1629. func waitForScaleUpStatus(c clientset.Interface, cond func(s *scaleUpStatus) bool, timeout time.Duration) (*scaleUpStatus, error) {
  1630. var finalErr error
  1631. var status *scaleUpStatus
  1632. err := wait.PollImmediate(5*time.Second, timeout, func() (bool, error) {
  1633. status, finalErr = getScaleUpStatus(c)
  1634. if finalErr != nil {
  1635. return false, nil
  1636. }
  1637. if status.timestamp.Add(freshStatusLimit).Before(time.Now()) {
  1638. // stale status
  1639. finalErr = fmt.Errorf("Status too old")
  1640. return false, nil
  1641. }
  1642. return cond(status), nil
  1643. })
  1644. if err != nil {
  1645. err = fmt.Errorf("Failed to find expected scale up status: %v, last status: %v, final err: %v", err, status, finalErr)
  1646. }
  1647. return status, err
  1648. }
  1649. // This is a temporary fix to allow CA to migrate some kube-system pods
  1650. // TODO: Remove this when the PDB is added for some of those components
  1651. func addKubeSystemPdbs(f *framework.Framework) (func(), error) {
  1652. ginkgo.By("Create PodDisruptionBudgets for kube-system components, so they can be migrated if required")
  1653. var newPdbs []string
  1654. cleanup := func() {
  1655. var finalErr error
  1656. for _, newPdbName := range newPdbs {
  1657. ginkgo.By(fmt.Sprintf("Delete PodDisruptionBudget %v", newPdbName))
  1658. err := f.ClientSet.PolicyV1beta1().PodDisruptionBudgets("kube-system").Delete(context.TODO(), newPdbName, &metav1.DeleteOptions{})
  1659. if err != nil {
  1660. // log error, but attempt to remove other pdbs
  1661. klog.Errorf("Failed to delete PodDisruptionBudget %v, err: %v", newPdbName, err)
  1662. finalErr = err
  1663. }
  1664. }
  1665. if finalErr != nil {
  1666. framework.Failf("Error during PodDisruptionBudget cleanup: %v", finalErr)
  1667. }
  1668. }
  1669. type pdbInfo struct {
  1670. label string
  1671. minAvailable int
  1672. }
  1673. pdbsToAdd := []pdbInfo{
  1674. {label: "kube-dns", minAvailable: 1},
  1675. {label: "kube-dns-autoscaler", minAvailable: 0},
  1676. {label: "metrics-server", minAvailable: 0},
  1677. {label: "kubernetes-dashboard", minAvailable: 0},
  1678. {label: "glbc", minAvailable: 0},
  1679. }
  1680. for _, pdbData := range pdbsToAdd {
  1681. ginkgo.By(fmt.Sprintf("Create PodDisruptionBudget for %v", pdbData.label))
  1682. labelMap := map[string]string{"k8s-app": pdbData.label}
  1683. pdbName := fmt.Sprintf("test-pdb-for-%v", pdbData.label)
  1684. minAvailable := intstr.FromInt(pdbData.minAvailable)
  1685. pdb := &policyv1beta1.PodDisruptionBudget{
  1686. ObjectMeta: metav1.ObjectMeta{
  1687. Name: pdbName,
  1688. Namespace: "kube-system",
  1689. },
  1690. Spec: policyv1beta1.PodDisruptionBudgetSpec{
  1691. Selector: &metav1.LabelSelector{MatchLabels: labelMap},
  1692. MinAvailable: &minAvailable,
  1693. },
  1694. }
  1695. _, err := f.ClientSet.PolicyV1beta1().PodDisruptionBudgets("kube-system").Create(context.TODO(), pdb, metav1.CreateOptions{})
  1696. newPdbs = append(newPdbs, pdbName)
  1697. if err != nil {
  1698. return cleanup, err
  1699. }
  1700. }
  1701. return cleanup, nil
  1702. }
  1703. func createPriorityClasses(f *framework.Framework) func() {
  1704. priorityClasses := map[string]int32{
  1705. expendablePriorityClassName: -15,
  1706. highPriorityClassName: 1000,
  1707. }
  1708. for className, priority := range priorityClasses {
  1709. _, err := f.ClientSet.SchedulingV1().PriorityClasses().Create(context.TODO(), &schedulingv1.PriorityClass{ObjectMeta: metav1.ObjectMeta{Name: className}, Value: priority}, metav1.CreateOptions{})
  1710. if err != nil {
  1711. klog.Errorf("Error creating priority class: %v", err)
  1712. }
  1713. framework.ExpectEqual(err == nil || apierrors.IsAlreadyExists(err), true)
  1714. }
  1715. return func() {
  1716. for className := range priorityClasses {
  1717. err := f.ClientSet.SchedulingV1().PriorityClasses().Delete(context.TODO(), className, nil)
  1718. if err != nil {
  1719. klog.Errorf("Error deleting priority class: %v", err)
  1720. }
  1721. }
  1722. }
  1723. }