forked from kubevirt/kubevirt
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add unit tests for clone controller (kubevirt#247)
- Loading branch information
1 parent
bef0092
commit 59005c0
Showing
5 changed files
with
1,275 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,346 @@ | ||
package controller | ||
|
||
import ( | ||
"fmt" | ||
"reflect" | ||
"testing" | ||
|
||
"k8s.io/api/core/v1" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/client-go/kubernetes" | ||
k8sfake "k8s.io/client-go/kubernetes/fake" | ||
"k8s.io/client-go/tools/cache" | ||
k8stesting "k8s.io/client-go/tools/cache/testing" | ||
. "kubevirt.io/containerized-data-importer/pkg/common" | ||
) | ||
|
||
func TestNewCloneController(t *testing.T) { | ||
type args struct { | ||
client kubernetes.Interface | ||
pvcInformer cache.SharedIndexInformer | ||
podInformer cache.SharedIndexInformer | ||
cloneImage string | ||
pullPolicy string | ||
verbose string | ||
} | ||
// Set up environment | ||
myclient := k8sfake.NewSimpleClientset() | ||
pvcSource := k8stesting.NewFakePVCControllerSource() | ||
podSource := k8stesting.NewFakeControllerSource() | ||
|
||
// create informers | ||
pvcInformer := cache.NewSharedIndexInformer(pvcSource, nil, DEFAULT_RESYNC_PERIOD, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}) | ||
podInformer := cache.NewSharedIndexInformer(podSource, nil, DEFAULT_RESYNC_PERIOD, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}) | ||
|
||
tests := []struct { | ||
name string | ||
args args | ||
want *CloneController | ||
}{ | ||
{ | ||
name: "expect controller to be created with expected informers", | ||
args: args{myclient, pvcInformer, podInformer, CLONER_DEFAULT_IMAGE, "Always", "-v=5"}, | ||
want: &CloneController{myclient, nil, nil, pvcInformer, podInformer, "test/image", "Always", "-v=5"}, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
got := NewCloneController(tt.args.client, tt.args.pvcInformer, tt.args.podInformer, tt.args.cloneImage, tt.args.pullPolicy, tt.args.verbose) | ||
if got == nil { | ||
t.Errorf("NewCloneController() not created - %v", got) | ||
} | ||
if !reflect.DeepEqual(got.podInformer, tt.want.podInformer) { | ||
t.Errorf("NewCloneController() podInformer = %v, want %v", got.podInformer, tt.want.podInformer) | ||
} | ||
if !reflect.DeepEqual(got.pvcInformer, tt.want.pvcInformer) { | ||
t.Errorf("NewCloneController() pvcInformer = %v, want %v", got.pvcInformer, tt.want.pvcInformer) | ||
} | ||
// queues are generated from the controller | ||
if got.pvcQueue == nil { | ||
t.Errorf("NewCloneController() pvcQueue was not generated properly = %v", got.pvcQueue) | ||
} | ||
if got.podQueue == nil { | ||
t.Errorf("NewCloneController() podQueue was not generated properly = %v", got.podQueue) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestCloneController_ProcessNextPodItem(t *testing.T) { | ||
//create pod and pvc | ||
pvcWithEndPointAnno := createPvc("testPvcWithEndPointAnno", "default", map[string]string{AnnEndpoint: "http://test"}, nil) | ||
podWithCdiAnno := createPod(pvcWithEndPointAnno, DataVolName) | ||
|
||
//create and run the informers | ||
c, _, _, err := createCloneController(pvcWithEndPointAnno, podWithCdiAnno, "default") | ||
if err != nil { | ||
t.Errorf("CloneController.ProcessNextPodItem() failed to initialize fake controller error = %v", err) | ||
return | ||
} | ||
tests := []struct { | ||
name string | ||
want bool | ||
}{ | ||
{ | ||
name: "successfully process pod", | ||
want: true, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
if got := c.ProcessNextPodItem(); got != tt.want { | ||
t.Errorf("CloneController.ProcessNextPodItem() = %v, want %v", got, tt.want) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestCloneController_processPodItem(t *testing.T) { | ||
//create pod and pvc | ||
const stageCount = 3 | ||
|
||
//Generate Staging Specs | ||
// dictates what NS the spec is actually created in | ||
stageNs := make([]string, stageCount) | ||
stageNs[0] = "default" | ||
stageNs[1] = "default" | ||
stageNs[2] = "test" | ||
|
||
// create pvc specs | ||
stagePvcs := make([]*v1.PersistentVolumeClaim, stageCount) | ||
stagePvcs[0] = createPvc("testPvc1", "default", map[string]string{AnnEndpoint: "http://test", AnnCloneRequest: "default/golden-pvc"}, nil) | ||
stagePvcs[1] = createPvc("testPvc2", "default", map[string]string{AnnEndpoint: "http://test"}, nil) | ||
stagePvcs[2] = createPvc("testPvc3", "default", map[string]string{AnnEndpoint: "http://test"}, nil) | ||
|
||
// create pod specs with pvc reference | ||
stagePods := make([]*v1.Pod, stageCount) | ||
stagePods[0] = createPod(stagePvcs[0], ImagePathName) | ||
stagePods[1] = createPod(stagePvcs[1], "myvolume") | ||
stagePods[2] = createPod(stagePvcs[2], ImagePathName) | ||
|
||
//create and run the informers passing back v1 processed pods | ||
c, _, pods, err := createCloneControllerMultiObject(stagePvcs, stagePods, stageNs) | ||
if err != nil { | ||
t.Errorf("Controller.ProcessPodItem() failed to initialize fake controller error = %v", err) | ||
return | ||
} | ||
|
||
type args struct { | ||
pod *v1.Pod | ||
} | ||
tests := []struct { | ||
name string | ||
args args | ||
wantErr bool | ||
}{ | ||
{ | ||
name: "successfully process pod", | ||
args: args{pods[0]}, | ||
wantErr: false, | ||
}, | ||
{ | ||
name: "expect error when processing pod with no image-path volume", | ||
args: args{pods[1]}, | ||
wantErr: true, | ||
}, | ||
{ | ||
name: "expect error when proessing pvc with different NS than expected pod", | ||
args: args{pods[2]}, | ||
wantErr: true, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
if err := c.processPodItem(tt.args.pod); (err != nil) != tt.wantErr { | ||
t.Errorf("Controller.processPodItem() error = %v, wantErr %v, Pod Name = %v, Pod ClaimRef = %v", err, tt.wantErr, tt.args.pod.Name, tt.args.pod.Spec.Volumes) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestCloneController_ProcessNextPvcItem(t *testing.T) { | ||
//create pod and pvc | ||
const stageCount = 4 | ||
|
||
//Generate Staging Specs | ||
// dictates what NS the spec is actually created in | ||
stageNs := make([]string, stageCount) | ||
stageNs[0] = "default" | ||
stageNs[1] = "default" | ||
stageNs[2] = "default" | ||
stageNs[3] = "default" | ||
|
||
// create pvc specs | ||
stagePvcs := make([]*v1.PersistentVolumeClaim, stageCount) | ||
stagePvcs[0] = createPvc("testPvc1", "default", map[string]string{AnnEndpoint: "http://test"}, nil) | ||
stagePvcs[1] = createPvc("testPvc2", "default", map[string]string{AnnEndpoint: "http://test", AnnImportPod: "cdi-importer-pod"}, nil) | ||
stagePvcs[2] = createPvc("testPvc3", "default", nil, nil) | ||
stagePvcs[3] = createPvc("testPvc4", "default", map[string]string{AnnEndpoint: "http://test"}, nil) | ||
|
||
// create pod specs with pvc reference | ||
stagePods := make([]*v1.Pod, stageCount) | ||
stagePods[0] = createPod(stagePvcs[0], DataVolName) | ||
stagePods[1] = createPod(stagePvcs[1], DataVolName) | ||
stagePods[2] = createPod(stagePvcs[2], DataVolName) | ||
stagePods[3] = createPod(stagePvcs[3], DataVolName) | ||
|
||
//create and run queues and informers | ||
c, _, _, err := createCloneControllerMultiObject(stagePvcs, stagePods, stageNs) | ||
if err != nil { | ||
t.Errorf("Controller.ProcessNextPvcItem() failed to initialize fake controller error = %v", err) | ||
return | ||
} | ||
|
||
tests := []struct { | ||
name string | ||
want bool | ||
}{ | ||
{ | ||
name: "expect successful processing of queue", | ||
want: true, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
if got := c.ProcessNextPvcItem(); got != tt.want { | ||
t.Errorf("Controller.ProcessNextPvcItem() = %v, want %v", got, tt.want) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestCloneController_processPvcItem(t *testing.T) { | ||
const ( | ||
stageCount = 4 | ||
srcNs = "test" | ||
srcPvc = "testPvcSource" | ||
) | ||
srcCloneRequestValue := srcNs + "/" + srcPvc | ||
|
||
//Generate Staging Specs | ||
// create pvc specs | ||
stagePvcs := make([]*v1.PersistentVolumeClaim, stageCount) | ||
stagePvcs[0] = createPvc(srcPvc, srcNs, map[string]string{}, nil) | ||
stagePvcs[1] = createPvc("testPvc1", "default", map[string]string{AnnCloneRequest: srcCloneRequestValue}, nil) | ||
stagePvcs[2] = createPvc("testPvc2", "default", map[string]string{AnnEndpoint: "http://test", AnnCloningPods: "cdi-cloner-pod", AnnCloneRequest: "default/testPvcSource"}, nil) | ||
stagePvcs[3] = createPvc("testPvc3", "default", map[string]string{AnnEndpoint: "http://test"}, nil) | ||
//create and run queues and informers | ||
c, _, _, err := createCloneControllerMultiObject(stagePvcs, nil, nil) | ||
if err != nil { | ||
t.Errorf("Controller.ProcessPvcItem() failed to initialize fake controller error = %v", err) | ||
return | ||
} | ||
type args struct { | ||
pvc *v1.PersistentVolumeClaim | ||
} | ||
tests := []struct { | ||
name string | ||
args args | ||
podSourceName string | ||
podTargetName string | ||
wantErr bool | ||
wantFind bool | ||
}{ | ||
{ | ||
name: "process pvc and expect clone pods to be created", | ||
args: args{stagePvcs[1]}, | ||
podSourceName: fmt.Sprintf("%s-", CLONER_SOURCE_PODNAME), | ||
podTargetName: fmt.Sprintf("%s-", CLONER_TARGET_PODNAME), | ||
wantErr: false, | ||
wantFind: true, | ||
}, | ||
{ | ||
name: "process pvc and do not create cloner pods because AnnCloningPods annotation exists", | ||
args: args{stagePvcs[2]}, | ||
podSourceName: fmt.Sprintf("%s-", CLONER_SOURCE_PODNAME), | ||
podTargetName: fmt.Sprintf("%s-", CLONER_TARGET_PODNAME), | ||
wantErr: false, | ||
wantFind: false, | ||
}, | ||
{ | ||
name: "should not process pvc as it does not contain correct cloner request annotation", | ||
args: args{stagePvcs[3]}, | ||
podSourceName: fmt.Sprintf("%s-", CLONER_SOURCE_PODNAME), | ||
podTargetName: fmt.Sprintf("%s-", CLONER_TARGET_PODNAME), | ||
wantErr: true, | ||
wantFind: false, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
err := c.processPvcItem(tt.args.pvc) | ||
if err == nil && tt.wantErr { | ||
t.Errorf("Controller.processPvcItem() expected error but did not get one %v", tt.wantErr) | ||
} else if err != nil && tt.wantErr { | ||
// good case | ||
} else { | ||
// retrieve the pods from respective namespaces | ||
gotSource, err := c.clientset.CoreV1().Pods(srcNs).List(metav1.ListOptions{}) | ||
if err != nil { | ||
// could not get pods | ||
t.Errorf("Controller.processPvcItem() could not retrieve pods from API") | ||
return | ||
} | ||
gotTarget, err := c.clientset.CoreV1().Pods(tt.args.pvc.Namespace).List(metav1.ListOptions{}) | ||
if err != nil { | ||
// could not get pods | ||
t.Errorf("Controller.processPvcItem() could not retrieve pods from API") | ||
return | ||
} | ||
// expect 2 pods to be created - 1 in test namespace and 1 in default namespace | ||
if len(gotSource.Items) != 1 && len(gotTarget.Items) != 1 && tt.wantFind { | ||
t.Errorf("Controller.processPvcItem() did not find both the source and target pods, pod counts = %v - %v in namespaces %s - %s", len(gotSource.Items), len(gotTarget.Items), srcNs, tt.args.pvc.Namespace) | ||
return | ||
} | ||
if gotSource.Items[0].GenerateName != tt.podSourceName && tt.wantFind { | ||
t.Errorf("Controller.processPvcItem() could not retrieve Source Pod %v", gotSource.Items[0].GenerateName) | ||
} | ||
if gotTarget.Items[0].GenerateName != tt.podTargetName && tt.wantFind { | ||
t.Errorf("Controller.processPvcItem() could not retrieve Target Pod %v", gotTarget.Items[0].GenerateName) | ||
} | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestCloneController_forgetKey(t *testing.T) { | ||
//create staging pvc and pod | ||
pvcWithEndPointAnno := createPvc("testPvcWithEndPointAnno", "default", map[string]string{AnnEndpoint: "http://test"}, nil) | ||
podWithCdiAnno := createPod(pvcWithEndPointAnno, DataVolName) | ||
|
||
//run the informers | ||
c, _, _, err := createCloneController(pvcWithEndPointAnno, podWithCdiAnno, "default") | ||
if err != nil { | ||
t.Errorf("Controller.forgetKey() failed to initialize fake controller error = %v", err) | ||
return | ||
} | ||
key, shutdown := c.pvcQueue.Get() | ||
if shutdown { | ||
t.Errorf("Controller.forgetKey() failed to retrieve key from pvcQueue") | ||
return | ||
} | ||
defer c.pvcQueue.Done(key) | ||
|
||
type args struct { | ||
key interface{} | ||
msg string | ||
} | ||
tests := []struct { | ||
name string | ||
args args | ||
want bool | ||
}{ | ||
{ | ||
name: "successfully forget key", | ||
args: args{key, "test of forgetKey func"}, | ||
want: true, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
if got := c.forgetKey(tt.args.key, tt.args.msg); got != tt.want { | ||
t.Errorf("Controller.forgetKey() = %v, want %v", got, tt.want) | ||
} | ||
}) | ||
} | ||
} |
Oops, something went wrong.