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
}
Beispiel #2
0
// 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)
	}
}