forked from clan/clan-core
Added state directory.
This commit is contained in:
parent
fa5f39f226
commit
711c70d1f0
@ -2,7 +2,7 @@ import asyncio
|
||||
import logging
|
||||
import shlex
|
||||
from pathlib import Path
|
||||
from typing import NamedTuple, Optional
|
||||
from typing import Any, Callable, Coroutine, Dict, NamedTuple, Optional
|
||||
|
||||
from .errors import ClanError
|
||||
|
||||
@ -36,10 +36,28 @@ async def run(cmd: list[str], cwd: Optional[Path] = None) -> CmdOut:
|
||||
raise ClanError(
|
||||
f"""
|
||||
command: {shlex.join(cmd)}
|
||||
working directory: {cwd_res}
|
||||
exit code: {proc.returncode}
|
||||
command output:
|
||||
stderr:
|
||||
{stderr.decode("utf-8")}
|
||||
stdout:
|
||||
{stdout.decode("utf-8")}
|
||||
"""
|
||||
)
|
||||
|
||||
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)
|
@ -1,14 +1,12 @@
|
||||
# !/usr/bin/env python3
|
||||
import argparse
|
||||
import asyncio
|
||||
from pathlib import Path
|
||||
from typing import Dict
|
||||
|
||||
from pydantic import AnyUrl
|
||||
from pydantic.tools import parse_obj_as
|
||||
|
||||
from ..async_cmd import CmdOut, run
|
||||
from ..errors import ClanError
|
||||
from ..async_cmd import CmdOut, run, runforcli
|
||||
from ..nix import nix_command, nix_shell
|
||||
|
||||
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)
|
||||
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"])
|
||||
out = await run(command, directory)
|
||||
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:
|
||||
try:
|
||||
res = asyncio.run(create_flake(args.directory, DEFAULT_URL))
|
||||
runforcli(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
|
||||
|
@ -1,18 +1,31 @@
|
||||
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
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def create_machine(name: str) -> None:
|
||||
async def create_machine(name: str) -> Dict[str, CmdOut]:
|
||||
folder = machine_folder(name)
|
||||
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)]))
|
||||
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:
|
||||
create_machine(args.host)
|
||||
runforcli(create_machine, args.host)
|
||||
|
||||
|
||||
def register_create_parser(parser: argparse.ArgumentParser) -> None:
|
||||
|
@ -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'
|
||||
]
|
||||
)
|
||||
stdout, stderr = await run(cmd)
|
||||
data = json.loads(stdout)
|
||||
out = await run(cmd)
|
||||
data = json.loads(out.stdout)
|
||||
return VmConfig(flake_url=flake_url, flake_attr=flake_attr, **data)
|
||||
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
# mypy: ignore-errors
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from pydantic import AnyUrl, BaseModel, validator
|
||||
|
||||
@ -28,7 +30,7 @@ class ClanDataPath(BaseModel):
|
||||
dest: Path
|
||||
|
||||
@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)
|
||||
|
||||
|
||||
@ -36,7 +38,7 @@ class ClanFlakePath(BaseModel):
|
||||
dest: Path
|
||||
|
||||
@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)
|
||||
|
||||
|
||||
|
@ -6,15 +6,15 @@ from typing import Annotated
|
||||
from fastapi import APIRouter, Body, HTTPException, status
|
||||
from pydantic import AnyUrl
|
||||
|
||||
from clan_cli.webui.api_inputs import (
|
||||
FlakeCreateInput,
|
||||
)
|
||||
from clan_cli.webui.api_outputs import (
|
||||
FlakeAction,
|
||||
FlakeAttrResponse,
|
||||
FlakeCreateResponse,
|
||||
FlakeResponse,
|
||||
)
|
||||
from clan_cli.webui.api_inputs import (
|
||||
FlakeCreateInput,
|
||||
)
|
||||
|
||||
from ...async_cmd import run
|
||||
from ...flake import create
|
||||
@ -25,11 +25,11 @@ router = APIRouter()
|
||||
# TODO: Check for directory traversal
|
||||
async def get_attrs(url: AnyUrl | Path) -> list[str]:
|
||||
cmd = nix_flake_show(url)
|
||||
stdout, stderr = await run(cmd)
|
||||
out = await run(cmd)
|
||||
|
||||
data: dict[str, dict] = {}
|
||||
try:
|
||||
data = json.loads(stdout)
|
||||
data = json.loads(out.stdout)
|
||||
except JSONDecodeError:
|
||||
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
|
||||
# We do this by running 'nix flake prefetch {url} --json'
|
||||
cmd = nix_command(["flake", "prefetch", str(url), "--json", "--refresh"])
|
||||
stdout, stderr = await run(cmd)
|
||||
data: dict[str, str] = json.loads(stdout)
|
||||
out = await run(cmd)
|
||||
data: dict[str, str] = json.loads(out.stdout)
|
||||
|
||||
if data.get("storePath") is None:
|
||||
raise HTTPException(status_code=500, detail="Could not load flake")
|
||||
|
@ -37,7 +37,8 @@ async def list_machines() -> MachinesResponse:
|
||||
|
||||
@router.post("/api/machines", status_code=201)
|
||||
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))
|
||||
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Annotated, Iterator
|
||||
from uuid import UUID
|
||||
|
||||
@ -6,13 +7,17 @@ from fastapi import APIRouter, Body, status
|
||||
from fastapi.exceptions import HTTPException
|
||||
from fastapi.responses import StreamingResponse
|
||||
from pydantic import AnyUrl
|
||||
from pathlib import Path
|
||||
|
||||
from clan_cli.webui.routers.flake import get_attrs
|
||||
|
||||
from ...task_manager import get_task
|
||||
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__)
|
||||
router = APIRouter()
|
||||
|
@ -1,3 +1,4 @@
|
||||
import os
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from typing import Iterator
|
||||
@ -7,5 +8,11 @@ import pytest
|
||||
|
||||
@pytest.fixture
|
||||
def temporary_dir() -> Iterator[Path]:
|
||||
with tempfile.TemporaryDirectory(prefix="pytest-") as dirpath:
|
||||
yield Path(dirpath)
|
||||
if os.getenv("TEST_KEEP_TEMPORARY_DIR"):
|
||||
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)
|
||||
|
@ -74,8 +74,9 @@ def generic_create_vm_test(api: TestClient, flake: Path, vm: str) -> None:
|
||||
print(line.decode("utf-8"))
|
||||
print("=========END 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")
|
||||
print("Finished Get /api/vms/{uuid}/status")
|
||||
assert response.status_code == 200, "Failed to get vm status"
|
||||
data = response.json()
|
||||
assert (
|
||||
|
Loading…
Reference in New Issue
Block a user