diff options
Diffstat (limited to 'moonv4/moon_orchestrator/tests/unit_python')
5 files changed, 643 insertions, 0 deletions
diff --git a/moonv4/moon_orchestrator/tests/unit_python/conftest.py b/moonv4/moon_orchestrator/tests/unit_python/conftest.py new file mode 100644 index 00000000..044489e6 --- /dev/null +++ b/moonv4/moon_orchestrator/tests/unit_python/conftest.py @@ -0,0 +1,18 @@ +import pytest +import requests_mock +import mock_pods +from utilities import CONTEXT + + +@pytest.fixture +def context(): + return CONTEXT + + +@pytest.fixture(autouse=True) +def no_requests(monkeypatch): + """ Modify the response from Requests module + """ + with requests_mock.Mocker(real_http=True) as m: + mock_pods.register_pods(m) + yield m
\ No newline at end of file diff --git a/moonv4/moon_orchestrator/tests/unit_python/mock_pods.py b/moonv4/moon_orchestrator/tests/unit_python/mock_pods.py new file mode 100644 index 00000000..c5633152 --- /dev/null +++ b/moonv4/moon_orchestrator/tests/unit_python/mock_pods.py @@ -0,0 +1,404 @@ +from kubernetes import client, config +from utilities import CONF, get_b64_conf, COMPONENTS + +pdp_mock = { + "pdp_id1": { + "name": "...", + "security_pipeline": ["policy_id_1", "policy_id_2"], + "keystone_project_id": "keystone_project_id1", + "description": "...", + }, + "pdp_id12": { + "name": "...", + "security_pipeline": ["policy_id_1", "policy_id_2"], + "keystone_project_id": "keystone_project_id1", + "description": "...", + } +} + +meta_rules_mock = { + "meta_rule_id1": { + "name": "meta_rule1", + "algorithm": "name of the meta rule algorithm", + "subject_categories": ["subject_category_id1", + "subject_category_id2"], + "object_categories": ["object_category_id1"], + "action_categories": ["action_category_id1"] + }, + "meta_rule_id2": { + "name": "name of the meta rules2", + "algorithm": "name of the meta rule algorithm", + "subject_categories": ["subject_category_id1", + "subject_category_id2"], + "object_categories": ["object_category_id1"], + "action_categories": ["action_category_id1"] + } +} + +policies_mock = { + "policy_id_1": { + "name": "test_policy1", + "model_id": "model_id_1", + "genre": "authz", + "description": "test", + }, + "policy_id_2": { + "name": "test_policy2", + "model_id": "model_id_2", + "genre": "authz", + "description": "test", + } +} + +subject_mock = { + "policy_id_1": { + "subject_id": { + "name": "subject_name", + "keystone_id": "keystone_project_id1", + "description": "a description" + } + }, + "policy_id_2": { + "subject_id": { + "name": "subject_name", + "keystone_id": "keystone_project_id1", + "description": "a description" + } + } +} + +subject_assignment_mock = { + "subject_id": { + "policy_id": "ID of the policy", + "subject_id": "ID of the subject", + "category_id": "ID of the category", + "assignments": [], + } +} + +object_mock = { + "policy_id_1": { + "object_id": { + "name": "object_name", + "description": "a description" + } + }, + "policy_id_2": { + "object_id": { + "name": "object_name", + "description": "a description" + } + } +} + +object_assignment_mock = { + "object_id": { + "policy_id": "ID of the policy", + "object_id": "ID of the object", + "category_id": "ID of the category", + "assignments": [], + } +} + +action_mock = { + "policy_id_1": { + "action_id": { + "name": "action_name", + "description": "a description" + } + }, + "policy_id_2": { + "action_id": { + "name": "action_name", + "description": "a description" + } + } +} + +action_assignment_mock = { + "action_id": { + "policy_id": "ID of the policy", + "action_id": "ID of the action", + "category_id": "ID of the category", + "assignments": [], + } +} + +models_mock = { + "model_id_1": { + "name": "test_model", + "description": "test", + "meta_rules": ["meta_rule_id1"] + }, + "model_id_2": { + "name": "test_model", + "description": "test", + "meta_rules": ["meta_rule_id2"] + }, +} + +rules_mock = { + "rules": { + "meta_rule_id": "meta_rule_id1", + "rule_id1": { + "rule": ["subject_data_id1", + "object_data_id1", + "action_data_id1"], + "instructions": ( + {"decision": "grant"}, + # "grant" to immediately exit, + # "continue" to wait for the result of next policy + # "deny" to deny the request + ) + }, + "rule_id2": { + "rule": ["subject_data_id2", + "object_data_id2", + "action_data_id2"], + "instructions": ( + { + "update": { + "operation": "add", + # operations may be "add" or "delete" + "target": "rbac:role:admin" + # add the role admin to the current user + } + }, + {"chain": {"name": "rbac"}} + # chain with the policy named rbac + ) + } + } +} + + +def patch_k8s(monkeypatch): + def _load_kube_config_mockreturn(*args, **kwargs): + return + monkeypatch.setattr(config, 'load_kube_config', + _load_kube_config_mockreturn) + + def list_kube_config_contexts_mockreturn(*args, **kwargs): + return [{"name": "active_context"}], {"name": "active_context"} + monkeypatch.setattr(config, 'list_kube_config_contexts', + list_kube_config_contexts_mockreturn) + + def new_client_from_config_mockreturn(*args, **kwargs): + return {"client": True} + monkeypatch.setattr(config, 'new_client_from_config', + new_client_from_config_mockreturn) + + def list_pod_for_all_namespaces_mockreturn(*args, **kwargs): + class pods: + items = [] + return pods + monkeypatch.setattr(client.CoreV1Api, 'list_pod_for_all_namespaces', + list_pod_for_all_namespaces_mockreturn) + + def create_namespaced_deployment_mockreturn(*args, **kwargs): + + class metadata: + uid = "123456789" + + class pod: + def __init__(self): + self.metadata = metadata() + return pod() + monkeypatch.setattr(client.ExtensionsV1beta1Api, + 'create_namespaced_deployment', + create_namespaced_deployment_mockreturn) + + def create_namespaced_service_mockreturn(*args, **kwargs): + return {} + monkeypatch.setattr(client.CoreV1Api, + 'create_namespaced_service', + create_namespaced_service_mockreturn) + + +def register_pods(m): + """ Modify the response from Requests module + """ + register_consul(m) + register_pdp(m) + # register_meta_rules(m) + register_policies(m) + register_models(m) + # register_policy_subject(m, "policy_id_1") + # register_policy_subject(m, "policy_id_2") + # register_policy_object(m, "policy_id_1") + # register_policy_object(m, "policy_id_2") + # register_policy_action(m, "policy_id_1") + # register_policy_action(m, "policy_id_2") + # register_policy_subject_assignment(m, "policy_id_1", "subject_id") + # register_policy_subject_assignment_list(m1, "policy_id_1") + # register_policy_subject_assignment(m, "policy_id_2", "subject_id") + # register_policy_subject_assignment_list(m1, "policy_id_2") + # register_policy_object_assignment(m, "policy_id_1", "object_id") + # register_policy_object_assignment_list(m1, "policy_id_1") + # register_policy_object_assignment(m, "policy_id_2", "object_id") + # register_policy_object_assignment_list(m1, "policy_id_2") + # register_policy_action_assignment(m, "policy_id_1", "action_id") + # register_policy_action_assignment_list(m1, "policy_id_1") + # register_policy_action_assignment(m, "policy_id_2", "action_id") + # register_policy_action_assignment_list(m1, "policy_id_2") + # register_rules(m, "policy_id1") + + +def register_consul(m): + for component in COMPONENTS: + m.register_uri( + 'GET', 'http://consul:8500/v1/kv/{}'.format(component), + json=[{'Key': component, 'Value': get_b64_conf(component)}] + ) + m.register_uri( + 'GET', 'http://consul:8500/v1/kv/components_port_start', + json=[ + { + "LockIndex": 0, + "Key": "components_port_start", + "Flags": 0, + "Value": "MzEwMDE=", + "CreateIndex": 9, + "ModifyIndex": 9 + } + ], + ) + m.register_uri( + 'PUT', 'http://consul:8500/v1/kv/components_port_start', + json=[], + ) + m.register_uri( + 'GET', 'http://consul:8500/v1/kv/plugins?recurse=true', + json=[ + { + "LockIndex": 0, + "Key": "plugins/authz", + "Flags": 0, + "Value": "eyJjb250YWluZXIiOiAid3Vrb25nc3VuL21vb25fYXV0aHo6djQuMyIsICJwb3J0IjogODA4MX0=", + "CreateIndex": 14, + "ModifyIndex": 656 + } + ], + ) + + +def register_pdp(m): + m.register_uri( + 'GET', 'http://{}:{}/{}'.format(CONF['components']['manager']['hostname'], + CONF['components']['manager']['port'], 'pdp'), + json={'pdps': pdp_mock} + ) + + +def register_meta_rules(m): + m.register_uri( + 'GET', 'http://{}:{}/{}'.format(CONF['components']['manager']['hostname'], + CONF['components']['manager']['port'], 'meta_rules'), + json={'meta_rules': meta_rules_mock} + ) + + +def register_policies(m): + m.register_uri( + 'GET', 'http://{}:{}/{}'.format(CONF['components']['manager']['hostname'], + CONF['components']['manager']['port'], 'policies'), + json={'policies': policies_mock} + ) + + +def register_models(m): + m.register_uri( + 'GET', 'http://{}:{}/{}'.format(CONF['components']['manager']['hostname'], + CONF['components']['manager']['port'], 'models'), + json={'models': models_mock} + ) + + +def register_policy_subject(m, policy_id): + m.register_uri( + 'GET', 'http://{}:{}/{}/{}/subjects'.format(CONF['components']['manager']['hostname'], + CONF['components']['manager']['port'], 'policies', policy_id), + json={'subjects': subject_mock[policy_id]} + ) + + +def register_policy_object(m, policy_id): + m.register_uri( + 'GET', 'http://{}:{}/{}/{}/objects'.format(CONF['components']['manager']['hostname'], + CONF['components']['manager']['port'], 'policies', policy_id), + json={'objects': object_mock[policy_id]} + ) + + +def register_policy_action(m, policy_id): + m.register_uri( + 'GET', 'http://{}:{}/{}/{}/actions'.format(CONF['components']['manager']['hostname'], + CONF['components']['manager']['port'], 'policies', policy_id), + json={'actions': action_mock[policy_id]} + ) + + +def register_policy_subject_assignment(m, policy_id, subj_id): + m.register_uri( + 'GET', 'http://{}:{}/{}/{}/subject_assignments/{}'.format(CONF['components']['manager']['hostname'], + CONF['components']['manager']['port'], 'policies', + policy_id, + subj_id), + json={'subject_assignments': subject_assignment_mock} + ) + + +def register_policy_subject_assignment_list(m, policy_id): + m.register_uri( + 'GET', 'http://{}:{}/{}/{}/subject_assignments'.format(CONF['components']['manager']['hostname'], + CONF['components']['manager']['port'], 'policies', + policy_id), + json={'subject_assignments': subject_assignment_mock} + ) + + +def register_policy_object_assignment(m, policy_id, obj_id): + m.register_uri( + 'GET', 'http://{}:{}/{}/{}/object_assignments/{}'.format(CONF['components']['manager']['hostname'], + CONF['components']['manager']['port'], 'policies', + policy_id, + obj_id), + json={'object_assignments': object_assignment_mock} + ) + + +def register_policy_object_assignment_list(m, policy_id): + m.register_uri( + 'GET', 'http://{}:{}/{}/{}/object_assignments'.format(CONF['components']['manager']['hostname'], + CONF['components']['manager']['port'], 'policies', + policy_id), + json={'object_assignments': object_assignment_mock} + ) + + +def register_policy_action_assignment(m, policy_id, action_id): + m.register_uri( + 'GET', 'http://{}:{}/{}/{}/action_assignments/{}'.format(CONF['components']['manager']['hostname'], + CONF['components']['manager']['port'], 'policies', + policy_id, + action_id), + json={'action_assignments': action_assignment_mock} + ) + + +def register_policy_action_assignment_list(m, policy_id): + m.register_uri( + 'GET', 'http://{}:{}/{}/{}/action_assignments'.format(CONF['components']['manager']['hostname'], + CONF['components']['manager']['port'], 'policies', + policy_id), + json={'action_assignments': action_assignment_mock} + ) + + +def register_rules(m, policy_id): + m.register_uri( + 'GET', 'http://{}:{}/{}/{}/{}'.format(CONF['components']['manager']['hostname'], + CONF['components']['manager']['port'], 'policies', + policy_id, 'rules'), + json={'rules': rules_mock} + )
\ No newline at end of file diff --git a/moonv4/moon_orchestrator/tests/unit_python/requirements.txt b/moonv4/moon_orchestrator/tests/unit_python/requirements.txt new file mode 100644 index 00000000..8bd8449f --- /dev/null +++ b/moonv4/moon_orchestrator/tests/unit_python/requirements.txt @@ -0,0 +1,5 @@ +flask +flask_cors +flask_restful +moon_db +moon_utilities
\ No newline at end of file diff --git a/moonv4/moon_orchestrator/tests/unit_python/test_pods.py b/moonv4/moon_orchestrator/tests/unit_python/test_pods.py new file mode 100644 index 00000000..42c8404b --- /dev/null +++ b/moonv4/moon_orchestrator/tests/unit_python/test_pods.py @@ -0,0 +1,43 @@ +import json +from mock_pods import patch_k8s +from utilities import get_json + + +def test_get_pods(context, monkeypatch): + patch_k8s(monkeypatch) + + import moon_orchestrator.server + server = moon_orchestrator.server.main() + _client = server.app.test_client() + req = _client.get("/pods") + assert req.status_code == 200 + assert req.data + data = get_json(req.data) + assert isinstance(data, dict) + assert "pods" in data + + +def test_add_pods(context, monkeypatch): + patch_k8s(monkeypatch) + + import moon_orchestrator.server + server = moon_orchestrator.server.main() + _client = server.app.test_client() + data = { + "keystone_project_id": context.get('project_id'), + "pdp_id": context.get('pdp_id'), + "security_pipeline": context.get('security_pipeline'), + } + req = _client.post("/pods", data=json.dumps(data), + headers={'Content-Type': 'application/json'}) + assert req.status_code == 200 + assert req.data + data = get_json(req.data) + assert isinstance(data, dict) + assert "pods" in data + assert data["pods"] + + +def test_delete_pods(context, monkeypatch): + # TODO + pass diff --git a/moonv4/moon_orchestrator/tests/unit_python/utilities.py b/moonv4/moon_orchestrator/tests/unit_python/utilities.py new file mode 100644 index 00000000..aec03d9d --- /dev/null +++ b/moonv4/moon_orchestrator/tests/unit_python/utilities.py @@ -0,0 +1,173 @@ +import base64 +import json +import pytest +from uuid import uuid4 + + +CONF = { + "openstack": { + "keystone": { + "url": "http://keystone:5000/v3", + "user": "admin", + "check_token": False, + "password": "p4ssw0rd", + "domain": "default", + "certificate": False, + "project": "admin" + } + }, + "components": { + "wrapper": { + "bind": "0.0.0.0", + "port": 8080, + "container": "wukongsun/moon_wrapper:v4.3", + "timeout": 5, + "hostname": "wrapper" + }, + "manager": { + "bind": "0.0.0.0", + "port": 8082, + "container": "wukongsun/moon_manager:v4.3", + "hostname": "manager" + }, + "port_start": 31001, + "orchestrator": { + "bind": "0.0.0.0", + "port": 8083, + "container": "wukongsun/moon_orchestrator:v4.3", + "hostname": "interface" + }, + "interface": { + "bind": "0.0.0.0", + "port": 8080, + "container": "wukongsun/moon_interface:v4.3", + "hostname": "interface" + } + }, + "plugins": { + "session": { + "port": 8082, + "container": "asteroide/session:latest" + }, + "authz": { + "port": 8081, + "container": "wukongsun/moon_authz:v4.3" + } + }, + "logging": { + "handlers": { + "file": { + "filename": "/tmp/moon.log", + "class": "logging.handlers.RotatingFileHandler", + "level": "DEBUG", + "formatter": "custom", + "backupCount": 3, + "maxBytes": 1048576 + }, + "console": { + "class": "logging.StreamHandler", + "formatter": "brief", + "level": "INFO", + "stream": "ext://sys.stdout" + } + }, + "formatters": { + "brief": { + "format": "%(levelname)s %(name)s %(message)-30s" + }, + "custom": { + "format": "%(asctime)-15s %(levelname)s %(name)s %(message)s" + } + }, + "root": { + "handlers": [ + "console" + ], + "level": "ERROR" + }, + "version": 1, + "loggers": { + "moon": { + "handlers": [ + "console", + "file" + ], + "propagate": False, + "level": "DEBUG" + } + } + }, + "slave": { + "name": None, + "master": { + "url": None, + "login": None, + "password": None + } + }, + "docker": { + "url": "tcp://172.88.88.1:2376", + "network": "moon" + }, + "database": { + "url": "sqlite:///database.db", + # "url": "mysql+pymysql://moon:p4sswOrd1@db/moon", + "driver": "sql" + }, + "messenger": { + "url": "rabbit://moon:p4sswOrd1@messenger:5672/moon" + } +} + + +CONTEXT = { + "project_id": "a64beb1cc224474fb4badd43173e7101", + "subject_name": "testuser", + "object_name": "vm1", + "action_name": "boot", + "request_id": uuid4().hex, + "interface_name": "interface", + "manager_url": "http://{}:{}".format( + CONF["components"]["manager"]["hostname"], + CONF["components"]["manager"]["port"] + ), + "cookie": uuid4().hex, + "pdp_id": "b3d3e18abf3340e8b635fd49e6634ccd", + "security_pipeline": ["f8f49a779ceb47b3ac810f01ef71b4e0"] + } + + +COMPONENTS = ( + "logging", + "openstack/keystone", + "database", + "slave", + "components/manager", + "components/orchestrator", + "components/interface", + "components/wrapper", +) + + +def get_b64_conf(component=None): + if component == "components": + return base64.b64encode( + json.dumps(CONF["components"]).encode('utf-8')+b"\n").decode('utf-8') + elif component in CONF: + return base64.b64encode( + json.dumps( + CONF[component]).encode('utf-8')+b"\n").decode('utf-8') + elif not component: + return base64.b64encode( + json.dumps(CONF).encode('utf-8')+b"\n").decode('utf-8') + elif "/" in component: + key1, _, key2 = component.partition("/") + return base64.b64encode( + json.dumps( + CONF[key1][key2]).encode('utf-8')+b"\n").decode('utf-8') + + +def get_json(data): + return json.loads(data.decode("utf-8")) + + |