k8s.io/kubernetes@v1.29.3/test/e2e/upgrades/apps/deployments.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package apps 18 19 import ( 20 "context" 21 "fmt" 22 "time" 23 24 appsv1 "k8s.io/api/apps/v1" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/apimachinery/pkg/types" 27 "k8s.io/apimachinery/pkg/util/wait" 28 clientset "k8s.io/client-go/kubernetes" 29 deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util" 30 "k8s.io/kubernetes/test/e2e/framework" 31 e2edeployment "k8s.io/kubernetes/test/e2e/framework/deployment" 32 "k8s.io/kubernetes/test/e2e/upgrades" 33 34 "github.com/onsi/ginkgo/v2" 35 "github.com/onsi/gomega" 36 37 imageutils "k8s.io/kubernetes/test/utils/image" 38 ) 39 40 const ( 41 deploymentName = "dp" 42 // poll is how often to poll pods, nodes and claims. 43 poll = 2 * time.Second 44 pollLongTimeout = 5 * time.Minute 45 ) 46 47 // TODO: Test that the deployment stays available during master (and maybe 48 // node and cluster upgrades). 49 50 // DeploymentUpgradeTest tests that a deployment is using the same replica 51 // sets before and after a cluster upgrade. 52 type DeploymentUpgradeTest struct { 53 oldDeploymentUID types.UID 54 oldRSUID types.UID 55 newRSUID types.UID 56 } 57 58 // Name returns the tracking name of the test. 59 func (DeploymentUpgradeTest) Name() string { return "[sig-apps] deployment-upgrade" } 60 61 // Setup creates a deployment and makes sure it has a new and an old replicaset running. 62 func (t *DeploymentUpgradeTest) Setup(ctx context.Context, f *framework.Framework) { 63 c := f.ClientSet 64 nginxImage := imageutils.GetE2EImage(imageutils.Nginx) 65 66 ns := f.Namespace.Name 67 deploymentClient := c.AppsV1().Deployments(ns) 68 rsClient := c.AppsV1().ReplicaSets(ns) 69 70 ginkgo.By(fmt.Sprintf("Creating a deployment %q with 1 replica in namespace %q", deploymentName, ns)) 71 d := e2edeployment.NewDeployment(deploymentName, int32(1), map[string]string{"test": "upgrade"}, "nginx", nginxImage, appsv1.RollingUpdateDeploymentStrategyType) 72 deployment, err := deploymentClient.Create(ctx, d, metav1.CreateOptions{}) 73 framework.ExpectNoError(err) 74 75 ginkgo.By(fmt.Sprintf("Waiting deployment %q to complete", deploymentName)) 76 framework.ExpectNoError(e2edeployment.WaitForDeploymentComplete(c, deployment)) 77 78 ginkgo.By(fmt.Sprintf("Getting replicaset revision 1 of deployment %q", deploymentName)) 79 rsSelector, err := metav1.LabelSelectorAsSelector(d.Spec.Selector) 80 framework.ExpectNoError(err) 81 rsList, err := rsClient.List(ctx, metav1.ListOptions{LabelSelector: rsSelector.String()}) 82 framework.ExpectNoError(err) 83 rss := rsList.Items 84 gomega.Expect(rss).To(gomega.HaveLen(1), "expected one replicaset, got %d", len(rss)) 85 t.oldRSUID = rss[0].UID 86 87 ginkgo.By(fmt.Sprintf("Waiting for revision of the deployment %q to become 1", deploymentName)) 88 framework.ExpectNoError(waitForDeploymentRevision(ctx, c, deployment, "1")) 89 90 // Trigger a new rollout so that we have some history. 91 ginkgo.By(fmt.Sprintf("Triggering a new rollout for deployment %q", deploymentName)) 92 deployment, err = e2edeployment.UpdateDeploymentWithRetries(c, ns, deploymentName, func(update *appsv1.Deployment) { 93 update.Spec.Template.Spec.Containers[0].Name = "updated-name" 94 }) 95 framework.ExpectNoError(err) 96 97 ginkgo.By(fmt.Sprintf("Waiting deployment %q to complete", deploymentName)) 98 framework.ExpectNoError(e2edeployment.WaitForDeploymentComplete(c, deployment)) 99 100 ginkgo.By(fmt.Sprintf("Getting replicasets revision 1 and 2 of deployment %q", deploymentName)) 101 rsList, err = rsClient.List(ctx, metav1.ListOptions{LabelSelector: rsSelector.String()}) 102 framework.ExpectNoError(err) 103 rss = rsList.Items 104 gomega.Expect(rss).To(gomega.HaveLen(2), "expected 2 replicaset, got %d", len(rss)) 105 106 ginkgo.By(fmt.Sprintf("Checking replicaset of deployment %q that is created before rollout survives the rollout", deploymentName)) 107 switch t.oldRSUID { 108 case rss[0].UID: 109 t.newRSUID = rss[1].UID 110 case rss[1].UID: 111 t.newRSUID = rss[0].UID 112 default: 113 framework.ExpectNoError(fmt.Errorf("old replicaset with UID %q does not survive rollout", t.oldRSUID)) 114 } 115 116 ginkgo.By(fmt.Sprintf("Waiting for revision of the deployment %q to become 2", deploymentName)) 117 framework.ExpectNoError(waitForDeploymentRevision(ctx, c, deployment, "2")) 118 119 t.oldDeploymentUID = deployment.UID 120 } 121 122 // Test checks whether the replicasets for a deployment are the same after an upgrade. 123 func (t *DeploymentUpgradeTest) Test(ctx context.Context, f *framework.Framework, done <-chan struct{}, upgrade upgrades.UpgradeType) { 124 // Block until upgrade is done 125 ginkgo.By(fmt.Sprintf("Waiting for upgrade to finish before checking replicasets for deployment %q", deploymentName)) 126 <-done 127 128 c := f.ClientSet 129 ns := f.Namespace.Name 130 deploymentClient := c.AppsV1().Deployments(ns) 131 rsClient := c.AppsV1().ReplicaSets(ns) 132 133 deployment, err := deploymentClient.Get(ctx, deploymentName, metav1.GetOptions{}) 134 framework.ExpectNoError(err) 135 136 ginkgo.By(fmt.Sprintf("Checking UID to verify deployment %q survives upgrade", deploymentName)) 137 gomega.Expect(deployment.UID).To(gomega.Equal(t.oldDeploymentUID)) 138 139 ginkgo.By(fmt.Sprintf("Verifying deployment %q does not create new replicasets", deploymentName)) 140 rsSelector, err := metav1.LabelSelectorAsSelector(deployment.Spec.Selector) 141 framework.ExpectNoError(err) 142 rsList, err := rsClient.List(ctx, metav1.ListOptions{LabelSelector: rsSelector.String()}) 143 framework.ExpectNoError(err) 144 rss := rsList.Items 145 gomega.Expect(rss).To(gomega.HaveLen(2), "expected 2 replicaset, got %d", len(rss)) 146 147 switch t.oldRSUID { 148 case rss[0].UID: 149 gomega.Expect(rss[1].UID).To(gomega.Equal(t.newRSUID)) 150 case rss[1].UID: 151 gomega.Expect(rss[0].UID).To(gomega.Equal(t.newRSUID)) 152 default: 153 framework.ExpectNoError(fmt.Errorf("new replicasets are created during upgrade of deployment %q", deploymentName)) 154 } 155 156 ginkgo.By(fmt.Sprintf("Verifying revision of the deployment %q is still 2", deploymentName)) 157 gomega.Expect(deployment.Annotations).To(gomega.HaveKeyWithValue(deploymentutil.RevisionAnnotation, "2")) 158 159 ginkgo.By(fmt.Sprintf("Waiting for deployment %q to complete adoption", deploymentName)) 160 framework.ExpectNoError(e2edeployment.WaitForDeploymentComplete(c, deployment)) 161 162 // Verify the upgraded deployment is active by scaling up the deployment by 1 163 ginkgo.By(fmt.Sprintf("Scaling up replicaset of deployment %q by 1", deploymentName)) 164 deploymentWithUpdatedReplicas, err := e2edeployment.UpdateDeploymentWithRetries(c, ns, deploymentName, func(deployment *appsv1.Deployment) { 165 *deployment.Spec.Replicas = *deployment.Spec.Replicas + 1 166 }) 167 framework.ExpectNoError(err) 168 169 ginkgo.By(fmt.Sprintf("Waiting for deployment %q to complete after scaling", deploymentName)) 170 framework.ExpectNoError(e2edeployment.WaitForDeploymentComplete(c, deploymentWithUpdatedReplicas)) 171 } 172 173 // Teardown cleans up any remaining resources. 174 func (t *DeploymentUpgradeTest) Teardown(ctx context.Context, f *framework.Framework) { 175 // rely on the namespace deletion to clean up everything 176 } 177 178 // waitForDeploymentRevision waits for becoming the target revision of a delopyment. 179 func waitForDeploymentRevision(ctx context.Context, c clientset.Interface, d *appsv1.Deployment, targetRevision string) error { 180 err := wait.PollImmediate(poll, pollLongTimeout, func() (bool, error) { 181 deployment, err := c.AppsV1().Deployments(d.Namespace).Get(ctx, d.Name, metav1.GetOptions{}) 182 if err != nil { 183 return false, err 184 } 185 revision := deployment.Annotations[deploymentutil.RevisionAnnotation] 186 return revision == targetRevision, nil 187 }) 188 if err != nil { 189 return fmt.Errorf("error waiting for revision to become %q for deployment %q: %w", targetRevision, d.Name, err) 190 } 191 return nil 192 }