k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/scheduler/framework/plugins/volumebinding/assume_cache_test.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 volumebinding
    18  
    19  import (
    20  	"fmt"
    21  	"testing"
    22  
    23  	v1 "k8s.io/api/core/v1"
    24  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    25  	"k8s.io/component-helpers/storage/volume"
    26  	"k8s.io/klog/v2/ktesting"
    27  	"k8s.io/kubernetes/pkg/scheduler/util/assumecache"
    28  )
    29  
    30  func verifyListPVs(t *testing.T, cache *PVAssumeCache, expectedPVs map[string]*v1.PersistentVolume, storageClassName string) {
    31  	pvList := cache.ListPVs(storageClassName)
    32  	if len(pvList) != len(expectedPVs) {
    33  		t.Errorf("ListPVs() returned %v PVs, expected %v", len(pvList), len(expectedPVs))
    34  	}
    35  	for _, pv := range pvList {
    36  		expectedPV, ok := expectedPVs[pv.Name]
    37  		if !ok {
    38  			t.Errorf("ListPVs() returned unexpected PV %q", pv.Name)
    39  		}
    40  		if expectedPV != pv {
    41  			t.Errorf("ListPVs() returned PV %p, expected %p", pv, expectedPV)
    42  		}
    43  	}
    44  }
    45  
    46  func verifyPV(cache *PVAssumeCache, name string, expectedPV *v1.PersistentVolume) error {
    47  	pv, err := cache.GetPV(name)
    48  	if err != nil {
    49  		return err
    50  	}
    51  	if pv != expectedPV {
    52  		return fmt.Errorf("GetPV() returned %p, expected %p", pv, expectedPV)
    53  	}
    54  	return nil
    55  }
    56  
    57  func TestAssumePV(t *testing.T) {
    58  	logger, _ := ktesting.NewTestContext(t)
    59  	scenarios := map[string]struct {
    60  		oldPV         *v1.PersistentVolume
    61  		newPV         *v1.PersistentVolume
    62  		shouldSucceed bool
    63  	}{
    64  		"success-same-version": {
    65  			oldPV:         makePV("pv1", "").withVersion("5").PersistentVolume,
    66  			newPV:         makePV("pv1", "").withVersion("5").PersistentVolume,
    67  			shouldSucceed: true,
    68  		},
    69  		"success-storageclass-same-version": {
    70  			oldPV:         makePV("pv1", "class1").withVersion("5").PersistentVolume,
    71  			newPV:         makePV("pv1", "class1").withVersion("5").PersistentVolume,
    72  			shouldSucceed: true,
    73  		},
    74  		"success-new-higher-version": {
    75  			oldPV:         makePV("pv1", "").withVersion("5").PersistentVolume,
    76  			newPV:         makePV("pv1", "").withVersion("6").PersistentVolume,
    77  			shouldSucceed: true,
    78  		},
    79  		"fail-old-not-found": {
    80  			oldPV:         makePV("pv2", "").withVersion("5").PersistentVolume,
    81  			newPV:         makePV("pv1", "").withVersion("5").PersistentVolume,
    82  			shouldSucceed: false,
    83  		},
    84  		"fail-new-lower-version": {
    85  			oldPV:         makePV("pv1", "").withVersion("5").PersistentVolume,
    86  			newPV:         makePV("pv1", "").withVersion("4").PersistentVolume,
    87  			shouldSucceed: false,
    88  		},
    89  		"fail-new-bad-version": {
    90  			oldPV:         makePV("pv1", "").withVersion("5").PersistentVolume,
    91  			newPV:         makePV("pv1", "").withVersion("a").PersistentVolume,
    92  			shouldSucceed: false,
    93  		},
    94  		"fail-old-bad-version": {
    95  			oldPV:         makePV("pv1", "").withVersion("a").PersistentVolume,
    96  			newPV:         makePV("pv1", "").withVersion("5").PersistentVolume,
    97  			shouldSucceed: false,
    98  		},
    99  	}
   100  
   101  	for name, scenario := range scenarios {
   102  		cache := NewPVAssumeCache(logger, nil)
   103  
   104  		// Add oldPV to cache
   105  		assumecache.AddTestObject(cache.AssumeCache, scenario.oldPV)
   106  		if err := verifyPV(cache, scenario.oldPV.Name, scenario.oldPV); err != nil {
   107  			t.Errorf("Failed to GetPV() after initial update: %v", err)
   108  			continue
   109  		}
   110  
   111  		// Assume newPV
   112  		err := cache.Assume(scenario.newPV)
   113  		if scenario.shouldSucceed && err != nil {
   114  			t.Errorf("Test %q failed: Assume() returned error %v", name, err)
   115  		}
   116  		if !scenario.shouldSucceed && err == nil {
   117  			t.Errorf("Test %q failed: Assume() returned success but expected error", name)
   118  		}
   119  
   120  		// Check that GetPV returns correct PV
   121  		expectedPV := scenario.newPV
   122  		if !scenario.shouldSucceed {
   123  			expectedPV = scenario.oldPV
   124  		}
   125  		if err := verifyPV(cache, scenario.oldPV.Name, expectedPV); err != nil {
   126  			t.Errorf("Failed to GetPV() after initial update: %v", err)
   127  		}
   128  	}
   129  }
   130  
   131  func TestRestorePV(t *testing.T) {
   132  	logger, _ := ktesting.NewTestContext(t)
   133  	cache := NewPVAssumeCache(logger, nil)
   134  
   135  	oldPV := makePV("pv1", "").withVersion("5").PersistentVolume
   136  	newPV := makePV("pv1", "").withVersion("5").PersistentVolume
   137  
   138  	// Restore PV that doesn't exist
   139  	cache.Restore("nothing")
   140  
   141  	// Add oldPV to cache
   142  	assumecache.AddTestObject(cache.AssumeCache, oldPV)
   143  	if err := verifyPV(cache, oldPV.Name, oldPV); err != nil {
   144  		t.Fatalf("Failed to GetPV() after initial update: %v", err)
   145  	}
   146  
   147  	// Restore PV
   148  	cache.Restore(oldPV.Name)
   149  	if err := verifyPV(cache, oldPV.Name, oldPV); err != nil {
   150  		t.Fatalf("Failed to GetPV() after initial restore: %v", err)
   151  	}
   152  
   153  	// Assume newPV
   154  	if err := cache.Assume(newPV); err != nil {
   155  		t.Fatalf("Assume() returned error %v", err)
   156  	}
   157  	if err := verifyPV(cache, oldPV.Name, newPV); err != nil {
   158  		t.Fatalf("Failed to GetPV() after Assume: %v", err)
   159  	}
   160  
   161  	// Restore PV
   162  	cache.Restore(oldPV.Name)
   163  	if err := verifyPV(cache, oldPV.Name, oldPV); err != nil {
   164  		t.Fatalf("Failed to GetPV() after restore: %v", err)
   165  	}
   166  }
   167  
   168  func TestBasicPVCache(t *testing.T) {
   169  	logger, _ := ktesting.NewTestContext(t)
   170  	cache := NewPVAssumeCache(logger, nil)
   171  
   172  	// Get object that doesn't exist
   173  	pv, err := cache.GetPV("nothere")
   174  	if err == nil {
   175  		t.Errorf("GetPV() returned unexpected success")
   176  	}
   177  	if pv != nil {
   178  		t.Errorf("GetPV() returned unexpected PV %q", pv.Name)
   179  	}
   180  
   181  	// Add a bunch of PVs
   182  	pvs := map[string]*v1.PersistentVolume{}
   183  	for i := 0; i < 10; i++ {
   184  		pv := makePV(fmt.Sprintf("test-pv%v", i), "").withVersion("1").PersistentVolume
   185  		pvs[pv.Name] = pv
   186  		assumecache.AddTestObject(cache.AssumeCache, pv)
   187  	}
   188  
   189  	// List them
   190  	verifyListPVs(t, cache, pvs, "")
   191  
   192  	// Update a PV
   193  	updatedPV := makePV("test-pv3", "").withVersion("2").PersistentVolume
   194  	pvs[updatedPV.Name] = updatedPV
   195  	assumecache.UpdateTestObject(cache.AssumeCache, updatedPV)
   196  
   197  	// List them
   198  	verifyListPVs(t, cache, pvs, "")
   199  
   200  	// Delete a PV
   201  	deletedPV := pvs["test-pv7"]
   202  	delete(pvs, deletedPV.Name)
   203  	assumecache.DeleteTestObject(cache.AssumeCache, deletedPV)
   204  
   205  	// List them
   206  	verifyListPVs(t, cache, pvs, "")
   207  }
   208  
   209  func TestPVCacheWithStorageClasses(t *testing.T) {
   210  	logger, _ := ktesting.NewTestContext(t)
   211  	cache := NewPVAssumeCache(logger, nil)
   212  
   213  	// Add a bunch of PVs
   214  	pvs1 := map[string]*v1.PersistentVolume{}
   215  	for i := 0; i < 10; i++ {
   216  		pv := makePV(fmt.Sprintf("test-pv%v", i), "class1").withVersion("1").PersistentVolume
   217  		pvs1[pv.Name] = pv
   218  		assumecache.AddTestObject(cache.AssumeCache, pv)
   219  	}
   220  
   221  	// Add a bunch of PVs
   222  	pvs2 := map[string]*v1.PersistentVolume{}
   223  	for i := 0; i < 10; i++ {
   224  		pv := makePV(fmt.Sprintf("test2-pv%v", i), "class2").withVersion("1").PersistentVolume
   225  		pvs2[pv.Name] = pv
   226  		assumecache.AddTestObject(cache.AssumeCache, pv)
   227  	}
   228  
   229  	// List them
   230  	verifyListPVs(t, cache, pvs1, "class1")
   231  	verifyListPVs(t, cache, pvs2, "class2")
   232  
   233  	// Update a PV
   234  	updatedPV := makePV("test-pv3", "class1").withVersion("2").PersistentVolume
   235  	pvs1[updatedPV.Name] = updatedPV
   236  	assumecache.UpdateTestObject(cache.AssumeCache, updatedPV)
   237  
   238  	// List them
   239  	verifyListPVs(t, cache, pvs1, "class1")
   240  	verifyListPVs(t, cache, pvs2, "class2")
   241  
   242  	// Delete a PV
   243  	deletedPV := pvs1["test-pv7"]
   244  	delete(pvs1, deletedPV.Name)
   245  	assumecache.DeleteTestObject(cache.AssumeCache, deletedPV)
   246  
   247  	// List them
   248  	verifyListPVs(t, cache, pvs1, "class1")
   249  	verifyListPVs(t, cache, pvs2, "class2")
   250  }
   251  
   252  func TestAssumeUpdatePVCache(t *testing.T) {
   253  	logger, _ := ktesting.NewTestContext(t)
   254  	cache := NewPVAssumeCache(logger, nil)
   255  
   256  	pvName := "test-pv0"
   257  
   258  	// Add a PV
   259  	pv := makePV(pvName, "").withVersion("1").PersistentVolume
   260  	assumecache.AddTestObject(cache.AssumeCache, pv)
   261  	if err := verifyPV(cache, pvName, pv); err != nil {
   262  		t.Fatalf("failed to get PV: %v", err)
   263  	}
   264  
   265  	// Assume PV
   266  	newPV := pv.DeepCopy()
   267  	newPV.Spec.ClaimRef = &v1.ObjectReference{Name: "test-claim"}
   268  	if err := cache.Assume(newPV); err != nil {
   269  		t.Fatalf("failed to assume PV: %v", err)
   270  	}
   271  	if err := verifyPV(cache, pvName, newPV); err != nil {
   272  		t.Fatalf("failed to get PV after assume: %v", err)
   273  	}
   274  
   275  	// Add old PV
   276  	assumecache.AddTestObject(cache.AssumeCache, pv)
   277  	if err := verifyPV(cache, pvName, newPV); err != nil {
   278  		t.Fatalf("failed to get PV after old PV added: %v", err)
   279  	}
   280  }
   281  
   282  func makeClaim(name, version, namespace string) *v1.PersistentVolumeClaim {
   283  	return &v1.PersistentVolumeClaim{
   284  		ObjectMeta: metav1.ObjectMeta{
   285  			Name:            name,
   286  			Namespace:       namespace,
   287  			ResourceVersion: version,
   288  			Annotations:     map[string]string{},
   289  		},
   290  	}
   291  }
   292  
   293  func verifyPVC(cache *PVCAssumeCache, pvcKey string, expectedPVC *v1.PersistentVolumeClaim) error {
   294  	pvc, err := cache.GetPVC(pvcKey)
   295  	if err != nil {
   296  		return err
   297  	}
   298  	if pvc != expectedPVC {
   299  		return fmt.Errorf("GetPVC() returned %p, expected %p", pvc, expectedPVC)
   300  	}
   301  	return nil
   302  }
   303  
   304  func TestAssumePVC(t *testing.T) {
   305  	logger, _ := ktesting.NewTestContext(t)
   306  	scenarios := map[string]struct {
   307  		oldPVC        *v1.PersistentVolumeClaim
   308  		newPVC        *v1.PersistentVolumeClaim
   309  		shouldSucceed bool
   310  	}{
   311  		"success-same-version": {
   312  			oldPVC:        makeClaim("pvc1", "5", "ns1"),
   313  			newPVC:        makeClaim("pvc1", "5", "ns1"),
   314  			shouldSucceed: true,
   315  		},
   316  		"success-new-higher-version": {
   317  			oldPVC:        makeClaim("pvc1", "5", "ns1"),
   318  			newPVC:        makeClaim("pvc1", "6", "ns1"),
   319  			shouldSucceed: true,
   320  		},
   321  		"fail-old-not-found": {
   322  			oldPVC:        makeClaim("pvc2", "5", "ns1"),
   323  			newPVC:        makeClaim("pvc1", "5", "ns1"),
   324  			shouldSucceed: false,
   325  		},
   326  		"fail-new-lower-version": {
   327  			oldPVC:        makeClaim("pvc1", "5", "ns1"),
   328  			newPVC:        makeClaim("pvc1", "4", "ns1"),
   329  			shouldSucceed: false,
   330  		},
   331  		"fail-new-bad-version": {
   332  			oldPVC:        makeClaim("pvc1", "5", "ns1"),
   333  			newPVC:        makeClaim("pvc1", "a", "ns1"),
   334  			shouldSucceed: false,
   335  		},
   336  		"fail-old-bad-version": {
   337  			oldPVC:        makeClaim("pvc1", "a", "ns1"),
   338  			newPVC:        makeClaim("pvc1", "5", "ns1"),
   339  			shouldSucceed: false,
   340  		},
   341  	}
   342  
   343  	for name, scenario := range scenarios {
   344  		cache := NewPVCAssumeCache(logger, nil)
   345  
   346  		// Add oldPVC to cache
   347  		assumecache.AddTestObject(cache.AssumeCache, scenario.oldPVC)
   348  		if err := verifyPVC(cache, getPVCName(scenario.oldPVC), scenario.oldPVC); err != nil {
   349  			t.Errorf("Failed to GetPVC() after initial update: %v", err)
   350  			continue
   351  		}
   352  
   353  		// Assume newPVC
   354  		err := cache.Assume(scenario.newPVC)
   355  		if scenario.shouldSucceed && err != nil {
   356  			t.Errorf("Test %q failed: Assume() returned error %v", name, err)
   357  		}
   358  		if !scenario.shouldSucceed && err == nil {
   359  			t.Errorf("Test %q failed: Assume() returned success but expected error", name)
   360  		}
   361  
   362  		// Check that GetPVC returns correct PVC
   363  		expectedPV := scenario.newPVC
   364  		if !scenario.shouldSucceed {
   365  			expectedPV = scenario.oldPVC
   366  		}
   367  		if err := verifyPVC(cache, getPVCName(scenario.oldPVC), expectedPV); err != nil {
   368  			t.Errorf("Failed to GetPVC() after initial update: %v", err)
   369  		}
   370  	}
   371  }
   372  
   373  func TestRestorePVC(t *testing.T) {
   374  	logger, _ := ktesting.NewTestContext(t)
   375  	cache := NewPVCAssumeCache(logger, nil)
   376  
   377  	oldPVC := makeClaim("pvc1", "5", "ns1")
   378  	newPVC := makeClaim("pvc1", "5", "ns1")
   379  
   380  	// Restore PVC that doesn't exist
   381  	cache.Restore("nothing")
   382  
   383  	// Add oldPVC to cache
   384  	assumecache.AddTestObject(cache.AssumeCache, oldPVC)
   385  	if err := verifyPVC(cache, getPVCName(oldPVC), oldPVC); err != nil {
   386  		t.Fatalf("Failed to GetPVC() after initial update: %v", err)
   387  	}
   388  
   389  	// Restore PVC
   390  	cache.Restore(getPVCName(oldPVC))
   391  	if err := verifyPVC(cache, getPVCName(oldPVC), oldPVC); err != nil {
   392  		t.Fatalf("Failed to GetPVC() after initial restore: %v", err)
   393  	}
   394  
   395  	// Assume newPVC
   396  	if err := cache.Assume(newPVC); err != nil {
   397  		t.Fatalf("Assume() returned error %v", err)
   398  	}
   399  	if err := verifyPVC(cache, getPVCName(oldPVC), newPVC); err != nil {
   400  		t.Fatalf("Failed to GetPVC() after Assume: %v", err)
   401  	}
   402  
   403  	// Restore PVC
   404  	cache.Restore(getPVCName(oldPVC))
   405  	if err := verifyPVC(cache, getPVCName(oldPVC), oldPVC); err != nil {
   406  		t.Fatalf("Failed to GetPVC() after restore: %v", err)
   407  	}
   408  }
   409  
   410  func TestAssumeUpdatePVCCache(t *testing.T) {
   411  	logger, _ := ktesting.NewTestContext(t)
   412  	cache := NewPVCAssumeCache(logger, nil)
   413  
   414  	pvcName := "test-pvc0"
   415  	pvcNamespace := "test-ns"
   416  
   417  	// Add a PVC
   418  	pvc := makeClaim(pvcName, "1", pvcNamespace)
   419  	assumecache.AddTestObject(cache.AssumeCache, pvc)
   420  	if err := verifyPVC(cache, getPVCName(pvc), pvc); err != nil {
   421  		t.Fatalf("failed to get PVC: %v", err)
   422  	}
   423  
   424  	// Assume PVC
   425  	newPVC := pvc.DeepCopy()
   426  	newPVC.Annotations[volume.AnnSelectedNode] = "test-node"
   427  	if err := cache.Assume(newPVC); err != nil {
   428  		t.Fatalf("failed to assume PVC: %v", err)
   429  	}
   430  	if err := verifyPVC(cache, getPVCName(pvc), newPVC); err != nil {
   431  		t.Fatalf("failed to get PVC after assume: %v", err)
   432  	}
   433  
   434  	// Add old PVC
   435  	assumecache.AddTestObject(cache.AssumeCache, pvc)
   436  	if err := verifyPVC(cache, getPVCName(pvc), newPVC); err != nil {
   437  		t.Fatalf("failed to get PVC after old PVC added: %v", err)
   438  	}
   439  }