k8s.io/kubernetes@v1.29.3/test/e2e/storage/ubernetes_lite_volumes.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 storage 18 19 import ( 20 "context" 21 "fmt" 22 "sync" 23 24 "github.com/onsi/ginkgo/v2" 25 v1 "k8s.io/api/core/v1" 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 utilerrors "k8s.io/apimachinery/pkg/util/errors" 28 "k8s.io/apimachinery/pkg/util/sets" 29 clientset "k8s.io/client-go/kubernetes" 30 "k8s.io/kubernetes/test/e2e/framework" 31 e2enode "k8s.io/kubernetes/test/e2e/framework/node" 32 e2epod "k8s.io/kubernetes/test/e2e/framework/pod" 33 e2epv "k8s.io/kubernetes/test/e2e/framework/pv" 34 e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper" 35 "k8s.io/kubernetes/test/e2e/storage/utils" 36 admissionapi "k8s.io/pod-security-admission/api" 37 ) 38 39 var _ = utils.SIGDescribe("Multi-AZ Cluster Volumes", func() { 40 f := framework.NewDefaultFramework("multi-az") 41 f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged 42 var zoneCount int 43 var err error 44 image := framework.ServeHostnameImage 45 ginkgo.BeforeEach(func(ctx context.Context) { 46 e2eskipper.SkipUnlessProviderIs("gce", "gke") 47 if zoneCount <= 0 { 48 zoneCount, err = getZoneCount(ctx, f.ClientSet) 49 framework.ExpectNoError(err) 50 } 51 ginkgo.By(fmt.Sprintf("Checking for multi-zone cluster. Zone count = %d", zoneCount)) 52 msg := fmt.Sprintf("Zone count is %d, only run for multi-zone clusters, skipping test", zoneCount) 53 e2eskipper.SkipUnlessAtLeast(zoneCount, 2, msg) 54 // TODO: SkipUnlessDefaultScheduler() // Non-default schedulers might not spread 55 }) 56 ginkgo.It("should schedule pods in the same zones as statically provisioned PVs", func(ctx context.Context) { 57 PodsUseStaticPVsOrFail(ctx, f, (2*zoneCount)+1, image) 58 }) 59 }) 60 61 // Return the number of zones in which we have nodes in this cluster. 62 func getZoneCount(ctx context.Context, c clientset.Interface) (int, error) { 63 zoneNames, err := e2enode.GetSchedulableClusterZones(ctx, c) 64 if err != nil { 65 return -1, err 66 } 67 return len(zoneNames), nil 68 } 69 70 type staticPVTestConfig struct { 71 pvSource *v1.PersistentVolumeSource 72 pv *v1.PersistentVolume 73 pvc *v1.PersistentVolumeClaim 74 pod *v1.Pod 75 } 76 77 // PodsUseStaticPVsOrFail Check that the pods using statically 78 // created PVs get scheduled to the same zone that the PV is in. 79 func PodsUseStaticPVsOrFail(ctx context.Context, f *framework.Framework, podCount int, image string) { 80 var err error 81 c := f.ClientSet 82 ns := f.Namespace.Name 83 84 zones, err := e2enode.GetSchedulableClusterZones(ctx, c) 85 framework.ExpectNoError(err) 86 zonelist := sets.List(zones) 87 ginkgo.By("Creating static PVs across zones") 88 configs := make([]*staticPVTestConfig, podCount) 89 for i := range configs { 90 configs[i] = &staticPVTestConfig{} 91 } 92 93 ginkgo.DeferCleanup(func(ctx context.Context) { 94 ginkgo.By("Cleaning up pods and PVs") 95 for _, config := range configs { 96 e2epod.DeletePodOrFail(ctx, c, ns, config.pod.Name) 97 } 98 var wg sync.WaitGroup 99 wg.Add(len(configs)) 100 for i := range configs { 101 go func(config *staticPVTestConfig) { 102 defer ginkgo.GinkgoRecover() 103 defer wg.Done() 104 err := e2epod.WaitForPodNotFoundInNamespace(ctx, c, config.pod.Name, ns, f.Timeouts.PodDelete) 105 framework.ExpectNoError(err, "while waiting for pod to disappear") 106 errs := e2epv.PVPVCCleanup(ctx, c, ns, config.pv, config.pvc) 107 framework.ExpectNoError(utilerrors.NewAggregate(errs), "while cleaning up PVs and PVCs") 108 err = e2epv.DeletePVSource(ctx, config.pvSource) 109 framework.ExpectNoError(err, "while deleting PVSource") 110 }(configs[i]) 111 } 112 wg.Wait() 113 }) 114 115 for i, config := range configs { 116 zone := zonelist[i%len(zones)] 117 config.pvSource, err = e2epv.CreatePVSource(ctx, zone) 118 framework.ExpectNoError(err) 119 120 pvConfig := e2epv.PersistentVolumeConfig{ 121 NamePrefix: "multizone-pv", 122 PVSource: *config.pvSource, 123 Prebind: nil, 124 } 125 className := "" 126 pvcConfig := e2epv.PersistentVolumeClaimConfig{StorageClassName: &className} 127 128 config.pv, config.pvc, err = e2epv.CreatePVPVC(ctx, c, f.Timeouts, pvConfig, pvcConfig, ns, true) 129 framework.ExpectNoError(err) 130 } 131 132 ginkgo.By("Waiting for all PVCs to be bound") 133 for _, config := range configs { 134 e2epv.WaitOnPVandPVC(ctx, c, f.Timeouts, ns, config.pv, config.pvc) 135 } 136 137 ginkgo.By("Creating pods for each static PV") 138 for _, config := range configs { 139 podConfig := e2epod.MakePod(ns, nil, []*v1.PersistentVolumeClaim{config.pvc}, f.NamespacePodSecurityLevel, "") 140 config.pod, err = c.CoreV1().Pods(ns).Create(ctx, podConfig, metav1.CreateOptions{}) 141 framework.ExpectNoError(err) 142 } 143 144 ginkgo.By("Waiting for all pods to be running") 145 for _, config := range configs { 146 err = e2epod.WaitForPodRunningInNamespace(ctx, c, config.pod) 147 framework.ExpectNoError(err) 148 } 149 }