k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/e2e/framework/pv/pv.go (about)

     1  /*
     2  Copyright 2015 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 pv
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"strings"
    23  	"time"
    24  
    25  	"k8s.io/apimachinery/pkg/util/wait"
    26  
    27  	"k8s.io/kubernetes/test/e2e/storage/utils"
    28  
    29  	"github.com/onsi/ginkgo/v2"
    30  	v1 "k8s.io/api/core/v1"
    31  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    32  	"k8s.io/apimachinery/pkg/api/resource"
    33  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    34  	"k8s.io/apimachinery/pkg/labels"
    35  	"k8s.io/apimachinery/pkg/types"
    36  	clientset "k8s.io/client-go/kubernetes"
    37  	"k8s.io/kubernetes/pkg/volume/util"
    38  	"k8s.io/kubernetes/test/e2e/framework"
    39  	e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
    40  )
    41  
    42  const (
    43  	pdRetryTimeout  = 5 * time.Minute
    44  	pdRetryPollTime = 5 * time.Second
    45  
    46  	// VolumeSelectorKey is the key for volume selector.
    47  	VolumeSelectorKey = "e2e-pv-pool"
    48  
    49  	// volumeGidAnnotationKey is the of the annotation on the PersistentVolume
    50  	// object that specifies a supplemental GID.
    51  	// it is copied from k8s.io/kubernetes/pkg/volume/util VolumeGidAnnotationKey
    52  	volumeGidAnnotationKey = "pv.beta.kubernetes.io/gid"
    53  )
    54  
    55  var (
    56  	// SELinuxLabel is common selinux labels.
    57  	SELinuxLabel = &v1.SELinuxOptions{
    58  		Level: "s0:c0,c1"}
    59  )
    60  
    61  type pvval struct{}
    62  
    63  // PVMap is a map of all PVs used in the multi pv-pvc tests. The key is the PV's name, which is
    64  // guaranteed to be unique. The value is {} (empty struct) since we're only interested
    65  // in the PV's name and if it is present. We must always Get the pv object before
    66  // referencing any of its values, eg its ClaimRef.
    67  type PVMap map[string]pvval
    68  
    69  type pvcval struct{}
    70  
    71  // PVCMap is a map of all PVCs used in the multi pv-pvc tests. The key is "namespace/pvc.Name". The
    72  // value is {} (empty struct) since we're only interested in the PVC's name and if it is
    73  // present. We must always Get the pvc object before referencing any of its values, eg.
    74  // its VolumeName.
    75  // Note: It's unsafe to add keys to a map in a loop. Their insertion in the map is
    76  //
    77  //	unpredictable and can result in the same key being iterated over again.
    78  type PVCMap map[types.NamespacedName]pvcval
    79  
    80  // PersistentVolumeConfig is consumed by MakePersistentVolume() to generate a PV object
    81  // for varying storage options (NFS, ceph, etc.).
    82  // (+optional) prebind holds a pre-bound PVC
    83  // Example pvSource:
    84  //
    85  //	pvSource: api.PersistentVolumeSource{
    86  //		NFS: &api.NFSVolumeSource{
    87  //	 		...
    88  //	 	},
    89  //	 }
    90  type PersistentVolumeConfig struct {
    91  	// [Optional] NamePrefix defaults to "pv-" if unset
    92  	NamePrefix string
    93  	// [Optional] Labels contains information used to organize and categorize
    94  	// objects
    95  	Labels labels.Set
    96  	// [Optional] Annotations contains information used to organize and categorize
    97  	// objects
    98  	Annotations map[string]string
    99  	// PVSource contains the details of the underlying volume and must be set
   100  	PVSource v1.PersistentVolumeSource
   101  	// [Optional] Prebind lets you specify a PVC to bind this PV to before
   102  	// creation
   103  	Prebind *v1.PersistentVolumeClaim
   104  	// [Optiona] ReclaimPolicy defaults to "Reclaim" if unset
   105  	ReclaimPolicy    v1.PersistentVolumeReclaimPolicy
   106  	StorageClassName string
   107  	// [Optional] NodeAffinity defines constraints that limit what nodes this
   108  	// volume can be accessed from.
   109  	NodeAffinity *v1.VolumeNodeAffinity
   110  	// [Optional] VolumeMode defaults to "Filesystem" if unset
   111  	VolumeMode *v1.PersistentVolumeMode
   112  	// [Optional] AccessModes defaults to RWO if unset
   113  	AccessModes []v1.PersistentVolumeAccessMode
   114  	// [Optional] Capacity is the storage capacity in Quantity format. Defaults
   115  	// to "2Gi" if unset
   116  	Capacity string
   117  }
   118  
   119  // PersistentVolumeClaimConfig is consumed by MakePersistentVolumeClaim() to
   120  // generate a PVC object.
   121  type PersistentVolumeClaimConfig struct {
   122  	// Name of the PVC. If set, overrides NamePrefix
   123  	Name string
   124  	// NamePrefix defaults to "pvc-" if unspecified
   125  	NamePrefix string
   126  	// ClaimSize must be specified in the Quantity format. Defaults to 2Gi if
   127  	// unspecified
   128  	ClaimSize string
   129  	// AccessModes defaults to RWO if unspecified
   130  	AccessModes      []v1.PersistentVolumeAccessMode
   131  	Annotations      map[string]string
   132  	Selector         *metav1.LabelSelector
   133  	StorageClassName *string
   134  	// VolumeMode defaults to nil if unspecified or specified as the empty
   135  	// string
   136  	VolumeMode *v1.PersistentVolumeMode
   137  }
   138  
   139  // PVPVCCleanup cleans up a pv and pvc in a single pv/pvc test case.
   140  // Note: delete errors are appended to []error so that we can attempt to delete both the pvc and pv.
   141  func PVPVCCleanup(ctx context.Context, c clientset.Interface, ns string, pv *v1.PersistentVolume, pvc *v1.PersistentVolumeClaim) []error {
   142  	var errs []error
   143  
   144  	if pvc != nil {
   145  		err := DeletePersistentVolumeClaim(ctx, c, pvc.Name, ns)
   146  		if err != nil {
   147  			errs = append(errs, fmt.Errorf("failed to delete PVC %q: %w", pvc.Name, err))
   148  		}
   149  	} else {
   150  		framework.Logf("pvc is nil")
   151  	}
   152  	if pv != nil {
   153  		err := DeletePersistentVolume(ctx, c, pv.Name)
   154  		if err != nil {
   155  			errs = append(errs, fmt.Errorf("failed to delete PV %q: %w", pv.Name, err))
   156  		}
   157  	} else {
   158  		framework.Logf("pv is nil")
   159  	}
   160  	return errs
   161  }
   162  
   163  // PVPVCMapCleanup Cleans up pvs and pvcs in multi-pv-pvc test cases. Entries found in the pv and claim maps are
   164  // deleted as long as the Delete api call succeeds.
   165  // Note: delete errors are appended to []error so that as many pvcs and pvs as possible are deleted.
   166  func PVPVCMapCleanup(ctx context.Context, c clientset.Interface, ns string, pvols PVMap, claims PVCMap) []error {
   167  	var errs []error
   168  
   169  	for pvcKey := range claims {
   170  		err := DeletePersistentVolumeClaim(ctx, c, pvcKey.Name, ns)
   171  		if err != nil {
   172  			errs = append(errs, fmt.Errorf("failed to delete PVC %q: %w", pvcKey.Name, err))
   173  		} else {
   174  			delete(claims, pvcKey)
   175  		}
   176  	}
   177  
   178  	for pvKey := range pvols {
   179  		err := DeletePersistentVolume(ctx, c, pvKey)
   180  		if err != nil {
   181  			errs = append(errs, fmt.Errorf("failed to delete PV %q: %w", pvKey, err))
   182  		} else {
   183  			delete(pvols, pvKey)
   184  		}
   185  	}
   186  	return errs
   187  }
   188  
   189  // DeletePersistentVolume deletes the PV.
   190  func DeletePersistentVolume(ctx context.Context, c clientset.Interface, pvName string) error {
   191  	if c != nil && len(pvName) > 0 {
   192  		framework.Logf("Deleting PersistentVolume %q", pvName)
   193  		err := c.CoreV1().PersistentVolumes().Delete(ctx, pvName, metav1.DeleteOptions{})
   194  		if err != nil && !apierrors.IsNotFound(err) {
   195  			return fmt.Errorf("PV Delete API error: %w", err)
   196  		}
   197  	}
   198  	return nil
   199  }
   200  
   201  // DeletePersistentVolumeClaim deletes the Claim.
   202  func DeletePersistentVolumeClaim(ctx context.Context, c clientset.Interface, pvcName string, ns string) error {
   203  	if c != nil && len(pvcName) > 0 {
   204  		framework.Logf("Deleting PersistentVolumeClaim %q", pvcName)
   205  		err := c.CoreV1().PersistentVolumeClaims(ns).Delete(ctx, pvcName, metav1.DeleteOptions{})
   206  		if err != nil && !apierrors.IsNotFound(err) {
   207  			return fmt.Errorf("PVC Delete API error: %w", err)
   208  		}
   209  	}
   210  	return nil
   211  }
   212  
   213  // DeletePVCandValidatePV deletes the PVC and waits for the PV to enter its expected phase. Validate that the PV
   214  // has been reclaimed (assumption here about reclaimPolicy). Caller tells this func which
   215  // phase value to expect for the pv bound to the to-be-deleted claim.
   216  func DeletePVCandValidatePV(ctx context.Context, c clientset.Interface, timeouts *framework.TimeoutContext, ns string, pvc *v1.PersistentVolumeClaim, pv *v1.PersistentVolume, expectPVPhase v1.PersistentVolumePhase) error {
   217  	pvname := pvc.Spec.VolumeName
   218  	framework.Logf("Deleting PVC %v to trigger reclamation of PV %v", pvc.Name, pvname)
   219  	err := DeletePersistentVolumeClaim(ctx, c, pvc.Name, ns)
   220  	if err != nil {
   221  		return err
   222  	}
   223  
   224  	// Wait for the PV's phase to return to be `expectPVPhase`
   225  	framework.Logf("Waiting for reclaim process to complete.")
   226  	err = WaitForPersistentVolumePhase(ctx, expectPVPhase, c, pv.Name, framework.Poll, timeouts.PVReclaim)
   227  	if err != nil {
   228  		return fmt.Errorf("pv %q phase did not become %v: %w", pv.Name, expectPVPhase, err)
   229  	}
   230  
   231  	// examine the pv's ClaimRef and UID and compare to expected values
   232  	pv, err = c.CoreV1().PersistentVolumes().Get(ctx, pv.Name, metav1.GetOptions{})
   233  	if err != nil {
   234  		return fmt.Errorf("PV Get API error: %w", err)
   235  	}
   236  	cr := pv.Spec.ClaimRef
   237  	if expectPVPhase == v1.VolumeAvailable {
   238  		if cr != nil && len(cr.UID) > 0 {
   239  			return fmt.Errorf("PV is 'Available' but ClaimRef.UID is not empty")
   240  		}
   241  	} else if expectPVPhase == v1.VolumeBound {
   242  		if cr == nil {
   243  			return fmt.Errorf("PV is 'Bound' but ClaimRef is nil")
   244  		}
   245  		if len(cr.UID) == 0 {
   246  			return fmt.Errorf("PV is 'Bound' but ClaimRef.UID is empty")
   247  		}
   248  	}
   249  
   250  	framework.Logf("PV %v now in %q phase", pv.Name, expectPVPhase)
   251  	return nil
   252  }
   253  
   254  // DeletePVCandValidatePVGroup wraps deletePVCandValidatePV() by calling the function in a loop over the PV map. Only bound PVs
   255  // are deleted. Validates that the claim was deleted and the PV is in the expected Phase (Released,
   256  // Available, Bound).
   257  // Note: if there are more claims than pvs then some of the remaining claims may bind to just made
   258  //
   259  //	available pvs.
   260  func DeletePVCandValidatePVGroup(ctx context.Context, c clientset.Interface, timeouts *framework.TimeoutContext, ns string, pvols PVMap, claims PVCMap, expectPVPhase v1.PersistentVolumePhase) error {
   261  	var boundPVs, deletedPVCs int
   262  
   263  	for pvName := range pvols {
   264  		pv, err := c.CoreV1().PersistentVolumes().Get(ctx, pvName, metav1.GetOptions{})
   265  		if err != nil {
   266  			return fmt.Errorf("PV Get API error: %w", err)
   267  		}
   268  		cr := pv.Spec.ClaimRef
   269  		// if pv is bound then delete the pvc it is bound to
   270  		if cr != nil && len(cr.Name) > 0 {
   271  			boundPVs++
   272  			// Assert bound PVC is tracked in this test. Failing this might
   273  			// indicate external PVCs interfering with the test.
   274  			pvcKey := makePvcKey(ns, cr.Name)
   275  			if _, found := claims[pvcKey]; !found {
   276  				return fmt.Errorf("internal: claims map is missing pvc %q", pvcKey)
   277  			}
   278  			// get the pvc for the delete call below
   279  			pvc, err := c.CoreV1().PersistentVolumeClaims(ns).Get(ctx, cr.Name, metav1.GetOptions{})
   280  			if err == nil {
   281  				if err = DeletePVCandValidatePV(ctx, c, timeouts, ns, pvc, pv, expectPVPhase); err != nil {
   282  					return err
   283  				}
   284  			} else if !apierrors.IsNotFound(err) {
   285  				return fmt.Errorf("PVC Get API error: %w", err)
   286  			}
   287  			// delete pvckey from map even if apierrors.IsNotFound above is true and thus the
   288  			// claim was not actually deleted here
   289  			delete(claims, pvcKey)
   290  			deletedPVCs++
   291  		}
   292  	}
   293  	if boundPVs != deletedPVCs {
   294  		return fmt.Errorf("expect number of bound PVs (%v) to equal number of deleted PVCs (%v)", boundPVs, deletedPVCs)
   295  	}
   296  	return nil
   297  }
   298  
   299  // create the PV resource. Fails test on error.
   300  func createPV(ctx context.Context, c clientset.Interface, timeouts *framework.TimeoutContext, pv *v1.PersistentVolume) (*v1.PersistentVolume, error) {
   301  	var resultPV *v1.PersistentVolume
   302  	var lastCreateErr error
   303  	err := wait.PollUntilContextTimeout(ctx, 29*time.Second, timeouts.PVCreate, true, func(ctx context.Context) (done bool, err error) {
   304  		resultPV, lastCreateErr = c.CoreV1().PersistentVolumes().Create(ctx, pv, metav1.CreateOptions{})
   305  		if lastCreateErr != nil {
   306  			// If we hit a quota problem, we are not done and should retry again.  This happens to be the quota failure string for GCP.
   307  			// If quota failure strings are found for other platforms, they can be added to improve reliability when running
   308  			// many parallel test jobs in a single cloud account.  This corresponds to controller-like behavior and
   309  			// to what we would recommend for general clients.
   310  			if strings.Contains(lastCreateErr.Error(), `googleapi: Error 403: Quota exceeded for quota group`) {
   311  				return false, nil
   312  			}
   313  
   314  			// if it was not a quota failure, fail immediately
   315  			return false, lastCreateErr
   316  		}
   317  
   318  		return true, nil
   319  	})
   320  	// if we have an error from creating the PV, use that instead of a timeout error
   321  	if lastCreateErr != nil {
   322  		return nil, fmt.Errorf("PV Create API error: %w", err)
   323  	}
   324  	if err != nil {
   325  		return nil, fmt.Errorf("PV Create API error: %w", err)
   326  	}
   327  
   328  	return resultPV, nil
   329  }
   330  
   331  // CreatePV creates the PV resource. Fails test on error.
   332  func CreatePV(ctx context.Context, c clientset.Interface, timeouts *framework.TimeoutContext, pv *v1.PersistentVolume) (*v1.PersistentVolume, error) {
   333  	return createPV(ctx, c, timeouts, pv)
   334  }
   335  
   336  // CreatePVC creates the PVC resource. Fails test on error.
   337  func CreatePVC(ctx context.Context, c clientset.Interface, ns string, pvc *v1.PersistentVolumeClaim) (*v1.PersistentVolumeClaim, error) {
   338  	pvc, err := c.CoreV1().PersistentVolumeClaims(ns).Create(ctx, pvc, metav1.CreateOptions{})
   339  	if err != nil {
   340  		return nil, fmt.Errorf("PVC Create API error: %w", err)
   341  	}
   342  	return pvc, nil
   343  }
   344  
   345  // CreatePVCPV creates a PVC followed by the PV based on the passed in nfs-server ip and
   346  // namespace. If the "preBind" bool is true then pre-bind the PV to the PVC
   347  // via the PV's ClaimRef. Return the pv and pvc to reflect the created objects.
   348  // Note: in the pre-bind case the real PVC name, which is generated, is not
   349  //
   350  //	known until after the PVC is instantiated. This is why the pvc is created
   351  //	before the pv.
   352  func CreatePVCPV(ctx context.Context, c clientset.Interface, timeouts *framework.TimeoutContext, pvConfig PersistentVolumeConfig, pvcConfig PersistentVolumeClaimConfig, ns string, preBind bool) (*v1.PersistentVolume, *v1.PersistentVolumeClaim, error) {
   353  	// make the pvc spec
   354  	pvc := MakePersistentVolumeClaim(pvcConfig, ns)
   355  	preBindMsg := ""
   356  	if preBind {
   357  		preBindMsg = " pre-bound"
   358  		pvConfig.Prebind = pvc
   359  	}
   360  	// make the pv spec
   361  	pv := MakePersistentVolume(pvConfig)
   362  
   363  	ginkgo.By(fmt.Sprintf("Creating a PVC followed by a%s PV", preBindMsg))
   364  	pvc, err := CreatePVC(ctx, c, ns, pvc)
   365  	if err != nil {
   366  		return nil, nil, err
   367  	}
   368  
   369  	// instantiate the pv, handle pre-binding by ClaimRef if needed
   370  	if preBind {
   371  		pv.Spec.ClaimRef.Name = pvc.Name
   372  	}
   373  	pv, err = createPV(ctx, c, timeouts, pv)
   374  	if err != nil {
   375  		return nil, pvc, err
   376  	}
   377  	return pv, pvc, nil
   378  }
   379  
   380  // CreatePVPVC creates a PV followed by the PVC based on the passed in nfs-server ip and
   381  // namespace. If the "preBind" bool is true then pre-bind the PVC to the PV
   382  // via the PVC's VolumeName. Return the pv and pvc to reflect the created
   383  // objects.
   384  // Note: in the pre-bind case the real PV name, which is generated, is not
   385  //
   386  //	known until after the PV is instantiated. This is why the pv is created
   387  //	before the pvc.
   388  func CreatePVPVC(ctx context.Context, c clientset.Interface, timeouts *framework.TimeoutContext, pvConfig PersistentVolumeConfig, pvcConfig PersistentVolumeClaimConfig, ns string, preBind bool) (*v1.PersistentVolume, *v1.PersistentVolumeClaim, error) {
   389  	preBindMsg := ""
   390  	if preBind {
   391  		preBindMsg = " pre-bound"
   392  	}
   393  	framework.Logf("Creating a PV followed by a%s PVC", preBindMsg)
   394  
   395  	// make the pv and pvc definitions
   396  	pv := MakePersistentVolume(pvConfig)
   397  	pvc := MakePersistentVolumeClaim(pvcConfig, ns)
   398  
   399  	// instantiate the pv
   400  	pv, err := createPV(ctx, c, timeouts, pv)
   401  	if err != nil {
   402  		return nil, nil, err
   403  	}
   404  	// instantiate the pvc, handle pre-binding by VolumeName if needed
   405  	if preBind {
   406  		pvc.Spec.VolumeName = pv.Name
   407  	}
   408  	pvc, err = CreatePVC(ctx, c, ns, pvc)
   409  	if err != nil {
   410  		return pv, nil, err
   411  	}
   412  	return pv, pvc, nil
   413  }
   414  
   415  // CreatePVsPVCs creates the desired number of PVs and PVCs and returns them in separate maps. If the
   416  // number of PVs != the number of PVCs then the min of those two counts is the number of
   417  // PVs expected to bind. If a Create error occurs, the returned maps may contain pv and pvc
   418  // entries for the resources that were successfully created. In other words, when the caller
   419  // sees an error returned, it needs to decide what to do about entries in the maps.
   420  // Note: when the test suite deletes the namespace orphaned pvcs and pods are deleted. However,
   421  //
   422  //	orphaned pvs are not deleted and will remain after the suite completes.
   423  func CreatePVsPVCs(ctx context.Context, numpvs, numpvcs int, c clientset.Interface, timeouts *framework.TimeoutContext, ns string, pvConfig PersistentVolumeConfig, pvcConfig PersistentVolumeClaimConfig) (PVMap, PVCMap, error) {
   424  	pvMap := make(PVMap, numpvs)
   425  	pvcMap := make(PVCMap, numpvcs)
   426  	extraPVCs := 0
   427  	extraPVs := numpvs - numpvcs
   428  	if extraPVs < 0 {
   429  		extraPVCs = -extraPVs
   430  		extraPVs = 0
   431  	}
   432  	pvsToCreate := numpvs - extraPVs // want the min(numpvs, numpvcs)
   433  
   434  	// create pvs and pvcs
   435  	for i := 0; i < pvsToCreate; i++ {
   436  		pv, pvc, err := CreatePVPVC(ctx, c, timeouts, pvConfig, pvcConfig, ns, false)
   437  		if err != nil {
   438  			return pvMap, pvcMap, err
   439  		}
   440  		pvMap[pv.Name] = pvval{}
   441  		pvcMap[makePvcKey(ns, pvc.Name)] = pvcval{}
   442  	}
   443  
   444  	// create extra pvs or pvcs as needed
   445  	for i := 0; i < extraPVs; i++ {
   446  		pv := MakePersistentVolume(pvConfig)
   447  		pv, err := createPV(ctx, c, timeouts, pv)
   448  		if err != nil {
   449  			return pvMap, pvcMap, err
   450  		}
   451  		pvMap[pv.Name] = pvval{}
   452  	}
   453  	for i := 0; i < extraPVCs; i++ {
   454  		pvc := MakePersistentVolumeClaim(pvcConfig, ns)
   455  		pvc, err := CreatePVC(ctx, c, ns, pvc)
   456  		if err != nil {
   457  			return pvMap, pvcMap, err
   458  		}
   459  		pvcMap[makePvcKey(ns, pvc.Name)] = pvcval{}
   460  	}
   461  	return pvMap, pvcMap, nil
   462  }
   463  
   464  // WaitOnPVandPVC waits for the pv and pvc to bind to each other.
   465  func WaitOnPVandPVC(ctx context.Context, c clientset.Interface, timeouts *framework.TimeoutContext, ns string, pv *v1.PersistentVolume, pvc *v1.PersistentVolumeClaim) error {
   466  	// Wait for newly created PVC to bind to the PV
   467  	framework.Logf("Waiting for PV %v to bind to PVC %v", pv.Name, pvc.Name)
   468  	err := WaitForPersistentVolumeClaimPhase(ctx, v1.ClaimBound, c, ns, pvc.Name, framework.Poll, timeouts.ClaimBound)
   469  	if err != nil {
   470  		return fmt.Errorf("PVC %q did not become Bound: %w", pvc.Name, err)
   471  	}
   472  
   473  	// Wait for PersistentVolume.Status.Phase to be Bound, which it should be
   474  	// since the PVC is already bound.
   475  	err = WaitForPersistentVolumePhase(ctx, v1.VolumeBound, c, pv.Name, framework.Poll, timeouts.PVBound)
   476  	if err != nil {
   477  		return fmt.Errorf("PV %q did not become Bound: %w", pv.Name, err)
   478  	}
   479  
   480  	// Re-get the pv and pvc objects
   481  	pv, err = c.CoreV1().PersistentVolumes().Get(ctx, pv.Name, metav1.GetOptions{})
   482  	if err != nil {
   483  		return fmt.Errorf("PV Get API error: %w", err)
   484  	}
   485  	pvc, err = c.CoreV1().PersistentVolumeClaims(ns).Get(ctx, pvc.Name, metav1.GetOptions{})
   486  	if err != nil {
   487  		return fmt.Errorf("PVC Get API error: %w", err)
   488  	}
   489  
   490  	// The pv and pvc are both bound, but to each other?
   491  	// Check that the PersistentVolume.ClaimRef matches the PVC
   492  	if pv.Spec.ClaimRef == nil {
   493  		return fmt.Errorf("PV %q ClaimRef is nil", pv.Name)
   494  	}
   495  	if pv.Spec.ClaimRef.Name != pvc.Name {
   496  		return fmt.Errorf("PV %q ClaimRef's name (%q) should be %q", pv.Name, pv.Spec.ClaimRef.Name, pvc.Name)
   497  	}
   498  	if pvc.Spec.VolumeName != pv.Name {
   499  		return fmt.Errorf("PVC %q VolumeName (%q) should be %q", pvc.Name, pvc.Spec.VolumeName, pv.Name)
   500  	}
   501  	if pv.Spec.ClaimRef.UID != pvc.UID {
   502  		return fmt.Errorf("PV %q ClaimRef's UID (%q) should be %q", pv.Name, pv.Spec.ClaimRef.UID, pvc.UID)
   503  	}
   504  	return nil
   505  }
   506  
   507  // WaitAndVerifyBinds searches for bound PVs and PVCs by examining pvols for non-nil claimRefs.
   508  // NOTE: Each iteration waits for a maximum of 3 minutes per PV and, if the PV is bound,
   509  //
   510  //	up to 3 minutes for the PVC. When the number of PVs != number of PVCs, this can lead
   511  //	to situations where the maximum wait times are reached several times in succession,
   512  //	extending test time. Thus, it is recommended to keep the delta between PVs and PVCs
   513  //	small.
   514  func WaitAndVerifyBinds(ctx context.Context, c clientset.Interface, timeouts *framework.TimeoutContext, ns string, pvols PVMap, claims PVCMap, testExpected bool) error {
   515  	var actualBinds int
   516  	expectedBinds := len(pvols)
   517  	if expectedBinds > len(claims) { // want the min of # pvs or #pvcs
   518  		expectedBinds = len(claims)
   519  	}
   520  
   521  	for pvName := range pvols {
   522  		err := WaitForPersistentVolumePhase(ctx, v1.VolumeBound, c, pvName, framework.Poll, timeouts.PVBound)
   523  		if err != nil && len(pvols) > len(claims) {
   524  			framework.Logf("WARN: pv %v is not bound after max wait", pvName)
   525  			framework.Logf("      This may be ok since there are more pvs than pvcs")
   526  			continue
   527  		}
   528  		if err != nil {
   529  			return fmt.Errorf("PV %q did not become Bound: %w", pvName, err)
   530  		}
   531  
   532  		pv, err := c.CoreV1().PersistentVolumes().Get(ctx, pvName, metav1.GetOptions{})
   533  		if err != nil {
   534  			return fmt.Errorf("PV Get API error: %w", err)
   535  		}
   536  		cr := pv.Spec.ClaimRef
   537  		if cr != nil && len(cr.Name) > 0 {
   538  			// Assert bound pvc is a test resource. Failing assertion could
   539  			// indicate non-test PVC interference or a bug in the test
   540  			pvcKey := makePvcKey(ns, cr.Name)
   541  			if _, found := claims[pvcKey]; !found {
   542  				return fmt.Errorf("internal: claims map is missing pvc %q", pvcKey)
   543  			}
   544  
   545  			err := WaitForPersistentVolumeClaimPhase(ctx, v1.ClaimBound, c, ns, cr.Name, framework.Poll, timeouts.ClaimBound)
   546  			if err != nil {
   547  				return fmt.Errorf("PVC %q did not become Bound: %w", cr.Name, err)
   548  			}
   549  			actualBinds++
   550  		}
   551  	}
   552  
   553  	if testExpected && actualBinds != expectedBinds {
   554  		return fmt.Errorf("expect number of bound PVs (%v) to equal number of claims (%v)", actualBinds, expectedBinds)
   555  	}
   556  	return nil
   557  }
   558  
   559  // Return a pvckey struct.
   560  func makePvcKey(ns, name string) types.NamespacedName {
   561  	return types.NamespacedName{Namespace: ns, Name: name}
   562  }
   563  
   564  // MakePersistentVolume returns a PV definition based on the nfs server IP. If the PVC is not nil
   565  // then the PV is defined with a ClaimRef which includes the PVC's namespace.
   566  // If the PVC is nil then the PV is not defined with a ClaimRef.  If no reclaimPolicy
   567  // is assigned, assumes "Retain". Specs are expected to match the test's PVC.
   568  // Note: the passed-in claim does not have a name until it is created and thus the PV's
   569  //
   570  //	ClaimRef cannot be completely filled-in in this func. Therefore, the ClaimRef's name
   571  //	is added later in CreatePVCPV.
   572  func MakePersistentVolume(pvConfig PersistentVolumeConfig) *v1.PersistentVolume {
   573  	var claimRef *v1.ObjectReference
   574  
   575  	if len(pvConfig.AccessModes) == 0 {
   576  		pvConfig.AccessModes = append(pvConfig.AccessModes, v1.ReadWriteOnce)
   577  	}
   578  
   579  	if len(pvConfig.NamePrefix) == 0 {
   580  		pvConfig.NamePrefix = "pv-"
   581  	}
   582  
   583  	if pvConfig.ReclaimPolicy == "" {
   584  		pvConfig.ReclaimPolicy = v1.PersistentVolumeReclaimRetain
   585  	}
   586  
   587  	if len(pvConfig.Capacity) == 0 {
   588  		pvConfig.Capacity = "2Gi"
   589  	}
   590  
   591  	if pvConfig.Prebind != nil {
   592  		claimRef = &v1.ObjectReference{
   593  			Kind:       "PersistentVolumeClaim",
   594  			APIVersion: "v1",
   595  			Name:       pvConfig.Prebind.Name,
   596  			Namespace:  pvConfig.Prebind.Namespace,
   597  			UID:        pvConfig.Prebind.UID,
   598  		}
   599  	}
   600  
   601  	annotations := map[string]string{
   602  		volumeGidAnnotationKey: "777",
   603  	}
   604  	for k, v := range pvConfig.Annotations {
   605  		annotations[k] = v
   606  	}
   607  
   608  	return &v1.PersistentVolume{
   609  		ObjectMeta: metav1.ObjectMeta{
   610  			GenerateName: pvConfig.NamePrefix,
   611  			Labels:       pvConfig.Labels,
   612  			Annotations:  annotations,
   613  		},
   614  		Spec: v1.PersistentVolumeSpec{
   615  			PersistentVolumeReclaimPolicy: pvConfig.ReclaimPolicy,
   616  			Capacity: v1.ResourceList{
   617  				v1.ResourceStorage: resource.MustParse(pvConfig.Capacity),
   618  			},
   619  			PersistentVolumeSource: pvConfig.PVSource,
   620  			AccessModes:            pvConfig.AccessModes,
   621  			ClaimRef:               claimRef,
   622  			StorageClassName:       pvConfig.StorageClassName,
   623  			NodeAffinity:           pvConfig.NodeAffinity,
   624  			VolumeMode:             pvConfig.VolumeMode,
   625  		},
   626  	}
   627  }
   628  
   629  // MakePersistentVolumeClaim returns a PVC API Object based on the PersistentVolumeClaimConfig.
   630  func MakePersistentVolumeClaim(cfg PersistentVolumeClaimConfig, ns string) *v1.PersistentVolumeClaim {
   631  
   632  	if len(cfg.AccessModes) == 0 {
   633  		cfg.AccessModes = append(cfg.AccessModes, v1.ReadWriteOnce)
   634  	}
   635  
   636  	if len(cfg.ClaimSize) == 0 {
   637  		cfg.ClaimSize = "2Gi"
   638  	}
   639  
   640  	if len(cfg.NamePrefix) == 0 {
   641  		cfg.NamePrefix = "pvc-"
   642  	}
   643  
   644  	if cfg.VolumeMode != nil && *cfg.VolumeMode == "" {
   645  		framework.Logf("Warning: Making PVC: VolumeMode specified as invalid empty string, treating as nil")
   646  		cfg.VolumeMode = nil
   647  	}
   648  
   649  	return &v1.PersistentVolumeClaim{
   650  		ObjectMeta: metav1.ObjectMeta{
   651  			Name:         cfg.Name,
   652  			GenerateName: cfg.NamePrefix,
   653  			Namespace:    ns,
   654  			Annotations:  cfg.Annotations,
   655  		},
   656  		Spec: v1.PersistentVolumeClaimSpec{
   657  			Selector:    cfg.Selector,
   658  			AccessModes: cfg.AccessModes,
   659  			Resources: v1.VolumeResourceRequirements{
   660  				Requests: v1.ResourceList{
   661  					v1.ResourceStorage: resource.MustParse(cfg.ClaimSize),
   662  				},
   663  			},
   664  			StorageClassName: cfg.StorageClassName,
   665  			VolumeMode:       cfg.VolumeMode,
   666  		},
   667  	}
   668  }
   669  
   670  func createPDWithRetry(ctx context.Context, zone string) (string, error) {
   671  	var err error
   672  	var newDiskName string
   673  	for start := time.Now(); ; time.Sleep(pdRetryPollTime) {
   674  		if time.Since(start) >= pdRetryTimeout ||
   675  			ctx.Err() != nil {
   676  			return "", fmt.Errorf("timed out while trying to create PD in zone %q, last error: %w", zone, err)
   677  		}
   678  
   679  		newDiskName, err = createPD(zone)
   680  		if err != nil {
   681  			framework.Logf("Couldn't create a new PD in zone %q, sleeping 5 seconds: %v", zone, err)
   682  			continue
   683  		}
   684  		framework.Logf("Successfully created a new PD in zone %q: %q.", zone, newDiskName)
   685  		return newDiskName, nil
   686  	}
   687  }
   688  
   689  func CreateShare() (string, string, string, error) {
   690  	return framework.TestContext.CloudConfig.Provider.CreateShare()
   691  }
   692  
   693  func DeleteShare(accountName, shareName string) error {
   694  	return framework.TestContext.CloudConfig.Provider.DeleteShare(accountName, shareName)
   695  }
   696  
   697  // CreatePDWithRetry creates PD with retry.
   698  func CreatePDWithRetry(ctx context.Context) (string, error) {
   699  	return createPDWithRetry(ctx, "")
   700  }
   701  
   702  // CreatePDWithRetryAndZone creates PD on zone with retry.
   703  func CreatePDWithRetryAndZone(ctx context.Context, zone string) (string, error) {
   704  	return createPDWithRetry(ctx, zone)
   705  }
   706  
   707  // DeletePDWithRetry deletes PD with retry.
   708  func DeletePDWithRetry(ctx context.Context, diskName string) error {
   709  	var err error
   710  	for start := time.Now(); ; time.Sleep(pdRetryPollTime) {
   711  		if time.Since(start) >= pdRetryTimeout ||
   712  			ctx.Err() != nil {
   713  			return fmt.Errorf("timed out while trying to delete PD %q, last error: %w", diskName, err)
   714  		}
   715  		err = deletePD(diskName)
   716  		if err != nil {
   717  			framework.Logf("Couldn't delete PD %q, sleeping %v: %v", diskName, pdRetryPollTime, err)
   718  			continue
   719  		}
   720  		framework.Logf("Successfully deleted PD %q.", diskName)
   721  		return nil
   722  	}
   723  }
   724  
   725  func createPD(zone string) (string, error) {
   726  	if zone == "" {
   727  		zone = framework.TestContext.CloudConfig.Zone
   728  	}
   729  	return framework.TestContext.CloudConfig.Provider.CreatePD(zone)
   730  }
   731  
   732  func deletePD(pdName string) error {
   733  	return framework.TestContext.CloudConfig.Provider.DeletePD(pdName)
   734  }
   735  
   736  // WaitForPVClaimBoundPhase waits until all pvcs phase set to bound
   737  func WaitForPVClaimBoundPhase(ctx context.Context, client clientset.Interface, pvclaims []*v1.PersistentVolumeClaim, timeout time.Duration) ([]*v1.PersistentVolume, error) {
   738  	persistentvolumes := make([]*v1.PersistentVolume, len(pvclaims))
   739  
   740  	for index, claim := range pvclaims {
   741  		err := WaitForPersistentVolumeClaimPhase(ctx, v1.ClaimBound, client, claim.Namespace, claim.Name, framework.Poll, timeout)
   742  		if err != nil {
   743  			return persistentvolumes, err
   744  		}
   745  		// Get new copy of the claim
   746  		claim, err = client.CoreV1().PersistentVolumeClaims(claim.Namespace).Get(ctx, claim.Name, metav1.GetOptions{})
   747  		if err != nil {
   748  			return persistentvolumes, fmt.Errorf("PVC Get API error: %w", err)
   749  		}
   750  		// Get the bounded PV
   751  		persistentvolumes[index], err = client.CoreV1().PersistentVolumes().Get(ctx, claim.Spec.VolumeName, metav1.GetOptions{})
   752  		if err != nil {
   753  			return persistentvolumes, fmt.Errorf("PV Get API error: %w", err)
   754  		}
   755  	}
   756  	return persistentvolumes, nil
   757  }
   758  
   759  // WaitForPersistentVolumePhase waits for a PersistentVolume to be in a specific phase or until timeout occurs, whichever comes first.
   760  func WaitForPersistentVolumePhase(ctx context.Context, phase v1.PersistentVolumePhase, c clientset.Interface, pvName string, poll, timeout time.Duration) error {
   761  	framework.Logf("Waiting up to %v for PersistentVolume %s to have phase %s", timeout, pvName, phase)
   762  	for start := time.Now(); time.Since(start) < timeout; time.Sleep(poll) {
   763  		pv, err := c.CoreV1().PersistentVolumes().Get(ctx, pvName, metav1.GetOptions{})
   764  		if err != nil {
   765  			framework.Logf("Get persistent volume %s in failed, ignoring for %v: %v", pvName, poll, err)
   766  			continue
   767  		}
   768  		if pv.Status.Phase == phase {
   769  			framework.Logf("PersistentVolume %s found and phase=%s (%v)", pvName, phase, time.Since(start))
   770  			return nil
   771  		}
   772  		framework.Logf("PersistentVolume %s found but phase is %s instead of %s.", pvName, pv.Status.Phase, phase)
   773  	}
   774  	return fmt.Errorf("PersistentVolume %s not in phase %s within %v", pvName, phase, timeout)
   775  }
   776  
   777  // WaitForPersistentVolumeClaimPhase waits for a PersistentVolumeClaim to be in a specific phase or until timeout occurs, whichever comes first.
   778  func WaitForPersistentVolumeClaimPhase(ctx context.Context, phase v1.PersistentVolumeClaimPhase, c clientset.Interface, ns string, pvcName string, poll, timeout time.Duration) error {
   779  	return WaitForPersistentVolumeClaimsPhase(ctx, phase, c, ns, []string{pvcName}, poll, timeout, true)
   780  }
   781  
   782  // WaitForPersistentVolumeClaimsPhase waits for any (if matchAny is true) or all (if matchAny is false) PersistentVolumeClaims
   783  // to be in a specific phase or until timeout occurs, whichever comes first.
   784  func WaitForPersistentVolumeClaimsPhase(ctx context.Context, phase v1.PersistentVolumeClaimPhase, c clientset.Interface, ns string, pvcNames []string, poll, timeout time.Duration, matchAny bool) error {
   785  	if len(pvcNames) == 0 {
   786  		return fmt.Errorf("Incorrect parameter: Need at least one PVC to track. Found 0")
   787  	}
   788  	framework.Logf("Waiting up to timeout=%v for PersistentVolumeClaims %v to have phase %s", timeout, pvcNames, phase)
   789  	for start := time.Now(); time.Since(start) < timeout; time.Sleep(poll) {
   790  		phaseFoundInAllClaims := true
   791  		for _, pvcName := range pvcNames {
   792  			pvc, err := c.CoreV1().PersistentVolumeClaims(ns).Get(ctx, pvcName, metav1.GetOptions{})
   793  			if err != nil {
   794  				framework.Logf("Failed to get claim %q, retrying in %v. Error: %v", pvcName, poll, err)
   795  				phaseFoundInAllClaims = false
   796  				break
   797  			}
   798  			if pvc.Status.Phase == phase {
   799  				framework.Logf("PersistentVolumeClaim %s found and phase=%s (%v)", pvcName, phase, time.Since(start))
   800  				if matchAny {
   801  					return nil
   802  				}
   803  			} else {
   804  				framework.Logf("PersistentVolumeClaim %s found but phase is %s instead of %s.", pvcName, pvc.Status.Phase, phase)
   805  				phaseFoundInAllClaims = false
   806  			}
   807  		}
   808  		if phaseFoundInAllClaims {
   809  			return nil
   810  		}
   811  	}
   812  	return fmt.Errorf("PersistentVolumeClaims %v not all in phase %s within %v", pvcNames, phase, timeout)
   813  }
   814  
   815  // CreatePVSource creates a PV source.
   816  func CreatePVSource(ctx context.Context, zone string) (*v1.PersistentVolumeSource, error) {
   817  	diskName, err := CreatePDWithRetryAndZone(ctx, zone)
   818  	if err != nil {
   819  		return nil, err
   820  	}
   821  	return framework.TestContext.CloudConfig.Provider.CreatePVSource(ctx, zone, diskName)
   822  }
   823  
   824  // DeletePVSource deletes a PV source.
   825  func DeletePVSource(ctx context.Context, pvSource *v1.PersistentVolumeSource) error {
   826  	return framework.TestContext.CloudConfig.Provider.DeletePVSource(ctx, pvSource)
   827  }
   828  
   829  // GetDefaultStorageClassName returns default storageClass or return error
   830  func GetDefaultStorageClassName(ctx context.Context, c clientset.Interface) (string, error) {
   831  	list, err := c.StorageV1().StorageClasses().List(ctx, metav1.ListOptions{})
   832  	if err != nil {
   833  		return "", fmt.Errorf("Error listing storage classes: %w", err)
   834  	}
   835  	var scName string
   836  	for _, sc := range list.Items {
   837  		if util.IsDefaultAnnotation(sc.ObjectMeta) {
   838  			if len(scName) != 0 {
   839  				return "", fmt.Errorf("Multiple default storage classes found: %q and %q", scName, sc.Name)
   840  			}
   841  			scName = sc.Name
   842  		}
   843  	}
   844  	if len(scName) == 0 {
   845  		return "", fmt.Errorf("No default storage class found")
   846  	}
   847  	framework.Logf("Default storage class: %q", scName)
   848  	return scName, nil
   849  }
   850  
   851  // SkipIfNoDefaultStorageClass skips tests if no default SC can be found.
   852  func SkipIfNoDefaultStorageClass(ctx context.Context, c clientset.Interface) {
   853  	_, err := GetDefaultStorageClassName(ctx, c)
   854  	if err != nil {
   855  		e2eskipper.Skipf("error finding default storageClass : %v", err)
   856  	}
   857  }
   858  
   859  // WaitForPersistentVolumeDeleted waits for a PersistentVolume to get deleted or until timeout occurs, whichever comes first.
   860  func WaitForPersistentVolumeDeleted(ctx context.Context, c clientset.Interface, pvName string, poll, timeout time.Duration) error {
   861  	framework.Logf("Waiting up to %v for PersistentVolume %s to get deleted", timeout, pvName)
   862  	for start := time.Now(); time.Since(start) < timeout; time.Sleep(poll) {
   863  		pv, err := c.CoreV1().PersistentVolumes().Get(ctx, pvName, metav1.GetOptions{})
   864  		if err == nil {
   865  			framework.Logf("PersistentVolume %s found and phase=%s (%v)", pvName, pv.Status.Phase, time.Since(start))
   866  			continue
   867  		}
   868  		if apierrors.IsNotFound(err) {
   869  			framework.Logf("PersistentVolume %s was removed", pvName)
   870  			return nil
   871  		}
   872  		framework.Logf("Get persistent volume %s in failed, ignoring for %v: %v", pvName, poll, err)
   873  	}
   874  	return fmt.Errorf("PersistentVolume %s still exists within %v", pvName, timeout)
   875  }
   876  
   877  // WaitForPVCFinalizer waits for a finalizer to be added to a PVC in a given namespace.
   878  func WaitForPVCFinalizer(ctx context.Context, cs clientset.Interface, name, namespace, finalizer string, poll, timeout time.Duration) error {
   879  	var (
   880  		err error
   881  		pvc *v1.PersistentVolumeClaim
   882  	)
   883  	framework.Logf("Waiting up to %v for PersistentVolumeClaim %s/%s to contain finalizer %s", timeout, namespace, name, finalizer)
   884  	if successful := utils.WaitUntil(poll, timeout, func() bool {
   885  		pvc, err = cs.CoreV1().PersistentVolumeClaims(namespace).Get(ctx, name, metav1.GetOptions{})
   886  		if err != nil {
   887  			framework.Logf("Failed to get PersistentVolumeClaim %s/%s with err: %v. Will retry in %v", name, namespace, err, timeout)
   888  			return false
   889  		}
   890  		for _, f := range pvc.Finalizers {
   891  			if f == finalizer {
   892  				return true
   893  			}
   894  		}
   895  		return false
   896  	}); successful {
   897  		return nil
   898  	}
   899  	if err == nil {
   900  		err = fmt.Errorf("finalizer %s not added to pvc %s/%s", finalizer, namespace, name)
   901  	}
   902  	return err
   903  }
   904  
   905  // GetDefaultFSType returns the default fsType
   906  func GetDefaultFSType() string {
   907  	if framework.NodeOSDistroIs("windows") {
   908  		return "ntfs"
   909  	}
   910  	return "ext4"
   911  }