k8s.io/kubernetes@v1.29.3/test/e2e/upgrades/apps/statefulset.go (about)

     1  /*
     2  Copyright 2017 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package apps
    18  
    19  import (
    20  	"context"
    21  
    22  	"github.com/onsi/ginkgo/v2"
    23  
    24  	appsv1 "k8s.io/api/apps/v1"
    25  	v1 "k8s.io/api/core/v1"
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	"k8s.io/apimachinery/pkg/util/version"
    28  
    29  	"k8s.io/kubernetes/test/e2e/framework"
    30  	e2estatefulset "k8s.io/kubernetes/test/e2e/framework/statefulset"
    31  	"k8s.io/kubernetes/test/e2e/upgrades"
    32  )
    33  
    34  // createStatefulSetService creates a Headless Service with Name name and Selector set to match labels.
    35  func createStatefulSetService(name string, labels map[string]string) *v1.Service {
    36  	headlessService := &v1.Service{
    37  		ObjectMeta: metav1.ObjectMeta{
    38  			Name: name,
    39  		},
    40  		Spec: v1.ServiceSpec{
    41  			Selector: labels,
    42  		},
    43  	}
    44  	headlessService.Spec.Ports = []v1.ServicePort{
    45  		{Port: 80, Name: "http", Protocol: v1.ProtocolTCP},
    46  	}
    47  	headlessService.Spec.ClusterIP = "None"
    48  	return headlessService
    49  }
    50  
    51  // StatefulSetUpgradeTest implements an upgrade test harness for StatefulSet upgrade testing.
    52  type StatefulSetUpgradeTest struct {
    53  	service *v1.Service
    54  	set     *appsv1.StatefulSet
    55  }
    56  
    57  // Name returns the tracking name of the test.
    58  func (StatefulSetUpgradeTest) Name() string { return "[sig-apps] statefulset-upgrade" }
    59  
    60  // Skip returns true when this test can be skipped.
    61  func (StatefulSetUpgradeTest) Skip(upgCtx upgrades.UpgradeContext) bool {
    62  	minVersion := version.MustParseSemantic("1.5.0")
    63  
    64  	for _, vCtx := range upgCtx.Versions {
    65  		if vCtx.Version.LessThan(minVersion) {
    66  			return true
    67  		}
    68  	}
    69  	return false
    70  }
    71  
    72  // Setup creates a StatefulSet and a HeadlessService. It verifies the basic SatefulSet properties
    73  func (t *StatefulSetUpgradeTest) Setup(ctx context.Context, f *framework.Framework) {
    74  	ssName := "ss"
    75  	labels := map[string]string{
    76  		"foo": "bar",
    77  		"baz": "blah",
    78  	}
    79  	headlessSvcName := "test"
    80  	statefulPodMounts := []v1.VolumeMount{{Name: "datadir", MountPath: "/data/"}}
    81  	podMounts := []v1.VolumeMount{{Name: "home", MountPath: "/home"}}
    82  	ns := f.Namespace.Name
    83  	t.set = e2estatefulset.NewStatefulSet(ssName, ns, headlessSvcName, 2, statefulPodMounts, podMounts, labels)
    84  	t.service = createStatefulSetService(ssName, labels)
    85  	*(t.set.Spec.Replicas) = 3
    86  	e2estatefulset.PauseNewPods(t.set)
    87  
    88  	ginkgo.By("Creating service " + headlessSvcName + " in namespace " + ns)
    89  	_, err := f.ClientSet.CoreV1().Services(ns).Create(ctx, t.service, metav1.CreateOptions{})
    90  	framework.ExpectNoError(err)
    91  
    92  	ginkgo.By("Creating statefulset " + ssName + " in namespace " + ns)
    93  	*(t.set.Spec.Replicas) = 3
    94  	_, err = f.ClientSet.AppsV1().StatefulSets(ns).Create(ctx, t.set, metav1.CreateOptions{})
    95  	framework.ExpectNoError(err)
    96  
    97  	ginkgo.By("Saturating stateful set " + t.set.Name)
    98  	e2estatefulset.Saturate(ctx, f.ClientSet, t.set)
    99  	t.verify(ctx, f)
   100  	t.restart(ctx, f)
   101  	t.verify(ctx, f)
   102  }
   103  
   104  // Test waits for the upgrade to complete and verifies the StatefulSet basic functionality
   105  func (t *StatefulSetUpgradeTest) Test(ctx context.Context, f *framework.Framework, done <-chan struct{}, upgrade upgrades.UpgradeType) {
   106  	<-done
   107  	t.verify(ctx, f)
   108  }
   109  
   110  // Teardown deletes all StatefulSets
   111  func (t *StatefulSetUpgradeTest) Teardown(ctx context.Context, f *framework.Framework) {
   112  	e2estatefulset.DeleteAllStatefulSets(ctx, f.ClientSet, t.set.Name)
   113  }
   114  
   115  func (t *StatefulSetUpgradeTest) verify(ctx context.Context, f *framework.Framework) {
   116  	ginkgo.By("Verifying statefulset mounted data directory is usable")
   117  	framework.ExpectNoError(e2estatefulset.CheckMount(ctx, f.ClientSet, t.set, "/data"))
   118  
   119  	ginkgo.By("Verifying statefulset provides a stable hostname for each pod")
   120  	framework.ExpectNoError(e2estatefulset.CheckHostname(ctx, f.ClientSet, t.set))
   121  
   122  	ginkgo.By("Verifying statefulset set proper service name")
   123  	framework.ExpectNoError(e2estatefulset.CheckServiceName(t.set, t.set.Spec.ServiceName))
   124  
   125  	cmd := "echo $(hostname) > /data/hostname; sync;"
   126  	ginkgo.By("Running " + cmd + " in all stateful pods")
   127  	framework.ExpectNoError(e2estatefulset.ExecInStatefulPods(ctx, f.ClientSet, t.set, cmd))
   128  }
   129  
   130  func (t *StatefulSetUpgradeTest) restart(ctx context.Context, f *framework.Framework) {
   131  	ginkgo.By("Restarting statefulset " + t.set.Name)
   132  	e2estatefulset.Restart(ctx, f.ClientSet, t.set)
   133  	e2estatefulset.WaitForRunningAndReady(ctx, f.ClientSet, *t.set.Spec.Replicas, t.set)
   134  }