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:
parent
80aeec3dcc
commit
76db817fb5
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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()
|
|
@ -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)
|
Loading…
Reference in New Issue