func newTestController(kubeClient clientset.Interface, volumeSource, claimSource, classSource cache.ListerWatcher, enableDynamicProvisioning bool) *PersistentVolumeController { if volumeSource == nil { volumeSource = framework.NewFakePVControllerSource() } if claimSource == nil { claimSource = framework.NewFakePVCControllerSource() } if classSource == nil { classSource = framework.NewFakeControllerSource() } ctrl := NewPersistentVolumeController( kubeClient, 5*time.Second, // sync period nil, // alpha provisioner []vol.VolumePlugin{}, // recyclers nil, // cloud "", volumeSource, claimSource, classSource, record.NewFakeRecorder(1000), // event recorder enableDynamicProvisioning, ) // Speed up the test ctrl.createProvisionedPVInterval = 5 * time.Millisecond return ctrl }
// 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) { expectedChanges := []int{4, 1, 1, 2, 1, 1, 1} 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. "5-2 - complete bind", 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, noerrors, // Custom test function that generates an add event func(ctrl *PersistentVolumeController, reactor *volumeReactor, test controllerTest) error { claim := newClaim("claim5-2", "uid5-2", "1Gi", "", api.ClaimPending) reactor.addClaimEvent(claim) return nil }, }, { // deleteClaim with a bound claim makes bound volume released. "5-3 - delete claim", 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, noerrors, // Custom test function that generates a delete event func(ctrl *PersistentVolumeController, reactor *volumeReactor, test controllerTest) error { obj := ctrl.claims.List()[0] claim := obj.(*api.PersistentVolumeClaim) reactor.deleteClaimEvent(claim) return nil }, }, { // deleteVolume with a bound volume. Check the claim is Lost. "5-4 - delete volume", 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), []string{"Warning ClaimLost"}, noerrors, // Custom test function that generates a delete event func(ctrl *PersistentVolumeController, reactor *volumeReactor, test controllerTest) error { obj := ctrl.volumes.store.List()[0] volume := obj.(*api.PersistentVolume) reactor.deleteVolumeEvent(volume) return nil }, }, { // addVolume with provisioned volume from Kubernetes 1.2. No "action" // is expected - it should stay bound. "5-5 - add bound volume from 1.2", novolumes, []*api.PersistentVolume{addVolumeAnnotation(newVolume("volume5-5", "10Gi", "uid5-5", "claim5-5", api.VolumeBound, api.PersistentVolumeReclaimDelete), pvProvisioningRequiredAnnotationKey, pvProvisioningCompletedAnnotationValue)}, newClaimArray("claim5-5", "uid5-5", "1Gi", "", api.ClaimPending), newClaimArray("claim5-5", "uid5-5", "1Gi", "volume5-5", api.ClaimBound, annBindCompleted, annBoundByController), noevents, noerrors, // Custom test function that generates a add event func(ctrl *PersistentVolumeController, reactor *volumeReactor, test controllerTest) error { volume := newVolume("volume5-5", "10Gi", "uid5-5", "claim5-5", api.VolumeBound, api.PersistentVolumeReclaimDelete) volume = addVolumeAnnotation(volume, pvProvisioningRequiredAnnotationKey, pvProvisioningCompletedAnnotationValue) reactor.addVolumeEvent(volume) return nil }, }, { // updateVolume with provisioned volume from Kubernetes 1.2. No // "action" is expected - it should stay bound. "5-6 - update bound volume from 1.2", []*api.PersistentVolume{addVolumeAnnotation(newVolume("volume5-6", "10Gi", "uid5-6", "claim5-6", api.VolumeBound, api.PersistentVolumeReclaimDelete), pvProvisioningRequiredAnnotationKey, pvProvisioningCompletedAnnotationValue)}, []*api.PersistentVolume{addVolumeAnnotation(newVolume("volume5-6", "10Gi", "uid5-6", "claim5-6", api.VolumeBound, api.PersistentVolumeReclaimDelete), pvProvisioningRequiredAnnotationKey, pvProvisioningCompletedAnnotationValue)}, newClaimArray("claim5-6", "uid5-6", "1Gi", "volume5-6", api.ClaimBound), newClaimArray("claim5-6", "uid5-6", "1Gi", "volume5-6", api.ClaimBound, annBindCompleted), noevents, noerrors, // Custom test function that generates a add event func(ctrl *PersistentVolumeController, reactor *volumeReactor, test controllerTest) error { volume := newVolume("volume5-6", "10Gi", "uid5-6", "claim5-6", api.VolumeBound, api.PersistentVolumeReclaimDelete) volume = addVolumeAnnotation(volume, pvProvisioningRequiredAnnotationKey, pvProvisioningCompletedAnnotationValue) reactor.modifyVolumeEvent(volume) return nil }, }, { // addVolume with unprovisioned volume from Kubernetes 1.2. The // volume should be deleted. "5-7 - add unprovisioned volume from 1.2", novolumes, novolumes, newClaimArray("claim5-7", "uid5-7", "1Gi", "", api.ClaimPending), newClaimArray("claim5-7", "uid5-7", "1Gi", "", api.ClaimPending), noevents, noerrors, // Custom test function that generates a add event func(ctrl *PersistentVolumeController, reactor *volumeReactor, test controllerTest) error { volume := newVolume("volume5-7", "10Gi", "uid5-7", "claim5-7", api.VolumeBound, api.PersistentVolumeReclaimDelete) volume = addVolumeAnnotation(volume, pvProvisioningRequiredAnnotationKey, "yes") reactor.addVolumeEvent(volume) return nil }, }, { // updateVolume with unprovisioned volume from Kubernetes 1.2. The // volume should be deleted. "5-8 - update bound volume from 1.2", novolumes, novolumes, newClaimArray("claim5-8", "uid5-8", "1Gi", "", api.ClaimPending), newClaimArray("claim5-8", "uid5-8", "1Gi", "", api.ClaimPending), noevents, noerrors, // Custom test function that generates a add event func(ctrl *PersistentVolumeController, reactor *volumeReactor, test controllerTest) error { volume := newVolume("volume5-8", "10Gi", "uid5-8", "claim5-8", api.VolumeBound, api.PersistentVolumeReclaimDelete) volume = addVolumeAnnotation(volume, pvProvisioningRequiredAnnotationKey, "yes") reactor.modifyVolumeEvent(volume) return nil }, }, } for ix, test := range tests { glog.V(4).Infof("starting test %q", test.name) // Initialize the controller client := &fake.Clientset{} volumeSource := framework.NewFakePVControllerSource() claimSource := framework.NewFakePVCControllerSource() ctrl := newTestController(client, volumeSource, claimSource, true) reactor := newVolumeReactor(client, ctrl, volumeSource, claimSource, test.errors) for _, claim := range test.initialClaims { claimSource.Add(claim) reactor.claims[claim.Name] = claim } for _, volume := range test.initialVolumes { volumeSource.Add(volume) reactor.volumes[volume.Name] = volume } // Start the controller count := reactor.getChangeCount() go ctrl.Run() // Wait for the controller to pass initial sync and fill its caches. for !ctrl.volumeController.HasSynced() || !ctrl.claimController.HasSynced() || len(ctrl.claims.ListKeys()) < len(test.initialClaims) || len(ctrl.volumes.store.ListKeys()) < len(test.initialVolumes) { time.Sleep(10 * time.Millisecond) } glog.V(4).Infof("controller synced, starting test") // Call the tested function err := test.test(ctrl, reactor, test) if err != nil { t.Errorf("Test %q initial test call failed: %v", test.name, err) } // Simulate a periodic resync, just in case some events arrived in a // wrong order. ctrl.claims.Resync() ctrl.volumes.store.Resync() // Wait at least once, just in case expectedChanges[ix] == 0 reactor.waitTest() // Wait for expected number of operations. for reactor.getChangeCount() < count+expectedChanges[ix] { reactor.waitTest() } ctrl.Stop() evaluateTestResults(ctrl, reactor, test, t) } }