Skip to content

Commit

Permalink
UDN: Add vrfmanager unit tests
Browse files Browse the repository at this point in the history
This commit adds required unit tests for vrf manager and secondary
node network controller to test add vrf, delete vrf and reconcile
vrf functions.

Signed-off-by: Periyasamy Palanisamy <[email protected]>
  • Loading branch information
pperiyasamy authored and trozet committed Aug 7, 2024
1 parent 570f216 commit 3e9f84e
Show file tree
Hide file tree
Showing 7 changed files with 461 additions and 13 deletions.
2 changes: 1 addition & 1 deletion go-controller/hack/test-go.sh
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ function testrun {
}

# These packages requires root for network namespace manipulation in unit tests
root_pkgs=("github.com/ovn-org/ovn-kubernetes/go-controller/pkg/node" "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/node/iptables" "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/node/rulemanager" "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/node/routemanager" "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/node/controllers/egressip")
root_pkgs=("github.com/ovn-org/ovn-kubernetes/go-controller/pkg/network-controller-manager" "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/node" "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/node/iptables" "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/node/rulemanager" "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/node/routemanager" "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/node/controllers/egressip")

# These packages are big and require more than the 10m default to run the unit tests
big_pkgs=("github.com/ovn-org/ovn-kubernetes/go-controller/pkg/ovn")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,23 +71,28 @@ func NewNetAttachDefinitionController(
ncm NetworkControllerManager,
wf watchFactory,
) (*NetAttachDefinitionController, error) {
nadInformer := wf.NADInformer()
nadController := &NetAttachDefinitionController{
name: fmt.Sprintf("[%s NAD controller]", name),
netAttachDefLister: nadInformer.Lister(),
networkManager: newNetworkManager(name, ncm),
networks: map[string]util.NetInfo{},
nads: map[string]string{},
name: fmt.Sprintf("[%s NAD controller]", name),
networkManager: newNetworkManager(name, ncm),
networks: map[string]util.NetInfo{},
nads: map[string]string{},
}

config := &controller.ControllerConfig[nettypes.NetworkAttachmentDefinition]{
RateLimiter: workqueue.DefaultControllerRateLimiter(),
Informer: nadInformer.Informer(),
Lister: nadController.netAttachDefLister.List,
Reconcile: nadController.sync,
ObjNeedsUpdate: nadNeedsUpdate,
// this controller is not thread safe
Threadiness: 1,
}

nadInformer := wf.NADInformer()
if nadInformer != nil {
nadController.netAttachDefLister = nadInformer.Lister()
config.Informer = nadInformer.Informer()
config.Lister = nadController.netAttachDefLister.List
}

nadController.controller = controller.NewController(
nadController.name,
config,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@ import (
"fmt"
"sync"

"github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/config"
factoryMocks "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/factory/mocks"
ovntest "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/testing"
"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/types"
"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/util"

v1 "k8s.io/api/core/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes/fake"
Expand Down Expand Up @@ -97,15 +100,15 @@ var _ = Describe("Healthcheck tests", func() {
Describe("checkForStaleOVSRepresentorInterfaces", func() {
var ncm *nodeNetworkControllerManager
nodeName := "localNode"
podList := []*v1.Pod{
podList := []*corev1.Pod{
{
ObjectMeta: metav1.ObjectMeta{
Name: "a-pod",
Namespace: "a-ns",
Annotations: map[string]string{},
UID: "pod-a-uuid-1",
},
Spec: v1.PodSpec{
Spec: corev1.PodSpec{
NodeName: nodeName,
},
},
Expand All @@ -116,7 +119,7 @@ var _ = Describe("Healthcheck tests", func() {
Annotations: map[string]string{},
UID: "pod-b-uuid-2",
},
Spec: v1.PodSpec{
Spec: corev1.PodSpec{
NodeName: nodeName,
},
},
Expand Down Expand Up @@ -164,5 +167,92 @@ var _ = Describe("Healthcheck tests", func() {
Expect(execMock.CalledMatchesExpected()).To(BeTrue(), execMock.ErrorDesc)
})
})

})

Context("verify cleanup of deleted networks", func() {
var (
staleNetID uint = 100
nodeName string = "worker1"
nad = ovntest.GenerateNAD("bluenet", "rednad", "greenamespace",
types.Layer3Topology, "100.128.0.0/16", types.NetworkRolePrimary)
netName = "bluenet"
netID = 3
v4NodeSubnet = "10.128.0.0/24"
v6NodeSubnet = "ae70::66/112"
testNS ns.NetNS
fakeClient *util.OVNClientset
)

BeforeEach(func() {
// Restore global default values before each testcase
Expect(config.PrepareTestConfig()).To(Succeed())

testNS, err = testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
v1Objects := []runtime.Object{}
fakeClient = &util.OVNClientset{
KubeClient: fake.NewSimpleClientset(v1Objects...),
}
})

AfterEach(func() {
Expect(testNS.Close()).To(Succeed())
Expect(testutils.UnmountNS(testNS)).To(Succeed())
})

It("check vrf devices are cleaned for deleted networks", func() {
config.OVNKubernetesFeature.EnableNetworkSegmentation = true
config.OVNKubernetesFeature.EnableMultiNetwork = true

factoryMock := factoryMocks.NodeWatchFactory{}
NetInfo, err := util.ParseNADInfo(nad)
Expect(err).NotTo(HaveOccurred())
node := &corev1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: nodeName,
Annotations: map[string]string{
"k8s.ovn.org/network-ids": fmt.Sprintf("{\"%s\": \"%d\"}", netName, netID),
"k8s.ovn.org/node-subnets": fmt.Sprintf("{\"%s\":[\"%s\", \"%s\"]}", netName, v4NodeSubnet, v6NodeSubnet)},
},
}
nodeList := []*corev1.Node{node}
factoryMock.On("GetNode", nodeName).Return(nodeList[0], nil)
factoryMock.On("GetNodes").Return(nodeList, nil)
factoryMock.On("NADInformer").Return(nil)

ncm, err := NewNodeNetworkControllerManager(fakeClient, &factoryMock, nodeName, &sync.WaitGroup{}, nil)
Expect(err).NotTo(HaveOccurred())

err = testNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()

staleVrfDevice := util.GetVRFDeviceNameForUDN(int(staleNetID))
ovntest.AddVRFLink(staleVrfDevice, uint32(staleNetID))
_, err = util.GetNetLinkOps().LinkByName(staleVrfDevice)
Expect(err).NotTo(HaveOccurred())

validVrfDevice := util.GetVRFDeviceNameForUDN(int(netID))
ovntest.AddVRFLink(validVrfDevice, uint32(netID))
_, err = util.GetNetLinkOps().LinkByName(validVrfDevice)
Expect(err).NotTo(HaveOccurred())

err = ncm.CleanupDeletedNetworks(NetInfo)
Expect(err).NotTo(HaveOccurred())

// Verify CleanupDeletedNetworks cleans up VRF configuration for
// already deleted network.
_, err = util.GetNetLinkOps().LinkByName(staleVrfDevice)
Expect(err).To(HaveOccurred())

// Verify CleanupDeletedNetworks didn't cleanup VRF configuration for
// existing network.
_, err = util.GetNetLinkOps().LinkByName(validVrfDevice)
Expect(err).NotTo(HaveOccurred())

return nil
})
Expect(err).NotTo(HaveOccurred())
})
})
})
148 changes: 148 additions & 0 deletions go-controller/pkg/node/secondary_node_network_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,16 @@ package node

import (
"context"
"fmt"
"sync"
"time"

"github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/stretchr/testify/mock"
"github.com/vishvananda/netlink"

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand All @@ -13,6 +20,8 @@ import (
"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/config"
"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/factory"
factoryMocks "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/factory/mocks"
kubemocks "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/kube/mocks"
"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/node/vrfmanager"
ovntest "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/testing"
coreinformermocks "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/testing/mocks/k8s.io/client-go/informers/core/v1"
v1mocks "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/testing/mocks/k8s.io/client-go/listers/core/v1"
Expand All @@ -24,7 +33,62 @@ var _ = Describe("SecondaryNodeNetworkController", func() {
var (
nad = ovntest.GenerateNAD("bluenet", "rednad", "greenamespace",
types.Layer3Topology, "100.128.0.0/16", types.NetworkRolePrimary)
netName = "bluenet"
netID = 3
nodeName string = "worker1"
mgtPortMAC string = "00:00:00:55:66:77"
fexec *ovntest.FakeExec
testNS ns.NetNS
vrf *vrfmanager.Controller
v4NodeSubnet = "10.128.0.0/24"
v6NodeSubnet = "ae70::66/112"
mgtPort = fmt.Sprintf("%s%d", types.K8sMgmtIntfNamePrefix, netID)
stopCh chan struct{}
wg *sync.WaitGroup
kubeMock kubemocks.Interface
)
BeforeEach(func() {
// Restore global default values before each testcase
Expect(config.PrepareTestConfig()).To(Succeed())
// Set up a fake vsctl command mock interface
kubeMock = kubemocks.Interface{}
fexec = ovntest.NewFakeExec()
err := util.SetExec(fexec)
Expect(err).NotTo(HaveOccurred())
// Set up a fake k8sMgmt interface
testNS, err = testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
err = testNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
ovntest.AddLink(mgtPort)
return nil
})
Expect(err).NotTo(HaveOccurred())
wg = &sync.WaitGroup{}
stopCh = make(chan struct{})
vrf = vrfmanager.NewController()
wg2 := &sync.WaitGroup{}
defer func() {
wg2.Wait()
}()
wg2.Add(1)
go testNS.Do(func(netNS ns.NetNS) error {
defer wg2.Done()
defer GinkgoRecover()
err = vrf.Run(stopCh, wg)
Expect(err).NotTo(HaveOccurred())
return nil
})
})
AfterEach(func() {
defer func() {
close(stopCh)
wg.Wait()
}()
Expect(testNS.Close()).To(Succeed())
Expect(testutils.UnmountNS(testNS)).To(Succeed())
})

It("should return networkID from one of the nodes in the cluster", func() {
fakeClient := &util.OVNNodeClientset{
KubeClient: fake.NewSimpleClientset(&corev1.Node{
Expand Down Expand Up @@ -156,4 +220,88 @@ var _ = Describe("SecondaryNodeNetworkController", func() {
Expect(err).NotTo(HaveOccurred())
Expect(controller.gateway).To(BeNil())
})
It("ensure UDNGateway and VRFManager is invoked for Primary UDNs when feature gate is ON", func() {
config.OVNKubernetesFeature.EnableNetworkSegmentation = true
config.OVNKubernetesFeature.EnableMultiNetwork = true

By("creating necessary mocks")
factoryMock := factoryMocks.NodeWatchFactory{}
node := &corev1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: nodeName,
Annotations: map[string]string{
"k8s.ovn.org/network-ids": fmt.Sprintf("{\"%s\": \"%d\"}", netName, netID),
"k8s.ovn.org/node-subnets": fmt.Sprintf("{\"%s\":[\"%s\", \"%s\"]}", netName, v4NodeSubnet, v6NodeSubnet)},
},
}
nodeList := []*corev1.Node{node}
factoryMock.On("GetNode", nodeName).Return(nodeList[0], nil)
factoryMock.On("GetNodes").Return(nodeList, nil)
nodeInformer := coreinformermocks.NodeInformer{}
factoryMock.On("NodeCoreInformer").Return(&nodeInformer)
nodeLister := v1mocks.NodeLister{}
nodeInformer.On("Lister").Return(&nodeLister)
nodeLister.On("Get", mock.AnythingOfType("string")).Return(node, nil)
cnode := node.DeepCopy()
cnode.Annotations[util.OvnNodeManagementPortMacAddresses] = `{"bluenet":"00:00:00:55:66:77"}`
kubeMock.On("UpdateNodeStatus", cnode).Return(nil)

By("creating NAD for primary UDN")
nad = ovntest.GenerateNAD("bluenet", "rednad", "greenamespace",
types.Layer3Topology, "100.128.0.0/16", types.NetworkRolePrimary)
NetInfo, err := util.ParseNADInfo(nad)
Expect(err).NotTo(HaveOccurred())

By("creating secondary network controller for user defined primary network")
cnnci := CommonNodeNetworkControllerInfo{name: nodeName, watchFactory: &factoryMock}
controller, err := NewSecondaryNodeNetworkController(&cnnci, NetInfo, vrf)
Expect(err).NotTo(HaveOccurred())
Expect(controller.gateway).To(Not(BeNil()))
controller.gateway.kubeInterface = &kubeMock

err = testNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
getCreationFakeOVSCommands(fexec, mgtPort, mgtPortMAC, netName, nodeName, NetInfo.MTU())
Expect(err).NotTo(HaveOccurred())
getDeletionFakeOVSCommands(fexec, mgtPort)
Expect(err).NotTo(HaveOccurred())

By("starting secondary network controller for user defined primary network")
err = controller.Start(context.Background())
Expect(err).NotTo(HaveOccurred())

By("check management interface and VRF device is created for the network")
vrfDeviceName := util.GetVRFDeviceNameForUDN(netID)
vrfLink, err := util.GetNetLinkOps().LinkByName(vrfDeviceName)
Expect(err).NotTo(HaveOccurred())
Expect(vrfLink.Type()).To(Equal("vrf"))
vrfDev, ok := vrfLink.(*netlink.Vrf)
Expect(ok).To(Equal(true))
mplink, err := util.GetNetLinkOps().LinkByName(mgtPort)
Expect(err).NotTo(HaveOccurred())
vrfTableId := util.CalculateRouteTableID(mplink.Attrs().Index)
Expect(vrfDev.Table).To(Equal(uint32(vrfTableId)))

By("delete VRF device explicitly and ensure VRF Manager reconciles it")
err = util.GetNetLinkOps().LinkDelete(vrfLink)
Expect(err).NotTo(HaveOccurred())
Eventually(func() error {
_, err := util.GetNetLinkOps().LinkByName(vrfDeviceName)
return err
}).WithTimeout(120 * time.Second).Should(BeNil())

By("delete the network and ensure its associated VRF device is also deleted")
cnode = node.DeepCopy()
kubeMock.On("UpdateNodeStatus", cnode).Return(nil)
err = controller.Cleanup()
Expect(err).NotTo(HaveOccurred())
Eventually(func() error {
_, err := util.GetNetLinkOps().LinkByName(vrfDeviceName)
return err
}).WithTimeout(120 * time.Second).ShouldNot(BeNil())
return nil
})
Expect(err).NotTo(HaveOccurred())
Expect(fexec.CalledMatchesExpected()).To(BeTrue(), fexec.ErrorDesc)
})
})
13 changes: 13 additions & 0 deletions go-controller/pkg/node/vrfmanager/vrf_manager_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package vrfmanager

import (
"testing"

"github.com/onsi/ginkgo"
"github.com/onsi/gomega"
)

func TestAdder(t *testing.T) {
gomega.RegisterFailHandler(ginkgo.Fail)
ginkgo.RunSpecs(t, "Vrf Manager Suite")
}
Loading

0 comments on commit 3e9f84e

Please sign in to comment.