Skip to content

Commit

Permalink
Add network policies actions
Browse files Browse the repository at this point in the history
Signed-off-by: Sylvain Hellegouarch <sh@defuze.org>
  • Loading branch information
Lawouach committed Nov 11, 2020
1 parent d096a45 commit eab30ac
Show file tree
Hide file tree
Showing 4 changed files with 211 additions and 0 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

[Unreleased]: https://github.com/chaostoolkit/chaostoolkit-kubernetes/compare/0.24.1...HEAD

### Added

- Networking package with actions to add/remove network policies

### Changed

- Fix `actions.scale_deployment` to use AppsV1Api in line with the rest of the actions.
Expand Down
Empty file added chaosk8s/networking/__init__.py
Empty file.
172 changes: 172 additions & 0 deletions chaosk8s/networking/actions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
# -*- coding: utf-8 -*-
import json
import os.path
from typing import Any, Dict

from chaoslib.exceptions import ActivityFailed
from chaoslib.types import Secrets
from kubernetes import client
import yaml

from chaosk8s import create_k8s_api_client

__all__ = ["create_network_policy", "remove_network_policy",
"deny_all_ingress", "remove_deny_all_ingress",
"deny_all_egress", "remove_deny_all_egress",
"allow_dns_access", "remove_allow_dns_access"]


def create_network_policy(spec: Dict[str, Any] = None, spec_path: str = None,
ns: str = "default", secrets: Secrets = None):
"""
Create a network policy in the given namespace eitehr from the definition
as `spec` or from a file containing the definition at `spec_path`.
"""
api = create_k8s_api_client(secrets)

if spec_path and os.path.isfile(spec_path):
with open(spec_path) as f:
p, ext = os.path.splitext(spec_path)
if ext == '.json':
spec = json.loads(f.read())
elif ext in ['.yml', '.yaml']:
spec = yaml.safe_load(f.read())
else:
raise ActivityFailed(
"cannot process {path}".format(path=spec_path))

v1 = client.NetworkingV1Api(api)
v1.create_namespaced_network_policy(ns, body=spec)


def remove_network_policy(name: str, ns: str = "default",
secrets: Secrets = None):
"""
Create a network policy in the given namespace eitehr from the definition
as `spec` or from a file containing the definition at `spec_path`.
"""
api = create_k8s_api_client(secrets)
v1 = client.NetworkingV1Api(api)
v1.delete_namespaced_network_policy(name, ns)


def deny_all_ingress(label_selectors: Dict[str, Any] = None,
ns: str = "default", secrets: Secrets = None):
"""
Convenient helper policy to deny ingress network to all pods in a
namespace, unless `label_selectors, in which case, only matching pods will
be impacted.
"""
pod_selector = {}
if label_selectors:
pod_selector["matchLabels"] = label_selectors

create_network_policy(spec={
"apiVersion": "networking.k8s.io/v1",
"kind": "NetworkPolicy",
"metadata": {
"name": "chaostoolkit-deny-all-ingress"
},
"spec": {
"podSelector": pod_selector,
"policyTypes": [
"Ingress"
],
"ingress": []
}
}, ns=ns, secrets=secrets)


def remove_deny_all_ingress(ns: str = "default", secrets: Secrets = None):
"""
Remove the rule set by the `deny_all_ingress` action.
"""
remove_network_policy(
"chaostoolkit-deny-all-ingress", ns=ns, secrets=secrets)


def deny_all_egress(label_selectors: Dict[str, Any] = None,
ns: str = "default", secrets: Secrets = None):
"""
Convenient helper rule to deny all egress network from all pods in a
namespace, unless `label_selectors, in which case, only matching pods will
be impacted.
"""
pod_selector = {}
if label_selectors:
pod_selector["matchLabels"] = label_selectors

create_network_policy({
"apiVersion": "networking.k8s.io/v1",
"kind": "NetworkPolicy",
"metadata": {
"name": "chaostoolkit-deny-all-egress"
},
"spec": {
"podSelector": pod_selector,
"policyTypes": [
"Egress"
]
}
}, ns=ns, secrets=secrets)


def remove_deny_all_egress(ns: str = "default", secrets: Secrets = None):
"""
Remove the rule set by the `deny_all_egress` action.
"""
remove_network_policy(
"chaostoolkit-deny-all-egress", ns=ns, secrets=secrets)


def allow_dns_access(label_selectors: Dict[str, Any] = None,
ns: str = "default", secrets: Secrets = None):
"""
Convenient helper rule to DNS access from all pods
in a namespace, unless `label_selectors, in which case, only matching pods
will be impacted.
"""
pod_selector = {}
if label_selectors:
pod_selector["matchLabels"] = label_selectors

create_network_policy({
"apiVersion": "networking.k8s.io/v1",
"kind": "NetworkPolicy",
"metadata": {
"name": "chaostoolkit-allow-dns"
},
"spec": {
"podSelector": pod_selector,
"policyTypes": [
"Egress"
],
"egress": [
{
"to": [
{
"namespaceSelector": {}
}
],
"ports": [
{
"port": 53,
"protocol": "UDP"
},
{
"port": 53,
"protocol": "TCP"
}
]
}
]
}
}, ns=ns, secrets=secrets)


def remove_allow_dns_access(ns: str = "default", secrets: Secrets = None):
"""
Remove the rule set by the `allow_dns_access` action.
"""
remove_network_policy(
"chaostoolkit-allow-dns", ns=ns, secrets=secrets)
35 changes: 35 additions & 0 deletions tests/test_networking.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
from unittest.mock import MagicMock, patch

from chaosk8s.networking.actions import create_network_policy


@patch('chaosk8s.has_local_config_file', autospec=True)
@patch('chaosk8s.networking.actions.client', autospec=True)
@patch('chaosk8s.client')
def test_create_network_policy_from_spec(cl, client, has_conf):
has_conf.return_value = False

spec = {
"apiVersion": "networking.k8s.io/v1",
"kind": "NetworkPolicy",
"metadata": {
"name": "deny-all-ingress-to-foo"
},
"spec": {
"podSelector": "app=foo",
"policyTypes": [
"Ingress"
],
"ingress": []
}
}

v1 = MagicMock()
client.NetworkingV1Api.return_value = v1

create_network_policy(spec=spec)

assert v1.create_namespaced_network_policy.call_count == 1
v1.create_namespaced_network_policy.assert_called_with(
"default", body=spec)

0 comments on commit eab30ac

Please sign in to comment.