From 90bae310c7d6b255572d7fe5df65d15da8d0b7c0 Mon Sep 17 00:00:00 2001 From: Olivier Le Thanh Duong Date: Tue, 1 Oct 2024 15:02:26 +0200 Subject: [PATCH] Endpoint /confidential/initialize return json error now and proper https status return json errors for missing field --- src/aleph/vm/orchestrator/views/operator.py | 22 +++++++-- tests/supervisor/views/test_operator.py | 52 +++++++++++++++++++-- 2 files changed, 66 insertions(+), 8 deletions(-) diff --git a/src/aleph/vm/orchestrator/views/operator.py b/src/aleph/vm/orchestrator/views/operator.py index 48fe7d03..af0e98f4 100644 --- a/src/aleph/vm/orchestrator/views/operator.py +++ b/src/aleph/vm/orchestrator/views/operator.py @@ -1,6 +1,7 @@ import json import logging from datetime import timedelta +from http import HTTPStatus import aiohttp.web_exceptions import pydantic @@ -187,10 +188,15 @@ async def operate_confidential_initialize(request: web.Request, authenticated_se return web.Response(status=403, body="Unauthorized sender") if execution.is_running: - return web.Response(status=403, body=f"VM with ref {vm_hash} already running") - + return web.json_response( + {"code": "vm_running", "description": "Operation not allowed, instance already running"}, + status=HTTPStatus.BAD_REQUEST, + ) if not execution.is_confidential: - return web.Response(status=403, body=f"Operation not allowed for VM {vm_hash} because it isn't confidential") + return web.json_response( + {"code": "not_confidential", "description": "Instance is not a confidential instance"}, + status=HTTPStatus.BAD_REQUEST, + ) post = await request.post() @@ -199,14 +205,20 @@ async def operate_confidential_initialize(request: web.Request, authenticated_se session_file_content = post.get("session") if not session_file_content: - return web.Response(status=403, body=f"Session file required for VM with ref {vm_hash}") + return web.json_response( + {"code": "field_missing", "description": "Session field is missing"}, + status=HTTPStatus.BAD_REQUEST, + ) session_file_path = vm_session_path / "vm_session.b64" session_file_path.write_bytes(session_file_content.file.read()) godh_file_content = post.get("godh") if not godh_file_content: - return web.Response(status=403, body=f"GODH file required for VM with ref {vm_hash}") + return web.json_response( + {"code": "field_missing", "description": "godh field is missing. Please provide a GODH file"}, + status=HTTPStatus.BAD_REQUEST, + ) godh_file_path = vm_session_path / "vm_godh.b64" godh_file_path.write_bytes(godh_file_content.file.read()) diff --git a/tests/supervisor/views/test_operator.py b/tests/supervisor/views/test_operator.py index 911eaeb4..b8e370de 100644 --- a/tests/supervisor/views/test_operator.py +++ b/tests/supervisor/views/test_operator.py @@ -89,13 +89,59 @@ async def test_operator_confidential_initialize_already_running(aiohttp_client, ) app = setup_webapp() app["vm_pool"] = fake_vm_pool - client = await aiohttp_client(app) + client: TestClient = await aiohttp_client(app) response = await client.post( f"/control/machine/{vm_hash}/confidential/initialize", json={"persistent_vms": []}, ) - assert response.status == 403 - assert await response.text() == f"VM with ref {vm_hash} already running" + assert response.status == 400 + assert response.content_type == "application/json" + assert await response.json() == { + "code": "vm_running", + "description": "Operation not allowed, instance already running", + } + + +@pytest.mark.asyncio +async def test_operator_confidential_initialize_not_confidential(aiohttp_client, mocker): + """Test that the confidential initialize endpoint rejects if the VM is not confidential""" + + settings.ENABLE_QEMU_SUPPORT = True + settings.ENABLE_CONFIDENTIAL_COMPUTING = True + settings.setup() + + vm_hash = ItemHash(settings.FAKE_INSTANCE_ID) + instance_message = await get_message(ref=vm_hash) + + fake_vm_pool = mocker.Mock( + executions={ + vm_hash: mocker.Mock( + vm_hash=vm_hash, + message=instance_message.content, + is_confidential=False, + is_running=False, + ), + }, + ) + + # Disable auth + mocker.patch( + "aleph.vm.orchestrator.views.authentication.authenticate_jwk", + return_value=instance_message.sender, + ) + app = setup_webapp() + app["vm_pool"] = fake_vm_pool + client: TestClient = await aiohttp_client(app) + response = await client.post( + f"/control/machine/{vm_hash}/confidential/initialize", + json={"persistent_vms": []}, + ) + assert response.status == 400 + assert response.content_type == "application/json" + assert await response.json() == { + "code": "not_confidential", + "description": "Instance is not a confidential instance", + } @pytest.mark.asyncio