containerized-data-importer/pkg/controller/config-controller_test.go
Michael Henriksen 834b85ecbf Network clone (#897)
* network cloning

* fix clone progress
2019-08-01 16:01:25 -04:00

487 lines
14 KiB
Go

package controller
import (
"reflect"
"testing"
routev1 "github.com/openshift/api/route/v1"
routefake "github.com/openshift/client-go/route/clientset/versioned/fake"
routeinformers "github.com/openshift/client-go/route/informers/externalversions"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/diff"
kubeinformers "k8s.io/client-go/informers"
k8sfake "k8s.io/client-go/kubernetes/fake"
core "k8s.io/client-go/testing"
"k8s.io/client-go/tools/cache"
cdiv1 "kubevirt.io/containerized-data-importer/pkg/apis/core/v1alpha1"
"kubevirt.io/containerized-data-importer/pkg/client/clientset/versioned/fake"
informers "kubevirt.io/containerized-data-importer/pkg/client/informers/externalversions"
)
type configFixture struct {
t *testing.T
client *fake.Clientset
kubeclient *k8sfake.Clientset
routeClient *routefake.Clientset
// Objects to put in the store.
configLister []*cdiv1.CDIConfig
ingressLister []*extensionsv1beta1.Ingress
routeLister []*routev1.Route
// Actions expected to happen on the client.
kubeactions []core.Action
actions []core.Action
routeactions []core.Action
// Objects from here preloaded into NewSimpleFake.
kubeobjects []runtime.Object
objects []runtime.Object
routeobjects []runtime.Object
}
func newConfigFixture(t *testing.T) *configFixture {
f := &configFixture{}
f.t = t
f.objects = []runtime.Object{}
f.kubeobjects = []runtime.Object{}
return f
}
func getConfigKey(config *cdiv1.CDIConfig, t *testing.T) string {
key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(config)
if err != nil {
t.Errorf("Unexpected error getting key for config %v: %v", config.Name, err)
return ""
}
return key
}
func createRoute(name, ns, service string) *routev1.Route {
route := &routev1.Route{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: ns,
},
Spec: routev1.RouteSpec{
To: routev1.RouteTargetReference{
Kind: "Service",
Name: service,
},
},
Status: routev1.RouteStatus{
Ingress: []routev1.RouteIngress{
{Host: "cdi-uploadproxy.example.com"},
},
},
}
return route
}
func createIngress(name, ns, service, url string) *extensionsv1beta1.Ingress {
return &extensionsv1beta1.Ingress{
TypeMeta: metav1.TypeMeta{
APIVersion: "extensions/v1beta1",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: ns,
},
Spec: extensionsv1beta1.IngressSpec{
Backend: &extensionsv1beta1.IngressBackend{
ServiceName: service,
},
Rules: []extensionsv1beta1.IngressRule{
{Host: url},
},
},
}
}
func (f *configFixture) newController() (*ConfigController, informers.SharedInformerFactory, routeinformers.SharedInformerFactory, kubeinformers.SharedInformerFactory) {
f.client = fake.NewSimpleClientset(f.objects...)
f.kubeclient = k8sfake.NewSimpleClientset(f.kubeobjects...)
f.routeClient = routefake.NewSimpleClientset(f.routeobjects...)
i := informers.NewSharedInformerFactory(f.client, noResyncPeriodFunc())
k8sI := kubeinformers.NewSharedInformerFactory(f.kubeclient, noResyncPeriodFunc())
r := routeinformers.NewSharedInformerFactory(f.routeClient, noResyncPeriodFunc())
for _, conf := range f.configLister {
i.Cdi().V1alpha1().CDIConfigs().Informer().GetIndexer().Add(conf)
}
for _, ing := range f.ingressLister {
k8sI.Extensions().V1beta1().Ingresses().Informer().GetIndexer().Add(ing)
}
for _, route := range f.routeLister {
r.Route().V1().Routes().Informer().GetIndexer().Add(route)
}
c := NewConfigController(f.kubeclient,
f.client,
k8sI.Extensions().V1beta1().Ingresses(),
r.Route().V1().Routes(),
i.Cdi().V1alpha1().CDIConfigs(),
"cdi-uploadproxy",
"testConfig",
"Always",
"5")
c.ingressesSynced = alwaysReady
c.routesSynced = alwaysReady
c.configsSynced = alwaysReady
return c, i, r, k8sI
}
func (f *configFixture) run(configName string) {
f.runController(configName, true, false)
}
func (f *configFixture) runController(configName string, startInformers bool, expectError bool) {
c, i, r, k8sI := f.newController()
if startInformers {
stopCh := make(chan struct{})
defer close(stopCh)
i.Start(stopCh)
r.Start(stopCh)
k8sI.Start(stopCh)
}
err := c.syncHandler(configName)
if !expectError && err != nil {
f.t.Errorf("error syncing foo: %v", err)
} else if expectError && err == nil {
f.t.Error("expected error syncing foo, got nil")
}
actions := filterInformerActions(f.client.Actions())
for i, action := range actions {
if len(f.actions) < i+1 {
f.t.Errorf("%d unexpected actions: %+v", len(actions)-len(f.actions), actions[i:])
break
}
expectedAction := f.actions[i]
checkConfigAction(expectedAction, action, f.t)
}
if len(f.actions) > len(actions) {
f.t.Errorf("%d additional expected actions:%+v", len(f.actions)-len(actions), f.actions[len(actions):])
}
routeactions := filterInformerActions(f.routeClient.Actions())
for i, action := range routeactions {
if len(f.routeactions) < i+1 {
f.t.Errorf("%d unexpected actions: %+v", len(routeactions)-len(f.routeactions), routeactions[i:])
break
}
expectedAction := f.routeactions[i]
checkConfigAction(expectedAction, action, f.t)
}
if len(f.routeactions) > len(routeactions) {
f.t.Errorf("%d additional expected actions:%+v", len(f.routeactions)-len(routeactions), f.routeactions[len(routeactions):])
}
k8sActions := filterInformerActions(f.kubeclient.Actions())
for i, action := range k8sActions {
if len(f.kubeactions) < i+1 {
f.t.Errorf("%d unexpected actions: %+v", len(k8sActions)-len(f.kubeactions), k8sActions[i:])
break
}
expectedAction := f.kubeactions[i]
checkConfigAction(expectedAction, action, f.t)
}
if len(f.kubeactions) > len(k8sActions) {
f.t.Errorf("%d additional expected actions:%+v", len(f.kubeactions)-len(k8sActions), f.kubeactions[len(k8sActions):])
}
}
func filterConfigInformerActions(actions []core.Action) []core.Action {
ret := []core.Action{}
for _, action := range actions {
if len(action.GetNamespace()) == 0 &&
(action.Matches("list", "ingresses") ||
action.Matches("watch", "ingresses") ||
action.Matches("list", "routes") ||
action.Matches("watch", "routes") ||
action.Matches("list", "cdiconfigs") ||
action.Matches("watch", "cdiconfigs")) {
continue
}
ret = append(ret, action)
}
return ret
}
func checkConfigAction(expected, actual core.Action, t *testing.T) {
if !(expected.Matches(actual.GetVerb(), actual.GetResource().Resource) && actual.GetSubresource() == expected.GetSubresource()) {
t.Errorf("Expected\n\t%#v\ngot\n\t%#v", expected, actual)
return
}
if reflect.TypeOf(actual) != reflect.TypeOf(expected) {
t.Errorf("Action has wrong type. Expected: %t. Got: %t", expected, actual)
return
}
switch a := actual.(type) {
case core.CreateAction:
e, _ := expected.(core.CreateAction)
expObject := e.GetObject()
object := a.GetObject()
if !reflect.DeepEqual(expObject, object) {
t.Errorf("Action %s %s has wrong object\nDiff:\n %s",
a.GetVerb(), a.GetResource().Resource, diff.ObjectGoPrintDiff(expObject, object))
}
case core.UpdateAction:
e, _ := expected.(core.UpdateAction)
expObject := e.GetObject()
object := a.GetObject()
if !reflect.DeepEqual(expObject, object) {
t.Errorf("Action %s %s has wrong object\nDiff:\n %s",
a.GetVerb(), a.GetResource().Resource, diff.ObjectGoPrintDiff(expObject, object))
}
case core.PatchAction:
e, _ := expected.(core.PatchAction)
expPatch := e.GetPatch()
patch := a.GetPatch()
if !reflect.DeepEqual(expPatch, expPatch) {
t.Errorf("Action %s %s has wrong patch\nDiff:\n %s",
a.GetVerb(), a.GetResource().Resource, diff.ObjectGoPrintDiff(expPatch, patch))
}
}
}
func (f *configFixture) expectUpdateConfigAction(config *cdiv1.CDIConfig) {
action := core.NewUpdateAction(schema.GroupVersionResource{Group: "cdi.kubevirt.io", Resource: "cdiconfigs", Version: "v1alpha1"}, config.Namespace, config)
f.actions = append(f.actions, action)
}
func (f *configFixture) expectListStorageClass() {
f.kubeactions = append(f.kubeactions,
core.NewRootListAction(schema.GroupVersionResource{Resource: "storageclasses", Version: "v1"}, schema.GroupVersionKind{Group: "storage.k8s.io", Version: "v1", Kind: "StorageClass"}, metav1.ListOptions{}))
}
// very flaky
/*
func TestCreatesCDIConfig(t *testing.T) {
f := newConfigFixture(t)
config := createCDIConfig("testConfig")
f.configLister = append(f.configLister, config)
f.objects = append(f.objects, config)
f.expectListStorageClass()
f.run(getConfigKey(config, t))
}
*/
// another flaky test
/*
func TestCDIConfigStatusChanged(t *testing.T) {
f := newConfigFixture(t)
config := createCDIConfig("testConfig")
url := "www.example.com"
config.Spec.UploadProxyURLOverride = &url
f.configLister = append(f.configLister, config)
f.objects = append(f.objects, config)
result := config.DeepCopy()
result.Status.UploadProxyURL = &url
f.expectListStorageClass()
f.expectUpdateConfigAction(result)
f.run(getConfigKey(config, t))
}
*/
func TestCreatesRoute(t *testing.T) {
f := newConfigFixture(t)
config := createCDIConfig("testConfig")
f.configLister = append(f.configLister, config)
f.objects = append(f.objects, config)
route := createRoute("testRoute", "default", "cdi-uploadproxy")
f.routeLister = append(f.routeLister, route)
url := route.Status.Ingress[0].Host
result := config.DeepCopy()
result.Status.UploadProxyURL = &url
f.expectListStorageClass()
f.expectUpdateConfigAction(result)
f.run(getConfigKey(config, t))
}
// another flaky one
/*
func TestCreatesRouteOverrideExists(t *testing.T) {
f := newConfigFixture(t)
config := createCDIConfig("testConfig")
newURL := "www.override.com"
config.Spec.UploadProxyURLOverride = &newURL
f.configLister = append(f.configLister, config)
f.objects = append(f.objects, config)
route := createRoute("testRoute", "default", "cdi-uploadproxy")
f.routeLister = append(f.routeLister, route)
result := config.DeepCopy()
result.Status.UploadProxyURL = &newURL
f.expectListStorageClass()
f.expectUpdateConfigAction(result)
f.run(getConfigKey(config, t))
}
*/
func TestCreatesRouteDifferentService(t *testing.T) {
f := newConfigFixture(t)
config := createCDIConfig("testConfig")
f.configLister = append(f.configLister, config)
f.objects = append(f.objects, config)
route := createRoute("testRoute", "default", "other-service")
f.routeLister = append(f.routeLister, route)
f.expectListStorageClass()
f.run(getConfigKey(config, t))
}
func TestCreatesIngress(t *testing.T) {
f := newConfigFixture(t)
config := createCDIConfig("testConfig")
f.configLister = append(f.configLister, config)
f.objects = append(f.objects, config)
url := "www.example.com"
ing := createIngress("ing", "default", "cdi-uploadproxy", url)
f.ingressLister = append(f.ingressLister, ing)
f.kubeobjects = append(f.kubeobjects, ing)
result := config.DeepCopy()
result.Status.UploadProxyURL = &url
f.expectListStorageClass()
f.expectUpdateConfigAction(result)
f.run(getConfigKey(config, t))
}
func TestCreatesIngressOverrideExists(t *testing.T) {
f := newConfigFixture(t)
config := createCDIConfig("testConfig")
newURL := "www.override.com"
config.Spec.UploadProxyURLOverride = &newURL
f.configLister = append(f.configLister, config)
f.objects = append(f.objects, config)
url := "www.example.com"
ing := createIngress("ing", "default", "cdi-uploadproxy", url)
f.ingressLister = append(f.ingressLister, ing)
f.kubeobjects = append(f.kubeobjects, ing)
result := config.DeepCopy()
result.Status.UploadProxyURL = &newURL
f.expectListStorageClass()
f.expectUpdateConfigAction(result)
f.run(getConfigKey(config, t))
}
func TestCreatesIngressDifferentService(t *testing.T) {
f := newConfigFixture(t)
config := createCDIConfig("testConfig")
f.configLister = append(f.configLister, config)
f.objects = append(f.objects, config)
url := "www.example.com"
ing := createIngress("ing", "default", "other-service", url)
f.ingressLister = append(f.ingressLister, ing)
f.kubeobjects = append(f.kubeobjects, ing)
f.expectListStorageClass()
f.run(getConfigKey(config, t))
}
func TestCreatesScratchStorageClassOverrideExists(t *testing.T) {
f := newConfigFixture(t)
f.kubeobjects = append(f.kubeobjects, createStorageClass("test1", nil))
f.kubeobjects = append(f.kubeobjects, createStorageClass("test2", nil))
f.kubeobjects = append(f.kubeobjects, createStorageClass("test3", map[string]string{
AnnDefaultStorageClass: "true",
}))
config := createCDIConfig("testConfig")
scratchStorageClass := "test2"
config.Spec.ScratchSpaceStorageClass = &scratchStorageClass
f.configLister = append(f.configLister, config)
f.objects = append(f.objects, config)
result := config.DeepCopy()
result.Status.ScratchSpaceStorageClass = scratchStorageClass
f.expectListStorageClass()
f.expectUpdateConfigAction(result)
f.run(getConfigKey(config, t))
}
// TODO Enable me when we refactor the controller.
//func TestCreatesScratchStorageClassOverrideMissing(t *testing.T) {
// f := newConfigFixture(t)
//
// f.kubeobjects = append(f.kubeobjects, createStorageClass("test1", nil))
// f.kubeobjects = append(f.kubeobjects, createStorageClass("test2", nil))
// f.kubeobjects = append(f.kubeobjects, createStorageClass("test3", map[string]string{
// AnnDefaultStorageClass: "true",
// }))
//
// config := createCDIConfig("testConfig")
// scratchStorageClass := "test3"
//
// f.configLister = append(f.configLister, config)
// f.objects = append(f.objects, config)
//
// result := config.DeepCopy()
// result.Status.ScratchSpaceStorageClass = scratchStorageClass
// f.expectListStorageClass()
// f.expectUpdateConfigAction(result)
//
// f.run(getConfigKey(config, t))
//}