2016-05-17 08:55:15 -04:00
/ *
2016-06-02 20:25:58 -04:00
Copyright 2016 The Kubernetes Authors .
2016-05-17 08:55:15 -04:00
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
package persistentvolume
import (
2021-04-22 14:27:59 -04:00
"context"
2019-05-17 20:15:38 -04:00
"errors"
2020-01-16 18:50:53 -05:00
"reflect"
2016-05-17 08:55:15 -04:00
"testing"
"time"
2019-09-02 09:22:06 -04:00
v1 "k8s.io/api/core/v1"
2017-11-08 16:09:45 -05:00
storagev1 "k8s.io/api/storage/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2020-08-03 11:55:16 -04:00
"k8s.io/apimachinery/pkg/util/wait"
2017-02-16 10:08:23 -05:00
"k8s.io/apimachinery/pkg/watch"
2020-01-16 18:50:53 -05:00
utilfeature "k8s.io/apiserver/pkg/util/feature"
2017-06-23 16:56:37 -04:00
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes/fake"
2019-05-17 20:15:38 -04:00
storagelisters "k8s.io/client-go/listers/storage/v1"
2017-02-16 10:08:23 -05:00
core "k8s.io/client-go/testing"
2017-01-24 09:11:51 -05:00
"k8s.io/client-go/tools/cache"
2020-01-16 18:50:53 -05:00
featuregatetesting "k8s.io/component-base/featuregate/testing"
2021-08-18 03:12:42 -04:00
"k8s.io/component-helpers/storage/volume"
2019-09-12 20:59:06 -04:00
csitrans "k8s.io/csi-translation-lib"
2022-11-03 05:19:04 -04:00
"k8s.io/klog/v2/ktesting"
2017-02-16 10:08:23 -05:00
"k8s.io/kubernetes/pkg/controller"
2019-03-17 21:55:28 -04:00
pvtesting "k8s.io/kubernetes/pkg/controller/volume/persistentvolume/testing"
2020-01-16 18:50:53 -05:00
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/volume/csimigration"
2021-01-27 18:54:03 -05:00
"k8s.io/kubernetes/pkg/volume/util"
2018-11-15 21:57:14 -05:00
)
2016-05-17 08:55:15 -04:00
// Test the real controller methods (add/update/delete claim/volume) with
// a fake API server.
// There is no controller API to 'initiate syncAll now', therefore these tests
// can't reliably simulate periodic sync of volumes/claims - it would be
// either very timing-sensitive or slow to wait for real periodic sync.
func TestControllerSync ( t * testing . T ) {
2022-03-31 11:46:53 -04:00
// Default enable the HonorPVReclaimPolicy feature gate.
defer featuregatetesting . SetFeatureGateDuringTest ( t , utilfeature . DefaultFeatureGate , features . HonorPVReclaimPolicy , true ) ( )
2016-05-17 08:55:15 -04:00
tests := [ ] controllerTest {
// [Unit test set 5] - controller tests.
// We test the controller as if
// it was connected to real API server, i.e. we call add/update/delete
// Claim/Volume methods. Also, all changes to volumes and claims are
// sent to add/update/delete Claim/Volume as real controller would do.
{
// addClaim gets a new claim. Check it's bound to a volume.
2022-08-04 09:30:23 -04:00
name : "5-2 - complete bind" ,
initialVolumes : newVolumeArray ( "volume5-2" , "1Gi" , "" , "" , v1 . VolumeAvailable , v1 . PersistentVolumeReclaimRetain , classEmpty ) ,
expectedVolumes : newVolumeArray ( "volume5-2" , "1Gi" , "uid5-2" , "claim5-2" , v1 . VolumeBound , v1 . PersistentVolumeReclaimRetain , classEmpty , volume . AnnBoundByController ) ,
initialClaims : noclaims , /* added in testAddClaim5_2 */
expectedClaims : newClaimArray ( "claim5-2" , "uid5-2" , "1Gi" , "volume5-2" , v1 . ClaimBound , nil , volume . AnnBoundByController , volume . AnnBindCompleted ) ,
expectedEvents : noevents ,
errors : noerrors ,
2016-05-17 08:55:15 -04:00
// Custom test function that generates an add event
2022-08-04 09:30:23 -04:00
test : func ( ctrl * PersistentVolumeController , reactor * pvtesting . VolumeReactor , test controllerTest ) error {
2017-03-02 04:23:56 -05:00
claim := newClaim ( "claim5-2" , "uid5-2" , "1Gi" , "" , v1 . ClaimPending , nil )
2019-03-17 21:55:28 -04:00
reactor . AddClaimEvent ( claim )
2016-05-17 08:55:15 -04:00
return nil
} ,
} ,
2020-10-28 05:40:42 -04:00
{
2022-08-04 09:30:23 -04:00
name : "5-2-2 - complete bind when PV and PVC both exist" ,
initialVolumes : newVolumeArray ( "volume5-2" , "1Gi" , "" , "" , v1 . VolumeAvailable , v1 . PersistentVolumeReclaimRetain , classEmpty ) ,
expectedVolumes : newVolumeArray ( "volume5-2" , "1Gi" , "uid5-2" , "claim5-2" , v1 . VolumeBound , v1 . PersistentVolumeReclaimRetain , classEmpty , volume . AnnBoundByController ) ,
initialClaims : newClaimArray ( "claim5-2" , "uid5-2" , "1Gi" , "" , v1 . ClaimPending , nil ) ,
expectedClaims : newClaimArray ( "claim5-2" , "uid5-2" , "1Gi" , "volume5-2" , v1 . ClaimBound , nil , volume . AnnBoundByController , volume . AnnBindCompleted ) ,
expectedEvents : noevents ,
errors : noerrors ,
test : func ( ctrl * PersistentVolumeController , reactor * pvtesting . VolumeReactor , test controllerTest ) error {
2020-10-28 05:40:42 -04:00
return nil
} ,
} ,
2021-01-27 18:54:03 -05:00
{
2022-08-04 09:30:23 -04:00
name : "5-2-3 - complete bind when PV and PVC both exist and PV has AnnPreResizeCapacity annotation" ,
initialVolumes : volumesWithAnnotation ( util . AnnPreResizeCapacity , "1Gi" , newVolumeArray ( "volume5-2" , "2Gi" , "" , "" , v1 . VolumeAvailable , v1 . PersistentVolumeReclaimRetain , classEmpty , volume . AnnBoundByController ) ) ,
expectedVolumes : volumesWithAnnotation ( util . AnnPreResizeCapacity , "1Gi" , newVolumeArray ( "volume5-2" , "2Gi" , "uid5-2" , "claim5-2" , v1 . VolumeBound , v1 . PersistentVolumeReclaimRetain , classEmpty , volume . AnnBoundByController ) ) ,
initialClaims : withExpectedCapacity ( "2Gi" , newClaimArray ( "claim5-2" , "uid5-2" , "2Gi" , "" , v1 . ClaimPending , nil ) ) ,
expectedClaims : withExpectedCapacity ( "1Gi" , newClaimArray ( "claim5-2" , "uid5-2" , "2Gi" , "volume5-2" , v1 . ClaimBound , nil , volume . AnnBoundByController , volume . AnnBindCompleted ) ) ,
expectedEvents : noevents ,
errors : noerrors ,
test : func ( ctrl * PersistentVolumeController , reactor * pvtesting . VolumeReactor , test controllerTest ) error {
2021-01-27 18:54:03 -05:00
return nil
} ,
} ,
2016-05-17 08:55:15 -04:00
{
// deleteClaim with a bound claim makes bound volume released.
2022-08-04 09:30:23 -04:00
name : "5-3 - delete claim" ,
initialVolumes : newVolumeArray ( "volume5-3" , "10Gi" , "uid5-3" , "claim5-3" , v1 . VolumeBound , v1 . PersistentVolumeReclaimRetain , classEmpty , volume . AnnBoundByController ) ,
expectedVolumes : newVolumeArray ( "volume5-3" , "10Gi" , "uid5-3" , "claim5-3" , v1 . VolumeReleased , v1 . PersistentVolumeReclaimRetain , classEmpty , volume . AnnBoundByController ) ,
initialClaims : newClaimArray ( "claim5-3" , "uid5-3" , "1Gi" , "volume5-3" , v1 . ClaimBound , nil , volume . AnnBoundByController , volume . AnnBindCompleted ) ,
expectedClaims : noclaims ,
expectedEvents : noevents ,
errors : noerrors ,
2016-05-17 08:55:15 -04:00
// Custom test function that generates a delete event
2022-08-04 09:30:23 -04:00
test : func ( ctrl * PersistentVolumeController , reactor * pvtesting . VolumeReactor , test controllerTest ) error {
2016-05-17 08:55:15 -04:00
obj := ctrl . claims . List ( ) [ 0 ]
2016-11-18 15:50:17 -05:00
claim := obj . ( * v1 . PersistentVolumeClaim )
2019-03-17 21:55:28 -04:00
reactor . DeleteClaimEvent ( claim )
2016-05-17 08:55:15 -04:00
return nil
} ,
} ,
{
// deleteVolume with a bound volume. Check the claim is Lost.
2022-08-04 09:30:23 -04:00
name : "5-4 - delete volume" ,
initialVolumes : newVolumeArray ( "volume5-4" , "1Gi" , "uid5-4" , "claim5-4" , v1 . VolumeBound , v1 . PersistentVolumeReclaimRetain , classEmpty ) ,
expectedVolumes : novolumes ,
initialClaims : newClaimArray ( "claim5-4" , "uid5-4" , "1Gi" , "volume5-4" , v1 . ClaimBound , nil , volume . AnnBoundByController , volume . AnnBindCompleted ) ,
expectedClaims : newClaimArray ( "claim5-4" , "uid5-4" , "1Gi" , "volume5-4" , v1 . ClaimLost , nil , volume . AnnBoundByController , volume . AnnBindCompleted ) ,
expectedEvents : [ ] string { "Warning ClaimLost" } ,
errors : noerrors ,
2016-05-17 08:55:15 -04:00
// Custom test function that generates a delete event
2022-08-04 09:30:23 -04:00
test : func ( ctrl * PersistentVolumeController , reactor * pvtesting . VolumeReactor , test controllerTest ) error {
2016-05-17 08:55:15 -04:00
obj := ctrl . volumes . store . List ( ) [ 0 ]
2016-11-18 15:50:17 -05:00
volume := obj . ( * v1 . PersistentVolume )
2019-03-17 21:55:28 -04:00
reactor . DeleteVolumeEvent ( volume )
2016-05-17 08:55:15 -04:00
return nil
} ,
} ,
2019-05-17 20:15:38 -04:00
{
// deleteClaim with a bound claim makes bound volume released with external deleter.
// delete the corresponding volume from apiserver, and report latency metric
2022-08-04 09:30:23 -04:00
name : "5-5 - delete claim and delete volume report metric" ,
initialVolumes : volumesWithAnnotation ( volume . AnnDynamicallyProvisioned , "gcr.io/vendor-csi" ,
2021-08-18 03:12:42 -04:00
newVolumeArray ( "volume5-5" , "10Gi" , "uid5-5" , "claim5-5" , v1 . VolumeBound , v1 . PersistentVolumeReclaimDelete , classExternal , volume . AnnBoundByController ) ) ,
2022-08-04 09:30:23 -04:00
expectedVolumes : novolumes ,
initialClaims : claimWithAnnotation ( volume . AnnStorageProvisioner , "gcr.io/vendor-csi" ,
2021-08-18 03:12:42 -04:00
newClaimArray ( "claim5-5" , "uid5-5" , "1Gi" , "volume5-5" , v1 . ClaimBound , & classExternal , volume . AnnBoundByController , volume . AnnBindCompleted ) ) ,
2022-08-04 09:30:23 -04:00
expectedClaims : noclaims ,
expectedEvents : noevents ,
errors : noerrors ,
2019-05-17 20:15:38 -04:00
// Custom test function that generates a delete claim event which should have been caught by
// "deleteClaim" to remove the claim from controller's cache, after that, a volume deleted
// event will be generated to trigger "deleteVolume" call for metric reporting
2022-08-04 09:30:23 -04:00
test : func ( ctrl * PersistentVolumeController , reactor * pvtesting . VolumeReactor , test controllerTest ) error {
2021-08-18 03:12:42 -04:00
test . initialVolumes [ 0 ] . Annotations [ volume . AnnDynamicallyProvisioned ] = "gcr.io/vendor-csi"
2019-05-17 20:15:38 -04:00
obj := ctrl . claims . List ( ) [ 0 ]
claim := obj . ( * v1 . PersistentVolumeClaim )
reactor . DeleteClaimEvent ( claim )
2020-08-04 04:03:33 -04:00
err := wait . Poll ( 10 * time . Millisecond , wait . ForeverTestTimeout , func ( ) ( bool , error ) {
return len ( ctrl . claims . ListKeys ( ) ) == 0 , nil
} )
if err != nil {
return err
2019-05-17 20:15:38 -04:00
}
// claim has been removed from controller's cache, generate a volume deleted event
volume := ctrl . volumes . store . List ( ) [ 0 ] . ( * v1 . PersistentVolume )
reactor . DeleteVolumeEvent ( volume )
return nil
} ,
} ,
{
// deleteClaim with a bound claim makes bound volume released with external deleter pending
// there should be an entry in operation timestamps cache in controller
2022-08-04 09:30:23 -04:00
name : "5-6 - delete claim and waiting for external volume deletion" ,
initialVolumes : volumesWithAnnotation ( volume . AnnDynamicallyProvisioned , "gcr.io/vendor-csi" , [ ] * v1 . PersistentVolume { newExternalProvisionedVolume ( "volume5-6" , "10Gi" , "uid5-6" , "claim5-6" , v1 . VolumeBound , v1 . PersistentVolumeReclaimDelete , classExternal , "fake.driver.csi" , nil , volume . AnnBoundByController ) } ) ,
expectedVolumes : volumesWithAnnotation ( volume . AnnDynamicallyProvisioned , "gcr.io/vendor-csi" , [ ] * v1 . PersistentVolume { newExternalProvisionedVolume ( "volume5-6" , "10Gi" , "uid5-6" , "claim5-6" , v1 . VolumeReleased , v1 . PersistentVolumeReclaimDelete , classExternal , "fake.driver.csi" , nil , volume . AnnBoundByController ) } ) ,
initialClaims : claimWithAnnotation ( volume . AnnStorageProvisioner , "gcr.io/vendor-csi" ,
2021-08-18 03:12:42 -04:00
newClaimArray ( "claim5-6" , "uid5-6" , "1Gi" , "volume5-6" , v1 . ClaimBound , & classExternal , volume . AnnBoundByController , volume . AnnBindCompleted ) ) ,
2022-08-04 09:30:23 -04:00
expectedClaims : noclaims ,
expectedEvents : noevents ,
errors : noerrors ,
2019-05-17 20:15:38 -04:00
// Custom test function that generates a delete claim event which should have been caught by
// "deleteClaim" to remove the claim from controller's cache and mark bound volume to be released
2022-08-04 09:30:23 -04:00
test : func ( ctrl * PersistentVolumeController , reactor * pvtesting . VolumeReactor , test controllerTest ) error {
2019-05-17 20:15:38 -04:00
// should have been provisioned by external provisioner
obj := ctrl . claims . List ( ) [ 0 ]
claim := obj . ( * v1 . PersistentVolumeClaim )
reactor . DeleteClaimEvent ( claim )
// wait until claim is cleared from cache, i.e., deleteClaim is called
2020-08-04 04:03:33 -04:00
err := wait . Poll ( 10 * time . Millisecond , wait . ForeverTestTimeout , func ( ) ( bool , error ) {
return len ( ctrl . claims . ListKeys ( ) ) == 0 , nil
} )
if err != nil {
return err
2019-05-17 20:15:38 -04:00
}
2020-08-03 11:55:16 -04:00
// wait for volume delete operation to appear once volumeWorker() runs
return wait . PollImmediate ( 10 * time . Millisecond , wait . ForeverTestTimeout , func ( ) ( bool , error ) {
// make sure the operation timestamp cache is NOT empty
if ctrl . operationTimestamps . Has ( "volume5-6" ) {
return true , nil
}
t . Logf ( "missing volume5-6 from timestamp cache, will retry" )
return false , nil
} )
2019-05-17 20:15:38 -04:00
} ,
} ,
{
// deleteVolume event issued before deleteClaim, no metric should have been reported
// and no delete operation start timestamp should be inserted into controller.operationTimestamps cache
2022-08-04 09:30:23 -04:00
name : "5-7 - delete volume event makes claim lost, delete claim event will not report metric" ,
initialVolumes : newVolumeArray ( "volume5-7" , "10Gi" , "uid5-7" , "claim5-7" , v1 . VolumeBound , v1 . PersistentVolumeReclaimDelete , classExternal , volume . AnnBoundByController , volume . AnnDynamicallyProvisioned ) ,
expectedVolumes : novolumes ,
initialClaims : claimWithAnnotation ( volume . AnnStorageProvisioner , "gcr.io/vendor-csi" ,
2021-08-18 03:12:42 -04:00
newClaimArray ( "claim5-7" , "uid5-7" , "1Gi" , "volume5-7" , v1 . ClaimBound , & classExternal , volume . AnnBoundByController , volume . AnnBindCompleted ) ) ,
2022-08-04 09:30:23 -04:00
expectedClaims : noclaims ,
expectedEvents : [ ] string { "Warning ClaimLost" } ,
errors : noerrors ,
2019-05-17 20:15:38 -04:00
// Custom test function that generates a delete claim event which should have been caught by
// "deleteClaim" to remove the claim from controller's cache and mark bound volume to be released
2022-08-04 09:30:23 -04:00
test : func ( ctrl * PersistentVolumeController , reactor * pvtesting . VolumeReactor , test controllerTest ) error {
2019-05-17 20:15:38 -04:00
volume := ctrl . volumes . store . List ( ) [ 0 ] . ( * v1 . PersistentVolume )
reactor . DeleteVolumeEvent ( volume )
2020-08-04 04:03:33 -04:00
err := wait . Poll ( 10 * time . Millisecond , wait . ForeverTestTimeout , func ( ) ( bool , error ) {
return len ( ctrl . volumes . store . ListKeys ( ) ) == 0 , nil
} )
if err != nil {
return err
2019-05-17 20:15:38 -04:00
}
2021-12-21 09:36:44 -05:00
// Wait for the PVC to get fully processed. This avoids races between PV controller and DeleteClaimEvent
// below.
err = wait . Poll ( 10 * time . Millisecond , wait . ForeverTestTimeout , func ( ) ( bool , error ) {
obj := ctrl . claims . List ( ) [ 0 ]
claim := obj . ( * v1 . PersistentVolumeClaim )
return claim . Status . Phase == v1 . ClaimLost , nil
} )
if err != nil {
return err
}
2019-05-17 20:15:38 -04:00
// trying to remove the claim as well
obj := ctrl . claims . List ( ) [ 0 ]
claim := obj . ( * v1 . PersistentVolumeClaim )
reactor . DeleteClaimEvent ( claim )
// wait until claim is cleared from cache, i.e., deleteClaim is called
2020-08-04 04:03:33 -04:00
err = wait . Poll ( 10 * time . Millisecond , wait . ForeverTestTimeout , func ( ) ( bool , error ) {
return len ( ctrl . claims . ListKeys ( ) ) == 0 , nil
} )
if err != nil {
return err
2019-05-17 20:15:38 -04:00
}
// make sure operation timestamp cache is empty
if ctrl . operationTimestamps . Has ( "volume5-7" ) {
return errors . New ( "failed checking timestamp cache" )
}
return nil
} ,
} ,
{
// delete a claim waiting for being bound cleans up provision(volume ref == "") entry from timestamp cache
2022-08-04 09:30:23 -04:00
name : "5-8 - delete claim cleans up operation timestamp cache for provision" ,
initialVolumes : novolumes ,
expectedVolumes : novolumes ,
initialClaims : claimWithAnnotation ( volume . AnnStorageProvisioner , "gcr.io/vendor-csi" ,
2019-05-17 20:15:38 -04:00
newClaimArray ( "claim5-8" , "uid5-8" , "1Gi" , "" , v1 . ClaimPending , & classExternal ) ) ,
2022-08-04 09:30:23 -04:00
expectedClaims : noclaims ,
expectedEvents : [ ] string { "Normal ExternalProvisioning" } ,
errors : noerrors ,
2019-05-17 20:15:38 -04:00
// Custom test function that generates a delete claim event which should have been caught by
// "deleteClaim" to remove the claim from controller's cache and mark bound volume to be released
2022-08-04 09:30:23 -04:00
test : func ( ctrl * PersistentVolumeController , reactor * pvtesting . VolumeReactor , test controllerTest ) error {
2019-05-17 20:15:38 -04:00
// wait until the provision timestamp has been inserted
2020-08-04 04:03:33 -04:00
err := wait . Poll ( 10 * time . Millisecond , wait . ForeverTestTimeout , func ( ) ( bool , error ) {
return ctrl . operationTimestamps . Has ( "default/claim5-8" ) , nil
} )
if err != nil {
return err
2019-05-17 20:15:38 -04:00
}
// delete the claim
obj := ctrl . claims . List ( ) [ 0 ]
claim := obj . ( * v1 . PersistentVolumeClaim )
reactor . DeleteClaimEvent ( claim )
// wait until claim is cleared from cache, i.e., deleteClaim is called
2020-08-04 04:03:33 -04:00
err = wait . Poll ( 10 * time . Millisecond , wait . ForeverTestTimeout , func ( ) ( bool , error ) {
return len ( ctrl . claims . ListKeys ( ) ) == 0 , nil
} )
if err != nil {
return err
2019-05-17 20:15:38 -04:00
}
// make sure operation timestamp cache is empty
if ctrl . operationTimestamps . Has ( "default/claim5-8" ) {
return errors . New ( "failed checking timestamp cache" )
}
return nil
} ,
} ,
2020-02-18 15:59:47 -05:00
{
2022-02-09 12:50:56 -05:00
// Test that the finalizer gets removed if CSI migration is disabled. The in-tree finalizer is added
// back on the PV since migration is disabled.
2022-08-04 09:30:23 -04:00
name : "5-9 - volume has its external PV deletion protection finalizer removed as CSI migration is disabled" ,
initialVolumes : volumesWithFinalizers (
2021-08-18 03:12:42 -04:00
volumesWithAnnotation ( volume . AnnMigratedTo , "pd.csi.storage.gke.io" ,
newVolumeArray ( "volume-5-9" , "1Gi" , "" , "" , v1 . VolumeAvailable , v1 . PersistentVolumeReclaimDelete , classEmpty , volume . AnnDynamicallyProvisioned ) ) ,
[ ] string { volume . PVDeletionProtectionFinalizer } ,
2021-10-19 17:26:28 -04:00
) ,
2022-08-04 09:30:23 -04:00
expectedVolumes : volumesWithFinalizers ( newVolumeArray ( "volume-5-9" , "1Gi" , "" , "" , v1 . VolumeAvailable , v1 . PersistentVolumeReclaimDelete , classEmpty , volume . AnnDynamicallyProvisioned ) , [ ] string { volume . PVDeletionInTreeProtectionFinalizer } ) ,
initialClaims : noclaims ,
expectedClaims : noclaims ,
expectedEvents : noevents ,
errors : noerrors ,
test : func ( ctrl * PersistentVolumeController , reactor * pvtesting . VolumeReactor , test controllerTest ) error {
2021-10-19 17:26:28 -04:00
return nil
} ,
} ,
2016-05-17 08:55:15 -04:00
}
2023-03-26 10:10:19 -04:00
logger , ctx := ktesting . NewTestContext ( t )
2020-10-28 05:39:59 -04:00
doit := func ( test controllerTest ) {
2016-05-17 08:55:15 -04:00
// Initialize the controller
client := & fake . Clientset { }
2017-02-16 10:08:23 -05:00
fakeVolumeWatch := watch . NewFake ( )
client . PrependWatchReactor ( "persistentvolumes" , core . DefaultWatchReactor ( fakeVolumeWatch , nil ) )
fakeClaimWatch := watch . NewFake ( )
client . PrependWatchReactor ( "persistentvolumeclaims" , core . DefaultWatchReactor ( fakeClaimWatch , nil ) )
client . PrependWatchReactor ( "storageclasses" , core . DefaultWatchReactor ( watch . NewFake ( ) , nil ) )
2021-12-21 09:36:34 -05:00
client . PrependWatchReactor ( "nodes" , core . DefaultWatchReactor ( watch . NewFake ( ) , nil ) )
client . PrependWatchReactor ( "pods" , core . DefaultWatchReactor ( watch . NewFake ( ) , nil ) )
2017-02-16 10:08:23 -05:00
informers := informers . NewSharedInformerFactory ( client , controller . NoResyncPeriodFunc ( ) )
2022-11-03 05:19:04 -04:00
ctrl , err := newTestController ( ctx , client , informers , true )
2017-03-13 03:41:59 -04:00
if err != nil {
t . Fatalf ( "Test %q construct persistent volume failed: %v" , test . name , err )
}
2017-02-16 10:08:23 -05:00
2019-05-17 20:15:38 -04:00
// Inject storage classes into controller via a custom lister for test [5-5]
storageClasses := [ ] * storagev1 . StorageClass {
makeStorageClass ( classExternal , & modeImmediate ) ,
}
storageClasses [ 0 ] . Provisioner = "gcr.io/vendor-csi"
indexer := cache . NewIndexer ( cache . MetaNamespaceKeyFunc , cache . Indexers { } )
for _ , class := range storageClasses {
indexer . Add ( class )
}
ctrl . classLister = storagelisters . NewStorageClassLister ( indexer )
2022-11-03 05:19:04 -04:00
reactor := newVolumeReactor ( ctx , client , ctrl , fakeVolumeWatch , fakeClaimWatch , test . errors )
2016-05-17 08:55:15 -04:00
for _ , claim := range test . initialClaims {
2019-09-02 09:22:06 -04:00
claim = claim . DeepCopy ( )
2019-03-17 21:55:28 -04:00
reactor . AddClaim ( claim )
2017-02-16 10:08:23 -05:00
go func ( claim * v1 . PersistentVolumeClaim ) {
fakeClaimWatch . Add ( claim )
} ( claim )
2016-05-17 08:55:15 -04:00
}
for _ , volume := range test . initialVolumes {
2019-09-02 09:22:06 -04:00
volume = volume . DeepCopy ( )
2019-03-17 21:55:28 -04:00
reactor . AddVolume ( volume )
2017-02-16 10:08:23 -05:00
go func ( volume * v1 . PersistentVolume ) {
fakeVolumeWatch . Add ( volume )
} ( volume )
2016-05-17 08:55:15 -04:00
}
// Start the controller
2021-04-22 14:27:59 -04:00
ctx , cancel := context . WithCancel ( context . TODO ( ) )
informers . Start ( ctx . Done ( ) )
informers . WaitForCacheSync ( ctx . Done ( ) )
go ctrl . Run ( ctx )
2016-05-17 08:55:15 -04:00
2016-06-03 04:53:56 -04:00
// Wait for the controller to pass initial sync and fill its caches.
2020-08-04 04:03:33 -04:00
err = wait . Poll ( 10 * time . Millisecond , wait . ForeverTestTimeout , func ( ) ( bool , error ) {
2020-09-12 02:12:43 -04:00
return len ( ctrl . claims . ListKeys ( ) ) >= len ( test . initialClaims ) &&
2020-08-04 04:03:33 -04:00
len ( ctrl . volumes . store . ListKeys ( ) ) >= len ( test . initialVolumes ) , nil
} )
if err != nil {
t . Errorf ( "Test %q controller sync failed: %v" , test . name , err )
2016-05-17 08:55:15 -04:00
}
2023-03-26 10:10:19 -04:00
logger . V ( 4 ) . Info ( "controller synced, starting test" )
2016-05-17 08:55:15 -04:00
// Call the tested function
2019-03-17 21:55:28 -04:00
err = test . test ( ctrl , reactor . VolumeReactor , test )
2016-05-17 08:55:15 -04:00
if err != nil {
t . Errorf ( "Test %q initial test call failed: %v" , test . name , err )
}
2016-05-31 06:07:47 -04:00
// Simulate a periodic resync, just in case some events arrived in a
// wrong order.
2022-11-03 05:19:04 -04:00
ctrl . resync ( ctx )
2016-05-17 08:55:15 -04:00
2016-07-11 09:35:01 -04:00
err = reactor . waitTest ( test )
if err != nil {
t . Errorf ( "Failed to run test %s: %v" , test . name , err )
2016-05-23 19:25:02 -04:00
}
2021-04-22 14:27:59 -04:00
cancel ( )
2016-06-03 08:26:06 -04:00
2022-11-03 05:19:04 -04:00
evaluateTestResults ( ctx , ctrl , reactor . VolumeReactor , test , t )
2016-05-17 08:55:15 -04:00
}
2020-10-28 05:39:59 -04:00
for _ , test := range tests {
test := test
t . Run ( test . name , func ( t * testing . T ) {
doit ( test )
} )
}
2016-05-17 08:55:15 -04:00
}
volume controller: Add cache with the latest version of PVs and PVCs
When the controller binds a PV to PVC, it saves both objects to etcd.
However, there is still an old version of these objects in the controller
Informer cache. So, when a new PVC comes, the PV is still seen as available
and may get bound to the new PVC. This will be blocked by etcd, still, it
creates unnecessary traffic that slows everything down.
Also, we save bound PV/PVC as two transactions - we save PV/PVC.Spec first
and then .Status. The controller gets "PV/PVC.Spec updated" event from etcd
and tries to fix the Status, as it seems to the controller it's outdated.
This write again fails - there already is a correct version in etcd.
We can't influence the Informer cache, it is read-only to the controller.
To prevent these useless writes to etcd, this patch introduces second cache
in the controller, which holds latest and greatest version on PVs and PVCs.
It gets updated with events from etcd *and* after etcd confirms successful
save of PV/PVC modified by the controller.
The cache stores only *pointers* to PVs/PVCs, so in ideal case it shares the
actual object data with the informer cache. They will diverge only when
the controller modifies something and the informer cache did not get update
events yet.
2016-05-19 07:31:19 -04:00
func storeVersion ( t * testing . T , prefix string , c cache . Store , version string , expectedReturn bool ) {
2017-03-02 04:23:56 -05:00
pv := newVolume ( "pvName" , "1Gi" , "" , "" , v1 . VolumeAvailable , v1 . PersistentVolumeReclaimDelete , classEmpty )
volume controller: Add cache with the latest version of PVs and PVCs
When the controller binds a PV to PVC, it saves both objects to etcd.
However, there is still an old version of these objects in the controller
Informer cache. So, when a new PVC comes, the PV is still seen as available
and may get bound to the new PVC. This will be blocked by etcd, still, it
creates unnecessary traffic that slows everything down.
Also, we save bound PV/PVC as two transactions - we save PV/PVC.Spec first
and then .Status. The controller gets "PV/PVC.Spec updated" event from etcd
and tries to fix the Status, as it seems to the controller it's outdated.
This write again fails - there already is a correct version in etcd.
We can't influence the Informer cache, it is read-only to the controller.
To prevent these useless writes to etcd, this patch introduces second cache
in the controller, which holds latest and greatest version on PVs and PVCs.
It gets updated with events from etcd *and* after etcd confirms successful
save of PV/PVC modified by the controller.
The cache stores only *pointers* to PVs/PVCs, so in ideal case it shares the
actual object data with the informer cache. They will diverge only when
the controller modifies something and the informer cache did not get update
events yet.
2016-05-19 07:31:19 -04:00
pv . ResourceVersion = version
2022-11-03 05:19:04 -04:00
logger , _ := ktesting . NewTestContext ( t )
ret , err := storeObjectUpdate ( logger , c , pv , "volume" )
volume controller: Add cache with the latest version of PVs and PVCs
When the controller binds a PV to PVC, it saves both objects to etcd.
However, there is still an old version of these objects in the controller
Informer cache. So, when a new PVC comes, the PV is still seen as available
and may get bound to the new PVC. This will be blocked by etcd, still, it
creates unnecessary traffic that slows everything down.
Also, we save bound PV/PVC as two transactions - we save PV/PVC.Spec first
and then .Status. The controller gets "PV/PVC.Spec updated" event from etcd
and tries to fix the Status, as it seems to the controller it's outdated.
This write again fails - there already is a correct version in etcd.
We can't influence the Informer cache, it is read-only to the controller.
To prevent these useless writes to etcd, this patch introduces second cache
in the controller, which holds latest and greatest version on PVs and PVCs.
It gets updated with events from etcd *and* after etcd confirms successful
save of PV/PVC modified by the controller.
The cache stores only *pointers* to PVs/PVCs, so in ideal case it shares the
actual object data with the informer cache. They will diverge only when
the controller modifies something and the informer cache did not get update
events yet.
2016-05-19 07:31:19 -04:00
if err != nil {
t . Errorf ( "%s: expected storeObjectUpdate to succeed, got: %v" , prefix , err )
}
if expectedReturn != ret {
t . Errorf ( "%s: expected storeObjectUpdate to return %v, got: %v" , prefix , expectedReturn , ret )
}
// find the stored version
pvObj , found , err := c . GetByKey ( "pvName" )
if err != nil {
t . Errorf ( "expected volume 'pvName' in the cache, got error instead: %v" , err )
}
if ! found {
t . Errorf ( "expected volume 'pvName' in the cache but it was not found" )
}
2016-11-18 15:50:17 -05:00
pv , ok := pvObj . ( * v1 . PersistentVolume )
volume controller: Add cache with the latest version of PVs and PVCs
When the controller binds a PV to PVC, it saves both objects to etcd.
However, there is still an old version of these objects in the controller
Informer cache. So, when a new PVC comes, the PV is still seen as available
and may get bound to the new PVC. This will be blocked by etcd, still, it
creates unnecessary traffic that slows everything down.
Also, we save bound PV/PVC as two transactions - we save PV/PVC.Spec first
and then .Status. The controller gets "PV/PVC.Spec updated" event from etcd
and tries to fix the Status, as it seems to the controller it's outdated.
This write again fails - there already is a correct version in etcd.
We can't influence the Informer cache, it is read-only to the controller.
To prevent these useless writes to etcd, this patch introduces second cache
in the controller, which holds latest and greatest version on PVs and PVCs.
It gets updated with events from etcd *and* after etcd confirms successful
save of PV/PVC modified by the controller.
The cache stores only *pointers* to PVs/PVCs, so in ideal case it shares the
actual object data with the informer cache. They will diverge only when
the controller modifies something and the informer cache did not get update
events yet.
2016-05-19 07:31:19 -04:00
if ! ok {
2016-06-14 08:04:38 -04:00
t . Errorf ( "expected volume in the cache, got different object instead: %#v" , pvObj )
volume controller: Add cache with the latest version of PVs and PVCs
When the controller binds a PV to PVC, it saves both objects to etcd.
However, there is still an old version of these objects in the controller
Informer cache. So, when a new PVC comes, the PV is still seen as available
and may get bound to the new PVC. This will be blocked by etcd, still, it
creates unnecessary traffic that slows everything down.
Also, we save bound PV/PVC as two transactions - we save PV/PVC.Spec first
and then .Status. The controller gets "PV/PVC.Spec updated" event from etcd
and tries to fix the Status, as it seems to the controller it's outdated.
This write again fails - there already is a correct version in etcd.
We can't influence the Informer cache, it is read-only to the controller.
To prevent these useless writes to etcd, this patch introduces second cache
in the controller, which holds latest and greatest version on PVs and PVCs.
It gets updated with events from etcd *and* after etcd confirms successful
save of PV/PVC modified by the controller.
The cache stores only *pointers* to PVs/PVCs, so in ideal case it shares the
actual object data with the informer cache. They will diverge only when
the controller modifies something and the informer cache did not get update
events yet.
2016-05-19 07:31:19 -04:00
}
if ret {
if pv . ResourceVersion != version {
t . Errorf ( "expected volume with version %s in the cache, got %s instead" , version , pv . ResourceVersion )
}
} else {
if pv . ResourceVersion == version {
t . Errorf ( "expected volume with version other than %s in the cache, got %s instead" , version , pv . ResourceVersion )
}
}
}
// TestControllerCache tests func storeObjectUpdate()
func TestControllerCache ( t * testing . T ) {
// Cache under test
2016-09-14 14:35:38 -04:00
c := cache . NewStore ( cache . DeletionHandlingMetaNamespaceKeyFunc )
volume controller: Add cache with the latest version of PVs and PVCs
When the controller binds a PV to PVC, it saves both objects to etcd.
However, there is still an old version of these objects in the controller
Informer cache. So, when a new PVC comes, the PV is still seen as available
and may get bound to the new PVC. This will be blocked by etcd, still, it
creates unnecessary traffic that slows everything down.
Also, we save bound PV/PVC as two transactions - we save PV/PVC.Spec first
and then .Status. The controller gets "PV/PVC.Spec updated" event from etcd
and tries to fix the Status, as it seems to the controller it's outdated.
This write again fails - there already is a correct version in etcd.
We can't influence the Informer cache, it is read-only to the controller.
To prevent these useless writes to etcd, this patch introduces second cache
in the controller, which holds latest and greatest version on PVs and PVCs.
It gets updated with events from etcd *and* after etcd confirms successful
save of PV/PVC modified by the controller.
The cache stores only *pointers* to PVs/PVCs, so in ideal case it shares the
actual object data with the informer cache. They will diverge only when
the controller modifies something and the informer cache did not get update
events yet.
2016-05-19 07:31:19 -04:00
// Store new PV
storeVersion ( t , "Step1" , c , "1" , true )
// Store the same PV
storeVersion ( t , "Step2" , c , "1" , true )
// Store newer PV
storeVersion ( t , "Step3" , c , "2" , true )
// Store older PV - simulating old "PV updated" event or periodic sync with
// old data
storeVersion ( t , "Step4" , c , "1" , false )
// Store newer PV - test integer parsing ("2" > "10" as string,
// while 2 < 10 as integers)
storeVersion ( t , "Step5" , c , "10" , true )
}
func TestControllerCacheParsingError ( t * testing . T ) {
2016-09-14 14:35:38 -04:00
c := cache . NewStore ( cache . DeletionHandlingMetaNamespaceKeyFunc )
volume controller: Add cache with the latest version of PVs and PVCs
When the controller binds a PV to PVC, it saves both objects to etcd.
However, there is still an old version of these objects in the controller
Informer cache. So, when a new PVC comes, the PV is still seen as available
and may get bound to the new PVC. This will be blocked by etcd, still, it
creates unnecessary traffic that slows everything down.
Also, we save bound PV/PVC as two transactions - we save PV/PVC.Spec first
and then .Status. The controller gets "PV/PVC.Spec updated" event from etcd
and tries to fix the Status, as it seems to the controller it's outdated.
This write again fails - there already is a correct version in etcd.
We can't influence the Informer cache, it is read-only to the controller.
To prevent these useless writes to etcd, this patch introduces second cache
in the controller, which holds latest and greatest version on PVs and PVCs.
It gets updated with events from etcd *and* after etcd confirms successful
save of PV/PVC modified by the controller.
The cache stores only *pointers* to PVs/PVCs, so in ideal case it shares the
actual object data with the informer cache. They will diverge only when
the controller modifies something and the informer cache did not get update
events yet.
2016-05-19 07:31:19 -04:00
// There must be something in the cache to compare with
storeVersion ( t , "Step1" , c , "1" , true )
2017-03-02 04:23:56 -05:00
pv := newVolume ( "pvName" , "1Gi" , "" , "" , v1 . VolumeAvailable , v1 . PersistentVolumeReclaimDelete , classEmpty )
volume controller: Add cache with the latest version of PVs and PVCs
When the controller binds a PV to PVC, it saves both objects to etcd.
However, there is still an old version of these objects in the controller
Informer cache. So, when a new PVC comes, the PV is still seen as available
and may get bound to the new PVC. This will be blocked by etcd, still, it
creates unnecessary traffic that slows everything down.
Also, we save bound PV/PVC as two transactions - we save PV/PVC.Spec first
and then .Status. The controller gets "PV/PVC.Spec updated" event from etcd
and tries to fix the Status, as it seems to the controller it's outdated.
This write again fails - there already is a correct version in etcd.
We can't influence the Informer cache, it is read-only to the controller.
To prevent these useless writes to etcd, this patch introduces second cache
in the controller, which holds latest and greatest version on PVs and PVCs.
It gets updated with events from etcd *and* after etcd confirms successful
save of PV/PVC modified by the controller.
The cache stores only *pointers* to PVs/PVCs, so in ideal case it shares the
actual object data with the informer cache. They will diverge only when
the controller modifies something and the informer cache did not get update
events yet.
2016-05-19 07:31:19 -04:00
pv . ResourceVersion = "xxx"
2022-11-03 05:19:04 -04:00
logger , _ := ktesting . NewTestContext ( t )
_ , err := storeObjectUpdate ( logger , c , pv , "volume" )
volume controller: Add cache with the latest version of PVs and PVCs
When the controller binds a PV to PVC, it saves both objects to etcd.
However, there is still an old version of these objects in the controller
Informer cache. So, when a new PVC comes, the PV is still seen as available
and may get bound to the new PVC. This will be blocked by etcd, still, it
creates unnecessary traffic that slows everything down.
Also, we save bound PV/PVC as two transactions - we save PV/PVC.Spec first
and then .Status. The controller gets "PV/PVC.Spec updated" event from etcd
and tries to fix the Status, as it seems to the controller it's outdated.
This write again fails - there already is a correct version in etcd.
We can't influence the Informer cache, it is read-only to the controller.
To prevent these useless writes to etcd, this patch introduces second cache
in the controller, which holds latest and greatest version on PVs and PVCs.
It gets updated with events from etcd *and* after etcd confirms successful
save of PV/PVC modified by the controller.
The cache stores only *pointers* to PVs/PVCs, so in ideal case it shares the
actual object data with the informer cache. They will diverge only when
the controller modifies something and the informer cache did not get update
events yet.
2016-05-19 07:31:19 -04:00
if err == nil {
t . Errorf ( "Expected parsing error, got nil instead" )
}
}
2016-06-03 08:26:06 -04:00
2017-11-08 16:09:45 -05:00
func makeStorageClass ( scName string , mode * storagev1 . VolumeBindingMode ) * storagev1 . StorageClass {
return & storagev1 . StorageClass {
ObjectMeta : metav1 . ObjectMeta {
Name : scName ,
} ,
2022-07-29 02:37:38 -04:00
Provisioner : "kubernetes.io/no-provisioner" ,
VolumeBindingMode : mode ,
}
}
func makeDefaultStorageClass ( scName string , mode * storagev1 . VolumeBindingMode ) * storagev1 . StorageClass {
return & storagev1 . StorageClass {
ObjectMeta : metav1 . ObjectMeta {
Name : scName ,
Annotations : map [ string ] string {
util . IsDefaultStorageClassAnnotation : "true" ,
} ,
} ,
Provisioner : "kubernetes.io/no-provisioner" ,
2017-11-08 16:09:45 -05:00
VolumeBindingMode : mode ,
}
}
2020-01-16 18:50:53 -05:00
func TestAnnealMigrationAnnotations ( t * testing . T ) {
2022-07-20 20:53:29 -04:00
// The gce-pd plugin is used to test a migrated plugin (as the feature is
// locked as of 1.25), and rbd is used as a non-migrated plugin (still alpha
// as of 1.25). As plugins are migrated, rbd should be changed to a non-
// migrated plugin. If there are no other non-migrated plugins, then those
// test cases are moot and they can be removed (keeping only the test cases
// with gce-pd).
2020-01-16 18:50:53 -05:00
const testPlugin = "non-migrated-plugin"
2022-07-20 20:53:29 -04:00
const migratedPlugin = "kubernetes.io/gce-pd"
const migratedDriver = "pd.csi.storage.gke.io"
const nonmigratedPlugin = "kubernetes.io/rbd"
const nonmigratedDriver = "rbd.csi.ceph.com"
2020-01-16 18:50:53 -05:00
tests := [ ] struct {
name string
volumeAnnotations map [ string ] string
expVolumeAnnotations map [ string ] string
claimAnnotations map [ string ] string
expClaimAnnotations map [ string ] string
2022-07-20 20:53:29 -04:00
testMigration bool
2020-01-16 18:50:53 -05:00
} {
{
2022-07-20 20:53:29 -04:00
name : "migration on" ,
volumeAnnotations : map [ string ] string { volume . AnnDynamicallyProvisioned : migratedPlugin } ,
expVolumeAnnotations : map [ string ] string { volume . AnnDynamicallyProvisioned : migratedPlugin , volume . AnnMigratedTo : migratedDriver } ,
claimAnnotations : map [ string ] string { volume . AnnStorageProvisioner : migratedPlugin } ,
expClaimAnnotations : map [ string ] string { volume . AnnStorageProvisioner : migratedPlugin , volume . AnnMigratedTo : migratedDriver } ,
2020-01-16 18:50:53 -05:00
} ,
2021-08-25 19:30:45 -04:00
{
2022-07-20 20:53:29 -04:00
name : "migration on with Beta storage provisioner annontation" ,
volumeAnnotations : map [ string ] string { volume . AnnDynamicallyProvisioned : migratedPlugin } ,
expVolumeAnnotations : map [ string ] string { volume . AnnDynamicallyProvisioned : migratedPlugin , volume . AnnMigratedTo : migratedDriver } ,
claimAnnotations : map [ string ] string { volume . AnnBetaStorageProvisioner : migratedPlugin } ,
expClaimAnnotations : map [ string ] string { volume . AnnBetaStorageProvisioner : migratedPlugin , volume . AnnMigratedTo : migratedDriver } ,
2021-08-25 19:30:45 -04:00
} ,
2020-01-16 18:50:53 -05:00
{
2022-07-20 20:53:29 -04:00
name : "migration off" ,
volumeAnnotations : map [ string ] string { volume . AnnDynamicallyProvisioned : nonmigratedPlugin } ,
expVolumeAnnotations : map [ string ] string { volume . AnnDynamicallyProvisioned : nonmigratedPlugin } ,
claimAnnotations : map [ string ] string { volume . AnnStorageProvisioner : nonmigratedPlugin } ,
expClaimAnnotations : map [ string ] string { volume . AnnStorageProvisioner : nonmigratedPlugin } ,
2020-01-16 18:50:53 -05:00
} ,
{
2022-07-20 20:53:29 -04:00
name : "migration off removes migrated to (rollback)" ,
volumeAnnotations : map [ string ] string { volume . AnnDynamicallyProvisioned : nonmigratedPlugin , volume . AnnMigratedTo : nonmigratedDriver } ,
expVolumeAnnotations : map [ string ] string { volume . AnnDynamicallyProvisioned : nonmigratedPlugin } ,
claimAnnotations : map [ string ] string { volume . AnnStorageProvisioner : nonmigratedPlugin , volume . AnnMigratedTo : nonmigratedDriver } ,
expClaimAnnotations : map [ string ] string { volume . AnnStorageProvisioner : nonmigratedPlugin } ,
2020-01-16 18:50:53 -05:00
} ,
2021-08-25 19:30:45 -04:00
{
2022-07-20 20:53:29 -04:00
name : "migration off removes migrated to (rollback) with Beta storage provisioner annontation" ,
volumeAnnotations : map [ string ] string { volume . AnnDynamicallyProvisioned : nonmigratedPlugin , volume . AnnMigratedTo : nonmigratedDriver } ,
expVolumeAnnotations : map [ string ] string { volume . AnnDynamicallyProvisioned : nonmigratedPlugin } ,
claimAnnotations : map [ string ] string { volume . AnnBetaStorageProvisioner : nonmigratedPlugin , volume . AnnMigratedTo : nonmigratedDriver } ,
expClaimAnnotations : map [ string ] string { volume . AnnBetaStorageProvisioner : nonmigratedPlugin } ,
2021-08-25 19:30:45 -04:00
} ,
2020-01-16 18:50:53 -05:00
{
2022-07-20 20:53:29 -04:00
name : "migration on, other plugin not affected" ,
2021-08-18 03:12:42 -04:00
volumeAnnotations : map [ string ] string { volume . AnnDynamicallyProvisioned : testPlugin } ,
expVolumeAnnotations : map [ string ] string { volume . AnnDynamicallyProvisioned : testPlugin } ,
claimAnnotations : map [ string ] string { volume . AnnStorageProvisioner : testPlugin } ,
expClaimAnnotations : map [ string ] string { volume . AnnStorageProvisioner : testPlugin } ,
2020-01-16 18:50:53 -05:00
} ,
{
2022-07-20 20:53:29 -04:00
name : "not dynamically provisioned" ,
2020-01-16 18:50:53 -05:00
volumeAnnotations : map [ string ] string { } ,
expVolumeAnnotations : map [ string ] string { } ,
claimAnnotations : map [ string ] string { } ,
expClaimAnnotations : map [ string ] string { } ,
2022-07-20 20:53:29 -04:00
testMigration : false ,
2020-01-16 18:50:53 -05:00
} ,
{
2022-07-20 20:53:29 -04:00
name : "nil annotations" ,
2020-01-16 18:50:53 -05:00
volumeAnnotations : nil ,
expVolumeAnnotations : nil ,
claimAnnotations : nil ,
expClaimAnnotations : nil ,
2022-07-20 20:53:29 -04:00
testMigration : false ,
2020-01-16 18:50:53 -05:00
} ,
}
translator := csitrans . New ( )
2021-03-08 07:49:57 -05:00
cmpm := csimigration . NewPluginManager ( translator , utilfeature . DefaultFeatureGate )
2022-11-03 05:19:04 -04:00
logger , _ := ktesting . NewTestContext ( t )
2020-01-16 18:50:53 -05:00
for _ , tc := range tests {
t . Run ( tc . name , func ( t * testing . T ) {
if tc . volumeAnnotations != nil {
ann := tc . volumeAnnotations
2022-11-03 05:19:04 -04:00
updateMigrationAnnotations ( logger , cmpm , translator , ann , false )
2020-01-16 18:50:53 -05:00
if ! reflect . DeepEqual ( tc . expVolumeAnnotations , ann ) {
t . Errorf ( "got volume annoations: %v, but expected: %v" , ann , tc . expVolumeAnnotations )
}
}
if tc . claimAnnotations != nil {
ann := tc . claimAnnotations
2022-11-03 05:19:04 -04:00
updateMigrationAnnotations ( logger , cmpm , translator , ann , true )
2020-01-16 18:50:53 -05:00
if ! reflect . DeepEqual ( tc . expClaimAnnotations , ann ) {
t . Errorf ( "got volume annoations: %v, but expected: %v" , ann , tc . expVolumeAnnotations )
}
}
} )
}
}
2021-10-19 17:26:28 -04:00
2022-03-31 11:46:53 -04:00
func TestModifyDeletionFinalizers ( t * testing . T ) {
2021-11-12 04:45:00 -05:00
// This set of tests ensures that protection finalizer is removed when CSI migration is disabled
2022-07-20 20:53:29 -04:00
// and PV controller needs to remove finalizers added by the external-provisioner. The rbd
// in-tree plugin is used as migration is disabled. When that plugin is migrated, a different
// non-migrated one should be used. If all plugins are migrated this test can be removed. The
// gce in-tree plugin is used for a migrated driver as it is feature-locked as of 1.25.
2022-03-31 11:46:53 -04:00
defer featuregatetesting . SetFeatureGateDuringTest ( t , utilfeature . DefaultFeatureGate , features . HonorPVReclaimPolicy , true ) ( )
2022-07-20 20:53:29 -04:00
const nonmigratedDriver = "rbd.csi.ceph.com"
const migratedPlugin = "kubernetes.io/gce-pd"
const migratedDriver = "pd.csi.storage.gke.io"
2021-10-19 17:26:28 -04:00
const customFinalizer = "test.volume.kubernetes.io/finalizer"
tests := [ ] struct {
name string
2022-03-31 11:46:53 -04:00
initialVolume * v1 . PersistentVolume
2021-10-19 17:26:28 -04:00
volumeAnnotations map [ string ] string
expVolumeFinalizers [ ] string
expModified bool
} {
{
2022-03-31 11:46:53 -04:00
// Represents a CSI volume provisioned through external-provisioner, no CSI migration enabled.
2021-10-19 17:26:28 -04:00
name : "13-1 migration was never enabled, volume has the finalizer" ,
2022-07-20 20:53:29 -04:00
initialVolume : newExternalProvisionedVolume ( "volume-13-1" , "1Gi" , "uid11-23" , "claim11-23" , v1 . VolumeBound , v1 . PersistentVolumeReclaimDelete , classCopper , nonmigratedDriver , [ ] string { volume . PVDeletionProtectionFinalizer } , volume . AnnDynamicallyProvisioned , volume . AnnBoundByController ) ,
2021-08-18 03:12:42 -04:00
expVolumeFinalizers : [ ] string { volume . PVDeletionProtectionFinalizer } ,
2021-10-19 17:26:28 -04:00
expModified : false ,
} ,
{
// Represents a volume provisioned through external-provisioner but the external-provisioner has
// yet to sync the volume to add the new finalizer
name : "13-2 migration was never enabled, volume does not have the finalizer" ,
2022-07-20 20:53:29 -04:00
initialVolume : newExternalProvisionedVolume ( "volume-13-2" , "1Gi" , "uid11-23" , "claim11-23" , v1 . VolumeBound , v1 . PersistentVolumeReclaimDelete , classCopper , nonmigratedDriver , nil , volume . AnnDynamicallyProvisioned , volume . AnnBoundByController ) ,
2021-10-19 17:26:28 -04:00
expVolumeFinalizers : nil ,
expModified : false ,
} ,
{
// Represents an in-tree volume that has the migrated-to annotation but the external-provisioner is
// yet to sync the volume and add the pv deletion protection finalizer. The custom finalizer is some
2022-02-09 12:50:56 -05:00
// pre-existing finalizer, for example the pv-protection finalizer. When csi-migration is disabled,
// the migrated-to annotation will be removed shortly when updateVolumeMigrationAnnotationsAndFinalizers
// is called followed by adding back the in-tree pv protection finalizer.
2022-03-31 11:46:53 -04:00
name : "13-3 migration was disabled, volume has existing custom finalizer, does not have in-tree pv deletion protection finalizer" ,
initialVolume : newVolumeWithFinalizers ( "volume-13-3" , "1Gi" , "uid11-23" , "claim11-23" , v1 . VolumeBound , v1 . PersistentVolumeReclaimDelete , classCopper , [ ] string { customFinalizer } , volume . AnnDynamicallyProvisioned , volume . AnnBoundByController ) ,
2021-08-18 03:12:42 -04:00
expVolumeFinalizers : [ ] string { customFinalizer , volume . PVDeletionInTreeProtectionFinalizer } ,
2021-10-19 17:26:28 -04:00
expModified : true ,
} ,
{
2022-03-31 11:46:53 -04:00
name : "13-4 migration was disabled, volume has no finalizers" ,
initialVolume : newVolumeWithFinalizers ( "volume-13-4" , "1Gi" , "uid11-23" , "claim11-23" , v1 . VolumeBound , v1 . PersistentVolumeReclaimDelete , classCopper , nil , volume . AnnDynamicallyProvisioned , volume . AnnBoundByController ) ,
2021-08-18 03:12:42 -04:00
expVolumeFinalizers : [ ] string { volume . PVDeletionInTreeProtectionFinalizer } ,
2021-10-19 17:26:28 -04:00
expModified : true ,
} ,
{
// Represents roll back scenario where the external-provisioner has added the pv deletion protection
// finalizer and later the csi migration was disabled. The pv deletion protection finalizer added through
2022-02-09 12:50:56 -05:00
// external-provisioner will be removed and the in-tree pv deletion protection finalizer will be added.
2022-03-31 11:46:53 -04:00
name : "13-5 migration was disabled, volume has external PV deletion finalizer" ,
initialVolume : newVolumeWithFinalizers ( "volume-13-5" , "1Gi" , "uid11-23" , "claim11-23" , v1 . VolumeBound , v1 . PersistentVolumeReclaimDelete , classCopper , [ ] string { volume . PVDeletionProtectionFinalizer } , volume . AnnDynamicallyProvisioned , volume . AnnBoundByController ) ,
2021-08-18 03:12:42 -04:00
expVolumeFinalizers : [ ] string { volume . PVDeletionInTreeProtectionFinalizer } ,
2021-10-19 17:26:28 -04:00
expModified : true ,
} ,
{
// Represents roll-back of csi-migration as 13-5, here there are multiple finalizers, only the pv deletion
2022-02-09 12:50:56 -05:00
// protection finalizer added by external-provisioner will be removed and the in-tree pv deletion protection
// finalizer will be added.
2022-03-31 11:46:53 -04:00
name : "13-6 migration was disabled, volume has multiple finalizers" ,
initialVolume : newVolumeWithFinalizers ( "volume-13-6" , "1Gi" , "uid11-23" , "claim11-23" , v1 . VolumeBound , v1 . PersistentVolumeReclaimDelete , classCopper , [ ] string { volume . PVDeletionProtectionFinalizer , customFinalizer } , volume . AnnDynamicallyProvisioned , volume . AnnBoundByController ) ,
2021-08-18 03:12:42 -04:00
expVolumeFinalizers : [ ] string { customFinalizer , volume . PVDeletionInTreeProtectionFinalizer } ,
2021-10-19 17:26:28 -04:00
expModified : true ,
} ,
{
// csi migration is enabled, the pv controller should not delete the finalizer added by the
2022-02-09 12:50:56 -05:00
// external-provisioner and the in-tree finalizer should be deleted.
2022-03-31 11:46:53 -04:00
name : "13-7 migration is enabled, volume has both the in-tree and external PV deletion protection finalizer" ,
initialVolume : newVolumeWithFinalizers ( "volume-13-7" , "1Gi" , "uid11-23" , "claim11-23" , v1 . VolumeBound , v1 . PersistentVolumeReclaimDelete , classCopper , [ ] string { volume . PVDeletionProtectionFinalizer , volume . PVDeletionInTreeProtectionFinalizer } , volume . AnnDynamicallyProvisioned , volume . AnnBoundByController ) ,
2022-07-20 20:53:29 -04:00
volumeAnnotations : map [ string ] string { volume . AnnDynamicallyProvisioned : migratedPlugin , volume . AnnMigratedTo : migratedDriver } ,
2021-08-18 03:12:42 -04:00
expVolumeFinalizers : [ ] string { volume . PVDeletionProtectionFinalizer } ,
2022-02-09 12:50:56 -05:00
expModified : true ,
2021-10-19 17:26:28 -04:00
} ,
{
// csi-migration is not completely enabled as the specific plugin feature is not present. This is equivalent
// of disabled csi-migration.
2022-03-31 11:46:53 -04:00
name : "13-8 migration is enabled but plugin migration feature is disabled, volume has the external PV deletion protection finalizer" ,
initialVolume : newVolumeWithFinalizers ( "volume-13-8" , "1Gi" , "uid11-23" , "claim11-23" , v1 . VolumeBound , v1 . PersistentVolumeReclaimDelete , classCopper , [ ] string { volume . PVDeletionProtectionFinalizer } , volume . AnnDynamicallyProvisioned , volume . AnnBoundByController ) ,
2021-08-18 03:12:42 -04:00
expVolumeFinalizers : [ ] string { volume . PVDeletionInTreeProtectionFinalizer } ,
2021-10-19 17:26:28 -04:00
expModified : true ,
} ,
{
2022-02-09 12:50:56 -05:00
// same as 13-8 but multiple finalizers exists, only the pv deletion protection finalizer needs to be
// removed and the in-tree pv deletion protection finalizer needs to be added.
2022-03-31 11:46:53 -04:00
name : "13-9 migration is enabled but plugin migration feature is disabled, volume has multiple finalizers including external PV deletion protection finalizer" ,
initialVolume : newVolumeWithFinalizers ( "volume-13-9" , "1Gi" , "uid11-23" , "claim11-23" , v1 . VolumeBound , v1 . PersistentVolumeReclaimDelete , classCopper , [ ] string { volume . PVDeletionProtectionFinalizer , customFinalizer } , volume . AnnDynamicallyProvisioned , volume . AnnBoundByController ) ,
2021-08-18 03:12:42 -04:00
expVolumeFinalizers : [ ] string { customFinalizer , volume . PVDeletionInTreeProtectionFinalizer } ,
2021-10-19 17:26:28 -04:00
expModified : true ,
} ,
{
// corner error case.
name : "13-10 missing annotations but finalizers exist" ,
2022-03-31 11:46:53 -04:00
initialVolume : newVolumeWithFinalizers ( "volume-13-10" , "1Gi" , "uid11-23" , "claim11-23" , v1 . VolumeBound , v1 . PersistentVolumeReclaimDelete , classCopper , [ ] string { volume . PVDeletionProtectionFinalizer } ) ,
2021-08-18 03:12:42 -04:00
expVolumeFinalizers : [ ] string { volume . PVDeletionProtectionFinalizer } ,
2021-10-19 17:26:28 -04:00
expModified : false ,
} ,
{
name : "13-11 missing annotations and finalizers" ,
2022-03-31 11:46:53 -04:00
initialVolume : newVolumeWithFinalizers ( "volume-13-11" , "1Gi" , "uid11-23" , "claim11-23" , v1 . VolumeBound , v1 . PersistentVolumeReclaimDelete , classCopper , nil ) ,
2021-10-19 17:26:28 -04:00
expVolumeFinalizers : nil ,
expModified : false ,
} ,
{
2022-03-31 11:46:53 -04:00
// When ReclaimPolicy is Retain ensure that in-tree pv deletion protection finalizer is not added.
name : "13-12 migration is disabled, volume has no finalizers, reclaimPolicy is Retain" ,
initialVolume : newVolumeWithFinalizers ( "volume-13-12" , "1Gi" , "uid11-23" , "claim11-23" , v1 . VolumeBound , v1 . PersistentVolumeReclaimRetain , classCopper , nil , volume . AnnDynamicallyProvisioned , volume . AnnBoundByController ) ,
expVolumeFinalizers : nil ,
2021-10-19 17:26:28 -04:00
expModified : false ,
} ,
2022-02-09 12:50:56 -05:00
{
2022-03-31 11:46:53 -04:00
// When ReclaimPolicy is Recycle ensure that in-tree pv deletion protection finalizer is not added.
name : "13-13 migration is disabled, volume has no finalizers, reclaimPolicy is Recycle" ,
initialVolume : newVolumeWithFinalizers ( "volume-13-13" , "1Gi" , "uid11-23" , "claim11-23" , v1 . VolumeBound , v1 . PersistentVolumeReclaimRecycle , classCopper , nil , volume . AnnDynamicallyProvisioned , volume . AnnBoundByController ) ,
expVolumeFinalizers : nil ,
expModified : false ,
} ,
{
// When ReclaimPolicy is Retain ensure that in-tree pv deletion protection finalizer present is removed.
name : "13-14 migration is disabled, volume has in-tree pv deletion finalizers, reclaimPolicy is Retain" ,
initialVolume : newVolumeWithFinalizers ( "volume-13-14" , "1Gi" , "uid11-23" , "claim11-23" , v1 . VolumeBound , v1 . PersistentVolumeReclaimRetain , classCopper , [ ] string { volume . PVDeletionInTreeProtectionFinalizer } , volume . AnnDynamicallyProvisioned , volume . AnnBoundByController ) ,
2022-02-09 12:50:56 -05:00
expVolumeFinalizers : nil ,
expModified : true ,
2022-03-31 11:46:53 -04:00
} ,
{
// Statically provisioned volumes should not have the in-tree pv deletion protection finalizer
name : "13-15 migration is disabled, statically provisioned PV" ,
initialVolume : newVolumeWithFinalizers ( "volume-13-14" , "1Gi" , "" , "" , v1 . VolumeAvailable , v1 . PersistentVolumeReclaimDelete , classCopper , nil ) ,
expVolumeFinalizers : nil ,
expModified : false ,
2022-02-09 12:50:56 -05:00
} ,
2021-10-19 17:26:28 -04:00
}
translator := csitrans . New ( )
cmpm := csimigration . NewPluginManager ( translator , utilfeature . DefaultFeatureGate )
2022-11-03 05:19:04 -04:00
logger , _ := ktesting . NewTestContext ( t )
2021-10-19 17:26:28 -04:00
for _ , tc := range tests {
t . Run ( tc . name , func ( t * testing . T ) {
if tc . volumeAnnotations != nil {
2022-03-31 11:46:53 -04:00
tc . initialVolume . SetAnnotations ( tc . volumeAnnotations )
}
2022-11-03 05:19:04 -04:00
modifiedFinalizers , modified := modifyDeletionFinalizers ( logger , cmpm , tc . initialVolume )
2022-03-31 11:46:53 -04:00
if modified != tc . expModified {
t . Errorf ( "got modified: %v, but expected: %v" , modified , tc . expModified )
}
if ! reflect . DeepEqual ( tc . expVolumeFinalizers , modifiedFinalizers ) {
t . Errorf ( "got volume finaliers: %v, but expected: %v" , modifiedFinalizers , tc . expVolumeFinalizers )
2021-10-19 17:26:28 -04:00
}
} )
}
}
2022-07-29 02:37:38 -04:00
func TestRetroactiveStorageClassAssignment ( t * testing . T ) {
tests := [ ] struct {
storageClasses [ ] * storagev1 . StorageClass
tests [ ] controllerTest
} {
// [Unit test set 15] - retroactive storage class assignment tests
{
storageClasses : [ ] * storagev1 . StorageClass { } ,
tests : [ ] controllerTest {
{
name : "15-1 - pvc storage class is not assigned retroactively if there are no default storage classes" ,
initialVolumes : novolumes ,
expectedVolumes : novolumes ,
initialClaims : newClaimArray ( "claim15-1" , "uid15-1" , "1Gi" , "" , v1 . ClaimPending , nil ) ,
expectedClaims : newClaimArray ( "claim15-1" , "uid15-1" , "1Gi" , "" , v1 . ClaimPending , nil ) ,
expectedEvents : noevents ,
errors : noerrors ,
test : testSyncClaim ,
} ,
} ,
} ,
{
storageClasses : [ ] * storagev1 . StorageClass {
makeDefaultStorageClass ( classGold , & modeImmediate ) ,
2022-08-04 00:07:27 -04:00
makeStorageClass ( classSilver , & modeImmediate ) ,
} ,
2022-07-29 02:37:38 -04:00
tests : [ ] controllerTest {
{
2022-08-04 00:07:27 -04:00
name : "15-3 - pvc storage class is not assigned retroactively if claim is already bound" ,
2022-07-29 02:37:38 -04:00
initialVolumes : novolumes ,
expectedVolumes : novolumes ,
2022-08-04 00:07:27 -04:00
initialClaims : newClaimArray ( "claim15-3" , "uid15-3" , "1Gi" , "test" , v1 . ClaimBound , & classCopper , volume . AnnBoundByController , volume . AnnBindCompleted ) ,
expectedClaims : newClaimArray ( "claim15-3" , "uid15-3" , "1Gi" , "test" , v1 . ClaimLost , & classCopper , volume . AnnBoundByController , volume . AnnBindCompleted ) ,
2022-07-29 02:37:38 -04:00
expectedEvents : noevents ,
errors : noerrors ,
test : testSyncClaim ,
} ,
} ,
} ,
{
storageClasses : [ ] * storagev1 . StorageClass {
makeDefaultStorageClass ( classGold , & modeImmediate ) ,
makeStorageClass ( classSilver , & modeImmediate ) ,
} ,
tests : [ ] controllerTest {
{
2022-08-04 00:07:27 -04:00
name : "15-4 - pvc storage class is not assigned retroactively if claim is already bound but annotations are missing" ,
2022-07-29 02:37:38 -04:00
initialVolumes : novolumes ,
expectedVolumes : novolumes ,
2022-08-04 00:07:27 -04:00
initialClaims : newClaimArray ( "claim15-4" , "uid15-4" , "1Gi" , "test" , v1 . ClaimBound , & classCopper ) ,
expectedClaims : newClaimArray ( "claim15-4" , "uid15-4" , "1Gi" , "test" , v1 . ClaimPending , & classCopper ) ,
2022-07-29 02:37:38 -04:00
expectedEvents : noevents ,
errors : noerrors ,
test : testSyncClaim ,
} ,
} ,
} ,
{
storageClasses : [ ] * storagev1 . StorageClass {
makeDefaultStorageClass ( classGold , & modeImmediate ) ,
makeStorageClass ( classSilver , & modeImmediate ) ,
} ,
tests : [ ] controllerTest {
{
2022-08-04 00:07:27 -04:00
name : "15-5 - pvc storage class is assigned retroactively if there is a default" ,
2022-07-29 02:37:38 -04:00
initialVolumes : novolumes ,
expectedVolumes : novolumes ,
2022-08-04 00:07:27 -04:00
initialClaims : newClaimArray ( "claim15-5" , "uid15-5" , "1Gi" , "" , v1 . ClaimPending , nil ) ,
expectedClaims : newClaimArray ( "claim15-5" , "uid15-5" , "1Gi" , "" , v1 . ClaimPending , & classGold ) ,
2022-07-29 02:37:38 -04:00
expectedEvents : noevents ,
errors : noerrors ,
test : testSyncClaim ,
} ,
} ,
} ,
{
storageClasses : [ ] * storagev1 . StorageClass {
makeDefaultStorageClass ( classGold , & modeImmediate ) ,
2022-08-04 00:07:27 -04:00
makeDefaultStorageClass ( classSilver , & modeImmediate ) } ,
2022-07-29 02:37:38 -04:00
tests : [ ] controllerTest {
{
2022-08-04 00:07:27 -04:00
name : "15-2 - pvc storage class is assigned retroactively if there are multiple default storage classes" ,
2022-07-29 02:37:38 -04:00
initialVolumes : novolumes ,
expectedVolumes : novolumes ,
2022-08-04 00:07:27 -04:00
initialClaims : newClaimArray ( "claim15-2" , "uid15-2" , "1Gi" , "" , v1 . ClaimPending , nil ) ,
expectedClaims : newClaimArray ( "claim15-2" , "uid15-2" , "1Gi" , "" , v1 . ClaimPending , & classGold ) ,
2022-07-29 02:37:38 -04:00
expectedEvents : noevents ,
errors : noerrors ,
test : testSyncClaim ,
} ,
} ,
} ,
{
storageClasses : [ ] * storagev1 . StorageClass {
makeDefaultStorageClass ( classGold , & modeImmediate ) ,
makeStorageClass ( classCopper , & modeImmediate ) ,
} ,
tests : [ ] controllerTest {
{
name : "15-6 - pvc storage class is not changed if claim is not bound but already has a storage class" ,
initialVolumes : novolumes ,
expectedVolumes : novolumes ,
initialClaims : newClaimArray ( "claim15-6" , "uid15-6" , "1Gi" , "" , v1 . ClaimPending , & classCopper ) ,
expectedClaims : newClaimArray ( "claim15-6" , "uid15-6" , "1Gi" , "" , v1 . ClaimPending , & classCopper ) ,
expectedEvents : noevents ,
errors : noerrors ,
test : testSyncClaim ,
} ,
} ,
} ,
2023-03-01 01:48:37 -05:00
{
storageClasses : [ ] * storagev1 . StorageClass {
makeDefaultStorageClass ( classGold , & modeImmediate ) ,
makeStorageClass ( classCopper , & modeImmediate ) ,
} ,
tests : [ ] controllerTest {
{
name : "15-7 - pvc storage class is not changed if claim is not bound but already set annotation \"volume.beta.kubernetes.io/storage-class\"" ,
initialVolumes : novolumes ,
expectedVolumes : novolumes ,
initialClaims : newClaimArray ( "claim15-7" , "uid15-7" , "1Gi" , "" , v1 . ClaimPending , nil , v1 . BetaStorageClassAnnotation ) ,
expectedClaims : newClaimArray ( "claim15-7" , "uid15-7" , "1Gi" , "" , v1 . ClaimPending , nil , v1 . BetaStorageClassAnnotation ) ,
expectedEvents : noevents ,
errors : noerrors ,
test : testSyncClaim ,
} ,
} ,
} ,
2022-07-29 02:37:38 -04:00
}
2022-11-03 05:19:04 -04:00
_ , ctx := ktesting . NewTestContext ( t )
2022-07-29 02:37:38 -04:00
for _ , test := range tests {
2022-11-03 05:19:04 -04:00
runSyncTests ( t , ctx , test . tests , test . storageClasses , nil )
2022-07-29 02:37:38 -04:00
}
}