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  }