diff --git a/test/e2e/apps/deployment.go b/test/e2e/apps/deployment.go index f320df2f6c..147adac891 100644 --- a/test/e2e/apps/deployment.go +++ b/test/e2e/apps/deployment.go @@ -87,9 +87,6 @@ var _ = SIGDescribe("Deployment", func() { It("deployment should support rollback", func() { testRollbackDeployment(f) }) - It("deployment should label adopted RSs and pods", func() { - testDeploymentLabelAdopted(f) - }) It("scaled rollout deployment should not block on annotation check", func() { testScaledRolloutDeployment(f) }) @@ -629,57 +626,6 @@ func testRollbackDeployment(f *framework.Framework) { Expect(err).NotTo(HaveOccurred()) } -func testDeploymentLabelAdopted(f *framework.Framework) { - ns := f.Namespace.Name - c := f.ClientSet - // Create nginx pods. - podName := "nginx" - podLabels := map[string]string{"name": podName} - - rsName := "test-adopted-controller" - replicas := int32(1) - image := NginxImage - _, err := c.Extensions().ReplicaSets(ns).Create(newRS(rsName, replicas, podLabels, podName, image)) - Expect(err).NotTo(HaveOccurred()) - // Verify that the required pods have come up. - err = framework.VerifyPodsRunning(c, ns, podName, false, replicas) - Expect(err).NotTo(HaveOccurred(), "error in waiting for pods to come up: %v", err) - - // Create a nginx deployment to adopt the old rs. - deploymentName := "test-adopted-deployment" - framework.Logf("Creating deployment %s", deploymentName) - deploy, err := c.Extensions().Deployments(ns).Create(framework.NewDeployment(deploymentName, replicas, podLabels, podName, image, extensions.RollingUpdateDeploymentStrategyType)) - Expect(err).NotTo(HaveOccurred()) - - // Wait for it to be updated to revision 1 - err = framework.WaitForDeploymentRevisionAndImage(c, ns, deploymentName, "1", image) - Expect(err).NotTo(HaveOccurred()) - - // The RS and pods should be relabeled before the status is updated by syncRollingUpdateDeployment - err = framework.WaitForDeploymentComplete(c, deploy) - Expect(err).NotTo(HaveOccurred()) - - // There should be no old RSs (overlapping RS) - deployment, err := c.Extensions().Deployments(ns).Get(deploymentName, metav1.GetOptions{}) - Expect(err).NotTo(HaveOccurred()) - oldRSs, allOldRSs, newRS, err := deploymentutil.GetAllReplicaSets(deployment, c.ExtensionsV1beta1()) - Expect(err).NotTo(HaveOccurred()) - Expect(len(oldRSs)).Should(Equal(0)) - Expect(len(allOldRSs)).Should(Equal(0)) - // New RS should contain pod-template-hash in its selector, label, and template label - err = framework.CheckRSHashLabel(newRS) - Expect(err).NotTo(HaveOccurred()) - // All pods targeted by the deployment should contain pod-template-hash in their labels, and there should be only 3 pods - selector, err := metav1.LabelSelectorAsSelector(deployment.Spec.Selector) - Expect(err).NotTo(HaveOccurred()) - options := metav1.ListOptions{LabelSelector: selector.String()} - pods, err := c.Core().Pods(ns).List(options) - Expect(err).NotTo(HaveOccurred()) - err = framework.CheckPodHashLabel(pods) - Expect(err).NotTo(HaveOccurred()) - Expect(int32(len(pods.Items))).Should(Equal(replicas)) -} - func testScaledRolloutDeployment(f *framework.Framework) { ns := f.Namespace.Name c := f.ClientSet diff --git a/test/e2e/framework/util.go b/test/e2e/framework/util.go index 92ccc7f289..c2077fc0c7 100644 --- a/test/e2e/framework/util.go +++ b/test/e2e/framework/util.go @@ -4397,31 +4397,6 @@ func isElementOf(podUID types.UID, pods *v1.PodList) bool { return false } -func CheckRSHashLabel(rs *extensions.ReplicaSet) error { - if len(rs.Labels[extensions.DefaultDeploymentUniqueLabelKey]) == 0 || - len(rs.Spec.Selector.MatchLabels[extensions.DefaultDeploymentUniqueLabelKey]) == 0 || - len(rs.Spec.Template.Labels[extensions.DefaultDeploymentUniqueLabelKey]) == 0 { - return fmt.Errorf("unexpected RS missing required pod-hash-template: %+v, selector = %+v, template = %+v", rs, rs.Spec.Selector, rs.Spec.Template) - } - return nil -} - -func CheckPodHashLabel(pods *v1.PodList) error { - invalidPod := "" - for _, pod := range pods.Items { - if len(pod.Labels[extensions.DefaultDeploymentUniqueLabelKey]) == 0 { - if len(invalidPod) == 0 { - invalidPod = "unexpected pods missing required pod-hash-template:" - } - invalidPod = fmt.Sprintf("%s %+v;", invalidPod, pod) - } - } - if len(invalidPod) > 0 { - return fmt.Errorf("%s", invalidPod) - } - return nil -} - // timeout for proxy requests. const proxyTimeout = 2 * time.Minute diff --git a/test/integration/deployment/deployment_test.go b/test/integration/deployment/deployment_test.go index 0ec03b04b7..6e228511b3 100644 --- a/test/integration/deployment/deployment_test.go +++ b/test/integration/deployment/deployment_test.go @@ -17,6 +17,7 @@ limitations under the License. package deployment import ( + "fmt" "reflect" "strings" "testing" @@ -498,3 +499,137 @@ func TestRollbackDeploymentRSNoRevision(t *testing.T) { t.Fatal(err) } } + +func checkRSHashLabels(rs *v1beta1.ReplicaSet) (string, error) { + hash := rs.Labels[v1beta1.DefaultDeploymentUniqueLabelKey] + selectorHash := rs.Spec.Selector.MatchLabels[v1beta1.DefaultDeploymentUniqueLabelKey] + templateLabelHash := rs.Spec.Template.Labels[v1beta1.DefaultDeploymentUniqueLabelKey] + + if hash != selectorHash || selectorHash != templateLabelHash { + return "", fmt.Errorf("mismatching hash value found in replicaset %s: %#v", rs.Name, rs) + } + if len(hash) == 0 { + return "", fmt.Errorf("unexpected replicaset %s missing required pod-template-hash labels", rs.Name) + } + + return hash, nil +} + +func checkPodsHashLabel(pods *v1.PodList) (string, error) { + if len(pods.Items) == 0 { + return "", fmt.Errorf("no pods given") + } + var hash string + for _, pod := range pods.Items { + podHash := pod.Labels[v1beta1.DefaultDeploymentUniqueLabelKey] + if len(podHash) == 0 { + return "", fmt.Errorf("found pod %s missing pod-template-hash label: %#v", pod.Name, pods) + } + // Save the first valid hash + if len(hash) == 0 { + hash = podHash + } + if podHash != hash { + return "", fmt.Errorf("found pod %s with mismatching pod-template-hash value %s: %#v", pod.Name, podHash, pods) + } + } + return hash, nil +} + +// Deployment should label adopted ReplicaSets and Pods. +func TestDeploymentLabelAdopted(t *testing.T) { + s, closeFn, rm, dc, informers, c := dcSetup(t) + defer closeFn() + name := "test-adopted-deployment" + ns := framework.CreateTestingNamespace(name, s, t) + defer framework.DeleteTestingNamespace(ns, s, t) + + // Start informer and controllers + stopCh := make(chan struct{}) + defer close(stopCh) + informers.Start(stopCh) + go rm.Run(5, stopCh) + go dc.Run(5, stopCh) + + // Create a RS to be adopted by the deployment. + rsName := "test-adopted-controller" + replicas := int32(1) + rs := newReplicaSet(rsName, ns.Name, replicas) + _, err := c.ExtensionsV1beta1().ReplicaSets(ns.Name).Create(rs) + if err != nil { + t.Fatalf("failed to create replicaset %s: %v", rsName, err) + } + // Mark RS pods as ready. + selector, err := metav1.LabelSelectorAsSelector(rs.Spec.Selector) + if err != nil { + t.Fatalf("failed to parse replicaset %s selector: %v", rsName, err) + } + if err = wait.PollImmediate(pollInterval, pollTimeout, func() (bool, error) { + pods, err := c.CoreV1().Pods(ns.Name).List(metav1.ListOptions{LabelSelector: selector.String()}) + if err != nil { + return false, err + } + if len(pods.Items) != int(replicas) { + return false, nil + } + for _, pod := range pods.Items { + if err = markPodReady(c, ns.Name, &pod); err != nil { + return false, nil + } + } + return true, nil + }); err != nil { + t.Fatalf("failed to mark pods replicaset %s as ready: %v", rsName, err) + } + + // Create a Deployment to adopt the old rs. + tester := &deploymentTester{t: t, c: c, deployment: newDeployment(name, ns.Name, replicas)} + if tester.deployment, err = c.ExtensionsV1beta1().Deployments(ns.Name).Create(tester.deployment); err != nil { + t.Fatalf("failed to create deployment %s: %v", tester.deployment.Name, err) + } + + // Wait for the Deployment to be updated to revision 1 + if err = tester.waitForDeploymentRevisionAndImage("1", fakeImage); err != nil { + t.Fatal(err) + } + + // The RS and pods should be relabeled after the Deployment finishes adopting it and completes. + if err := tester.waitForDeploymentComplete(); err != nil { + t.Fatal(err) + } + + // There should be no old RSes (overlapping RS) + oldRSs, allOldRSs, newRS, err := deploymentutil.GetAllReplicaSets(tester.deployment, c.ExtensionsV1beta1()) + if err != nil { + t.Fatalf("failed to get all replicasets owned by deployment %s: %v", name, err) + } + if len(oldRSs) != 0 || len(allOldRSs) != 0 { + t.Errorf("expected deployment to have no old replicasets, got %d old replicasets", len(allOldRSs)) + } + + // New RS should be relabeled, i.e. contain pod-template-hash in its selector, label, and template label + rsHash, err := checkRSHashLabels(newRS) + if err != nil { + t.Error(err) + } + + // All pods targeted by the deployment should contain pod-template-hash in their labels, and there should be only 3 pods + selector, err = metav1.LabelSelectorAsSelector(tester.deployment.Spec.Selector) + if err != nil { + t.Fatalf("failed to parse deployment %s selector: %v", name, err) + } + pods, err := c.CoreV1().Pods(ns.Name).List(metav1.ListOptions{LabelSelector: selector.String()}) + if err != nil { + t.Fatalf("failed to list pods of deployment %s: %v", name, err) + } + if len(pods.Items) != int(replicas) { + t.Errorf("expected %d pods, got %d pods", replicas, len(pods.Items)) + } + podHash, err := checkPodsHashLabel(pods) + if err != nil { + t.Error(err) + } + if rsHash != podHash { + t.Errorf("found mismatching pod-template-hash value: rs hash = %s whereas pod hash = %s", rsHash, podHash) + } +} diff --git a/test/integration/deployment/util.go b/test/integration/deployment/util.go index 0f7dcb4048..7d771d1a75 100644 --- a/test/integration/deployment/util.go +++ b/test/integration/deployment/util.go @@ -200,6 +200,12 @@ func (d *deploymentTester) waitForDeploymentRevisionAndImage(revision, image str return nil } +func markPodReady(c clientset.Interface, ns string, pod *v1.Pod) error { + addPodConditionReady(pod, metav1.Now()) + _, err := c.Core().Pods(ns).UpdateStatus(pod) + return err +} + // markAllPodsReady manually updates all Deployment pods status to ready func (d *deploymentTester) markAllPodsReady() { ns := d.deployment.Namespace @@ -215,14 +221,17 @@ func (d *deploymentTester) markAllPodsReady() { d.t.Logf("failed to list Deployment pods, will retry later: %v", err) return false, nil } + if len(pods.Items) != int(*d.deployment.Spec.Replicas) { + d.t.Logf("%d/%d of deployment pods are created", len(pods.Items), *d.deployment.Spec.Replicas) + return false, nil + } for i := range pods.Items { pod := pods.Items[i] if podutil.IsPodReady(&pod) { readyPods++ continue } - addPodConditionReady(&pod, metav1.Now()) - if _, err = d.c.Core().Pods(ns).UpdateStatus(&pod); err != nil { + if err = markPodReady(d.c, ns, &pod); err != nil { d.t.Logf("failed to update Deployment pod %s, will retry later: %v", pod.Name, err) } else { readyPods++ diff --git a/test/utils/deployment.go b/test/utils/deployment.go index 1e8ff06594..896a610fea 100644 --- a/test/utils/deployment.go +++ b/test/utils/deployment.go @@ -172,7 +172,7 @@ func WaitForDeploymentRevisionAndImage(c clientset.Interface, ns, deploymentName var deployment *extensions.Deployment var newRS *extensions.ReplicaSet var reason string - err := wait.Poll(pollInterval, pollTimeout, func() (bool, error) { + err := wait.PollImmediate(pollInterval, pollTimeout, func() (bool, error) { var err error deployment, err = c.ExtensionsV1beta1().Deployments(ns).Get(deploymentName, metav1.GetOptions{}) if err != nil {