PageRenderTime 3904ms CodeModel.GetById 37ms RepoModel.GetById 4ms app.codeStats 1ms

/test/e2e/autoscaling/cluster_size_autoscaling.go

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