diff --git a/kuryr_kubernetes/constants.py b/kuryr_kubernetes/constants.py index 9d4e98de2..b579033ae 100644 --- a/kuryr_kubernetes/constants.py +++ b/kuryr_kubernetes/constants.py @@ -72,3 +72,8 @@ DEFAULT_IFNAME = 'eth0' ADDITIONAL_IFNAME_PREFIX = 'eth' K8S_NPWG_SRIOV_PREFIX = "intel.com/sriov" + +K8S_OPERATOR_IN = 'in' +K8S_OPERATOR_NOT_IN = 'notin' +K8S_OPERATOR_DOES_NOT_EXIST = 'doesnotexist' +K8S_OPERATOR_EXISTS = 'exists' diff --git a/kuryr_kubernetes/controller/drivers/network_policy.py b/kuryr_kubernetes/controller/drivers/network_policy.py index 615f79b03..9912eefab 100644 --- a/kuryr_kubernetes/controller/drivers/network_policy.py +++ b/kuryr_kubernetes/controller/drivers/network_policy.py @@ -22,6 +22,7 @@ from neutronclient.common import exceptions as n_exc from kuryr_kubernetes import clients from kuryr_kubernetes import constants from kuryr_kubernetes.controller.drivers import base +from kuryr_kubernetes.controller.drivers import utils from kuryr_kubernetes import exceptions LOG = logging.getLogger(__name__) @@ -120,10 +121,7 @@ class NetworkPolicyDriver(base.NetworkPolicyDriver): 'egressSgRules': e_rules, 'podSelector': pod_selector, 'networkpolicy_spec': policy['spec']}) - # TODO(ltomasbo): allow patching both spec and metadata in the - # same call - self.kubernetes.patch('metadata', crd['metadata']['selfLink'], - {'labels': pod_selector.get('matchLabels')}) + except exceptions.K8sClientException: LOG.exception('Error updating kuryrnetpolicy CRD %s', crd_name) raise @@ -443,13 +441,6 @@ class NetworkPolicyDriver(base.NetworkPolicyDriver): 'networkpolicy_spec': policy['spec'] }, } - if pod_selector: - try: - netpolicy_crd['metadata']['labels'] = pod_selector[ - 'matchLabels'] - except KeyError: - # NOTE(ltomasbo): Only supporting matchLabels for now - LOG.info("Pod Selector only allowed with matchLabels") try: LOG.debug("Creating KuryrNetPolicy CRD %s" % netpolicy_crd) @@ -482,18 +473,8 @@ class NetworkPolicyDriver(base.NetworkPolicyDriver): else: pod_selector = policy['spec'].get('podSelector') if pod_selector: - pod_label = pod_selector['matchLabels'] - pod_namespace = policy['metadata']['namespace'] - # Removing pod-template-hash as pods will not have it and - # otherwise there will be no match - pod_label.pop('pod-template-hash', None) - pod_label = urlencode(pod_label) - # NOTE(ltomasbo): K8s API does not accept &, so we need to AND - # the matchLabels with ',' or '%2C' instead - pod_label = pod_label.replace('&', ',') - pods = self.kubernetes.get( - '{}/namespaces/{}/pods?labelSelector={}'.format( - constants.K8S_API_BASE, pod_namespace, pod_label)) + policy_namespace = policy['metadata']['namespace'] + pods = utils.get_pods(pod_selector, policy_namespace) return pods.get('items') else: # NOTE(ltomasbo): It affects all the pods on the namespace diff --git a/kuryr_kubernetes/controller/drivers/network_policy_security_groups.py b/kuryr_kubernetes/controller/drivers/network_policy_security_groups.py index 4f7bae687..641af9369 100644 --- a/kuryr_kubernetes/controller/drivers/network_policy_security_groups.py +++ b/kuryr_kubernetes/controller/drivers/network_policy_security_groups.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -from six.moves.urllib.parse import urlencode from kuryr_kubernetes import clients from kuryr_kubernetes import config @@ -26,25 +25,19 @@ from oslo_log import log as logging LOG = logging.getLogger(__name__) -def _get_kuryrnetpolicy_crds(labels=None, namespace='default'): +OPERATORS_WITH_VALUES = [constants.K8S_OPERATOR_IN, + constants.K8S_OPERATOR_NOT_IN] + + +def _get_kuryrnetpolicy_crds(namespace='default'): kubernetes = clients.get_kubernetes_client() + try: - if labels: - LOG.debug("Using labels %s", labels) - labels.pop('pod-template-hash', None) - # removing pod-template-hash is necessary to fetch the proper list - labels = urlencode(labels) - # NOTE(maysams): K8s API does not accept &, so we need to replace - # it with ',' or '%2C' instead - labels = labels.replace('&', ',') - knp_path = '{}/{}/kuryrnetpolicies?labelSelector={}'.format( - constants.K8S_API_CRD_NAMESPACES, namespace, labels) - LOG.debug("K8s API Query %s", knp_path) - knps = kubernetes.get(knp_path) - LOG.debug("Return Kuryr Network Policies with label %s", knps) - else: - knps = kubernetes.get('{}/{}/kuryrnetpolicies'.format( - constants.K8S_API_CRD_NAMESPACES, namespace)) + knp_path = '{}/{}/kuryrnetpolicies'.format( + constants.K8S_API_CRD_NAMESPACES, namespace) + LOG.debug("K8s API Query %s", knp_path) + knps = kubernetes.get(knp_path) + LOG.debug("Return Kuryr Network Policies with label %s", knps) except exceptions.K8sResourceNotFound: LOG.exception("KuryrNetPolicy CRD not found") raise @@ -54,29 +47,76 @@ def _get_kuryrnetpolicy_crds(labels=None, namespace='default'): return knps +def _match_expressions(expressions, pod_labels): + for exp in expressions: + exp_op = exp['operator'].lower() + if pod_labels: + if exp_op in OPERATORS_WITH_VALUES: + exp_values = exp['values'] + pod_value = pod_labels.get(str(exp['key']), None) + if exp_op == constants.K8S_OPERATOR_IN: + if pod_value is None or pod_value not in exp_values: + return False + elif exp_op == constants.K8S_OPERATOR_NOT_IN: + if pod_value in exp_values: + return False + else: + if exp_op == constants.K8S_OPERATOR_EXISTS: + exists = pod_labels.get(str(exp['key']), None) + if exists is None: + return False + elif exp_op == constants.K8S_OPERATOR_DOES_NOT_EXIST: + exists = pod_labels.get(str(exp['key']), None) + if exists is not None: + return False + else: + if exp_op in (constants.K8S_OPERATOR_IN, + constants.K8S_OPERATOR_EXISTS): + return False + return True + + +def _match_labels(crd_labels, pod_labels): + for label_key, label_value in crd_labels.items(): + pod_value = pod_labels.get(label_key, None) + if not pod_value or label_value != pod_value: + return False + return True + + class NetworkPolicySecurityGroupsDriver(base.PodSecurityGroupsDriver): """Provides security groups for pods based on network policies""" def get_security_groups(self, pod, project_id): sg_list = [] - pod_namespace = pod['metadata']['namespace'] + pod_labels = pod['metadata'].get('labels') - LOG.debug("Using labels %s", pod_labels) + pod_namespace = pod['metadata']['namespace'] - if pod_labels: - knp_crds = _get_kuryrnetpolicy_crds(pod_labels, - namespace=pod_namespace) - for crd in knp_crds.get('items'): + knp_crds = _get_kuryrnetpolicy_crds(namespace=pod_namespace) + for crd in knp_crds.get('items'): + pod_selector = crd['spec'].get('podSelector') + if pod_selector: + crd_labels = pod_selector.get('matchLabels', None) + crd_expressions = pod_selector.get('matchExpressions', None) + + match_exp = match_lb = True + if crd_expressions: + match_exp = _match_expressions(crd_expressions, + pod_labels) + if crd_labels and pod_labels: + match_lb = _match_labels(crd_labels, pod_labels) + if match_exp and match_lb: + LOG.debug("Appending %s", + str(crd['spec']['securityGroupId'])) + sg_list.append(str(crd['spec']['securityGroupId'])) + else: LOG.debug("Appending %s", str(crd['spec']['securityGroupId'])) sg_list.append(str(crd['spec']['securityGroupId'])) - knp_namespace_crds = _get_kuryrnetpolicy_crds(namespace=pod_namespace) - for crd in knp_namespace_crds.get('items'): - if not crd['metadata'].get('labels'): - LOG.debug("Appending %s", str(crd['spec']['securityGroupId'])) - sg_list.append(str(crd['spec']['securityGroupId'])) - - if not knp_namespace_crds.get('items') and not sg_list: + # NOTE(maysams) Pods that are not selected by any Networkpolicy + # are fully accessible. Thus, the default security group is associated. + if not sg_list: sg_list = config.CONF.neutron_defaults.pod_security_groups if not sg_list: raise cfg.RequiredOptError('pod_security_groups', @@ -104,20 +144,30 @@ class NetworkPolicyServiceSecurityGroupsDriver( svc_labels = service['metadata'].get('labels') LOG.debug("Using labels %s", svc_labels) - if svc_labels: - knp_crds = _get_kuryrnetpolicy_crds(svc_labels, - namespace=svc_namespace) - for crd in knp_crds.get('items'): + knp_crds = _get_kuryrnetpolicy_crds(namespace=svc_namespace) + for crd in knp_crds.get('items'): + pod_selector = crd['spec'].get('podSelector') + if pod_selector: + crd_labels = pod_selector.get('matchLabels', None) + crd_expressions = pod_selector.get('matchExpressions', None) + + match_exp = match_lb = True + if crd_expressions: + match_exp = _match_expressions(crd_expressions, + svc_labels) + if crd_labels and svc_labels: + match_lb = _match_labels(crd_labels, svc_labels) + if match_exp and match_lb: + LOG.debug("Appending %s", + str(crd['spec']['securityGroupId'])) + sg_list.append(str(crd['spec']['securityGroupId'])) + else: LOG.debug("Appending %s", str(crd['spec']['securityGroupId'])) sg_list.append(str(crd['spec']['securityGroupId'])) - knp_namespace_crds = _get_kuryrnetpolicy_crds(namespace=svc_namespace) - for crd in knp_namespace_crds.get('items'): - if not crd['metadata'].get('labels'): - LOG.debug("Appending %s", str(crd['spec']['securityGroupId'])) - sg_list.append(str(crd['spec']['securityGroupId'])) - - if not knp_namespace_crds.get('items') and not sg_list: + # NOTE(maysams) Pods that are not selected by any Networkpolicy + # are fully accessible. Thus, the default security group is associated. + if not sg_list: sg_list = config.CONF.neutron_defaults.pod_security_groups if not sg_list: raise cfg.RequiredOptError('pod_security_groups', diff --git a/kuryr_kubernetes/controller/drivers/utils.py b/kuryr_kubernetes/controller/drivers/utils.py index 02ab934c1..63f1430d3 100644 --- a/kuryr_kubernetes/controller/drivers/utils.py +++ b/kuryr_kubernetes/controller/drivers/utils.py @@ -14,12 +14,17 @@ # under the License. from oslo_serialization import jsonutils +from six.moves.urllib import parse +from kuryr_kubernetes import clients from kuryr_kubernetes import constants from kuryr_kubernetes import exceptions as k_exc from kuryr_kubernetes import os_vif_util as ovu from kuryr_kubernetes import utils +OPERATORS_WITH_VALUES = [constants.K8S_OPERATOR_IN, + constants.K8S_OPERATOR_NOT_IN] + def get_network_id(subnets): ids = ovu.osvif_to_neutron_network_ids(subnets) @@ -58,3 +63,51 @@ def get_pod_state(pod): def is_host_network(pod): return pod['spec'].get('hostNetwork', False) + + +def get_pods(selector, namespace): + kubernetes = clients.get_kubernetes_client() + labels = selector.get('matchLabels', None) + if labels: + # Removing pod-template-hash as pods will not have it and + # otherwise there will be no match + labels.pop('pod-template-hash', None) + labels = replace_encoded_characters(labels) + + exps = selector.get('matchExpressions', None) + if exps: + exps = ', '.join(format_expression(exp) for exp in exps) + if labels: + expressions = parse.quote("," + exps) + labels += expressions + else: + labels = parse.quote(exps) + + pods = kubernetes.get( + '{}/namespaces/{}/pods?labelSelector={}'.format( + constants.K8S_API_BASE, namespace, labels)) + + return pods + + +def format_expression(expression): + key = expression['key'] + operator = expression['operator'].lower() + if operator in OPERATORS_WITH_VALUES: + values = expression['values'] + values = str(', '.join(values)) + values = "(%s)" % values + return "%s %s %s" % (key, operator, values) + else: + if operator == constants.K8S_OPERATOR_DOES_NOT_EXIST: + return "!%s" % key + else: + return key + + +def replace_encoded_characters(labels): + labels = parse.urlencode(labels) + # NOTE(ltomasbo): K8s API does not accept &, so we need to AND + # the matchLabels with ',' or '%2C' instead + labels = labels.replace('&', ',') + return labels diff --git a/kuryr_kubernetes/controller/handlers/policy.py b/kuryr_kubernetes/controller/handlers/policy.py index 288450a3f..f92eb2b38 100644 --- a/kuryr_kubernetes/controller/handlers/policy.py +++ b/kuryr_kubernetes/controller/handlers/policy.py @@ -61,12 +61,6 @@ class NetworkPolicyHandler(k8s_base.ResourceEventHandler): project_id = self._drv_project.get_project(policy) pods_to_update = [] - knps_on_namespace = self._drv_policy.knps_on_namespace( - policy['metadata']['namespace']) - if not knps_on_namespace: - namespace_pods = self._drv_policy.namespaced_pods(policy) - pods_to_update.extend(namespace_pods) - modified_pods = self._drv_policy.ensure_network_policy(policy, project_id) if modified_pods: @@ -89,18 +83,15 @@ class NetworkPolicyHandler(k8s_base.ResourceEventHandler): pod_sgs = self._drv_pod_sg.get_security_groups(pod, project_id) if crd_sg in pod_sgs: pod_sgs.remove(crd_sg) + if not pod_sgs: + pod_sgs = oslo_cfg.CONF.neutron_defaults.pod_security_groups + if not pod_sgs: + raise oslo_cfg.RequiredOptError('pod_security_groups', + oslo_cfg.OptGroup( + 'neutron_defaults')) self._drv_vif_pool.update_vif_sgs(pod, pod_sgs) self._drv_policy.release_network_policy(netpolicy_crd) - # re-apply original security groups for the namespace - knps_on_namespace = self._drv_policy.knps_on_namespace( - policy['metadata']['namespace']) - if not knps_on_namespace: - namespace_pods = self._drv_policy.namespaced_pods(policy) - for pod in namespace_pods: - pod_sgs = self._drv_pod_sg.get_security_groups(pod, - project_id) - self._drv_vif_pool.update_vif_sgs(pod, pod_sgs) @MEMOIZE def is_ready(self, quota): diff --git a/kuryr_kubernetes/tests/unit/controller/drivers/test_network_policy_security_groups.py b/kuryr_kubernetes/tests/unit/controller/drivers/test_network_policy_security_groups.py index dcc5aff03..7d2c6fefb 100644 --- a/kuryr_kubernetes/tests/unit/controller/drivers/test_network_policy_security_groups.py +++ b/kuryr_kubernetes/tests/unit/controller/drivers/test_network_policy_security_groups.py @@ -25,9 +25,9 @@ class TestNetworkPolicySecurityGroupsDriver(test_base.TestCase): def setUp(self): super(TestNetworkPolicySecurityGroupsDriver, self).setUp() - self._labels = mock.sentinel.labels self._project_id = mock.sentinel.project_id self._sg_id = mock.sentinel.sg_id + self._sg_id2 = mock.sentinel._sg_id2 self._namespace = 'default' self._crd = { 'metadata': {'name': mock.sentinel.name, @@ -55,9 +55,38 @@ class TestNetworkPolicySecurityGroupsDriver(test_base.TestCase): 'security_group_id': self._sg_id, 'id': mock.sentinel.id }}], + 'podSelector': { + 'matchExpressions': [ + { + 'key': 'environment', + 'operator': 'In', + 'values': [ + 'production']}], + 'matchLabels': { + 'run': 'demo' + }}, 'securityGroupId': self._sg_id, 'securityGroupName': mock.sentinel.sg_name}} + self._crd2 = { + 'metadata': {'name': mock.sentinel.name3, + 'selfLink': mock.sentinel.selfLink}, + 'spec': { + 'ingressSgRules': [ + {'security_group_rule': + {'description': 'Kuryr-Kubernetes NetPolicy SG rule', + 'direction': 'ingress', + 'ethertype': 'IPv4', + 'port_range_max': 8080, + 'port_range_min': 8080, + 'protocol': 'tcp', + 'security_group_id': self._sg_id2, + 'id': mock.sentinel.id + }}], + 'podSelector': {}, + 'securityGroupId': self._sg_id2, + 'securityGroupName': mock.sentinel.sg_name}} + self._crds = { "apiVersion": "v1", "items": [self._crd], @@ -66,6 +95,14 @@ class TestNetworkPolicySecurityGroupsDriver(test_base.TestCase): "resourceVersion": "", "selfLink": mock.sentinel.selfLink}} + self._multiple_crds = { + "apiVersion": "v1", + "items": [self._crd, self._crd2], + "kind": "List", + "metadata": { + "resourceVersion": "", + "selfLink": mock.sentinel.selfLink}} + self._empty_crds = { "apiVersion": "v1", "items": [], @@ -81,7 +118,39 @@ class TestNetworkPolicySecurityGroupsDriver(test_base.TestCase): 'name': mock.sentinel.pod_name, 'namespace': self._namespace, 'labels': { - 'run': 'demo'}}, + 'run': 'demo', + 'environment': 'production'}}, + 'spec': { + 'containers': [{ + 'image': 'kuryr/demo', + 'imagePullPolicy': 'Always', + 'name': mock.sentinel.pod_name + }] + }} + + self._pod2 = { + 'apiVersion': 'v1', + 'kind': 'Pod', + 'metadata': { + 'name': mock.sentinel.pod_name, + 'namespace': self._namespace, + 'labels': { + 'run': 'demo', + 'environment': 'development'}}, + 'spec': { + 'containers': [{ + 'image': 'kuryr/demo', + 'imagePullPolicy': 'Always', + 'name': mock.sentinel.pod_name + }] + }} + + self._pod_without_label = { + 'apiVersion': 'v1', + 'kind': 'Pod', + 'metadata': { + 'name': mock.sentinel.pod_name, + 'namespace': self._namespace}, 'spec': { 'containers': [{ 'image': 'kuryr/demo', @@ -94,35 +163,92 @@ class TestNetworkPolicySecurityGroupsDriver(test_base.TestCase): self._driver = ( network_policy_security_groups.NetworkPolicySecurityGroupsDriver()) + @mock.patch('kuryr_kubernetes.config.CONF') @mock.patch.object(network_policy_security_groups, '_get_kuryrnetpolicy_crds') - def test_get_security_groups(self, m_get_crds): + def test_get_sgs_for_pod_without_label(self, m_get_crds, m_cfg): m_get_crds.return_value = self._crds - self._driver.get_security_groups(self._pod, self._project_id) - calls = [mock.call(self._pod['metadata']['labels'], - namespace=self._namespace), - mock.call(namespace=self._namespace)] - m_get_crds.assert_has_calls(calls) + sg_list = [str(mock.sentinel.sg_id)] + m_cfg.neutron_defaults.pod_security_groups = sg_list + + sgs = self._driver.get_security_groups(self._pod_without_label, + self._project_id) - @mock.patch.object(network_policy_security_groups, - '_get_kuryrnetpolicy_crds') - def test_get_security_groups_without_label(self, m_get_crds): - pod = self._pod.copy() - del pod['metadata']['labels'] - labels = {'run': 'demo'} - self._crds['items'][0]['metadata']['labels'] = labels - m_get_crds.return_value = self._crds - self._driver.get_security_groups(pod, self._project_id) m_get_crds.assert_called_once_with(namespace=self._namespace) + self.assertEqual(sg_list, sgs) + + @mock.patch.object(network_policy_security_groups, + '_match_expressions') + @mock.patch.object(network_policy_security_groups, + '_match_labels') + @mock.patch.object(network_policy_security_groups, + '_get_kuryrnetpolicy_crds') + def test_get_sgs_for_pod_with_label(self, m_get_crds, m_match_labels, + m_match_expressions): + m_get_crds.return_value = self._crds + m_match_expressions.return_value = True + m_match_labels.return_value = True + pod_labels = self._pod['metadata']['labels'] + resp = self._driver.get_security_groups(self._pod, self._project_id) + + m_get_crds.assert_called_once_with(namespace=self._namespace) + m_match_expressions.assert_called_once_with( + self._crd['spec']['podSelector']['matchExpressions'], pod_labels) + m_match_labels.assert_called_once_with( + self._crd['spec']['podSelector']['matchLabels'], pod_labels) + self.assertEqual(resp, [str(self._sg_id)]) + + @mock.patch('kuryr_kubernetes.config.CONF') + @mock.patch.object(network_policy_security_groups, + '_match_expressions') + @mock.patch.object(network_policy_security_groups, + '_match_labels') + @mock.patch.object(network_policy_security_groups, + '_get_kuryrnetpolicy_crds') + def test_get_sgs_for_pod_with_label_no_match(self, m_get_crds, + m_match_labels, + m_match_expressions, m_cfg): + m_get_crds.return_value = self._crds + m_match_expressions.return_value = False + m_match_labels.return_value = True + sg_list = [mock.sentinel.sg_id] + m_cfg.neutron_defaults.pod_security_groups = sg_list + pod_labels = self._pod2['metadata']['labels'] + + sgs = self._driver.get_security_groups(self._pod2, self._project_id) + + m_get_crds.assert_called_once_with(namespace=self._namespace) + m_match_expressions.assert_called_once_with( + self._crd['spec']['podSelector']['matchExpressions'], pod_labels) + m_match_labels.assert_called_once_with( + self._crd['spec']['podSelector']['matchLabels'], pod_labels) + self.assertEqual(sg_list, sgs) @mock.patch.object(network_policy_security_groups, '_get_kuryrnetpolicy_crds') - def test_get_security_groups_no_crds(self, m_get_crds): + def test_get_sgs_no_crds(self, m_get_crds): m_get_crds.return_value = self._empty_crds + cfg.CONF.set_override('pod_security_groups', [], + group='neutron_defaults') + self.assertRaises(cfg.RequiredOptError, self._driver.get_security_groups, self._pod, self._project_id) - calls = [mock.call(self._pod['metadata']['labels'], - namespace=self._namespace), - mock.call(namespace=self._namespace)] - m_get_crds.assert_has_calls(calls) + m_get_crds.assert_called_with(namespace=self._namespace) + + @mock.patch.object(network_policy_security_groups, + '_match_expressions') + @mock.patch.object(network_policy_security_groups, + '_match_labels') + @mock.patch.object(network_policy_security_groups, + '_get_kuryrnetpolicy_crds') + def test_get_sgs_multiple_crds(self, m_get_crds, m_match_labels, + m_match_expressions): + m_match_expressions.return_value = True + m_match_labels.return_value = True + m_get_crds.return_value = self._multiple_crds + + resp = self._driver.get_security_groups(self._pod, self._project_id) + + m_get_crds.assert_called_once_with(namespace=self._namespace) + self.assertEqual([str(self._sg_id), str(self._sg_id2)], resp) diff --git a/kuryr_kubernetes/tests/unit/controller/handlers/test_policy.py b/kuryr_kubernetes/tests/unit/controller/handlers/test_policy.py index b88791b7d..d9afbcae1 100644 --- a/kuryr_kubernetes/tests/unit/controller/handlers/test_policy.py +++ b/kuryr_kubernetes/tests/unit/controller/handlers/test_policy.py @@ -139,34 +139,25 @@ class TestPolicyHandler(test_base.TestCase): def test_on_present_without_knps_on_namespace(self): modified_pod = mock.sentinel.modified_pod match_pod = mock.sentinel.match_pod - namespace_pod = mock.sentinel.namespace_pod - knp_on_ns = self._handler._drv_policy.knps_on_namespace - knp_on_ns.return_value = False - namespaced_pods = self._handler._drv_policy.namespaced_pods - namespaced_pods.return_value = [namespace_pod] ensure_nw_policy = self._handler._drv_policy.ensure_network_policy ensure_nw_policy.return_value = [modified_pod] affected_pods = self._handler._drv_policy.affected_pods affected_pods.return_value = [match_pod] - sg1 = [mock.sentinel.sg1] sg2 = [mock.sentinel.sg2] sg3 = [mock.sentinel.sg3] - self._get_security_groups.side_effect = [sg1, sg2, sg3] + self._get_security_groups.side_effect = [sg2, sg3] policy.NetworkPolicyHandler.on_present(self._handler, self._policy) - namespaced_pods.assert_called_once_with(self._policy) ensure_nw_policy.assert_called_once_with(self._policy, self._project_id) affected_pods.assert_called_once_with(self._policy) - calls = [mock.call(namespace_pod, self._project_id), - mock.call(modified_pod, self._project_id), + calls = [mock.call(modified_pod, self._project_id), mock.call(match_pod, self._project_id)] self._get_security_groups.assert_has_calls(calls) - calls = [mock.call(namespace_pod, sg1), - mock.call(modified_pod, sg2), + calls = [mock.call(modified_pod, sg2), mock.call(match_pod, sg3)] self._update_vif_sgs.assert_has_calls(calls) @@ -189,8 +180,6 @@ class TestPolicyHandler(test_base.TestCase): policy.NetworkPolicyHandler.on_deleted(self._handler, self._policy) release_nw_policy.assert_called_once_with(knp_obj) - calls = [mock.call(match_pod, self._project_id), - mock.call(namespace_pod, self._project_id)] - self._get_security_groups.assert_has_calls(calls) - calls = [mock.call(match_pod, sg1), mock.call(namespace_pod, sg2)] - self._update_vif_sgs.assert_has_calls(calls) + self._get_security_groups.assert_called_once_with(match_pod, + self._project_id) + self._update_vif_sgs.assert_called_once_with(match_pod, sg1)