From 9a80ad7980b1842a8fa9ee04c31580108b93cd2e Mon Sep 17 00:00:00 2001 From: mhh Date: Sat, 12 Aug 2023 15:44:14 +0200 Subject: [PATCH 1/3] implement AlephApp.vm_hash property and test --- src/aleph/sdk/vm/app.py | 21 +++++++++++++++++++++ tests/unit/test_vm_app.py | 15 +++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/aleph/sdk/vm/app.py b/src/aleph/sdk/vm/app.py index 88bf3eb8..b6e3fbd6 100644 --- a/src/aleph/sdk/vm/app.py +++ b/src/aleph/sdk/vm/app.py @@ -1,3 +1,5 @@ +import base64 +import socket from dataclasses import dataclass from typing import ( Any, @@ -85,3 +87,22 @@ async def send_handler_result(): def __getattr__(self, name): # Default all calls to the HTTP handler return getattr(self.http_app, name) + + @property + def vm_hash(self): + """Returns the hash of the VM that is running this app.""" + # Get hostname from environment + hostname = socket.gethostname() + + # Add padding if necessary + padding_length = len(hostname) % 8 + if padding_length != 0: + hostname += "=" * (8 - padding_length) + + # Convert the hostname back to its original binary form + item_hash_binary = base64.b32decode(hostname.upper()) + + # Convert the binary form to the original vm_hash + vm_hash = base64.b16encode(item_hash_binary).decode().lower() + + return vm_hash diff --git a/tests/unit/test_vm_app.py b/tests/unit/test_vm_app.py index c9ed5aa9..4578f20a 100644 --- a/tests/unit/test_vm_app.py +++ b/tests/unit/test_vm_app.py @@ -1,4 +1,6 @@ import asyncio +import base64 +from unittest.mock import patch import pytest from fastapi.testclient import TestClient @@ -31,3 +33,16 @@ def test_app_http(): response = client.get("/") assert response.status_code == 200 assert response.json() == {"index": "/"} + + +@patch("socket.gethostname") +def test_get_vm_hash(mock_gethostname): + vm_hash = "deadbeef" * 8 + # Uses the same logic as + # https://github.com/aleph-im/aleph-vm/blob/main/runtimes/aleph-debian-11-python/init1.py#L488 + item_hash_binary: bytes = base64.b16decode(vm_hash.encode().upper()) + hostname = base64.b32encode(item_hash_binary).decode().strip("=").lower() + + mock_gethostname.return_value = hostname + + assert app.vm_hash == vm_hash From 39e0c4df8f6023dac75d00c8690c743097b61cd2 Mon Sep 17 00:00:00 2001 From: mhh Date: Mon, 28 Aug 2023 22:28:00 +0200 Subject: [PATCH 2/3] handle error case in AlephApp.vm_hash --- src/aleph/sdk/vm/app.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/aleph/sdk/vm/app.py b/src/aleph/sdk/vm/app.py index b6e3fbd6..0ca53807 100644 --- a/src/aleph/sdk/vm/app.py +++ b/src/aleph/sdk/vm/app.py @@ -1,4 +1,5 @@ import base64 +import binascii import socket from dataclasses import dataclass from typing import ( @@ -89,8 +90,11 @@ def __getattr__(self, name): return getattr(self.http_app, name) @property - def vm_hash(self): - """Returns the hash of the VM that is running this app.""" + def vm_hash(self) -> Optional[str]: + """ + Returns the hash of the VM that is running this app. If the VM is not + running in Aleph, this will return None. + """ # Get hostname from environment hostname = socket.gethostname() @@ -99,10 +103,14 @@ def vm_hash(self): if padding_length != 0: hostname += "=" * (8 - padding_length) - # Convert the hostname back to its original binary form - item_hash_binary = base64.b32decode(hostname.upper()) + try: + # Convert the hostname back to its original binary form + item_hash_binary = base64.b32decode(hostname.upper()) - # Convert the binary form to the original vm_hash - vm_hash = base64.b16encode(item_hash_binary).decode().lower() + # Convert the binary form to the original vm_hash + vm_hash = base64.b16encode(item_hash_binary).decode().lower() + except binascii.Error: + # If the hostname is not a valid base32 string, just return None + return None return vm_hash From 77934c881322d8c57ea22b2b3916e0663ff9304d Mon Sep 17 00:00:00 2001 From: mhh Date: Tue, 5 Sep 2023 14:56:32 +0200 Subject: [PATCH 3/3] add complementary test and use pytest mocker --- tests/unit/test_vm_app.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/unit/test_vm_app.py b/tests/unit/test_vm_app.py index 4578f20a..80b2d156 100644 --- a/tests/unit/test_vm_app.py +++ b/tests/unit/test_vm_app.py @@ -1,6 +1,5 @@ import asyncio import base64 -from unittest.mock import patch import pytest from fastapi.testclient import TestClient @@ -35,14 +34,19 @@ def test_app_http(): assert response.json() == {"index": "/"} -@patch("socket.gethostname") -def test_get_vm_hash(mock_gethostname): +def test_get_vm_hash(mocker): vm_hash = "deadbeef" * 8 # Uses the same logic as # https://github.com/aleph-im/aleph-vm/blob/main/runtimes/aleph-debian-11-python/init1.py#L488 item_hash_binary: bytes = base64.b16decode(vm_hash.encode().upper()) hostname = base64.b32encode(item_hash_binary).decode().strip("=").lower() - mock_gethostname.return_value = hostname + mocker.patch("socket.gethostname", return_value=hostname) assert app.vm_hash == vm_hash + + +def test_get_vm_hash_no_vm(mocker): + mocker.patch("socket.gethostname", return_value="not-a-vm") + + assert app.vm_hash is None