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