diff --git a/pkg/controller/persistentvolume/persistentvolume_controller.go b/pkg/controller/persistentvolume/persistentvolume_controller.go index dcf2ac6a42c..7d47f149a05 100644 --- a/pkg/controller/persistentvolume/persistentvolume_controller.go +++ b/pkg/controller/persistentvolume/persistentvolume_controller.go @@ -108,6 +108,10 @@ type PersistentVolumeController struct { // runningOperations is map of running operations. The value does not // matter, presence of a key is enough to consider an operation running. runningOperations map[string]bool + + // For testing only: hook to call before an asynchronous operation starts. + // Not used when set to nil. + preOperationHook func(operationName string, operationArgument interface{}) } // NewPersistentVolumeController creates a new PersistentVolumeController @@ -1184,6 +1188,11 @@ func (ctrl *PersistentVolumeController) isVolumeReleased(volume *api.PersistentV func (ctrl *PersistentVolumeController) scheduleOperation(operationName string, operation func(arg interface{}), arg interface{}) { glog.V(4).Infof("scheduleOperation[%s]", operationName) + // Poke test code that an operation is just about to get started. + if ctrl.preOperationHook != nil { + ctrl.preOperationHook(operationName, arg) + } + isRunning := func() bool { // In anonymous func() to get the locking right. ctrl.runningOperationsMapLock.Lock() diff --git a/pkg/controller/persistentvolume/persistentvolume_controller_test.go b/pkg/controller/persistentvolume/persistentvolume_controller_test.go index 8deeaeba4a3..67a103d06ac 100644 --- a/pkg/controller/persistentvolume/persistentvolume_controller_test.go +++ b/pkg/controller/persistentvolume/persistentvolume_controller_test.go @@ -45,13 +45,13 @@ func TestControllerSync(t *testing.T) { // syncClaim, not on addVolume. "5-1 - addVolume", novolumes, /* added in testCall below */ - newVolumeArray("volume5-1", "10Gi", "", "", api.VolumeAvailable), + newVolumeArray("volume5-1", "10Gi", "", "", api.VolumeAvailable, api.PersistentVolumeReclaimRetain), newClaimArray("claim5-1", "uid5-1", "1Gi", "", api.ClaimPending), newClaimArray("claim5-1", "uid5-1", "1Gi", "", api.ClaimPending), noevents, // Custom test function that generates an add event func(ctrl *PersistentVolumeController, reactor *volumeReactor, test controllerTest) error { - volume := newVolume("volume5-1", "10Gi", "", "", api.VolumePending) + volume := newVolume("volume5-1", "10Gi", "", "", api.VolumePending, api.PersistentVolumeReclaimRetain) reactor.volumes[volume.Name] = volume reactor.volumeSource.Add(volume) return nil @@ -60,8 +60,8 @@ func TestControllerSync(t *testing.T) { { // addClaim gets a new claim. Check it's bound to a volume. "5-2 - complete bind", - newVolumeArray("volume5-2", "10Gi", "", "", api.VolumeAvailable), - newVolumeArray("volume5-2", "10Gi", "uid5-2", "claim5-2", api.VolumeBound, annBoundByController), + newVolumeArray("volume5-2", "10Gi", "", "", api.VolumeAvailable, api.PersistentVolumeReclaimRetain), + newVolumeArray("volume5-2", "10Gi", "uid5-2", "claim5-2", api.VolumeBound, api.PersistentVolumeReclaimRetain, annBoundByController), noclaims, /* added in testAddClaim5_2 */ newClaimArray("claim5-2", "uid5-2", "1Gi", "volume5-2", api.ClaimBound, annBoundByController, annBindCompleted), noevents, @@ -76,8 +76,8 @@ func TestControllerSync(t *testing.T) { { // deleteClaim with a bound claim makes bound volume released. "5-3 - delete claim", - newVolumeArray("volume5-3", "10Gi", "uid5-3", "claim5-3", api.VolumeBound, annBoundByController), - newVolumeArray("volume5-3", "10Gi", "uid5-3", "claim5-3", api.VolumeReleased, annBoundByController), + newVolumeArray("volume5-3", "10Gi", "uid5-3", "claim5-3", api.VolumeBound, api.PersistentVolumeReclaimRetain, annBoundByController), + newVolumeArray("volume5-3", "10Gi", "uid5-3", "claim5-3", api.VolumeReleased, api.PersistentVolumeReclaimRetain, annBoundByController), newClaimArray("claim5-3", "uid5-3", "1Gi", "volume5-3", api.ClaimBound, annBoundByController, annBindCompleted), noclaims, noevents, @@ -99,7 +99,7 @@ func TestControllerSync(t *testing.T) { { // deleteVolume with a bound volume. Check the claim is Lost. "5-4 - delete volume", - newVolumeArray("volume5-4", "10Gi", "uid5-4", "claim5-4", api.VolumeBound), + newVolumeArray("volume5-4", "10Gi", "uid5-4", "claim5-4", api.VolumeBound, api.PersistentVolumeReclaimRetain), novolumes, newClaimArray("claim5-4", "uid5-4", "1Gi", "volume5-4", api.ClaimBound, annBoundByController, annBindCompleted), newClaimArray("claim5-4", "uid5-4", "1Gi", "volume5-4", api.ClaimLost, annBoundByController, annBindCompleted), diff --git a/pkg/controller/persistentvolume/persistentvolume_framework_test.go b/pkg/controller/persistentvolume/persistentvolume_framework_test.go index 2f2c6a0d09a..c43dac4eab3 100644 --- a/pkg/controller/persistentvolume/persistentvolume_framework_test.go +++ b/pkg/controller/persistentvolume/persistentvolume_framework_test.go @@ -23,6 +23,7 @@ import ( "strconv" "strings" "sync" + "sync/atomic" "testing" "time" @@ -41,6 +42,7 @@ import ( "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/types" "k8s.io/kubernetes/pkg/util/diff" + vol "k8s.io/kubernetes/pkg/volume" ) // This is a unit test framework for persistent volume controller. @@ -185,6 +187,17 @@ func (r *volumeReactor) React(action core.Action) (handled bool, ret runtime.Obj r.changedSinceLastSync++ glog.V(4).Infof("saved updated claim %s", claim.Name) return true, claim, nil + + case action.Matches("get", "persistentvolumes"): + name := action.(core.GetAction).GetName() + volume, found := r.volumes[name] + if found { + glog.V(4).Infof("GetVolume: found %s", volume.Name) + return true, volume, nil + } else { + glog.V(4).Infof("GetVolume: volume %s not found", name) + return true, nil, fmt.Errorf("Cannot find volume %s", name) + } } return false, nil, nil } @@ -382,16 +395,31 @@ func newVolumeReactor(client *fake.Clientset, ctrl *PersistentVolumeController, func newPersistentVolumeController(kubeClient clientset.Interface) *PersistentVolumeController { ctrl := &PersistentVolumeController{ - volumes: newPersistentVolumeOrderedIndex(), - claims: cache.NewStore(cache.MetaNamespaceKeyFunc), - kubeClient: kubeClient, - eventRecorder: record.NewFakeRecorder(1000), + volumes: newPersistentVolumeOrderedIndex(), + claims: cache.NewStore(cache.MetaNamespaceKeyFunc), + kubeClient: kubeClient, + eventRecorder: record.NewFakeRecorder(1000), + runningOperations: make(map[string]bool), } return ctrl } +func addRecyclePlugin(ctrl *PersistentVolumeController, expectedRecycleCalls []error) { + plugin := &mockVolumePlugin{ + recycleCalls: expectedRecycleCalls, + } + ctrl.recyclePluginMgr.InitPlugins([]vol.VolumePlugin{plugin}, ctrl) +} + +func addDeletePlugin(ctrl *PersistentVolumeController, expectedDeleteCalls []error) { + plugin := &mockVolumePlugin{ + deleteCalls: expectedDeleteCalls, + } + ctrl.recyclePluginMgr.InitPlugins([]vol.VolumePlugin{plugin}, ctrl) +} + // newVolume returns a new volume with given attributes -func newVolume(name, capacity, boundToClaimUID, boundToClaimName string, phase api.PersistentVolumePhase, annotations ...string) *api.PersistentVolume { +func newVolume(name, capacity, boundToClaimUID, boundToClaimName string, phase api.PersistentVolumePhase, reclaimPolicy api.PersistentVolumeReclaimPolicy, annotations ...string) *api.PersistentVolume { volume := api.PersistentVolume{ ObjectMeta: api.ObjectMeta{ Name: name, @@ -404,7 +432,8 @@ func newVolume(name, capacity, boundToClaimUID, boundToClaimName string, phase a PersistentVolumeSource: api.PersistentVolumeSource{ GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{}, }, - AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce, api.ReadOnlyMany}, + AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce, api.ReadOnlyMany}, + PersistentVolumeReclaimPolicy: reclaimPolicy, }, Status: api.PersistentVolumeStatus{ Phase: phase, @@ -433,9 +462,9 @@ func newVolume(name, capacity, boundToClaimUID, boundToClaimName string, phase a // newVolumeArray returns array with a single volume that would be returned by // newVolume() with the same parameters. -func newVolumeArray(name, capacity, boundToClaimUID, boundToClaimName string, phase api.PersistentVolumePhase, annotations ...string) []*api.PersistentVolume { +func newVolumeArray(name, capacity, boundToClaimUID, boundToClaimName string, phase api.PersistentVolumePhase, reclaimPolicy api.PersistentVolumeReclaimPolicy, annotations ...string) []*api.PersistentVolume { return []*api.PersistentVolume{ - newVolume(name, capacity, boundToClaimUID, boundToClaimName, phase, annotations...), + newVolume(name, capacity, boundToClaimUID, boundToClaimName, phase, reclaimPolicy, annotations...), } } @@ -498,6 +527,66 @@ func testSyncVolume(ctrl *PersistentVolumeController, reactor *volumeReactor, te return ctrl.syncVolume(test.initialVolumes[0]) } +type operationType string + +const operationDelete = "Delete" +const operationRecycle = "Recycle" + +// wrapTestWithControllerConfig returns a testCall that: +// - configures controller with recycler or deleter which will return provided +// errors when a volume is deleted or recycled. +// - calls given testCall +func wrapTestWithControllerConfig(operation operationType, expectedOperationCalls []error, toWrap testCall) testCall { + expected := expectedOperationCalls + + return func(ctrl *PersistentVolumeController, reactor *volumeReactor, test controllerTest) error { + switch operation { + case operationDelete: + addDeletePlugin(ctrl, expected) + case operationRecycle: + addRecyclePlugin(ctrl, expected) + } + + return toWrap(ctrl, reactor, test) + } +} + +// wrapTestWithInjectedOperation returns a testCall that: +// - starts the controller and lets it run original testCall until +// scheduleOperation() call. It blocks the controller there and calls the +// injected function to simulate that something is happenning when the +// controller waits for the operation lock. Controller is then resumed and we +// check how it behaves. +func wrapTestWithInjectedOperation(toWrap testCall, injectBeforeOperation func(ctrl *PersistentVolumeController, reactor *volumeReactor)) testCall { + + return func(ctrl *PersistentVolumeController, reactor *volumeReactor, test controllerTest) error { + // Inject a hook before async operation starts + ctrl.preOperationHook = func(operationName string, arg interface{}) { + // Inside the hook, run the function to inject + glog.V(4).Infof("reactor: scheduleOperation reached, injecting call") + injectBeforeOperation(ctrl, reactor) + } + + // Run the tested function (typically syncClaim/syncVolume) in a + // separate goroutine. + var testError error + var testFinished int32 + + go func() { + testError = toWrap(ctrl, reactor, test) + // Let the "main" test function know that syncVolume has finished. + atomic.StoreInt32(&testFinished, 1) + }() + + // Wait for the controler to finish the test function. + for atomic.LoadInt32(&testFinished) == 0 { + time.Sleep(time.Millisecond * 10) + } + + return testError + } +} + func evaluateTestResults(ctrl *PersistentVolumeController, reactor *volumeReactor, test controllerTest, t *testing.T) { // Evaluate results if err := reactor.checkClaims(t, test.expectedClaims); err != nil { @@ -542,6 +631,9 @@ func runSyncTests(t *testing.T, tests []controllerTest) { t.Errorf("Test %q failed: %v", test.name, err) } + // Wait for all goroutines to finish + reactor.waitTest() + evaluateTestResults(ctrl, reactor, test, t) } } @@ -596,6 +688,9 @@ func runMultisyncTests(t *testing.T, tests []controllerTest) { break } + // Wait for all goroutines to finish + reactor.waitTest() + obj := reactor.popChange() if obj == nil { // Nothing was changed, should we exit? @@ -655,3 +750,122 @@ func runMultisyncTests(t *testing.T, tests []controllerTest) { glog.V(4).Infof("test %q finished after %d iterations", test.name, counter) } } + +// Dummy volume plugin for provisioning, deletion and recycling. It contains +// lists of expected return values to simulate errors. +type mockVolumePlugin struct { + provisionCalls []error + provisionCallCounter int + deleteCalls []error + deleteCallCounter int + recycleCalls []error + recycleCallCounter int +} + +var _ vol.VolumePlugin = &mockVolumePlugin{} + +func (plugin *mockVolumePlugin) Init(host vol.VolumeHost) error { + return nil +} + +func (plugin *mockVolumePlugin) Name() string { + return "mockVolumePlugin" +} + +func (plugin *mockVolumePlugin) CanSupport(spec *vol.Spec) bool { + return true +} + +func (plugin *mockVolumePlugin) NewMounter(spec *vol.Spec, podRef *api.Pod, opts vol.VolumeOptions) (vol.Mounter, error) { + return nil, fmt.Errorf("Mounter is not supported by this plugin") +} + +func (plugin *mockVolumePlugin) NewUnmounter(name string, podUID types.UID) (vol.Unmounter, error) { + return nil, fmt.Errorf("Unmounter is not supported by this plugin") +} + +// Provisioner interfaces + +func (plugin *mockVolumePlugin) NewProvisioner(options vol.VolumeOptions) (vol.Provisioner, error) { + if len(plugin.provisionCalls) > 0 { + // mockVolumePlugin directly implements Provisioner interface + glog.V(4).Infof("mock plugin NewProvisioner called, returning mock provisioner") + return plugin, nil + } else { + return nil, fmt.Errorf("Mock plugin error: no provisionCalls configured") + } +} + +func (plugin *mockVolumePlugin) Provision(*api.PersistentVolume) error { + if len(plugin.provisionCalls) <= plugin.provisionCallCounter { + return fmt.Errorf("Mock plugin error: unexpected provisioner call %d", plugin.provisionCallCounter) + } + ret := plugin.provisionCalls[plugin.provisionCallCounter] + plugin.provisionCallCounter++ + glog.V(4).Infof("mock plugin Provision call nr. %d, returning %v", plugin.provisionCallCounter, ret) + return ret +} + +func (plugin *mockVolumePlugin) NewPersistentVolumeTemplate() (*api.PersistentVolume, error) { + if len(plugin.provisionCalls) <= plugin.provisionCallCounter { + return nil, fmt.Errorf("Mock plugin error: unexpected provisioner call %d", plugin.provisionCallCounter) + } + ret := plugin.provisionCalls[plugin.provisionCallCounter] + plugin.provisionCallCounter++ + glog.V(4).Infof("mock plugin NewPersistentVolumeTemplate call nr. %d, returning %v", plugin.provisionCallCounter, ret) + return nil, ret +} + +// Deleter interfaces + +func (plugin *mockVolumePlugin) NewDeleter(spec *vol.Spec) (vol.Deleter, error) { + if len(plugin.deleteCalls) > 0 { + // mockVolumePlugin directly implements Deleter interface + glog.V(4).Infof("mock plugin NewDeleter called, returning mock deleter") + return plugin, nil + } else { + return nil, fmt.Errorf("Mock plugin error: no deleteCalls configured") + } +} + +func (plugin *mockVolumePlugin) Delete() error { + if len(plugin.deleteCalls) <= plugin.deleteCallCounter { + return fmt.Errorf("Mock plugin error: unexpected deleter call %d", plugin.deleteCallCounter) + } + ret := plugin.deleteCalls[plugin.deleteCallCounter] + plugin.deleteCallCounter++ + glog.V(4).Infof("mock plugin Delete call nr. %d, returning %v", plugin.deleteCallCounter, ret) + return ret +} + +// Volume interfaces + +func (plugin *mockVolumePlugin) GetPath() string { + return "" +} + +func (plugin *mockVolumePlugin) GetMetrics() (*vol.Metrics, error) { + return nil, nil +} + +// Recycler interfaces + +func (plugin *mockVolumePlugin) NewRecycler(spec *vol.Spec) (vol.Recycler, error) { + if len(plugin.recycleCalls) > 0 { + // mockVolumePlugin directly implements Recycler interface + glog.V(4).Infof("mock plugin NewRecycler called, returning mock recycler") + return plugin, nil + } else { + return nil, fmt.Errorf("Mock plugin error: no recycleCalls configured") + } +} + +func (plugin *mockVolumePlugin) Recycle() error { + if len(plugin.recycleCalls) <= plugin.recycleCallCounter { + return fmt.Errorf("Mock plugin error: unexpected recycle call %d", plugin.recycleCallCounter) + } + ret := plugin.recycleCalls[plugin.recycleCallCounter] + plugin.recycleCallCounter++ + glog.V(4).Infof("mock plugin Recycle call nr. %d, returning %v", plugin.recycleCallCounter, ret) + return ret +} diff --git a/pkg/controller/persistentvolume/persistentvolume_recycle_test.go b/pkg/controller/persistentvolume/persistentvolume_recycle_test.go new file mode 100644 index 00000000000..acac9436a23 --- /dev/null +++ b/pkg/controller/persistentvolume/persistentvolume_recycle_test.go @@ -0,0 +1,196 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +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 ( + "errors" + "testing" + + "k8s.io/kubernetes/pkg/api" +) + +// Test single call to syncVolume, expecting recycling to happen. +// 1. Fill in the controller with initial data +// 2. Call the syncVolume *once*. +// 3. Compare resulting volumes with expected volumes. +func TestRecycleSync(t *testing.T) { + tests := []controllerTest{ + { + // recycle volume bound by controller + "6-1 - successful recycle", + newVolumeArray("volume6-1", "1Gi", "uid6-1", "claim6-1", api.VolumeBound, api.PersistentVolumeReclaimRecycle, annBoundByController), + newVolumeArray("volume6-1", "1Gi", "", "", api.VolumeAvailable, api.PersistentVolumeReclaimRecycle), + noclaims, + noclaims, + noevents, + // Inject recycler into the controller and call syncVolume. The + // recycler simulates one recycle() call that succeeds. + wrapTestWithControllerConfig(operationRecycle, []error{nil}, testSyncVolume), + }, + { + // recycle volume bound by user + "6-2 - successful recycle with prebound volume", + newVolumeArray("volume6-2", "1Gi", "uid6-2", "claim6-2", api.VolumeBound, api.PersistentVolumeReclaimRecycle), + newVolumeArray("volume6-2", "1Gi", "", "claim6-2", api.VolumeAvailable, api.PersistentVolumeReclaimRecycle), + noclaims, + noclaims, + noevents, + // Inject recycler into the controller and call syncVolume. The + // recycler simulates one recycle() call that succeeds. + wrapTestWithControllerConfig(operationRecycle, []error{nil}, testSyncVolume), + }, + { + // recycle failure - plugin not found + "6-3 - plugin not found", + newVolumeArray("volume6-3", "1Gi", "uid6-3", "claim6-3", api.VolumeBound, api.PersistentVolumeReclaimRecycle), + newVolumeArray("volume6-3", "1Gi", "uid6-3", "claim6-3", api.VolumeFailed, api.PersistentVolumeReclaimRecycle), + noclaims, + noclaims, + []string{"Warning VolumeFailedRecycle"}, testSyncVolume, + }, + { + // recycle failure - newRecycler returns error + "6-4 - newRecycler returns error", + newVolumeArray("volume6-4", "1Gi", "uid6-4", "claim6-4", api.VolumeBound, api.PersistentVolumeReclaimRecycle), + newVolumeArray("volume6-4", "1Gi", "uid6-4", "claim6-4", api.VolumeFailed, api.PersistentVolumeReclaimRecycle), + noclaims, + noclaims, + []string{"Warning VolumeFailedRecycle"}, + wrapTestWithControllerConfig(operationRecycle, []error{}, testSyncVolume), + }, + { + // recycle failure - recycle returns error + "6-5 - recycle returns error", + newVolumeArray("volume6-5", "1Gi", "uid6-5", "claim6-5", api.VolumeBound, api.PersistentVolumeReclaimRecycle), + newVolumeArray("volume6-5", "1Gi", "uid6-5", "claim6-5", api.VolumeFailed, api.PersistentVolumeReclaimRecycle), + noclaims, + noclaims, + []string{"Warning VolumeFailedRecycle"}, + wrapTestWithControllerConfig(operationRecycle, []error{errors.New("Mock recycle error")}, testSyncVolume), + }, + { + // recycle success(?) - volume is deleted before doRecycle() starts + "6-6 - volume is deleted before recycling", + newVolumeArray("volume6-6", "1Gi", "uid6-6", "claim6-6", api.VolumeBound, api.PersistentVolumeReclaimRecycle), + novolumes, + noclaims, + noclaims, + noevents, + wrapTestWithInjectedOperation(wrapTestWithControllerConfig(operationRecycle, []error{}, testSyncVolume), func(ctrl *PersistentVolumeController, reactor *volumeReactor) { + // Delete the volume before recycle operation starts + reactor.lock.Lock() + delete(reactor.volumes, "volume6-6") + reactor.lock.Unlock() + }), + }, + { + // recycle success(?) - volume is recycled by previous recycler just + // at the time new doRecycle() starts. This simulates "volume no + // longer needs recycling, skipping". + "6-7 - volume is deleted before recycling", + newVolumeArray("volume6-7", "1Gi", "uid6-7", "claim6-7", api.VolumeBound, api.PersistentVolumeReclaimRecycle, annBoundByController), + newVolumeArray("volume6-7", "1Gi", "", "", api.VolumeAvailable, api.PersistentVolumeReclaimRecycle), + noclaims, + noclaims, + noevents, + wrapTestWithInjectedOperation(wrapTestWithControllerConfig(operationRecycle, []error{}, testSyncVolume), func(ctrl *PersistentVolumeController, reactor *volumeReactor) { + // Mark the volume as Available before the recycler starts + reactor.lock.Lock() + volume := reactor.volumes["volume6-7"] + volume.Spec.ClaimRef = nil + volume.Status.Phase = api.VolumeAvailable + volume.Annotations = nil + reactor.lock.Unlock() + }), + }, + { + // recycle success(?) - volume bound by user is recycled by previous + // recycler just at the time new doRecycle() starts. This simulates + // "volume no longer needs recycling, skipping" with volume bound by + // user. + "6-8 - prebound volume is deleted before recycling", + newVolumeArray("volume6-8", "1Gi", "uid6-8", "claim6-8", api.VolumeBound, api.PersistentVolumeReclaimRecycle), + newVolumeArray("volume6-8", "1Gi", "", "claim6-8", api.VolumeAvailable, api.PersistentVolumeReclaimRecycle), + noclaims, + noclaims, + noevents, + wrapTestWithInjectedOperation(wrapTestWithControllerConfig(operationRecycle, []error{}, testSyncVolume), func(ctrl *PersistentVolumeController, reactor *volumeReactor) { + // Mark the volume as Available before the recycler starts + reactor.lock.Lock() + volume := reactor.volumes["volume6-8"] + volume.Spec.ClaimRef.UID = "" + volume.Status.Phase = api.VolumeAvailable + reactor.lock.Unlock() + }), + }, + { + // recycle success - volume bound by user is recycled, while a new + // claim is created with another UID. + "6-9 - prebound volume is recycled while the claim exists", + newVolumeArray("volume6-9", "1Gi", "uid6-9", "claim6-9", api.VolumeBound, api.PersistentVolumeReclaimRecycle), + newVolumeArray("volume6-9", "1Gi", "", "claim6-9", api.VolumeAvailable, api.PersistentVolumeReclaimRecycle), + newClaimArray("claim6-9", "uid6-9-x", "10Gi", "", api.ClaimPending), + newClaimArray("claim6-9", "uid6-9-x", "10Gi", "", api.ClaimPending), + noevents, + // Inject recycler into the controller and call syncVolume. The + // recycler simulates one recycle() call that succeeds. + wrapTestWithControllerConfig(operationRecycle, []error{nil}, testSyncVolume), + }, + { + // volume has unknown reclaim policy - failure expected + "6-10 - unknown reclaim policy", + newVolumeArray("volume6-10", "1Gi", "uid6-10", "claim6-10", api.VolumeBound, "Unknown"), + newVolumeArray("volume6-10", "1Gi", "uid6-10", "claim6-10", api.VolumeFailed, "Unknown"), + noclaims, + noclaims, + []string{"Warning VolumeUnknownReclaimPolicy"}, testSyncVolume, + }, + } + runSyncTests(t, tests) +} + +// Test multiple calls to syncClaim/syncVolume and periodic sync of all +// volume/claims. The test follows this pattern: +// 0. Load the controller with initial data. +// 1. Call controllerTest.testCall() once as in TestSync() +// 2. For all volumes/claims changed by previous syncVolume/syncClaim calls, +// call appropriate syncVolume/syncClaim (simulating "volume/claim changed" +// events). Go to 2. if these calls change anything. +// 3. When all changes are processed and no new changes were made, call +// syncVolume/syncClaim on all volumes/claims (simulating "periodic sync"). +// 4. If some changes were done by step 3., go to 2. (simulation of +// "volume/claim updated" events, eventually performing step 3. again) +// 5. When 3. does not do any changes, finish the tests and compare final set +// of volumes/claims with expected claims/volumes and report differences. +// Some limit of calls in enforced to prevent endless loops. +func TestRecycleMultiSync(t *testing.T) { + tests := []controllerTest{ + { + // recycle failure - recycle returns error. The controller should + // try again. + "7-1 - recycle returns error", + newVolumeArray("volume7-1", "1Gi", "uid7-1", "claim7-1", api.VolumeBound, api.PersistentVolumeReclaimRecycle), + newVolumeArray("volume7-1", "1Gi", "", "claim7-1", api.VolumeAvailable, api.PersistentVolumeReclaimRecycle), + noclaims, + noclaims, + []string{"Warning VolumeFailedRecycle"}, + wrapTestWithControllerConfig(operationRecycle, []error{errors.New("Mock recycle error"), nil}, testSyncVolume), + }, + } + + runMultisyncTests(t, tests) +} diff --git a/pkg/controller/persistentvolume/persistentvolume_sync_test.go b/pkg/controller/persistentvolume/persistentvolume_sync_test.go index 91529c02fa3..8cdb5442b8e 100644 --- a/pkg/controller/persistentvolume/persistentvolume_sync_test.go +++ b/pkg/controller/persistentvolume/persistentvolume_sync_test.go @@ -35,8 +35,8 @@ func TestSync(t *testing.T) { { // syncClaim binds to a matching unbound volume. "1-1 - successful bind", - newVolumeArray("volume1-1", "1Gi", "", "", api.VolumePending), - newVolumeArray("volume1-1", "1Gi", "uid1-1", "claim1-1", api.VolumeBound, annBoundByController), + newVolumeArray("volume1-1", "1Gi", "", "", api.VolumePending, api.PersistentVolumeReclaimRetain), + newVolumeArray("volume1-1", "1Gi", "uid1-1", "claim1-1", api.VolumeBound, api.PersistentVolumeReclaimRetain, annBoundByController), newClaimArray("claim1-1", "uid1-1", "1Gi", "", api.ClaimPending), newClaimArray("claim1-1", "uid1-1", "1Gi", "volume1-1", api.ClaimBound, annBoundByController, annBindCompleted), noevents, testSyncClaim, @@ -44,8 +44,8 @@ func TestSync(t *testing.T) { { // syncClaim does not do anything when there is no matching volume. "1-2 - noop", - newVolumeArray("volume1-2", "1Gi", "", "", api.VolumePending), - newVolumeArray("volume1-2", "1Gi", "", "", api.VolumePending), + newVolumeArray("volume1-2", "1Gi", "", "", api.VolumePending, api.PersistentVolumeReclaimRetain), + newVolumeArray("volume1-2", "1Gi", "", "", api.VolumePending, api.PersistentVolumeReclaimRetain), newClaimArray("claim1-2", "uid1-2", "10Gi", "", api.ClaimPending), newClaimArray("claim1-2", "uid1-2", "10Gi", "", api.ClaimPending), noevents, testSyncClaim, @@ -54,8 +54,8 @@ func TestSync(t *testing.T) { // syncClaim resets claim.Status to Pending when there is no // matching volume. "1-3 - reset to Pending", - newVolumeArray("volume1-3", "1Gi", "", "", api.VolumePending), - newVolumeArray("volume1-3", "1Gi", "", "", api.VolumePending), + newVolumeArray("volume1-3", "1Gi", "", "", api.VolumePending, api.PersistentVolumeReclaimRetain), + newVolumeArray("volume1-3", "1Gi", "", "", api.VolumePending, api.PersistentVolumeReclaimRetain), newClaimArray("claim1-3", "uid1-3", "10Gi", "", api.ClaimBound), newClaimArray("claim1-3", "uid1-3", "10Gi", "", api.ClaimPending), noevents, testSyncClaim, @@ -64,12 +64,12 @@ func TestSync(t *testing.T) { // syncClaim binds claims to the smallest matching volume "1-4 - smallest volume", []*api.PersistentVolume{ - newVolume("volume1-4_1", "10Gi", "", "", api.VolumePending), - newVolume("volume1-4_2", "1Gi", "", "", api.VolumePending), + newVolume("volume1-4_1", "10Gi", "", "", api.VolumePending, api.PersistentVolumeReclaimRetain), + newVolume("volume1-4_2", "1Gi", "", "", api.VolumePending, api.PersistentVolumeReclaimRetain), }, []*api.PersistentVolume{ - newVolume("volume1-4_1", "10Gi", "", "", api.VolumePending), - newVolume("volume1-4_2", "1Gi", "uid1-4", "claim1-4", api.VolumeBound, annBoundByController), + newVolume("volume1-4_1", "10Gi", "", "", api.VolumePending, api.PersistentVolumeReclaimRetain), + newVolume("volume1-4_2", "1Gi", "uid1-4", "claim1-4", api.VolumeBound, api.PersistentVolumeReclaimRetain, annBoundByController), }, newClaimArray("claim1-4", "uid1-4", "1Gi", "", api.ClaimPending), newClaimArray("claim1-4", "uid1-4", "1Gi", "volume1-4_2", api.ClaimBound, annBoundByController, annBindCompleted), @@ -80,12 +80,12 @@ func TestSync(t *testing.T) { // name), even though a smaller one is available. "1-5 - prebound volume by name - success", []*api.PersistentVolume{ - newVolume("volume1-5_1", "10Gi", "", "claim1-5", api.VolumePending), - newVolume("volume1-5_2", "1Gi", "", "", api.VolumePending), + newVolume("volume1-5_1", "10Gi", "", "claim1-5", api.VolumePending, api.PersistentVolumeReclaimRetain), + newVolume("volume1-5_2", "1Gi", "", "", api.VolumePending, api.PersistentVolumeReclaimRetain), }, []*api.PersistentVolume{ - newVolume("volume1-5_1", "10Gi", "uid1-5", "claim1-5", api.VolumeBound), - newVolume("volume1-5_2", "1Gi", "", "", api.VolumePending), + newVolume("volume1-5_1", "10Gi", "uid1-5", "claim1-5", api.VolumeBound, api.PersistentVolumeReclaimRetain), + newVolume("volume1-5_2", "1Gi", "", "", api.VolumePending, api.PersistentVolumeReclaimRetain), }, newClaimArray("claim1-5", "uid1-5", "1Gi", "", api.ClaimPending), newClaimArray("claim1-5", "uid1-5", "1Gi", "volume1-5_1", api.ClaimBound, annBoundByController, annBindCompleted), @@ -96,12 +96,12 @@ func TestSync(t *testing.T) { // UID), even though a smaller one is available. "1-6 - prebound volume by UID - success", []*api.PersistentVolume{ - newVolume("volume1-6_1", "10Gi", "uid1-6", "claim1-6", api.VolumePending), - newVolume("volume1-6_2", "1Gi", "", "", api.VolumePending), + newVolume("volume1-6_1", "10Gi", "uid1-6", "claim1-6", api.VolumePending, api.PersistentVolumeReclaimRetain), + newVolume("volume1-6_2", "1Gi", "", "", api.VolumePending, api.PersistentVolumeReclaimRetain), }, []*api.PersistentVolume{ - newVolume("volume1-6_1", "10Gi", "uid1-6", "claim1-6", api.VolumeBound), - newVolume("volume1-6_2", "1Gi", "", "", api.VolumePending), + newVolume("volume1-6_1", "10Gi", "uid1-6", "claim1-6", api.VolumeBound, api.PersistentVolumeReclaimRetain), + newVolume("volume1-6_2", "1Gi", "", "", api.VolumePending, api.PersistentVolumeReclaimRetain), }, newClaimArray("claim1-6", "uid1-6", "1Gi", "", api.ClaimPending), newClaimArray("claim1-6", "uid1-6", "1Gi", "volume1-6_1", api.ClaimBound, annBoundByController, annBindCompleted), @@ -111,8 +111,8 @@ func TestSync(t *testing.T) { // syncClaim does not bind claim to a volume prebound to a claim with // same name and different UID "1-7 - prebound volume to different claim", - newVolumeArray("volume1-7", "10Gi", "uid1-777", "claim1-7", api.VolumePending), - newVolumeArray("volume1-7", "10Gi", "uid1-777", "claim1-7", api.VolumePending), + newVolumeArray("volume1-7", "10Gi", "uid1-777", "claim1-7", api.VolumePending, api.PersistentVolumeReclaimRetain), + newVolumeArray("volume1-7", "10Gi", "uid1-777", "claim1-7", api.VolumePending, api.PersistentVolumeReclaimRetain), newClaimArray("claim1-7", "uid1-7", "1Gi", "", api.ClaimPending), newClaimArray("claim1-7", "uid1-7", "1Gi", "", api.ClaimPending), noevents, testSyncClaim, @@ -121,8 +121,8 @@ func TestSync(t *testing.T) { // syncClaim completes binding - simulates controller crash after // PV.ClaimRef is saved "1-8 - complete bind after crash - PV bound", - newVolumeArray("volume1-8", "1Gi", "uid1-8", "claim1-8", api.VolumePending, annBoundByController), - newVolumeArray("volume1-8", "1Gi", "uid1-8", "claim1-8", api.VolumeBound, annBoundByController), + newVolumeArray("volume1-8", "1Gi", "uid1-8", "claim1-8", api.VolumePending, api.PersistentVolumeReclaimRetain, annBoundByController), + newVolumeArray("volume1-8", "1Gi", "uid1-8", "claim1-8", api.VolumeBound, api.PersistentVolumeReclaimRetain, annBoundByController), newClaimArray("claim1-8", "uid1-8", "1Gi", "", api.ClaimPending), newClaimArray("claim1-8", "uid1-8", "1Gi", "volume1-8", api.ClaimBound, annBoundByController, annBindCompleted), noevents, testSyncClaim, @@ -131,8 +131,8 @@ func TestSync(t *testing.T) { // syncClaim completes binding - simulates controller crash after // PV.Status is saved "1-9 - complete bind after crash - PV status saved", - newVolumeArray("volume1-9", "1Gi", "uid1-9", "claim1-9", api.VolumeBound, annBoundByController), - newVolumeArray("volume1-9", "1Gi", "uid1-9", "claim1-9", api.VolumeBound, annBoundByController), + newVolumeArray("volume1-9", "1Gi", "uid1-9", "claim1-9", api.VolumeBound, api.PersistentVolumeReclaimRetain, annBoundByController), + newVolumeArray("volume1-9", "1Gi", "uid1-9", "claim1-9", api.VolumeBound, api.PersistentVolumeReclaimRetain, annBoundByController), newClaimArray("claim1-9", "uid1-9", "1Gi", "", api.ClaimPending), newClaimArray("claim1-9", "uid1-9", "1Gi", "volume1-9", api.ClaimBound, annBoundByController, annBindCompleted), noevents, testSyncClaim, @@ -141,8 +141,8 @@ func TestSync(t *testing.T) { // syncClaim completes binding - simulates controller crash after // PVC.VolumeName is saved "10 - complete bind after crash - PVC bound", - newVolumeArray("volume1-10", "1Gi", "uid1-10", "claim1-10", api.VolumeBound, annBoundByController), - newVolumeArray("volume1-10", "1Gi", "uid1-10", "claim1-10", api.VolumeBound, annBoundByController), + newVolumeArray("volume1-10", "1Gi", "uid1-10", "claim1-10", api.VolumeBound, api.PersistentVolumeReclaimRetain, annBoundByController), + newVolumeArray("volume1-10", "1Gi", "uid1-10", "claim1-10", api.VolumeBound, api.PersistentVolumeReclaimRetain, annBoundByController), newClaimArray("claim1-10", "uid1-10", "1Gi", "volume1-10", api.ClaimPending, annBoundByController, annBindCompleted), newClaimArray("claim1-10", "uid1-10", "1Gi", "volume1-10", api.ClaimBound, annBoundByController, annBindCompleted), noevents, testSyncClaim, @@ -173,8 +173,8 @@ func TestSync(t *testing.T) { // syncClaim with claim pre-bound to a PV that exists and is // unbound. Check it gets bound and no annBoundByController is set. "2-3 - claim prebound to unbound volume", - newVolumeArray("volume2-3", "1Gi", "", "", api.VolumePending), - newVolumeArray("volume2-3", "1Gi", "uid2-3", "claim2-3", api.VolumeBound, annBoundByController), + newVolumeArray("volume2-3", "1Gi", "", "", api.VolumePending, api.PersistentVolumeReclaimRetain), + newVolumeArray("volume2-3", "1Gi", "uid2-3", "claim2-3", api.VolumeBound, api.PersistentVolumeReclaimRetain, annBoundByController), newClaimArray("claim2-3", "uid2-3", "10Gi", "volume2-3", api.ClaimPending), newClaimArray("claim2-3", "uid2-3", "10Gi", "volume2-3", api.ClaimBound, annBindCompleted), noevents, testSyncClaim, @@ -183,8 +183,8 @@ func TestSync(t *testing.T) { // claim with claim pre-bound to a PV that is pre-bound to the claim // by name. Check it gets bound and no annBoundByController is set. "2-4 - claim prebound to prebound volume by name", - newVolumeArray("volume2-4", "1Gi", "", "claim2-4", api.VolumePending), - newVolumeArray("volume2-4", "1Gi", "uid2-4", "claim2-4", api.VolumeBound), + newVolumeArray("volume2-4", "1Gi", "", "claim2-4", api.VolumePending, api.PersistentVolumeReclaimRetain), + newVolumeArray("volume2-4", "1Gi", "uid2-4", "claim2-4", api.VolumeBound, api.PersistentVolumeReclaimRetain), newClaimArray("claim2-4", "uid2-4", "10Gi", "volume2-4", api.ClaimPending), newClaimArray("claim2-4", "uid2-4", "10Gi", "volume2-4", api.ClaimBound, annBindCompleted), noevents, testSyncClaim, @@ -194,8 +194,8 @@ func TestSync(t *testing.T) { // claim by UID. Check it gets bound and no annBoundByController is // set. "2-5 - claim prebound to prebound volume by UID", - newVolumeArray("volume2-5", "1Gi", "uid2-5", "claim2-5", api.VolumePending), - newVolumeArray("volume2-5", "1Gi", "uid2-5", "claim2-5", api.VolumeBound), + newVolumeArray("volume2-5", "1Gi", "uid2-5", "claim2-5", api.VolumePending, api.PersistentVolumeReclaimRetain), + newVolumeArray("volume2-5", "1Gi", "uid2-5", "claim2-5", api.VolumeBound, api.PersistentVolumeReclaimRetain), newClaimArray("claim2-5", "uid2-5", "10Gi", "volume2-5", api.ClaimPending), newClaimArray("claim2-5", "uid2-5", "10Gi", "volume2-5", api.ClaimBound, annBindCompleted), noevents, testSyncClaim, @@ -204,8 +204,8 @@ func TestSync(t *testing.T) { // syncClaim with claim pre-bound to a PV that is bound to different // claim. Check it's reset to Pending. "2-6 - claim prebound to already bound volume", - newVolumeArray("volume2-6", "1Gi", "uid2-6_1", "claim2-6_1", api.VolumeBound), - newVolumeArray("volume2-6", "1Gi", "uid2-6_1", "claim2-6_1", api.VolumeBound), + newVolumeArray("volume2-6", "1Gi", "uid2-6_1", "claim2-6_1", api.VolumeBound, api.PersistentVolumeReclaimRetain), + newVolumeArray("volume2-6", "1Gi", "uid2-6_1", "claim2-6_1", api.VolumeBound, api.PersistentVolumeReclaimRetain), newClaimArray("claim2-6", "uid2-6", "10Gi", "volume2-6", api.ClaimBound), newClaimArray("claim2-6", "uid2-6", "10Gi", "volume2-6", api.ClaimPending), noevents, testSyncClaim, @@ -214,8 +214,8 @@ func TestSync(t *testing.T) { // syncClaim with claim bound by controller to a PV that is bound to // different claim. Check it throws an error. "2-7 - claim bound by controller to already bound volume", - newVolumeArray("volume2-7", "1Gi", "uid2-7_1", "claim2-7_1", api.VolumeBound), - newVolumeArray("volume2-7", "1Gi", "uid2-7_1", "claim2-7_1", api.VolumeBound), + newVolumeArray("volume2-7", "1Gi", "uid2-7_1", "claim2-7_1", api.VolumeBound, api.PersistentVolumeReclaimRetain), + newVolumeArray("volume2-7", "1Gi", "uid2-7_1", "claim2-7_1", api.VolumeBound, api.PersistentVolumeReclaimRetain), newClaimArray("claim2-7", "uid2-7", "10Gi", "volume2-7", api.ClaimBound, annBoundByController), newClaimArray("claim2-7", "uid2-7", "10Gi", "volume2-7", api.ClaimBound, annBoundByController), noevents, testSyncClaimError, @@ -245,8 +245,8 @@ func TestSync(t *testing.T) { // syncClaim with claim bound to unbound volume. Check it's bound. // Also check that Pending phase is set to Bound "3-3 - bound claim with unbound volume", - newVolumeArray("volume3-3", "10Gi", "", "", api.VolumePending), - newVolumeArray("volume3-3", "10Gi", "uid3-3", "claim3-3", api.VolumeBound, annBoundByController), + newVolumeArray("volume3-3", "10Gi", "", "", api.VolumePending, api.PersistentVolumeReclaimRetain), + newVolumeArray("volume3-3", "10Gi", "uid3-3", "claim3-3", api.VolumeBound, api.PersistentVolumeReclaimRetain, annBoundByController), newClaimArray("claim3-3", "uid3-3", "10Gi", "volume3-3", api.ClaimPending, annBoundByController, annBindCompleted), newClaimArray("claim3-3", "uid3-3", "10Gi", "volume3-3", api.ClaimBound, annBoundByController, annBindCompleted), noevents, testSyncClaim, @@ -255,8 +255,8 @@ func TestSync(t *testing.T) { // syncClaim with claim bound to volume with missing (or different) // volume.Spec.ClaimRef.UID. Check that the claim is marked as lost. "3-4 - bound claim with prebound volume", - newVolumeArray("volume3-4", "10Gi", "claim3-4-x", "claim3-4", api.VolumePending), - newVolumeArray("volume3-4", "10Gi", "claim3-4-x", "claim3-4", api.VolumePending), + newVolumeArray("volume3-4", "10Gi", "claim3-4-x", "claim3-4", api.VolumePending, api.PersistentVolumeReclaimRetain), + newVolumeArray("volume3-4", "10Gi", "claim3-4-x", "claim3-4", api.VolumePending, api.PersistentVolumeReclaimRetain), newClaimArray("claim3-4", "uid3-4", "10Gi", "volume3-4", api.ClaimPending, annBoundByController, annBindCompleted), newClaimArray("claim3-4", "uid3-4", "10Gi", "volume3-4", api.ClaimLost, annBoundByController, annBindCompleted), []string{"Warning ClaimMisbound"}, testSyncClaim, @@ -266,8 +266,8 @@ func TestSync(t *testing.T) { // controller does not do anything. Also check that Pending phase is // set to Bound "3-5 - bound claim with bound volume", - newVolumeArray("volume3-5", "10Gi", "uid3-5", "claim3-5", api.VolumePending), - newVolumeArray("volume3-5", "10Gi", "uid3-5", "claim3-5", api.VolumeBound), + newVolumeArray("volume3-5", "10Gi", "uid3-5", "claim3-5", api.VolumePending, api.PersistentVolumeReclaimRetain), + newVolumeArray("volume3-5", "10Gi", "uid3-5", "claim3-5", api.VolumeBound, api.PersistentVolumeReclaimRetain), newClaimArray("claim3-5", "uid3-5", "10Gi", "volume3-5", api.ClaimPending, annBindCompleted), newClaimArray("claim3-5", "uid3-5", "10Gi", "volume3-5", api.ClaimBound, annBindCompleted), noevents, testSyncClaim, @@ -277,8 +277,8 @@ func TestSync(t *testing.T) { // claim. Check that the claim is marked as lost. // TODO: test that an event is emitted "3-6 - bound claim with bound volume", - newVolumeArray("volume3-6", "10Gi", "uid3-6-x", "claim3-6-x", api.VolumePending), - newVolumeArray("volume3-6", "10Gi", "uid3-6-x", "claim3-6-x", api.VolumePending), + newVolumeArray("volume3-6", "10Gi", "uid3-6-x", "claim3-6-x", api.VolumePending, api.PersistentVolumeReclaimRetain), + newVolumeArray("volume3-6", "10Gi", "uid3-6-x", "claim3-6-x", api.VolumePending, api.PersistentVolumeReclaimRetain), newClaimArray("claim3-6", "uid3-6", "10Gi", "volume3-6", api.ClaimPending, annBindCompleted), newClaimArray("claim3-6", "uid3-6", "10Gi", "volume3-6", api.ClaimLost, annBindCompleted), []string{"Warning ClaimMisbound"}, testSyncClaim, @@ -287,8 +287,8 @@ func TestSync(t *testing.T) { { // syncVolume with pending volume. Check it's marked as Available. "4-1 - pending volume", - newVolumeArray("volume4-1", "10Gi", "", "", api.VolumePending), - newVolumeArray("volume4-1", "10Gi", "", "", api.VolumeAvailable), + newVolumeArray("volume4-1", "10Gi", "", "", api.VolumePending, api.PersistentVolumeReclaimRetain), + newVolumeArray("volume4-1", "10Gi", "", "", api.VolumeAvailable, api.PersistentVolumeReclaimRetain), noclaims, noclaims, noevents, testSyncVolume, @@ -297,8 +297,8 @@ func TestSync(t *testing.T) { // syncVolume with prebound pending volume. Check it's marked as // Available. "4-2 - pending prebound volume", - newVolumeArray("volume4-2", "10Gi", "", "claim4-2", api.VolumePending), - newVolumeArray("volume4-2", "10Gi", "", "claim4-2", api.VolumeAvailable), + newVolumeArray("volume4-2", "10Gi", "", "claim4-2", api.VolumePending, api.PersistentVolumeReclaimRetain), + newVolumeArray("volume4-2", "10Gi", "", "claim4-2", api.VolumeAvailable, api.PersistentVolumeReclaimRetain), noclaims, noclaims, noevents, testSyncVolume, @@ -307,8 +307,8 @@ func TestSync(t *testing.T) { // syncVolume with volume bound to missing claim. // Check the volume gets Released "4-3 - bound volume with missing claim", - newVolumeArray("volume4-3", "10Gi", "uid4-3", "claim4-3", api.VolumeBound), - newVolumeArray("volume4-3", "10Gi", "uid4-3", "claim4-3", api.VolumeReleased), + newVolumeArray("volume4-3", "10Gi", "uid4-3", "claim4-3", api.VolumeBound, api.PersistentVolumeReclaimRetain), + newVolumeArray("volume4-3", "10Gi", "uid4-3", "claim4-3", api.VolumeReleased, api.PersistentVolumeReclaimRetain), noclaims, noclaims, noevents, testSyncVolume, @@ -317,8 +317,8 @@ func TestSync(t *testing.T) { // syncVolume with volume bound to claim with different UID. // Check the volume gets Released. "4-4 - volume bound to claim with different UID", - newVolumeArray("volume4-4", "10Gi", "uid4-4", "claim4-4", api.VolumeBound), - newVolumeArray("volume4-4", "10Gi", "uid4-4", "claim4-4", api.VolumeReleased), + newVolumeArray("volume4-4", "10Gi", "uid4-4", "claim4-4", api.VolumeBound, api.PersistentVolumeReclaimRetain), + newVolumeArray("volume4-4", "10Gi", "uid4-4", "claim4-4", api.VolumeReleased, api.PersistentVolumeReclaimRetain), newClaimArray("claim4-4", "uid4-4-x", "10Gi", "volume4-4", api.ClaimBound, annBindCompleted), newClaimArray("claim4-4", "uid4-4-x", "10Gi", "volume4-4", api.ClaimBound, annBindCompleted), noevents, testSyncVolume, @@ -327,8 +327,8 @@ func TestSync(t *testing.T) { // syncVolume with volume bound by controller to unbound claim. // Check syncVolume does not do anything. "4-5 - volume bound by controller to unbound claim", - newVolumeArray("volume4-5", "10Gi", "uid4-5", "claim4-5", api.VolumeBound, annBoundByController), - newVolumeArray("volume4-5", "10Gi", "uid4-5", "claim4-5", api.VolumeBound, annBoundByController), + newVolumeArray("volume4-5", "10Gi", "uid4-5", "claim4-5", api.VolumeBound, api.PersistentVolumeReclaimRetain, annBoundByController), + newVolumeArray("volume4-5", "10Gi", "uid4-5", "claim4-5", api.VolumeBound, api.PersistentVolumeReclaimRetain, annBoundByController), newClaimArray("claim4-5", "uid4-5", "10Gi", "", api.ClaimPending), newClaimArray("claim4-5", "uid4-5", "10Gi", "", api.ClaimPending), noevents, testSyncVolume, @@ -337,8 +337,8 @@ func TestSync(t *testing.T) { // syncVolume with volume bound by user to unbound claim. // Check syncVolume does not do anything. "4-5 - volume bound by user to bound claim", - newVolumeArray("volume4-5", "10Gi", "uid4-5", "claim4-5", api.VolumeBound), - newVolumeArray("volume4-5", "10Gi", "uid4-5", "claim4-5", api.VolumeBound), + newVolumeArray("volume4-5", "10Gi", "uid4-5", "claim4-5", api.VolumeBound, api.PersistentVolumeReclaimRetain), + newVolumeArray("volume4-5", "10Gi", "uid4-5", "claim4-5", api.VolumeBound, api.PersistentVolumeReclaimRetain), newClaimArray("claim4-5", "uid4-5", "10Gi", "", api.ClaimPending), newClaimArray("claim4-5", "uid4-5", "10Gi", "", api.ClaimPending), noevents, testSyncVolume, @@ -347,8 +347,8 @@ func TestSync(t *testing.T) { // syncVolume with volume bound to bound claim. // Check that the volume is marked as Bound. "4-6 - volume bound by to bound claim", - newVolumeArray("volume4-6", "10Gi", "uid4-6", "claim4-6", api.VolumeAvailable), - newVolumeArray("volume4-6", "10Gi", "uid4-6", "claim4-6", api.VolumeBound), + newVolumeArray("volume4-6", "10Gi", "uid4-6", "claim4-6", api.VolumeAvailable, api.PersistentVolumeReclaimRetain), + newVolumeArray("volume4-6", "10Gi", "uid4-6", "claim4-6", api.VolumeBound, api.PersistentVolumeReclaimRetain), newClaimArray("claim4-6", "uid4-6", "10Gi", "volume4-6", api.ClaimBound), newClaimArray("claim4-6", "uid4-6", "10Gi", "volume4-6", api.ClaimBound), noevents, testSyncVolume, @@ -357,8 +357,8 @@ func TestSync(t *testing.T) { // syncVolume with volume bound by controller to claim bound to // another volume. Check that the volume is rolled back. "4-7 - volume bound by controller to claim bound somewhere else", - newVolumeArray("volume4-7", "10Gi", "uid4-7", "claim4-7", api.VolumeBound, annBoundByController), - newVolumeArray("volume4-7", "10Gi", "", "", api.VolumeAvailable), + newVolumeArray("volume4-7", "10Gi", "uid4-7", "claim4-7", api.VolumeBound, api.PersistentVolumeReclaimRetain, annBoundByController), + newVolumeArray("volume4-7", "10Gi", "", "", api.VolumeAvailable, api.PersistentVolumeReclaimRetain), newClaimArray("claim4-7", "uid4-7", "10Gi", "volume4-7-x", api.ClaimBound), newClaimArray("claim4-7", "uid4-7", "10Gi", "volume4-7-x", api.ClaimBound), noevents, testSyncVolume, @@ -368,8 +368,8 @@ func TestSync(t *testing.T) { // another volume. Check that the volume is marked as Available // and its UID is reset. "4-8 - volume bound by user to claim bound somewhere else", - newVolumeArray("volume4-8", "10Gi", "uid4-8", "claim4-8", api.VolumeBound), - newVolumeArray("volume4-8", "10Gi", "", "claim4-8", api.VolumeAvailable), + newVolumeArray("volume4-8", "10Gi", "uid4-8", "claim4-8", api.VolumeBound, api.PersistentVolumeReclaimRetain), + newVolumeArray("volume4-8", "10Gi", "", "claim4-8", api.VolumeAvailable, api.PersistentVolumeReclaimRetain), newClaimArray("claim4-8", "uid4-8", "10Gi", "volume4-8-x", api.ClaimBound), newClaimArray("claim4-8", "uid4-8", "10Gi", "volume4-8-x", api.ClaimBound), noevents, testSyncVolume, @@ -398,8 +398,8 @@ func TestMultiSync(t *testing.T) { { // syncClaim binds to a matching unbound volume. "10-1 - successful bind", - newVolumeArray("volume10-1", "1Gi", "", "", api.VolumePending), - newVolumeArray("volume10-1", "1Gi", "uid10-1", "claim10-1", api.VolumeBound, annBoundByController), + newVolumeArray("volume10-1", "1Gi", "", "", api.VolumePending, api.PersistentVolumeReclaimRetain), + newVolumeArray("volume10-1", "1Gi", "uid10-1", "claim10-1", api.VolumeBound, api.PersistentVolumeReclaimRetain, annBoundByController), newClaimArray("claim10-1", "uid10-1", "1Gi", "", api.ClaimPending), newClaimArray("claim10-1", "uid10-1", "1Gi", "volume10-1", api.ClaimBound, annBoundByController, annBindCompleted), noevents, testSyncClaim, @@ -409,12 +409,12 @@ func TestMultiSync(t *testing.T) { // wins and the second rolls back. "10-2 - bind PV race", []*api.PersistentVolume{ - newVolume("volume10-2-1", "1Gi", "uid10-2", "claim10-2", api.VolumeBound, annBoundByController), - newVolume("volume10-2-2", "1Gi", "uid10-2", "claim10-2", api.VolumeBound, annBoundByController), + newVolume("volume10-2-1", "1Gi", "uid10-2", "claim10-2", api.VolumeBound, api.PersistentVolumeReclaimRetain, annBoundByController), + newVolume("volume10-2-2", "1Gi", "uid10-2", "claim10-2", api.VolumeBound, api.PersistentVolumeReclaimRetain, annBoundByController), }, []*api.PersistentVolume{ - newVolume("volume10-2-1", "1Gi", "uid10-2", "claim10-2", api.VolumeBound, annBoundByController), - newVolume("volume10-2-2", "1Gi", "", "", api.VolumeAvailable), + newVolume("volume10-2-1", "1Gi", "uid10-2", "claim10-2", api.VolumeBound, api.PersistentVolumeReclaimRetain, annBoundByController), + newVolume("volume10-2-2", "1Gi", "", "", api.VolumeAvailable, api.PersistentVolumeReclaimRetain), }, newClaimArray("claim10-2", "uid10-2", "1Gi", "volume10-2-1", api.ClaimBound, annBoundByController, annBindCompleted), newClaimArray("claim10-2", "uid10-2", "1Gi", "volume10-2-1", api.ClaimBound, annBoundByController, annBindCompleted),