api/schema: return list of missing modules
All checks were successful
checks-impure / test (pull_request) Successful in 1m58s
checks / test (pull_request) Successful in 2m48s

This commit is contained in:
DavHau 2023-11-10 15:06:59 +07:00
parent 53ce82984d
commit 39607a0925
4 changed files with 80 additions and 8 deletions

View File

@ -6,6 +6,8 @@ from pathlib import Path
from tempfile import NamedTemporaryFile
from typing import Optional
from fastapi import HTTPException
from clan_cli.dirs import (
nixpkgs_source,
specific_flake_dir,
@ -25,19 +27,59 @@ def machine_schema(
# use nix eval to lib.evalModules .#nixosConfigurations.<machine_name>.options.clan
with NamedTemporaryFile(mode="w", dir=flake) as clan_machine_settings_file:
env = os.environ.copy()
inject_config_flags = []
if clan_imports is not None:
config["clanImports"] = clan_imports
# dump config to file
json.dump(config, clan_machine_settings_file, indent=2)
clan_machine_settings_file.seek(0)
env["CLAN_MACHINE_SETTINGS_FILE"] = clan_machine_settings_file.name
inject_config_flags = [
"--impure", # needed to access CLAN_MACHINE_SETTINGS_FILE
]
# ensure that the requested clanImports exist
proc = subprocess.run(
nix_eval(
flags=inject_config_flags
+ [
flags=[
"--impure",
"--show-trace",
"--expr",
f"""
let
b = builtins;
system = b.currentSystem;
flake = b.getFlake (toString {flake});
clan-core = flake.inputs.clan-core;
config = b.fromJSON (b.readFile (b.getEnv "CLAN_MACHINE_SETTINGS_FILE"));
modules_not_found =
b.filter
(modName: ! clan-core.clanModules ? ${{modName}})
config.clanImports or [];
in
modules_not_found
""",
]
),
capture_output=True,
text=True,
cwd=flake,
env=env,
)
if proc.returncode != 0:
print(proc.stderr, file=sys.stderr)
raise ClanError(
f"Failed to check clanImports for existence:\n{proc.stderr}"
)
modules_not_found = json.loads(proc.stdout)
if len(modules_not_found) > 0:
raise HTTPException(
status_code=400,
detail={
"msg": "Some requested clan modules could not be found",
"modules_not_found": modules_not_found,
},
)
# get the schema
proc = subprocess.run(
nix_eval(
flags=[
"--impure",
"--show-trace",
"--expr",

View File

@ -0,0 +1,10 @@
import logging
from pydantic import BaseModel
log = logging.getLogger(__name__)
class MissingClanImports(BaseModel):
missing_clan_imports: list[str] = []
msg: str = "Some requested clan modules could not be found"

View File

@ -4,6 +4,7 @@ from typing import Annotated
from fastapi import APIRouter, Body
from clan_cli.webui.api_errors import MissingClanImports
from clan_cli.webui.api_inputs import MachineConfig
from ...config.machine import (
@ -68,7 +69,11 @@ async def set_machine_config(
set_config_for_machine(flake_name, name, conf)
@router.put("/api/{flake_name}/schema", tags=[Tags.machine])
@router.put(
"/api/{flake_name}/schema",
tags=[Tags.machine],
responses={400: {"model": MissingClanImports}},
)
async def get_machine_schema(
flake_name: FlakeName, config: Annotated[dict, Body()]
) -> SchemaResponse:

View File

@ -30,13 +30,28 @@ def test_schema_errors(api: TestClient, test_flake_with_core: FlakeForTest) -> N
json={"imports": ["some-inavlid-import"]},
)
assert response.status_code == 422
# expect error to contain "error: string 'some-inavlid-import' doesn't represent an absolute path"
assert (
"error: string 'some-inavlid-import' doesn't represent an absolute path"
in response.json()["detail"][0]["msg"]
)
@pytest.mark.with_core
def test_schema_invalid_clan_imports(
api: TestClient, test_flake_with_core: FlakeForTest
) -> None:
response = api.put(
f"/api/{test_flake_with_core.name}/schema",
json={"clanImports": ["non-existing-clan-module"]},
)
assert response.status_code == 400
assert (
"Some requested clan modules could not be found"
in response.json()["detail"]["msg"]
)
assert "non-existing-clan-module" in response.json()["detail"]["modules_not_found"]
@pytest.mark.with_core
def test_configure_machine(api: TestClient, test_flake_with_core: FlakeForTest) -> None:
# ensure error 404 if machine does not exist when accessing the config