diff --git a/pkgs/clan-cli/clan_cli/webui/routers/flake.py b/pkgs/clan-cli/clan_cli/webui/routers/flake.py index 09541803f..c5f15a970 100644 --- a/pkgs/clan-cli/clan_cli/webui/routers/flake.py +++ b/pkgs/clan-cli/clan_cli/webui/routers/flake.py @@ -1,4 +1,5 @@ import json +from json.decoder import JSONDecodeError from pathlib import Path from fastapi import APIRouter, HTTPException @@ -11,14 +12,29 @@ from .utils import run_cmd router = APIRouter() -@router.get("/api/flake/attrs") -async def inspect_flake_attrs(url: str) -> FlakeAttrResponse: +async def get_attrs(url: str) -> list[str]: cmd = nix_flake_show(url) stdout = await run_cmd(cmd) - data = json.loads(stdout) - nixos_configs = data["nixosConfigurations"] + + data: dict[str, dict] = {} + try: + data = json.loads(stdout) + except JSONDecodeError: + raise HTTPException(status_code=422, detail="Could not load flake.") + + nixos_configs = data.get("nixosConfigurations", {}) flake_attrs = list(nixos_configs.keys()) - return FlakeAttrResponse(flake_attrs=flake_attrs) + + if not flake_attrs: + raise HTTPException( + status_code=422, detail="No entry or no attribute: nixosConfigurations" + ) + return flake_attrs + + +@router.get("/api/flake/attrs") +async def inspect_flake_attrs(url: str) -> FlakeAttrResponse: + return FlakeAttrResponse(flake_attrs=await get_attrs(url)) @router.get("/api/flake") diff --git a/pkgs/clan-cli/clan_cli/webui/routers/vms.py b/pkgs/clan-cli/clan_cli/webui/routers/vms.py index 3011c32c5..2cd37b2bd 100644 --- a/pkgs/clan-cli/clan_cli/webui/routers/vms.py +++ b/pkgs/clan-cli/clan_cli/webui/routers/vms.py @@ -3,9 +3,12 @@ import logging from typing import Annotated, Iterator from uuid import UUID -from fastapi import APIRouter, BackgroundTasks, Body +from fastapi import APIRouter, BackgroundTasks, Body, status +from fastapi.exceptions import HTTPException from fastapi.responses import StreamingResponse +from clan_cli.webui.routers.flake import get_attrs + from ...nix import nix_build, nix_eval from ..schemas import VmConfig, VmCreateResponse, VmInspectResponse, VmStatusResponse from ..task_manager import BaseTask, get_task, register_task @@ -107,5 +110,11 @@ async def get_vm_logs(uuid: UUID) -> StreamingResponse: async def create_vm( vm: Annotated[VmConfig, Body()], background_tasks: BackgroundTasks ) -> VmCreateResponse: + flake_attrs = await get_attrs(vm.flake_url) + if vm.flake_attr not in flake_attrs: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=f"Provided attribute '{vm.flake_attr}' does not exist.", + ) uuid = register_task(BuildVmTask, vm) return VmCreateResponse(uuid=str(uuid)) diff --git a/pkgs/clan-cli/tests/test_flake_api.py b/pkgs/clan-cli/tests/test_flake_api.py index 767af4f7b..2fa65d281 100644 --- a/pkgs/clan-cli/tests/test_flake_api.py +++ b/pkgs/clan-cli/tests/test_flake_api.py @@ -5,7 +5,7 @@ from api import TestClient @pytest.mark.impure -def test_inspect(api: TestClient, test_flake_with_core: Path) -> None: +def test_inspect_ok(api: TestClient, test_flake_with_core: Path) -> None: params = {"url": str(test_flake_with_core)} response = api.get( "/api/flake/attrs", @@ -15,3 +15,16 @@ def test_inspect(api: TestClient, test_flake_with_core: Path) -> None: data = response.json() print("Data: ", data) assert data.get("flake_attrs") == ["vm1"] + + +@pytest.mark.impure +def test_inspect_err(api: TestClient) -> None: + params = {"url": "flake-parts"} + response = api.get( + "/api/flake/attrs", + params=params, + ) + assert response.status_code != 200, "Succeed to inspect vm but expected to fail" + data = response.json() + print("Data: ", data) + assert data.get("detail")