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 }