From bd0d27532fe7d31917fc55c7718974dfca7337d5 Mon Sep 17 00:00:00 2001 From: NamanAg30 <47788349+NamanAg30@users.noreply.github.com> Date: Sat, 14 Jan 2023 00:04:08 +0530 Subject: [PATCH] Add unit tests for pkg/monitor (#4370) Signed-off-by: Naman Agarwal --- pkg/monitor/agent_test.go | 153 ++++++++ pkg/monitor/controller_test.go | 626 +++++++++++++++++++++++++++++++++ 2 files changed, 779 insertions(+) create mode 100644 pkg/monitor/agent_test.go create mode 100644 pkg/monitor/controller_test.go diff --git a/pkg/monitor/agent_test.go b/pkg/monitor/agent_test.go new file mode 100644 index 00000000000..2fcad18691b --- /dev/null +++ b/pkg/monitor/agent_test.go @@ -0,0 +1,153 @@ +// Copyright 2023 Antrea Authors +// +// 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 monitor + +import ( + "context" + "errors" + "testing" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes/fake" + cgtesting "k8s.io/client-go/testing" + + "antrea.io/antrea/pkg/agent/config" + interfacestoretest "antrea.io/antrea/pkg/agent/interfacestore/testing" + openflowtest "antrea.io/antrea/pkg/agent/openflow/testing" + "antrea.io/antrea/pkg/agent/querier" + "antrea.io/antrea/pkg/apis/crd/v1beta1" + fakeclientset "antrea.io/antrea/pkg/client/clientset/versioned/fake" + binding "antrea.io/antrea/pkg/ovs/openflow" + ovsconfigtest "antrea.io/antrea/pkg/ovs/ovsconfig/testing" + queriertest "antrea.io/antrea/pkg/querier/testing" +) + +const ovsVersion = "2.10.0" + +func TestSyncAgentCRD(t *testing.T) { + ctx := context.Background() + existingCRD := &v1beta1.AntreaAgentInfo{ + ObjectMeta: metav1.ObjectMeta{ + Name: "testAgentCRD", + }, + NetworkPolicyControllerInfo: v1beta1.NetworkPolicyControllerInfo{ + NetworkPolicyNum: 1, + }, + APIPort: 0, + } + partiallyUpdatedCRD := &v1beta1.AntreaAgentInfo{ + ObjectMeta: metav1.ObjectMeta{ + Name: crdName, + }, + NetworkPolicyControllerInfo: v1beta1.NetworkPolicyControllerInfo{ + NetworkPolicyNum: 10, + }, + APIPort: 0, + } + entirelyUpdatedCRD := &v1beta1.AntreaAgentInfo{ + ObjectMeta: metav1.ObjectMeta{ + Name: crdName, + }, + NetworkPolicyControllerInfo: v1beta1.NetworkPolicyControllerInfo{ + NetworkPolicyNum: 0, + }, + APIPort: 10349, + } + t.Run("partial update-success", func(t *testing.T) { + clientset := fakeclientset.NewSimpleClientset(existingCRD) + monitor := newAgentMonitor(clientset, t) + monitor.agentCRD = existingCRD + monitor.syncAgentCRD() + crd, err := monitor.client.CrdV1beta1().AntreaAgentInfos().Get(ctx, "testAgentCRD", metav1.GetOptions{}) + require.NoError(t, err) + assert.Equal(t, partiallyUpdatedCRD.NetworkPolicyControllerInfo.NetworkPolicyNum, crd.NetworkPolicyControllerInfo.NetworkPolicyNum) + }) + t.Run("partial update-failure", func(t *testing.T) { + clientset := fakeclientset.NewSimpleClientset(existingCRD) + clientset.PrependReactor("update", "antreaagentinfos", func(action cgtesting.Action) (handled bool, ret runtime.Object, err error) { + return true, &v1beta1.AntreaAgentInfo{}, errors.New("error updating agent crd") + }) + monitor := newAgentMonitor(clientset, t) + monitor.agentCRD = existingCRD + monitor.syncAgentCRD() + assert.Nil(t, monitor.agentCRD) + }) + t.Run("get-failure", func(t *testing.T) { + clientset := fakeclientset.NewSimpleClientset() + clientset.PrependReactor("get", "antreaagentinfos", func(action cgtesting.Action) (handled bool, ret runtime.Object, err error) { + return true, &v1beta1.AntreaAgentInfo{}, errors.New("error getting agent crd") + }) + monitor := newAgentMonitor(clientset, t) + monitor.agentCRD = existingCRD + monitor.syncAgentCRD() + assert.Nil(t, monitor.agentCRD) + }) + t.Run("entire update-success", func(t *testing.T) { + clientset := fakeclientset.NewSimpleClientset(existingCRD) + monitor := newAgentMonitor(clientset, t) + monitor.syncAgentCRD() + crd, err := monitor.client.CrdV1beta1().AntreaAgentInfos().Get(ctx, "testAgentCRD", metav1.GetOptions{}) + require.NoError(t, err) + assert.Equal(t, entirelyUpdatedCRD.APIPort, crd.APIPort) + }) + t.Run("entire update-failure", func(t *testing.T) { + clientset := fakeclientset.NewSimpleClientset(existingCRD) + clientset.PrependReactor("update", "antreaagentinfos", func(action cgtesting.Action) (handled bool, ret runtime.Object, err error) { + return true, &v1beta1.AntreaAgentInfo{}, errors.New("error updating agent crd") + }) + monitor := newAgentMonitor(clientset, t) + monitor.syncAgentCRD() + assert.Nil(t, monitor.agentCRD) + }) +} + +func newAgentMonitor(crdClient *fakeclientset.Clientset, t *testing.T) *agentMonitor { + client := fake.NewSimpleClientset() + ctrl := gomock.NewController(t) + + nodeConfig := &config.NodeConfig{ + Name: "testAgentCRD", + } + + interfaceStore := interfacestoretest.NewMockInterfaceStore(ctrl) + interfaceStore.EXPECT().GetContainerInterfaceNum().Return(2).AnyTimes() + + ofClient := openflowtest.NewMockClient(ctrl) + ofClient.EXPECT().GetFlowTableStatus().Return([]binding.TableStatus{ + { + ID: 1, + Name: "1", + FlowCount: 2, + }, + }).AnyTimes() + ofClient.EXPECT().IsConnected().Return(true).AnyTimes() + + ovsBridgeClient := ovsconfigtest.NewMockOVSBridgeClient(ctrl) + ovsBridgeClient.EXPECT().GetOVSVersion().Return(ovsVersion, nil).AnyTimes() + + networkPolicyInfoQuerier := queriertest.NewMockAgentNetworkPolicyInfoQuerier(ctrl) + networkPolicyInfoQuerier.EXPECT().GetNetworkPolicyNum().Return(10).AnyTimes() + networkPolicyInfoQuerier.EXPECT().GetAppliedToGroupNum().Return(20).AnyTimes() + networkPolicyInfoQuerier.EXPECT().GetAddressGroupNum().Return(30).AnyTimes() + networkPolicyInfoQuerier.EXPECT().GetControllerConnectionStatus().Return(true).AnyTimes() + + querier := querier.NewAgentQuerier(nodeConfig, nil, interfaceStore, client, ofClient, ovsBridgeClient, nil, networkPolicyInfoQuerier, 10349, "") + + return NewAgentMonitor(crdClient, querier) +} diff --git a/pkg/monitor/controller_test.go b/pkg/monitor/controller_test.go new file mode 100644 index 00000000000..452bdcf2f57 --- /dev/null +++ b/pkg/monitor/controller_test.go @@ -0,0 +1,626 @@ +// Copyright 2023 Antrea Authors +// +// 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 monitor + +import ( + "context" + "errors" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + v1 "k8s.io/api/core/v1" + errortesting "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes/fake" + cgtesting "k8s.io/client-go/testing" + + "antrea.io/antrea/pkg/apis/crd/v1alpha1" + "antrea.io/antrea/pkg/apis/crd/v1beta1" + fakeclientset "antrea.io/antrea/pkg/client/clientset/versioned/fake" + crdinformers "antrea.io/antrea/pkg/client/informers/externalversions" + "antrea.io/antrea/pkg/controller/grouping" + "antrea.io/antrea/pkg/controller/labelidentity" + "antrea.io/antrea/pkg/controller/networkpolicy" + "antrea.io/antrea/pkg/controller/networkpolicy/store" + "antrea.io/antrea/pkg/controller/querier" +) + +const ( + informerDefaultResync = 12 * time.Hour +) + +type fakeController struct { + controllerMonitor *controllerMonitor + crdClient *fakeclientset.Clientset + client *fake.Clientset + informerFactory informers.SharedInformerFactory + crdInformerFactory crdinformers.SharedInformerFactory +} + +func newControllerMonitor(crdClient *fakeclientset.Clientset) *fakeController { + client := fake.NewSimpleClientset() + informerFactory := informers.NewSharedInformerFactory(client, informerDefaultResync) + crdInformerFactory := crdinformers.NewSharedInformerFactory(crdClient, informerDefaultResync) + namespaceInformer := informerFactory.Core().V1().Namespaces() + serviceInformer := informerFactory.Core().V1().Services() + networkPolicyInformer := informerFactory.Networking().V1().NetworkPolicies() + nodeInformer := informerFactory.Core().V1().Nodes() + cnpInformer := crdInformerFactory.Crd().V1alpha1().ClusterNetworkPolicies() + anpInformer := crdInformerFactory.Crd().V1alpha1().NetworkPolicies() + tierInformer := crdInformerFactory.Crd().V1alpha1().Tiers() + cgInformer := crdInformerFactory.Crd().V1alpha3().ClusterGroups() + grpInformer := crdInformerFactory.Crd().V1alpha3().Groups() + externalNodeInformer := crdInformerFactory.Crd().V1alpha1().ExternalNodes() + + addressGroupStore := store.NewAddressGroupStore() + appliedToGroupStore := store.NewAppliedToGroupStore() + networkPolicyStore := store.NewNetworkPolicyStore() + groupStore := store.NewGroupStore() + groupEntityIndex := grouping.NewGroupEntityIndex() + labelIdentityIndex := labelidentity.NewLabelIdentityIndex() + + networkPolicyController := networkpolicy.NewNetworkPolicyController(client, + crdClient, + groupEntityIndex, + labelIdentityIndex, + namespaceInformer, + serviceInformer, + networkPolicyInformer, + nodeInformer, + cnpInformer, + anpInformer, + tierInformer, + cgInformer, + grpInformer, + addressGroupStore, + appliedToGroupStore, + networkPolicyStore, + groupStore, + false) + + controllerQuerier := querier.NewControllerQuerier(networkPolicyController, 10349) + externalNodeEnabled := true + controllerMonitor := NewControllerMonitor(crdClient, nodeInformer, externalNodeInformer, controllerQuerier, externalNodeEnabled) + + return &fakeController{ + controllerMonitor, + crdClient, + client, + informerFactory, + crdInformerFactory, + } +} + +func initController(controller *fakeController, stopCh chan struct{}) { + controller.informerFactory.Start(stopCh) + controller.crdInformerFactory.Start(stopCh) + controller.informerFactory.WaitForCacheSync(stopCh) + controller.crdInformerFactory.WaitForCacheSync(stopCh) +} + +func TestSyncExternalNode(t *testing.T) { + ctx := context.Background() + en := &v1alpha1.ExternalNode{ + ObjectMeta: metav1.ObjectMeta{ + Name: "TestExternalNode", + Namespace: "ns1", + Labels: map[string]string{"en": "vm2"}, + }, + } + tc := []struct { + name string + key string + existingExternalNode *v1alpha1.ExternalNode + expectedAgentCRD *v1beta1.AntreaAgentInfo + expectedError string + }{ + { + name: "Invalid key format", + key: "namespace/name/error", + expectedError: "unexpected key format: \"namespace/name/error\"", + }, + { + name: "Key does not exist", + key: "default/vm2-e8be5", + expectedError: "", + }, + { + name: "Key exists", + key: "ns1/TestExternalNode", + existingExternalNode: en, + expectedAgentCRD: &v1beta1.AntreaAgentInfo{ + ObjectMeta: metav1.ObjectMeta{ + Name: "TestExternalNode", + }, + }, + expectedError: "", + }, + } + for _, tt := range tc { + t.Run(tt.name, func(t *testing.T) { + var clientset *fakeclientset.Clientset + if tt.existingExternalNode != nil { + clientset = fakeclientset.NewSimpleClientset(tt.existingExternalNode) + } else { + clientset = fakeclientset.NewSimpleClientset() + } + controller := newControllerMonitor(clientset) + stopCh := make(chan struct{}) + initController(controller, stopCh) + defer close(stopCh) + err := controller.controllerMonitor.syncExternalNode(tt.key) + if tt.expectedError != "" { + assert.EqualError(t, err, tt.expectedError) + } else { + require.NoError(t, err) + if tt.expectedAgentCRD == nil { + _, name, _ := splitKeyFunc(tt.key) + _, err := controller.controllerMonitor.client.CrdV1beta1().AntreaAgentInfos().Get(ctx, name, metav1.GetOptions{}) + assert.True(t, errortesting.IsNotFound(err)) + } else { + crd, err := controller.controllerMonitor.client.CrdV1beta1().AntreaAgentInfos().Get(ctx, tt.expectedAgentCRD.Name, metav1.GetOptions{}) + require.NoError(t, err) + assert.Equal(t, tt.expectedAgentCRD, crd) + } + } + }) + } +} + +func TestSyncNode(t *testing.T) { + ctx := context.Background() + node := &v1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: "TestNode", + Labels: map[string]string{"en": "vm2"}, + }, + } + tc := []struct { + name string + key string + existingNode *v1.Node + expectedAgentCRD *v1beta1.AntreaAgentInfo + expectedError string + }{ + { + name: "Invalid key format", + key: "namespace/name/error", + expectedError: "unexpected key format: \"namespace/name/error\"", + }, + { + name: "Key does not exist", + key: "default/vm2-e8be5", + expectedError: "", + }, + { + name: "Key exists", + key: "default/TestNode", + existingNode: node, + expectedAgentCRD: &v1beta1.AntreaAgentInfo{ + ObjectMeta: metav1.ObjectMeta{ + Name: "TestNode", + }, + }, + expectedError: "", + }, + } + for _, tt := range tc { + t.Run(tt.name, func(t *testing.T) { + clientset := fakeclientset.NewSimpleClientset() + controller := newControllerMonitor(clientset) + if tt.existingNode != nil { + controller.client = fake.NewSimpleClientset(node) + controller.informerFactory = informers.NewSharedInformerFactory(controller.client, informerDefaultResync) + controller.controllerMonitor.nodeInformer = controller.informerFactory.Core().V1().Nodes() + controller.controllerMonitor.nodeLister = controller.controllerMonitor.nodeInformer.Lister() + } + stopCh := make(chan struct{}) + initController(controller, stopCh) + defer close(stopCh) + err := controller.controllerMonitor.syncNode(tt.key) + if tt.expectedError != "" { + assert.EqualError(t, err, tt.expectedError) + } else { + require.NoError(t, err) + if tt.expectedAgentCRD == nil { + _, name, _ := splitKeyFunc(tt.key) + _, err := controller.controllerMonitor.client.CrdV1beta1().AntreaAgentInfos().Get(ctx, name, metav1.GetOptions{}) + assert.True(t, errortesting.IsNotFound(err)) + } else { + crd, err := controller.controllerMonitor.client.CrdV1beta1().AntreaAgentInfos().Get(ctx, tt.expectedAgentCRD.Name, metav1.GetOptions{}) + require.NoError(t, err) + assert.Equal(t, tt.expectedAgentCRD, crd) + } + } + }) + } +} + +func TestDeleteAgentCRD(t *testing.T) { + ctx := context.Background() + testAgentCRD := &v1beta1.AntreaAgentInfo{ + ObjectMeta: metav1.ObjectMeta{ + Name: "testAgentCRD", + ResourceVersion: "0", + Generation: 0, + }, + NetworkPolicyControllerInfo: v1beta1.NetworkPolicyControllerInfo{ + NetworkPolicyNum: 1, + AddressGroupNum: 1, + AppliedToGroupNum: 1, + }, + } + tc := []struct { + name string + existingAgentCRD *v1beta1.AntreaAgentInfo + prepareReactor func(clientset *fakeclientset.Clientset) + expectedError string + }{ + { + name: "deleted CRD AntreaAgentInfo with given name successfully", + prepareReactor: func(clientset *fakeclientset.Clientset) {}, + existingAgentCRD: testAgentCRD, + expectedError: "", + }, + { + name: "no-ops when the CRD AntreaAgentInfo with given name does not exist", + prepareReactor: func(clientset *fakeclientset.Clientset) {}, + expectedError: "", + }, + { + name: "fail to delete CRD AntreaAgentInfo with given name due to error", + prepareReactor: func(clientset *fakeclientset.Clientset) { + clientset.PrependReactor("delete", "antreaagentinfos", func(action cgtesting.Action) (handled bool, ret runtime.Object, err error) { + return true, &v1beta1.AntreaAgentInfo{}, errors.New("error deleting agent crd") + }) + }, + expectedError: "error deleting agent crd", + }, + } + for _, tt := range tc { + t.Run(tt.name, func(t *testing.T) { + clientset := fakeclientset.NewSimpleClientset() + if tt.existingAgentCRD != nil { + clientset = fakeclientset.NewSimpleClientset(tt.existingAgentCRD) + } + tt.prepareReactor(clientset) + controller := newControllerMonitor(clientset) + stopCh := make(chan struct{}) + initController(controller, stopCh) + defer close(stopCh) + err := controller.controllerMonitor.deleteAgentCRD("testAgentCRD") + if tt.expectedError != "" { + assert.EqualError(t, err, tt.expectedError) + } else { + require.NoError(t, err) + _, err = controller.controllerMonitor.client.CrdV1beta1().AntreaAgentInfos().Get(ctx, "testAgentCRD", metav1.GetOptions{}) + assert.True(t, errortesting.IsNotFound(err)) + } + }) + } +} + +func TestCreateAgentCRD(t *testing.T) { + ctx := context.Background() + testAgentCRD := &v1beta1.AntreaAgentInfo{ + ObjectMeta: metav1.ObjectMeta{ + Name: "testAgentCRD", + ResourceVersion: "", + Generation: 0, + }, + NetworkPolicyControllerInfo: v1beta1.NetworkPolicyControllerInfo{ + NetworkPolicyNum: 0, + AddressGroupNum: 0, + AppliedToGroupNum: 0, + }, + } + tc := []struct { + name string + existingAgentCRD *v1beta1.AntreaAgentInfo + prepareReactor func(clientset *fakeclientset.Clientset) + expectedAgentCRD *v1beta1.AntreaAgentInfo + expectedError string + }{ + { + name: "created CRD AntreaAgentInfo with given name successfully", + prepareReactor: func(clientset *fakeclientset.Clientset) {}, + expectedAgentCRD: testAgentCRD, + expectedError: "", + }, + { + name: "no-ops when the CRD AntreaAgentInfo with given name already exists", + prepareReactor: func(clientset *fakeclientset.Clientset) {}, + existingAgentCRD: testAgentCRD, + expectedAgentCRD: testAgentCRD, + expectedError: "", + }, + { + name: "fail to create CRD AntreaAgentInfo with given name due to error", + prepareReactor: func(clientset *fakeclientset.Clientset) { + clientset.PrependReactor("create", "antreaagentinfos", func(action cgtesting.Action) (handled bool, ret runtime.Object, err error) { + return true, &v1beta1.AntreaAgentInfo{}, errors.New("error creating agent crd as crd already exists") + }) + }, + existingAgentCRD: testAgentCRD, + expectedError: "error creating agent crd as crd already exists", + }, + } + for _, tt := range tc { + t.Run(tt.name, func(t *testing.T) { + clientset := fakeclientset.NewSimpleClientset() + if tt.existingAgentCRD != nil { + clientset = fakeclientset.NewSimpleClientset(tt.existingAgentCRD) + } + tt.prepareReactor(clientset) + controller := newControllerMonitor(clientset) + stopCh := make(chan struct{}) + initController(controller, stopCh) + defer close(stopCh) + err := controller.controllerMonitor.createAgentCRD("testAgentCRD") + if tt.expectedError != "" { + assert.EqualError(t, err, tt.expectedError) + } else { + require.NoError(t, err) + crd, err := controller.controllerMonitor.client.CrdV1beta1().AntreaAgentInfos().Get(ctx, tt.expectedAgentCRD.Name, metav1.GetOptions{}) + require.NoError(t, err) + assert.Equal(t, tt.expectedAgentCRD, crd) + } + }) + } +} + +func TestDeleteStaleAgentCRD(t *testing.T) { + ctx := context.Background() + tc := []struct { + name string + agentCRDs []string + existingExternalNode *v1alpha1.ExternalNode + prepareReactor func(clientset *fakeclientset.Clientset) + expectedError string + remainingAgentCRDs []string + }{ + { + name: "No external Nodes present", + agentCRDs: []string{"testAgentCRD1", "testAgentCRD2", "testAgentCRD3", "testAgentCRD4"}, + prepareReactor: func(clientset *fakeclientset.Clientset) {}, + remainingAgentCRDs: []string{}, + }, + { + name: "No Agent CRDs present", + prepareReactor: func(clientset *fakeclientset.Clientset) { + clientset.PrependReactor("list", "antreaagentinfos", func(action cgtesting.Action) (handled bool, ret runtime.Object, err error) { + return true, &v1beta1.AntreaAgentInfoList{}, errors.New("error getting agent crds") + }) + }, + expectedError: "error getting agent crds", + }, + { + name: "Both Agent CRDs and external Node present", + agentCRDs: []string{"testAgentCRD1", "testAgentCRD2"}, + existingExternalNode: &v1alpha1.ExternalNode{ObjectMeta: metav1.ObjectMeta{ + Name: "testAgentCRD1", + Namespace: "ns1", + Labels: map[string]string{"en": "vm2"}, + }, + }, + prepareReactor: func(clientset *fakeclientset.Clientset) {}, + remainingAgentCRDs: []string{"testAgentCRD1"}, + }, + } + for _, tt := range tc { + t.Run(tt.name, func(t *testing.T) { + clientset := fakeclientset.NewSimpleClientset() + if tt.existingExternalNode != nil { + clientset = fakeclientset.NewSimpleClientset(tt.existingExternalNode) + } + tt.prepareReactor(clientset) + controller := newControllerMonitor(clientset) + stopCh := make(chan struct{}) + initController(controller, stopCh) + defer close(stopCh) + controller.controllerMonitor.externalNodeEnabled = true + for _, crd := range tt.agentCRDs { + agentCRD := new(v1beta1.AntreaAgentInfo) + agentCRD.Name = crd + agentCRD.ResourceVersion = "0" + _, err := controller.controllerMonitor.client.CrdV1beta1().AntreaAgentInfos().Create(ctx, agentCRD, metav1.CreateOptions{}) + assert.NoError(t, err) + } + controller.controllerMonitor.deleteStaleAgentCRDs() + crds, err := controller.controllerMonitor.client.CrdV1beta1().AntreaAgentInfos().List(ctx, metav1.ListOptions{ResourceVersion: "0"}) + if tt.expectedError != "" { + assert.EqualError(t, err, tt.expectedError) + } else { + require.NoError(t, err) + remainingAgentCRDs := []string{} + for _, crd := range crds.Items { + remainingAgentCRDs = append(remainingAgentCRDs, crd.Name) + } + assert.Equal(t, tt.remainingAgentCRDs, remainingAgentCRDs) + } + }) + } +} + +func TestSyncControllerCRD(t *testing.T) { + ctx := context.Background() + existingCRD := &v1beta1.AntreaControllerInfo{ + ObjectMeta: metav1.ObjectMeta{ + Name: crdName, + }, + NetworkPolicyControllerInfo: v1beta1.NetworkPolicyControllerInfo{ + NetworkPolicyNum: 1, + }, + APIPort: 0, + } + partiallyUpdatedCRD := &v1beta1.AntreaControllerInfo{ + ObjectMeta: metav1.ObjectMeta{ + Name: crdName, + }, + NetworkPolicyControllerInfo: v1beta1.NetworkPolicyControllerInfo{ + NetworkPolicyNum: 0, + }, + APIPort: 0, + } + entirelyUpdatedCRD := &v1beta1.AntreaControllerInfo{ + ObjectMeta: metav1.ObjectMeta{ + Name: crdName, + }, + NetworkPolicyControllerInfo: v1beta1.NetworkPolicyControllerInfo{ + NetworkPolicyNum: 0, + }, + APIPort: 10349, + } + newCRD := &v1beta1.AntreaControllerInfo{ + ObjectMeta: metav1.ObjectMeta{ + Name: crdName, + }, + Version: "UNKNOWN", + NodeRef: v1.ObjectReference{ + Kind: "Node", + }, + ServiceRef: v1.ObjectReference{ + Kind: "Service", + Name: "antrea", + }, + NetworkPolicyControllerInfo: v1beta1.NetworkPolicyControllerInfo{ + NetworkPolicyNum: 0, + AddressGroupNum: 0, + AppliedToGroupNum: 0, + }, + APIPort: 10349, + } + t.Run("partial update-success", func(t *testing.T) { + clientset := fakeclientset.NewSimpleClientset(existingCRD) + controller := newControllerMonitor(clientset) + stopCh := make(chan struct{}) + initController(controller, stopCh) + defer close(stopCh) + controller.controllerMonitor.controllerCRD = existingCRD + controller.controllerMonitor.syncControllerCRD() + crd, err := controller.controllerMonitor.client.CrdV1beta1().AntreaControllerInfos().Get(ctx, crdName, metav1.GetOptions{}) + require.NoError(t, err) + assert.Equal(t, partiallyUpdatedCRD.NetworkPolicyControllerInfo.NetworkPolicyNum, crd.NetworkPolicyControllerInfo.NetworkPolicyNum) + }) + t.Run("partial update-failure", func(t *testing.T) { + clientset := fakeclientset.NewSimpleClientset(existingCRD) + clientset.PrependReactor("update", "antreacontrollerinfos", func(action cgtesting.Action) (handled bool, ret runtime.Object, err error) { + return true, &v1beta1.AntreaControllerInfo{}, errors.New("error updating controller crd") + }) + controller := newControllerMonitor(clientset) + stopCh := make(chan struct{}) + initController(controller, stopCh) + defer close(stopCh) + controller.controllerMonitor.controllerCRD = existingCRD + controller.controllerMonitor.syncControllerCRD() + assert.Nil(t, controller.controllerMonitor.controllerCRD) + }) + t.Run("get-failure", func(t *testing.T) { + clientset := fakeclientset.NewSimpleClientset() + clientset.PrependReactor("get", "antreacontrollerinfos", func(action cgtesting.Action) (handled bool, ret runtime.Object, err error) { + return true, &v1beta1.AntreaControllerInfo{}, errors.New("error getting controller crd") + }) + controller := newControllerMonitor(clientset) + stopCh := make(chan struct{}) + initController(controller, stopCh) + defer close(stopCh) + controller.controllerMonitor.syncControllerCRD() + assert.Nil(t, controller.controllerMonitor.controllerCRD) + }) + t.Run("create-success", func(t *testing.T) { + clientset := fakeclientset.NewSimpleClientset() + controller := newControllerMonitor(clientset) + stopCh := make(chan struct{}) + initController(controller, stopCh) + defer close(stopCh) + controller.controllerMonitor.querier.GetControllerInfo(newCRD, false) + controller.controllerMonitor.syncControllerCRD() + crd, err := controller.controllerMonitor.client.CrdV1beta1().AntreaControllerInfos().Get(ctx, crdName, metav1.GetOptions{}) + newCRD.ControllerConditions[0].LastHeartbeatTime.Time = crd.ControllerConditions[0].LastHeartbeatTime.Time + require.NoError(t, err) + assert.Equal(t, newCRD, crd) + }) + t.Run("create-failure", func(t *testing.T) { + clientset := fakeclientset.NewSimpleClientset() + clientset.PrependReactor("create", "antreacontrollerinfos", func(action cgtesting.Action) (handled bool, ret runtime.Object, err error) { + return true, &v1beta1.AntreaControllerInfo{}, errors.New("error in creating controller crd") + }) + controller := newControllerMonitor(clientset) + stopCh := make(chan struct{}) + initController(controller, stopCh) + defer close(stopCh) + controller.controllerMonitor.syncControllerCRD() + assert.Nil(t, controller.controllerMonitor.controllerCRD) + }) + t.Run("entire update-success", func(t *testing.T) { + clientset := fakeclientset.NewSimpleClientset(existingCRD) + controller := newControllerMonitor(clientset) + stopCh := make(chan struct{}) + initController(controller, stopCh) + defer close(stopCh) + controller.controllerMonitor.syncControllerCRD() + crd, err := controller.controllerMonitor.client.CrdV1beta1().AntreaControllerInfos().Get(ctx, crdName, metav1.GetOptions{}) + require.NoError(t, err) + assert.Equal(t, entirelyUpdatedCRD.APIPort, crd.APIPort) + }) + t.Run("entire update-failure", func(t *testing.T) { + clientset := fakeclientset.NewSimpleClientset(existingCRD) + clientset.PrependReactor("update", "antreacontrollerinfos", func(action cgtesting.Action) (handled bool, ret runtime.Object, err error) { + return true, &v1beta1.AntreaControllerInfo{}, errors.New("error updating controller crd") + }) + controller := newControllerMonitor(clientset) + stopCh := make(chan struct{}) + initController(controller, stopCh) + defer close(stopCh) + controller.controllerMonitor.syncControllerCRD() + assert.Nil(t, controller.controllerMonitor.controllerCRD) + }) +} + +func TestEnqueueNode(t *testing.T) { + node := &v1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: "TestNode", + Namespace: "ns1", + Labels: map[string]string{"en": "vm2"}, + }, + } + clientset := fakeclientset.NewSimpleClientset() + controller := newControllerMonitor(clientset) + controller.controllerMonitor.enqueueNode(node) + expectedkey, _ := keyFunc(node) + obj, _ := controller.controllerMonitor.nodeQueue.Get() + assert.Equal(t, expectedkey, obj.(string)) +} + +func TestEnqueueExternalNode(t *testing.T) { + externalNode := &v1alpha1.ExternalNode{ + ObjectMeta: metav1.ObjectMeta{ + Name: "TestExternalNode", + Namespace: "ns1", + Labels: map[string]string{"en": "vm2"}, + }, + } + clientset := fakeclientset.NewSimpleClientset() + controller := newControllerMonitor(clientset) + controller.controllerMonitor.enqueueExternalNode(externalNode) + expectedkey, _ := keyFunc(externalNode) + obj, _ := controller.controllerMonitor.externalNodeQueue.Get() + assert.Equal(t, expectedkey, obj.(string)) +}