Added state directory.

This commit is contained in:
Luis Hebendanz 2023-10-13 19:56:10 +02:00
parent fa5f39f226
commit 711c70d1f0
10 changed files with 74 additions and 35 deletions

View File

@ -2,7 +2,7 @@ import asyncio
import logging import logging
import shlex import shlex
from pathlib import Path from pathlib import Path
from typing import NamedTuple, Optional from typing import Any, Callable, Coroutine, Dict, NamedTuple, Optional
from .errors import ClanError from .errors import ClanError
@ -36,10 +36,28 @@ async def run(cmd: list[str], cwd: Optional[Path] = None) -> CmdOut:
raise ClanError( raise ClanError(
f""" f"""
command: {shlex.join(cmd)} command: {shlex.join(cmd)}
working directory: {cwd_res}
exit code: {proc.returncode} exit code: {proc.returncode}
command output: stderr:
{stderr.decode("utf-8")} {stderr.decode("utf-8")}
stdout:
{stdout.decode("utf-8")}
""" """
) )
return CmdOut(stdout.decode("utf-8"), stderr.decode("utf-8"), cwd=cwd) return CmdOut(stdout.decode("utf-8"), stderr.decode("utf-8"), cwd=cwd)
def runforcli(func: Callable[..., Coroutine[Any, Any, Dict[str, CmdOut]]], *args: Any) -> None:
try:
res = asyncio.run(func(*args))
for i in res.items():
name, out = i
if out.stderr:
print(f"{name}: {out.stderr}", end="")
if out.stdout:
print(f"{name}: {out.stdout}", end="")
except ClanError as e:
print(e)
exit(1)

View File

@ -1,14 +1,12 @@
# !/usr/bin/env python3 # !/usr/bin/env python3
import argparse import argparse
import asyncio
from pathlib import Path from pathlib import Path
from typing import Dict from typing import Dict
from pydantic import AnyUrl from pydantic import AnyUrl
from pydantic.tools import parse_obj_as from pydantic.tools import parse_obj_as
from ..async_cmd import CmdOut, run from ..async_cmd import CmdOut, run, runforcli
from ..errors import ClanError
from ..nix import nix_command, nix_shell from ..nix import nix_command, nix_shell
DEFAULT_URL: AnyUrl = parse_obj_as(AnyUrl, "git+https://git.clan.lol/clan/clan-core#new-clan") DEFAULT_URL: AnyUrl = parse_obj_as(AnyUrl, "git+https://git.clan.lol/clan/clan-core#new-clan")
@ -33,6 +31,10 @@ async def create_flake(directory: Path, url: AnyUrl) -> Dict[str, CmdOut]:
out = await run(command, directory) out = await run(command, directory)
response["git init"] = out response["git init"] = out
command = nix_shell(["git"], ["git", "add", "."])
out = await run(command, directory)
response["git add"] = out
command = nix_shell(["git"], ["git", "config", "user.name", "clan-tool"]) command = nix_shell(["git"], ["git", "config", "user.name", "clan-tool"])
out = await run(command, directory) out = await run(command, directory)
response["git config"] = out response["git config"] = out
@ -49,18 +51,8 @@ async def create_flake(directory: Path, url: AnyUrl) -> Dict[str, CmdOut]:
def create_flake_command(args: argparse.Namespace) -> None: def create_flake_command(args: argparse.Namespace) -> None:
try: runforcli(create_flake, args.directory, DEFAULT_URL)
res = asyncio.run(create_flake(args.directory, DEFAULT_URL))
for i in res.items():
name, out = i
if out.stderr:
print(f"{name}: {out.stderr}", end="")
if out.stdout:
print(f"{name}: {out.stdout}", end="")
except ClanError as e:
print(e)
exit(1)
# takes a (sub)parser and configures it # takes a (sub)parser and configures it

View File

@ -1,18 +1,31 @@
import argparse import argparse
import logging
from typing import Dict
from ..async_cmd import CmdOut, run, runforcli
from ..nix import nix_shell
from .folders import machine_folder from .folders import machine_folder
log = logging.getLogger(__name__)
def create_machine(name: str) -> None: async def create_machine(name: str) -> Dict[str, CmdOut]:
folder = machine_folder(name) folder = machine_folder(name)
folder.mkdir(parents=True, exist_ok=True) folder.mkdir(parents=True, exist_ok=True)
# create empty settings.json file inside the folder # create empty settings.json file inside the folder
with open(folder / "settings.json", "w") as f: with open(folder / "settings.json", "w") as f:
f.write("{}") f.write("{}")
response = {}
out = await run(nix_shell(["git"], ["git", "add", str(folder)]))
response["git add"] = out
out = await run(nix_shell(["git"], ["git", "commit", "-m", f"Added machine {name}", str(folder)]))
response["git commit"] = out
return response
def create_command(args: argparse.Namespace) -> None: def create_command(args: argparse.Namespace) -> None:
create_machine(args.host) runforcli(create_machine, args.host)
def register_create_parser(parser: argparse.ArgumentParser) -> None: def register_create_parser(parser: argparse.ArgumentParser) -> None:

View File

@ -27,8 +27,8 @@ async def inspect_vm(flake_url: AnyUrl | Path, flake_attr: str) -> VmConfig:
f'{flake_url}#clanInternals.machines."{system}"."{flake_attr}".config.system.clan.vm.config' f'{flake_url}#clanInternals.machines."{system}"."{flake_attr}".config.system.clan.vm.config'
] ]
) )
stdout, stderr = await run(cmd) out = await run(cmd)
data = json.loads(stdout) data = json.loads(out.stdout)
return VmConfig(flake_url=flake_url, flake_attr=flake_attr, **data) return VmConfig(flake_url=flake_url, flake_attr=flake_attr, **data)

View File

@ -1,5 +1,7 @@
# mypy: ignore-errors
import logging import logging
from pathlib import Path from pathlib import Path
from typing import Any
from pydantic import AnyUrl, BaseModel, validator from pydantic import AnyUrl, BaseModel, validator
@ -28,7 +30,7 @@ class ClanDataPath(BaseModel):
dest: Path dest: Path
@validator("dest") @validator("dest")
def check_data_path(cls, v: Path) -> Path: def check_data_path(cls: Any, v: Path) -> Path: # type: ignore
return validate_path(clan_data_dir(), v) return validate_path(clan_data_dir(), v)
@ -36,7 +38,7 @@ class ClanFlakePath(BaseModel):
dest: Path dest: Path
@validator("dest") @validator("dest")
def check_dest(cls, v: Path) -> Path: def check_dest(cls: Any, v: Path) -> Path: # type: ignore
return validate_path(clan_flake_dir(), v) return validate_path(clan_flake_dir(), v)

View File

@ -6,15 +6,15 @@ from typing import Annotated
from fastapi import APIRouter, Body, HTTPException, status from fastapi import APIRouter, Body, HTTPException, status
from pydantic import AnyUrl from pydantic import AnyUrl
from clan_cli.webui.api_inputs import (
FlakeCreateInput,
)
from clan_cli.webui.api_outputs import ( from clan_cli.webui.api_outputs import (
FlakeAction, FlakeAction,
FlakeAttrResponse, FlakeAttrResponse,
FlakeCreateResponse, FlakeCreateResponse,
FlakeResponse, FlakeResponse,
) )
from clan_cli.webui.api_inputs import (
FlakeCreateInput,
)
from ...async_cmd import run from ...async_cmd import run
from ...flake import create from ...flake import create
@ -25,11 +25,11 @@ router = APIRouter()
# TODO: Check for directory traversal # TODO: Check for directory traversal
async def get_attrs(url: AnyUrl | Path) -> list[str]: async def get_attrs(url: AnyUrl | Path) -> list[str]:
cmd = nix_flake_show(url) cmd = nix_flake_show(url)
stdout, stderr = await run(cmd) out = await run(cmd)
data: dict[str, dict] = {} data: dict[str, dict] = {}
try: try:
data = json.loads(stdout) data = json.loads(out.stdout)
except JSONDecodeError: except JSONDecodeError:
raise HTTPException(status_code=422, detail="Could not load flake.") raise HTTPException(status_code=422, detail="Could not load flake.")
@ -57,8 +57,8 @@ async def inspect_flake(
# Extract the flake from the given URL # Extract the flake from the given URL
# We do this by running 'nix flake prefetch {url} --json' # We do this by running 'nix flake prefetch {url} --json'
cmd = nix_command(["flake", "prefetch", str(url), "--json", "--refresh"]) cmd = nix_command(["flake", "prefetch", str(url), "--json", "--refresh"])
stdout, stderr = await run(cmd) out = await run(cmd)
data: dict[str, str] = json.loads(stdout) data: dict[str, str] = json.loads(out.stdout)
if data.get("storePath") is None: if data.get("storePath") is None:
raise HTTPException(status_code=500, detail="Could not load flake") raise HTTPException(status_code=500, detail="Could not load flake")

View File

@ -37,7 +37,8 @@ async def list_machines() -> MachinesResponse:
@router.post("/api/machines", status_code=201) @router.post("/api/machines", status_code=201)
async def create_machine(machine: Annotated[MachineCreate, Body()]) -> MachineResponse: async def create_machine(machine: Annotated[MachineCreate, Body()]) -> MachineResponse:
_create_machine(machine.name) out = await _create_machine(machine.name)
log.debug(out)
return MachineResponse(machine=Machine(name=machine.name, status=Status.UNKNOWN)) return MachineResponse(machine=Machine(name=machine.name, status=Status.UNKNOWN))

View File

@ -1,4 +1,5 @@
import logging import logging
from pathlib import Path
from typing import Annotated, Iterator from typing import Annotated, Iterator
from uuid import UUID from uuid import UUID
@ -6,13 +7,17 @@ from fastapi import APIRouter, Body, status
from fastapi.exceptions import HTTPException from fastapi.exceptions import HTTPException
from fastapi.responses import StreamingResponse from fastapi.responses import StreamingResponse
from pydantic import AnyUrl from pydantic import AnyUrl
from pathlib import Path
from clan_cli.webui.routers.flake import get_attrs from clan_cli.webui.routers.flake import get_attrs
from ...task_manager import get_task from ...task_manager import get_task
from ...vms import create, inspect from ...vms import create, inspect
from ..api_outputs import VmConfig, VmCreateResponse, VmInspectResponse, VmStatusResponse from ..api_outputs import (
VmConfig,
VmCreateResponse,
VmInspectResponse,
VmStatusResponse,
)
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
router = APIRouter() router = APIRouter()

View File

@ -1,3 +1,4 @@
import os
import tempfile import tempfile
from pathlib import Path from pathlib import Path
from typing import Iterator from typing import Iterator
@ -7,5 +8,11 @@ import pytest
@pytest.fixture @pytest.fixture
def temporary_dir() -> Iterator[Path]: def temporary_dir() -> Iterator[Path]:
with tempfile.TemporaryDirectory(prefix="pytest-") as dirpath: if os.getenv("TEST_KEEP_TEMPORARY_DIR"):
yield Path(dirpath) temp_dir = tempfile.mkdtemp(prefix="pytest-")
path = Path(temp_dir)
yield path
print("=========> Keeping temporary directory: ", path)
else:
with tempfile.TemporaryDirectory(prefix="pytest-") as dirpath:
yield Path(dirpath)

View File

@ -74,8 +74,9 @@ def generic_create_vm_test(api: TestClient, flake: Path, vm: str) -> None:
print(line.decode("utf-8")) print(line.decode("utf-8"))
print("=========END LOGS==========") print("=========END LOGS==========")
assert response.status_code == 200, "Failed to get vm logs" assert response.status_code == 200, "Failed to get vm logs"
print("Get /api/vms/{uuid}/status")
response = api.get(f"/api/vms/{uuid}/status") response = api.get(f"/api/vms/{uuid}/status")
print("Finished Get /api/vms/{uuid}/status")
assert response.status_code == 200, "Failed to get vm status" assert response.status_code == 200, "Failed to get vm status"
data = response.json() data = response.json()
assert ( assert (