util.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. /*
  2. Copyright 2017 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 deployment
  14. import (
  15. "fmt"
  16. "net/http/httptest"
  17. "sync"
  18. "testing"
  19. "time"
  20. apps "k8s.io/api/apps/v1"
  21. "k8s.io/api/core/v1"
  22. extensions "k8s.io/api/extensions/v1beta1"
  23. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  24. "k8s.io/apimachinery/pkg/util/intstr"
  25. "k8s.io/apimachinery/pkg/util/wait"
  26. "k8s.io/client-go/informers"
  27. clientset "k8s.io/client-go/kubernetes"
  28. restclient "k8s.io/client-go/rest"
  29. podutil "k8s.io/kubernetes/pkg/api/v1/pod"
  30. "k8s.io/kubernetes/pkg/controller/deployment"
  31. deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
  32. "k8s.io/kubernetes/pkg/controller/replicaset"
  33. "k8s.io/kubernetes/test/integration/framework"
  34. testutil "k8s.io/kubernetes/test/utils"
  35. )
  36. const (
  37. pollInterval = 100 * time.Millisecond
  38. pollTimeout = 60 * time.Second
  39. fakeContainerName = "fake-name"
  40. fakeImage = "fakeimage"
  41. )
  42. var pauseFn = func(update *apps.Deployment) {
  43. update.Spec.Paused = true
  44. }
  45. var resumeFn = func(update *apps.Deployment) {
  46. update.Spec.Paused = false
  47. }
  48. type deploymentTester struct {
  49. t *testing.T
  50. c clientset.Interface
  51. deployment *apps.Deployment
  52. }
  53. func testLabels() map[string]string {
  54. return map[string]string{"name": "test"}
  55. }
  56. // newDeployment returns a RollingUpdate Deployment with a fake container image
  57. func newDeployment(name, ns string, replicas int32) *apps.Deployment {
  58. return &apps.Deployment{
  59. TypeMeta: metav1.TypeMeta{
  60. Kind: "Deployment",
  61. APIVersion: "apps/v1",
  62. },
  63. ObjectMeta: metav1.ObjectMeta{
  64. Namespace: ns,
  65. Name: name,
  66. },
  67. Spec: apps.DeploymentSpec{
  68. Replicas: &replicas,
  69. Selector: &metav1.LabelSelector{MatchLabels: testLabels()},
  70. Strategy: apps.DeploymentStrategy{
  71. Type: apps.RollingUpdateDeploymentStrategyType,
  72. RollingUpdate: new(apps.RollingUpdateDeployment),
  73. },
  74. Template: v1.PodTemplateSpec{
  75. ObjectMeta: metav1.ObjectMeta{
  76. Labels: testLabels(),
  77. },
  78. Spec: v1.PodSpec{
  79. Containers: []v1.Container{
  80. {
  81. Name: fakeContainerName,
  82. Image: fakeImage,
  83. },
  84. },
  85. },
  86. },
  87. },
  88. }
  89. }
  90. func newReplicaSet(name, ns string, replicas int32) *apps.ReplicaSet {
  91. return &apps.ReplicaSet{
  92. TypeMeta: metav1.TypeMeta{
  93. Kind: "ReplicaSet",
  94. APIVersion: "apps/v1",
  95. },
  96. ObjectMeta: metav1.ObjectMeta{
  97. Namespace: ns,
  98. Name: name,
  99. Labels: testLabels(),
  100. },
  101. Spec: apps.ReplicaSetSpec{
  102. Selector: &metav1.LabelSelector{
  103. MatchLabels: testLabels(),
  104. },
  105. Replicas: &replicas,
  106. Template: v1.PodTemplateSpec{
  107. ObjectMeta: metav1.ObjectMeta{
  108. Labels: testLabels(),
  109. },
  110. Spec: v1.PodSpec{
  111. Containers: []v1.Container{
  112. {
  113. Name: fakeContainerName,
  114. Image: fakeImage,
  115. },
  116. },
  117. },
  118. },
  119. },
  120. }
  121. }
  122. func newDeploymentRollback(name string, annotations map[string]string, revision int64) *extensions.DeploymentRollback {
  123. return &extensions.DeploymentRollback{
  124. Name: name,
  125. UpdatedAnnotations: annotations,
  126. RollbackTo: extensions.RollbackConfig{Revision: revision},
  127. }
  128. }
  129. // dcSetup sets up necessities for Deployment integration test, including master, apiserver, informers, and clientset
  130. func dcSetup(t *testing.T) (*httptest.Server, framework.CloseFunc, *replicaset.ReplicaSetController, *deployment.DeploymentController, informers.SharedInformerFactory, clientset.Interface) {
  131. masterConfig := framework.NewIntegrationTestMasterConfig()
  132. _, s, closeFn := framework.RunAMaster(masterConfig)
  133. config := restclient.Config{Host: s.URL}
  134. clientSet, err := clientset.NewForConfig(&config)
  135. if err != nil {
  136. t.Fatalf("error in create clientset: %v", err)
  137. }
  138. resyncPeriod := 12 * time.Hour
  139. informers := informers.NewSharedInformerFactory(clientset.NewForConfigOrDie(restclient.AddUserAgent(&config, "deployment-informers")), resyncPeriod)
  140. dc, err := deployment.NewDeploymentController(
  141. informers.Apps().V1().Deployments(),
  142. informers.Apps().V1().ReplicaSets(),
  143. informers.Core().V1().Pods(),
  144. clientset.NewForConfigOrDie(restclient.AddUserAgent(&config, "deployment-controller")),
  145. )
  146. if err != nil {
  147. t.Fatalf("error creating Deployment controller: %v", err)
  148. }
  149. rm := replicaset.NewReplicaSetController(
  150. informers.Apps().V1().ReplicaSets(),
  151. informers.Core().V1().Pods(),
  152. clientset.NewForConfigOrDie(restclient.AddUserAgent(&config, "replicaset-controller")),
  153. replicaset.BurstReplicas,
  154. )
  155. return s, closeFn, rm, dc, informers, clientSet
  156. }
  157. // dcSimpleSetup sets up necessities for Deployment integration test, including master, apiserver,
  158. // and clientset, but not controllers and informers
  159. func dcSimpleSetup(t *testing.T) (*httptest.Server, framework.CloseFunc, clientset.Interface) {
  160. masterConfig := framework.NewIntegrationTestMasterConfig()
  161. _, s, closeFn := framework.RunAMaster(masterConfig)
  162. config := restclient.Config{Host: s.URL}
  163. clientSet, err := clientset.NewForConfig(&config)
  164. if err != nil {
  165. t.Fatalf("error in create clientset: %v", err)
  166. }
  167. return s, closeFn, clientSet
  168. }
  169. // addPodConditionReady sets given pod status to ready at given time
  170. func addPodConditionReady(pod *v1.Pod, time metav1.Time) {
  171. pod.Status = v1.PodStatus{
  172. Phase: v1.PodRunning,
  173. Conditions: []v1.PodCondition{
  174. {
  175. Type: v1.PodReady,
  176. Status: v1.ConditionTrue,
  177. LastTransitionTime: time,
  178. },
  179. },
  180. }
  181. }
  182. func (d *deploymentTester) waitForDeploymentRevisionAndImage(revision, image string) error {
  183. if err := testutil.WaitForDeploymentRevisionAndImage(d.c, d.deployment.Namespace, d.deployment.Name, revision, image, d.t.Logf, pollInterval, pollTimeout); err != nil {
  184. return fmt.Errorf("failed to wait for Deployment revision %s: %v", d.deployment.Name, err)
  185. }
  186. return nil
  187. }
  188. func markPodReady(c clientset.Interface, ns string, pod *v1.Pod) error {
  189. addPodConditionReady(pod, metav1.Now())
  190. _, err := c.CoreV1().Pods(ns).UpdateStatus(pod)
  191. return err
  192. }
  193. func intOrStrP(num int) *intstr.IntOrString {
  194. intstr := intstr.FromInt(num)
  195. return &intstr
  196. }
  197. // markUpdatedPodsReady manually marks updated Deployment pods status to ready,
  198. // until the deployment is complete
  199. func (d *deploymentTester) markUpdatedPodsReady(wg *sync.WaitGroup) {
  200. defer wg.Done()
  201. ns := d.deployment.Namespace
  202. err := wait.PollImmediate(pollInterval, pollTimeout, func() (bool, error) {
  203. // We're done when the deployment is complete
  204. if completed, err := d.deploymentComplete(); err != nil {
  205. return false, err
  206. } else if completed {
  207. return true, nil
  208. }
  209. // Otherwise, mark remaining pods as ready
  210. pods, err := d.listUpdatedPods()
  211. if err != nil {
  212. d.t.Log(err)
  213. return false, nil
  214. }
  215. d.t.Logf("%d/%d of deployment pods are created", len(pods), *d.deployment.Spec.Replicas)
  216. for i := range pods {
  217. pod := pods[i]
  218. if podutil.IsPodReady(&pod) {
  219. continue
  220. }
  221. if err = markPodReady(d.c, ns, &pod); err != nil {
  222. d.t.Logf("failed to update Deployment pod %s, will retry later: %v", pod.Name, err)
  223. }
  224. }
  225. return false, nil
  226. })
  227. if err != nil {
  228. d.t.Fatalf("failed to mark updated Deployment pods to ready: %v", err)
  229. }
  230. }
  231. func (d *deploymentTester) deploymentComplete() (bool, error) {
  232. latest, err := d.c.AppsV1().Deployments(d.deployment.Namespace).Get(d.deployment.Name, metav1.GetOptions{})
  233. if err != nil {
  234. return false, err
  235. }
  236. return deploymentutil.DeploymentComplete(d.deployment, &latest.Status), nil
  237. }
  238. // Waits for the deployment to complete, and check rolling update strategy isn't broken at any times.
  239. // Rolling update strategy should not be broken during a rolling update.
  240. func (d *deploymentTester) waitForDeploymentCompleteAndCheckRolling() error {
  241. return testutil.WaitForDeploymentCompleteAndCheckRolling(d.c, d.deployment, d.t.Logf, pollInterval, pollTimeout)
  242. }
  243. // Waits for the deployment to complete, and don't check if rolling update strategy is broken.
  244. // Rolling update strategy is used only during a rolling update, and can be violated in other situations,
  245. // such as shortly after a scaling event or the deployment is just created.
  246. func (d *deploymentTester) waitForDeploymentComplete() error {
  247. return testutil.WaitForDeploymentComplete(d.c, d.deployment, d.t.Logf, pollInterval, pollTimeout)
  248. }
  249. // waitForDeploymentCompleteAndCheckRollingAndMarkPodsReady waits for the Deployment to complete
  250. // while marking updated Deployment pods as ready at the same time.
  251. // Uses hard check to make sure rolling update strategy is not violated at any times.
  252. func (d *deploymentTester) waitForDeploymentCompleteAndCheckRollingAndMarkPodsReady() error {
  253. var wg sync.WaitGroup
  254. // Manually mark updated Deployment pods as ready in a separate goroutine
  255. wg.Add(1)
  256. go d.markUpdatedPodsReady(&wg)
  257. // Wait for goroutine to finish, for all return paths.
  258. defer wg.Wait()
  259. // Wait for the Deployment status to complete while Deployment pods are becoming ready
  260. err := d.waitForDeploymentCompleteAndCheckRolling()
  261. if err != nil {
  262. return fmt.Errorf("failed to wait for Deployment %s to complete: %v", d.deployment.Name, err)
  263. }
  264. return nil
  265. }
  266. // waitForDeploymentCompleteAndMarkPodsReady waits for the Deployment to complete
  267. // while marking updated Deployment pods as ready at the same time.
  268. func (d *deploymentTester) waitForDeploymentCompleteAndMarkPodsReady() error {
  269. var wg sync.WaitGroup
  270. // Manually mark updated Deployment pods as ready in a separate goroutine
  271. wg.Add(1)
  272. go d.markUpdatedPodsReady(&wg)
  273. // Wait for the Deployment status to complete using soft check, while Deployment pods are becoming ready
  274. err := d.waitForDeploymentComplete()
  275. if err != nil {
  276. return fmt.Errorf("failed to wait for Deployment status %s: %v", d.deployment.Name, err)
  277. }
  278. // Wait for goroutine to finish
  279. wg.Wait()
  280. return nil
  281. }
  282. func (d *deploymentTester) updateDeployment(applyUpdate testutil.UpdateDeploymentFunc) (*apps.Deployment, error) {
  283. return testutil.UpdateDeploymentWithRetries(d.c, d.deployment.Namespace, d.deployment.Name, applyUpdate, d.t.Logf, pollInterval, pollTimeout)
  284. }
  285. func (d *deploymentTester) waitForObservedDeployment(desiredGeneration int64) error {
  286. if err := testutil.WaitForObservedDeployment(d.c, d.deployment.Namespace, d.deployment.Name, desiredGeneration); err != nil {
  287. return fmt.Errorf("failed waiting for ObservedGeneration of deployment %s to become %d: %v", d.deployment.Name, desiredGeneration, err)
  288. }
  289. return nil
  290. }
  291. func (d *deploymentTester) getNewReplicaSet() (*apps.ReplicaSet, error) {
  292. deployment, err := d.c.AppsV1().Deployments(d.deployment.Namespace).Get(d.deployment.Name, metav1.GetOptions{})
  293. if err != nil {
  294. return nil, fmt.Errorf("failed retrieving deployment %s: %v", d.deployment.Name, err)
  295. }
  296. rs, err := deploymentutil.GetNewReplicaSet(deployment, d.c.AppsV1())
  297. if err != nil {
  298. return nil, fmt.Errorf("failed retrieving new replicaset of deployment %s: %v", d.deployment.Name, err)
  299. }
  300. return rs, nil
  301. }
  302. func (d *deploymentTester) expectNoNewReplicaSet() error {
  303. rs, err := d.getNewReplicaSet()
  304. if err != nil {
  305. return err
  306. }
  307. if rs != nil {
  308. return fmt.Errorf("expected deployment %s not to create a new replicaset, got %v", d.deployment.Name, rs)
  309. }
  310. return nil
  311. }
  312. func (d *deploymentTester) expectNewReplicaSet() (*apps.ReplicaSet, error) {
  313. rs, err := d.getNewReplicaSet()
  314. if err != nil {
  315. return nil, err
  316. }
  317. if rs == nil {
  318. return nil, fmt.Errorf("expected deployment %s to create a new replicaset, got nil", d.deployment.Name)
  319. }
  320. return rs, nil
  321. }
  322. func (d *deploymentTester) updateReplicaSet(name string, applyUpdate testutil.UpdateReplicaSetFunc) (*apps.ReplicaSet, error) {
  323. return testutil.UpdateReplicaSetWithRetries(d.c, d.deployment.Namespace, name, applyUpdate, d.t.Logf, pollInterval, pollTimeout)
  324. }
  325. func (d *deploymentTester) updateReplicaSetStatus(name string, applyStatusUpdate testutil.UpdateReplicaSetFunc) (*apps.ReplicaSet, error) {
  326. return testutil.UpdateReplicaSetStatusWithRetries(d.c, d.deployment.Namespace, name, applyStatusUpdate, d.t.Logf, pollInterval, pollTimeout)
  327. }
  328. // waitForDeploymentRollbackCleared waits for deployment either started rolling back or doesn't need to rollback.
  329. func (d *deploymentTester) waitForDeploymentRollbackCleared() error {
  330. return testutil.WaitForDeploymentRollbackCleared(d.c, d.deployment.Namespace, d.deployment.Name, pollInterval, pollTimeout)
  331. }
  332. // checkDeploymentRevisionAndImage checks if the input deployment's and its new replica set's revision and image are as expected.
  333. func (d *deploymentTester) checkDeploymentRevisionAndImage(revision, image string) error {
  334. return testutil.CheckDeploymentRevisionAndImage(d.c, d.deployment.Namespace, d.deployment.Name, revision, image)
  335. }
  336. func (d *deploymentTester) waitForDeploymentUpdatedReplicasGTE(minUpdatedReplicas int32) error {
  337. return testutil.WaitForDeploymentUpdatedReplicasGTE(d.c, d.deployment.Namespace, d.deployment.Name, minUpdatedReplicas, d.deployment.Generation, pollInterval, pollTimeout)
  338. }
  339. func (d *deploymentTester) waitForDeploymentWithCondition(reason string, condType apps.DeploymentConditionType) error {
  340. return testutil.WaitForDeploymentWithCondition(d.c, d.deployment.Namespace, d.deployment.Name, reason, condType, d.t.Logf, pollInterval, pollTimeout)
  341. }
  342. func (d *deploymentTester) listUpdatedPods() ([]v1.Pod, error) {
  343. selector, err := metav1.LabelSelectorAsSelector(d.deployment.Spec.Selector)
  344. if err != nil {
  345. return nil, fmt.Errorf("failed to parse deployment selector: %v", err)
  346. }
  347. pods, err := d.c.CoreV1().Pods(d.deployment.Namespace).List(metav1.ListOptions{LabelSelector: selector.String()})
  348. if err != nil {
  349. return nil, fmt.Errorf("failed to list deployment pods, will retry later: %v", err)
  350. }
  351. newRS, err := d.getNewReplicaSet()
  352. if err != nil {
  353. return nil, fmt.Errorf("failed to get new replicaset of deployment %q: %v", d.deployment.Name, err)
  354. }
  355. if newRS == nil {
  356. return nil, fmt.Errorf("unable to find new replicaset of deployment %q", d.deployment.Name)
  357. }
  358. var ownedPods []v1.Pod
  359. for _, pod := range pods.Items {
  360. rs := metav1.GetControllerOf(&pod)
  361. if rs.UID == newRS.UID {
  362. ownedPods = append(ownedPods, pod)
  363. }
  364. }
  365. return ownedPods, nil
  366. }
  367. func (d *deploymentTester) waitRSStable(replicaset *apps.ReplicaSet) error {
  368. return testutil.WaitRSStable(d.t, d.c, replicaset, pollInterval, pollTimeout)
  369. }
  370. func (d *deploymentTester) scaleDeployment(newReplicas int32) error {
  371. var err error
  372. d.deployment, err = d.updateDeployment(func(update *apps.Deployment) {
  373. update.Spec.Replicas = &newReplicas
  374. })
  375. if err != nil {
  376. return fmt.Errorf("failed updating deployment %q: %v", d.deployment.Name, err)
  377. }
  378. if err := d.waitForDeploymentCompleteAndMarkPodsReady(); err != nil {
  379. return err
  380. }
  381. rs, err := d.expectNewReplicaSet()
  382. if err != nil {
  383. return err
  384. }
  385. if *rs.Spec.Replicas != newReplicas {
  386. return fmt.Errorf("expected new replicaset replicas = %d, got %d", newReplicas, *rs.Spec.Replicas)
  387. }
  388. return nil
  389. }
  390. // waitForReadyReplicas waits for number of ready replicas to equal number of replicas.
  391. func (d *deploymentTester) waitForReadyReplicas() error {
  392. if err := wait.PollImmediate(pollInterval, pollTimeout, func() (bool, error) {
  393. deployment, err := d.c.AppsV1().Deployments(d.deployment.Namespace).Get(d.deployment.Name, metav1.GetOptions{})
  394. if err != nil {
  395. return false, fmt.Errorf("failed to get deployment %q: %v", d.deployment.Name, err)
  396. }
  397. return deployment.Status.ReadyReplicas == *deployment.Spec.Replicas, nil
  398. }); err != nil {
  399. return fmt.Errorf("failed to wait for .readyReplicas to equal .replicas: %v", err)
  400. }
  401. return nil
  402. }
  403. // markUpdatedPodsReadyWithoutComplete marks updated Deployment pods as ready without waiting for deployment to complete.
  404. func (d *deploymentTester) markUpdatedPodsReadyWithoutComplete() error {
  405. if err := wait.PollImmediate(pollInterval, pollTimeout, func() (bool, error) {
  406. pods, err := d.listUpdatedPods()
  407. if err != nil {
  408. return false, err
  409. }
  410. for i := range pods {
  411. pod := pods[i]
  412. if podutil.IsPodReady(&pod) {
  413. continue
  414. }
  415. if err = markPodReady(d.c, d.deployment.Namespace, &pod); err != nil {
  416. d.t.Logf("failed to update Deployment pod %q, will retry later: %v", pod.Name, err)
  417. return false, nil
  418. }
  419. }
  420. return true, nil
  421. }); err != nil {
  422. return fmt.Errorf("failed to mark all updated pods as ready: %v", err)
  423. }
  424. return nil
  425. }
  426. // Verify all replicas fields of DeploymentStatus have desired count.
  427. // Immediately return an error when found a non-matching replicas field.
  428. func (d *deploymentTester) checkDeploymentStatusReplicasFields(replicas, updatedReplicas, readyReplicas, availableReplicas, unavailableReplicas int32) error {
  429. deployment, err := d.c.AppsV1().Deployments(d.deployment.Namespace).Get(d.deployment.Name, metav1.GetOptions{})
  430. if err != nil {
  431. return fmt.Errorf("failed to get deployment %q: %v", d.deployment.Name, err)
  432. }
  433. if deployment.Status.Replicas != replicas {
  434. return fmt.Errorf("unexpected .replicas: expect %d, got %d", replicas, deployment.Status.Replicas)
  435. }
  436. if deployment.Status.UpdatedReplicas != updatedReplicas {
  437. return fmt.Errorf("unexpected .updatedReplicas: expect %d, got %d", updatedReplicas, deployment.Status.UpdatedReplicas)
  438. }
  439. if deployment.Status.ReadyReplicas != readyReplicas {
  440. return fmt.Errorf("unexpected .readyReplicas: expect %d, got %d", readyReplicas, deployment.Status.ReadyReplicas)
  441. }
  442. if deployment.Status.AvailableReplicas != availableReplicas {
  443. return fmt.Errorf("unexpected .replicas: expect %d, got %d", availableReplicas, deployment.Status.AvailableReplicas)
  444. }
  445. if deployment.Status.UnavailableReplicas != unavailableReplicas {
  446. return fmt.Errorf("unexpected .replicas: expect %d, got %d", unavailableReplicas, deployment.Status.UnavailableReplicas)
  447. }
  448. return nil
  449. }