...

Source file src/k8s.io/kubernetes/test/e2e/apps/statefulset.go

Documentation: k8s.io/kubernetes/test/e2e/apps

     1  /*
     2  Copyright 2014 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  	"encoding/json"
    22  	"fmt"
    23  	"reflect"
    24  	"regexp"
    25  	"strconv"
    26  	"strings"
    27  	"sync"
    28  	"time"
    29  
    30  	"github.com/google/go-cmp/cmp"
    31  	"github.com/onsi/ginkgo/v2"
    32  	"github.com/onsi/gomega"
    33  
    34  	appsv1 "k8s.io/api/apps/v1"
    35  	autoscalingv1 "k8s.io/api/autoscaling/v1"
    36  	v1 "k8s.io/api/core/v1"
    37  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    38  	"k8s.io/apimachinery/pkg/fields"
    39  	klabels "k8s.io/apimachinery/pkg/labels"
    40  	"k8s.io/apimachinery/pkg/runtime/schema"
    41  	"k8s.io/apimachinery/pkg/types"
    42  	"k8s.io/apimachinery/pkg/util/intstr"
    43  	"k8s.io/apimachinery/pkg/util/strategicpatch"
    44  	"k8s.io/apimachinery/pkg/util/wait"
    45  	"k8s.io/apimachinery/pkg/watch"
    46  	clientset "k8s.io/client-go/kubernetes"
    47  	"k8s.io/client-go/tools/cache"
    48  	watchtools "k8s.io/client-go/tools/watch"
    49  	"k8s.io/client-go/util/retry"
    50  	"k8s.io/kubernetes/test/e2e/feature"
    51  	"k8s.io/kubernetes/test/e2e/framework"
    52  	e2ekubectl "k8s.io/kubernetes/test/e2e/framework/kubectl"
    53  	e2enode "k8s.io/kubernetes/test/e2e/framework/node"
    54  	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
    55  	e2eoutput "k8s.io/kubernetes/test/e2e/framework/pod/output"
    56  	e2epv "k8s.io/kubernetes/test/e2e/framework/pv"
    57  	e2eservice "k8s.io/kubernetes/test/e2e/framework/service"
    58  	e2estatefulset "k8s.io/kubernetes/test/e2e/framework/statefulset"
    59  	imageutils "k8s.io/kubernetes/test/utils/image"
    60  	admissionapi "k8s.io/pod-security-admission/api"
    61  	"k8s.io/utils/pointer"
    62  )
    63  
    64  const (
    65  	zookeeperManifestPath   = "test/e2e/testing-manifests/statefulset/zookeeper"
    66  	mysqlGaleraManifestPath = "test/e2e/testing-manifests/statefulset/mysql-galera"
    67  	redisManifestPath       = "test/e2e/testing-manifests/statefulset/redis"
    68  	cockroachDBManifestPath = "test/e2e/testing-manifests/statefulset/cockroachdb"
    69  	// We don't restart MySQL cluster regardless of restartCluster, since MySQL doesn't handle restart well
    70  	restartCluster = true
    71  
    72  	// Timeout for reads from databases running on stateful pods.
    73  	readTimeout = 60 * time.Second
    74  
    75  	// statefulSetPoll is a poll interval for StatefulSet tests
    76  	statefulSetPoll = 10 * time.Second
    77  	// statefulSetTimeout is a timeout interval for StatefulSet operations
    78  	statefulSetTimeout = 10 * time.Minute
    79  	// statefulPodTimeout is a timeout for stateful pods to change state
    80  	statefulPodTimeout = 5 * time.Minute
    81  
    82  	testFinalizer = "example.com/test-finalizer"
    83  )
    84  
    85  var httpProbe = &v1.Probe{
    86  	ProbeHandler: v1.ProbeHandler{
    87  		HTTPGet: &v1.HTTPGetAction{
    88  			Path: "/index.html",
    89  			Port: intstr.IntOrString{IntVal: 80},
    90  		},
    91  	},
    92  	PeriodSeconds:    1,
    93  	SuccessThreshold: 1,
    94  	FailureThreshold: 1,
    95  }
    96  
    97  // GCE Quota requirements: 3 pds, one per stateful pod manifest declared above.
    98  // GCE Api requirements: nodes and master need storage r/w permissions.
    99  var _ = SIGDescribe("StatefulSet", func() {
   100  	f := framework.NewDefaultFramework("statefulset")
   101  	f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged
   102  	var ns string
   103  	var c clientset.Interface
   104  
   105  	ginkgo.BeforeEach(func() {
   106  		c = f.ClientSet
   107  		ns = f.Namespace.Name
   108  	})
   109  
   110  	ginkgo.Describe("Basic StatefulSet functionality [StatefulSetBasic]", func() {
   111  		ssName := "ss"
   112  		labels := map[string]string{
   113  			"foo": "bar",
   114  			"baz": "blah",
   115  		}
   116  		headlessSvcName := "test"
   117  		var statefulPodMounts, podMounts []v1.VolumeMount
   118  		var ss *appsv1.StatefulSet
   119  
   120  		ginkgo.BeforeEach(func(ctx context.Context) {
   121  			statefulPodMounts = []v1.VolumeMount{{Name: "datadir", MountPath: "/data/"}}
   122  			podMounts = []v1.VolumeMount{{Name: "home", MountPath: "/home"}}
   123  			ss = e2estatefulset.NewStatefulSet(ssName, ns, headlessSvcName, 2, statefulPodMounts, podMounts, labels)
   124  
   125  			ginkgo.By("Creating service " + headlessSvcName + " in namespace " + ns)
   126  			headlessService := e2eservice.CreateServiceSpec(headlessSvcName, "", true, labels)
   127  			_, err := c.CoreV1().Services(ns).Create(ctx, headlessService, metav1.CreateOptions{})
   128  			framework.ExpectNoError(err)
   129  		})
   130  
   131  		ginkgo.AfterEach(func(ctx context.Context) {
   132  			if ginkgo.CurrentSpecReport().Failed() {
   133  				e2eoutput.DumpDebugInfo(ctx, c, ns)
   134  			}
   135  			framework.Logf("Deleting all statefulset in ns %v", ns)
   136  			e2estatefulset.DeleteAllStatefulSets(ctx, c, ns)
   137  		})
   138  
   139  		// This can't be Conformance yet because it depends on a default
   140  		// StorageClass and a dynamic provisioner.
   141  		ginkgo.It("should provide basic identity", func(ctx context.Context) {
   142  			ginkgo.By("Creating statefulset " + ssName + " in namespace " + ns)
   143  			e2epv.SkipIfNoDefaultStorageClass(ctx, c)
   144  			*(ss.Spec.Replicas) = 3
   145  			e2estatefulset.PauseNewPods(ss)
   146  
   147  			_, err := c.AppsV1().StatefulSets(ns).Create(ctx, ss, metav1.CreateOptions{})
   148  			framework.ExpectNoError(err)
   149  
   150  			ginkgo.By("Saturating stateful set " + ss.Name)
   151  			e2estatefulset.Saturate(ctx, c, ss)
   152  
   153  			ginkgo.By("Verifying statefulset mounted data directory is usable")
   154  			framework.ExpectNoError(e2estatefulset.CheckMount(ctx, c, ss, "/data"))
   155  
   156  			ginkgo.By("Verifying statefulset provides a stable hostname for each pod")
   157  			framework.ExpectNoError(e2estatefulset.CheckHostname(ctx, c, ss))
   158  
   159  			ginkgo.By("Verifying statefulset set proper service name")
   160  			framework.ExpectNoError(e2estatefulset.CheckServiceName(ss, headlessSvcName))
   161  
   162  			cmd := "echo $(hostname) | dd of=/data/hostname conv=fsync"
   163  			ginkgo.By("Running " + cmd + " in all stateful pods")
   164  			framework.ExpectNoError(e2estatefulset.ExecInStatefulPods(ctx, c, ss, cmd))
   165  
   166  			cmd = "ln -s /data/hostname /data/hostname-symlink"
   167  			ginkgo.By("Running " + cmd + " in all stateful pods")
   168  			framework.ExpectNoError(e2estatefulset.ExecInStatefulPods(ctx, c, ss, cmd))
   169  
   170  			ginkgo.By("Restarting statefulset " + ss.Name)
   171  			e2estatefulset.Restart(ctx, c, ss)
   172  			e2estatefulset.WaitForRunningAndReady(ctx, c, *ss.Spec.Replicas, ss)
   173  
   174  			ginkgo.By("Verifying statefulset mounted data directory is usable")
   175  			framework.ExpectNoError(e2estatefulset.CheckMount(ctx, c, ss, "/data"))
   176  
   177  			cmd = "if [ \"$(cat /data/hostname)\" = \"$(hostname)\" ]; then exit 0; else exit 1; fi"
   178  			ginkgo.By("Running " + cmd + " in all stateful pods")
   179  			framework.ExpectNoError(e2estatefulset.ExecInStatefulPods(ctx, c, ss, cmd))
   180  
   181  			cmd = "if [ \"$(cat /data/hostname-symlink)\" = \"$(hostname)\" ]; then exit 0; else exit 1; fi"
   182  			ginkgo.By("Running " + cmd + " in all stateful pods")
   183  			framework.ExpectNoError(e2estatefulset.ExecInStatefulPods(ctx, c, ss, cmd))
   184  		})
   185  
   186  		// This can't be Conformance yet because it depends on a default
   187  		// StorageClass and a dynamic provisioner.
   188  		ginkgo.It("should adopt matching orphans and release non-matching pods", func(ctx context.Context) {
   189  			ginkgo.By("Creating statefulset " + ssName + " in namespace " + ns)
   190  			e2epv.SkipIfNoDefaultStorageClass(ctx, c)
   191  			*(ss.Spec.Replicas) = 1
   192  			e2estatefulset.PauseNewPods(ss)
   193  
   194  			// Replace ss with the one returned from Create() so it has the UID.
   195  			// Save Kind since it won't be populated in the returned ss.
   196  			kind := ss.Kind
   197  			ss, err := c.AppsV1().StatefulSets(ns).Create(ctx, ss, metav1.CreateOptions{})
   198  			framework.ExpectNoError(err)
   199  			ss.Kind = kind
   200  
   201  			ginkgo.By("Saturating stateful set " + ss.Name)
   202  			e2estatefulset.Saturate(ctx, c, ss)
   203  			pods := e2estatefulset.GetPodList(ctx, c, ss)
   204  			gomega.Expect(pods.Items).To(gomega.HaveLen(int(*ss.Spec.Replicas)))
   205  
   206  			ginkgo.By("Checking that stateful set pods are created with ControllerRef")
   207  			pod := pods.Items[0]
   208  			controllerRef := metav1.GetControllerOf(&pod)
   209  			gomega.Expect(controllerRef).ToNot(gomega.BeNil())
   210  			gomega.Expect(controllerRef.Kind).To(gomega.Equal(ss.Kind))
   211  			gomega.Expect(controllerRef.Name).To(gomega.Equal(ss.Name))
   212  			gomega.Expect(controllerRef.UID).To(gomega.Equal(ss.UID))
   213  
   214  			ginkgo.By("Orphaning one of the stateful set's pods")
   215  			e2epod.NewPodClient(f).Update(ctx, pod.Name, func(pod *v1.Pod) {
   216  				pod.OwnerReferences = nil
   217  			})
   218  
   219  			ginkgo.By("Checking that the stateful set readopts the pod")
   220  			gomega.Expect(e2epod.WaitForPodCondition(ctx, c, pod.Namespace, pod.Name, "adopted", statefulSetTimeout,
   221  				func(pod *v1.Pod) (bool, error) {
   222  					controllerRef := metav1.GetControllerOf(pod)
   223  					if controllerRef == nil {
   224  						return false, nil
   225  					}
   226  					if controllerRef.Kind != ss.Kind || controllerRef.Name != ss.Name || controllerRef.UID != ss.UID {
   227  						return false, fmt.Errorf("pod has wrong controllerRef: %v", controllerRef)
   228  					}
   229  					return true, nil
   230  				},
   231  			)).To(gomega.Succeed(), "wait for pod %q to be readopted", pod.Name)
   232  
   233  			ginkgo.By("Removing the labels from one of the stateful set's pods")
   234  			prevLabels := pod.Labels
   235  			e2epod.NewPodClient(f).Update(ctx, pod.Name, func(pod *v1.Pod) {
   236  				pod.Labels = nil
   237  			})
   238  
   239  			ginkgo.By("Checking that the stateful set releases the pod")
   240  			gomega.Expect(e2epod.WaitForPodCondition(ctx, c, pod.Namespace, pod.Name, "released", statefulSetTimeout,
   241  				func(pod *v1.Pod) (bool, error) {
   242  					controllerRef := metav1.GetControllerOf(pod)
   243  					if controllerRef != nil {
   244  						return false, nil
   245  					}
   246  					return true, nil
   247  				},
   248  			)).To(gomega.Succeed(), "wait for pod %q to be released", pod.Name)
   249  
   250  			// If we don't do this, the test leaks the Pod and PVC.
   251  			ginkgo.By("Readding labels to the stateful set's pod")
   252  			e2epod.NewPodClient(f).Update(ctx, pod.Name, func(pod *v1.Pod) {
   253  				pod.Labels = prevLabels
   254  			})
   255  
   256  			ginkgo.By("Checking that the stateful set readopts the pod")
   257  			gomega.Expect(e2epod.WaitForPodCondition(ctx, c, pod.Namespace, pod.Name, "adopted", statefulSetTimeout,
   258  				func(pod *v1.Pod) (bool, error) {
   259  					controllerRef := metav1.GetControllerOf(pod)
   260  					if controllerRef == nil {
   261  						return false, nil
   262  					}
   263  					if controllerRef.Kind != ss.Kind || controllerRef.Name != ss.Name || controllerRef.UID != ss.UID {
   264  						return false, fmt.Errorf("pod has wrong controllerRef: %v", controllerRef)
   265  					}
   266  					return true, nil
   267  				},
   268  			)).To(gomega.Succeed(), "wait for pod %q to be readopted", pod.Name)
   269  		})
   270  
   271  		// This can't be Conformance yet because it depends on a default
   272  		// StorageClass and a dynamic provisioner.
   273  		ginkgo.It("should not deadlock when a pod's predecessor fails", func(ctx context.Context) {
   274  			ginkgo.By("Creating statefulset " + ssName + " in namespace " + ns)
   275  			e2epv.SkipIfNoDefaultStorageClass(ctx, c)
   276  			*(ss.Spec.Replicas) = 2
   277  			e2estatefulset.PauseNewPods(ss)
   278  
   279  			_, err := c.AppsV1().StatefulSets(ns).Create(ctx, ss, metav1.CreateOptions{})
   280  			framework.ExpectNoError(err)
   281  
   282  			e2estatefulset.WaitForRunning(ctx, c, 1, 0, ss)
   283  
   284  			ginkgo.By("Resuming stateful pod at index 0.")
   285  			e2estatefulset.ResumeNextPod(ctx, c, ss)
   286  
   287  			ginkgo.By("Waiting for stateful pod at index 1 to enter running.")
   288  			e2estatefulset.WaitForRunning(ctx, c, 2, 1, ss)
   289  
   290  			// Now we have 1 healthy and 1 unhealthy stateful pod. Deleting the healthy stateful pod should *not*
   291  			// create a new stateful pod till the remaining stateful pod becomes healthy, which won't happen till
   292  			// we set the healthy bit.
   293  
   294  			ginkgo.By("Deleting healthy stateful pod at index 0.")
   295  			deleteStatefulPodAtIndex(ctx, c, 0, ss)
   296  
   297  			ginkgo.By("Confirming stateful pod at index 0 is recreated.")
   298  			e2estatefulset.WaitForRunning(ctx, c, 2, 1, ss)
   299  
   300  			ginkgo.By("Resuming stateful pod at index 1.")
   301  			e2estatefulset.ResumeNextPod(ctx, c, ss)
   302  
   303  			ginkgo.By("Confirming all stateful pods in statefulset are created.")
   304  			e2estatefulset.WaitForRunningAndReady(ctx, c, *ss.Spec.Replicas, ss)
   305  		})
   306  
   307  		// This can't be Conformance yet because it depends on a default
   308  		// StorageClass and a dynamic provisioner.
   309  		ginkgo.It("should perform rolling updates and roll backs of template modifications with PVCs", func(ctx context.Context) {
   310  			ginkgo.By("Creating a new StatefulSet with PVCs")
   311  			e2epv.SkipIfNoDefaultStorageClass(ctx, c)
   312  			*(ss.Spec.Replicas) = 3
   313  			rollbackTest(ctx, c, ns, ss)
   314  		})
   315  
   316  		/*
   317  		   Release: v1.9
   318  		   Testname: StatefulSet, Rolling Update
   319  		   Description: StatefulSet MUST support the RollingUpdate strategy to automatically replace Pods one at a time when the Pod template changes. The StatefulSet's status MUST indicate the CurrentRevision and UpdateRevision. If the template is changed to match a prior revision, StatefulSet MUST detect this as a rollback instead of creating a new revision. This test does not depend on a preexisting default StorageClass or a dynamic provisioner.
   320  		*/
   321  		framework.ConformanceIt("should perform rolling updates and roll backs of template modifications", func(ctx context.Context) {
   322  			ginkgo.By("Creating a new StatefulSet")
   323  			ss := e2estatefulset.NewStatefulSet("ss2", ns, headlessSvcName, 3, nil, nil, labels)
   324  			rollbackTest(ctx, c, ns, ss)
   325  		})
   326  
   327  		/*
   328  		   Release: v1.9
   329  		   Testname: StatefulSet, Rolling Update with Partition
   330  		   Description: StatefulSet's RollingUpdate strategy MUST support the Partition parameter for canaries and phased rollouts. If a Pod is deleted while a rolling update is in progress, StatefulSet MUST restore the Pod without violating the Partition. This test does not depend on a preexisting default StorageClass or a dynamic provisioner.
   331  		*/
   332  		framework.ConformanceIt("should perform canary updates and phased rolling updates of template modifications", func(ctx context.Context) {
   333  			ginkgo.By("Creating a new StatefulSet")
   334  			ss := e2estatefulset.NewStatefulSet("ss2", ns, headlessSvcName, 3, nil, nil, labels)
   335  			setHTTPProbe(ss)
   336  			ss.Spec.UpdateStrategy = appsv1.StatefulSetUpdateStrategy{
   337  				Type: appsv1.RollingUpdateStatefulSetStrategyType,
   338  				RollingUpdate: func() *appsv1.RollingUpdateStatefulSetStrategy {
   339  					return &appsv1.RollingUpdateStatefulSetStrategy{
   340  						Partition: pointer.Int32(3),
   341  					}
   342  				}(),
   343  			}
   344  			ss, err := c.AppsV1().StatefulSets(ns).Create(ctx, ss, metav1.CreateOptions{})
   345  			framework.ExpectNoError(err)
   346  			e2estatefulset.WaitForRunningAndReady(ctx, c, *ss.Spec.Replicas, ss)
   347  			ss = waitForStatus(ctx, c, ss)
   348  			currentRevision, updateRevision := ss.Status.CurrentRevision, ss.Status.UpdateRevision
   349  			gomega.Expect(currentRevision).To(gomega.Equal(updateRevision), "StatefulSet %s/%s created with update revision %s not equal to current revision %s",
   350  				ss.Namespace, ss.Name, updateRevision, currentRevision)
   351  			pods := e2estatefulset.GetPodList(ctx, c, ss)
   352  			for i := range pods.Items {
   353  				gomega.Expect(pods.Items[i].Labels).To(gomega.HaveKeyWithValue(appsv1.StatefulSetRevisionLabel, currentRevision), "Pod %s/%s revision %s is not equal to currentRevision %s",
   354  					pods.Items[i].Namespace,
   355  					pods.Items[i].Name,
   356  					pods.Items[i].Labels[appsv1.StatefulSetRevisionLabel],
   357  					currentRevision)
   358  			}
   359  			newImage := NewWebserverImage
   360  			oldImage := ss.Spec.Template.Spec.Containers[0].Image
   361  
   362  			ginkgo.By(fmt.Sprintf("Updating stateful set template: update image from %s to %s", oldImage, newImage))
   363  			gomega.Expect(oldImage).NotTo(gomega.Equal(newImage), "Incorrect test setup: should update to a different image")
   364  			ss, err = updateStatefulSetWithRetries(ctx, c, ns, ss.Name, func(update *appsv1.StatefulSet) {
   365  				update.Spec.Template.Spec.Containers[0].Image = newImage
   366  			})
   367  			framework.ExpectNoError(err)
   368  
   369  			ginkgo.By("Creating a new revision")
   370  			ss = waitForStatus(ctx, c, ss)
   371  			currentRevision, updateRevision = ss.Status.CurrentRevision, ss.Status.UpdateRevision
   372  			gomega.Expect(currentRevision).NotTo(gomega.Equal(updateRevision), "Current revision should not equal update revision during rolling update")
   373  
   374  			ginkgo.By("Not applying an update when the partition is greater than the number of replicas")
   375  			for i := range pods.Items {
   376  				gomega.Expect(pods.Items[i].Spec.Containers[0].Image).To(gomega.Equal(oldImage), "Pod %s/%s has image %s not equal to current image %s",
   377  					pods.Items[i].Namespace,
   378  					pods.Items[i].Name,
   379  					pods.Items[i].Spec.Containers[0].Image,
   380  					oldImage)
   381  				gomega.Expect(pods.Items[i].Labels).To(gomega.HaveKeyWithValue(appsv1.StatefulSetRevisionLabel, currentRevision), "Pod %s/%s has revision %s not equal to current revision %s",
   382  					pods.Items[i].Namespace,
   383  					pods.Items[i].Name,
   384  					pods.Items[i].Labels[appsv1.StatefulSetRevisionLabel],
   385  					currentRevision)
   386  			}
   387  
   388  			ginkgo.By("Performing a canary update")
   389  			ss.Spec.UpdateStrategy = appsv1.StatefulSetUpdateStrategy{
   390  				Type: appsv1.RollingUpdateStatefulSetStrategyType,
   391  				RollingUpdate: func() *appsv1.RollingUpdateStatefulSetStrategy {
   392  					return &appsv1.RollingUpdateStatefulSetStrategy{
   393  						Partition: pointer.Int32(2),
   394  					}
   395  				}(),
   396  			}
   397  			ss, err = updateStatefulSetWithRetries(ctx, c, ns, ss.Name, func(update *appsv1.StatefulSet) {
   398  				update.Spec.UpdateStrategy = appsv1.StatefulSetUpdateStrategy{
   399  					Type: appsv1.RollingUpdateStatefulSetStrategyType,
   400  					RollingUpdate: func() *appsv1.RollingUpdateStatefulSetStrategy {
   401  						return &appsv1.RollingUpdateStatefulSetStrategy{
   402  							Partition: pointer.Int32(2),
   403  						}
   404  					}(),
   405  				}
   406  			})
   407  			framework.ExpectNoError(err)
   408  			ss, pods = waitForPartitionedRollingUpdate(ctx, c, ss)
   409  			for i := range pods.Items {
   410  				if i < int(*ss.Spec.UpdateStrategy.RollingUpdate.Partition) {
   411  					gomega.Expect(pods.Items[i].Spec.Containers[0].Image).To(gomega.Equal(oldImage), "Pod %s/%s has image %s not equal to current image %s",
   412  						pods.Items[i].Namespace,
   413  						pods.Items[i].Name,
   414  						pods.Items[i].Spec.Containers[0].Image,
   415  						oldImage)
   416  					gomega.Expect(pods.Items[i].Labels).To(gomega.HaveKeyWithValue(appsv1.StatefulSetRevisionLabel, currentRevision), "Pod %s/%s has revision %s not equal to current revision %s",
   417  						pods.Items[i].Namespace,
   418  						pods.Items[i].Name,
   419  						pods.Items[i].Labels[appsv1.StatefulSetRevisionLabel],
   420  						currentRevision)
   421  				} else {
   422  					gomega.Expect(pods.Items[i].Spec.Containers[0].Image).To(gomega.Equal(newImage), "Pod %s/%s has image %s not equal to new image  %s",
   423  						pods.Items[i].Namespace,
   424  						pods.Items[i].Name,
   425  						pods.Items[i].Spec.Containers[0].Image,
   426  						newImage)
   427  					gomega.Expect(pods.Items[i].Labels).To(gomega.HaveKeyWithValue(appsv1.StatefulSetRevisionLabel, updateRevision), "Pod %s/%s has revision %s not equal to new revision %s",
   428  						pods.Items[i].Namespace,
   429  						pods.Items[i].Name,
   430  						pods.Items[i].Labels[appsv1.StatefulSetRevisionLabel],
   431  						updateRevision)
   432  				}
   433  			}
   434  
   435  			ginkgo.By("Restoring Pods to the correct revision when they are deleted")
   436  			deleteStatefulPodAtIndex(ctx, c, 0, ss)
   437  			deleteStatefulPodAtIndex(ctx, c, 2, ss)
   438  			e2estatefulset.WaitForRunningAndReady(ctx, c, 3, ss)
   439  			ss = getStatefulSet(ctx, c, ss.Namespace, ss.Name)
   440  			pods = e2estatefulset.GetPodList(ctx, c, ss)
   441  			for i := range pods.Items {
   442  				if i < int(*ss.Spec.UpdateStrategy.RollingUpdate.Partition) {
   443  					gomega.Expect(pods.Items[i].Spec.Containers[0].Image).To(gomega.Equal(oldImage), "Pod %s/%s has image %s not equal to current image %s",
   444  						pods.Items[i].Namespace,
   445  						pods.Items[i].Name,
   446  						pods.Items[i].Spec.Containers[0].Image,
   447  						oldImage)
   448  					gomega.Expect(pods.Items[i].Labels).To(gomega.HaveKeyWithValue(appsv1.StatefulSetRevisionLabel, currentRevision), "Pod %s/%s has revision %s not equal to current revision %s",
   449  						pods.Items[i].Namespace,
   450  						pods.Items[i].Name,
   451  						pods.Items[i].Labels[appsv1.StatefulSetRevisionLabel],
   452  						currentRevision)
   453  				} else {
   454  					gomega.Expect(pods.Items[i].Spec.Containers[0].Image).To(gomega.Equal(newImage), "Pod %s/%s has image %s not equal to new image  %s",
   455  						pods.Items[i].Namespace,
   456  						pods.Items[i].Name,
   457  						pods.Items[i].Spec.Containers[0].Image,
   458  						newImage)
   459  					gomega.Expect(pods.Items[i].Labels).To(gomega.HaveKeyWithValue(appsv1.StatefulSetRevisionLabel, updateRevision), "Pod %s/%s has revision %s not equal to new revision %s",
   460  						pods.Items[i].Namespace,
   461  						pods.Items[i].Name,
   462  						pods.Items[i].Labels[appsv1.StatefulSetRevisionLabel],
   463  						updateRevision)
   464  				}
   465  			}
   466  
   467  			ginkgo.By("Performing a phased rolling update")
   468  			for i := int(*ss.Spec.UpdateStrategy.RollingUpdate.Partition) - 1; i >= 0; i-- {
   469  				ss, err = updateStatefulSetWithRetries(ctx, c, ns, ss.Name, func(update *appsv1.StatefulSet) {
   470  					update.Spec.UpdateStrategy = appsv1.StatefulSetUpdateStrategy{
   471  						Type: appsv1.RollingUpdateStatefulSetStrategyType,
   472  						RollingUpdate: func() *appsv1.RollingUpdateStatefulSetStrategy {
   473  							j := int32(i)
   474  							return &appsv1.RollingUpdateStatefulSetStrategy{
   475  								Partition: &j,
   476  							}
   477  						}(),
   478  					}
   479  				})
   480  				framework.ExpectNoError(err)
   481  				ss, pods = waitForPartitionedRollingUpdate(ctx, c, ss)
   482  				for i := range pods.Items {
   483  					if i < int(*ss.Spec.UpdateStrategy.RollingUpdate.Partition) {
   484  						gomega.Expect(pods.Items[i].Spec.Containers[0].Image).To(gomega.Equal(oldImage), "Pod %s/%s has image %s not equal to current image %s",
   485  							pods.Items[i].Namespace,
   486  							pods.Items[i].Name,
   487  							pods.Items[i].Spec.Containers[0].Image,
   488  							oldImage)
   489  						gomega.Expect(pods.Items[i].Labels).To(gomega.HaveKeyWithValue(appsv1.StatefulSetRevisionLabel, currentRevision), "Pod %s/%s has revision %s not equal to current revision %s",
   490  							pods.Items[i].Namespace,
   491  							pods.Items[i].Name,
   492  							pods.Items[i].Labels[appsv1.StatefulSetRevisionLabel],
   493  							currentRevision)
   494  					} else {
   495  						gomega.Expect(pods.Items[i].Spec.Containers[0].Image).To(gomega.Equal(newImage), "Pod %s/%s has image %s not equal to new image  %s",
   496  							pods.Items[i].Namespace,
   497  							pods.Items[i].Name,
   498  							pods.Items[i].Spec.Containers[0].Image,
   499  							newImage)
   500  						gomega.Expect(pods.Items[i].Labels).To(gomega.HaveKeyWithValue(appsv1.StatefulSetRevisionLabel, updateRevision), "Pod %s/%s has revision %s not equal to new revision %s",
   501  							pods.Items[i].Namespace,
   502  							pods.Items[i].Name,
   503  							pods.Items[i].Labels[appsv1.StatefulSetRevisionLabel],
   504  							updateRevision)
   505  					}
   506  				}
   507  			}
   508  			gomega.Expect(ss.Status.CurrentRevision).To(gomega.Equal(updateRevision), "StatefulSet %s/%s current revision %s does not equal update revision %s on update completion",
   509  				ss.Namespace,
   510  				ss.Name,
   511  				ss.Status.CurrentRevision,
   512  				updateRevision)
   513  
   514  		})
   515  
   516  		ginkgo.It("should perform canary updates and phased rolling updates of template modifications for partiton1 and delete pod-0 without failing container", func(ctx context.Context) {
   517  			ginkgo.By("Creating a new StatefulSet without failing container")
   518  			ss := e2estatefulset.NewStatefulSet("ss2", ns, headlessSvcName, 3, nil, nil, labels)
   519  			deletingPodForRollingUpdatePartitionTest(ctx, f, c, ns, ss)
   520  		})
   521  
   522  		ginkgo.It("should perform canary updates and phased rolling updates of template modifications for partiton1 and delete pod-0 with failing container", func(ctx context.Context) {
   523  			ginkgo.By("Creating a new StatefulSet with failing container")
   524  			ss := e2estatefulset.NewStatefulSet("ss3", ns, headlessSvcName, 3, nil, nil, labels)
   525  			ss.Spec.Template.Spec.Containers = append(ss.Spec.Template.Spec.Containers, v1.Container{
   526  				Name:    "sleep-exit-with-1",
   527  				Image:   imageutils.GetE2EImage(imageutils.BusyBox),
   528  				Command: []string{"sh", "-c"},
   529  				Args: []string{`
   530  						echo "Running in pod $POD_NAME"
   531  						_term(){
   532  							echo "Received SIGTERM signal"
   533  							if [ "${POD_NAME}" = "ss3-0" ]; then
   534  								exit 1
   535  							else
   536  								exit 0
   537  							fi
   538  						}
   539  						trap _term SIGTERM
   540  						while true; do
   541  							echo "Running in infinite loop in $POD_NAME"
   542  							sleep 1
   543  						done
   544  						`,
   545  				},
   546  				Env: []v1.EnvVar{
   547  					{
   548  						Name: "POD_NAME",
   549  						ValueFrom: &v1.EnvVarSource{
   550  							FieldRef: &v1.ObjectFieldSelector{
   551  								APIVersion: "v1",
   552  								FieldPath:  "metadata.name",
   553  							},
   554  						},
   555  					},
   556  				},
   557  			})
   558  			deletingPodForRollingUpdatePartitionTest(ctx, f, c, ns, ss)
   559  		})
   560  
   561  		// Do not mark this as Conformance.
   562  		// The legacy OnDelete strategy only exists for backward compatibility with pre-v1 APIs.
   563  		ginkgo.It("should implement legacy replacement when the update strategy is OnDelete", func(ctx context.Context) {
   564  			ginkgo.By("Creating a new StatefulSet")
   565  			ss := e2estatefulset.NewStatefulSet("ss2", ns, headlessSvcName, 3, nil, nil, labels)
   566  			setHTTPProbe(ss)
   567  			ss.Spec.UpdateStrategy = appsv1.StatefulSetUpdateStrategy{
   568  				Type: appsv1.OnDeleteStatefulSetStrategyType,
   569  			}
   570  			ss, err := c.AppsV1().StatefulSets(ns).Create(ctx, ss, metav1.CreateOptions{})
   571  			framework.ExpectNoError(err)
   572  			e2estatefulset.WaitForRunningAndReady(ctx, c, *ss.Spec.Replicas, ss)
   573  			ss = waitForStatus(ctx, c, ss)
   574  			currentRevision, updateRevision := ss.Status.CurrentRevision, ss.Status.UpdateRevision
   575  			gomega.Expect(currentRevision).To(gomega.Equal(updateRevision), "StatefulSet %s/%s created with update revision %s not equal to current revision %s",
   576  				ss.Namespace, ss.Name, updateRevision, currentRevision)
   577  			pods := e2estatefulset.GetPodList(ctx, c, ss)
   578  			for i := range pods.Items {
   579  				gomega.Expect(pods.Items[i].Labels).To(gomega.HaveKeyWithValue(appsv1.StatefulSetRevisionLabel, currentRevision), "Pod %s/%s revision %s is not equal to current revision %s",
   580  					pods.Items[i].Namespace,
   581  					pods.Items[i].Name,
   582  					pods.Items[i].Labels[appsv1.StatefulSetRevisionLabel],
   583  					currentRevision)
   584  			}
   585  
   586  			ginkgo.By("Restoring Pods to the current revision")
   587  			deleteStatefulPodAtIndex(ctx, c, 0, ss)
   588  			deleteStatefulPodAtIndex(ctx, c, 1, ss)
   589  			deleteStatefulPodAtIndex(ctx, c, 2, ss)
   590  			e2estatefulset.WaitForRunningAndReady(ctx, c, 3, ss)
   591  			ss = getStatefulSet(ctx, c, ss.Namespace, ss.Name)
   592  			pods = e2estatefulset.GetPodList(ctx, c, ss)
   593  			for i := range pods.Items {
   594  				gomega.Expect(pods.Items[i].Labels).To(gomega.HaveKeyWithValue(appsv1.StatefulSetRevisionLabel, currentRevision), "Pod %s/%s revision %s is not equal to current revision %s",
   595  					pods.Items[i].Namespace,
   596  					pods.Items[i].Name,
   597  					pods.Items[i].Labels[appsv1.StatefulSetRevisionLabel],
   598  					currentRevision)
   599  			}
   600  			newImage := NewWebserverImage
   601  			oldImage := ss.Spec.Template.Spec.Containers[0].Image
   602  
   603  			ginkgo.By(fmt.Sprintf("Updating stateful set template: update image from %s to %s", oldImage, newImage))
   604  			gomega.Expect(oldImage).NotTo(gomega.Equal(newImage), "Incorrect test setup: should update to a different image")
   605  			ss, err = updateStatefulSetWithRetries(ctx, c, ns, ss.Name, func(update *appsv1.StatefulSet) {
   606  				update.Spec.Template.Spec.Containers[0].Image = newImage
   607  			})
   608  			framework.ExpectNoError(err)
   609  
   610  			ginkgo.By("Creating a new revision")
   611  			ss = waitForStatus(ctx, c, ss)
   612  			currentRevision, updateRevision = ss.Status.CurrentRevision, ss.Status.UpdateRevision
   613  			gomega.Expect(currentRevision).NotTo(gomega.Equal(updateRevision), "Current revision should not equal update revision during rolling update")
   614  
   615  			ginkgo.By("Recreating Pods at the new revision")
   616  			deleteStatefulPodAtIndex(ctx, c, 0, ss)
   617  			deleteStatefulPodAtIndex(ctx, c, 1, ss)
   618  			deleteStatefulPodAtIndex(ctx, c, 2, ss)
   619  			e2estatefulset.WaitForRunningAndReady(ctx, c, 3, ss)
   620  			ss = getStatefulSet(ctx, c, ss.Namespace, ss.Name)
   621  			pods = e2estatefulset.GetPodList(ctx, c, ss)
   622  			for i := range pods.Items {
   623  				gomega.Expect(pods.Items[i].Spec.Containers[0].Image).To(gomega.Equal(newImage), "Pod %s/%s has image %s not equal to new image %s",
   624  					pods.Items[i].Namespace,
   625  					pods.Items[i].Name,
   626  					pods.Items[i].Spec.Containers[0].Image,
   627  					newImage)
   628  				gomega.Expect(pods.Items[i].Labels).To(gomega.HaveKeyWithValue(appsv1.StatefulSetRevisionLabel, updateRevision), "Pod %s/%s has revision %s not equal to current revision %s",
   629  					pods.Items[i].Namespace,
   630  					pods.Items[i].Name,
   631  					pods.Items[i].Labels[appsv1.StatefulSetRevisionLabel],
   632  					updateRevision)
   633  			}
   634  		})
   635  
   636  		/*
   637  		   Release: v1.9
   638  		   Testname: StatefulSet, Scaling
   639  		   Description: StatefulSet MUST create Pods in ascending order by ordinal index when scaling up, and delete Pods in descending order when scaling down. Scaling up or down MUST pause if any Pods belonging to the StatefulSet are unhealthy. This test does not depend on a preexisting default StorageClass or a dynamic provisioner.
   640  		*/
   641  		framework.ConformanceIt("Scaling should happen in predictable order and halt if any stateful pod is unhealthy", f.WithSlow(), func(ctx context.Context) {
   642  			psLabels := klabels.Set(labels)
   643  			w := &cache.ListWatch{
   644  				WatchFunc: func(options metav1.ListOptions) (i watch.Interface, e error) {
   645  					options.LabelSelector = psLabels.AsSelector().String()
   646  					return f.ClientSet.CoreV1().Pods(ns).Watch(ctx, options)
   647  				},
   648  			}
   649  			ginkgo.By("Initializing watcher for selector " + psLabels.String())
   650  			pl, err := f.ClientSet.CoreV1().Pods(ns).List(ctx, metav1.ListOptions{
   651  				LabelSelector: psLabels.AsSelector().String(),
   652  			})
   653  			framework.ExpectNoError(err)
   654  
   655  			// Verify that stateful set will be scaled up in order.
   656  			wg := sync.WaitGroup{}
   657  			var orderErr error
   658  			wg.Add(1)
   659  			go func() {
   660  				defer ginkgo.GinkgoRecover()
   661  				defer wg.Done()
   662  
   663  				expectedOrder := []string{ssName + "-0", ssName + "-1", ssName + "-2"}
   664  				ctx, cancel := watchtools.ContextWithOptionalTimeout(ctx, statefulSetTimeout)
   665  				defer cancel()
   666  
   667  				_, orderErr = watchtools.Until(ctx, pl.ResourceVersion, w, func(event watch.Event) (bool, error) {
   668  					if event.Type != watch.Added {
   669  						return false, nil
   670  					}
   671  					pod := event.Object.(*v1.Pod)
   672  					if pod.Name == expectedOrder[0] {
   673  						expectedOrder = expectedOrder[1:]
   674  					}
   675  					return len(expectedOrder) == 0, nil
   676  				})
   677  			}()
   678  
   679  			ginkgo.By("Creating stateful set " + ssName + " in namespace " + ns)
   680  			ss := e2estatefulset.NewStatefulSet(ssName, ns, headlessSvcName, 1, nil, nil, psLabels)
   681  			setHTTPProbe(ss)
   682  			ss, err = c.AppsV1().StatefulSets(ns).Create(ctx, ss, metav1.CreateOptions{})
   683  			framework.ExpectNoError(err)
   684  
   685  			ginkgo.By("Waiting until all stateful set " + ssName + " replicas will be running in namespace " + ns)
   686  			e2estatefulset.WaitForRunningAndReady(ctx, c, *ss.Spec.Replicas, ss)
   687  
   688  			ginkgo.By("Confirming that stateful set scale up will halt with unhealthy stateful pod")
   689  			breakHTTPProbe(ctx, c, ss)
   690  			waitForRunningAndNotReady(ctx, c, *ss.Spec.Replicas, ss)
   691  			e2estatefulset.WaitForStatusReadyReplicas(ctx, c, ss, 0)
   692  			e2estatefulset.UpdateReplicas(ctx, c, ss, 3)
   693  			confirmStatefulPodCount(ctx, c, 1, ss, 10*time.Second, true)
   694  
   695  			ginkgo.By("Scaling up stateful set " + ssName + " to 3 replicas and waiting until all of them will be running in namespace " + ns)
   696  			restoreHTTPProbe(ctx, c, ss)
   697  			e2estatefulset.WaitForRunningAndReady(ctx, c, 3, ss)
   698  
   699  			ginkgo.By("Verifying that stateful set " + ssName + " was scaled up in order")
   700  			wg.Wait()
   701  			framework.ExpectNoError(orderErr)
   702  
   703  			ginkgo.By("Scale down will halt with unhealthy stateful pod")
   704  			pl, err = f.ClientSet.CoreV1().Pods(ns).List(ctx, metav1.ListOptions{
   705  				LabelSelector: psLabels.AsSelector().String(),
   706  			})
   707  			framework.ExpectNoError(err)
   708  
   709  			// Verify that stateful set will be scaled down in order.
   710  			wg.Add(1)
   711  			go func() {
   712  				defer ginkgo.GinkgoRecover()
   713  				defer wg.Done()
   714  
   715  				expectedOrder := []string{ssName + "-2", ssName + "-1", ssName + "-0"}
   716  				ctx, cancel := watchtools.ContextWithOptionalTimeout(ctx, statefulSetTimeout)
   717  				defer cancel()
   718  
   719  				_, orderErr = watchtools.Until(ctx, pl.ResourceVersion, w, func(event watch.Event) (bool, error) {
   720  					if event.Type != watch.Deleted {
   721  						return false, nil
   722  					}
   723  					pod := event.Object.(*v1.Pod)
   724  					if pod.Name == expectedOrder[0] {
   725  						expectedOrder = expectedOrder[1:]
   726  					}
   727  					return len(expectedOrder) == 0, nil
   728  				})
   729  			}()
   730  
   731  			breakHTTPProbe(ctx, c, ss)
   732  			e2estatefulset.WaitForStatusReadyReplicas(ctx, c, ss, 0)
   733  			waitForRunningAndNotReady(ctx, c, 3, ss)
   734  			e2estatefulset.UpdateReplicas(ctx, c, ss, 0)
   735  			confirmStatefulPodCount(ctx, c, 3, ss, 10*time.Second, true)
   736  
   737  			ginkgo.By("Scaling down stateful set " + ssName + " to 0 replicas and waiting until none of pods will run in namespace" + ns)
   738  			restoreHTTPProbe(ctx, c, ss)
   739  			e2estatefulset.Scale(ctx, c, ss, 0)
   740  
   741  			ginkgo.By("Verifying that stateful set " + ssName + " was scaled down in reverse order")
   742  			wg.Wait()
   743  			framework.ExpectNoError(orderErr)
   744  		})
   745  
   746  		/*
   747  		   Release: v1.9
   748  		   Testname: StatefulSet, Burst Scaling
   749  		   Description: StatefulSet MUST support the Parallel PodManagementPolicy for burst scaling. This test does not depend on a preexisting default StorageClass or a dynamic provisioner.
   750  		*/
   751  		framework.ConformanceIt("Burst scaling should run to completion even with unhealthy pods", f.WithSlow(), func(ctx context.Context) {
   752  			psLabels := klabels.Set(labels)
   753  
   754  			ginkgo.By("Creating stateful set " + ssName + " in namespace " + ns)
   755  			ss := e2estatefulset.NewStatefulSet(ssName, ns, headlessSvcName, 1, nil, nil, psLabels)
   756  			ss.Spec.PodManagementPolicy = appsv1.ParallelPodManagement
   757  			setHTTPProbe(ss)
   758  			ss, err := c.AppsV1().StatefulSets(ns).Create(ctx, ss, metav1.CreateOptions{})
   759  			framework.ExpectNoError(err)
   760  
   761  			ginkgo.By("Waiting until all stateful set " + ssName + " replicas will be running in namespace " + ns)
   762  			e2estatefulset.WaitForRunningAndReady(ctx, c, *ss.Spec.Replicas, ss)
   763  
   764  			ginkgo.By("Confirming that stateful set scale up will not halt with unhealthy stateful pod")
   765  			breakHTTPProbe(ctx, c, ss)
   766  			waitForRunningAndNotReady(ctx, c, *ss.Spec.Replicas, ss)
   767  			e2estatefulset.WaitForStatusReadyReplicas(ctx, c, ss, 0)
   768  			e2estatefulset.UpdateReplicas(ctx, c, ss, 3)
   769  			confirmStatefulPodCount(ctx, c, 3, ss, 10*time.Second, false)
   770  
   771  			ginkgo.By("Scaling up stateful set " + ssName + " to 3 replicas and waiting until all of them will be running in namespace " + ns)
   772  			restoreHTTPProbe(ctx, c, ss)
   773  			e2estatefulset.WaitForRunningAndReady(ctx, c, 3, ss)
   774  
   775  			ginkgo.By("Scale down will not halt with unhealthy stateful pod")
   776  			breakHTTPProbe(ctx, c, ss)
   777  			e2estatefulset.WaitForStatusReadyReplicas(ctx, c, ss, 0)
   778  			waitForRunningAndNotReady(ctx, c, 3, ss)
   779  			e2estatefulset.UpdateReplicas(ctx, c, ss, 0)
   780  			confirmStatefulPodCount(ctx, c, 0, ss, 10*time.Second, false)
   781  
   782  			ginkgo.By("Scaling down stateful set " + ssName + " to 0 replicas and waiting until none of pods will run in namespace" + ns)
   783  			restoreHTTPProbe(ctx, c, ss)
   784  			e2estatefulset.Scale(ctx, c, ss, 0)
   785  			e2estatefulset.WaitForStatusReplicas(ctx, c, ss, 0)
   786  		})
   787  
   788  		/*
   789  		   Release: v1.9
   790  		   Testname: StatefulSet, Recreate Failed Pod
   791  		   Description: StatefulSet MUST delete and recreate Pods it owns that go into a Failed state, such as when they are rejected or evicted by a Node. This test does not depend on a preexisting default StorageClass or a dynamic provisioner.
   792  		*/
   793  		framework.ConformanceIt("Should recreate evicted statefulset", func(ctx context.Context) {
   794  			podName := "test-pod"
   795  			statefulPodName := ssName + "-0"
   796  			ginkgo.By("Looking for a node to schedule stateful set and pod")
   797  			node, err := e2enode.GetRandomReadySchedulableNode(ctx, f.ClientSet)
   798  			framework.ExpectNoError(err)
   799  
   800  			ginkgo.By("Creating pod with conflicting port in namespace " + f.Namespace.Name)
   801  			conflictingPort := v1.ContainerPort{HostPort: 21017, ContainerPort: 21017, Name: "conflict"}
   802  			pod := &v1.Pod{
   803  				ObjectMeta: metav1.ObjectMeta{
   804  					Name: podName,
   805  				},
   806  				Spec: v1.PodSpec{
   807  					Containers: []v1.Container{
   808  						{
   809  							Name:  "webserver",
   810  							Image: imageutils.GetE2EImage(imageutils.Httpd),
   811  							Ports: []v1.ContainerPort{conflictingPort},
   812  						},
   813  					},
   814  					NodeName: node.Name,
   815  				},
   816  			}
   817  			pod, err = f.ClientSet.CoreV1().Pods(f.Namespace.Name).Create(ctx, pod, metav1.CreateOptions{})
   818  			framework.ExpectNoError(err)
   819  			ginkgo.By("Waiting until pod " + podName + " will start running in namespace " + f.Namespace.Name)
   820  			if err := e2epod.WaitForPodNameRunningInNamespace(ctx, f.ClientSet, podName, f.Namespace.Name); err != nil {
   821  				framework.Failf("Pod %v did not start running: %v", podName, err)
   822  			}
   823  
   824  			ginkgo.By("Creating statefulset with conflicting port in namespace " + f.Namespace.Name)
   825  			ss := e2estatefulset.NewStatefulSet(ssName, f.Namespace.Name, headlessSvcName, 1, nil, nil, labels)
   826  			statefulPodContainer := &ss.Spec.Template.Spec.Containers[0]
   827  			statefulPodContainer.Ports = append(statefulPodContainer.Ports, conflictingPort)
   828  			ss.Spec.Template.Spec.NodeName = node.Name
   829  			_, err = f.ClientSet.AppsV1().StatefulSets(f.Namespace.Name).Create(ctx, ss, metav1.CreateOptions{})
   830  			framework.ExpectNoError(err)
   831  
   832  			var initialStatefulPodUID types.UID
   833  			ginkgo.By("Waiting until stateful pod " + statefulPodName + " will be recreated and deleted at least once in namespace " + f.Namespace.Name)
   834  
   835  			fieldSelector := fields.OneTermEqualSelector("metadata.name", statefulPodName).String()
   836  			pl, err := f.ClientSet.CoreV1().Pods(ns).List(ctx, metav1.ListOptions{
   837  				FieldSelector: fieldSelector,
   838  			})
   839  			framework.ExpectNoError(err)
   840  			if len(pl.Items) > 0 {
   841  				pod := pl.Items[0]
   842  				framework.Logf("Observed stateful pod in namespace: %v, name: %v, uid: %v, status phase: %v. Waiting for statefulset controller to delete.",
   843  					pod.Namespace, pod.Name, pod.UID, pod.Status.Phase)
   844  				initialStatefulPodUID = pod.UID
   845  			}
   846  
   847  			lw := &cache.ListWatch{
   848  				WatchFunc: func(options metav1.ListOptions) (i watch.Interface, e error) {
   849  					options.FieldSelector = fieldSelector
   850  					return f.ClientSet.CoreV1().Pods(f.Namespace.Name).Watch(ctx, options)
   851  				},
   852  			}
   853  			ctx, cancel := watchtools.ContextWithOptionalTimeout(ctx, statefulPodTimeout)
   854  			defer cancel()
   855  			// we need to get UID from pod in any state and wait until stateful set controller will remove pod at least once
   856  			_, err = watchtools.Until(ctx, pl.ResourceVersion, lw, func(event watch.Event) (bool, error) {
   857  				pod := event.Object.(*v1.Pod)
   858  				switch event.Type {
   859  				case watch.Deleted:
   860  					framework.Logf("Observed delete event for stateful pod %v in namespace %v", pod.Name, pod.Namespace)
   861  					if initialStatefulPodUID == "" {
   862  						return false, nil
   863  					}
   864  					return true, nil
   865  				}
   866  				framework.Logf("Observed stateful pod in namespace: %v, name: %v, uid: %v, status phase: %v. Waiting for statefulset controller to delete.",
   867  					pod.Namespace, pod.Name, pod.UID, pod.Status.Phase)
   868  				initialStatefulPodUID = pod.UID
   869  				return false, nil
   870  			})
   871  			if err != nil {
   872  				framework.Failf("Pod %v expected to be re-created at least once", statefulPodName)
   873  			}
   874  
   875  			ginkgo.By("Removing pod with conflicting port in namespace " + f.Namespace.Name)
   876  			err = f.ClientSet.CoreV1().Pods(f.Namespace.Name).Delete(ctx, pod.Name, *metav1.NewDeleteOptions(0))
   877  			framework.ExpectNoError(err)
   878  
   879  			ginkgo.By("Waiting when stateful pod " + statefulPodName + " will be recreated in namespace " + f.Namespace.Name + " and will be in running state")
   880  			// we may catch delete event, that's why we are waiting for running phase like this, and not with watchtools.UntilWithoutRetry
   881  			gomega.Eventually(ctx, func() error {
   882  				statefulPod, err := f.ClientSet.CoreV1().Pods(f.Namespace.Name).Get(ctx, statefulPodName, metav1.GetOptions{})
   883  				if err != nil {
   884  					return err
   885  				}
   886  				if statefulPod.Status.Phase != v1.PodRunning {
   887  					return fmt.Errorf("pod %v is not in running phase: %v", statefulPod.Name, statefulPod.Status.Phase)
   888  				} else if statefulPod.UID == initialStatefulPodUID {
   889  					return fmt.Errorf("pod %v wasn't recreated: %v == %v", statefulPod.Name, statefulPod.UID, initialStatefulPodUID)
   890  				}
   891  				return nil
   892  			}, statefulPodTimeout, 2*time.Second).Should(gomega.BeNil())
   893  		})
   894  
   895  		/*
   896  			Release: v1.16, v1.21
   897  			Testname: StatefulSet resource Replica scaling
   898  			Description: Create a StatefulSet resource.
   899  			Newly created StatefulSet resource MUST have a scale of one.
   900  			Bring the scale of the StatefulSet resource up to two. StatefulSet scale MUST be at two replicas.
   901  		*/
   902  		framework.ConformanceIt("should have a working scale subresource", func(ctx context.Context) {
   903  			ginkgo.By("Creating statefulset " + ssName + " in namespace " + ns)
   904  			ss := e2estatefulset.NewStatefulSet(ssName, ns, headlessSvcName, 1, nil, nil, labels)
   905  			setHTTPProbe(ss)
   906  			ss, err := c.AppsV1().StatefulSets(ns).Create(ctx, ss, metav1.CreateOptions{})
   907  			framework.ExpectNoError(err)
   908  			e2estatefulset.WaitForRunningAndReady(ctx, c, *ss.Spec.Replicas, ss)
   909  			waitForStatus(ctx, c, ss)
   910  
   911  			ginkgo.By("getting scale subresource")
   912  			scale, err := c.AppsV1().StatefulSets(ns).GetScale(ctx, ssName, metav1.GetOptions{})
   913  			if err != nil {
   914  				framework.Failf("Failed to get scale subresource: %v", err)
   915  			}
   916  			gomega.Expect(scale.Spec.Replicas).To(gomega.Equal(int32(1)))
   917  			gomega.Expect(scale.Status.Replicas).To(gomega.Equal(int32(1)))
   918  
   919  			ginkgo.By("updating a scale subresource")
   920  			scale.ResourceVersion = "" // indicate the scale update should be unconditional
   921  			scale.Spec.Replicas = 2
   922  			scaleResult, err := c.AppsV1().StatefulSets(ns).UpdateScale(ctx, ssName, scale, metav1.UpdateOptions{})
   923  			if err != nil {
   924  				framework.Failf("Failed to put scale subresource: %v", err)
   925  			}
   926  			gomega.Expect(scaleResult.Spec.Replicas).To(gomega.Equal(int32(2)))
   927  
   928  			ginkgo.By("verifying the statefulset Spec.Replicas was modified")
   929  			ss, err = c.AppsV1().StatefulSets(ns).Get(ctx, ssName, metav1.GetOptions{})
   930  			if err != nil {
   931  				framework.Failf("Failed to get statefulset resource: %v", err)
   932  			}
   933  			gomega.Expect(*(ss.Spec.Replicas)).To(gomega.Equal(int32(2)))
   934  
   935  			ginkgo.By("Patch a scale subresource")
   936  			scale.ResourceVersion = "" // indicate the scale update should be unconditional
   937  			scale.Spec.Replicas = 4    // should be 2 after "UpdateScale" operation, now Patch to 4
   938  			ssScalePatchPayload, err := json.Marshal(autoscalingv1.Scale{
   939  				Spec: autoscalingv1.ScaleSpec{
   940  					Replicas: scale.Spec.Replicas,
   941  				},
   942  			})
   943  			framework.ExpectNoError(err, "Could not Marshal JSON for patch payload")
   944  
   945  			_, err = c.AppsV1().StatefulSets(ns).Patch(ctx, ssName, types.StrategicMergePatchType, []byte(ssScalePatchPayload), metav1.PatchOptions{}, "scale")
   946  			framework.ExpectNoError(err, "Failed to patch stateful set: %v", err)
   947  
   948  			ginkgo.By("verifying the statefulset Spec.Replicas was modified")
   949  			ss, err = c.AppsV1().StatefulSets(ns).Get(ctx, ssName, metav1.GetOptions{})
   950  			framework.ExpectNoError(err, "Failed to get statefulset resource: %v", err)
   951  			gomega.Expect(*(ss.Spec.Replicas)).To(gomega.Equal(int32(4)), "statefulset should have 4 replicas")
   952  		})
   953  
   954  		/*
   955  			Release: v1.22
   956  			Testname: StatefulSet, list, patch and delete a collection of StatefulSets
   957  			Description: When a StatefulSet is created it MUST succeed. It
   958  			MUST succeed when listing StatefulSets via a label selector. It
   959  			MUST succeed when patching a StatefulSet. It MUST succeed when
   960  			deleting the StatefulSet via deleteCollection.
   961  		*/
   962  		framework.ConformanceIt("should list, patch and delete a collection of StatefulSets", func(ctx context.Context) {
   963  
   964  			ssPatchReplicas := int32(2)
   965  			ssPatchImage := imageutils.GetE2EImage(imageutils.Pause)
   966  			one := int64(1)
   967  			ssName := "test-ss"
   968  
   969  			// Define StatefulSet Labels
   970  			ssPodLabels := map[string]string{
   971  				"name": "sample-pod",
   972  				"pod":  WebserverImageName,
   973  			}
   974  			ss := e2estatefulset.NewStatefulSet(ssName, ns, headlessSvcName, 1, nil, nil, ssPodLabels)
   975  			setHTTPProbe(ss)
   976  			ss, err := c.AppsV1().StatefulSets(ns).Create(ctx, ss, metav1.CreateOptions{})
   977  			framework.ExpectNoError(err)
   978  			e2estatefulset.WaitForRunningAndReady(ctx, c, *ss.Spec.Replicas, ss)
   979  			waitForStatus(ctx, c, ss)
   980  
   981  			ginkgo.By("patching the StatefulSet")
   982  			ssPatch, err := json.Marshal(map[string]interface{}{
   983  				"metadata": map[string]interface{}{
   984  					"labels": map[string]string{"test-ss": "patched"},
   985  				},
   986  				"spec": map[string]interface{}{
   987  					"replicas": ssPatchReplicas,
   988  					"template": map[string]interface{}{
   989  						"spec": map[string]interface{}{
   990  							"TerminationGracePeriodSeconds": &one,
   991  							"containers": [1]map[string]interface{}{{
   992  								"name":  ssName,
   993  								"image": ssPatchImage,
   994  							}},
   995  						},
   996  					},
   997  				},
   998  			})
   999  			framework.ExpectNoError(err, "failed to Marshal StatefulSet JSON patch")
  1000  			_, err = f.ClientSet.AppsV1().StatefulSets(ns).Patch(ctx, ssName, types.StrategicMergePatchType, []byte(ssPatch), metav1.PatchOptions{})
  1001  			framework.ExpectNoError(err, "failed to patch Set")
  1002  			ss, err = c.AppsV1().StatefulSets(ns).Get(ctx, ssName, metav1.GetOptions{})
  1003  			framework.ExpectNoError(err, "Failed to get statefulset resource: %v", err)
  1004  			gomega.Expect(*(ss.Spec.Replicas)).To(gomega.Equal(ssPatchReplicas), "statefulset should have 2 replicas")
  1005  			gomega.Expect(ss.Spec.Template.Spec.Containers[0].Image).To(gomega.Equal(ssPatchImage), "statefulset not using ssPatchImage. Is using %v", ss.Spec.Template.Spec.Containers[0].Image)
  1006  			e2estatefulset.WaitForRunningAndReady(ctx, c, *ss.Spec.Replicas, ss)
  1007  			waitForStatus(ctx, c, ss)
  1008  
  1009  			ginkgo.By("Listing all StatefulSets")
  1010  			ssList, err := c.AppsV1().StatefulSets("").List(ctx, metav1.ListOptions{LabelSelector: "test-ss=patched"})
  1011  			framework.ExpectNoError(err, "failed to list StatefulSets")
  1012  			gomega.Expect(ssList.Items).To(gomega.HaveLen(1), "filtered list wasn't found")
  1013  
  1014  			ginkgo.By("Delete all of the StatefulSets")
  1015  			err = c.AppsV1().StatefulSets(ns).DeleteCollection(ctx, metav1.DeleteOptions{GracePeriodSeconds: &one}, metav1.ListOptions{LabelSelector: "test-ss=patched"})
  1016  			framework.ExpectNoError(err, "failed to delete StatefulSets")
  1017  
  1018  			ginkgo.By("Verify that StatefulSets have been deleted")
  1019  			ssList, err = c.AppsV1().StatefulSets("").List(ctx, metav1.ListOptions{LabelSelector: "test-ss=patched"})
  1020  			framework.ExpectNoError(err, "failed to list StatefulSets")
  1021  			gomega.Expect(ssList.Items).To(gomega.BeEmpty(), "filtered list should have no Statefulsets")
  1022  		})
  1023  
  1024  		/*
  1025  			Release: v1.22
  1026  			Testname: StatefulSet, status sub-resource
  1027  			Description: When a StatefulSet is created it MUST succeed.
  1028  			Attempt to read, update and patch its status sub-resource; all
  1029  			mutating sub-resource operations MUST be visible to subsequent reads.
  1030  		*/
  1031  		framework.ConformanceIt("should validate Statefulset Status endpoints", func(ctx context.Context) {
  1032  			ssClient := c.AppsV1().StatefulSets(ns)
  1033  			labelSelector := "e2e=testing"
  1034  
  1035  			w := &cache.ListWatch{
  1036  				WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
  1037  					options.LabelSelector = labelSelector
  1038  					return ssClient.Watch(ctx, options)
  1039  				},
  1040  			}
  1041  			ssList, err := c.AppsV1().StatefulSets("").List(ctx, metav1.ListOptions{LabelSelector: labelSelector})
  1042  			framework.ExpectNoError(err, "failed to list StatefulSets")
  1043  
  1044  			ginkgo.By("Creating statefulset " + ssName + " in namespace " + ns)
  1045  			ss := e2estatefulset.NewStatefulSet(ssName, ns, headlessSvcName, 1, nil, nil, labels)
  1046  			setHTTPProbe(ss)
  1047  			ss, err = c.AppsV1().StatefulSets(ns).Create(ctx, ss, metav1.CreateOptions{})
  1048  			framework.ExpectNoError(err)
  1049  			e2estatefulset.WaitForRunningAndReady(ctx, c, *ss.Spec.Replicas, ss)
  1050  			waitForStatus(ctx, c, ss)
  1051  
  1052  			ginkgo.By("Patch Statefulset to include a label")
  1053  			payload := []byte(`{"metadata":{"labels":{"e2e":"testing"}}}`)
  1054  			ss, err = ssClient.Patch(ctx, ssName, types.StrategicMergePatchType, payload, metav1.PatchOptions{})
  1055  			framework.ExpectNoError(err)
  1056  
  1057  			ginkgo.By("Getting /status")
  1058  			ssResource := schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "statefulsets"}
  1059  			ssStatusUnstructured, err := f.DynamicClient.Resource(ssResource).Namespace(ns).Get(ctx, ssName, metav1.GetOptions{}, "status")
  1060  			framework.ExpectNoError(err, "Failed to fetch the status of replica set %s in namespace %s", ssName, ns)
  1061  			ssStatusBytes, err := json.Marshal(ssStatusUnstructured)
  1062  			framework.ExpectNoError(err, "Failed to marshal unstructured response. %v", err)
  1063  
  1064  			var ssStatus appsv1.StatefulSet
  1065  			err = json.Unmarshal(ssStatusBytes, &ssStatus)
  1066  			framework.ExpectNoError(err, "Failed to unmarshal JSON bytes to a Statefulset object type")
  1067  			framework.Logf("StatefulSet %s has Conditions: %#v", ssName, ssStatus.Status.Conditions)
  1068  
  1069  			ginkgo.By("updating the StatefulSet Status")
  1070  			var statusToUpdate, updatedStatus *appsv1.StatefulSet
  1071  
  1072  			err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
  1073  				statusToUpdate, err = ssClient.Get(ctx, ssName, metav1.GetOptions{})
  1074  				framework.ExpectNoError(err, "Unable to retrieve statefulset %s", ssName)
  1075  
  1076  				statusToUpdate.Status.Conditions = append(statusToUpdate.Status.Conditions, appsv1.StatefulSetCondition{
  1077  					Type:    "StatusUpdate",
  1078  					Status:  "True",
  1079  					Reason:  "E2E",
  1080  					Message: "Set from e2e test",
  1081  				})
  1082  
  1083  				updatedStatus, err = ssClient.UpdateStatus(ctx, statusToUpdate, metav1.UpdateOptions{})
  1084  				return err
  1085  			})
  1086  			framework.ExpectNoError(err, "Failed to update status. %v", err)
  1087  			framework.Logf("updatedStatus.Conditions: %#v", updatedStatus.Status.Conditions)
  1088  
  1089  			ginkgo.By("watching for the statefulset status to be updated")
  1090  
  1091  			ctxUntil, cancel := context.WithTimeout(ctx, statefulSetTimeout)
  1092  			defer cancel()
  1093  
  1094  			_, err = watchtools.Until(ctxUntil, ssList.ResourceVersion, w, func(event watch.Event) (bool, error) {
  1095  
  1096  				if e, ok := event.Object.(*appsv1.StatefulSet); ok {
  1097  					found := e.ObjectMeta.Name == ss.ObjectMeta.Name &&
  1098  						e.ObjectMeta.Namespace == ss.ObjectMeta.Namespace &&
  1099  						e.ObjectMeta.Labels["e2e"] == ss.ObjectMeta.Labels["e2e"]
  1100  					if !found {
  1101  						framework.Logf("Observed Statefulset %v in namespace %v with annotations: %v & Conditions: %v", ss.ObjectMeta.Name, ss.ObjectMeta.Namespace, ss.Annotations, ss.Status.Conditions)
  1102  						return false, nil
  1103  					}
  1104  					for _, cond := range e.Status.Conditions {
  1105  						if cond.Type == "StatusUpdate" &&
  1106  							cond.Reason == "E2E" &&
  1107  							cond.Message == "Set from e2e test" {
  1108  							framework.Logf("Found Statefulset %v in namespace %v with labels: %v annotations: %v & Conditions: %v", ss.ObjectMeta.Name, ss.ObjectMeta.Namespace, ss.ObjectMeta.Labels, ss.Annotations, cond)
  1109  							return found, nil
  1110  						}
  1111  						framework.Logf("Observed Statefulset %v in namespace %v with annotations: %v & Conditions: %v", ss.ObjectMeta.Name, ss.ObjectMeta.Namespace, ss.Annotations, cond)
  1112  					}
  1113  				}
  1114  				object := strings.Split(fmt.Sprintf("%v", event.Object), "{")[0]
  1115  				framework.Logf("Observed %v event: %+v", object, event.Type)
  1116  				return false, nil
  1117  			})
  1118  			framework.ExpectNoError(err, "failed to locate Statefulset %v in namespace %v", ss.ObjectMeta.Name, ns)
  1119  			framework.Logf("Statefulset %s has an updated status", ssName)
  1120  
  1121  			ginkgo.By("patching the Statefulset Status")
  1122  			payload = []byte(`{"status":{"conditions":[{"type":"StatusPatched","status":"True"}]}}`)
  1123  			framework.Logf("Patch payload: %v", string(payload))
  1124  
  1125  			patchedStatefulSet, err := ssClient.Patch(ctx, ssName, types.MergePatchType, payload, metav1.PatchOptions{}, "status")
  1126  			framework.ExpectNoError(err, "Failed to patch status. %v", err)
  1127  			framework.Logf("Patched status conditions: %#v", patchedStatefulSet.Status.Conditions)
  1128  
  1129  			ginkgo.By("watching for the Statefulset status to be patched")
  1130  			ctxUntil, cancel = context.WithTimeout(ctx, statefulSetTimeout)
  1131  
  1132  			_, err = watchtools.Until(ctxUntil, ssList.ResourceVersion, w, func(event watch.Event) (bool, error) {
  1133  
  1134  				defer cancel()
  1135  				if e, ok := event.Object.(*appsv1.StatefulSet); ok {
  1136  					found := e.ObjectMeta.Name == ss.ObjectMeta.Name &&
  1137  						e.ObjectMeta.Namespace == ss.ObjectMeta.Namespace &&
  1138  						e.ObjectMeta.Labels["e2e"] == ss.ObjectMeta.Labels["e2e"]
  1139  					if !found {
  1140  						framework.Logf("Observed Statefulset %v in namespace %v with annotations: %v & Conditions: %v", ss.ObjectMeta.Name, ss.ObjectMeta.Namespace, ss.Annotations, ss.Status.Conditions)
  1141  						return false, nil
  1142  					}
  1143  					for _, cond := range e.Status.Conditions {
  1144  						if cond.Type == "StatusPatched" {
  1145  							framework.Logf("Found Statefulset %v in namespace %v with labels: %v annotations: %v & Conditions: %v", ss.ObjectMeta.Name, ss.ObjectMeta.Namespace, ss.ObjectMeta.Labels, ss.Annotations, cond)
  1146  							return found, nil
  1147  						}
  1148  						framework.Logf("Observed Statefulset %v in namespace %v with annotations: %v & Conditions: %v", ss.ObjectMeta.Name, ss.ObjectMeta.Namespace, ss.Annotations, cond)
  1149  					}
  1150  				}
  1151  				object := strings.Split(fmt.Sprintf("%v", event.Object), "{")[0]
  1152  				framework.Logf("Observed %v event: %+v", object, event.Type)
  1153  				return false, nil
  1154  			})
  1155  		})
  1156  	})
  1157  
  1158  	f.Describe("Deploy clustered applications", feature.StatefulSet, framework.WithSlow(), func() {
  1159  		var appTester *clusterAppTester
  1160  
  1161  		ginkgo.BeforeEach(func(ctx context.Context) {
  1162  			appTester = &clusterAppTester{client: c, ns: ns}
  1163  		})
  1164  
  1165  		ginkgo.AfterEach(func(ctx context.Context) {
  1166  			if ginkgo.CurrentSpecReport().Failed() {
  1167  				e2eoutput.DumpDebugInfo(ctx, c, ns)
  1168  			}
  1169  			framework.Logf("Deleting all statefulset in ns %v", ns)
  1170  			e2estatefulset.DeleteAllStatefulSets(ctx, c, ns)
  1171  		})
  1172  
  1173  		// Do not mark this as Conformance.
  1174  		// StatefulSet Conformance should not be dependent on specific applications.
  1175  		ginkgo.It("should creating a working zookeeper cluster", func(ctx context.Context) {
  1176  			e2epv.SkipIfNoDefaultStorageClass(ctx, c)
  1177  			appTester.statefulPod = &zookeeperTester{client: c}
  1178  			appTester.run(ctx)
  1179  		})
  1180  
  1181  		// Do not mark this as Conformance.
  1182  		// StatefulSet Conformance should not be dependent on specific applications.
  1183  		ginkgo.It("should creating a working redis cluster", func(ctx context.Context) {
  1184  			e2epv.SkipIfNoDefaultStorageClass(ctx, c)
  1185  			appTester.statefulPod = &redisTester{client: c}
  1186  			appTester.run(ctx)
  1187  		})
  1188  
  1189  		// Do not mark this as Conformance.
  1190  		// StatefulSet Conformance should not be dependent on specific applications.
  1191  		ginkgo.It("should creating a working mysql cluster", func(ctx context.Context) {
  1192  			e2epv.SkipIfNoDefaultStorageClass(ctx, c)
  1193  			appTester.statefulPod = &mysqlGaleraTester{client: c}
  1194  			appTester.run(ctx)
  1195  		})
  1196  
  1197  		// Do not mark this as Conformance.
  1198  		// StatefulSet Conformance should not be dependent on specific applications.
  1199  		ginkgo.It("should creating a working CockroachDB cluster", func(ctx context.Context) {
  1200  			e2epv.SkipIfNoDefaultStorageClass(ctx, c)
  1201  			appTester.statefulPod = &cockroachDBTester{client: c}
  1202  			appTester.run(ctx)
  1203  		})
  1204  	})
  1205  
  1206  	// Make sure minReadySeconds is honored
  1207  	// Don't mark it as conformance yet
  1208  	ginkgo.It("MinReadySeconds should be honored when enabled", func(ctx context.Context) {
  1209  		ssName := "test-ss"
  1210  		headlessSvcName := "test"
  1211  		// Define StatefulSet Labels
  1212  		ssPodLabels := map[string]string{
  1213  			"name": "sample-pod",
  1214  			"pod":  WebserverImageName,
  1215  		}
  1216  		ss := e2estatefulset.NewStatefulSet(ssName, ns, headlessSvcName, 1, nil, nil, ssPodLabels)
  1217  		setHTTPProbe(ss)
  1218  		ss, err := c.AppsV1().StatefulSets(ns).Create(ctx, ss, metav1.CreateOptions{})
  1219  		framework.ExpectNoError(err)
  1220  		e2estatefulset.WaitForStatusAvailableReplicas(ctx, c, ss, 1)
  1221  	})
  1222  
  1223  	ginkgo.It("AvailableReplicas should get updated accordingly when MinReadySeconds is enabled", func(ctx context.Context) {
  1224  		ssName := "test-ss"
  1225  		headlessSvcName := "test"
  1226  		// Define StatefulSet Labels
  1227  		ssPodLabels := map[string]string{
  1228  			"name": "sample-pod",
  1229  			"pod":  WebserverImageName,
  1230  		}
  1231  		ss := e2estatefulset.NewStatefulSet(ssName, ns, headlessSvcName, 2, nil, nil, ssPodLabels)
  1232  		ss.Spec.MinReadySeconds = 30
  1233  		setHTTPProbe(ss)
  1234  		ss, err := c.AppsV1().StatefulSets(ns).Create(ctx, ss, metav1.CreateOptions{})
  1235  		framework.ExpectNoError(err)
  1236  		e2estatefulset.WaitForStatusAvailableReplicas(ctx, c, ss, 0)
  1237  		// let's check that the availableReplicas have still not updated
  1238  		time.Sleep(5 * time.Second)
  1239  		ss, err = c.AppsV1().StatefulSets(ns).Get(ctx, ss.Name, metav1.GetOptions{})
  1240  		framework.ExpectNoError(err)
  1241  		if ss.Status.AvailableReplicas != 0 {
  1242  			framework.Failf("invalid number of availableReplicas: expected=%v received=%v", 0, ss.Status.AvailableReplicas)
  1243  		}
  1244  		e2estatefulset.WaitForStatusAvailableReplicas(ctx, c, ss, 2)
  1245  
  1246  		ss, err = updateStatefulSetWithRetries(ctx, c, ns, ss.Name, func(update *appsv1.StatefulSet) {
  1247  			update.Spec.MinReadySeconds = 3600
  1248  		})
  1249  		framework.ExpectNoError(err)
  1250  		// We don't expect replicas to be updated till 1 hour, so the availableReplicas should be 0
  1251  		e2estatefulset.WaitForStatusAvailableReplicas(ctx, c, ss, 0)
  1252  
  1253  		ss, err = updateStatefulSetWithRetries(ctx, c, ns, ss.Name, func(update *appsv1.StatefulSet) {
  1254  			update.Spec.MinReadySeconds = 0
  1255  		})
  1256  		framework.ExpectNoError(err)
  1257  		e2estatefulset.WaitForStatusAvailableReplicas(ctx, c, ss, 2)
  1258  
  1259  		ginkgo.By("check availableReplicas are shown in status")
  1260  		out, err := e2ekubectl.RunKubectl(ns, "get", "statefulset", ss.Name, "-o=yaml")
  1261  		framework.ExpectNoError(err)
  1262  		if !strings.Contains(out, "availableReplicas: 2") {
  1263  			framework.Failf("invalid number of availableReplicas: expected=%v received=%v", 2, out)
  1264  		}
  1265  	})
  1266  
  1267  	ginkgo.Describe("Non-retain StatefulSetPersistentVolumeClaimPolicy", func() {
  1268  		ssName := "ss"
  1269  		labels := map[string]string{
  1270  			"foo": "bar",
  1271  			"baz": "blah",
  1272  		}
  1273  		headlessSvcName := "test"
  1274  		var statefulPodMounts, podMounts []v1.VolumeMount
  1275  		var ss *appsv1.StatefulSet
  1276  
  1277  		ginkgo.BeforeEach(func(ctx context.Context) {
  1278  			statefulPodMounts = []v1.VolumeMount{{Name: "datadir", MountPath: "/data/"}}
  1279  			podMounts = []v1.VolumeMount{{Name: "home", MountPath: "/home"}}
  1280  			ss = e2estatefulset.NewStatefulSet(ssName, ns, headlessSvcName, 2, statefulPodMounts, podMounts, labels)
  1281  
  1282  			ginkgo.By("Creating service " + headlessSvcName + " in namespace " + ns)
  1283  			headlessService := e2eservice.CreateServiceSpec(headlessSvcName, "", true, labels)
  1284  			_, err := c.CoreV1().Services(ns).Create(ctx, headlessService, metav1.CreateOptions{})
  1285  			framework.ExpectNoError(err)
  1286  		})
  1287  
  1288  		ginkgo.AfterEach(func(ctx context.Context) {
  1289  			if ginkgo.CurrentSpecReport().Failed() {
  1290  				e2eoutput.DumpDebugInfo(ctx, c, ns)
  1291  			}
  1292  			framework.Logf("Deleting all statefulset in ns %v", ns)
  1293  			e2estatefulset.DeleteAllStatefulSets(ctx, c, ns)
  1294  		})
  1295  
  1296  		ginkgo.It("should delete PVCs with a WhenDeleted policy", func(ctx context.Context) {
  1297  			e2epv.SkipIfNoDefaultStorageClass(ctx, c)
  1298  			ginkgo.By("Creating statefulset " + ssName + " in namespace " + ns)
  1299  			*(ss.Spec.Replicas) = 3
  1300  			ss.Spec.PersistentVolumeClaimRetentionPolicy = &appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy{
  1301  				WhenDeleted: appsv1.DeletePersistentVolumeClaimRetentionPolicyType,
  1302  			}
  1303  			_, err := c.AppsV1().StatefulSets(ns).Create(ctx, ss, metav1.CreateOptions{})
  1304  			framework.ExpectNoError(err)
  1305  
  1306  			ginkgo.By("Confirming all 3 PVCs exist with their owner refs")
  1307  			err = verifyStatefulSetPVCsExistWithOwnerRefs(ctx, c, ss, []int{0, 1, 2}, true, false)
  1308  			framework.ExpectNoError(err)
  1309  
  1310  			ginkgo.By("Deleting stateful set " + ss.Name)
  1311  			err = c.AppsV1().StatefulSets(ns).Delete(ctx, ss.Name, metav1.DeleteOptions{})
  1312  			framework.ExpectNoError(err)
  1313  
  1314  			ginkgo.By("Verifying PVCs deleted")
  1315  			err = verifyStatefulSetPVCsExist(ctx, c, ss, []int{})
  1316  			framework.ExpectNoError(err)
  1317  		})
  1318  
  1319  		ginkgo.It("should delete PVCs with a OnScaledown policy", func(ctx context.Context) {
  1320  			e2epv.SkipIfNoDefaultStorageClass(ctx, c)
  1321  			ginkgo.By("Creating statefulset " + ssName + " in namespace " + ns)
  1322  			*(ss.Spec.Replicas) = 3
  1323  			ss.Spec.PersistentVolumeClaimRetentionPolicy = &appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy{
  1324  				WhenScaled: appsv1.DeletePersistentVolumeClaimRetentionPolicyType,
  1325  			}
  1326  			_, err := c.AppsV1().StatefulSets(ns).Create(ctx, ss, metav1.CreateOptions{})
  1327  			framework.ExpectNoError(err)
  1328  
  1329  			ginkgo.By("Confirming all 3 PVCs exist")
  1330  			err = verifyStatefulSetPVCsExist(ctx, c, ss, []int{0, 1, 2})
  1331  			framework.ExpectNoError(err)
  1332  
  1333  			ginkgo.By("Scaling stateful set " + ss.Name + " to one replica")
  1334  			ss, err = e2estatefulset.Scale(ctx, c, ss, 1)
  1335  			framework.ExpectNoError(err)
  1336  
  1337  			ginkgo.By("Verifying all but one PVC deleted")
  1338  			err = verifyStatefulSetPVCsExist(ctx, c, ss, []int{0})
  1339  			framework.ExpectNoError(err)
  1340  		})
  1341  
  1342  		ginkgo.It("should delete PVCs after adopting pod (WhenDeleted)", func(ctx context.Context) {
  1343  			e2epv.SkipIfNoDefaultStorageClass(ctx, c)
  1344  			ginkgo.By("Creating statefulset " + ssName + " in namespace " + ns)
  1345  			*(ss.Spec.Replicas) = 3
  1346  			ss.Spec.PersistentVolumeClaimRetentionPolicy = &appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy{
  1347  				WhenDeleted: appsv1.DeletePersistentVolumeClaimRetentionPolicyType,
  1348  			}
  1349  			_, err := c.AppsV1().StatefulSets(ns).Create(ctx, ss, metav1.CreateOptions{})
  1350  			framework.ExpectNoError(err)
  1351  
  1352  			ginkgo.By("Confirming all 3 PVCs exist with their owner refs")
  1353  			err = verifyStatefulSetPVCsExistWithOwnerRefs(ctx, c, ss, []int{0, 1, 2}, true, false)
  1354  			framework.ExpectNoError(err)
  1355  
  1356  			ginkgo.By("Orphaning the 3rd pod")
  1357  			patch, err := json.Marshal(metav1.ObjectMeta{
  1358  				OwnerReferences: []metav1.OwnerReference{},
  1359  			})
  1360  			framework.ExpectNoError(err, "Could not Marshal JSON for patch payload")
  1361  			_, err = c.CoreV1().Pods(ns).Patch(ctx, fmt.Sprintf("%s-2", ss.Name), types.StrategicMergePatchType, []byte(patch), metav1.PatchOptions{}, "")
  1362  			framework.ExpectNoError(err, "Could not patch payload")
  1363  
  1364  			ginkgo.By("Deleting stateful set " + ss.Name)
  1365  			err = c.AppsV1().StatefulSets(ns).Delete(ctx, ss.Name, metav1.DeleteOptions{})
  1366  			framework.ExpectNoError(err)
  1367  
  1368  			ginkgo.By("Verifying PVCs deleted")
  1369  			err = verifyStatefulSetPVCsExist(ctx, c, ss, []int{})
  1370  			framework.ExpectNoError(err)
  1371  		})
  1372  
  1373  		ginkgo.It("should delete PVCs after adopting pod (WhenScaled)", func(ctx context.Context) {
  1374  			e2epv.SkipIfNoDefaultStorageClass(ctx, c)
  1375  			ginkgo.By("Creating statefulset " + ssName + " in namespace " + ns)
  1376  			*(ss.Spec.Replicas) = 3
  1377  			ss.Spec.PersistentVolumeClaimRetentionPolicy = &appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy{
  1378  				WhenScaled: appsv1.DeletePersistentVolumeClaimRetentionPolicyType,
  1379  			}
  1380  			_, err := c.AppsV1().StatefulSets(ns).Create(ctx, ss, metav1.CreateOptions{})
  1381  			framework.ExpectNoError(err)
  1382  
  1383  			ginkgo.By("Confirming all 3 PVCs exist")
  1384  			err = verifyStatefulSetPVCsExist(ctx, c, ss, []int{0, 1, 2})
  1385  			framework.ExpectNoError(err)
  1386  
  1387  			ginkgo.By("Orphaning the 3rd pod")
  1388  			patch, err := json.Marshal(metav1.ObjectMeta{
  1389  				OwnerReferences: []metav1.OwnerReference{},
  1390  			})
  1391  			framework.ExpectNoError(err, "Could not Marshal JSON for patch payload")
  1392  			_, err = c.CoreV1().Pods(ns).Patch(ctx, fmt.Sprintf("%s-2", ss.Name), types.StrategicMergePatchType, []byte(patch), metav1.PatchOptions{}, "")
  1393  			framework.ExpectNoError(err, "Could not patch payload")
  1394  
  1395  			ginkgo.By("Scaling stateful set " + ss.Name + " to one replica")
  1396  			ss, err = e2estatefulset.Scale(ctx, c, ss, 1)
  1397  			framework.ExpectNoError(err)
  1398  
  1399  			ginkgo.By("Verifying all but one PVC deleted")
  1400  			err = verifyStatefulSetPVCsExist(ctx, c, ss, []int{0})
  1401  			framework.ExpectNoError(err)
  1402  		})
  1403  	})
  1404  
  1405  	ginkgo.Describe("Automatically recreate PVC for pending pod when PVC is missing", func() {
  1406  		ssName := "ss"
  1407  		labels := map[string]string{
  1408  			"foo": "bar",
  1409  			"baz": "blah",
  1410  		}
  1411  		headlessSvcName := "test"
  1412  		var statefulPodMounts []v1.VolumeMount
  1413  		var ss *appsv1.StatefulSet
  1414  
  1415  		ginkgo.BeforeEach(func(ctx context.Context) {
  1416  			statefulPodMounts = []v1.VolumeMount{{Name: "datadir", MountPath: "/data/"}}
  1417  			ss = e2estatefulset.NewStatefulSet(ssName, ns, headlessSvcName, 1, statefulPodMounts, nil, labels)
  1418  		})
  1419  
  1420  		ginkgo.AfterEach(func(ctx context.Context) {
  1421  			if ginkgo.CurrentSpecReport().Failed() {
  1422  				e2eoutput.DumpDebugInfo(ctx, c, ns)
  1423  			}
  1424  			framework.Logf("Deleting all statefulset in ns %v", ns)
  1425  			e2estatefulset.DeleteAllStatefulSets(ctx, c, ns)
  1426  		})
  1427  
  1428  		f.It("PVC should be recreated when pod is pending due to missing PVC", f.WithDisruptive(), f.WithSerial(), func(ctx context.Context) {
  1429  			e2epv.SkipIfNoDefaultStorageClass(ctx, c)
  1430  
  1431  			readyNode, err := e2enode.GetRandomReadySchedulableNode(ctx, c)
  1432  			framework.ExpectNoError(err)
  1433  			hostLabel := "kubernetes.io/hostname"
  1434  			hostLabelVal := readyNode.Labels[hostLabel]
  1435  
  1436  			ss.Spec.Template.Spec.NodeSelector = map[string]string{hostLabel: hostLabelVal} // force the pod on a specific node
  1437  			ginkgo.By("Creating statefulset " + ssName + " in namespace " + ns)
  1438  			_, err = c.AppsV1().StatefulSets(ns).Create(ctx, ss, metav1.CreateOptions{})
  1439  			framework.ExpectNoError(err)
  1440  
  1441  			ginkgo.By("Confirming PVC exists")
  1442  			err = verifyStatefulSetPVCsExist(ctx, c, ss, []int{0})
  1443  			framework.ExpectNoError(err)
  1444  
  1445  			ginkgo.By("Confirming Pod is ready")
  1446  			e2estatefulset.WaitForStatusReadyReplicas(ctx, c, ss, 1)
  1447  			podName := getStatefulSetPodNameAtIndex(0, ss)
  1448  			pod, err := c.CoreV1().Pods(ns).Get(ctx, podName, metav1.GetOptions{})
  1449  			framework.ExpectNoError(err)
  1450  
  1451  			nodeName := pod.Spec.NodeName
  1452  			gomega.Expect(nodeName).To(gomega.Equal(readyNode.Name))
  1453  			node, err := c.CoreV1().Nodes().Get(ctx, nodeName, metav1.GetOptions{})
  1454  			framework.ExpectNoError(err)
  1455  
  1456  			oldData, err := json.Marshal(node)
  1457  			framework.ExpectNoError(err)
  1458  
  1459  			node.Spec.Unschedulable = true
  1460  
  1461  			newData, err := json.Marshal(node)
  1462  			framework.ExpectNoError(err)
  1463  
  1464  			// cordon node, to make sure pod does not get scheduled to the node until the pvc is deleted
  1465  			patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, v1.Node{})
  1466  			framework.ExpectNoError(err)
  1467  			ginkgo.By("Cordoning Node")
  1468  			_, err = c.CoreV1().Nodes().Patch(ctx, nodeName, types.StrategicMergePatchType, patchBytes, metav1.PatchOptions{})
  1469  			framework.ExpectNoError(err)
  1470  			cordoned := true
  1471  
  1472  			defer func() {
  1473  				if cordoned {
  1474  					uncordonNode(ctx, c, oldData, newData, nodeName)
  1475  				}
  1476  			}()
  1477  
  1478  			// wait for the node to be unschedulable
  1479  			e2enode.WaitForNodeSchedulable(ctx, c, nodeName, 10*time.Second, false)
  1480  
  1481  			ginkgo.By("Deleting Pod")
  1482  			err = c.CoreV1().Pods(ns).Delete(ctx, podName, metav1.DeleteOptions{})
  1483  			framework.ExpectNoError(err)
  1484  
  1485  			// wait for the pod to be recreated
  1486  			waitForStatusCurrentReplicas(ctx, c, ss, 1)
  1487  			_, err = c.CoreV1().Pods(ns).Get(ctx, podName, metav1.GetOptions{})
  1488  			framework.ExpectNoError(err)
  1489  
  1490  			pvcList, err := c.CoreV1().PersistentVolumeClaims(ns).List(ctx, metav1.ListOptions{LabelSelector: klabels.Everything().String()})
  1491  			framework.ExpectNoError(err)
  1492  			gomega.Expect(pvcList.Items).To(gomega.HaveLen(1))
  1493  			pvcName := pvcList.Items[0].Name
  1494  
  1495  			ginkgo.By("Deleting PVC")
  1496  			err = c.CoreV1().PersistentVolumeClaims(ns).Delete(ctx, pvcName, metav1.DeleteOptions{})
  1497  			framework.ExpectNoError(err)
  1498  
  1499  			uncordonNode(ctx, c, oldData, newData, nodeName)
  1500  			cordoned = false
  1501  
  1502  			ginkgo.By("Confirming PVC recreated")
  1503  			err = verifyStatefulSetPVCsExist(ctx, c, ss, []int{0})
  1504  			framework.ExpectNoError(err)
  1505  
  1506  			ginkgo.By("Confirming Pod is ready after being recreated")
  1507  			e2estatefulset.WaitForStatusReadyReplicas(ctx, c, ss, 1)
  1508  			pod, err = c.CoreV1().Pods(ns).Get(ctx, podName, metav1.GetOptions{})
  1509  			framework.ExpectNoError(err)
  1510  			gomega.Expect(pod.Spec.NodeName).To(gomega.Equal(readyNode.Name)) // confirm the pod was scheduled back to the original node
  1511  		})
  1512  	})
  1513  
  1514  	ginkgo.Describe("Scaling StatefulSetStartOrdinal", func() {
  1515  		ssName := "ss"
  1516  		labels := map[string]string{
  1517  			"foo": "bar",
  1518  			"baz": "blah",
  1519  		}
  1520  		headlessSvcName := "test"
  1521  		var ss *appsv1.StatefulSet
  1522  
  1523  		ginkgo.BeforeEach(func(ctx context.Context) {
  1524  			ss = e2estatefulset.NewStatefulSet(ssName, ns, headlessSvcName, 2, nil, nil, labels)
  1525  
  1526  			ginkgo.By("Creating service " + headlessSvcName + " in namespace " + ns)
  1527  			headlessService := e2eservice.CreateServiceSpec(headlessSvcName, "", true, labels)
  1528  			_, err := c.CoreV1().Services(ns).Create(ctx, headlessService, metav1.CreateOptions{})
  1529  			framework.ExpectNoError(err)
  1530  		})
  1531  
  1532  		ginkgo.AfterEach(func(ctx context.Context) {
  1533  			if ginkgo.CurrentSpecReport().Failed() {
  1534  				e2eoutput.DumpDebugInfo(ctx, c, ns)
  1535  			}
  1536  			framework.Logf("Deleting all statefulset in ns %v", ns)
  1537  			e2estatefulset.DeleteAllStatefulSets(ctx, c, ns)
  1538  		})
  1539  
  1540  		ginkgo.It("Setting .start.ordinal", func(ctx context.Context) {
  1541  			ginkgo.By("Creating statefulset " + ssName + " in namespace " + ns)
  1542  			*(ss.Spec.Replicas) = 2
  1543  			_, err := c.AppsV1().StatefulSets(ns).Create(ctx, ss, metav1.CreateOptions{})
  1544  			framework.ExpectNoError(err)
  1545  			waitForStatus(ctx, c, ss)
  1546  			e2estatefulset.WaitForStatusReplicas(ctx, c, ss, 2)
  1547  			e2estatefulset.WaitForStatusReadyReplicas(ctx, c, ss, 2)
  1548  
  1549  			ginkgo.By("Confirming 2 replicas, with start ordinal 0")
  1550  			pods := e2estatefulset.GetPodList(ctx, c, ss)
  1551  			err = expectPodNames(pods, []string{"ss-0", "ss-1"})
  1552  			framework.ExpectNoError(err)
  1553  
  1554  			ginkgo.By("Setting .spec.replicas = 3 .spec.ordinals.start = 2")
  1555  			ss, err = updateStatefulSetWithRetries(ctx, c, ns, ss.Name, func(update *appsv1.StatefulSet) {
  1556  				update.Spec.Ordinals = &appsv1.StatefulSetOrdinals{
  1557  					Start: 2,
  1558  				}
  1559  				*(update.Spec.Replicas) = 3
  1560  			})
  1561  			framework.ExpectNoError(err)
  1562  
  1563  			// we need to ensure we wait for all the new ones to show up, not
  1564  			// just for any random 3
  1565  			waitForStatus(ctx, c, ss)
  1566  			waitForPodNames(ctx, c, ss, []string{"ss-2", "ss-3", "ss-4"})
  1567  			ginkgo.By("Confirming 3 replicas, with start ordinal 2")
  1568  			e2estatefulset.WaitForStatusReplicas(ctx, c, ss, 3)
  1569  			e2estatefulset.WaitForStatusReadyReplicas(ctx, c, ss, 3)
  1570  		})
  1571  
  1572  		ginkgo.It("Increasing .start.ordinal", func(ctx context.Context) {
  1573  			ginkgo.By("Creating statefulset " + ssName + " in namespace " + ns)
  1574  			*(ss.Spec.Replicas) = 2
  1575  			ss.Spec.Ordinals = &appsv1.StatefulSetOrdinals{
  1576  				Start: 2,
  1577  			}
  1578  			_, err := c.AppsV1().StatefulSets(ns).Create(ctx, ss, metav1.CreateOptions{})
  1579  			framework.ExpectNoError(err)
  1580  			waitForStatus(ctx, c, ss)
  1581  			e2estatefulset.WaitForStatusReplicas(ctx, c, ss, 2)
  1582  			e2estatefulset.WaitForStatusReadyReplicas(ctx, c, ss, 2)
  1583  
  1584  			ginkgo.By("Confirming 2 replicas, with start ordinal 2")
  1585  			pods := e2estatefulset.GetPodList(ctx, c, ss)
  1586  			err = expectPodNames(pods, []string{"ss-2", "ss-3"})
  1587  			framework.ExpectNoError(err)
  1588  
  1589  			ginkgo.By("Increasing .spec.ordinals.start = 4")
  1590  			ss, err = updateStatefulSetWithRetries(ctx, c, ns, ss.Name, func(update *appsv1.StatefulSet) {
  1591  				update.Spec.Ordinals = &appsv1.StatefulSetOrdinals{
  1592  					Start: 4,
  1593  				}
  1594  			})
  1595  			framework.ExpectNoError(err)
  1596  
  1597  			// since we are replacing 2 pods for 2, we need to ensure we wait
  1598  			// for the new ones to show up, not just for any random 2
  1599  			ginkgo.By("Confirming 2 replicas, with start ordinal 4")
  1600  			waitForStatus(ctx, c, ss)
  1601  			waitForPodNames(ctx, c, ss, []string{"ss-4", "ss-5"})
  1602  			e2estatefulset.WaitForStatusReplicas(ctx, c, ss, 2)
  1603  			e2estatefulset.WaitForStatusReadyReplicas(ctx, c, ss, 2)
  1604  		})
  1605  
  1606  		ginkgo.It("Decreasing .start.ordinal", func(ctx context.Context) {
  1607  			ginkgo.By("Creating statefulset " + ssName + " in namespace " + ns)
  1608  			*(ss.Spec.Replicas) = 2
  1609  			ss.Spec.Ordinals = &appsv1.StatefulSetOrdinals{
  1610  				Start: 3,
  1611  			}
  1612  			_, err := c.AppsV1().StatefulSets(ns).Create(ctx, ss, metav1.CreateOptions{})
  1613  			framework.ExpectNoError(err)
  1614  			waitForStatus(ctx, c, ss)
  1615  			e2estatefulset.WaitForStatusReplicas(ctx, c, ss, 2)
  1616  			e2estatefulset.WaitForStatusReadyReplicas(ctx, c, ss, 2)
  1617  
  1618  			ginkgo.By("Confirming 2 replicas, with start ordinal 3")
  1619  			pods := e2estatefulset.GetPodList(ctx, c, ss)
  1620  			err = expectPodNames(pods, []string{"ss-3", "ss-4"})
  1621  			framework.ExpectNoError(err)
  1622  
  1623  			ginkgo.By("Decreasing .spec.ordinals.start = 2")
  1624  			ss, err = updateStatefulSetWithRetries(ctx, c, ns, ss.Name, func(update *appsv1.StatefulSet) {
  1625  				update.Spec.Ordinals = &appsv1.StatefulSetOrdinals{
  1626  					Start: 2,
  1627  				}
  1628  			})
  1629  			framework.ExpectNoError(err)
  1630  
  1631  			// since we are replacing 2 pods for 2, we need to ensure we wait
  1632  			// for the new ones to show up, not just for any random 2
  1633  			ginkgo.By("Confirming 2 replicas, with start ordinal 2")
  1634  			waitForStatus(ctx, c, ss)
  1635  			waitForPodNames(ctx, c, ss, []string{"ss-2", "ss-3"})
  1636  			e2estatefulset.WaitForStatusReplicas(ctx, c, ss, 2)
  1637  			e2estatefulset.WaitForStatusReadyReplicas(ctx, c, ss, 2)
  1638  		})
  1639  
  1640  		ginkgo.It("Removing .start.ordinal", func(ctx context.Context) {
  1641  			ginkgo.By("Creating statefulset " + ssName + " in namespace " + ns)
  1642  			*(ss.Spec.Replicas) = 2
  1643  			ss.Spec.Ordinals = &appsv1.StatefulSetOrdinals{
  1644  				Start: 3,
  1645  			}
  1646  			_, err := c.AppsV1().StatefulSets(ns).Create(ctx, ss, metav1.CreateOptions{})
  1647  			framework.ExpectNoError(err)
  1648  			e2estatefulset.WaitForStatusReplicas(ctx, c, ss, 2)
  1649  			e2estatefulset.WaitForStatusReadyReplicas(ctx, c, ss, 2)
  1650  
  1651  			ginkgo.By("Confirming 2 replicas, with start ordinal 3")
  1652  			pods := e2estatefulset.GetPodList(ctx, c, ss)
  1653  			err = expectPodNames(pods, []string{"ss-3", "ss-4"})
  1654  			framework.ExpectNoError(err)
  1655  
  1656  			ginkgo.By("Removing .spec.ordinals")
  1657  			ss, err = updateStatefulSetWithRetries(ctx, c, ns, ss.Name, func(update *appsv1.StatefulSet) {
  1658  				update.Spec.Ordinals = nil
  1659  			})
  1660  			framework.ExpectNoError(err)
  1661  
  1662  			// since we are replacing 2 pods for 2, we need to ensure we wait
  1663  			// for the new ones to show up, not just for any random 2
  1664  			framework.Logf("Confirming 2 replicas, with start ordinal 0")
  1665  			waitForStatus(ctx, c, ss)
  1666  			waitForPodNames(ctx, c, ss, []string{"ss-0", "ss-1"})
  1667  			e2estatefulset.WaitForStatusReplicas(ctx, c, ss, 2)
  1668  			e2estatefulset.WaitForStatusReadyReplicas(ctx, c, ss, 2)
  1669  		})
  1670  	})
  1671  })
  1672  
  1673  func uncordonNode(ctx context.Context, c clientset.Interface, oldData, newData []byte, nodeName string) {
  1674  	ginkgo.By("Uncordoning Node")
  1675  	// uncordon node, by reverting patch
  1676  	revertPatchBytes, err := strategicpatch.CreateTwoWayMergePatch(newData, oldData, v1.Node{})
  1677  	framework.ExpectNoError(err)
  1678  	_, err = c.CoreV1().Nodes().Patch(ctx, nodeName, types.StrategicMergePatchType, revertPatchBytes, metav1.PatchOptions{})
  1679  	framework.ExpectNoError(err)
  1680  }
  1681  
  1682  func kubectlExecWithRetries(ns string, args ...string) (out string) {
  1683  	var err error
  1684  	for i := 0; i < 3; i++ {
  1685  		if out, err = e2ekubectl.RunKubectl(ns, args...); err == nil {
  1686  			return
  1687  		}
  1688  		framework.Logf("Retrying %v:\nerror %v\nstdout %v", args, err, out)
  1689  	}
  1690  	framework.Failf("Failed to execute \"%v\" with retries: %v", args, err)
  1691  	return
  1692  }
  1693  
  1694  type statefulPodTester interface {
  1695  	deploy(ctx context.Context, ns string) *appsv1.StatefulSet
  1696  	write(statefulPodIndex int, kv map[string]string)
  1697  	read(statefulPodIndex int, key string) string
  1698  	name() string
  1699  }
  1700  
  1701  type clusterAppTester struct {
  1702  	ns          string
  1703  	statefulPod statefulPodTester
  1704  	client      clientset.Interface
  1705  }
  1706  
  1707  func (c *clusterAppTester) run(ctx context.Context) {
  1708  	ginkgo.By("Deploying " + c.statefulPod.name())
  1709  	ss := c.statefulPod.deploy(ctx, c.ns)
  1710  
  1711  	ginkgo.By("Creating foo:bar in member with index 0")
  1712  	c.statefulPod.write(0, map[string]string{"foo": "bar"})
  1713  
  1714  	switch c.statefulPod.(type) {
  1715  	case *mysqlGaleraTester:
  1716  		// Don't restart MySQL cluster since it doesn't handle restarts well
  1717  	default:
  1718  		if restartCluster {
  1719  			ginkgo.By("Restarting stateful set " + ss.Name)
  1720  			e2estatefulset.Restart(ctx, c.client, ss)
  1721  			e2estatefulset.WaitForRunningAndReady(ctx, c.client, *ss.Spec.Replicas, ss)
  1722  		}
  1723  	}
  1724  
  1725  	ginkgo.By("Reading value under foo from member with index 2")
  1726  	if err := pollReadWithTimeout(ctx, c.statefulPod, 2, "foo", "bar"); err != nil {
  1727  		framework.Failf("%v", err)
  1728  	}
  1729  }
  1730  
  1731  type zookeeperTester struct {
  1732  	ss     *appsv1.StatefulSet
  1733  	client clientset.Interface
  1734  }
  1735  
  1736  func (z *zookeeperTester) name() string {
  1737  	return "zookeeper"
  1738  }
  1739  
  1740  func (z *zookeeperTester) deploy(ctx context.Context, ns string) *appsv1.StatefulSet {
  1741  	z.ss = e2estatefulset.CreateStatefulSet(ctx, z.client, zookeeperManifestPath, ns)
  1742  	return z.ss
  1743  }
  1744  
  1745  func (z *zookeeperTester) write(statefulPodIndex int, kv map[string]string) {
  1746  	name := fmt.Sprintf("%v-%d", z.ss.Name, statefulPodIndex)
  1747  	for k, v := range kv {
  1748  		cmd := fmt.Sprintf("/opt/zookeeper/bin/zkCli.sh create /%v %v", k, v)
  1749  		framework.Logf(e2ekubectl.RunKubectlOrDie(z.ss.Namespace, "exec", name, "--", "/bin/sh", "-c", cmd))
  1750  	}
  1751  }
  1752  
  1753  func (z *zookeeperTester) read(statefulPodIndex int, key string) string {
  1754  	name := fmt.Sprintf("%v-%d", z.ss.Name, statefulPodIndex)
  1755  	cmd := fmt.Sprintf("/opt/zookeeper/bin/zkCli.sh get /%v", key)
  1756  	return lastLine(e2ekubectl.RunKubectlOrDie(z.ss.Namespace, "exec", name, "--", "/bin/sh", "-c", cmd))
  1757  }
  1758  
  1759  type mysqlGaleraTester struct {
  1760  	ss     *appsv1.StatefulSet
  1761  	client clientset.Interface
  1762  }
  1763  
  1764  func (m *mysqlGaleraTester) name() string {
  1765  	return "mysql: galera"
  1766  }
  1767  
  1768  func (m *mysqlGaleraTester) mysqlExec(cmd, ns, podName string) string {
  1769  	cmd = fmt.Sprintf("/usr/bin/mysql -u root -B -e '%v'", cmd)
  1770  	// TODO: Find a readiness probe for mysql that guarantees writes will
  1771  	// succeed and ditch retries. Current probe only reads, so there's a window
  1772  	// for a race.
  1773  	return kubectlExecWithRetries(ns, "exec", podName, "--", "/bin/sh", "-c", cmd)
  1774  }
  1775  
  1776  func (m *mysqlGaleraTester) deploy(ctx context.Context, ns string) *appsv1.StatefulSet {
  1777  	m.ss = e2estatefulset.CreateStatefulSet(ctx, m.client, mysqlGaleraManifestPath, ns)
  1778  
  1779  	framework.Logf("Deployed statefulset %v, initializing database", m.ss.Name)
  1780  	for _, cmd := range []string{
  1781  		"create database statefulset;",
  1782  		"use statefulset; create table foo (k varchar(20), v varchar(20));",
  1783  	} {
  1784  		framework.Logf(m.mysqlExec(cmd, ns, fmt.Sprintf("%v-0", m.ss.Name)))
  1785  	}
  1786  	return m.ss
  1787  }
  1788  
  1789  func (m *mysqlGaleraTester) write(statefulPodIndex int, kv map[string]string) {
  1790  	name := fmt.Sprintf("%v-%d", m.ss.Name, statefulPodIndex)
  1791  	for k, v := range kv {
  1792  		cmd := fmt.Sprintf("use statefulset; insert into foo (k, v) values (\"%v\", \"%v\");", k, v)
  1793  		framework.Logf(m.mysqlExec(cmd, m.ss.Namespace, name))
  1794  	}
  1795  }
  1796  
  1797  func (m *mysqlGaleraTester) read(statefulPodIndex int, key string) string {
  1798  	name := fmt.Sprintf("%v-%d", m.ss.Name, statefulPodIndex)
  1799  	return lastLine(m.mysqlExec(fmt.Sprintf("use statefulset; select v from foo where k=\"%v\";", key), m.ss.Namespace, name))
  1800  }
  1801  
  1802  type redisTester struct {
  1803  	ss     *appsv1.StatefulSet
  1804  	client clientset.Interface
  1805  }
  1806  
  1807  func (m *redisTester) name() string {
  1808  	return "redis: master/slave"
  1809  }
  1810  
  1811  func (m *redisTester) redisExec(cmd, ns, podName string) string {
  1812  	cmd = fmt.Sprintf("/opt/redis/redis-cli -h %v %v", podName, cmd)
  1813  	return e2ekubectl.RunKubectlOrDie(ns, "exec", podName, "--", "/bin/sh", "-c", cmd)
  1814  }
  1815  
  1816  func (m *redisTester) deploy(ctx context.Context, ns string) *appsv1.StatefulSet {
  1817  	m.ss = e2estatefulset.CreateStatefulSet(ctx, m.client, redisManifestPath, ns)
  1818  	return m.ss
  1819  }
  1820  
  1821  func (m *redisTester) write(statefulPodIndex int, kv map[string]string) {
  1822  	name := fmt.Sprintf("%v-%d", m.ss.Name, statefulPodIndex)
  1823  	for k, v := range kv {
  1824  		framework.Logf(m.redisExec(fmt.Sprintf("SET %v %v", k, v), m.ss.Namespace, name))
  1825  	}
  1826  }
  1827  
  1828  func (m *redisTester) read(statefulPodIndex int, key string) string {
  1829  	name := fmt.Sprintf("%v-%d", m.ss.Name, statefulPodIndex)
  1830  	return lastLine(m.redisExec(fmt.Sprintf("GET %v", key), m.ss.Namespace, name))
  1831  }
  1832  
  1833  type cockroachDBTester struct {
  1834  	ss     *appsv1.StatefulSet
  1835  	client clientset.Interface
  1836  }
  1837  
  1838  func (c *cockroachDBTester) name() string {
  1839  	return "CockroachDB"
  1840  }
  1841  
  1842  func (c *cockroachDBTester) cockroachDBExec(cmd, ns, podName string) string {
  1843  	cmd = fmt.Sprintf("/cockroach/cockroach sql --insecure --host %s.cockroachdb -e \"%v\"", podName, cmd)
  1844  	return e2ekubectl.RunKubectlOrDie(ns, "exec", podName, "--", "/bin/sh", "-c", cmd)
  1845  }
  1846  
  1847  func (c *cockroachDBTester) deploy(ctx context.Context, ns string) *appsv1.StatefulSet {
  1848  	c.ss = e2estatefulset.CreateStatefulSet(ctx, c.client, cockroachDBManifestPath, ns)
  1849  	framework.Logf("Deployed statefulset %v, initializing database", c.ss.Name)
  1850  	for _, cmd := range []string{
  1851  		"CREATE DATABASE IF NOT EXISTS foo;",
  1852  		"CREATE TABLE IF NOT EXISTS foo.bar (k STRING PRIMARY KEY, v STRING);",
  1853  	} {
  1854  		framework.Logf(c.cockroachDBExec(cmd, ns, fmt.Sprintf("%v-0", c.ss.Name)))
  1855  	}
  1856  	return c.ss
  1857  }
  1858  
  1859  func (c *cockroachDBTester) write(statefulPodIndex int, kv map[string]string) {
  1860  	name := fmt.Sprintf("%v-%d", c.ss.Name, statefulPodIndex)
  1861  	for k, v := range kv {
  1862  		cmd := fmt.Sprintf("UPSERT INTO foo.bar VALUES ('%v', '%v');", k, v)
  1863  		framework.Logf(c.cockroachDBExec(cmd, c.ss.Namespace, name))
  1864  	}
  1865  }
  1866  func (c *cockroachDBTester) read(statefulPodIndex int, key string) string {
  1867  	name := fmt.Sprintf("%v-%d", c.ss.Name, statefulPodIndex)
  1868  	return lastLine(c.cockroachDBExec(fmt.Sprintf("SELECT v FROM foo.bar WHERE k='%v';", key), c.ss.Namespace, name))
  1869  }
  1870  
  1871  func lastLine(out string) string {
  1872  	outLines := strings.Split(strings.Trim(out, "\n"), "\n")
  1873  	return outLines[len(outLines)-1]
  1874  }
  1875  
  1876  func pollReadWithTimeout(ctx context.Context, statefulPod statefulPodTester, statefulPodNumber int, key, expectedVal string) error {
  1877  	err := wait.PollUntilContextTimeout(ctx, time.Second, readTimeout, true, func(ctx context.Context) (bool, error) {
  1878  		val := statefulPod.read(statefulPodNumber, key)
  1879  		if val == "" {
  1880  			return false, nil
  1881  		} else if val != expectedVal {
  1882  			return false, fmt.Errorf("expected value %v, found %v", expectedVal, val)
  1883  		}
  1884  		return true, nil
  1885  	})
  1886  
  1887  	if wait.Interrupted(err) {
  1888  		return fmt.Errorf("timed out when trying to read value for key %v from stateful pod %d", key, statefulPodNumber)
  1889  	}
  1890  	return err
  1891  }
  1892  
  1893  // This function is used by two tests to test StatefulSet rollbacks: one using
  1894  // PVCs and one using no storage.
  1895  func rollbackTest(ctx context.Context, c clientset.Interface, ns string, ss *appsv1.StatefulSet) {
  1896  	setHTTPProbe(ss)
  1897  	ss, err := c.AppsV1().StatefulSets(ns).Create(ctx, ss, metav1.CreateOptions{})
  1898  	framework.ExpectNoError(err)
  1899  	e2estatefulset.WaitForRunningAndReady(ctx, c, *ss.Spec.Replicas, ss)
  1900  	ss = waitForStatus(ctx, c, ss)
  1901  	currentRevision, updateRevision := ss.Status.CurrentRevision, ss.Status.UpdateRevision
  1902  	gomega.Expect(currentRevision).To(gomega.Equal(updateRevision), "StatefulSet %s/%s created with update revision %s not equal to current revision %s",
  1903  		ss.Namespace, ss.Name, updateRevision, currentRevision)
  1904  	pods := e2estatefulset.GetPodList(ctx, c, ss)
  1905  	for i := range pods.Items {
  1906  		gomega.Expect(pods.Items[i].Labels).To(gomega.HaveKeyWithValue(appsv1.StatefulSetRevisionLabel, currentRevision), "Pod %s/%s revision %s is not equal to current revision %s",
  1907  			pods.Items[i].Namespace,
  1908  			pods.Items[i].Name,
  1909  			pods.Items[i].Labels[appsv1.StatefulSetRevisionLabel],
  1910  			currentRevision)
  1911  	}
  1912  	e2estatefulset.SortStatefulPods(pods)
  1913  	err = breakPodHTTPProbe(ss, &pods.Items[1])
  1914  	framework.ExpectNoError(err)
  1915  	ss, _ = waitForPodNotReady(ctx, c, ss, pods.Items[1].Name)
  1916  	newImage := NewWebserverImage
  1917  	oldImage := ss.Spec.Template.Spec.Containers[0].Image
  1918  
  1919  	ginkgo.By(fmt.Sprintf("Updating StatefulSet template: update image from %s to %s", oldImage, newImage))
  1920  	gomega.Expect(oldImage).NotTo(gomega.Equal(newImage), "Incorrect test setup: should update to a different image")
  1921  	ss, err = updateStatefulSetWithRetries(ctx, c, ns, ss.Name, func(update *appsv1.StatefulSet) {
  1922  		update.Spec.Template.Spec.Containers[0].Image = newImage
  1923  	})
  1924  	framework.ExpectNoError(err)
  1925  
  1926  	ginkgo.By("Creating a new revision")
  1927  	ss = waitForStatus(ctx, c, ss)
  1928  	currentRevision, updateRevision = ss.Status.CurrentRevision, ss.Status.UpdateRevision
  1929  	gomega.Expect(currentRevision).NotTo(gomega.Equal(updateRevision), "Current revision should not equal update revision during rolling update")
  1930  
  1931  	ginkgo.By("Updating Pods in reverse ordinal order")
  1932  	pods = e2estatefulset.GetPodList(ctx, c, ss)
  1933  	e2estatefulset.SortStatefulPods(pods)
  1934  	err = restorePodHTTPProbe(ss, &pods.Items[1])
  1935  	framework.ExpectNoError(err)
  1936  	ss, _ = e2estatefulset.WaitForPodReady(ctx, c, ss, pods.Items[1].Name)
  1937  	ss, pods = waitForRollingUpdate(ctx, c, ss)
  1938  	gomega.Expect(ss.Status.CurrentRevision).To(gomega.Equal(updateRevision), "StatefulSet %s/%s current revision %s does not equal update revision %s on update completion",
  1939  		ss.Namespace,
  1940  		ss.Name,
  1941  		ss.Status.CurrentRevision,
  1942  		updateRevision)
  1943  	for i := range pods.Items {
  1944  		gomega.Expect(pods.Items[i].Spec.Containers[0].Image).To(gomega.Equal(newImage), "Pod %s/%s has image %s not have new image %s",
  1945  			pods.Items[i].Namespace,
  1946  			pods.Items[i].Name,
  1947  			pods.Items[i].Spec.Containers[0].Image,
  1948  			newImage)
  1949  		gomega.Expect(pods.Items[i].Labels).To(gomega.HaveKeyWithValue(appsv1.StatefulSetRevisionLabel, updateRevision), "Pod %s/%s revision %s is not equal to update revision %s",
  1950  			pods.Items[i].Namespace,
  1951  			pods.Items[i].Name,
  1952  			pods.Items[i].Labels[appsv1.StatefulSetRevisionLabel],
  1953  			updateRevision)
  1954  	}
  1955  
  1956  	ginkgo.By("Rolling back to a previous revision")
  1957  	err = breakPodHTTPProbe(ss, &pods.Items[1])
  1958  	framework.ExpectNoError(err)
  1959  	ss, _ = waitForPodNotReady(ctx, c, ss, pods.Items[1].Name)
  1960  	priorRevision := currentRevision
  1961  	ss, err = updateStatefulSetWithRetries(ctx, c, ns, ss.Name, func(update *appsv1.StatefulSet) {
  1962  		update.Spec.Template.Spec.Containers[0].Image = oldImage
  1963  	})
  1964  	framework.ExpectNoError(err)
  1965  	ss = waitForStatus(ctx, c, ss)
  1966  	currentRevision, updateRevision = ss.Status.CurrentRevision, ss.Status.UpdateRevision
  1967  	gomega.Expect(priorRevision).To(gomega.Equal(updateRevision), "Prior revision should equal update revision during roll back")
  1968  	gomega.Expect(currentRevision).NotTo(gomega.Equal(updateRevision), "Current revision should not equal update revision during roll back")
  1969  
  1970  	ginkgo.By("Rolling back update in reverse ordinal order")
  1971  	pods = e2estatefulset.GetPodList(ctx, c, ss)
  1972  	e2estatefulset.SortStatefulPods(pods)
  1973  	restorePodHTTPProbe(ss, &pods.Items[1])
  1974  	ss, _ = e2estatefulset.WaitForPodReady(ctx, c, ss, pods.Items[1].Name)
  1975  	ss, pods = waitForRollingUpdate(ctx, c, ss)
  1976  	gomega.Expect(ss.Status.CurrentRevision).To(gomega.Equal(priorRevision), "StatefulSet %s/%s current revision %s does not equal prior revision %s on rollback completion",
  1977  		ss.Namespace,
  1978  		ss.Name,
  1979  		ss.Status.CurrentRevision,
  1980  		updateRevision)
  1981  
  1982  	for i := range pods.Items {
  1983  		gomega.Expect(pods.Items[i].Spec.Containers[0].Image).To(gomega.Equal(oldImage), "Pod %s/%s has image %s not equal to previous image %s",
  1984  			pods.Items[i].Namespace,
  1985  			pods.Items[i].Name,
  1986  			pods.Items[i].Spec.Containers[0].Image,
  1987  			oldImage)
  1988  		gomega.Expect(pods.Items[i].Labels).To(gomega.HaveKeyWithValue(appsv1.StatefulSetRevisionLabel, priorRevision), "Pod %s/%s revision %s is not equal to prior revision %s",
  1989  			pods.Items[i].Namespace,
  1990  			pods.Items[i].Name,
  1991  			pods.Items[i].Labels[appsv1.StatefulSetRevisionLabel],
  1992  			priorRevision)
  1993  	}
  1994  }
  1995  
  1996  // This function is used canary updates and phased rolling updates of template modifications for partiton1 and delete pod-0
  1997  func deletingPodForRollingUpdatePartitionTest(ctx context.Context, f *framework.Framework, c clientset.Interface, ns string, ss *appsv1.StatefulSet) {
  1998  	setHTTPProbe(ss)
  1999  	ss.Spec.UpdateStrategy = appsv1.StatefulSetUpdateStrategy{
  2000  		Type: appsv1.RollingUpdateStatefulSetStrategyType,
  2001  		RollingUpdate: func() *appsv1.RollingUpdateStatefulSetStrategy {
  2002  			return &appsv1.RollingUpdateStatefulSetStrategy{
  2003  				Partition: pointer.Int32(1),
  2004  			}
  2005  		}(),
  2006  	}
  2007  	ss, err := c.AppsV1().StatefulSets(ns).Create(ctx, ss, metav1.CreateOptions{})
  2008  	framework.ExpectNoError(err)
  2009  	e2estatefulset.WaitForRunningAndReady(ctx, c, *ss.Spec.Replicas, ss)
  2010  	ss = waitForStatus(ctx, c, ss)
  2011  	currentRevision, updateRevision := ss.Status.CurrentRevision, ss.Status.UpdateRevision
  2012  	gomega.Expect(currentRevision).To(gomega.Equal(updateRevision), fmt.Sprintf("StatefulSet %s/%s created with update revision %s not equal to current revision %s",
  2013  		ss.Namespace, ss.Name, updateRevision, currentRevision))
  2014  	pods := e2estatefulset.GetPodList(ctx, c, ss)
  2015  	for i := range pods.Items {
  2016  		gomega.Expect(pods.Items[i].Labels[appsv1.StatefulSetRevisionLabel]).To(gomega.Equal(currentRevision), fmt.Sprintf("Pod %s/%s revision %s is not equal to currentRevision %s",
  2017  			pods.Items[i].Namespace,
  2018  			pods.Items[i].Name,
  2019  			pods.Items[i].Labels[appsv1.StatefulSetRevisionLabel],
  2020  			currentRevision))
  2021  	}
  2022  
  2023  	ginkgo.By("Adding finalizer for pod-0")
  2024  	pod0name := getStatefulSetPodNameAtIndex(0, ss)
  2025  	pod0, err := c.CoreV1().Pods(ns).Get(ctx, pod0name, metav1.GetOptions{})
  2026  	framework.ExpectNoError(err)
  2027  	pod0.Finalizers = append(pod0.Finalizers, testFinalizer)
  2028  	pod0, err = c.CoreV1().Pods(ss.Namespace).Update(ctx, pod0, metav1.UpdateOptions{})
  2029  	framework.ExpectNoError(err)
  2030  	pods.Items[0] = *pod0
  2031  	defer e2epod.NewPodClient(f).RemoveFinalizer(ctx, pod0.Name, testFinalizer)
  2032  
  2033  	ginkgo.By("Updating image on StatefulSet")
  2034  	newImage := NewWebserverImage
  2035  	oldImage := ss.Spec.Template.Spec.Containers[0].Image
  2036  	ginkgo.By(fmt.Sprintf("Updating stateful set template: update image from %s to %s", oldImage, newImage))
  2037  	gomega.Expect(oldImage).ToNot(gomega.Equal(newImage), "Incorrect test setup: should update to a different image")
  2038  	ss, err = updateStatefulSetWithRetries(ctx, c, ns, ss.Name, func(update *appsv1.StatefulSet) {
  2039  		update.Spec.Template.Spec.Containers[0].Image = newImage
  2040  	})
  2041  	framework.ExpectNoError(err)
  2042  
  2043  	ginkgo.By("Creating a new revision")
  2044  	ss = waitForStatus(ctx, c, ss)
  2045  	currentRevision, updateRevision = ss.Status.CurrentRevision, ss.Status.UpdateRevision
  2046  	gomega.Expect(currentRevision).ToNot(gomega.Equal(updateRevision), "Current revision should not equal update revision during rolling update")
  2047  
  2048  	ginkgo.By("Await for all replicas running, all are updated but pod-0")
  2049  	e2estatefulset.WaitForState(ctx, c, ss, func(set2 *appsv1.StatefulSet, pods2 *v1.PodList) (bool, error) {
  2050  		ss = set2
  2051  		pods = pods2
  2052  		if ss.Status.UpdatedReplicas == *ss.Spec.Replicas-1 && ss.Status.Replicas == *ss.Spec.Replicas && ss.Status.ReadyReplicas == *ss.Spec.Replicas {
  2053  			// rolling updated is not completed, because replica 0 isn't ready
  2054  			return true, nil
  2055  		}
  2056  		return false, nil
  2057  	})
  2058  
  2059  	ginkgo.By("Verify pod images before pod-0 deletion and recreation")
  2060  	for i := range pods.Items {
  2061  		if i < int(*ss.Spec.UpdateStrategy.RollingUpdate.Partition) {
  2062  			gomega.Expect(pods.Items[i].Spec.Containers[0].Image).To(gomega.Equal(oldImage), fmt.Sprintf("Pod %s/%s has image %s not equal to oldimage image %s",
  2063  				pods.Items[i].Namespace,
  2064  				pods.Items[i].Name,
  2065  				pods.Items[i].Spec.Containers[0].Image,
  2066  				oldImage))
  2067  			gomega.Expect(pods.Items[i].Labels[appsv1.StatefulSetRevisionLabel]).To(gomega.Equal(currentRevision), fmt.Sprintf("Pod %s/%s has revision %s not equal to current revision %s",
  2068  				pods.Items[i].Namespace,
  2069  				pods.Items[i].Name,
  2070  				pods.Items[i].Labels[appsv1.StatefulSetRevisionLabel],
  2071  				currentRevision))
  2072  		} else {
  2073  			gomega.Expect(pods.Items[i].Spec.Containers[0].Image).To(gomega.Equal(newImage), fmt.Sprintf("Pod %s/%s has image %s not equal to new image  %s",
  2074  				pods.Items[i].Namespace,
  2075  				pods.Items[i].Name,
  2076  				pods.Items[i].Spec.Containers[0].Image,
  2077  				newImage))
  2078  			gomega.Expect(pods.Items[i].Labels[appsv1.StatefulSetRevisionLabel]).To(gomega.Equal(updateRevision), fmt.Sprintf("Pod %s/%s has revision %s not equal to new revision %s",
  2079  				pods.Items[i].Namespace,
  2080  				pods.Items[i].Name,
  2081  				pods.Items[i].Labels[appsv1.StatefulSetRevisionLabel],
  2082  				updateRevision))
  2083  		}
  2084  	}
  2085  
  2086  	ginkgo.By("Deleting the pod-0 so that kubelet terminates it and StatefulSet controller recreates it")
  2087  	deleteStatefulPodAtIndex(ctx, c, 0, ss)
  2088  	ginkgo.By("Await for two replicas to be updated, while the pod-0 is not running")
  2089  	e2estatefulset.WaitForState(ctx, c, ss, func(set2 *appsv1.StatefulSet, pods2 *v1.PodList) (bool, error) {
  2090  		ss = set2
  2091  		pods = pods2
  2092  		return ss.Status.ReadyReplicas == *ss.Spec.Replicas-1, nil
  2093  	})
  2094  
  2095  	ginkgo.By(fmt.Sprintf("Removing finalizer from pod-0 (%v/%v) to allow recreation", pod0.Namespace, pod0.Name))
  2096  	e2epod.NewPodClient(f).RemoveFinalizer(ctx, pod0.Name, testFinalizer)
  2097  
  2098  	ginkgo.By("Await for recreation of pod-0, so that all replicas are running")
  2099  	e2estatefulset.WaitForState(ctx, c, ss, func(set2 *appsv1.StatefulSet, pods2 *v1.PodList) (bool, error) {
  2100  		ss = set2
  2101  		pods = pods2
  2102  		return ss.Status.ReadyReplicas == *ss.Spec.Replicas, nil
  2103  	})
  2104  
  2105  	ginkgo.By("Verify pod images after pod-0 deletion and recreation")
  2106  	pods = e2estatefulset.GetPodList(ctx, c, ss)
  2107  	for i := range pods.Items {
  2108  		if i < int(*ss.Spec.UpdateStrategy.RollingUpdate.Partition) {
  2109  			gomega.Expect(pods.Items[i].Spec.Containers[0].Image).To(gomega.Equal(oldImage), fmt.Sprintf("Pod %s/%s has image %s not equal to current image %s",
  2110  				pods.Items[i].Namespace,
  2111  				pods.Items[i].Name,
  2112  				pods.Items[i].Spec.Containers[0].Image,
  2113  				oldImage))
  2114  			gomega.Expect(pods.Items[i].Labels[appsv1.StatefulSetRevisionLabel]).To(gomega.Equal(currentRevision), fmt.Sprintf("Pod %s/%s has revision %s not equal to current revision %s",
  2115  				pods.Items[i].Namespace,
  2116  				pods.Items[i].Name,
  2117  				pods.Items[i].Labels[appsv1.StatefulSetRevisionLabel],
  2118  				currentRevision))
  2119  		} else {
  2120  			gomega.Expect(pods.Items[i].Spec.Containers[0].Image).To(gomega.Equal(newImage), fmt.Sprintf("Pod %s/%s has image %s not equal to new image  %s",
  2121  				pods.Items[i].Namespace,
  2122  				pods.Items[i].Name,
  2123  				pods.Items[i].Spec.Containers[0].Image,
  2124  				newImage))
  2125  			gomega.Expect(pods.Items[i].Labels[appsv1.StatefulSetRevisionLabel]).To(gomega.Equal(updateRevision), fmt.Sprintf("Pod %s/%s has revision %s not equal to new revision %s",
  2126  				pods.Items[i].Namespace,
  2127  				pods.Items[i].Name,
  2128  				pods.Items[i].Labels[appsv1.StatefulSetRevisionLabel],
  2129  				updateRevision))
  2130  		}
  2131  	}
  2132  }
  2133  
  2134  // confirmStatefulPodCount asserts that the current number of Pods in ss is count, waiting up to timeout for ss to
  2135  // scale to count.
  2136  func confirmStatefulPodCount(ctx context.Context, c clientset.Interface, count int, ss *appsv1.StatefulSet, timeout time.Duration, hard bool) {
  2137  	start := time.Now()
  2138  	deadline := start.Add(timeout)
  2139  	for t := time.Now(); t.Before(deadline) && ctx.Err() == nil; t = time.Now() {
  2140  		podList := e2estatefulset.GetPodList(ctx, c, ss)
  2141  		statefulPodCount := len(podList.Items)
  2142  		if statefulPodCount != count {
  2143  			e2epod.LogPodStates(podList.Items)
  2144  			if hard {
  2145  				framework.Failf("StatefulSet %v scaled unexpectedly scaled to %d -> %d replicas", ss.Name, count, len(podList.Items))
  2146  			} else {
  2147  				framework.Logf("StatefulSet %v has not reached scale %d, at %d", ss.Name, count, statefulPodCount)
  2148  			}
  2149  			time.Sleep(1 * time.Second)
  2150  			continue
  2151  		}
  2152  		framework.Logf("Verifying statefulset %v doesn't scale past %d for another %+v", ss.Name, count, deadline.Sub(t))
  2153  		time.Sleep(1 * time.Second)
  2154  	}
  2155  }
  2156  
  2157  // setHTTPProbe sets the pod template's ReadinessProbe for Webserver StatefulSet containers.
  2158  // This probe can then be controlled with BreakHTTPProbe() and RestoreHTTPProbe().
  2159  // Note that this cannot be used together with PauseNewPods().
  2160  func setHTTPProbe(ss *appsv1.StatefulSet) {
  2161  	ss.Spec.Template.Spec.Containers[0].ReadinessProbe = httpProbe
  2162  }
  2163  
  2164  // breakHTTPProbe breaks the readiness probe for Nginx StatefulSet containers in ss.
  2165  func breakHTTPProbe(ctx context.Context, c clientset.Interface, ss *appsv1.StatefulSet) error {
  2166  	path := httpProbe.HTTPGet.Path
  2167  	if path == "" {
  2168  		return fmt.Errorf("path expected to be not empty: %v", path)
  2169  	}
  2170  	// Ignore 'mv' errors to make this idempotent.
  2171  	cmd := fmt.Sprintf("mv -v /usr/local/apache2/htdocs%v /tmp/ || true", path)
  2172  	return e2estatefulset.ExecInStatefulPods(ctx, c, ss, cmd)
  2173  }
  2174  
  2175  // breakPodHTTPProbe breaks the readiness probe for Nginx StatefulSet containers in one pod.
  2176  func breakPodHTTPProbe(ss *appsv1.StatefulSet, pod *v1.Pod) error {
  2177  	path := httpProbe.HTTPGet.Path
  2178  	if path == "" {
  2179  		return fmt.Errorf("path expected to be not empty: %v", path)
  2180  	}
  2181  	// Ignore 'mv' errors to make this idempotent.
  2182  	cmd := fmt.Sprintf("mv -v /usr/local/apache2/htdocs%v /tmp/ || true", path)
  2183  	stdout, err := e2eoutput.RunHostCmdWithRetries(pod.Namespace, pod.Name, cmd, statefulSetPoll, statefulPodTimeout)
  2184  	framework.Logf("stdout of %v on %v: %v", cmd, pod.Name, stdout)
  2185  	return err
  2186  }
  2187  
  2188  // restoreHTTPProbe restores the readiness probe for Nginx StatefulSet containers in ss.
  2189  func restoreHTTPProbe(ctx context.Context, c clientset.Interface, ss *appsv1.StatefulSet) error {
  2190  	path := httpProbe.HTTPGet.Path
  2191  	if path == "" {
  2192  		return fmt.Errorf("path expected to be not empty: %v", path)
  2193  	}
  2194  	// Ignore 'mv' errors to make this idempotent.
  2195  	cmd := fmt.Sprintf("mv -v /tmp%v /usr/local/apache2/htdocs/ || true", path)
  2196  	return e2estatefulset.ExecInStatefulPods(ctx, c, ss, cmd)
  2197  }
  2198  
  2199  // restorePodHTTPProbe restores the readiness probe for Nginx StatefulSet containers in pod.
  2200  func restorePodHTTPProbe(ss *appsv1.StatefulSet, pod *v1.Pod) error {
  2201  	path := httpProbe.HTTPGet.Path
  2202  	if path == "" {
  2203  		return fmt.Errorf("path expected to be not empty: %v", path)
  2204  	}
  2205  	// Ignore 'mv' errors to make this idempotent.
  2206  	cmd := fmt.Sprintf("mv -v /tmp%v /usr/local/apache2/htdocs/ || true", path)
  2207  	stdout, err := e2eoutput.RunHostCmdWithRetries(pod.Namespace, pod.Name, cmd, statefulSetPoll, statefulPodTimeout)
  2208  	framework.Logf("stdout of %v on %v: %v", cmd, pod.Name, stdout)
  2209  	return err
  2210  }
  2211  
  2212  // deleteStatefulPodAtIndex deletes the Pod with ordinal index in ss.
  2213  func deleteStatefulPodAtIndex(ctx context.Context, c clientset.Interface, index int, ss *appsv1.StatefulSet) {
  2214  	name := getStatefulSetPodNameAtIndex(index, ss)
  2215  	noGrace := int64(0)
  2216  	if err := c.CoreV1().Pods(ss.Namespace).Delete(ctx, name, metav1.DeleteOptions{GracePeriodSeconds: &noGrace}); err != nil {
  2217  		framework.Failf("Failed to delete stateful pod %v for StatefulSet %v/%v: %v", name, ss.Namespace, ss.Name, err)
  2218  	}
  2219  }
  2220  
  2221  // getStatefulSetPodNameAtIndex gets formatted pod name given index.
  2222  func getStatefulSetPodNameAtIndex(index int, ss *appsv1.StatefulSet) string {
  2223  	// TODO: we won't use "-index" as the name strategy forever,
  2224  	// pull the name out from an identity mapper.
  2225  	return fmt.Sprintf("%v-%v", ss.Name, index)
  2226  }
  2227  
  2228  type updateStatefulSetFunc func(*appsv1.StatefulSet)
  2229  
  2230  // updateStatefulSetWithRetries updates statefulset template with retries.
  2231  func updateStatefulSetWithRetries(ctx context.Context, c clientset.Interface, namespace, name string, applyUpdate updateStatefulSetFunc) (statefulSet *appsv1.StatefulSet, err error) {
  2232  	statefulSets := c.AppsV1().StatefulSets(namespace)
  2233  	var updateErr error
  2234  	pollErr := wait.PollWithContext(ctx, 10*time.Millisecond, 1*time.Minute, func(ctx context.Context) (bool, error) {
  2235  		if statefulSet, err = statefulSets.Get(ctx, name, metav1.GetOptions{}); err != nil {
  2236  			return false, err
  2237  		}
  2238  		// Apply the update, then attempt to push it to the apiserver.
  2239  		applyUpdate(statefulSet)
  2240  		if statefulSet, err = statefulSets.Update(ctx, statefulSet, metav1.UpdateOptions{}); err == nil {
  2241  			framework.Logf("Updating stateful set %s", name)
  2242  			return true, nil
  2243  		}
  2244  		updateErr = err
  2245  		return false, nil
  2246  	})
  2247  	if wait.Interrupted(pollErr) {
  2248  		pollErr = fmt.Errorf("couldn't apply the provided updated to stateful set %q: %v", name, updateErr)
  2249  	}
  2250  	return statefulSet, pollErr
  2251  }
  2252  
  2253  // getStatefulSet gets the StatefulSet named name in namespace.
  2254  func getStatefulSet(ctx context.Context, c clientset.Interface, namespace, name string) *appsv1.StatefulSet {
  2255  	ss, err := c.AppsV1().StatefulSets(namespace).Get(ctx, name, metav1.GetOptions{})
  2256  	if err != nil {
  2257  		framework.Failf("Failed to get StatefulSet %s/%s: %v", namespace, name, err)
  2258  	}
  2259  	return ss
  2260  }
  2261  
  2262  // verifyStatefulSetPVCsExist confirms that exactly the PVCs for ss with the specified ids exist. This polls until the situation occurs, an error happens, or until timeout (in the latter case an error is also returned). Beware that this cannot tell if a PVC will be deleted at some point in the future, so if used to confirm that no PVCs are deleted, the caller should wait for some event giving the PVCs a reasonable chance to be deleted, before calling this function.
  2263  func verifyStatefulSetPVCsExist(ctx context.Context, c clientset.Interface, ss *appsv1.StatefulSet, claimIds []int) error {
  2264  	idSet := map[int]struct{}{}
  2265  	for _, id := range claimIds {
  2266  		idSet[id] = struct{}{}
  2267  	}
  2268  	return wait.PollImmediate(e2estatefulset.StatefulSetPoll, e2estatefulset.StatefulSetTimeout, func() (bool, error) {
  2269  		pvcList, err := c.CoreV1().PersistentVolumeClaims(ss.Namespace).List(ctx, metav1.ListOptions{LabelSelector: klabels.Everything().String()})
  2270  		if err != nil {
  2271  			framework.Logf("WARNING: Failed to list pvcs for verification, retrying: %v", err)
  2272  			return false, nil
  2273  		}
  2274  		for _, claim := range ss.Spec.VolumeClaimTemplates {
  2275  			pvcNameRE := regexp.MustCompile(fmt.Sprintf("^%s-%s-([0-9]+)$", claim.Name, ss.Name))
  2276  			seenPVCs := map[int]struct{}{}
  2277  			for _, pvc := range pvcList.Items {
  2278  				matches := pvcNameRE.FindStringSubmatch(pvc.Name)
  2279  				if len(matches) != 2 {
  2280  					continue
  2281  				}
  2282  				ordinal, err := strconv.ParseInt(matches[1], 10, 32)
  2283  				if err != nil {
  2284  					framework.Logf("ERROR: bad pvc name %s (%v)", pvc.Name, err)
  2285  					return false, err
  2286  				}
  2287  				if _, found := idSet[int(ordinal)]; !found {
  2288  					return false, nil // Retry until the PVCs are consistent.
  2289  				} else {
  2290  					seenPVCs[int(ordinal)] = struct{}{}
  2291  				}
  2292  			}
  2293  			if len(seenPVCs) != len(idSet) {
  2294  				framework.Logf("Found %d of %d PVCs", len(seenPVCs), len(idSet))
  2295  				return false, nil // Retry until the PVCs are consistent.
  2296  			}
  2297  		}
  2298  		return true, nil
  2299  	})
  2300  }
  2301  
  2302  // verifyStatefulSetPVCsExistWithOwnerRefs works as verifyStatefulSetPVCsExist, but also waits for the ownerRefs to match.
  2303  func verifyStatefulSetPVCsExistWithOwnerRefs(ctx context.Context, c clientset.Interface, ss *appsv1.StatefulSet, claimIndicies []int, wantSetRef, wantPodRef bool) error {
  2304  	indexSet := map[int]struct{}{}
  2305  	for _, id := range claimIndicies {
  2306  		indexSet[id] = struct{}{}
  2307  	}
  2308  	set := getStatefulSet(ctx, c, ss.Namespace, ss.Name)
  2309  	setUID := set.GetUID()
  2310  	if setUID == "" {
  2311  		framework.Failf("Statefulset %s missing UID", ss.Name)
  2312  	}
  2313  	return wait.PollImmediate(e2estatefulset.StatefulSetPoll, e2estatefulset.StatefulSetTimeout, func() (bool, error) {
  2314  		pvcList, err := c.CoreV1().PersistentVolumeClaims(ss.Namespace).List(ctx, metav1.ListOptions{LabelSelector: klabels.Everything().String()})
  2315  		if err != nil {
  2316  			framework.Logf("WARNING: Failed to list pvcs for verification, retrying: %v", err)
  2317  			return false, nil
  2318  		}
  2319  		for _, claim := range ss.Spec.VolumeClaimTemplates {
  2320  			pvcNameRE := regexp.MustCompile(fmt.Sprintf("^%s-%s-([0-9]+)$", claim.Name, ss.Name))
  2321  			seenPVCs := map[int]struct{}{}
  2322  			for _, pvc := range pvcList.Items {
  2323  				matches := pvcNameRE.FindStringSubmatch(pvc.Name)
  2324  				if len(matches) != 2 {
  2325  					continue
  2326  				}
  2327  				ordinal, err := strconv.ParseInt(matches[1], 10, 32)
  2328  				if err != nil {
  2329  					framework.Logf("ERROR: bad pvc name %s (%v)", pvc.Name, err)
  2330  					return false, err
  2331  				}
  2332  				if _, found := indexSet[int(ordinal)]; !found {
  2333  					framework.Logf("Unexpected, retrying")
  2334  					return false, nil // Retry until the PVCs are consistent.
  2335  				}
  2336  				var foundSetRef, foundPodRef bool
  2337  				for _, ref := range pvc.GetOwnerReferences() {
  2338  					if ref.Kind == "StatefulSet" && ref.UID == setUID {
  2339  						foundSetRef = true
  2340  					}
  2341  					if ref.Kind == "Pod" {
  2342  						podName := fmt.Sprintf("%s-%d", ss.Name, ordinal)
  2343  						pod, err := c.CoreV1().Pods(ss.Namespace).Get(ctx, podName, metav1.GetOptions{})
  2344  						if err != nil {
  2345  							framework.Logf("Pod %s not found, retrying (%v)", podName, err)
  2346  							return false, nil
  2347  						}
  2348  						podUID := pod.GetUID()
  2349  						if podUID == "" {
  2350  							framework.Failf("Pod %s is missing UID", pod.Name)
  2351  						}
  2352  						if ref.UID == podUID {
  2353  							foundPodRef = true
  2354  						}
  2355  					}
  2356  				}
  2357  				if foundSetRef == wantSetRef && foundPodRef == wantPodRef {
  2358  					seenPVCs[int(ordinal)] = struct{}{}
  2359  				}
  2360  			}
  2361  			if len(seenPVCs) != len(indexSet) {
  2362  				framework.Logf("Only %d PVCs, retrying", len(seenPVCs))
  2363  				return false, nil // Retry until the PVCs are consistent.
  2364  			}
  2365  		}
  2366  		return true, nil
  2367  	})
  2368  }
  2369  
  2370  // expectPodNames compares the names of the pods from actualPods with expectedPodNames.
  2371  // actualPods can be in any list, since we'll sort by their ordinals and filter
  2372  // active ones. expectedPodNames should be ordered by statefulset ordinals.
  2373  func expectPodNames(actualPods *v1.PodList, expectedPodNames []string) error {
  2374  	e2estatefulset.SortStatefulPods(actualPods)
  2375  	pods := []string{}
  2376  	for _, pod := range actualPods.Items {
  2377  		// ignore terminating pods, similarly to how the controller does it
  2378  		// when calculating status information
  2379  		if e2epod.IsPodActive(&pod) {
  2380  			pods = append(pods, pod.Name)
  2381  		}
  2382  	}
  2383  	if !reflect.DeepEqual(expectedPodNames, pods) {
  2384  		diff := cmp.Diff(expectedPodNames, pods)
  2385  		return fmt.Errorf("pod names don't match, diff (- for expected, + for actual):\n%s", diff)
  2386  	}
  2387  	return nil
  2388  }
  2389  

View as plain text