Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/test-on-droplets-matrix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,13 @@ jobs:
-d '{"persistent_vms": [], "instances": ["${{ matrix.check_vm.item_hash }}"]}' \
"http://${DROPLET_IPV4}:4020/control/allocations"

- name: Fetch system usage endpoint
run: |
export DROPLET_IPV4="$(doctl compute droplet get aleph-vm-ci-${{ matrix.os_config.alias }}-${{ matrix.check_vm.alias }} --output json | ./.github/scripts/extract_droplet_ipv4.py)"
curl -X GET -H "Content-Type: application/json" \
"http://${DROPLET_IPV4}:4020/about/usage/system"


- name: Export aleph logs
if: always()
run: |
Expand Down
5 changes: 3 additions & 2 deletions src/aleph/vm/orchestrator/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ def get_machine_properties() -> MachineProperties:
cpu_info = cpuinfo.get_cpu_info() # Slow
return MachineProperties(
cpu=CpuProperties(
architecture=cpu_info["raw_arch_string"],
vendor=cpu_info["vendor_id"],
architecture=cpu_info.get("raw_arch_string", cpu_info.get("arch_string_raw")),
vendor=cpu_info.get("vendor_id", cpu_info.get("vendor_id_raw")),
),
)

Expand Down Expand Up @@ -118,6 +118,7 @@ async def about_system_usage(_: web.Request):
),
properties=get_machine_properties(),
)

return web.json_response(text=usage.json(exclude_none=True))


Expand Down
112 changes: 57 additions & 55 deletions src/aleph/vm/orchestrator/supervisor.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,62 +74,63 @@ async def http_not_found(request: web.Request):
return web.HTTPNotFound()


app = web.Application(middlewares=[server_version_middleware])
cors = setup(
app,
defaults={
"*": ResourceOptions(
allow_credentials=True,
expose_headers="*",
allow_headers="*",
)
},
)
def setup_webapp():
app = web.Application(middlewares=[server_version_middleware])
cors = setup(
app,
defaults={
"*": ResourceOptions(
allow_credentials=True,
expose_headers="*",
allow_headers="*",
)
},
)

# Routes that need CORS enabled
cors_routes = [
# /about APIs return information about the VM Orchestrator
web.get("/about/login", about_login),
web.get("/about/executions/list", list_executions),
web.get("/about/executions/details", about_executions),
web.get("/about/executions/records", about_execution_records),
web.get("/about/usage/system", about_system_usage),
web.get("/about/config", about_config),
# /control APIs are used to control the VMs and access their logs
web.post("/control/allocation/notify", notify_allocation),
web.get("/control/machine/{ref}/logs", stream_logs),
web.post("/control/machine/{ref}/expire", operate_expire),
web.post("/control/machine/{ref}/stop", operate_stop),
web.post("/control/machine/{ref}/erase", operate_erase),
web.post("/control/machine/{ref}/reboot", operate_reboot),
# /status APIs are used to check that the VM Orchestrator is running properly
web.get("/status/check/fastapi", status_check_fastapi),
web.get("/status/check/fastapi/legacy", status_check_fastapi_legacy),
web.get("/status/check/host", status_check_host),
web.get("/status/check/version", status_check_version),
web.get("/status/check/ipv6", status_check_ipv6),
web.get("/status/config", status_public_config),
]
routes = app.add_routes(cors_routes)
for route in routes:
cors.add(route)


# Routes that don't need CORS enabled
other_routes = [
# /control APIs are used to control the VMs and access their logs
web.post("/control/allocations", update_allocations),
# Raise an HTTP Error 404 if attempting to access an unknown URL within these paths.
web.get("/about/{suffix:.*}", http_not_found),
web.get("/control/{suffix:.*}", http_not_found),
web.get("/status/{suffix:.*}", http_not_found),
# /static is used to serve static files
web.static("/static", Path(__file__).parent / "views/static"),
# /vm is used to launch VMs on-demand
web.route("*", "/vm/{ref}{suffix:.*}", run_code_from_path),
web.route("*", "/{suffix:.*}", run_code_from_hostname),
]
app.add_routes(other_routes)
# Routes that need CORS enabled
cors_routes = [
# /about APIs return information about the VM Orchestrator
web.get("/about/login", about_login),
web.get("/about/executions/list", list_executions),
web.get("/about/executions/details", about_executions),
web.get("/about/executions/records", about_execution_records),
web.get("/about/usage/system", about_system_usage),
web.get("/about/config", about_config),
# /control APIs are used to control the VMs and access their logs
web.post("/control/allocation/notify", notify_allocation),
web.get("/control/machine/{ref}/logs", stream_logs),
web.post("/control/machine/{ref}/expire", operate_expire),
web.post("/control/machine/{ref}/stop", operate_stop),
web.post("/control/machine/{ref}/erase", operate_erase),
web.post("/control/machine/{ref}/reboot", operate_reboot),
# /status APIs are used to check that the VM Orchestrator is running properly
web.get("/status/check/fastapi", status_check_fastapi),
web.get("/status/check/fastapi/legacy", status_check_fastapi_legacy),
web.get("/status/check/host", status_check_host),
web.get("/status/check/version", status_check_version),
web.get("/status/check/ipv6", status_check_ipv6),
web.get("/status/config", status_public_config),
]
routes = app.add_routes(cors_routes)
for route in routes:
cors.add(route)

# Routes that don't need CORS enabled
other_routes = [
# /control APIs are used to control the VMs and access their logs
web.post("/control/allocations", update_allocations),
# Raise an HTTP Error 404 if attempting to access an unknown URL within these paths.
web.get("/about/{suffix:.*}", http_not_found),
web.get("/control/{suffix:.*}", http_not_found),
web.get("/status/{suffix:.*}", http_not_found),
# /static is used to serve static files
web.static("/static", Path(__file__).parent / "views/static"),
# /vm is used to launch VMs on-demand
web.route("*", "/vm/{ref}{suffix:.*}", run_code_from_path),
web.route("*", "/{suffix:.*}", run_code_from_hostname),
]
app.add_routes(other_routes)
return app


async def stop_all_vms(app: web.Application):
Expand All @@ -153,6 +154,7 @@ def run():

# Require a random token to access /about APIs
secret_token = token_urlsafe(nbytes=32)
app = setup_webapp()
# Store app singletons. Note that app["pubsub"] will also be created.
app["secret_token"] = secret_token
app["vm_pool"] = pool
Expand Down
16 changes: 15 additions & 1 deletion tests/supervisor/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
from aiohttp import web

from aleph.vm.conf import settings
from aleph.vm.orchestrator.supervisor import app
from aleph.vm.orchestrator.supervisor import setup_webapp


@pytest.mark.asyncio
async def test_allocation_fails_on_invalid_item_hash(aiohttp_client):
"""Test that the allocation endpoint fails when an invalid item_hash is provided."""
app = setup_webapp()
client = await aiohttp_client(app)
settings.ALLOCATION_TOKEN_HASH = "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08" # = "test"
response: web.Response = await client.post(
Expand All @@ -24,3 +25,16 @@ async def test_allocation_fails_on_invalid_item_hash(aiohttp_client):
"type": "value_error.unknownhash",
},
]


@pytest.mark.asyncio
async def test_system_usage(aiohttp_client):
"""Test that the usage system endpoints responds. No auth needed"""
app = setup_webapp()
client = await aiohttp_client(app)
response: web.Response = await client.get("/about/usage/system")
assert response.status == 200
# check if it is valid json
resp = await response.json()
assert "cpu" in resp
assert resp["cpu"]["count"] > 0