Support network policy update

This commit adds support for updating network policies. It handles the
patch to KuryrNetPolicy CRD and handles security group rules update.

Partially Implements: blueprint k8s-network-policies
Change-Id: I02f69616b8cf9cddd23fd415bbb7517b178907e8
This commit is contained in:
Daniel Mellado 2018-09-25 08:57:27 -04:00 committed by Luis Tomas Bolivar
parent 80aeec3dcc
commit 76db817fb5
6 changed files with 670 additions and 130 deletions

View File

@ -1,19 +1,16 @@
Enable network policy support functionality
===========================================
Please follow the next steps in order to enable the network policy support
feature:
1. Enable the policy handler to response to network policy events. As this is
not enabled by default you'd have to explicitly add that to the list of
enabled handlers at kuryr.conf (further info on how to do this can be found
at :doc:`./devstack/containerized`)::
Enable the policy handler to respond to network policy events. As this is not
done by default you'd have to explicitly add that to the list of enabled
handlers at kuryr.conf (further info on how to do this can be found at
:doc:`./devstack/containerized`)::
[kubernetes]
enabled_handlers=vif,lb,lbaasspec,policy
Note that you need to restart the kuryr controller after applying the above
detailed steps. For devstack non-containerized deployments::
Note you need to restart the kuryr controller after applying the above step.
For devstack non-containerized deployments::
$ sudo systemctl restart devstack@kuryr-kubernetes.service
@ -49,23 +46,11 @@ Testing the network policy support functionality
- Egress
ingress:
- from:
- ipBlock:
cidr: 172.17.0.0/16
except:
- 172.17.1.0/24
- namespaceSelector:
matchLabels:
project: myproject
- podSelector:
matchLabels:
role: frontend
ports:
- protocol: TCP
port: 6379
egress:
- to:
- ipBlock:
cidr: 10.0.0.0/24
ports:
- protocol: TCP
port: 5978
@ -84,48 +69,70 @@ Testing the network policy support functionality
NAME POD-SELECTOR AGE
test-network-policy role=db 2s
$ openstack security group list | grep test-network-policy
| dabdf308-7eed-43ef-a058-af84d1954acb | test-network-policy
$ openstack security group list | grep sg-test-network-policy
| dabdf308-7eed-43ef-a058-af84d1954acb | sg-test-network-policy
4. Check that the rules are in place for the security group::
$ kubectl get kuryrnetpolicy np-test-network-policy -o yaml
...
apiVersion: openstack.org/v1
kind: KuryrNetPolicy
metadata:
annotations:
networkpolicy_name: test-network-policy
networkpolicy_namespace: default
networkpolicy_uid: aee1c59f-c634-11e8-b63d-002564fdd760
clusterName: ""
creationTimestamp: 2018-10-02T11:17:02Z
generation: 0
name: np-test-network-policy
namespace: default
resourceVersion: "2117"
selfLink: /apis/openstack.org/v1/namespaces/default/kuryrnetpolicies/np-test-network-policy
uid: afb99326-c634-11e8-b63d-002564fdd760
spec:
egressSgRules:
- security_group_rule:
created_at: 2018-09-19T06:15:07Z
description: Kuryr-Kubernetes egress SG rule
direction: egress
ethertype: IPv4
id: 93a3b0cc-611c-493b-9a28-0fb8517a50f1
port_range_max: 5978
port_range_min: 5978
project_id: c54246797a8b485389c406e8571539ef
protocol: tcp
...
security_group_id: 7f4f8003-5585-4231-9306-e5bdcc6d23df
tenant_id: c54246797a8b485389c406e8571539ef
updated_at: 2018-09-19T06:15:07Z
description: Kuryr-Kubernetes NetPolicy SG rule
direction: egress
ethertype: IPv4
id: 6297c198-b385-44f3-8b43-29951f933a8f
port_range_max: 5978
port_range_min: 5978
protocol: tcp
security_group_id: cdee7815-3b49-4a3e-abc8-31e384ab75c5
ingressSgRules:
- security_group_rule:
created_at: 2018-09-19T06:15:07Z
description: Kuryr-Kubernetes ingress SG rule
direction: ingress
ethertype: IPv4
id: 659b7d61-3a48-4c4a-8810-df20e4c1bfa2
port_range_max: 6379
port_range_min: 6379
project_id: c54246797a8b485389c406e8571539ef
protocol: tcp
...
security_group_id: 7f4f8003-5585-4231-9306-e5bdcc6d23df
tenant_id: c54246797a8b485389c406e8571539ef
updated_at: 2018-09-19T06:15:07Z
securityGroupId: 7f4f8003-5585-4231-9306-e5bdcc6d23df
securityGroupName: test-network-policy
- security_group_rule:
description: Kuryr-Kubernetes NetPolicy SG rule
direction: ingress
ethertype: IPv4
id: f4e11e73-81c6-4c1b-9760-714eedff417b
port_range_max: 6379
port_range_min: 6379
protocol: tcp
security_group_id: cdee7815-3b49-4a3e-abc8-31e384ab75c5
securityGroupId: cdee7815-3b49-4a3e-abc8-31e384ab75c5
securityGroupName: sg-test-network-policy
networkpolicy_spec:
egress:
- ports:
- port: 5978
protocol: TCP
to:
ingress:
- from:
ports:
- port: 6379
protocol: TCP
podSelector:
matchLabels:
role: db
policyTypes:
- Ingress
- Egress
$ openstack security group rule list test-network-policy --protocol tcp -c "IP Protocol" -c "Port Range" -c "Direction" --long
$ openstack security group rule list sg-test-network-policy --protocol tcp -c "IP Protocol" -c "Port Range" -c "Direction" --long
+-------------+------------+-----------+
| IP Protocol | Port Range | Direction |
+-------------+------------+-----------+
@ -133,7 +140,72 @@ Testing the network policy support functionality
| tcp | 5978:5978 | egress |
+-------------+------------+-----------+
5. Confirm the teardown of the resources once the network policy is removed::
5. Network policies can also be updated in the following way::
$ kubectl patch networkpolicy test-network-policy -p '{"spec":{"ingress":[{"ports":[{"port": 8081,"protocol": "UDP"}]}]}}'
networkpolicy "test-network-policy" patched
$ kubectl get knp np-test-network-policy -o yaml
apiVersion: openstack.org/v1
kind: KuryrNetPolicy
metadata:
annotations:
networkpolicy_name: test-network-policy
networkpolicy_namespace: default
networkpolicy_uid: aee1c59f-c634-11e8-b63d-002564fdd760
clusterName: ""
creationTimestamp: 2018-10-02T11:17:02Z
generation: 0
name: np-test-network-policy
namespace: default
resourceVersion: "1546"
selfLink: /apis/openstack.org/v1/namespaces/default/kuryrnetpolicies/np-test-network-policy
uid: afb99326-c634-11e8-b63d-002564fdd760
spec:
egressSgRules:
- security_group_rule:
description: Kuryr-Kubernetes NetPolicy SG rule
direction: egress
ethertype: IPv4
id: 1969a0b3-55e1-43d7-ba16-005b4ed4cbb7
port_range_max: 5978
port_range_min: 5978
protocol: tcp
security_group_id: cdee7815-3b49-4a3e-abc8-31e384ab75c5
ingressSgRules:
- security_group_rule:
description: Kuryr-Kubernetes NetPolicy SG rule
direction: ingress
ethertype: IPv4
id: 6598aa1f-4f94-4fb2-81ce-d3649ba28f33
port_range_max: 8081
port_range_min: 8081
protocol: udp
security_group_id: cdee7815-3b49-4a3e-abc8-31e384ab75c5
securityGroupId: cdee7815-3b49-4a3e-abc8-31e384ab75c5
networkpolicy_spec:
egress:
- ports:
- port: 5978
protocol: TCP
to:
ingress:
- ports:
- port: 8081
protocol: UDP
policyTypes:
- Ingress
- Egress
$ openstack security group rule list sg-test-network-policy -c "IP Protocol" -c "Port Range" -c "Direction" --long
+-------------+------------+-----------+
| IP Protocol | Port Range | Direction |
+-------------+------------+-----------+
| tcp | 6379:6379 | ingress |
| udp | 8081:8081 | egress |
+-------------+------------+-----------+
6. Confirm the teardown of the resources once the network policy is removed::
$ kubectl delete -f network_policy.yml
@ -141,4 +213,4 @@ Testing the network policy support functionality
$ kubectl get networkpolicies
$ openstack security group list | grep test-network-policy
$ openstack security group list | grep sg-test-network-policy

View File

@ -26,27 +26,123 @@ LOG = logging.getLogger(__name__)
class NetworkPolicyDriver(base.NetworkPolicyDriver):
"""Provides security groups actions based on K8s Network Policies"""
"""Provide security groups actions based on K8s Network Policies"""
def __init__(self):
self.neutron = clients.get_neutron_client()
self.kubernetes = clients.get_kubernetes_client()
def ensure_network_policy(self, policy, project_id):
neutron = clients.get_neutron_client()
LOG.debug("Creating network policy %s" % policy['metadata']['name'])
"""Create security group rules out of network policies
Triggered by events from network policies, this method ensures that
security groups and security group rules are created or updated in
reaction to kubernetes network policies events.
"""
LOG.debug("Creating network policy %s", policy['metadata']['name'])
if self._get_kuryrnetpolicy_crd(policy):
LOG.debug("Already existing CRD")
return
self.update_security_group_rules_from_network_policy(policy)
else:
self.create_security_group_rules_from_network_policy(policy,
project_id)
def update_security_group_rules_from_network_policy(self, policy):
"""Update security group rules
This method updates security group rules based on CRUD events gotten
from a configuration or patch to an existing network policy
"""
crd = self._get_kuryrnetpolicy_crd(policy)
crd_name = crd['metadata']['name']
LOG.debug("Already existing CRD %s", crd_name)
sg_id = crd['spec']['securityGroupId']
# Fetch existing SG rules from kuryrnetpolicy CRD
existing_sg_rules = None
existing_i_rules = crd['spec'].get('ingressSgRules')
existing_e_rules = crd['spec'].get('egressSgRules')
if existing_i_rules or existing_e_rules:
existing_sg_rules = existing_i_rules + existing_e_rules
# Parse network policy update and get new ruleset
i_rules, e_rules = self.parse_network_policy_rules(policy, sg_id)
current_sg_rules = i_rules + e_rules
# Get existing security group rules ids
sgr_ids = [x['security_group_rule'].pop('id') for x in
existing_sg_rules]
# SG rules that are meant to be kept get their id back
sg_rules_to_keep = [existing_sg_rules.index(rule) for rule in
existing_sg_rules if rule in current_sg_rules]
for sg_rule in sg_rules_to_keep:
sgr_id = sgr_ids[sg_rule]
existing_sg_rules[sg_rule]['security_group_rule']['id'] = sgr_id
# Delete SG rules that are no longer in the updated policy
sg_rules_to_delete = [existing_sg_rules.index(rule) for rule in
existing_sg_rules if rule not in
current_sg_rules]
for sg_rule in sg_rules_to_delete:
try:
self._delete_security_group_rule(sgr_ids[sg_rule])
except n_exc.NotFound:
LOG.debug('Trying to delete non existing sg_rule %s', sg_rule)
# Create new rules that weren't already on the security group
sg_rules_to_add = [rule for rule in current_sg_rules if rule not in
existing_sg_rules]
for sg_rule in sg_rules_to_add:
sgr_id = self._create_security_group_rule(sg_rule)
if sg_rule['security_group_rule'].get('direction') == 'ingress':
for i_rule in i_rules:
if sg_rule == i_rule:
i_rule["security_group_rule"]["id"] = sgr_id
else:
for e_rule in e_rules:
if sg_rule == e_rule:
e_rule["security_group_rule"]["id"] = sgr_id
# Annotate kuryrnetpolicy CRD with current policy and ruleset
LOG.debug('Patching KuryrNetPolicy CRD %s', crd_name)
try:
self.kubernetes.patch('spec', crd['metadata']['selfLink'],
{'ingressSgRules': i_rules,
'egressSgRules': e_rules,
'networkpolicy_spec': policy['spec']})
except exceptions.K8sClientException:
LOG.exception('Error updating kuryrnetpolicy CRD %s', crd_name)
raise
def create_security_group_rules_from_network_policy(self, policy,
project_id):
"""Create initial security group and rules
This method creates the initial security group for hosting security
group rules coming out of network policies' parsing.
"""
sg_name = ("sg-" + policy['metadata']['namespace'] + "-" +
policy['metadata']['name'])
security_group_body = {
"security_group":
{
"name": policy['metadata']['name'],
"project_id": project_id,
"description": "Kuryr-Kubernetes NetPolicy SG"
{
"name": sg_name,
"project_id": project_id,
"description": "Kuryr-Kubernetes NetPolicy SG"
}
}
}
sg = None
try:
sg = neutron.create_security_group(body=security_group_body)
i_rules, e_rules = self.apply_network_policy_rules(policy, sg)
# Create initial security group
sg = self.neutron.create_security_group(body=security_group_body)
sg_id = sg['security_group']['id']
i_rules, e_rules = self.parse_network_policy_rules(policy, sg_id)
for i_rule in i_rules:
sgr_id = self._create_security_group_rule(i_rule)
i_rule['security_group_rule']['id'] = sgr_id
for e_rule in e_rules:
sgr_id = self._create_security_group_rule(e_rule)
e_rule['security_group_rule']['id'] = sgr_id
except n_exc.NeutronClientException:
LOG.exception("Error creating security group for network policy. ")
LOG.exception("Error creating security group for network policy "
" %s", policy['metadata']['name'])
# If there's any issue creating sg rules, remove them
if sg:
self.neutron.delete_security_group(sg['security_group']['id'])
raise
try:
self._add_kuryrnetpolicy_crd(policy, project_id,
@ -54,80 +150,128 @@ class NetworkPolicyDriver(base.NetworkPolicyDriver):
e_rules)
except exceptions.K8sClientException:
LOG.exception("Rolling back security groups")
neutron.delete_security_group(sg['security_group']['id'])
# Same with CRD creation
self.neutron.delete_security_group(sg['security_group']['id'])
raise
try:
crd = self._get_kuryrnetpolicy_crd(policy)
self.kubernetes.annotate(policy['metadata']['selfLink'],
{"kuryrnetpolicy_selfLink":
crd['metadata']['selfLink']})
except exceptions.K8sClientException:
LOG.exception('Error annotating network policy')
raise
def apply_network_policy_rules(self, policy, sg):
"""Creates and applies security group rules out of network policies.
def parse_network_policy_rules(self, policy, sg_id):
"""Create security group rule bodies out of network policies.
Whenever a notification from the handler 'on-present' method is
received, security group rules are created out of network policies'
ingress and egress ports blocks.
"""
LOG.debug('Parsing Network Policy %s' % policy['metadata']['name'])
ingress_rule_list = policy['spec']['ingress']
egress_rule_list = policy['spec']['egress']
ingress_sg_rule_list = []
egress_sg_rule_list = []
for ingress_rule in ingress_rule_list:
LOG.debug('Parsing Ingress Rule %s' % ingress_rule)
if 'ports' in ingress_rule:
for port in ingress_rule['ports']:
i_rule = self._create_security_group_rule(
sg['security_group']['id'], 'ingress', port['port'],
protocol=port['protocol'].lower())
ingress_sg_rule_list.append(i_rule)
else:
LOG.debug('This network policy specifies no ingress ports')
for egress_rule in egress_rule_list:
LOG.debug('Parsing Egress Rule %s' % egress_rule)
if 'ports' in egress_rule:
for port in egress_rule['ports']:
e_rule = self._create_security_group_rule(
sg['security_group']['id'], 'egress', port['port'],
protocol=port['protocol'].lower())
egress_sg_rule_list.append(e_rule)
else:
LOG.debug('This network policy specifies no egress ports')
return ingress_sg_rule_list, egress_sg_rule_list
ingress_rule_list = policy['spec'].get('ingress')
egress_rule_list = policy['spec'].get('egress')
ingress_sg_rule_body_list = []
egress_sg_rule_body_list = []
def _create_security_group_rule(
if ingress_rule_list:
if ingress_rule_list[0] == {}:
LOG.debug('Applying default all open policy from %s',
policy['metadata']['selfLink'])
i_rule = self._create_security_group_rule_body(
sg_id, 'ingress', port_range_min=1, port_range_max=65535)
ingress_sg_rule_body_list.append(i_rule)
for ingress_rule in ingress_rule_list:
LOG.debug('Parsing Ingress Rule %s', ingress_rule)
if 'ports' in ingress_rule:
for port in ingress_rule['ports']:
i_rule = self._create_security_group_rule_body(
sg_id, 'ingress', port['port'],
protocol=port['protocol'].lower())
ingress_sg_rule_body_list.append(i_rule)
else:
LOG.debug('This network policy specifies no ingress '
'ports: %s', policy['metadata']['selfLink'])
if egress_rule_list:
if egress_rule_list[0] == {}:
LOG.debug('Applying default all open policy from %s',
policy['metadata']['selfLink'])
e_rule = self._create_security_group_rule_body(
sg_id, 'egress', port_range_min=1, port_range_max=65535)
egress_sg_rule_body_list.append(e_rule)
for egress_rule in egress_rule_list:
LOG.debug('Parsing Egress Rule %s', egress_rule)
if 'ports' in egress_rule:
for port in egress_rule['ports']:
e_rule = self._create_security_group_rule_body(
sg_id, 'egress', port['port'],
protocol=port['protocol'].lower())
egress_sg_rule_body_list.append(e_rule)
else:
LOG.debug('This network policy specifies no egress '
'ports: %s', policy['metadata']['selfLink'])
return ingress_sg_rule_body_list, egress_sg_rule_body_list
def _create_security_group_rule_body(
self, security_group_id, direction, port_range_min,
port_range_max=None, protocol='TCP', ethertype='IPv4',
description="Kuryr-Kubernetes NetPolicy SG rule"):
if not port_range_max:
port_range_max = port_range_min
security_group_rule_body = {
"security_group_rule": {
"ethertype": ethertype,
"security_group_id": security_group_id,
"description": description,
"direction": direction,
"protocol": protocol,
"port_range_min": port_range_min,
"port_range_max": port_range_max
u'security_group_rule': {
u'ethertype': ethertype,
u'security_group_id': security_group_id,
u'description': description,
u'direction': direction,
u'protocol': protocol,
u'port_range_min': port_range_min,
u'port_range_max': port_range_max
}
}
LOG.debug("Creating sg rule %s" % security_group_rule_body)
neutron = clients.get_neutron_client()
LOG.debug("Creating sg rule body %s", security_group_rule_body)
return security_group_rule_body
def _create_security_group_rule(self, body):
sgr = ''
try:
sg_rule = neutron.create_security_group_rule(
body=security_group_rule_body)
sgr = self.neutron.create_security_group_rule(
body=body)
except n_exc.Conflict:
LOG.debug("Failed to create already existing security group "
"rule %s", body)
except n_exc.NeutronClientException:
LOG.exception("Error creating security group rule for the network "
"policy.")
LOG.debug("Error creating security group rule")
raise
return sgr["security_group_rule"]["id"]
def _delete_security_group_rule(self, security_group_rule_id):
try:
self.neutron.delete_security_group_rule(
security_group_rule=security_group_rule_id)
except n_exc.NotFound:
LOG.debug("Error deleting security group rule as it does not "
"exist: %s", security_group_rule_id)
except n_exc.NeutronClientException:
LOG.debug("Error deleting security group rule: %s",
security_group_rule_id)
raise
return sg_rule
def release_network_policy(self, policy, project_id):
neutron = clients.get_neutron_client()
netpolicy_crd = self._get_kuryrnetpolicy_crd(policy)
if netpolicy_crd is not None:
try:
sg_id = netpolicy_crd['spec']['securityGroupId']
neutron.delete_security_group(sg_id)
self.neutron.delete_security_group(sg_id)
except n_exc.NotFound:
LOG.debug("Security Group not found: %s", sg_id)
raise
except n_exc.Conflict:
LOG.debug("Segurity Group already in use: %s", sg_id)
raise
except n_exc.NeutronClientException:
LOG.exception("Error deleting security group %s.", sg_id)
raise
@ -136,13 +280,13 @@ class NetworkPolicyDriver(base.NetworkPolicyDriver):
netpolicy_crd['metadata']['namespace'])
def _get_kuryrnetpolicy_crd(self, policy):
kubernetes = clients.get_kubernetes_client()
netpolicy_crd_name = "np-" + policy['metadata']['name']
netpolicy_crd_namespace = policy['metadata']['namespace']
try:
netpolicy_crd = kubernetes.get('{}/{}/kuryrnetpolicies/{}'.format(
constants.K8S_API_CRD_NAMESPACES, netpolicy_crd_namespace,
netpolicy_crd_name))
netpolicy_crd = self.kubernetes.get(
'{}/{}/kuryrnetpolicies/{}'.format(
constants.K8S_API_CRD_NAMESPACES, netpolicy_crd_namespace,
netpolicy_crd_name))
except exceptions.K8sResourceNotFound:
return None
except exceptions.K8sClientException:
@ -152,7 +296,6 @@ class NetworkPolicyDriver(base.NetworkPolicyDriver):
def _add_kuryrnetpolicy_crd(self, policy, project_id, sg_id, i_rules,
e_rules):
kubernetes = clients.get_kubernetes_client()
networkpolicy_name = policy['metadata']['name']
netpolicy_crd_name = "np-" + networkpolicy_name
namespace = policy['metadata']['namespace']
@ -182,7 +325,7 @@ class NetworkPolicyDriver(base.NetworkPolicyDriver):
kubernetes_post = '{}/{}/kuryrnetpolicies'.format(
constants.K8S_API_CRD_NAMESPACES,
namespace)
kubernetes.post(kubernetes_post, netpolicy_crd)
self.kubernetes.post(kubernetes_post, netpolicy_crd)
except exceptions.K8sClientException:
LOG.exception("Kubernetes Client Exception creating kuryrnetpolicy"
" CRD. %s" % exceptions.K8sClientException)
@ -191,10 +334,9 @@ class NetworkPolicyDriver(base.NetworkPolicyDriver):
def _del_kuryrnetpolicy_crd(self, netpolicy_crd_name,
netpolicy_crd_namespace):
kubernetes = clients.get_kubernetes_client()
try:
LOG.debug("Deleting KuryrNetPolicy CRD %s" % netpolicy_crd_name)
kubernetes.delete('{}/{}/kuryrnetpolicies/{}'.format(
self.kubernetes.delete('{}/{}/kuryrnetpolicies/{}'.format(
constants.K8S_API_CRD_NAMESPACES,
netpolicy_crd_namespace,
netpolicy_crd_name))

View File

@ -544,7 +544,7 @@ class LoadBalancerHandler(k8s_base.ResourceEventHandler):
k8s = clients.get_kubernetes_client()
svc_link = self._get_service_link(endpoints)
try:
k8s.patch_status(svc_link, status_data)
k8s.patch("status", svc_link, status_data)
except k_exc.K8sClientException:
# REVISIT(ivc): only raise ResourceNotReady for NotFound
raise k_exc.ResourceNotReady(svc_link)

View File

@ -92,14 +92,13 @@ class K8sClient(object):
return url, header
def patch_status(self, path, data):
LOG.debug("Patch_status %(path)s: %(data)s", {
def patch(self, field, path, data):
LOG.debug("Patch %(path)s: %(data)s", {
'path': path, 'data': data})
path = path + '/status'
if field == 'status':
path = path + '/' + str(field)
url, header = self._get_url_and_header(path)
response = requests.patch(url, json={"status": data},
response = requests.patch(url, json={field: data},
headers=header, cert=self.cert,
verify=self.verify_server)
if response.ok:

View File

@ -0,0 +1,239 @@
# Copyright 2018 Red Hat, Inc.
#
# 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.
import mock
from kuryr_kubernetes.controller.drivers import network_policy
from kuryr_kubernetes import exceptions
from kuryr_kubernetes.tests import base as test_base
from kuryr_kubernetes.tests.unit import kuryr_fixtures as k_fix
from neutronclient.common import exceptions as n_exc
class TestNetworkPolicyDriver(test_base.TestCase):
def setUp(self):
super(TestNetworkPolicyDriver, self).setUp()
self._project_id = mock.sentinel.project_id
self._policy_name = 'np-test'
self._policy_uid = mock.sentinel.policy_uid
self._policy_link = mock.sentinel.policy_link
self._sg_id = mock.sentinel.sg_id
self._i_rules = [{'security_group_rule': {'id': ''}}]
self._e_rules = [{'security_group_rule': {'id': ''}}]
self._policy = {
'apiVersion': u'networking.k8s.io/v1',
'kind': u'NetworkPolicy',
'metadata': {
'name': self._policy_name,
'resourceVersion': u'2259309',
'generation': 1,
'creationTimestamp': u'2018-09-18T14:09:51Z',
'namespace': u'default',
'annotations': {},
'selfLink': self._policy_link,
'uid': self._policy_uid
},
'spec': {
'egress': [{'ports':
[{'port': 5978, 'protocol': 'TCP'}]}],
'ingress': [{'ports':
[{'port': 6379, 'protocol': 'TCP'}]}],
'policyTypes': ['Ingress', 'Egress']
}
}
self._crd = {
'metadata': {'name': mock.sentinel.name,
'selfLink': mock.sentinel.selfLink},
'spec': {
'egressSgRules': [
{'security_group_rule':
{'description': 'Kuryr-Kubernetes NetPolicy SG rule',
'direction': 'egress',
'ethertype': 'IPv4',
'port_range_max': 5978,
'port_range_min': 5978,
'protocol': 'tcp',
'security_group_id': self._sg_id,
'id': mock.sentinel.id
}}],
'ingressSgRules': [
{'security_group_rule':
{'description': 'Kuryr-Kubernetes NetPolicy SG rule',
'direction': 'ingress',
'ethertype': 'IPv4',
'port_range_max': 6379,
'port_range_min': 6379,
'protocol': 'tcp',
'security_group_id': self._sg_id,
'id': mock.sentinel.id
}}],
'securityGroupId': self._sg_id,
'securityGroupName': mock.sentinel.sg_name}}
self.neutron = self.useFixture(k_fix.MockNeutronClient()).client
self.kubernetes = self.useFixture(k_fix.MockK8sClient()).client
self._driver = network_policy.NetworkPolicyDriver()
@mock.patch.object(network_policy.NetworkPolicyDriver,
'_get_kuryrnetpolicy_crd', return_value=False)
@mock.patch.object(network_policy.NetworkPolicyDriver,
'create_security_group_rules_from_network_policy')
@mock.patch.object(network_policy.NetworkPolicyDriver,
'update_security_group_rules_from_network_policy')
def test_ensure_network_policy(self, m_update, m_create, m_get_crd):
self._driver.ensure_network_policy(self._policy, self._project_id)
m_get_crd.assert_called_once_with(self._policy)
m_create.assert_called_once_with(self._policy, self._project_id)
m_update.assert_not_called()
@mock.patch.object(network_policy.NetworkPolicyDriver,
'_get_kuryrnetpolicy_crd', return_value=True)
@mock.patch.object(network_policy.NetworkPolicyDriver,
'create_security_group_rules_from_network_policy')
@mock.patch.object(network_policy.NetworkPolicyDriver,
'update_security_group_rules_from_network_policy')
def test_ensure_network_policy_with_existing_crd(self, m_update, m_create,
m_get_crd):
self._driver.ensure_network_policy(self._policy, self._project_id)
m_get_crd.assert_called_once_with(self._policy)
m_create.assert_not_called()
m_update.assert_called_once_with(self._policy)
@mock.patch.object(network_policy.NetworkPolicyDriver,
'_get_kuryrnetpolicy_crd')
@mock.patch.object(network_policy.NetworkPolicyDriver,
'_add_kuryrnetpolicy_crd')
@mock.patch.object(network_policy.NetworkPolicyDriver,
'parse_network_policy_rules')
def test_create_security_group_rules_from_network_policy(self, m_parse,
m_add_crd,
m_get_crd):
self._driver.neutron.create_security_group.return_value = {
'security_group': {'id': mock.sentinel.id}}
m_parse.return_value = (self._i_rules, self._e_rules)
self._driver.neutron.create_security_group_rule.return_value = {
'security_group_rule': {'id': mock.sentinel.id}}
self._driver.create_security_group_rules_from_network_policy(
self._policy, self._project_id)
m_get_crd.assert_called_once()
m_add_crd.assert_called_once()
@mock.patch.object(network_policy.NetworkPolicyDriver,
'_get_kuryrnetpolicy_crd')
@mock.patch.object(network_policy.NetworkPolicyDriver,
'_add_kuryrnetpolicy_crd')
@mock.patch.object(network_policy.NetworkPolicyDriver,
'parse_network_policy_rules')
def test_create_security_group_rules_with_k8s_exc(self, m_parse,
m_add_crd, m_get_crd):
self._driver.neutron.create_security_group.return_value = {
'security_group': {'id': mock.sentinel.id}}
m_parse.return_value = (self._i_rules, self._e_rules)
m_get_crd.side_effect = exceptions.K8sClientException
self._driver.neutron.create_security_group_rule.return_value = {
'security_group_rule': {'id': mock.sentinel.id}}
self.assertRaises(
exceptions.K8sClientException,
self._driver.create_security_group_rules_from_network_policy,
self._policy, self._project_id)
m_add_crd.assert_called_once()
@mock.patch.object(network_policy.NetworkPolicyDriver,
'_get_kuryrnetpolicy_crd')
@mock.patch.object(network_policy.NetworkPolicyDriver,
'_add_kuryrnetpolicy_crd')
@mock.patch.object(network_policy.NetworkPolicyDriver,
'parse_network_policy_rules')
def test_create_security_group_rules_error_add_crd(self, m_parse,
m_add_crd, m_get_crd):
self._driver.neutron.create_security_group.return_value = {
'security_group': {'id': mock.sentinel.id}}
m_parse.return_value = (self._i_rules, self._e_rules)
m_add_crd.side_effect = exceptions.K8sClientException
self._driver.neutron.create_security_group_rule.return_value = {
'security_group_rule': {'id': mock.sentinel.id}}
self.assertRaises(
exceptions.K8sClientException,
self._driver.create_security_group_rules_from_network_policy,
self._policy, self._project_id)
m_get_crd.assert_not_called()
def test_create_security_group_rules_with_n_exc(self):
self._driver.neutron.create_security_group.side_effect = (
n_exc.NeutronClientException())
self.assertRaises(
n_exc.NeutronClientException,
self._driver.create_security_group_rules_from_network_policy,
self._policy, self._project_id)
@mock.patch.object(network_policy.NetworkPolicyDriver,
'_create_security_group_rule')
@mock.patch.object(network_policy.NetworkPolicyDriver,
'_get_kuryrnetpolicy_crd')
@mock.patch.object(network_policy.NetworkPolicyDriver,
'parse_network_policy_rules')
def test_update_security_group_rules(self, m_parse, m_get_crd,
m_create_sgr):
m_get_crd.return_value = self._crd
m_parse.return_value = (self._i_rules, self._e_rules)
self._driver.update_security_group_rules_from_network_policy(
self._policy)
m_parse.assert_called_with(self._policy, self._sg_id)
@mock.patch.object(network_policy.NetworkPolicyDriver,
'_create_security_group_rule')
@mock.patch.object(network_policy.NetworkPolicyDriver,
'_get_kuryrnetpolicy_crd')
@mock.patch.object(network_policy.NetworkPolicyDriver,
'parse_network_policy_rules')
def test_update_security_group_rules_with_k8s_exc(self, m_parse, m_get_crd,
m_create_sgr):
self._driver.kubernetes.patch.side_effect = (
exceptions.K8sClientException())
m_get_crd.return_value = self._crd
m_parse.return_value = (self._i_rules, self._e_rules)
self.assertRaises(
exceptions.K8sClientException,
self._driver.update_security_group_rules_from_network_policy,
self._policy)
m_parse.assert_called_with(self._policy, self._sg_id)
def test_parse_network_policy_rules(self):
i_rule, e_rule = (
self._driver.parse_network_policy_rules(self._policy, self._sg_id))
self.assertEqual(
self._policy['spec']['ingress'][0]['ports'][0]['port'],
i_rule[0]['security_group_rule']['port_range_min'])
self.assertEqual(
self._policy['spec']['egress'][0]['ports'][0]['port'],
e_rule[0]['security_group_rule']['port_range_min'])
@mock.patch.object(network_policy.NetworkPolicyDriver,
'_create_security_group_rule_body')
def test_parse_network_policy_rules_with_rules(self, m_create):
self._driver.parse_network_policy_rules(self._policy, self._sg_id)
m_create.assert_called()
@mock.patch.object(network_policy.NetworkPolicyDriver,
'_create_security_group_rule_body')
def test_parse_network_policy_rules_with_no_rules(self, m_create):
self._policy['spec'] = {}
self._driver.parse_network_policy_rules(self._policy, self._sg_id)
m_create.assert_not_called()

View File

@ -0,0 +1,88 @@
# Copyright 2018 Red Hat, Inc.
#
# 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.
import mock
from kuryr_kubernetes.controller.drivers import base as drivers
from kuryr_kubernetes.controller.handlers import policy
from kuryr_kubernetes.tests import base as test_base
class TestPolicyHandler(test_base.TestCase):
def setUp(self):
super(TestPolicyHandler, self).setUp()
self._project_id = mock.sentinel.project_id
self._policy_name = 'np-test'
self._policy_uid = mock.sentinel.policy_uid
self._policy_link = mock.sentinel.policy_link
self._policy = {
u'apiVersion': u'networking.k8s.io/v1',
u'kind': u'NetworkPolicy',
u'metadata': {
u'name': self._policy_name,
u'resourceVersion': u'2259309',
u'generation': 1,
u'creationTimestamp': u'2018-09-18T14:09:51Z',
u'namespace': u'default',
u'annotations': {},
u'selfLink': self._policy_link,
u'uid': self._policy_uid
},
u'spec': {
u'egress': [{u'ports':
[{u'port': 5978, u'protocol': u'TCP'}]}],
u'ingress': [{u'ports':
[{u'port': 6379, u'protocol': u'TCP'}]}],
u'policyTypes': [u'Ingress', u'Egress']
}
}
self._handler = mock.MagicMock(spec=policy.NetworkPolicyHandler)
self._handler._drv_project = mock.Mock(
spec=drivers.NetworkPolicyProjectDriver)
self._handler._drv_policy = mock.MagicMock(
spec=drivers.NetworkPolicyDriver)
self._get_project = self._handler._drv_project.get_project
self._get_project.return_value = self._project_id
@mock.patch.object(drivers.NetworkPolicyDriver, 'get_instance')
@mock.patch.object(drivers.NetworkPolicyProjectDriver, 'get_instance')
def test_init(self, m_get_project_driver, m_get_policy_driver):
handler = policy.NetworkPolicyHandler()
m_get_project_driver.assert_called_once()
m_get_policy_driver.assert_called_once()
self.assertEqual(m_get_project_driver.return_value,
handler._drv_project)
self.assertEqual(m_get_policy_driver.return_value, handler._drv_policy)
def test_on_present(self):
policy.NetworkPolicyHandler.on_present(self._handler, self._policy)
ensure_nw_policy = self._handler._drv_policy.ensure_network_policy
ensure_nw_policy.assert_called_once_with(self._policy,
self._project_id)
policy.NetworkPolicyHandler.on_present(self._handler, self._policy)
def test_on_deleted(self):
policy.NetworkPolicyHandler.on_deleted(self._handler, self._policy)
release_nw_policy = self._handler._drv_policy.release_network_policy
release_nw_policy.assert_called_once_with(self._policy,
self._project_id)
policy.NetworkPolicyHandler.on_present(self._handler, self._policy)