From 6fca64a04d9d3629912b1b1ffa3910d81e11881a Mon Sep 17 00:00:00 2001 From: "camillo.rossi@gmail.com" Date: Thu, 2 Feb 2023 22:33:46 +1100 Subject: [PATCH 1/7] Closes #22 --- app/graph.py | 18 ++++++++++++++++++ helm/templates/clusterrole.yaml | 3 +++ 2 files changed, 21 insertions(+) diff --git a/app/graph.py b/app/graph.py index 1991645..7b6b179 100644 --- a/app/graph.py +++ b/app/graph.py @@ -275,6 +275,24 @@ def get_cluster_as(self): if "--cluster-asn=" in arg: asn = arg[14:] logger.info('Kube-Router Detected! Cluster AS=%s',asn) + except Exception as e: + pass + + # Try to get Cluster AS from Cilium Config + try: + #We support only a singe AS per Cluster I use a set to ensure that + asn_set = set() + logger.debug("Try to detect Cilium") + #Get all the CiliumBGPPeeringPolicies traverse them and the virtualRouters and add all the found ASN in the set + CiliumBGPPeeringPolicies = self.custom_obj.list_cluster_custom_object(group="cilium.io",version="v2alpha1",plural="ciliumbgppeeringpolicies") + for policy in CiliumBGPPeeringPolicies['items']: + for virtualrotuer in policy['spec']['virtualRouters']: + asn_set.add(str(virtualrotuer['localASN'])) + if len(asn_set) == 1: + asn = asn_set.pop() + logger.info('Cilium Detected! Cluster AS=%s',asn) + elif len(asn_set) > 1: + logger.info('Cilium Detected! More than one AS is used, this is an unsupported configuration!') except Exception as e: pass if asn == 0: diff --git a/helm/templates/clusterrole.yaml b/helm/templates/clusterrole.yaml index ab2644e..34439cd 100644 --- a/helm/templates/clusterrole.yaml +++ b/helm/templates/clusterrole.yaml @@ -12,6 +12,9 @@ rules: - apiGroups: ["crd.projectcalico.org"] resources: ["bgpconfigurations"] verbs: ["list","get"] +- apiGroups: ["cilium.io"] + resources: ["ciliumbgppeeringpolicies"] + verbs: ["list","get"] --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 From 1a2d494ab1e4ea36fd6247f4dc3762fde9208af8 Mon Sep 17 00:00:00 2001 From: "camillo.rossi@gmail.com" Date: Thu, 2 Feb 2023 22:35:38 +1100 Subject: [PATCH 2/7] Chart version update --- helm/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm/Chart.yaml b/helm/Chart.yaml index a940ac5..6e69b94 100644 --- a/helm/Chart.yaml +++ b/helm/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.1.1 +version: 1.0.2 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to From 262fda95710bada5ae089c449e625a4bd3cecf9c Mon Sep 17 00:00:00 2001 From: "camillo.rossi@gmail.com" Date: Thu, 2 Feb 2023 22:45:33 +1100 Subject: [PATCH 3/7] Closes #34 --- app/graph.py | 1 + 1 file changed, 1 insertion(+) diff --git a/app/graph.py b/app/graph.py index 7b6b179..ab65cc5 100644 --- a/app/graph.py +++ b/app/graph.py @@ -346,6 +346,7 @@ def update_node(self, apic, node): '''Gets a K8s node and populates it with the LLDP/CDP and BGP information''' if 'mac' not in node: logger.error("Could not resolved the mac address of node with ip %s", node['node_ip'] ) + logger.error("This usually happnes if the Tenant/VRF config is wrong, I am configured to use '%s', is it coorect?", self.aci_vrf) exit() #Find the mac to interface mapping logger.info("Find the mac to interface mapping for Node %s with MAC %s", node['node_ip'], node['mac']) From 5cd76e318f0a29448b8e787cc8432673f9a08b3c Mon Sep 17 00:00:00 2001 From: "camillo.rossi@gmail.com" Date: Thu, 2 Feb 2023 23:06:07 +1100 Subject: [PATCH 4/7] Fix Typo --- app/graph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/graph.py b/app/graph.py index ab65cc5..72ba00c 100644 --- a/app/graph.py +++ b/app/graph.py @@ -346,7 +346,7 @@ def update_node(self, apic, node): '''Gets a K8s node and populates it with the LLDP/CDP and BGP information''' if 'mac' not in node: logger.error("Could not resolved the mac address of node with ip %s", node['node_ip'] ) - logger.error("This usually happnes if the Tenant/VRF config is wrong, I am configured to use '%s', is it coorect?", self.aci_vrf) + logger.error("This usually happnes if the Tenant/VRF config is wrong, I am configured to use '%s', is it correct?", self.aci_vrf) exit() #Find the mac to interface mapping logger.info("Find the mac to interface mapping for Node %s with MAC %s", node['node_ip'], node['mac']) From e70a50614d1fcfb41ce9fcc5e6e8a1ced80d8526 Mon Sep 17 00:00:00 2001 From: "camillo.rossi@gmail.com" Date: Tue, 21 Feb 2023 09:57:48 +1100 Subject: [PATCH 5/7] added unit testing --- app/graph.py | 6 +- unit_test.py | 182 ++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 175 insertions(+), 13 deletions(-) diff --git a/app/graph.py b/app/graph.py index 3c4a362..b0cb867 100644 --- a/app/graph.py +++ b/app/graph.py @@ -249,7 +249,7 @@ def get_cluster_as(self): logger.debug("Detect Cluster AS") # Try to get Cluster AS from Calico Config try: - logger.debug("Try to detect Calico") + logger.info("Try to detect Calico") res = self.custom_obj.get_cluster_custom_object( group="crd.projectcalico.org", version="v1", @@ -263,7 +263,7 @@ def get_cluster_as(self): pass # Try to get Cluster AS from kube-rotuer Config try: - logger.debug("Try to detect Kube-Router") + logger.info("Try to detect Kube-Router") pods = self.get_pods(ns='kube-system') kr_pod = False for pod in pods: @@ -283,7 +283,7 @@ def get_cluster_as(self): try: #We support only a singe AS per Cluster I use a set to ensure that asn_set = set() - logger.debug("Try to detect Cilium") + logger.info("Try to detect Cilium") #Get all the CiliumBGPPeeringPolicies traverse them and the virtualRouters and add all the found ASN in the set CiliumBGPPeeringPolicies = self.custom_obj.list_cluster_custom_object(group="cilium.io",version="v2alpha1",plural="ciliumbgppeeringpolicies") for policy in CiliumBGPPeeringPolicies['items']: diff --git a/unit_test.py b/unit_test.py index dcec638..825837d 100644 --- a/unit_test.py +++ b/unit_test.py @@ -3,7 +3,7 @@ from unittest.mock import patch, MagicMock from kubernetes import client from app.graph import ApicMethodsResolve, VkaciBuilTopology, VkaciEnvVariables, VkaciTable - +import json core.aciClassMetas = {"topRoot": { "properties": {}, "rnFormat": "something"}} @@ -19,8 +19,35 @@ spec=client.V1PodSpec( node_name="1234abc", containers=[] ) + ), + client.V1Pod( + status=client.V1PodStatus( + host_ip="192.168.1.2", pod_ip="192.168.1.2" + ), + metadata=client.V1ObjectMeta( + name="kube-router-xfgr", namespace="kube-system" + ), + spec=client.V1PodSpec( + node_name="1234abc", containers=[client.V1Container(name="kube-router", + args=[ + "--run-router=true", + "--run-firewall=true", + "--run-service-proxy=true", + "--bgp-graceful-restart=true", + "--bgp-holdtime=3s", + "--kubeconfig=/var/lib/kube-router/kubeconfig", + "--cluster-asn=56001", + "--advertise-external-ip", + "--advertise-loadbalancer-ip", + "--advertise-pod-cidr=true", + "--enable-ibgp=false", + "--enable-overlay=false", + "--enable-pod-egress=false", + "--override-nexthop=true" + ])] + ) ) -] + ] # Fake k8s cluster data for nodes nodes = [ @@ -155,6 +182,7 @@ def get_overlay_ip_to_switch_map(self, apic:Node): @patch('kubernetes.config.load_incluster_config', MagicMock(return_value=None)) @patch('pyaci.Node.useX509CertAuth', MagicMock(return_value=None)) @patch('kubernetes.client.CoreV1Api.list_pod_for_all_namespaces', MagicMock(return_value=client.V1PodList(api_version="1", items=pods))) +@patch('kubernetes.client.CoreV1Api.read_namespaced_pod', MagicMock(return_value=pods[1])) @patch('kubernetes.client.CoreV1Api.list_service_for_all_namespaces', MagicMock(return_value=client.V1ServiceList(api_version="1", items=services))) @patch('kubernetes.client.CoreV1Api.list_node', MagicMock(return_value=client.V1NodeList(api_version="1", items=nodes))) @patch('kubernetes.client.CustomObjectsApi.get_cluster_custom_object', MagicMock(return_value={'spec': {'asNumber': 56001}})) @@ -189,7 +217,8 @@ def test_valid_topology(self): """Test that a valid topology is created""" # Arrange expected = {'nodes': {'1234abc': {'node_ip': '192.168.1.2', - 'pods': {'dateformat': {'ip': '192.158.1.3', 'ns': 'dockerimage', 'labels': {'guest': 'frontend'}}}, + 'pods': {'dateformat': {'ip': '192.158.1.3', 'ns': 'dockerimage', 'labels': {'guest': 'frontend'}}, + 'kube-router-xfgr': {'ip': '192.168.1.2', 'ns': 'kube-system', 'labels': {}}}, 'bgp_peers': {'leaf-204': {'prefix_count': 2}}, 'neighbours': {'esxi4.cam.ciscolabs.com': {'switches': {'leaf-204': {'vmxnic1-eth1/1'}}, 'Description': 'VMware version 123'}}, 'labels': {'app': 'redis'}, 'mac': 'MOCKMO1C'}}, @@ -219,7 +248,11 @@ def test_valid_topology_cdpn(self): 'node_ip': '192.168.1.2', 'pods': {'dateformat': {'ip': '192.158.1.3', 'labels': {'guest': 'frontend'}, - 'ns': 'dockerimage'}}}}, + 'ns': 'dockerimage'}, + 'kube-router-xfgr': {'ip': '192.168.1.2', + 'labels': {}, + 'ns': 'kube-system'} + }}}, 'services': {'appx': [{'cluster_ip': '192.168.25.5', 'external_i_ps': ['192.168.5.1'], 'labels': {'app': 'guestbook'}, @@ -248,7 +281,11 @@ def test_valid_topology_no_neighbours(self): 'node_ip': '192.168.1.2', 'pods': {'dateformat': {'ip': '192.158.1.3', 'labels': {'guest': 'frontend'}, - 'ns': 'dockerimage'}}}}, + 'ns': 'dockerimage'}, + 'kube-router-xfgr': {'ip': '192.168.1.2', + 'labels': {}, + 'ns': 'kube-system'} + }}}, 'services': {'appx': [{'cluster_ip': '192.168.25.5', 'external_i_ps': ['192.168.5.1'], 'labels': {'app': 'guestbook'}, @@ -278,7 +315,11 @@ def test_valid_topology_no_desc_neighbour(self): 'node_ip': '192.168.1.2', 'pods': {'dateformat': {'ip': '192.158.1.3', 'labels': {'guest': 'frontend'}, - 'ns': 'dockerimage'}}}}, + 'ns': 'dockerimage'}, + 'kube-router-xfgr': {'ip': '192.168.1.2', + 'labels': {}, + 'ns': 'kube-system'}, + }}}, 'services': {'appx': [{'cluster_ip': '192.168.25.5', 'external_i_ps': ['192.168.5.1'], 'labels': {'app': 'guestbook'}, @@ -305,7 +346,11 @@ def test_leaf_table(self): 'data': [{'data': [{'data': [{'data': [{'image': 'pod.svg', 'ip': '192.158.1.3', 'ns': 'dockerimage', - 'value': 'dateformat'}], + 'value': 'dateformat'}, + {'image': 'pod.svg', + 'ip': '192.168.1.2', + 'ns': 'kube-system', + 'value': 'kube-router-xfgr'}], 'image': 'node.svg', 'ip': '192.168.1.2', 'ns': '', @@ -381,7 +426,10 @@ def test_pod_table(self): # Arrange expected = {'parent': 0, 'data': [{'value': 'leaf-204', 'ip': '', 'image': 'switch.png', 'data': [{'value': 'dateformat', 'ip': '192.158.1.3','ns': 'dockerimage', 'image': 'pod.svg', - 'data': [{'value': 'guest', 'label_value': 'frontend', 'image': 'label.svg'}]}]}]} + 'data': [{'value': 'guest', 'label_value': 'frontend', 'image': 'label.svg'}]}, + {'value': 'kube-router-xfgr', 'ip': '192.168.1.2','ns': 'kube-system', 'image': 'pod.svg', + 'data': []} + ]}]} build = VkaciBuilTopology( VkaciEnvVariables(self.vars), ApicMethodsMock()) @@ -411,7 +459,121 @@ def test_services_table(self): # Assert self.assertDictEqual(result, expected) - - + def test_bgp_as_detection(self): + """Test that the correct bgp as is detected""" + """Calico is tested by default in all thre previous tests""" + """Raise a Calico Exception, that triggers testing for Kube-Router""" + with patch('kubernetes.client.CustomObjectsApi.get_cluster_custom_object', MagicMock(side_effect=Exception("Test"))): + build = VkaciBuilTopology( + VkaciEnvVariables(self.vars), ApicMethodsMock()) + build.update() + asn = build.get_cluster_as() + self.assertEqual(asn, '56001') + CiliumBGPPeeringPolicies = json.loads(""" + { + "apiVersion": "v1", + "items": [ + { + "apiVersion": "cilium.io/v2alpha1", + "kind": "CiliumBGPPeeringPolicy", + "metadata": { + "creationTimestamp": "2023-01-20T04:49:55Z", + "generation": 1, + "name": "rack-1", + "resourceVersion": "976", + "uid": "6f05b9f0-0dbd-4846-a4c9-abb7c4ac559b" + }, + "spec": { + "nodeSelector": { + "matchLabels": { + "rack_id": "1" + } + }, + "virtualRouters": [ + { + "exportPodCIDR": true, + "localASN": 56001, + "neighbors": [ + { + "peerASN": 65002, + "peerAddress": "192.168.11.101/32" + }, + { + "peerASN": 65002, + "peerAddress": "192.168.11.102/32" + } + ], + "serviceSelector": { + "matchExpressions": [ + { + "key": "CiliumAdvertiseAllServices", + "operator": "DoesNotExist" + } + ] + } + } + ] + } + }, + { + "apiVersion": "cilium.io/v2alpha1", + "kind": "CiliumBGPPeeringPolicy", + "metadata": { + "creationTimestamp": "2023-01-20T04:49:55Z", + "generation": 1, + "name": "rack-2", + "resourceVersion": "977", + "uid": "f264c06b-4e65-44d1-9050-e87c3e52a39f" + }, + "spec": { + "nodeSelector": { + "matchLabels": { + "rack_id": "2" + } + }, + "virtualRouters": [ + { + "exportPodCIDR": true, + "localASN": 56001, + "neighbors": [ + { + "peerASN": 65002, + "peerAddress": "192.168.11.103/32" + }, + { + "peerASN": 65002, + "peerAddress": "192.168.11.104/32" + } + ], + "serviceSelector": { + "matchExpressions": [ + { + "key": "CiliumAdvertiseAllServices", + "operator": "DoesNotExist" + } + ] + } + } + ] + } + } + ], + "kind": "List", + "metadata": { + "resourceVersion": "" + } + } + """) + """Raise a Calico Exception and hide the Kube-Router POD , that triggers testing for Cilium""" + with ( + patch('kubernetes.client.CustomObjectsApi.get_cluster_custom_object', MagicMock(side_effect=Exception("Test"))), + patch('kubernetes.client.CustomObjectsApi.list_cluster_custom_object', MagicMock(return_value=CiliumBGPPeeringPolicies)), + patch('kubernetes.client.CoreV1Api.list_pod_for_all_namespaces', MagicMock(return_value=client.V1PodList(api_version="1", items=pods[0:1]))), + ): + build = VkaciBuilTopology( + VkaciEnvVariables(self.vars), ApicMethodsMock()) + build.update() + asn = build.get_cluster_as() + self.assertEqual(asn, '56001') if __name__ == '__main__': unittest.main() From dd271fcfd653713759ebc196f1f75c9175eccfe2 Mon Sep 17 00:00:00 2001 From: samitab Date: Sat, 25 Feb 2023 11:40:10 +1000 Subject: [PATCH 6/7] Refactored code for cilium support and added unit tests --- app/graph.py | 51 ++++++++------ unit_test.py | 185 ++++++++++++++++++--------------------------------- 2 files changed, 97 insertions(+), 139 deletions(-) diff --git a/app/graph.py b/app/graph.py index b0cb867..1982ad6 100644 --- a/app/graph.py +++ b/app/graph.py @@ -167,6 +167,7 @@ def __init__(self, env:VkaciEnvVariables, apic_methods:ApicMethodsResolve) -> No self.bgp_info = {} self.env = env self.apic_methods = apic_methods + self.k8s_as = None if self.env.tenant is not None and self.env.vrf is not None: self.aci_vrf = 'uni/tn-' + self.env.tenant + '/ctx-' + self.env.vrf @@ -244,22 +245,22 @@ def add_neighbour(self, node, neighbour): logger.info("Added neighbour details %s to %s - %s", neighbour_adj_port + '-' + neighbour.id, neighbour_adj.sysName, switch) def get_cluster_as(self): - ''' Get the AS from K8s Configuration this assumes Calico is used''' - asn = 0 + '''Returns the previously detected AS number''' + return self.k8s_as + + def detect_cluster_as(self): + ''' Detect the AS from K8s Configuration''' + asn = None logger.debug("Detect Cluster AS") # Try to get Cluster AS from Calico Config try: logger.info("Try to detect Calico") - res = self.custom_obj.get_cluster_custom_object( - group="crd.projectcalico.org", - version="v1", - name="default", - plural="bgpconfigurations" - ) - logger.info('Calico BGP Config Detected!') + res = self.get_calico_custom_object() asn = str(res['spec']['asNumber']) + logger.info('Calico BGP Config Detected!') + return asn except Exception as e: - # The the CRD does not exists I get an 404 not found exeption + # If the CRD does not exists it returns a 404 not found exeption pass # Try to get Cluster AS from kube-rotuer Config try: @@ -268,43 +269,55 @@ def get_cluster_as(self): kr_pod = False for pod in pods: if "kube-router" in pod: - #I just need one so I break immediately + # Only check the asn on the first kube-router pod found kr_pod = self.v1.read_namespaced_pod(pod,'kube-system') break if kr_pod: for arg in kr_pod.spec.containers[0].args: if "--cluster-asn=" in arg: asn = arg[14:] - logger.info('Kube-Router Detected! Cluster AS=%s',asn) + logger.info('Kube-Router Detected! Cluster AS=%s',asn) + return asn except Exception as e: pass - # Try to get Cluster AS from Cilium Config try: - #We support only a singe AS per Cluster I use a set to ensure that + # VKACI only supports a single AS per Cluster. A set is used to ensure that asn_set = set() logger.info("Try to detect Cilium") - #Get all the CiliumBGPPeeringPolicies traverse them and the virtualRouters and add all the found ASN in the set - CiliumBGPPeeringPolicies = self.custom_obj.list_cluster_custom_object(group="cilium.io",version="v2alpha1",plural="ciliumbgppeeringpolicies") + # Get all the CiliumBGPPeeringPolicies traverse them and the virtualRouters and add all the found ASN in the set + CiliumBGPPeeringPolicies = self.list_cilium_custom_objects() for policy in CiliumBGPPeeringPolicies['items']: for virtualrotuer in policy['spec']['virtualRouters']: asn_set.add(str(virtualrotuer['localASN'])) if len(asn_set) == 1: asn = asn_set.pop() logger.info('Cilium Detected! Cluster AS=%s',asn) + return asn elif len(asn_set) > 1: logger.info('Cilium Detected! More than one AS is used, this is an unsupported configuration!') except Exception as e: pass - if asn == 0: + if asn is None: logger.error("Can't detect K8s Cluster AS, BGP topology will not work corectly") return asn + def get_calico_custom_object(self): + return self.custom_obj.get_cluster_custom_object( + group="crd.projectcalico.org", + version="v1", + name="default", + plural="bgpconfigurations" + ) + + def list_cilium_custom_objects(self): + return self.custom_obj.list_cluster_custom_object(group="cilium.io", version="v2alpha1", plural="ciliumbgppeeringpolicies") + def update_bgp_info(self, apic:Node): '''Get the BGP information''' # Get the K8s Cluster AS - k8s_as = self.get_cluster_as() + self.k8s_as = self.detect_cluster_as() overlay_ip_to_switch = self.apic_methods.get_overlay_ip_to_switch_map(apic) self.bgp_info = {} vrf = self.env.tenant + ":" + self.env.vrf @@ -323,7 +336,7 @@ def update_bgp_info(self, apic:Node): self.bgp_info[leaf][route] = {} self.bgp_info[leaf][route]['hosts'] = [] self.bgp_info[leaf][route]['k8s_route'] = True - if hop.tag == k8s_as: + if hop.tag == self.k8s_as: #self.bgp_info[leaf][route]['ip'].add(next_hop) host_name = "" image = "node.svg" diff --git a/unit_test.py b/unit_test.py index 825837d..3836a3e 100644 --- a/unit_test.py +++ b/unit_test.py @@ -3,7 +3,7 @@ from unittest.mock import patch, MagicMock from kubernetes import client from app.graph import ApicMethodsResolve, VkaciBuilTopology, VkaciEnvVariables, VkaciTable -import json + core.aciClassMetas = {"topRoot": { "properties": {}, "rnFormat": "something"}} @@ -36,7 +36,7 @@ "--bgp-graceful-restart=true", "--bgp-holdtime=3s", "--kubeconfig=/var/lib/kube-router/kubeconfig", - "--cluster-asn=56001", + "--cluster-asn=56002", "--advertise-external-ip", "--advertise-loadbalancer-ip", "--advertise-pod-cidr=true", @@ -47,7 +47,7 @@ ])] ) ) - ] +] # Fake k8s cluster data for nodes nodes = [ @@ -182,10 +182,9 @@ def get_overlay_ip_to_switch_map(self, apic:Node): @patch('kubernetes.config.load_incluster_config', MagicMock(return_value=None)) @patch('pyaci.Node.useX509CertAuth', MagicMock(return_value=None)) @patch('kubernetes.client.CoreV1Api.list_pod_for_all_namespaces', MagicMock(return_value=client.V1PodList(api_version="1", items=pods))) -@patch('kubernetes.client.CoreV1Api.read_namespaced_pod', MagicMock(return_value=pods[1])) @patch('kubernetes.client.CoreV1Api.list_service_for_all_namespaces', MagicMock(return_value=client.V1ServiceList(api_version="1", items=services))) @patch('kubernetes.client.CoreV1Api.list_node', MagicMock(return_value=client.V1NodeList(api_version="1", items=nodes))) -@patch('kubernetes.client.CustomObjectsApi.get_cluster_custom_object', MagicMock(return_value={'spec': {'asNumber': 56001}})) +@patch('app.graph.VkaciBuilTopology.get_calico_custom_object', MagicMock(return_value={'spec': {'asNumber': 56001}})) class TestVkaciGraph(unittest.TestCase): vars = {"APIC_IPS": "192.168.25.192,192.168.1.2", @@ -459,121 +458,67 @@ def test_services_table(self): # Assert self.assertDictEqual(result, expected) - def test_bgp_as_detection(self): - """Test that the correct bgp as is detected""" - """Calico is tested by default in all thre previous tests""" - """Raise a Calico Exception, that triggers testing for Kube-Router""" - with patch('kubernetes.client.CustomObjectsApi.get_cluster_custom_object', MagicMock(side_effect=Exception("Test"))): - build = VkaciBuilTopology( - VkaciEnvVariables(self.vars), ApicMethodsMock()) - build.update() - asn = build.get_cluster_as() - self.assertEqual(asn, '56001') - CiliumBGPPeeringPolicies = json.loads(""" - { - "apiVersion": "v1", + + def assert_cluster_as(self, expected): + build = VkaciBuilTopology( + VkaciEnvVariables(self.vars), ApicMethodsMock()) + build.update() + asn = build.get_cluster_as() + self.assertEqual(asn, expected) + + + def test_calico_bgp_as_detection(self): + """Test that the bgp AS is detected with calico""" + """This is the default used on other tests but better be explicit so no one thinks it hasn't been tested""" + self.assert_cluster_as('56001') + + + @patch('kubernetes.client.CoreV1Api.read_namespaced_pod', MagicMock(return_value=pods[1])) + def test_kube_router_bgp_as_detection(self): + """Test that the bgp AS is detected with kube-router""" + with patch('app.graph.VkaciBuilTopology.get_calico_custom_object', MagicMock(return_value={})): + self.assert_cluster_as('56002') + + + # AS numbers are intentionally repeated for testing. + cilium_policies = { "items": [ - { - "apiVersion": "cilium.io/v2alpha1", - "kind": "CiliumBGPPeeringPolicy", - "metadata": { - "creationTimestamp": "2023-01-20T04:49:55Z", - "generation": 1, - "name": "rack-1", - "resourceVersion": "976", - "uid": "6f05b9f0-0dbd-4846-a4c9-abb7c4ac559b" - }, - "spec": { - "nodeSelector": { - "matchLabels": { - "rack_id": "1" - } - }, - "virtualRouters": [ - { - "exportPodCIDR": true, - "localASN": 56001, - "neighbors": [ - { - "peerASN": 65002, - "peerAddress": "192.168.11.101/32" - }, - { - "peerASN": 65002, - "peerAddress": "192.168.11.102/32" - } - ], - "serviceSelector": { - "matchExpressions": [ - { - "key": "CiliumAdvertiseAllServices", - "operator": "DoesNotExist" - } - ] - } - } - ] - } - }, - { - "apiVersion": "cilium.io/v2alpha1", - "kind": "CiliumBGPPeeringPolicy", - "metadata": { - "creationTimestamp": "2023-01-20T04:49:55Z", - "generation": 1, - "name": "rack-2", - "resourceVersion": "977", - "uid": "f264c06b-4e65-44d1-9050-e87c3e52a39f" - }, - "spec": { - "nodeSelector": { - "matchLabels": { - "rack_id": "2" - } - }, - "virtualRouters": [ - { - "exportPodCIDR": true, - "localASN": 56001, - "neighbors": [ - { - "peerASN": 65002, - "peerAddress": "192.168.11.103/32" - }, - { - "peerASN": 65002, - "peerAddress": "192.168.11.104/32" - } - ], - "serviceSelector": { - "matchExpressions": [ - { - "key": "CiliumAdvertiseAllServices", - "operator": "DoesNotExist" - } - ] - } - } - ] - } - } - ], - "kind": "List", - "metadata": { - "resourceVersion": "" - } - } - """) - """Raise a Calico Exception and hide the Kube-Router POD , that triggers testing for Cilium""" - with ( - patch('kubernetes.client.CustomObjectsApi.get_cluster_custom_object', MagicMock(side_effect=Exception("Test"))), - patch('kubernetes.client.CustomObjectsApi.list_cluster_custom_object', MagicMock(return_value=CiliumBGPPeeringPolicies)), - patch('kubernetes.client.CoreV1Api.list_pod_for_all_namespaces', MagicMock(return_value=client.V1PodList(api_version="1", items=pods[0:1]))), - ): - build = VkaciBuilTopology( - VkaciEnvVariables(self.vars), ApicMethodsMock()) - build.update() - asn = build.get_cluster_as() - self.assertEqual(asn, '56001') + {"spec": {"virtualRouters": [ + {'localASN': 56003}, {'localASN': 56003}]}}, + {"spec": {"virtualRouters": [{'localASN': 56003}]}} + ] + } + @patch('kubernetes.client.CoreV1Api.read_namespaced_pod', MagicMock(return_value=None)) + @patch('app.graph.VkaciBuilTopology.list_cilium_custom_objects', MagicMock(return_value=cilium_policies)) + def test_cilium_bgp_as_detection(self): + """Test that the bgp AS is detected with cilium""" + with patch('app.graph.VkaciBuilTopology.get_calico_custom_object', MagicMock(return_value={})): + self.assert_cluster_as('56003') + + + # Different AS numbers in Cilium is not supported. + invalid_cilium_policies = { + "items": [ + {"spec": {"virtualRouters": [ + {'localASN': 56003}, {'localASN': 56004}]}}, + {"spec": {"virtualRouters": [{'localASN': 56005}]}} + ] + } + @patch('kubernetes.client.CoreV1Api.read_namespaced_pod', MagicMock(return_value=None)) + @patch('app.graph.VkaciBuilTopology.list_cilium_custom_objects', MagicMock(return_value=invalid_cilium_policies)) + def test_invalid_cilium_bgp_as_detection(self): + """Test that the bgp AS is not detected with invalid cilium config""" + with patch('app.graph.VkaciBuilTopology.get_calico_custom_object', MagicMock(return_value={})): + self.assert_cluster_as(None) + + + @patch('kubernetes.client.CoreV1Api.read_namespaced_pod', MagicMock(return_value=None)) + @patch('app.graph.VkaciBuilTopology.list_cilium_custom_objects', MagicMock(return_value=[])) + def test_invalid_as_detection(self): + """Test that the bgp AS is not detected with no valid config""" + with patch('app.graph.VkaciBuilTopology.get_calico_custom_object', MagicMock(return_value={})): + self.assert_cluster_as(None) + + if __name__ == '__main__': unittest.main() From f52d0e1ba4e95baaea45d9ce9c900188ac6ec250 Mon Sep 17 00:00:00 2001 From: samitab Date: Sat, 25 Feb 2023 11:47:42 +1000 Subject: [PATCH 7/7] Version update to v1.1.0 --- app/buildah_build.sh | 2 +- app/version.txt | 2 +- helm/Chart.yaml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/buildah_build.sh b/app/buildah_build.sh index 8498ab0..9e5d3f2 100755 --- a/app/buildah_build.sh +++ b/app/buildah_build.sh @@ -9,7 +9,7 @@ export IMAGE_NAME="vkaci" export IMAGE_NAME_INIT="vkaci-init" if [ -z "$1" ] then - export IMAGE_TAG="v1.0.2" + export IMAGE_TAG="v1.1.0" else export IMAGE_TAG="$1" fi diff --git a/app/version.txt b/app/version.txt index 56e6375..f130d29 100644 --- a/app/version.txt +++ b/app/version.txt @@ -1 +1 @@ -Build: v1.0.2 +Build: v1.1.0 diff --git a/helm/Chart.yaml b/helm/Chart.yaml index d113b9e..09c4452 100644 --- a/helm/Chart.yaml +++ b/helm/Chart.yaml @@ -16,14 +16,14 @@ type: application # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.0.2 +version: 1.1.0 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v1.0.2" +appVersion: "v1.1.0" maintainers: - email: vkaci-tool@cisco.com