From 1652b5c27bc08f54de86a0cc1392b611faa967ac Mon Sep 17 00:00:00 2001 From: DavHau Date: Mon, 13 Nov 2023 20:25:52 +0700 Subject: [PATCH] api/machines: init put_machine replacing create_machine and set_machine_config This allows creating and configuring a machine in one single step. --- flake.lock | 2 +- pkgs/clan-cli/clan_cli/config/machine.py | 20 +++++++--- pkgs/clan-cli/clan_cli/machines/create.py | 37 +------------------ pkgs/clan-cli/clan_cli/webui/api_inputs.py | 13 ------- .../clan_cli/webui/routers/machines.py | 20 ++++------ pkgs/clan-cli/shell.nix | 2 +- pkgs/clan-cli/tests/test_machines_api.py | 22 ++++------- .../components/createMachineForm/index.tsx | 7 +--- 8 files changed, 35 insertions(+), 88 deletions(-) diff --git a/flake.lock b/flake.lock index a89039e1a..55ab803af 100644 --- a/flake.lock +++ b/flake.lock @@ -143,7 +143,7 @@ "sops-nix": { "inputs": { "nixpkgs": [ - "sops-nix" + "nixpkgs" ], "nixpkgs-stable": [] }, diff --git a/pkgs/clan-cli/clan_cli/config/machine.py b/pkgs/clan-cli/clan_cli/config/machine.py index eaf8d36cf..f6e2a049f 100644 --- a/pkgs/clan-cli/clan_cli/config/machine.py +++ b/pkgs/clan-cli/clan_cli/config/machine.py @@ -1,5 +1,6 @@ import json import os +import re import subprocess from pathlib import Path from tempfile import NamedTemporaryFile @@ -12,6 +13,7 @@ from clan_cli.dirs import ( specific_flake_dir, specific_machine_dir, ) +from clan_cli.errors import ClanError from clan_cli.git import commit_file from clan_cli.nix import nix_eval @@ -75,16 +77,22 @@ def config_for_machine(flake_name: FlakeName, machine_name: str) -> dict: def set_config_for_machine( flake_name: FlakeName, machine_name: str, config: dict ) -> None: + hostname_regex = r"^(?!-)[A-Za-z0-9-]{1,63}(? Dict[str, CmdOut]: - folder = specific_machine_dir(flake_name, machine_name) - if folder.exists(): - raise ClanError(f"Machine '{machine_name}' already exists") - folder.mkdir(parents=True, exist_ok=True) - - # create empty settings.json file inside the folder - with open(folder / "settings.json", "w") as f: - f.write("{}") - response = {} - out = await run(nix_shell(["git"], ["git", "add", str(folder)]), cwd=folder) - response["git add"] = out - - out = await run( - nix_shell( - ["git"], - ["git", "commit", "-m", f"Added machine {machine_name}", str(folder)], - ), - cwd=folder, - ) - response["git commit"] = out - return response - - def create_command(args: argparse.Namespace) -> None: - try: - flake_dir = specific_flake_dir(args.flake) - runforcli(create_machine, flake_dir, args.machine) - except ClanError as e: - print(e) + set_config_for_machine(args.flake, args.machine, dict()) def register_create_parser(parser: argparse.ArgumentParser) -> None: diff --git a/pkgs/clan-cli/clan_cli/webui/api_inputs.py b/pkgs/clan-cli/clan_cli/webui/api_inputs.py index 7456cce74..be3cd0990 100644 --- a/pkgs/clan-cli/clan_cli/webui/api_inputs.py +++ b/pkgs/clan-cli/clan_cli/webui/api_inputs.py @@ -1,5 +1,4 @@ import logging -import re from pathlib import Path from typing import Any @@ -31,15 +30,3 @@ class MachineConfig(BaseModel): # allow extra fields to cover the full spectrum of a nixos config class Config: extra = Extra.allow - - -class MachineCreate(BaseModel): - name: str - - @classmethod - @validator("name") - def validate_hostname(cls, v: str) -> str: - hostname_regex = r"^(?!-)[A-Za-z0-9-]{1,63}(? MachinesResponse: return MachinesResponse(machines=machines) -@router.post("/api/{flake_name}/machines", tags=[Tags.machine], status_code=201) -async def create_machine( - flake_name: FlakeName, machine: Annotated[MachineCreate, Body()] -) -> MachineResponse: - await _create_machine(flake_name, machine.name) - return MachineResponse(machine=Machine(name=machine.name, status=Status.UNKNOWN)) - - @router.get("/api/{flake_name}/machines/{name}", tags=[Tags.machine]) async def get_machine(flake_name: FlakeName, name: str) -> MachineResponse: log.error("TODO") @@ -61,10 +53,14 @@ async def get_machine_config(flake_name: FlakeName, name: str) -> ConfigResponse @router.put("/api/{flake_name}/machines/{name}/config", tags=[Tags.machine]) -async def set_machine_config( +async def put_machine( flake_name: FlakeName, name: str, config: Annotated[MachineConfig, Body()] ) -> None: - conf = dict(config) + """ + Set the config for a machine. + Creates the machine if it doesn't yet exist. + """ + conf = jsonable_encoder(config) set_config_for_machine(flake_name, name, conf) diff --git a/pkgs/clan-cli/shell.nix b/pkgs/clan-cli/shell.nix index c796a4d85..b225fc4e5 100644 --- a/pkgs/clan-cli/shell.nix +++ b/pkgs/clan-cli/shell.nix @@ -57,6 +57,6 @@ mkShell { ./bin/clan flakes create example_clan - ./bin/clan machines create example_machine example_clan + ./bin/clan machines create example-machine example_clan ''; } diff --git a/pkgs/clan-cli/tests/test_machines_api.py b/pkgs/clan-cli/tests/test_machines_api.py index 3782ea3be..35dbef09b 100644 --- a/pkgs/clan-cli/tests/test_machines_api.py +++ b/pkgs/clan-cli/tests/test_machines_api.py @@ -3,15 +3,13 @@ from api import TestClient from fixtures_flakes import FlakeForTest -def test_machines(api: TestClient, test_flake: FlakeForTest) -> None: +def test_create_and_list(api: TestClient, test_flake: FlakeForTest) -> None: response = api.get(f"/api/{test_flake.name}/machines") assert response.status_code == 200 assert response.json() == {"machines": []} - response = api.post(f"/api/{test_flake.name}/machines", json={"name": "test"}) - assert response.status_code == 201 - - assert response.json() == {"machine": {"name": "test", "status": "unknown"}} + response = api.put(f"/api/{test_flake.name}/machines/test/config", json=dict()) + assert response.status_code == 200 response = api.get(f"/api/{test_flake.name}/machines/test") assert response.status_code == 200 @@ -55,8 +53,8 @@ def test_schema_invalid_clan_imports( def test_create_machine_invalid_hostname( api: TestClient, test_flake: FlakeForTest ) -> None: - response = api.post( - f"/api/{test_flake.name}/machines", json={"name": "-invalid-hostname"} + response = api.put( + f"/api/{test_flake.name}/machines/-invalid-hostname/config", json=dict() ) assert response.status_code == 422 assert ( @@ -70,17 +68,11 @@ def test_configure_machine(api: TestClient, test_flake_with_core: FlakeForTest) response = api.get(f"/api/{test_flake_with_core.name}/machines/machine1/config") assert response.status_code == 404 - # ensure error 404 if machine does not exist when writing to the config + # create the machine response = api.put( f"/api/{test_flake_with_core.name}/machines/machine1/config", json={} ) - assert response.status_code == 404 - - # create the machine - response = api.post( - f"/api/{test_flake_with_core.name}/machines", json={"name": "machine1"} - ) - assert response.status_code == 201 + assert response.status_code == 200 # ensure an empty config is returned by default for a new machine response = api.get(f"/api/{test_flake_with_core.name}/machines/machine1/config") diff --git a/pkgs/ui/src/components/createMachineForm/index.tsx b/pkgs/ui/src/components/createMachineForm/index.tsx index 65b11e00d..7165326e3 100644 --- a/pkgs/ui/src/components/createMachineForm/index.tsx +++ b/pkgs/ui/src/components/createMachineForm/index.tsx @@ -1,4 +1,4 @@ -import { createMachine, setMachineConfig } from "@/api/machine/machine"; +import { putMachine } from "@/api/machine/machine"; import { Box, Button, @@ -79,10 +79,7 @@ export function CreateMachineForm() { toast.error("Machine name should not be empty"); return; } - await createMachine(clanName, { - name: data.name, - }); - await setMachineConfig(clanName, data.name, { + await putMachine(clanName, data.name, { clan: data.config.formData, clanImports: data.modules, });