k8s.io/kubernetes@v1.29.3/test/integration/statefulset/statefulset_test.go (about)

     1  /*
     2  Copyright 2018 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 statefulset
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"testing"
    23  	"time"
    24  
    25  	"github.com/google/go-cmp/cmp"
    26  	"github.com/google/go-cmp/cmp/cmpopts"
    27  	appsv1 "k8s.io/api/apps/v1"
    28  	v1 "k8s.io/api/core/v1"
    29  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    30  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    31  	"k8s.io/apimachinery/pkg/types"
    32  	"k8s.io/apimachinery/pkg/util/json"
    33  	"k8s.io/apimachinery/pkg/util/wait"
    34  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    35  	"k8s.io/client-go/dynamic"
    36  	"k8s.io/client-go/informers"
    37  	clientset "k8s.io/client-go/kubernetes"
    38  	restclient "k8s.io/client-go/rest"
    39  	featuregatetesting "k8s.io/component-base/featuregate/testing"
    40  	"k8s.io/klog/v2/ktesting"
    41  	apiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
    42  	podutil "k8s.io/kubernetes/pkg/api/v1/pod"
    43  	"k8s.io/kubernetes/pkg/controller/statefulset"
    44  	"k8s.io/kubernetes/pkg/controlplane"
    45  	"k8s.io/kubernetes/pkg/features"
    46  	"k8s.io/kubernetes/test/integration/framework"
    47  	"k8s.io/utils/ptr"
    48  )
    49  
    50  const (
    51  	interval = 100 * time.Millisecond
    52  	timeout  = 60 * time.Second
    53  )
    54  
    55  // TestVolumeTemplateNoopUpdate ensures embedded StatefulSet objects with embedded PersistentVolumes can be updated
    56  func TestVolumeTemplateNoopUpdate(t *testing.T) {
    57  	// Start the server with default storage setup
    58  	server := apiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd())
    59  	defer server.TearDownFn()
    60  
    61  	c, err := dynamic.NewForConfig(server.ClientConfig)
    62  	if err != nil {
    63  		t.Fatal(err)
    64  	}
    65  
    66  	// Use an unstructured client to ensure we send exactly the bytes we expect for the embedded PVC template
    67  	sts := &unstructured.Unstructured{}
    68  	err = json.Unmarshal([]byte(`{
    69  		"apiVersion": "apps/v1",
    70  		"kind": "StatefulSet",
    71  		"metadata": {"name": "web"},
    72  		"spec": {
    73  		  "selector": {"matchLabels": {"app": "nginx"}},
    74  		  "serviceName": "nginx",
    75  		  "replicas": 3,
    76  		  "template": {
    77  			"metadata": {"labels": {"app": "nginx"}},
    78  			"spec": {
    79  			  "terminationGracePeriodSeconds": 10,
    80  			  "containers": [{
    81  				  "name": "nginx",
    82  				  "image": "registry.k8s.io/nginx-slim:0.8",
    83  				  "ports": [{"containerPort": 80,"name": "web"}],
    84  				  "volumeMounts": [{"name": "www","mountPath": "/usr/share/nginx/html"}]
    85  			  }]
    86  			}
    87  		  },
    88  		  "volumeClaimTemplates": [{
    89  			  "apiVersion": "v1",
    90  			  "kind": "PersistentVolumeClaim",
    91  			  "metadata": {"name": "www"},
    92  			  "spec": {
    93  				"accessModes": ["ReadWriteOnce"],
    94  				"storageClassName": "my-storage-class",
    95  				"resources": {"requests": {"storage": "1Gi"}}
    96  			  }
    97  			}
    98  		  ]
    99  		}
   100  	  }`), &sts.Object)
   101  	if err != nil {
   102  		t.Fatal(err)
   103  	}
   104  
   105  	stsClient := c.Resource(appsv1.SchemeGroupVersion.WithResource("statefulsets")).Namespace("default")
   106  
   107  	// Create the statefulset
   108  	persistedSTS, err := stsClient.Create(context.TODO(), sts, metav1.CreateOptions{})
   109  	if err != nil {
   110  		t.Fatal(err)
   111  	}
   112  
   113  	// Update with the original spec (all the same defaulting should apply, should be a no-op and pass validation
   114  	originalSpec, ok, err := unstructured.NestedFieldCopy(sts.Object, "spec")
   115  	if err != nil || !ok {
   116  		t.Fatal(err, ok)
   117  	}
   118  	err = unstructured.SetNestedField(persistedSTS.Object, originalSpec, "spec")
   119  	if err != nil {
   120  		t.Fatal(err)
   121  	}
   122  	_, err = stsClient.Update(context.TODO(), persistedSTS, metav1.UpdateOptions{})
   123  	if err != nil {
   124  		t.Fatal(err)
   125  	}
   126  }
   127  
   128  func TestSpecReplicasChange(t *testing.T) {
   129  	_, ctx := ktesting.NewTestContext(t)
   130  	closeFn, rm, informers, c := scSetup(ctx, t)
   131  	defer closeFn()
   132  	ns := framework.CreateNamespaceOrDie(c, "test-spec-replicas-change", t)
   133  	defer framework.DeleteNamespaceOrDie(c, ns, t)
   134  	cancel := runControllerAndInformers(rm, informers)
   135  	defer cancel()
   136  
   137  	createHeadlessService(t, c, newHeadlessService(ns.Name))
   138  	sts := newSTS("sts", ns.Name, 2)
   139  	stss, _ := createSTSsPods(t, c, []*appsv1.StatefulSet{sts}, []*v1.Pod{})
   140  	sts = stss[0]
   141  	waitSTSStable(t, c, sts)
   142  
   143  	// Update .Spec.Replicas and verify .Status.Replicas is changed accordingly
   144  	scaleSTS(t, c, sts, 3)
   145  	scaleSTS(t, c, sts, 0)
   146  	scaleSTS(t, c, sts, 2)
   147  
   148  	// Add a template annotation change to test STS's status does update
   149  	// without .Spec.Replicas change
   150  	stsClient := c.AppsV1().StatefulSets(ns.Name)
   151  	var oldGeneration int64
   152  	newSTS := updateSTS(t, stsClient, sts.Name, func(sts *appsv1.StatefulSet) {
   153  		oldGeneration = sts.Generation
   154  		sts.Spec.Template.Annotations = map[string]string{"test": "annotation"}
   155  	})
   156  	savedGeneration := newSTS.Generation
   157  	if savedGeneration == oldGeneration {
   158  		t.Fatalf("failed to verify .Generation has incremented for sts %s", sts.Name)
   159  	}
   160  
   161  	if err := wait.PollImmediate(pollInterval, pollTimeout, func() (bool, error) {
   162  		newSTS, err := stsClient.Get(context.TODO(), sts.Name, metav1.GetOptions{})
   163  		if err != nil {
   164  			return false, err
   165  		}
   166  		return newSTS.Status.ObservedGeneration >= savedGeneration, nil
   167  	}); err != nil {
   168  		t.Fatalf("failed to verify .Status.ObservedGeneration has incremented for sts %s: %v", sts.Name, err)
   169  	}
   170  }
   171  
   172  func TestDeletingAndTerminatingPods(t *testing.T) {
   173  	_, ctx := ktesting.NewTestContext(t)
   174  	closeFn, rm, informers, c := scSetup(ctx, t)
   175  	defer closeFn()
   176  	ns := framework.CreateNamespaceOrDie(c, "test-deleting-and-failed-pods", t)
   177  	defer framework.DeleteNamespaceOrDie(c, ns, t)
   178  	cancel := runControllerAndInformers(rm, informers)
   179  	defer cancel()
   180  
   181  	podCount := 3
   182  
   183  	labelMap := labelMap()
   184  	sts := newSTS("sts", ns.Name, podCount)
   185  	stss, _ := createSTSsPods(t, c, []*appsv1.StatefulSet{sts}, []*v1.Pod{})
   186  	sts = stss[0]
   187  	waitSTSStable(t, c, sts)
   188  
   189  	// Verify STS creates 3 pods
   190  	podClient := c.CoreV1().Pods(ns.Name)
   191  	pods := getPods(t, podClient, labelMap)
   192  	if len(pods.Items) != podCount {
   193  		t.Fatalf("len(pods) = %d, want %d", len(pods.Items), podCount)
   194  	}
   195  
   196  	// Set first pod as deleting pod
   197  	// Set finalizers for the pod to simulate pending deletion status
   198  	deletingPod := &pods.Items[0]
   199  	updatePod(t, podClient, deletingPod.Name, func(pod *v1.Pod) {
   200  		pod.Finalizers = []string{"fake.example.com/blockDeletion"}
   201  	})
   202  	if err := c.CoreV1().Pods(ns.Name).Delete(context.TODO(), deletingPod.Name, metav1.DeleteOptions{}); err != nil {
   203  		t.Fatalf("error deleting pod %s: %v", deletingPod.Name, err)
   204  	}
   205  
   206  	// Set second pod as failed pod
   207  	failedPod := &pods.Items[1]
   208  	updatePodStatus(t, podClient, failedPod.Name, func(pod *v1.Pod) {
   209  		pod.Status.Phase = v1.PodFailed
   210  	})
   211  
   212  	// Set third pod as succeeded pod
   213  	succeededPod := &pods.Items[2]
   214  	updatePodStatus(t, podClient, succeededPod.Name, func(pod *v1.Pod) {
   215  		pod.Status.Phase = v1.PodSucceeded
   216  	})
   217  
   218  	exists := func(pods []v1.Pod, uid types.UID) bool {
   219  		for _, pod := range pods {
   220  			if pod.UID == uid {
   221  				return true
   222  			}
   223  		}
   224  		return false
   225  	}
   226  
   227  	if err := wait.PollImmediate(pollInterval, pollTimeout, func() (bool, error) {
   228  		// Verify only 3 pods exist: deleting pod and new pod replacing failed pod
   229  		pods = getPods(t, podClient, labelMap)
   230  		if len(pods.Items) != podCount {
   231  			return false, nil
   232  		}
   233  
   234  		// Verify deleting pod still exists
   235  		// Immediately return false with an error if it does not exist
   236  		if !exists(pods.Items, deletingPod.UID) {
   237  			return false, fmt.Errorf("expected deleting pod %s still exists, but it is not found", deletingPod.Name)
   238  		}
   239  		// Verify failed pod does not exist anymore
   240  		if exists(pods.Items, failedPod.UID) {
   241  			return false, nil
   242  		}
   243  		// Verify succeeded pod does not exist anymore
   244  		if exists(pods.Items, succeededPod.UID) {
   245  			return false, nil
   246  		}
   247  		// Verify all pods have non-terminated status
   248  		for _, pod := range pods.Items {
   249  			if pod.Status.Phase == v1.PodFailed || pod.Status.Phase == v1.PodSucceeded {
   250  				return false, nil
   251  			}
   252  		}
   253  		return true, nil
   254  	}); err != nil {
   255  		t.Fatalf("failed to verify failed pod %s has been replaced with a new non-failed pod, and deleting pod %s survives: %v", failedPod.Name, deletingPod.Name, err)
   256  	}
   257  
   258  	// Remove finalizers of deleting pod to simulate successful deletion
   259  	updatePod(t, podClient, deletingPod.Name, func(pod *v1.Pod) {
   260  		pod.Finalizers = []string{}
   261  	})
   262  
   263  	if err := wait.PollImmediate(pollInterval, pollTimeout, func() (bool, error) {
   264  		// Verify only 2 pods exist: new non-deleting pod replacing deleting pod and the non-failed pod
   265  		pods = getPods(t, podClient, labelMap)
   266  		if len(pods.Items) != podCount {
   267  			return false, nil
   268  		}
   269  		// Verify deleting pod does not exist anymore
   270  		return !exists(pods.Items, deletingPod.UID), nil
   271  	}); err != nil {
   272  		t.Fatalf("failed to verify deleting pod %s has been replaced with a new non-deleting pod: %v", deletingPod.Name, err)
   273  	}
   274  }
   275  
   276  func TestStatefulSetAvailable(t *testing.T) {
   277  	tests := []struct {
   278  		name           string
   279  		totalReplicas  int32
   280  		readyReplicas  int32
   281  		activeReplicas int32
   282  	}{
   283  		{
   284  			name:           "only certain replicas would become active",
   285  			totalReplicas:  4,
   286  			readyReplicas:  3,
   287  			activeReplicas: 2,
   288  		},
   289  	}
   290  	for _, test := range tests {
   291  		t.Run(test.name, func(t *testing.T) {
   292  			_, ctx := ktesting.NewTestContext(t)
   293  			closeFn, rm, informers, c := scSetup(ctx, t)
   294  			defer closeFn()
   295  			ns := framework.CreateNamespaceOrDie(c, "test-available-pods", t)
   296  			defer framework.DeleteNamespaceOrDie(c, ns, t)
   297  			cancel := runControllerAndInformers(rm, informers)
   298  			defer cancel()
   299  
   300  			labelMap := labelMap()
   301  			sts := newSTS("sts", ns.Name, 4)
   302  			sts.Spec.MinReadySeconds = int32(3600)
   303  			stss, _ := createSTSsPods(t, c, []*appsv1.StatefulSet{sts}, []*v1.Pod{})
   304  			sts = stss[0]
   305  			waitSTSStable(t, c, sts)
   306  
   307  			// Verify STS creates 4 pods
   308  			podClient := c.CoreV1().Pods(ns.Name)
   309  			pods := getPods(t, podClient, labelMap)
   310  			if len(pods.Items) != 4 {
   311  				t.Fatalf("len(pods) = %d, want 4", len(pods.Items))
   312  			}
   313  
   314  			// Separate 3 pods into their own list
   315  			firstPodList := &v1.PodList{Items: pods.Items[:1]}
   316  			secondPodList := &v1.PodList{Items: pods.Items[1:2]}
   317  			thirdPodList := &v1.PodList{Items: pods.Items[2:]}
   318  			// First pod: Running, but not Ready
   319  			// by setting the Ready condition to false with LastTransitionTime to be now
   320  			setPodsReadyCondition(t, c, firstPodList, v1.ConditionFalse, time.Now())
   321  			// Second pod: Running and Ready, but not Available
   322  			// by setting LastTransitionTime to now
   323  			setPodsReadyCondition(t, c, secondPodList, v1.ConditionTrue, time.Now())
   324  			// Third pod: Running, Ready, and Available
   325  			// by setting LastTransitionTime to more than 3600 seconds ago
   326  			setPodsReadyCondition(t, c, thirdPodList, v1.ConditionTrue, time.Now().Add(-120*time.Minute))
   327  
   328  			stsClient := c.AppsV1().StatefulSets(ns.Name)
   329  			if err := wait.PollImmediate(interval, timeout, func() (bool, error) {
   330  				newSts, err := stsClient.Get(context.TODO(), sts.Name, metav1.GetOptions{})
   331  				if err != nil {
   332  					return false, err
   333  				}
   334  				// Verify 4 pods exist, 3 pods are Ready, and 2 pods are Available
   335  				return newSts.Status.Replicas == test.totalReplicas && newSts.Status.ReadyReplicas == test.readyReplicas && newSts.Status.AvailableReplicas == test.activeReplicas, nil
   336  			}); err != nil {
   337  				t.Fatalf("Failed to verify number of Replicas, ReadyReplicas and AvailableReplicas of rs %s to be as expected: %v", sts.Name, err)
   338  			}
   339  		})
   340  	}
   341  }
   342  
   343  func setPodsReadyCondition(t *testing.T, clientSet clientset.Interface, pods *v1.PodList, conditionStatus v1.ConditionStatus, lastTransitionTime time.Time) {
   344  	replicas := int32(len(pods.Items))
   345  	var readyPods int32
   346  	err := wait.PollImmediate(interval, timeout, func() (bool, error) {
   347  		readyPods = 0
   348  		for i := range pods.Items {
   349  			pod := &pods.Items[i]
   350  			if podutil.IsPodReady(pod) {
   351  				readyPods++
   352  				continue
   353  			}
   354  			pod.Status.Phase = v1.PodRunning
   355  			_, condition := podutil.GetPodCondition(&pod.Status, v1.PodReady)
   356  			if condition != nil {
   357  				condition.Status = conditionStatus
   358  				condition.LastTransitionTime = metav1.Time{Time: lastTransitionTime}
   359  			} else {
   360  				condition = &v1.PodCondition{
   361  					Type:               v1.PodReady,
   362  					Status:             conditionStatus,
   363  					LastTransitionTime: metav1.Time{Time: lastTransitionTime},
   364  				}
   365  				pod.Status.Conditions = append(pod.Status.Conditions, *condition)
   366  			}
   367  			_, err := clientSet.CoreV1().Pods(pod.Namespace).UpdateStatus(context.TODO(), pod, metav1.UpdateOptions{})
   368  			if err != nil {
   369  				// When status fails to be updated, we continue to next pod
   370  				continue
   371  			}
   372  			readyPods++
   373  		}
   374  		return readyPods >= replicas, nil
   375  	})
   376  	if err != nil {
   377  		t.Fatalf("failed to mark all StatefulSet pods to ready: %v", err)
   378  	}
   379  }
   380  
   381  // add for issue: https://github.com/kubernetes/kubernetes/issues/108837
   382  func TestStatefulSetStatusWithPodFail(t *testing.T) {
   383  	_, ctx := ktesting.NewTestContext(t)
   384  	ctx, cancel := context.WithCancel(ctx)
   385  	defer cancel()
   386  
   387  	limitedPodNumber := 2
   388  	c, config, closeFn := framework.StartTestServer(ctx, t, framework.TestServerSetup{
   389  		ModifyServerConfig: func(config *controlplane.Config) {
   390  			config.GenericConfig.AdmissionControl = &fakePodFailAdmission{
   391  				limitedPodNumber: limitedPodNumber,
   392  			}
   393  		},
   394  	})
   395  	defer closeFn()
   396  
   397  	resyncPeriod := 12 * time.Hour
   398  	informers := informers.NewSharedInformerFactory(clientset.NewForConfigOrDie(restclient.AddUserAgent(config, "statefulset-informers")), resyncPeriod)
   399  	ssc := statefulset.NewStatefulSetController(
   400  		ctx,
   401  		informers.Core().V1().Pods(),
   402  		informers.Apps().V1().StatefulSets(),
   403  		informers.Core().V1().PersistentVolumeClaims(),
   404  		informers.Apps().V1().ControllerRevisions(),
   405  		clientset.NewForConfigOrDie(restclient.AddUserAgent(config, "statefulset-controller")),
   406  	)
   407  
   408  	ns := framework.CreateNamespaceOrDie(c, "test-pod-fail", t)
   409  	defer framework.DeleteNamespaceOrDie(c, ns, t)
   410  
   411  	informers.Start(ctx.Done())
   412  	go ssc.Run(ctx, 5)
   413  
   414  	sts := newSTS("sts", ns.Name, 4)
   415  	_, err := c.AppsV1().StatefulSets(sts.Namespace).Create(ctx, sts, metav1.CreateOptions{})
   416  	if err != nil {
   417  		t.Fatalf("Could not create statefulSet %s: %v", sts.Name, err)
   418  	}
   419  
   420  	wantReplicas := limitedPodNumber
   421  	var gotReplicas int32
   422  	if err := wait.PollImmediate(pollInterval, pollTimeout, func() (bool, error) {
   423  		newSTS, err := c.AppsV1().StatefulSets(sts.Namespace).Get(ctx, sts.Name, metav1.GetOptions{})
   424  		if err != nil {
   425  			return false, err
   426  		}
   427  		gotReplicas = newSTS.Status.Replicas
   428  		return gotReplicas == int32(wantReplicas), nil
   429  	}); err != nil {
   430  		t.Fatalf("StatefulSet %s status has %d replicas, want replicas %d: %v", sts.Name, gotReplicas, wantReplicas, err)
   431  	}
   432  }
   433  
   434  func TestAutodeleteOwnerRefs(t *testing.T) {
   435  	tests := []struct {
   436  		name              string
   437  		policy            appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy
   438  		expectPodOwnerRef bool
   439  		expectSetOwnerRef bool
   440  	}{
   441  		{
   442  			name: "always retain",
   443  			policy: appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy{
   444  				WhenDeleted: appsv1.RetainPersistentVolumeClaimRetentionPolicyType,
   445  				WhenScaled:  appsv1.RetainPersistentVolumeClaimRetentionPolicyType,
   446  			},
   447  			expectPodOwnerRef: false,
   448  			expectSetOwnerRef: false,
   449  		},
   450  		{
   451  			name: "delete on scaledown only",
   452  			policy: appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy{
   453  				WhenDeleted: appsv1.RetainPersistentVolumeClaimRetentionPolicyType,
   454  				WhenScaled:  appsv1.DeletePersistentVolumeClaimRetentionPolicyType,
   455  			},
   456  			expectPodOwnerRef: true,
   457  			expectSetOwnerRef: false,
   458  		},
   459  		{
   460  			name: "delete with set only",
   461  			policy: appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy{
   462  				WhenDeleted: appsv1.DeletePersistentVolumeClaimRetentionPolicyType,
   463  				WhenScaled:  appsv1.RetainPersistentVolumeClaimRetentionPolicyType,
   464  			},
   465  			expectPodOwnerRef: false,
   466  			expectSetOwnerRef: true,
   467  		},
   468  		{
   469  			name: "always delete",
   470  			policy: appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy{
   471  				WhenDeleted: appsv1.DeletePersistentVolumeClaimRetentionPolicyType,
   472  				WhenScaled:  appsv1.DeletePersistentVolumeClaimRetentionPolicyType,
   473  			},
   474  			expectPodOwnerRef: true,
   475  			expectSetOwnerRef: true,
   476  		},
   477  	}
   478  	for _, test := range tests {
   479  		t.Run(test.name, func(t *testing.T) {
   480  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StatefulSetAutoDeletePVC, true)()
   481  			_, ctx := ktesting.NewTestContext(t)
   482  			closeFn, rm, informers, c := scSetup(ctx, t)
   483  			defer closeFn()
   484  			ns := framework.CreateNamespaceOrDie(c, "test-autodelete-ownerrefs", t)
   485  			defer framework.DeleteNamespaceOrDie(c, ns, t)
   486  			cancel := runControllerAndInformers(rm, informers)
   487  			defer cancel()
   488  
   489  			sts := newSTS("sts", ns.Name, 3)
   490  			sts.Spec.PersistentVolumeClaimRetentionPolicy = &test.policy
   491  			stss, _ := createSTSsPods(t, c, []*appsv1.StatefulSet{sts}, []*v1.Pod{})
   492  			sts = stss[0]
   493  			waitSTSStable(t, c, sts)
   494  
   495  			// Verify StatefulSet ownerref has been added as appropriate.
   496  			pvcClient := c.CoreV1().PersistentVolumeClaims(ns.Name)
   497  			pvcs := getStatefulSetPVCs(t, pvcClient, sts)
   498  			for _, pvc := range pvcs {
   499  				verifyOwnerRef(t, pvc, "StatefulSet", test.expectSetOwnerRef)
   500  				verifyOwnerRef(t, pvc, "Pod", false)
   501  			}
   502  
   503  			// Scale down to 1 pod and verify Pod ownerrefs as appropriate.
   504  			one := int32(1)
   505  			sts.Spec.Replicas = &one
   506  			waitSTSStable(t, c, sts)
   507  
   508  			pvcs = getStatefulSetPVCs(t, pvcClient, sts)
   509  			for i, pvc := range pvcs {
   510  				verifyOwnerRef(t, pvc, "StatefulSet", test.expectSetOwnerRef)
   511  				if i == 0 {
   512  					verifyOwnerRef(t, pvc, "Pod", false)
   513  				} else {
   514  					verifyOwnerRef(t, pvc, "Pod", test.expectPodOwnerRef)
   515  				}
   516  			}
   517  		})
   518  	}
   519  }
   520  
   521  func TestDeletingPodForRollingUpdatePartition(t *testing.T) {
   522  	_, ctx := ktesting.NewTestContext(t)
   523  	closeFn, rm, informers, c := scSetup(ctx, t)
   524  	defer closeFn()
   525  	ns := framework.CreateNamespaceOrDie(c, "test-deleting-pod-for-rolling-update-partition", t)
   526  	defer framework.DeleteNamespaceOrDie(c, ns, t)
   527  	cancel := runControllerAndInformers(rm, informers)
   528  	defer cancel()
   529  
   530  	labelMap := labelMap()
   531  	sts := newSTS("sts", ns.Name, 2)
   532  	sts.Spec.UpdateStrategy = appsv1.StatefulSetUpdateStrategy{
   533  		Type: appsv1.RollingUpdateStatefulSetStrategyType,
   534  		RollingUpdate: func() *appsv1.RollingUpdateStatefulSetStrategy {
   535  			return &appsv1.RollingUpdateStatefulSetStrategy{
   536  				Partition: ptr.To[int32](1),
   537  			}
   538  		}(),
   539  	}
   540  	stss, _ := createSTSsPods(t, c, []*appsv1.StatefulSet{sts}, []*v1.Pod{})
   541  	sts = stss[0]
   542  	waitSTSStable(t, c, sts)
   543  
   544  	// Verify STS creates 2 pods
   545  	podClient := c.CoreV1().Pods(ns.Name)
   546  	pods := getPods(t, podClient, labelMap)
   547  	if len(pods.Items) != 2 {
   548  		t.Fatalf("len(pods) = %d, want 2", len(pods.Items))
   549  	}
   550  	// Setting all pods in Running, Ready, and Available
   551  	setPodsReadyCondition(t, c, &v1.PodList{Items: pods.Items}, v1.ConditionTrue, time.Now())
   552  
   553  	// 1. Roll out a new image.
   554  	oldImage := sts.Spec.Template.Spec.Containers[0].Image
   555  	newImage := "new-image"
   556  	if oldImage == newImage {
   557  		t.Fatalf("bad test setup, statefulSet %s roll out with the same image", sts.Name)
   558  	}
   559  	// Set finalizers for the pod-0 to trigger pod recreation failure while the status UpdateRevision is bumped
   560  	pod0 := &pods.Items[0]
   561  	updatePod(t, podClient, pod0.Name, func(pod *v1.Pod) {
   562  		pod.Finalizers = []string{"fake.example.com/blockDeletion"}
   563  	})
   564  
   565  	stsClient := c.AppsV1().StatefulSets(ns.Name)
   566  	_ = updateSTS(t, stsClient, sts.Name, func(sts *appsv1.StatefulSet) {
   567  		sts.Spec.Template.Spec.Containers[0].Image = newImage
   568  	})
   569  
   570  	// Await for the pod-1 to be recreated, while pod-0 remains running
   571  	if err := wait.PollUntilContextTimeout(ctx, pollInterval, pollTimeout, false, func(ctx context.Context) (bool, error) {
   572  		ss, err := stsClient.Get(ctx, sts.Name, metav1.GetOptions{})
   573  		if err != nil {
   574  			return false, err
   575  		}
   576  		pods := getPods(t, podClient, labelMap)
   577  		recreatedPods := v1.PodList{}
   578  		for _, pod := range pods.Items {
   579  			if pod.Status.Phase == v1.PodPending {
   580  				recreatedPods.Items = append(recreatedPods.Items, pod)
   581  			}
   582  		}
   583  		setPodsReadyCondition(t, c, &v1.PodList{Items: recreatedPods.Items}, v1.ConditionTrue, time.Now())
   584  		return ss.Status.UpdatedReplicas == *ss.Spec.Replicas-*sts.Spec.UpdateStrategy.RollingUpdate.Partition && ss.Status.Replicas == *ss.Spec.Replicas && ss.Status.ReadyReplicas == *ss.Spec.Replicas, nil
   585  	}); err != nil {
   586  		t.Fatalf("failed to await for pod-1 to be recreated by sts %s: %v", sts.Name, err)
   587  	}
   588  
   589  	// Mark pod-0 as terminal and not ready
   590  	updatePodStatus(t, podClient, pod0.Name, func(pod *v1.Pod) {
   591  		pod.Status.Phase = v1.PodFailed
   592  	})
   593  
   594  	// Make sure pod-0 gets deletion timestamp so that it is recreated
   595  	if err := c.CoreV1().Pods(ns.Name).Delete(context.TODO(), pod0.Name, metav1.DeleteOptions{}); err != nil {
   596  		t.Fatalf("error deleting pod %s: %v", pod0.Name, err)
   597  	}
   598  
   599  	// Await for pod-0 to be not ready
   600  	if err := wait.PollUntilContextTimeout(ctx, pollInterval, pollTimeout, false, func(ctx context.Context) (bool, error) {
   601  		ss, err := stsClient.Get(ctx, sts.Name, metav1.GetOptions{})
   602  		if err != nil {
   603  			return false, err
   604  		}
   605  		return ss.Status.ReadyReplicas == *ss.Spec.Replicas-1, nil
   606  	}); err != nil {
   607  		t.Fatalf("failed to await for pod-0 to be not counted as ready in status of sts %s: %v", sts.Name, err)
   608  	}
   609  
   610  	// Remove the finalizer to allow recreation
   611  	updatePod(t, podClient, pod0.Name, func(pod *v1.Pod) {
   612  		pod.Finalizers = []string{}
   613  	})
   614  
   615  	// Await for pod-0 to be recreated and make it running
   616  	if err := wait.PollUntilContextTimeout(ctx, pollInterval, pollTimeout, false, func(ctx context.Context) (bool, error) {
   617  		pods := getPods(t, podClient, labelMap)
   618  		recreatedPods := v1.PodList{}
   619  		for _, pod := range pods.Items {
   620  			if pod.Status.Phase == v1.PodPending {
   621  				recreatedPods.Items = append(recreatedPods.Items, pod)
   622  			}
   623  		}
   624  		setPodsReadyCondition(t, c, &v1.PodList{Items: recreatedPods.Items}, v1.ConditionTrue, time.Now().Add(-120*time.Minute))
   625  		return len(recreatedPods.Items) > 0, nil
   626  	}); err != nil {
   627  		t.Fatalf("failed to await for pod-0 to be recreated by sts %s: %v", sts.Name, err)
   628  	}
   629  
   630  	// Await for all stateful set status to record all replicas as ready
   631  	if err := wait.PollUntilContextTimeout(ctx, pollInterval, pollTimeout, false, func(ctx context.Context) (bool, error) {
   632  		ss, err := stsClient.Get(ctx, sts.Name, metav1.GetOptions{})
   633  		if err != nil {
   634  			return false, err
   635  		}
   636  		return ss.Status.ReadyReplicas == *ss.Spec.Replicas, nil
   637  	}); err != nil {
   638  		t.Fatalf("failed to verify .Spec.Template.Spec.Containers[0].Image is updated for sts %s: %v", sts.Name, err)
   639  	}
   640  
   641  	// Verify 3 pods exist
   642  	pods = getPods(t, podClient, labelMap)
   643  	if len(pods.Items) != int(*sts.Spec.Replicas) {
   644  		t.Fatalf("Unexpected number of pods")
   645  	}
   646  
   647  	// Verify pod images
   648  	for i := range pods.Items {
   649  		if i < int(*sts.Spec.UpdateStrategy.RollingUpdate.Partition) {
   650  			if pods.Items[i].Spec.Containers[0].Image != oldImage {
   651  				t.Fatalf("Pod %s has image %s not equal to old image %s", pods.Items[i].Name, pods.Items[i].Spec.Containers[0].Image, oldImage)
   652  			}
   653  		} else {
   654  			if pods.Items[i].Spec.Containers[0].Image != newImage {
   655  				t.Fatalf("Pod %s has image %s not equal to new image %s", pods.Items[i].Name, pods.Items[i].Spec.Containers[0].Image, newImage)
   656  			}
   657  		}
   658  	}
   659  }
   660  
   661  func TestStatefulSetStartOrdinal(t *testing.T) {
   662  	tests := []struct {
   663  		ordinals         *appsv1.StatefulSetOrdinals
   664  		name             string
   665  		namespace        string
   666  		replicas         int
   667  		expectedPodNames []string
   668  	}{
   669  		{
   670  			name:             "default start ordinal, no ordinals set",
   671  			namespace:        "no-ordinals",
   672  			replicas:         3,
   673  			expectedPodNames: []string{"sts-0", "sts-1", "sts-2"},
   674  		},
   675  		{
   676  			name:             "default start ordinal",
   677  			namespace:        "no-start-ordinals",
   678  			ordinals:         &appsv1.StatefulSetOrdinals{},
   679  			replicas:         3,
   680  			expectedPodNames: []string{"sts-0", "sts-1", "sts-2"},
   681  		},
   682  		{
   683  			name:      "start ordinal 4",
   684  			namespace: "start-ordinal-4",
   685  			ordinals: &appsv1.StatefulSetOrdinals{
   686  				Start: 4,
   687  			},
   688  			replicas:         4,
   689  			expectedPodNames: []string{"sts-4", "sts-5", "sts-6", "sts-7"},
   690  		},
   691  		{
   692  			name:      "start ordinal 5",
   693  			namespace: "start-ordinal-5",
   694  			ordinals: &appsv1.StatefulSetOrdinals{
   695  				Start: 2,
   696  			},
   697  			replicas:         7,
   698  			expectedPodNames: []string{"sts-2", "sts-3", "sts-4", "sts-5", "sts-6", "sts-7", "sts-8"},
   699  		},
   700  	}
   701  
   702  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StatefulSetStartOrdinal, true)()
   703  	_, ctx := ktesting.NewTestContext(t)
   704  	closeFn, rm, informers, c := scSetup(ctx, t)
   705  	defer closeFn()
   706  	cancel := runControllerAndInformers(rm, informers)
   707  	defer cancel()
   708  
   709  	for _, test := range tests {
   710  		t.Run(test.name, func(t *testing.T) {
   711  			ns := framework.CreateNamespaceOrDie(c, test.namespace, t)
   712  			defer framework.DeleteNamespaceOrDie(c, ns, t)
   713  
   714  			// Label map is the map of pod labels used in newSTS()
   715  			labelMap := labelMap()
   716  			sts := newSTS("sts", ns.Name, test.replicas)
   717  			sts.Spec.Ordinals = test.ordinals
   718  			stss := createSTSs(t, c, []*appsv1.StatefulSet{sts})
   719  			sts = stss[0]
   720  			waitSTSStable(t, c, sts)
   721  
   722  			podClient := c.CoreV1().Pods(ns.Name)
   723  			pods := getPods(t, podClient, labelMap)
   724  			if len(pods.Items) != test.replicas {
   725  				t.Errorf("len(pods) = %v, want %v", len(pods.Items), test.replicas)
   726  			}
   727  
   728  			var podNames []string
   729  			for _, pod := range pods.Items {
   730  				podNames = append(podNames, pod.Name)
   731  			}
   732  			ignoreOrder := cmpopts.SortSlices(func(a, b string) bool {
   733  				return a < b
   734  			})
   735  
   736  			// Validate all the expected pods were created.
   737  			if diff := cmp.Diff(test.expectedPodNames, podNames, ignoreOrder); diff != "" {
   738  				t.Errorf("Unexpected pod names: (-want +got): %v", diff)
   739  			}
   740  
   741  			// Scale down to 1 pod and verify it matches the first pod.
   742  			scaleSTS(t, c, sts, 1)
   743  			waitSTSStable(t, c, sts)
   744  
   745  			pods = getPods(t, podClient, labelMap)
   746  			if len(pods.Items) != 1 {
   747  				t.Errorf("len(pods) = %v, want %v", len(pods.Items), 1)
   748  			}
   749  			if pods.Items[0].Name != test.expectedPodNames[0] {
   750  				t.Errorf("Unexpected singleton pod name: got = %v, want %v", pods.Items[0].Name, test.expectedPodNames[0])
   751  			}
   752  		})
   753  	}
   754  }