From a9a8e02660893ab51736f4e77b97fe58bbda1663 Mon Sep 17 00:00:00 2001 From: Miguel Duarte Barroso Date: Fri, 4 Mar 2022 17:51:20 +0100 Subject: [PATCH] unit tests, informer caches: generic provisioning This commit automatically provisions the controller informer cache with the data present on the fake clientset tracker (the "fake" datastore). This way, users can just create the client with provisioned data, and that'll trickle down to the informer cache of the pod controller. Because the `network-attachment-definitions` resources feature dashes, the heuristic function that guesses - yes, guesses. very deterministic ... - the name of the resource can't be used - [0]. As such, it was needed to create an alternate `newFakeNetAttachDefClient` where it is possible to specify the correct resource name. [0] - https://github.com/k8snetworkplumbingwg/network-attachment-definition-client/blob/2fd7267afcc4d48dfe6a8cd756b5a08bd04c2c97/vendor/k8s.io/client-go/testing/fixture.go#L331 Signed-off-by: Miguel Duarte Barroso --- .../controlloop/pod_controller_test.go | 102 +++++++++++++++--- 1 file changed, 86 insertions(+), 16 deletions(-) diff --git a/pkg/reconciler/controlloop/pod_controller_test.go b/pkg/reconciler/controlloop/pod_controller_test.go index fddcdebd8..288a5747e 100644 --- a/pkg/reconciler/controlloop/pod_controller_test.go +++ b/pkg/reconciler/controlloop/pod_controller_test.go @@ -16,6 +16,7 @@ import ( v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" v1coreinformerfactory "k8s.io/client-go/informers" k8sclient "k8s.io/client-go/kubernetes" fakek8sclient "k8s.io/client-go/kubernetes/fake" @@ -46,6 +47,50 @@ type dummyPodController struct { podCache cache.Store } +func (dpc *dummyPodController) withSynchedIPPool(ipPoolClient wbclient.Interface) error { + ipPools, err := ipPoolClient.WhereaboutsV1alpha1().IPPools(ipPoolsNamespace()).List(context.TODO(), metav1.ListOptions{}) + if err != nil { + return err + } + for _, pool := range ipPools.Items { + if err := dpc.ipPoolCache.Add(&pool); err != nil { + return err + } + } + return nil +} + +func (dpc *dummyPodController) withSynchedNetworkAttachments(netAttachDefClient nadclient.Interface) error { + const allNamespaces = "" + + networkAttachments, err := netAttachDefClient.K8sCniCncfIoV1().NetworkAttachmentDefinitions(allNamespaces).List( + context.TODO(), metav1.ListOptions{}) + if err != nil { + return err + } + for _, network := range networkAttachments.Items { + if err := dpc.networkCache.Add(&network); err != nil { + return err + } + } + return nil +} + +func (dpc *dummyPodController) withSynchedPods(k8sClient k8sclient.Interface) error { + const allNamespaces = "" + + pods, err := k8sClient.CoreV1().Pods(allNamespaces).List(context.TODO(), metav1.ListOptions{}) + if err != nil { + return err + } + for _, pod := range pods.Items { + if err := dpc.podCache.Add(&pod); err != nil { + return err + } + } + return nil +} + var _ = Describe("IPControlLoop", func() { const ( dummyNetIPRange = "192.168.2.0/24" @@ -56,7 +101,6 @@ var _ = Describe("IPControlLoop", func() { k8sClient k8sclient.Interface cniConfigDir string netAttachDefClient nadclient.Interface - networkDefinition *nad.NetworkAttachmentDefinition pod *v1.Pod wbClient wbclient.Interface ) @@ -81,8 +125,10 @@ var _ = Describe("IPControlLoop", func() { pod = podSpec(podName, namespace, networkName) k8sClient = fakek8sclient.NewSimpleClientset(pod) - networkDefinition = netAttachDef(networkName, namespace, dummyNetSpec(networkName, dummyNetIPRange)) - netAttachDefClient = fakenadclient.NewSimpleClientset(networkDefinition) + + var err error + netAttachDefClient, err = newFakeNetAttachDefClient(namespace, netAttachDef(networkName, namespace, dummyNetSpec(networkName, dummyNetIPRange))) + Expect(err).NotTo(HaveOccurred()) }) AfterEach(func() { @@ -101,7 +147,7 @@ var _ = Describe("IPControlLoop", func() { BeforeEach(func() { stopChannel = make(chan struct{}) - _ = newDummyPodController(k8sClient, wbClient, netAttachDefClient, stopChannel, cniConfigDir) + Expect(newDummyPodController(k8sClient, wbClient, netAttachDefClient, stopChannel, cniConfigDir)).NotTo(BeNil()) }) AfterEach(func() { @@ -122,7 +168,6 @@ var _ = Describe("IPControlLoop", func() { Context("IPPool featuring an allocation for the pod", func() { var ( dummyNetworkPool *v1alpha1.IPPool - podController *dummyPodController stopChannel chan struct{} ) @@ -130,12 +175,8 @@ var _ = Describe("IPControlLoop", func() { stopChannel = make(chan struct{}) dummyNetworkPool = ipPool(dummyNetIPRange, podReference(pod)) wbClient = fakewbclient.NewSimpleClientset(dummyNetworkPool) - podController = newDummyPodController(k8sClient, wbClient, netAttachDefClient, stopChannel, cniConfigDir) - // provision informer cache - Expect(podController.ipPoolCache.Add(dummyNetworkPool)).To(Succeed()) - Expect(podController.networkCache.Add(networkDefinition)).To(Succeed()) - Expect(podController.podCache.Add(pod)).To(Succeed()) + Expect(newDummyPodController(k8sClient, wbClient, netAttachDefClient, stopChannel, cniConfigDir)).NotTo(BeNil()) }) AfterEach(func() { @@ -162,6 +203,23 @@ var _ = Describe("IPControlLoop", func() { }) }) +func newFakeNetAttachDefClient(namespace string, networkAttachments ...nad.NetworkAttachmentDefinition) (nadclient.Interface, error) { + var netAttachDefClient nadclient.Interface = fakenadclient.NewSimpleClientset() + gvr := metav1.GroupVersionResource{ + Group: "k8s.cni.cncf.io", + Version: "v1", + Resource: "network-attachment-definitions", + } + + clientSet := netAttachDefClient.(*fakenadclient.Clientset) + for _, networkAttachment := range networkAttachments { + if err := clientSet.Tracker().Create(schema.GroupVersionResource(gvr), &networkAttachment, namespace); err != nil { + return nil, err + } + } + return netAttachDefClient, nil +} + func dummyNetSpec(networkName string, ipRange string) string { return fmt.Sprintf(`{ "cniVersion": "0.3.0", @@ -186,8 +244,8 @@ func podSpec(name string, namespace string, networks ...string) *v1.Pod { } } -func netAttachDef(netName string, namespace string, config string) *nad.NetworkAttachmentDefinition { - return &nad.NetworkAttachmentDefinition{ +func netAttachDef(netName string, namespace string, config string) nad.NetworkAttachmentDefinition { + return nad.NetworkAttachmentDefinition{ ObjectMeta: metav1.ObjectMeta{ Name: netName, Namespace: namespace, @@ -251,7 +309,7 @@ func newDummyPodController( wbClient wbclient.Interface, nadClient nadclient.Interface, stopChannel chan struct{}, - mountPath string) *dummyPodController { + mountPath string) (*dummyPodController, error) { const noResyncPeriod = 0 netAttachDefInformerFactory := nadinformers.NewSharedInformerFactory(nadClient, noResyncPeriod) @@ -289,14 +347,26 @@ func newDummyPodController( podController.handler.mountPath = mountPath - go podController.Start(stopChannel) - - return &dummyPodController{ + controller := &dummyPodController{ PodController: podController, ipPoolCache: podController.ipPoolInformer.GetStore(), networkCache: podController.netAttachDefInformer.GetStore(), podCache: podController.podsInformer.GetStore(), } + + if err := controller.withSynchedIPPool(wbClient); err != nil { + return nil, err + } + if err := controller.withSynchedPods(k8sClient); err != nil { + return nil, err + } + if err := controller.withSynchedNetworkAttachments(nadClient); err != nil { + return nil, err + } + + go podController.Start(stopChannel) + + return controller, nil } func podReference(pod *v1.Pod) string {