k8s.io/kubernetes@v1.29.3/test/e2e/storage/vsphere/vsphere_volume_diskformat.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 vsphere 18 19 import ( 20 "context" 21 "path/filepath" 22 23 "github.com/onsi/ginkgo/v2" 24 "github.com/onsi/gomega" 25 "github.com/vmware/govmomi/object" 26 "github.com/vmware/govmomi/vim25/types" 27 28 v1 "k8s.io/api/core/v1" 29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 "k8s.io/apimachinery/pkg/util/uuid" 31 clientset "k8s.io/client-go/kubernetes" 32 "k8s.io/kubernetes/test/e2e/feature" 33 "k8s.io/kubernetes/test/e2e/framework" 34 e2enode "k8s.io/kubernetes/test/e2e/framework/node" 35 e2epod "k8s.io/kubernetes/test/e2e/framework/pod" 36 e2epv "k8s.io/kubernetes/test/e2e/framework/pv" 37 e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper" 38 "k8s.io/kubernetes/test/e2e/storage/utils" 39 admissionapi "k8s.io/pod-security-admission/api" 40 ) 41 42 /* 43 Test to verify diskformat specified in storage-class is being honored while volume creation. 44 Valid and supported options are eagerzeroedthick, zeroedthick and thin 45 46 Steps 47 1. Create StorageClass with diskformat set to valid type 48 2. Create PVC which uses the StorageClass created in step 1. 49 3. Wait for PV to be provisioned. 50 4. Wait for PVC's status to become Bound 51 5. Create pod using PVC on specific node. 52 6. Wait for Disk to be attached to the node. 53 7. Get node VM's devices and find PV's Volume Disk. 54 8. Get Backing Info of the Volume Disk and obtain EagerlyScrub and ThinProvisioned 55 9. Based on the value of EagerlyScrub and ThinProvisioned, verify diskformat is correct. 56 10. Delete pod and Wait for Volume Disk to be detached from the Node. 57 11. Delete PVC, PV and Storage Class 58 */ 59 60 var _ = utils.SIGDescribe("Volume Disk Format", feature.Vsphere, func() { 61 f := framework.NewDefaultFramework("volume-disk-format") 62 f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged 63 const ( 64 NodeLabelKey = "vsphere_e2e_label_volume_diskformat" 65 ) 66 var ( 67 client clientset.Interface 68 namespace string 69 nodeName string 70 nodeKeyValueLabel map[string]string 71 nodeLabelValue string 72 ) 73 ginkgo.BeforeEach(func(ctx context.Context) { 74 e2eskipper.SkipUnlessProviderIs("vsphere") 75 Bootstrap(f) 76 client = f.ClientSet 77 namespace = f.Namespace.Name 78 nodeName = GetReadySchedulableRandomNodeInfo(ctx, client).Name 79 nodeLabelValue = "vsphere_e2e_" + string(uuid.NewUUID()) 80 nodeKeyValueLabel = map[string]string{NodeLabelKey: nodeLabelValue} 81 e2enode.AddOrUpdateLabelOnNode(client, nodeName, NodeLabelKey, nodeLabelValue) 82 ginkgo.DeferCleanup(e2enode.RemoveLabelOffNode, client, nodeName, NodeLabelKey) 83 }) 84 85 ginkgo.It("verify disk format type - eagerzeroedthick is honored for dynamically provisioned pv using storageclass", func(ctx context.Context) { 86 ginkgo.By("Invoking Test for diskformat: eagerzeroedthick") 87 invokeTest(ctx, f, client, namespace, nodeName, nodeKeyValueLabel, "eagerzeroedthick") 88 }) 89 ginkgo.It("verify disk format type - zeroedthick is honored for dynamically provisioned pv using storageclass", func(ctx context.Context) { 90 ginkgo.By("Invoking Test for diskformat: zeroedthick") 91 invokeTest(ctx, f, client, namespace, nodeName, nodeKeyValueLabel, "zeroedthick") 92 }) 93 ginkgo.It("verify disk format type - thin is honored for dynamically provisioned pv using storageclass", func(ctx context.Context) { 94 ginkgo.By("Invoking Test for diskformat: thin") 95 invokeTest(ctx, f, client, namespace, nodeName, nodeKeyValueLabel, "thin") 96 }) 97 }) 98 99 func invokeTest(ctx context.Context, f *framework.Framework, client clientset.Interface, namespace string, nodeName string, nodeKeyValueLabel map[string]string, diskFormat string) { 100 101 framework.Logf("Invoking Test for DiskFomat: %s", diskFormat) 102 scParameters := make(map[string]string) 103 scParameters["diskformat"] = diskFormat 104 105 ginkgo.By("Creating Storage Class With DiskFormat") 106 storageClassSpec := getVSphereStorageClassSpec("thinsc", scParameters, nil, "") 107 storageclass, err := client.StorageV1().StorageClasses().Create(ctx, storageClassSpec, metav1.CreateOptions{}) 108 framework.ExpectNoError(err) 109 110 ginkgo.DeferCleanup(framework.IgnoreNotFound(client.StorageV1().StorageClasses().Delete), storageclass.Name, metav1.DeleteOptions{}) 111 112 ginkgo.By("Creating PVC using the Storage Class") 113 pvclaimSpec := getVSphereClaimSpecWithStorageClass(namespace, "2Gi", storageclass) 114 pvclaim, err := client.CoreV1().PersistentVolumeClaims(namespace).Create(ctx, pvclaimSpec, metav1.CreateOptions{}) 115 framework.ExpectNoError(err) 116 117 ginkgo.DeferCleanup(framework.IgnoreNotFound(client.CoreV1().PersistentVolumeClaims(namespace).Delete), pvclaimSpec.Name, metav1.DeleteOptions{}) 118 119 ginkgo.By("Waiting for claim to be in bound phase") 120 err = e2epv.WaitForPersistentVolumeClaimPhase(ctx, v1.ClaimBound, client, pvclaim.Namespace, pvclaim.Name, framework.Poll, f.Timeouts.ClaimProvision) 121 framework.ExpectNoError(err) 122 123 // Get new copy of the claim 124 pvclaim, err = client.CoreV1().PersistentVolumeClaims(pvclaim.Namespace).Get(ctx, pvclaim.Name, metav1.GetOptions{}) 125 framework.ExpectNoError(err) 126 127 // Get the bound PV 128 pv, err := client.CoreV1().PersistentVolumes().Get(ctx, pvclaim.Spec.VolumeName, metav1.GetOptions{}) 129 framework.ExpectNoError(err) 130 131 /* 132 PV is required to be attached to the Node. so that using govmomi API we can grab Disk's Backing Info 133 to check EagerlyScrub and ThinProvisioned property 134 */ 135 ginkgo.By("Creating pod to attach PV to the node") 136 // Create pod to attach Volume to Node 137 podSpec := getVSpherePodSpecWithClaim(pvclaim.Name, nodeKeyValueLabel, "while true ; do sleep 2 ; done") 138 pod, err := client.CoreV1().Pods(namespace).Create(ctx, podSpec, metav1.CreateOptions{}) 139 framework.ExpectNoError(err) 140 141 ginkgo.By("Waiting for pod to be running") 142 gomega.Expect(e2epod.WaitForPodNameRunningInNamespace(ctx, client, pod.Name, namespace)).To(gomega.Succeed()) 143 144 isAttached, err := diskIsAttached(ctx, pv.Spec.VsphereVolume.VolumePath, nodeName) 145 if !isAttached { 146 framework.Failf("Volume: %s is not attached to the node: %v", pv.Spec.VsphereVolume.VolumePath, nodeName) 147 } 148 framework.ExpectNoError(err) 149 150 ginkgo.By("Verify Disk Format") 151 if !verifyDiskFormat(ctx, client, nodeName, pv.Spec.VsphereVolume.VolumePath, diskFormat) { 152 framework.Failf("DiskFormat Verification Failed. Node: %s, VolumePath: %s, Expected Format: %s", nodeName, pv.Spec.VsphereVolume.VolumePath, diskFormat) 153 } 154 155 var volumePaths []string 156 volumePaths = append(volumePaths, pv.Spec.VsphereVolume.VolumePath) 157 158 ginkgo.By("Delete pod and wait for volume to be detached from node") 159 deletePodAndWaitForVolumeToDetach(ctx, f, client, pod, nodeName, volumePaths) 160 161 } 162 163 func verifyDiskFormat(ctx context.Context, client clientset.Interface, nodeName string, pvVolumePath string, diskFormat string) bool { 164 ginkgo.By("Verifying disk format") 165 eagerlyScrub := false 166 thinProvisioned := false 167 diskFound := false 168 pvvmdkfileName := filepath.Base(pvVolumePath) + filepath.Ext(pvVolumePath) 169 170 ctx, cancel := context.WithCancel(ctx) 171 defer cancel() 172 173 nodeInfo := TestContext.NodeMapper.GetNodeInfo(nodeName) 174 vm := object.NewVirtualMachine(nodeInfo.VSphere.Client.Client, nodeInfo.VirtualMachineRef) 175 vmDevices, err := vm.Device(ctx) 176 framework.ExpectNoError(err) 177 178 disks := vmDevices.SelectByType((*types.VirtualDisk)(nil)) 179 180 for _, disk := range disks { 181 backing := disk.GetVirtualDevice().Backing.(*types.VirtualDiskFlatVer2BackingInfo) 182 backingFileName := filepath.Base(backing.FileName) + filepath.Ext(backing.FileName) 183 if backingFileName == pvvmdkfileName { 184 diskFound = true 185 if backing.EagerlyScrub != nil { 186 eagerlyScrub = *backing.EagerlyScrub 187 } 188 if backing.ThinProvisioned != nil { 189 thinProvisioned = *backing.ThinProvisioned 190 } 191 break 192 } 193 } 194 195 if !diskFound { 196 framework.Failf("Failed to find disk: %s", pvVolumePath) 197 } 198 isDiskFormatCorrect := false 199 if diskFormat == "eagerzeroedthick" { 200 if eagerlyScrub && !thinProvisioned { 201 isDiskFormatCorrect = true 202 } 203 } else if diskFormat == "zeroedthick" { 204 if !eagerlyScrub && !thinProvisioned { 205 isDiskFormatCorrect = true 206 } 207 } else if diskFormat == "thin" { 208 if !eagerlyScrub && thinProvisioned { 209 isDiskFormatCorrect = true 210 } 211 } 212 return isDiskFormatCorrect 213 }