api/machines: split off config validation into separate endpoint
- This speeds up PUT /machines{name}/config as it doesn't do the expensive check anymore
- instead use PUT /machines/{name}/verify which allows a dry-run evaluation of a config which is passed without writing it to disk
This commit is contained in:
@@ -64,16 +64,13 @@ def config_for_machine(machine_name: str) -> dict:
|
|||||||
return json.load(f)
|
return json.load(f)
|
||||||
|
|
||||||
|
|
||||||
def set_config_for_machine(machine_name: str, config: dict) -> Optional[str]:
|
def set_config_for_machine(machine_name: str, config: dict) -> None:
|
||||||
# write the config to a json file located at {flake}/machines/{machine_name}/settings.json
|
# write the config to a json file located at {flake}/machines/{machine_name}/settings.json
|
||||||
if not machine_folder(machine_name).exists():
|
if not machine_folder(machine_name).exists():
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=404,
|
status_code=404,
|
||||||
detail=f"Machine {machine_name} not found. Create the machine first`",
|
detail=f"Machine {machine_name} not found. Create the machine first`",
|
||||||
)
|
)
|
||||||
error = verify_machine_config(machine_name, config)
|
|
||||||
if error is not None:
|
|
||||||
return error
|
|
||||||
settings_path = machine_settings_file(machine_name)
|
settings_path = machine_settings_file(machine_name)
|
||||||
settings_path.parent.mkdir(parents=True, exist_ok=True)
|
settings_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
with open(settings_path, "w") as f:
|
with open(settings_path, "w") as f:
|
||||||
@@ -82,7 +79,6 @@ def set_config_for_machine(machine_name: str, config: dict) -> Optional[str]:
|
|||||||
|
|
||||||
if repo_dir is not None:
|
if repo_dir is not None:
|
||||||
commit_file(settings_path, repo_dir)
|
commit_file(settings_path, repo_dir)
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def schema_for_machine(
|
def schema_for_machine(
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import logging
|
import logging
|
||||||
from typing import Annotated
|
from typing import Annotated
|
||||||
|
|
||||||
from fastapi import APIRouter, Body, HTTPException
|
from fastapi import APIRouter, Body
|
||||||
|
|
||||||
from ...config.machine import (
|
from ...config.machine import (
|
||||||
config_for_machine,
|
config_for_machine,
|
||||||
@@ -57,9 +57,7 @@ async def get_machine_config(name: str) -> ConfigResponse:
|
|||||||
async def set_machine_config(
|
async def set_machine_config(
|
||||||
name: str, config: Annotated[dict, Body()]
|
name: str, config: Annotated[dict, Body()]
|
||||||
) -> ConfigResponse:
|
) -> ConfigResponse:
|
||||||
error = set_config_for_machine(name, config)
|
set_config_for_machine(name, config)
|
||||||
if error is not None:
|
|
||||||
raise HTTPException(status_code=400, detail=error)
|
|
||||||
return ConfigResponse(config=config)
|
return ConfigResponse(config=config)
|
||||||
|
|
||||||
|
|
||||||
@@ -78,7 +76,17 @@ async def set_machine_schema(
|
|||||||
|
|
||||||
|
|
||||||
@router.get("/api/machines/{name}/verify")
|
@router.get("/api/machines/{name}/verify")
|
||||||
async def put_verify_machine_config(name: str) -> VerifyMachineResponse:
|
async def get_verify_machine_config(name: str) -> VerifyMachineResponse:
|
||||||
error = verify_machine_config(name)
|
error = verify_machine_config(name)
|
||||||
success = error is None
|
success = error is None
|
||||||
return VerifyMachineResponse(success=success, error=error)
|
return VerifyMachineResponse(success=success, error=error)
|
||||||
|
|
||||||
|
|
||||||
|
@router.put("/api/machines/{name}/verify")
|
||||||
|
async def put_verify_machine_config(
|
||||||
|
name: str,
|
||||||
|
config: Annotated[dict, Body()],
|
||||||
|
) -> VerifyMachineResponse:
|
||||||
|
error = verify_machine_config(name, config)
|
||||||
|
success = error is None
|
||||||
|
return VerifyMachineResponse(success=success, error=error)
|
||||||
|
|||||||
@@ -45,29 +45,39 @@ def test_configure_machine(api: TestClient, test_flake: Path) -> None:
|
|||||||
json_response = response.json()
|
json_response = response.json()
|
||||||
assert "schema" in json_response and "properties" in json_response["schema"]
|
assert "schema" in json_response and "properties" in json_response["schema"]
|
||||||
|
|
||||||
# set come invalid config (fileSystems missing)
|
# an invalid config missing the fileSystems
|
||||||
config = dict(
|
invalid_config = dict(
|
||||||
clan=dict(
|
clan=dict(
|
||||||
jitsi=dict(
|
jitsi=dict(
|
||||||
enable=True,
|
enable=True,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# verify an invalid config (fileSystems missing) fails
|
||||||
response = api.put(
|
response = api.put(
|
||||||
"/api/machines/machine1/config",
|
"/api/machines/machine1/verify",
|
||||||
json=config,
|
json=invalid_config,
|
||||||
)
|
)
|
||||||
assert response.status_code == 400
|
assert response.status_code == 200
|
||||||
assert (
|
assert (
|
||||||
"The ‘fileSystems’ option does not specify your root"
|
"The ‘fileSystems’ option does not specify your root"
|
||||||
in response.json()["detail"]
|
in response.json()["error"]
|
||||||
)
|
)
|
||||||
|
|
||||||
# ensure config is still empty after the invalid attempt
|
# set come invalid config (fileSystems missing)
|
||||||
|
response = api.put(
|
||||||
|
"/api/machines/machine1/config",
|
||||||
|
json=invalid_config,
|
||||||
|
)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
# ensure the config has actually been updated
|
||||||
response = api.get("/api/machines/machine1/config")
|
response = api.get("/api/machines/machine1/config")
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert response.json() == {"config": {}}
|
assert response.json() == {"config": invalid_config}
|
||||||
|
|
||||||
|
# the part of the config that makes the evaluation pass
|
||||||
fs_config = dict(
|
fs_config = dict(
|
||||||
fileSystems={
|
fileSystems={
|
||||||
"/": dict(
|
"/": dict(
|
||||||
|
|||||||
Reference in New Issue
Block a user