Update CRD when NP has namespaceSelectors

When a namespace is created, deleted or updated and
its labels matches the namespaceSelector of a NP,
the CRD and the respective sg must be updated.

Partially Implements: blueprint k8s-network-policies

Change-Id: I515de28647f5f06248555733c27dd4f5a56149ec
This commit is contained in:
Maysa Macedo 2018-12-30 22:10:51 +00:00
parent 7480cc36f8
commit 9deb322962
11 changed files with 643 additions and 167 deletions

View File

@ -37,6 +37,7 @@ K8S_POD_STATUS_PENDING = 'Pending'
K8S_ANNOTATION_PREFIX = 'openstack.org/kuryr'
K8S_ANNOTATION_VIF = K8S_ANNOTATION_PREFIX + '-vif'
K8S_ANNOTATION_LABEL = K8S_ANNOTATION_PREFIX + '-pod-label'
K8S_ANNOTATION_NAMESPACE_LABEL = K8S_ANNOTATION_PREFIX + '-namespace-label'
K8S_ANNOTATION_LBAAS_SPEC = K8S_ANNOTATION_PREFIX + '-lbaas-spec'
K8S_ANNOTATION_LBAAS_STATE = K8S_ANNOTATION_PREFIX + '-lbaas-state'
K8S_ANNOTATION_NET_CRD = K8S_ANNOTATION_PREFIX + '-net-crd'

View File

@ -268,6 +268,27 @@ class PodSecurityGroupsDriver(DriverBase):
"""
raise NotImplementedError()
def delete_namespace_sg_rules(self, namespace):
"""Delete security group rule associated to a namespace.
:param namespace: dict containing K8S Namespace object
"""
raise NotImplementedError()
def create_namespace_sg_rules(self, namespace):
"""Create security group rule associated to a namespace.
:param namespace: dict containing K8S Namespace object
"""
raise NotImplementedError()
def update_namespace_sg_rules(self, namespace):
"""Update security group rule associated to a namespace.
:param namespace: dict containing K8S Namespace object
"""
raise NotImplementedError()
@six.add_metaclass(abc.ABCMeta)
class ServiceSecurityGroupsDriver(DriverBase):

View File

@ -59,6 +59,18 @@ class DefaultPodSecurityGroupsDriver(base.PodSecurityGroupsDriver):
LOG.debug("Security group driver does not update SG rules for "
"the pods.")
def delete_namespace_sg_rules(self, namespace):
LOG.debug("Security group driver does not delete SG rules for "
"namespace.")
def create_namespace_sg_rules(self, namespace):
LOG.debug("Security group driver does not create SG rules for "
"namespace.")
def update_namespace_sg_rules(self, namespace):
LOG.debug("Security group driver does not update SG rules for "
"namespace.")
class DefaultServiceSecurityGroupsDriver(base.ServiceSecurityGroupsDriver):
"""Provides security groups for Service based on a configuration option."""

View File

@ -21,6 +21,7 @@ from kuryr_kubernetes import clients
from kuryr_kubernetes import config
from kuryr_kubernetes import constants
from kuryr_kubernetes.controller.drivers import base
from kuryr_kubernetes.controller.drivers import utils
from kuryr_kubernetes import exceptions
from neutronclient.common import exceptions as n_exc
@ -67,6 +68,73 @@ def _get_net_crd(namespace):
return net_crd
def _create_sg_rule(sg_id, direction, cidr, port=None, namespace=None):
if port:
sg_rule = utils.create_security_group_rule_body(
sg_id, direction, port.get('port'),
protocol=port.get('protocol'), cidr=cidr, namespace=namespace)
else:
sg_rule = utils.create_security_group_rule_body(
sg_id, direction, port_range_min=1,
port_range_max=65535, cidr=cidr, namespace=namespace)
sgr_id = utils.create_security_group_rule(sg_rule)
sg_rule['security_group_rule']['id'] = sgr_id
return sg_rule
def _parse_rules(direction, crd, namespace):
policy = crd['spec']['networkpolicy_spec']
sg_id = crd['spec']['securityGroupId']
ns_labels = namespace['metadata'].get('labels')
ns_name = namespace['metadata'].get('name')
ns_cidr = utils.get_namespace_subnet_cidr(namespace)
rule_direction = 'from'
crd_rules = crd['spec'].get('ingressSgRules')
if direction == 'egress':
rule_direction = 'to'
crd_rules = crd['spec'].get('egressSgRules')
matched = False
rule_list = policy.get(direction, None)
for rule_block in rule_list:
for rule in rule_block.get(rule_direction, []):
pod_selector = rule.get('podSelector')
ns_selector = rule.get('namespaceSelector')
if (ns_selector and ns_labels and
utils.match_selector(ns_selector, ns_labels)):
if pod_selector:
pods = utils.get_pods(pod_selector, ns_name)
for pod in pods.get('items'):
pod_ip = utils.get_pod_ip(pod)
if 'ports' in rule_block:
for port in rule_block['ports']:
matched = True
crd_rules.append(_create_sg_rule(
sg_id, direction, pod_ip, port=port,
namespace=ns_name))
else:
matched = True
crd_rules.append(_create_sg_rule(
sg_id, direction, pod_ip,
namespace=ns_name))
else:
if 'ports' in rule_block:
for port in rule_block['ports']:
matched = True
crd_rules.append(_create_sg_rule(
sg_id, direction, ns_cidr,
port=port, namespace=ns_name))
else:
matched = True
crd_rules.append(_create_sg_rule(
sg_id, direction, ns_cidr, namespace=ns_name))
return matched, crd_rules
class NamespacePodSecurityGroupsDriver(base.PodSecurityGroupsDriver):
"""Provides security groups for Pod based on a configuration option."""
@ -131,6 +199,68 @@ class NamespacePodSecurityGroupsDriver(base.PodSecurityGroupsDriver):
LOG.exception("Error deleting security group %s.", sg_id)
raise
def delete_namespace_sg_rules(self, namespace):
ns_name = namespace['metadata']['name']
LOG.debug("Deleting sg rule for namespace: %s",
ns_name)
knp_crds = utils.get_kuryrnetpolicy_crds()
for crd in knp_crds.get('items'):
crd_selector = crd['spec'].get('podSelector')
ingress_rule_list = crd['spec'].get('ingressSgRules')
egress_rule_list = crd['spec'].get('egressSgRules')
i_rules = []
e_rules = []
matched = False
for i_rule in ingress_rule_list:
LOG.debug("Parsing ingress rule: %r", i_rule)
rule_namespace = i_rule.get('namespace', None)
if rule_namespace and rule_namespace == ns_name:
matched = True
utils.delete_security_group_rule(
i_rule['security_group_rule']['id'])
else:
i_rules.append(i_rule)
for e_rule in egress_rule_list:
LOG.debug("Parsing egress rule: %r", e_rule)
rule_namespace = e_rule.get('namespace', None)
if rule_namespace and rule_namespace == ns_name:
matched = True
utils.delete_security_group_rule(
e_rule['security_group_rule']['id'])
else:
e_rules.append(e_rule)
if matched:
utils.patch_kuryr_crd(crd, i_rules, e_rules, crd_selector)
def create_namespace_sg_rules(self, namespace):
kubernetes = clients.get_kubernetes_client()
ns_name = namespace['metadata']['name']
LOG.debug("Creating sg rule for namespace: %s", ns_name)
namespace = kubernetes.get(
'{}/namespaces/{}'.format(constants.K8S_API_BASE, ns_name))
knp_crds = utils.get_kuryrnetpolicy_crds()
for crd in knp_crds.get('items'):
crd_selector = crd['spec'].get('podSelector')
i_matched, i_rules = _parse_rules('ingress', crd, namespace)
e_matched, e_rules = _parse_rules('egress', crd, namespace)
if i_matched or e_matched:
utils.patch_kuryr_crd(crd, i_rules,
e_rules, crd_selector)
def update_namespace_sg_rules(self, namespace):
LOG.debug("Updating sg rule for namespace: %s",
namespace['metadata']['name'])
self.delete_namespace_sg_rules(namespace)
self.create_namespace_sg_rules(namespace)
def create_sg_rules(self, pod):
LOG.debug("Security group driver does not create SG rules for "
"the pods.")

View File

@ -203,40 +203,27 @@ class NetworkPolicyDriver(base.NetworkPolicyDriver):
matching_pods = driver_utils.get_pods(pod_selector, namespace)
for pod in matching_pods.get('items'):
if pod['status']['podIP']:
ips.append(pod['status']['podIP'])
pod_ip = pod['status']['podIP']
ns = pod['metadata']['namespace']
ips.append({'cidr': pod_ip, 'namespace': ns})
return ips
def _get_namespace_subnet_cidr(self, namespace):
try:
ns_annotations = namespace['metadata']['annotations']
ns_name = ns_annotations[constants.K8S_ANNOTATION_NET_CRD]
except KeyError:
LOG.exception('Namespace handler must be enabled to support '
'Network Policies with namespaceSelector')
raise exceptions.ResourceNotReady(namespace)
try:
net_crd = self.kubernetes.get('{}/kuryrnets/{}'.format(
constants.K8S_API_CRD, ns_name))
except exceptions.K8sClientException:
LOG.exception("Kubernetes Client Exception.")
raise
return net_crd['spec']['subnetCIDR']
def _get_namespaces_cidr(self, namespace_selector, namespace=None):
cidrs = []
if not namespace_selector and namespace:
ns = self.kubernetes.get(
'{}/namespaces/{}'.format(constants.K8S_API_BASE, namespace))
ns_cidr = self._get_namespace_subnet_cidr(ns)
cidrs.append(ns_cidr)
ns_cidr = driver_utils.get_namespace_subnet_cidr(ns)
cidrs.append({'cidr': ns_cidr, 'namespace': namespace})
else:
matching_namespaces = driver_utils.get_namespaces(
namespace_selector)
for ns in matching_namespaces.get('items'):
# NOTE(ltomasbo): This requires the namespace handler to be
# also enabled
ns_cidr = self._get_namespace_subnet_cidr(ns)
cidrs.append(ns_cidr)
ns_cidr = driver_utils.get_namespace_subnet_cidr(ns)
ns_name = ns['metadata']['name']
cidrs.append({'cidr': ns_cidr, 'namespace': ns_name})
return cidrs
def _parse_selectors(self, rule_block, rule_direction, policy_namespace):
@ -315,7 +302,8 @@ class NetworkPolicyDriver(base.NetworkPolicyDriver):
driver_utils.create_security_group_rule_body(
sg_id, direction, port.get('port'),
protocol=port.get('protocol'),
cidr=cidr))
cidr=cidr.get('cidr'),
namespace=cidr.get('namespace')))
sg_rule_body_list.append(rule)
if allow_all:
rule = (
@ -334,7 +322,8 @@ class NetworkPolicyDriver(base.NetworkPolicyDriver):
sg_id, direction,
port_range_min=1,
port_range_max=65535,
cidr=cidr)
cidr=cidr.get('cidr'),
namespace=cidr.get('namespace'))
sg_rule_body_list.append(rule)
if allow_all:
rule = driver_utils.create_security_group_rule_body(

View File

@ -26,81 +26,6 @@ from oslo_log import log as logging
LOG = logging.getLogger(__name__)
OPERATORS_WITH_VALUES = [constants.K8S_OPERATOR_IN,
constants.K8S_OPERATOR_NOT_IN]
def _get_kuryrnetpolicy_crds(namespace=None):
kubernetes = clients.get_kubernetes_client()
try:
if namespace:
knp_path = '{}/{}/kuryrnetpolicies'.format(
constants.K8S_API_CRD_NAMESPACES, namespace)
else:
knp_path = constants.K8S_API_CRD_KURYRNETPOLICIES
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
except exceptions.K8sClientException:
LOG.exception("Kubernetes Client Exception")
raise
return knps
def _match_expressions(expressions, labels):
for exp in expressions:
exp_op = exp['operator'].lower()
if labels:
if exp_op in OPERATORS_WITH_VALUES:
exp_values = exp['values']
label_value = labels.get(str(exp['key']), None)
if exp_op == constants.K8S_OPERATOR_IN:
if label_value is None or label_value not in exp_values:
return False
elif exp_op == constants.K8S_OPERATOR_NOT_IN:
if label_value in exp_values:
return False
else:
if exp_op == constants.K8S_OPERATOR_EXISTS:
exists = labels.get(str(exp['key']), None)
if exists is None:
return False
elif exp_op == constants.K8S_OPERATOR_DOES_NOT_EXIST:
exists = 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, labels):
for crd_key, crd_value in crd_labels.items():
label_value = labels.get(crd_key, None)
if not label_value or crd_value != label_value:
return False
return True
def _match_selector(selector, labels):
crd_labels = selector.get('matchLabels', None)
crd_expressions = selector.get('matchExpressions', None)
match_exp = match_lb = True
if crd_expressions:
match_exp = _match_expressions(crd_expressions,
labels)
if crd_labels and labels:
match_lb = _match_labels(crd_labels, labels)
return match_exp and match_lb
def _get_namespace_labels(namespace):
kubernetes = clients.get_kubernetes_client()
@ -119,15 +44,15 @@ def _get_namespace_labels(namespace):
return namespaces['metadata'].get('labels')
def _create_sg_rules(crd, pod, namespace_selector, pod_selector,
rule_block, crd_rules, direction,
matched):
def _create_sg_rules(crd, pod, pod_selector, rule_block, crd_rules,
direction, matched, namespace=None):
pod_labels = pod['metadata'].get('labels')
# NOTE (maysams) No need to differentiate between podSelector
# with empty value or with '{}', as they have same result in here.
if (pod_selector and
_match_selector(pod_selector, pod_labels)):
driver_utils.match_selector(pod_selector, pod_labels)):
matched = True
pod_ip = driver_utils.get_pod_ip(pod)
sg_id = crd['spec']['securityGroupId']
@ -135,7 +60,8 @@ def _create_sg_rules(crd, pod, namespace_selector, pod_selector,
for port in rule_block['ports']:
sg_rule = driver_utils.create_security_group_rule_body(
sg_id, direction, port.get('port'),
protocol=port.get('protocol'), cidr=pod_ip)
protocol=port.get('protocol'), cidr=pod_ip,
namespace=namespace)
sgr_id = driver_utils.create_security_group_rule(sg_rule)
sg_rule['security_group_rule']['id'] = sgr_id
crd_rules.append(sg_rule)
@ -144,7 +70,8 @@ def _create_sg_rules(crd, pod, namespace_selector, pod_selector,
sg_id, direction,
port_range_min=1,
port_range_max=65535,
cidr=pod_ip)
cidr=pod_ip,
namespace=namespace)
sgr_id = driver_utils.create_security_group_rule(sg_rule)
sg_rule['security_group_rule']['id'] = sgr_id
crd_rules.append(sg_rule)
@ -171,23 +98,22 @@ def _parse_rules(direction, crd, pod):
namespace_selector = rule.get('namespaceSelector')
pod_selector = rule.get('podSelector')
if namespace_selector == {}:
if _create_sg_rules(crd, pod, namespace_selector,
pod_selector, rule_block, crd_rules,
direction, matched):
if _create_sg_rules(crd, pod, pod_selector, rule_block,
crd_rules, direction, matched):
matched = True
elif namespace_selector:
if (pod_namespace_labels and
_match_selector(namespace_selector,
pod_namespace_labels)):
if _create_sg_rules(crd, pod, namespace_selector,
pod_selector, rule_block, crd_rules,
direction, matched):
driver_utils.match_selector(namespace_selector,
pod_namespace_labels)):
if _create_sg_rules(crd, pod, pod_selector, rule_block,
crd_rules, direction, matched,
pod_namespace):
matched = True
else:
if pod_namespace == policy_namespace:
if _create_sg_rules(crd, pod, namespace_selector,
pod_selector, rule_block, crd_rules,
direction, matched):
if _create_sg_rules(crd, pod, pod_selector, rule_block,
crd_rules, direction, matched,
pod_namespace):
matched = True
return matched, crd_rules
@ -198,11 +124,12 @@ def _get_pod_sgs(pod, project_id):
pod_labels = pod['metadata'].get('labels')
pod_namespace = pod['metadata']['namespace']
knp_crds = _get_kuryrnetpolicy_crds(namespace=pod_namespace)
knp_crds = driver_utils.get_kuryrnetpolicy_crds(
namespace=pod_namespace)
for crd in knp_crds.get('items'):
pod_selector = crd['spec'].get('podSelector')
if pod_selector:
if _match_selector(pod_selector, pod_labels):
if driver_utils.match_selector(pod_selector, pod_labels):
LOG.debug("Appending %s",
str(crd['spec']['securityGroupId']))
sg_list.append(str(crd['spec']['securityGroupId']))
@ -229,7 +156,7 @@ class NetworkPolicySecurityGroupsDriver(base.PodSecurityGroupsDriver):
def create_sg_rules(self, pod):
LOG.debug("Creating sg rule for pod: %s", pod['metadata']['name'])
knp_crds = _get_kuryrnetpolicy_crds()
knp_crds = driver_utils.get_kuryrnetpolicy_crds()
for crd in knp_crds.get('items'):
crd_selector = crd['spec'].get('podSelector')
@ -244,7 +171,7 @@ class NetworkPolicySecurityGroupsDriver(base.PodSecurityGroupsDriver):
LOG.debug("Deleting sg rule for pod: %s", pod['metadata']['name'])
pod_ip = driver_utils.get_pod_ip(pod)
knp_crds = _get_kuryrnetpolicy_crds()
knp_crds = driver_utils.get_kuryrnetpolicy_crds()
for crd in knp_crds.get('items'):
crd_selector = crd['spec'].get('podSelector')
ingress_rule_list = crd['spec'].get('ingressSgRules')
@ -293,6 +220,18 @@ class NetworkPolicySecurityGroupsDriver(base.PodSecurityGroupsDriver):
LOG.debug("Security group driver does not implement deleting "
"SGs.")
def delete_namespace_sg_rules(self, namespace):
LOG.debug("Security group driver does not delete SG rules for "
"namespace.")
def create_namespace_sg_rules(self, namespace):
LOG.debug("Security group driver does not create SG rules for "
"namespace.")
def update_namespace_sg_rules(self, namespace):
LOG.debug("Security group driver does not update SG rules for "
"namespace.")
class NetworkPolicyServiceSecurityGroupsDriver(
base.ServiceSecurityGroupsDriver):

View File

@ -238,7 +238,7 @@ def patch_kuryr_crd(crd, i_rules, e_rules, pod_selector, np_spec=None):
def create_security_group_rule_body(
security_group_id, direction, port_range_min,
port_range_max=None, protocol=None, ethertype='IPv4', cidr=None,
description="Kuryr-Kubernetes NetPolicy SG rule"):
description="Kuryr-Kubernetes NetPolicy SG rule", namespace=None):
if not port_range_min:
port_range_min = 1
port_range_max = 65535
@ -260,6 +260,8 @@ def create_security_group_rule_body(
if cidr:
security_group_rule_body[u'security_group_rule'][
u'remote_ip_prefix'] = cidr
if namespace:
security_group_rule_body['namespace'] = namespace
LOG.debug("Creating sg rule body %s", security_group_rule_body)
return security_group_rule_body
@ -280,11 +282,100 @@ def get_pod_ip(pod):
return first_subnet_ip
def get_pod_annotated_labels(pod):
def get_annotated_labels(resource, annotation_labels):
try:
annotations = pod['metadata']['annotations']
pod_labels_annotation = annotations[constants.K8S_ANNOTATION_LABEL]
annotations = resource['metadata']['annotations']
labels_annotation = annotations[annotation_labels]
except KeyError:
return None
pod_labels = jsonutils.loads(pod_labels_annotation)
return pod_labels
labels = jsonutils.loads(labels_annotation)
return labels
def get_kuryrnetpolicy_crds(namespace=None):
kubernetes = clients.get_kubernetes_client()
try:
if namespace:
knp_path = '{}/{}/kuryrnetpolicies'.format(
constants.K8S_API_CRD_NAMESPACES, namespace)
else:
knp_path = constants.K8S_API_CRD_KURYRNETPOLICIES
LOG.debug("K8s API Query %s", knp_path)
knps = kubernetes.get(knp_path)
LOG.debug("Return Kuryr Network Policies with label %s", knps)
except k_exc.K8sResourceNotFound:
LOG.exception("KuryrNetPolicy CRD not found")
raise
except k_exc.K8sClientException:
LOG.exception("Kubernetes Client Exception")
raise
return knps
def match_expressions(expressions, labels):
for exp in expressions:
exp_op = exp['operator'].lower()
if labels:
if exp_op in OPERATORS_WITH_VALUES:
exp_values = exp['values']
label_value = labels.get(str(exp['key']), None)
if exp_op == constants.K8S_OPERATOR_IN:
if label_value is None or label_value not in exp_values:
return False
elif exp_op == constants.K8S_OPERATOR_NOT_IN:
if label_value in exp_values:
return False
else:
if exp_op == constants.K8S_OPERATOR_EXISTS:
exists = labels.get(str(exp['key']), None)
if exists is None:
return False
elif exp_op == constants.K8S_OPERATOR_DOES_NOT_EXIST:
exists = 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, labels):
for crd_key, crd_value in crd_labels.items():
label_value = labels.get(crd_key, None)
if not label_value or crd_value != label_value:
return False
return True
def match_selector(selector, labels):
crd_labels = selector.get('matchLabels', None)
crd_expressions = selector.get('matchExpressions', None)
match_exp = match_lb = True
if crd_expressions:
match_exp = match_expressions(crd_expressions,
labels)
if crd_labels and labels:
match_lb = match_labels(crd_labels, labels)
return match_exp and match_lb
def get_namespace_subnet_cidr(namespace):
kubernetes = clients.get_kubernetes_client()
try:
ns_annotations = namespace['metadata']['annotations']
ns_name = ns_annotations[constants.K8S_ANNOTATION_NET_CRD]
except KeyError:
LOG.exception('Namespace handler must be enabled to support '
'Network Policies with namespaceSelector')
raise k_exc.ResourceNotReady(namespace)
try:
net_crd = kubernetes.get('{}/kuryrnets/{}'.format(
constants.K8S_API_CRD, ns_name))
except k_exc.K8sClientException:
LOG.exception("Kubernetes Client Exception.")
raise
return net_crd['spec']['subnetCIDR']

View File

@ -15,10 +15,12 @@
from oslo_cache import core as cache
from oslo_config import cfg as oslo_cfg
from oslo_log import log as logging
from oslo_serialization import jsonutils
from kuryr_kubernetes import clients
from kuryr_kubernetes import constants
from kuryr_kubernetes.controller.drivers import base as drivers
from kuryr_kubernetes.controller.drivers import utils as drivers_utils
from kuryr_kubernetes import exceptions
from kuryr_kubernetes.handlers import k8s_base
from kuryr_kubernetes import utils
@ -57,6 +59,17 @@ class NamespaceHandler(k8s_base.ResourceEventHandler):
self._drv_vif_pool.set_vif_driver()
def on_present(self, namespace):
current_namespace_labels = namespace['metadata'].get('labels')
previous_namespace_labels = drivers_utils.get_annotated_labels(
namespace, constants.K8S_ANNOTATION_NAMESPACE_LABEL)
LOG.debug("Got previous namespace labels from annotation: %r",
previous_namespace_labels)
if (previous_namespace_labels and
current_namespace_labels != previous_namespace_labels):
self._drv_sg.update_namespace_sg_rules(namespace)
self._set_namespace_labels(namespace, current_namespace_labels)
ns_name = namespace['metadata']['name']
project_id = self._drv_project.get_project(namespace)
net_crd_id = self._get_net_crd_id(namespace)
@ -85,6 +98,8 @@ class NamespaceHandler(k8s_base.ResourceEventHandler):
try:
net_crd = self._add_kuryrnet_crd(ns_name, net_crd_spec)
self._set_net_crd(namespace, net_crd)
self._drv_sg.create_namespace_sg_rules(namespace)
self._set_namespace_labels(namespace, current_namespace_labels)
except exceptions.K8sClientException:
LOG.exception("Kuryrnet CRD could not be added. Rolling back "
"network resources created for the namespace.")
@ -108,8 +123,8 @@ class NamespaceHandler(k8s_base.ResourceEventHandler):
else:
LOG.debug("There is no security group associated with the "
"namespace to be deleted")
self._del_kuryrnet_crd(net_crd_id)
self._drv_sg.delete_namespace_sg_rules(namespace)
def is_ready(self, quota):
if not utils.has_kuryr_crd(constants.K8S_API_CRD_KURYRNETS):
@ -191,3 +206,16 @@ class NamespaceHandler(k8s_base.ResourceEventHandler):
LOG.exception("Kubernetes Client Exception deleting kuryrnet "
"CRD.")
raise
def _set_namespace_labels(self, namespace, labels):
if not labels:
LOG.debug("Removing Label annotation: %r", labels)
annotation = None
else:
annotation = jsonutils.dumps(labels, sort_keys=True)
LOG.debug("Setting Labels annotation: %r", annotation)
k8s = clients.get_kubernetes_client()
k8s.annotate(namespace['metadata']['selfLink'],
{constants.K8S_ANNOTATION_NAMESPACE_LABEL: annotation},
resource_version=namespace['metadata']['resourceVersion'])

View File

@ -61,6 +61,131 @@ def get_namespace_obj():
}
def get_no_match_crd_namespace_obj():
return {
"kind": "Namespace",
"metadata": {
"annotations": {
"openstack.org/kuryr-namespace-label": '{"name": "dev"}',
"openstack.org/kuryr-net-crd": "ns-dev"
},
"labels": {"name": "prod"},
"name": "prod",
"selfLink": "/api/v1/namespaces/dev"}}
def get_match_crd_namespace_obj():
return {
"kind": "Namespace",
"metadata": {
"annotations": {
"openstack.org/kuryr-namespace-label": '{"name": "dev"}',
"openstack.org/kuryr-net-crd": "ns-dev"
},
"labels": {
"name": "dev"
},
"name": "dev",
"selfLink": "/api/v1/namespaces/dev"}}
def get_match_crd_pod_obj():
return {
'kind': 'Pod',
'metadata': {
'name': mock.sentinel.pod_name,
'namespace': 'dev',
'labels': {
'tier': 'backend'},
'annotations': {
'openstack.org/kuryr-pod-label': '{"tier": "backend"}'}},
'status': {'podIP': mock.sentinel.podIP}}
def get_sg_rule():
pod_ip = get_match_crd_pod_obj()['status'].get('podIP')
return {
"namespace": 'dev',
"security_group_rule": {
"description": "Kuryr-Kubernetes NetPolicy SG rule",
"direction": "ingress",
"ethertype": "IPv4",
"id": 'f15ff50a-e8a4-4872-81bf-a04cbb8cb388',
"port_range_max": 6379,
"port_range_min": 6379,
"protocol": "tcp",
"remote_ip_prefix": pod_ip,
"security_group_id": '36923e76-026c-422b-8dfd-7292e7c88228'}}
def get_matched_crd_obj():
return {
"kind": "KuryrNetPolicy",
"metadata": {"name": "np-test-network-policy",
"namespace": "default"},
"spec": {
"egressSgRules": [],
"ingressSgRules": [get_sg_rule()],
"networkpolicy_spec": {
"ingress": [
{"from": [
{"namespaceSelector": {
"matchLabels": {"name": "dev"}}}],
"ports": [
{"port": 6379,
"protocol": "TCP"}]}],
"podSelector": {"matchLabels": {"app": "demo"}},
"policyTypes": ["Ingress"]},
"podSelector": {"matchLabels": {"app": "demo"}},
"securityGroupId": '36923e76-026c-422b-8dfd-7292e7c88228'}}
def get_crd_obj_no_match():
return {
"kind": "KuryrNetPolicy",
"metadata": {"name": "np-test-network-policy",
"namespace": "default"},
"spec": {
"egressSgRules": [],
"ingressSgRules": [],
"networkpolicy_spec": {
"ingress": [
{"from": [
{"namespaceSelector": {
"matchLabels": {"name": "dev"}}}],
"ports": [
{"port": 6379,
"protocol": "TCP"}]}],
"podSelector": {"matchLabels": {"app": "demo"}},
"policyTypes": ["Ingress"]},
"podSelector": {"matchLabels": {"app": "demo"}},
"securityGroupId": '36923e76-026c-422b-8dfd-7292e7c88228'}}
def get_crd_obj_with_all_selectors():
return {
"kind": "KuryrNetPolicy",
"metadata": {"name": "np-test-network-policy",
"namespace": "default"},
"spec": {
"egressSgRules": [],
"ingressSgRules": [],
"networkpolicy_spec": {
"ingress": [
{"from": [
{"namespaceSelector": {
"matchLabels": {"name": "dev"}},
"podSelector": {
"matchLabels": {"tier": "backend"}}}],
"ports": [
{"port": 6379,
"protocol": "TCP"}]}],
"podSelector": {"matchLabels": {"app": "demo"}},
"policyTypes": ["Ingress"]},
"podSelector": {"matchLabels": {"app": "demo"}},
"securityGroupId": '36923e76-026c-422b-8dfd-7292e7c88228'}}
class TestNamespacePodSecurityGroupsDriver(test_base.TestCase):
@mock.patch('kuryr_kubernetes.controller.drivers.'
@ -165,3 +290,143 @@ class TestNamespacePodSecurityGroupsDriver(test_base.TestCase):
cls.delete_sg(m_driver, sg_id)
neutron.delete_security_group.assert_called_once_with(sg_id)
@mock.patch('kuryr_kubernetes.controller.drivers.utils.'
'patch_kuryr_crd')
@mock.patch('kuryr_kubernetes.controller.drivers.utils.'
'delete_security_group_rule')
@mock.patch('kuryr_kubernetes.controller.drivers.utils.'
'get_kuryrnetpolicy_crds')
def test_delete_namespace_sg_rule(self, m_get_knp_crd, m_delete_sg_rule,
m_patch_kuryr_crd):
cls = namespace_security_groups.NamespacePodSecurityGroupsDriver
m_driver = mock.MagicMock(spec=cls)
i_rule = get_matched_crd_obj()['spec']['ingressSgRules'][0]
sg_rule_id = i_rule.get('security_group_rule')['id']
m_get_knp_crd.return_value = {"items": [get_matched_crd_obj()]}
cls.delete_namespace_sg_rules(m_driver, get_match_crd_namespace_obj())
m_get_knp_crd.assert_called_once()
m_delete_sg_rule.assert_called_once_with(sg_rule_id)
m_patch_kuryr_crd.assert_called_once()
@mock.patch('kuryr_kubernetes.controller.drivers.utils.'
'patch_kuryr_crd')
@mock.patch('kuryr_kubernetes.controller.drivers.utils.'
'delete_security_group_rule')
@mock.patch('kuryr_kubernetes.controller.drivers.utils.'
'get_kuryrnetpolicy_crds')
def test_delete_namespace_sg_rule_no_match(self, m_get_knp_crd,
m_delete_sg_rule,
m_patch_kuryr_crd):
cls = namespace_security_groups.NamespacePodSecurityGroupsDriver
m_driver = mock.MagicMock(spec=cls)
m_get_knp_crd.return_value = {"items": [get_matched_crd_obj()]}
cls.delete_namespace_sg_rules(m_driver,
get_no_match_crd_namespace_obj())
m_get_knp_crd.assert_called_once()
m_delete_sg_rule.assert_not_called()
m_patch_kuryr_crd.assert_not_called()
@mock.patch('kuryr_kubernetes.controller.drivers.'
'namespace_security_groups._create_sg_rule')
@mock.patch('kuryr_kubernetes.controller.drivers.utils.'
'match_selector')
@mock.patch('kuryr_kubernetes.controller.drivers.utils.'
'get_namespace_subnet_cidr')
def test__parse_rules(self, m_get_ns_subnet_cidr, m_match_selector,
m_create_sg_rule):
crd = get_crd_obj_no_match()
policy = crd['spec']['networkpolicy_spec']
i_rule = policy.get('ingress')[0]
ns_selector = i_rule['from'][0].get('namespaceSelector')
ns = get_match_crd_namespace_obj()
m_get_ns_subnet_cidr.return_value = '10.0.2.0/26'
m_match_selector.return_value = True
m_create_sg_rule.return_value = get_sg_rule()
matched, rules = namespace_security_groups._parse_rules('ingress',
crd, ns)
m_get_ns_subnet_cidr.assert_called_once_with(ns)
m_match_selector.assert_called_once_with(ns_selector,
ns['metadata']['labels'])
m_create_sg_rule.assert_called_once()
self.assertEqual(matched, True)
self.assertEqual(rules, [get_sg_rule()])
@mock.patch('kuryr_kubernetes.controller.drivers.'
'namespace_security_groups._create_sg_rule')
@mock.patch('kuryr_kubernetes.controller.drivers.utils.'
'match_selector')
@mock.patch('kuryr_kubernetes.controller.drivers.utils.'
'get_namespace_subnet_cidr')
def test__parse_rules_no_match(self, m_get_ns_subnet_cidr,
m_match_selector, m_create_sg_rule):
crd = get_crd_obj_no_match()
policy = crd['spec']['networkpolicy_spec']
i_rule = policy.get('ingress')[0]
ns_selector = i_rule['from'][0].get('namespaceSelector')
ns = get_no_match_crd_namespace_obj()
m_get_ns_subnet_cidr.return_value = '10.0.2.0/26'
m_match_selector.return_value = False
matched, rules = namespace_security_groups._parse_rules('ingress',
crd, ns)
m_get_ns_subnet_cidr.assert_called_once_with(ns)
m_match_selector.assert_called_once_with(ns_selector,
ns['metadata']['labels'])
m_create_sg_rule.assert_not_called()
self.assertEqual(matched, False)
self.assertEqual(rules, [])
@mock.patch('kuryr_kubernetes.controller.drivers.'
'namespace_security_groups._create_sg_rule')
@mock.patch('kuryr_kubernetes.controller.drivers.utils.'
'get_pod_ip')
@mock.patch('kuryr_kubernetes.controller.drivers.utils.'
'get_pods')
@mock.patch('kuryr_kubernetes.controller.drivers.utils.'
'match_selector')
@mock.patch('kuryr_kubernetes.controller.drivers.utils.'
'get_namespace_subnet_cidr')
def test__parse_rules_all_selectors(self, m_get_ns_subnet_cidr,
m_match_selector, m_get_pods,
m_get_pod_ip, m_create_sg_rule):
crd = get_crd_obj_with_all_selectors()
policy = crd['spec']['networkpolicy_spec']
i_rule = policy.get('ingress')[0]
ns_selector = i_rule['from'][0].get('namespaceSelector')
pod_selector = i_rule['from'][0].get('podSelector')
ns = get_match_crd_namespace_obj()
pod = get_match_crd_pod_obj()
m_get_ns_subnet_cidr.return_value = '10.0.2.0/26'
m_match_selector.return_value = True
m_get_pods.return_value = {"items": [pod]}
m_get_pod_ip.return_value = pod['status']['podIP']
m_create_sg_rule.return_value = get_sg_rule()
matched, rules = namespace_security_groups._parse_rules('ingress',
crd, ns)
m_get_ns_subnet_cidr.assert_called_once_with(ns)
m_match_selector.assert_called_once_with(ns_selector,
ns['metadata']['labels'])
m_get_pods.assert_called_once_with(pod_selector,
ns['metadata']['name'])
m_get_pod_ip.assert_called_once_with(pod)
m_create_sg_rule.assert_called_once()
self.assertEqual(matched, True)
self.assertEqual(rules, [get_sg_rule()])

View File

@ -302,7 +302,7 @@ class TestNetworkPolicyDriver(test_base.TestCase):
self.kubernetes.get.side_effect = [{'items': [pod]}, net_crd]
resp = self._driver._get_namespaces_cidr(namespace_selector)
self.assertEqual([subnet_cidr], resp)
self.assertEqual(subnet_cidr, resp[0].get('cidr'))
self.kubernetes.get.assert_called()
def test_get_namespaces_cidr_no_matches(self):
@ -330,7 +330,7 @@ class TestNetworkPolicyDriver(test_base.TestCase):
def test_parse_network_policy_rules_with_rules(self, m_create,
m_get_ns_cidr):
subnet_cidr = '10.10.0.0/24'
m_get_ns_cidr.return_value = [subnet_cidr]
m_get_ns_cidr.return_value = [{'cidr': subnet_cidr, 'namespace': ''}]
self._driver.parse_network_policy_rules(self._policy, self._sg_id)
m_create.assert_called()
m_get_ns_cidr.assert_called()
@ -374,7 +374,7 @@ class TestNetworkPolicyDriver(test_base.TestCase):
def test_parse_network_policy_rules_with_no_ports(self, m_create,
m_get_ns_cidr):
subnet_cidr = '10.10.0.0/24'
m_get_ns_cidr.return_value = [subnet_cidr]
m_get_ns_cidr.return_value = [{'cidr': subnet_cidr, 'namespace': ''}]
policy = self._policy.copy()
selectors = {'namespaceSelector': {
'matchLabels': {
@ -389,9 +389,11 @@ class TestNetworkPolicyDriver(test_base.TestCase):
self._driver.parse_network_policy_rules(policy, self._sg_id)
m_get_ns_cidr.assert_called()
calls = [mock.call(self._sg_id, 'ingress', port_range_min=1,
port_range_max=65535, cidr=subnet_cidr),
port_range_max=65535, cidr=subnet_cidr,
namespace=''),
mock.call(self._sg_id, 'egress', port_range_min=1,
port_range_max=65535, cidr=subnet_cidr)]
port_range_max=65535, cidr=subnet_cidr,
namespace='')]
m_create.assert_has_calls(calls)
def test_knps_on_namespace(self):

View File

@ -258,8 +258,8 @@ class TestNetworkPolicySecurityGroupsDriver(test_base.TestCase):
'create_security_group_rule')
@mock.patch('kuryr_kubernetes.controller.drivers.utils.'
'create_security_group_rule_body')
@mock.patch.object(network_policy_security_groups,
'_match_selector', return_value=True)
@mock.patch('kuryr_kubernetes.controller.drivers.utils.'
'match_selector', return_value=True)
@mock.patch('kuryr_kubernetes.controller.drivers.utils.get_pod_ip')
def test__create_sg_rules(self, m_get_pod_ip,
m_match_selector,
@ -277,16 +277,15 @@ class TestNetworkPolicySecurityGroupsDriver(test_base.TestCase):
policy = crd['spec']['networkpolicy_spec']
rule_list = policy.get('ingress', None)
crd_rules = crd['spec'].get('ingressSgRules')
pod_ns = pod['metadata']['namespace']
for rule_block in rule_list:
for rule in rule_block.get('from', []):
namespace_selector = rule.get('namespaceSelector')
pod_selector = rule.get('podSelector')
matched = network_policy_security_groups._create_sg_rules(
crd, pod, namespace_selector, pod_selector, rule_block,
crd_rules, 'ingress', matched)
new_sg_rule['namespaceSelector'] = namespace_selector
new_sg_rule['podSelector'] = pod_selector
crd, pod, pod_selector, rule_block,
crd_rules, 'ingress', matched, pod_ns)
new_sg_rule['namespace'] = pod_ns
new_sg_rule['security_group_rule']['id'] = sgr_id
m_match_selector.assert_called_once_with(
pod_selector, pod['metadata']['labels'])
@ -296,8 +295,8 @@ class TestNetworkPolicySecurityGroupsDriver(test_base.TestCase):
self.assertEqual([new_sg_rule], crd_rules)
self.assertEqual(matched, True)
@mock.patch.object(network_policy_security_groups,
'_match_selector', return_value=False)
@mock.patch('kuryr_kubernetes.controller.drivers.utils.'
'match_selector', return_value=False)
def test__create_sg_rules_no_match(self, m_match_selector):
crd = self._crd_without_rules
pod = self._pod2
@ -308,17 +307,16 @@ class TestNetworkPolicySecurityGroupsDriver(test_base.TestCase):
for rule_block in rule_list:
for rule in rule_block.get('from', []):
namespace_selector = rule.get('namespaceSelector')
pod_selector = rule.get('podSelector')
matched = network_policy_security_groups._create_sg_rules(
crd, pod, namespace_selector, pod_selector, rule_block,
crd_rules, 'ingress', False)
crd, pod, pod_selector, rule_block,
crd_rules, 'ingress', False, self._namespace)
self.assertEqual(matched, False)
@mock.patch('kuryr_kubernetes.controller.drivers.utils.'
'patch_kuryr_crd')
@mock.patch.object(network_policy_security_groups,
'_get_kuryrnetpolicy_crds')
@mock.patch('kuryr_kubernetes.controller.drivers.utils.'
'get_kuryrnetpolicy_crds')
@mock.patch('kuryr_kubernetes.controller.drivers.utils.'
'delete_security_group_rule')
@mock.patch('kuryr_kubernetes.controller.drivers.utils.get_pod_ip')
@ -347,8 +345,8 @@ class TestNetworkPolicySecurityGroupsDriver(test_base.TestCase):
crd, i_rules, e_rules, crd['spec'].get('podSelector'))
@mock.patch('kuryr_kubernetes.config.CONF')
@mock.patch.object(network_policy_security_groups,
'_get_kuryrnetpolicy_crds')
@mock.patch('kuryr_kubernetes.controller.drivers.utils.'
'get_kuryrnetpolicy_crds')
def test_get_sgs_for_pod_without_label(self, m_get_crds, m_cfg):
m_get_crds.return_value = self._crds
sg_list = [str(mock.sentinel.sg_id)]
@ -360,12 +358,12 @@ class TestNetworkPolicySecurityGroupsDriver(test_base.TestCase):
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')
@mock.patch('kuryr_kubernetes.controller.drivers.utils.'
'match_expressions')
@mock.patch('kuryr_kubernetes.controller.drivers.utils.'
'match_labels')
@mock.patch('kuryr_kubernetes.controller.drivers.utils.'
'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
@ -382,12 +380,12 @@ class TestNetworkPolicySecurityGroupsDriver(test_base.TestCase):
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')
@mock.patch('kuryr_kubernetes.controller.drivers.utils.'
'match_expressions')
@mock.patch('kuryr_kubernetes.controller.drivers.utils.'
'match_labels')
@mock.patch('kuryr_kubernetes.controller.drivers.utils.'
'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):
@ -407,8 +405,8 @@ class TestNetworkPolicySecurityGroupsDriver(test_base.TestCase):
self._crd['spec']['podSelector']['matchLabels'], pod_labels)
self.assertEqual(sg_list, sgs)
@mock.patch.object(network_policy_security_groups,
'_get_kuryrnetpolicy_crds')
@mock.patch('kuryr_kubernetes.controller.drivers.utils.'
'get_kuryrnetpolicy_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', [],
@ -419,12 +417,12 @@ class TestNetworkPolicySecurityGroupsDriver(test_base.TestCase):
self._project_id)
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')
@mock.patch('kuryr_kubernetes.controller.drivers.utils.'
'match_expressions')
@mock.patch('kuryr_kubernetes.controller.drivers.utils.'
'match_labels')
@mock.patch('kuryr_kubernetes.controller.drivers.utils.'
'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