mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-25 12:43:23 +00:00
Add controller method tests.
This commit is contained in:
parent
af295719f6
commit
7b73384fda
@ -0,0 +1,162 @@
|
|||||||
|
/*
|
||||||
|
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 (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/golang/glog"
|
||||||
|
"k8s.io/kubernetes/pkg/api"
|
||||||
|
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
|
||||||
|
"k8s.io/kubernetes/pkg/controller/framework"
|
||||||
|
"k8s.io/kubernetes/pkg/conversion"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
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.
|
||||||
|
{
|
||||||
|
// addVolume gets a new volume. Check it's marked as Available and
|
||||||
|
// that it's not bound to any claim - we bind volumes on periodic
|
||||||
|
// syncClaim, not on addVolume.
|
||||||
|
"5-1 - addVolume",
|
||||||
|
novolumes, /* added in testCall below */
|
||||||
|
newVolumeArray("volume5-1", "10Gi", "", "", api.VolumeAvailable),
|
||||||
|
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)
|
||||||
|
reactor.volumes[volume.Name] = volume
|
||||||
|
reactor.volumeSource.Add(volume)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 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),
|
||||||
|
noclaims, /* added in testAddClaim5_2 */
|
||||||
|
newClaimArray("claim5-2", "uid5-2", "1Gi", "volume5-2", api.ClaimBound, annBoundByController, annBindCompleted),
|
||||||
|
noevents,
|
||||||
|
// 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.claims[claim.Name] = claim
|
||||||
|
reactor.claimSource.Add(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, annBoundByController),
|
||||||
|
newVolumeArray("volume5-3", "10Gi", "uid5-3", "claim5-3", api.VolumeReleased, annBoundByController),
|
||||||
|
newClaimArray("claim5-3", "uid5-3", "1Gi", "volume5-3", api.ClaimBound, annBoundByController, annBindCompleted),
|
||||||
|
noclaims,
|
||||||
|
noevents,
|
||||||
|
// 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)
|
||||||
|
// Remove the claim from list of resulting claims.
|
||||||
|
delete(reactor.claims, claim.Name)
|
||||||
|
// Poke the controller with deletion event. Cloned claim is
|
||||||
|
// needed to prevent races (and we would get a clone from etcd
|
||||||
|
// too).
|
||||||
|
clone, _ := conversion.NewCloner().DeepCopy(claim)
|
||||||
|
claimClone := clone.(*api.PersistentVolumeClaim)
|
||||||
|
reactor.claimSource.Delete(claimClone)
|
||||||
|
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),
|
||||||
|
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"},
|
||||||
|
// 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)
|
||||||
|
// Remove the volume from list of resulting volumes.
|
||||||
|
delete(reactor.volumes, volume.Name)
|
||||||
|
// Poke the controller with deletion event. Cloned volume is
|
||||||
|
// needed to prevent races (and we would get a clone from etcd
|
||||||
|
// too).
|
||||||
|
clone, _ := conversion.NewCloner().DeepCopy(volume)
|
||||||
|
volumeClone := clone.(*api.PersistentVolume)
|
||||||
|
reactor.volumeSource.Delete(volumeClone)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
glog.V(4).Infof("starting test %q", test.name)
|
||||||
|
|
||||||
|
// Initialize the controller
|
||||||
|
client := &fake.Clientset{}
|
||||||
|
volumeSource := framework.NewFakeControllerSource()
|
||||||
|
claimSource := framework.NewFakeControllerSource()
|
||||||
|
ctrl := newPersistentVolumeController(client)
|
||||||
|
ctrl.initializeController(time.Minute, volumeSource, claimSource)
|
||||||
|
reactor := newVolumeReactor(client, ctrl, volumeSource, claimSource)
|
||||||
|
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
|
||||||
|
defer ctrl.Stop()
|
||||||
|
go ctrl.Run()
|
||||||
|
|
||||||
|
// Wait for the controller to pass initial sync.
|
||||||
|
for !ctrl.isFullySynced() {
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
reactor.waitTest()
|
||||||
|
|
||||||
|
evaluateTestResults(ctrl, reactor, test, t)
|
||||||
|
}
|
||||||
|
}
|
@ -24,6 +24,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
|
|
||||||
@ -144,6 +145,8 @@ func (r *volumeReactor) React(action core.Action) (handled bool, ret runtime.Obj
|
|||||||
return true, obj, versionConflictError
|
return true, obj, versionConflictError
|
||||||
}
|
}
|
||||||
volume.ResourceVersion = strconv.Itoa(storedVer + 1)
|
volume.ResourceVersion = strconv.Itoa(storedVer + 1)
|
||||||
|
} else {
|
||||||
|
return true, nil, fmt.Errorf("Cannot update volume %s: volume not found", volume.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the updated object to appropriate places.
|
// Store the updated object to appropriate places.
|
||||||
@ -169,6 +172,8 @@ func (r *volumeReactor) React(action core.Action) (handled bool, ret runtime.Obj
|
|||||||
return true, obj, versionConflictError
|
return true, obj, versionConflictError
|
||||||
}
|
}
|
||||||
claim.ResourceVersion = strconv.Itoa(storedVer + 1)
|
claim.ResourceVersion = strconv.Itoa(storedVer + 1)
|
||||||
|
} else {
|
||||||
|
return true, nil, fmt.Errorf("Cannot update claim %s: claim not found", claim.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the updated object to appropriate places.
|
// Store the updated object to appropriate places.
|
||||||
@ -340,6 +345,23 @@ func (r *volumeReactor) getChangeCount() int {
|
|||||||
return r.changedSinceLastSync
|
return r.changedSinceLastSync
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// waitTest waits until all tests, controllers and other goroutines do their
|
||||||
|
// job and no new actions are registered for 10 milliseconds.
|
||||||
|
func (r *volumeReactor) waitTest() {
|
||||||
|
// Check every 10ms if the controller does something and stop if it's
|
||||||
|
// idle.
|
||||||
|
oldChanges := -1
|
||||||
|
for {
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
changes := r.getChangeCount()
|
||||||
|
if changes == oldChanges {
|
||||||
|
// No changes for last 10ms -> controller must be idle.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
oldChanges = changes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func newVolumeReactor(client *fake.Clientset, ctrl *PersistentVolumeController, volumeSource, claimSource *framework.FakeControllerSource) *volumeReactor {
|
func newVolumeReactor(client *fake.Clientset, ctrl *PersistentVolumeController, volumeSource, claimSource *framework.FakeControllerSource) *volumeReactor {
|
||||||
reactor := &volumeReactor{
|
reactor := &volumeReactor{
|
||||||
volumes: make(map[string]*api.PersistentVolume),
|
volumes: make(map[string]*api.PersistentVolume),
|
||||||
|
Loading…
Reference in New Issue
Block a user