clan_cli: flake_name -> flake_dir

This commit is contained in:
lassulus
2023-11-15 14:28:40 +01:00
parent 426be3dd37
commit 082d628497
35 changed files with 199 additions and 354 deletions

View File

@@ -1,6 +1,7 @@
import argparse
import logging
import sys
from pathlib import Path
from types import ModuleType
from typing import Any, Optional, Sequence
@@ -57,7 +58,8 @@ def create_parser(prog: Optional[str] = None) -> argparse.ArgumentParser:
parser.add_argument(
"--flake",
help="path to the flake where the clan resides in",
default=None,
default=get_clan_flake_toplevel(),
type=Path,
)
subparsers = parser.add_subparsers()
@@ -93,8 +95,6 @@ def create_parser(prog: Optional[str] = None) -> argparse.ArgumentParser:
if argcomplete:
argcomplete.autocomplete(parser)
if len(sys.argv) == 1:
parser.print_help()
return parser
@@ -103,13 +103,13 @@ def main() -> None:
parser = create_parser()
args = parser.parse_args()
if len(sys.argv) == 1:
parser.print_help()
if args.debug:
setup_logging(logging.DEBUG)
log.debug("Debug log activated")
if args.flake is None:
args.flake = get_clan_flake_toplevel()
if not hasattr(args, "func"):
return

View File

@@ -57,8 +57,7 @@ def runforcli(
try:
res = asyncio.run(func(*args))
for i in res.items():
name, out = i
for name, out in res.items():
if out.stderr:
print(f"{name}: {out.stderr}", end="")
if out.stdout:

View File

@@ -1,20 +1,17 @@
import json
import subprocess
from pathlib import Path
from typing import Optional
from clan_cli.nix import nix_eval
from .dirs import specific_flake_dir
from .types import FlakeName
def get_clan_module_names(
flake_name: FlakeName,
flake_dir: Path,
) -> tuple[list[str], Optional[str]]:
"""
Get the list of clan modules from the clan-core flake input
"""
flake = specific_flake_dir(flake_name)
proc = subprocess.run(
nix_eval(
[
@@ -23,7 +20,7 @@ def get_clan_module_names(
"--expr",
f"""
let
flake = builtins.getFlake (toString {flake});
flake = builtins.getFlake (toString {flake_dir});
in
builtins.attrNames flake.inputs.clan-core.clanModules
""",
@@ -31,7 +28,7 @@ def get_clan_module_names(
),
capture_output=True,
text=True,
cwd=flake,
cwd=flake_dir,
)
if proc.returncode != 0:
return [], proc.stderr

View File

@@ -10,11 +10,10 @@ import sys
from pathlib import Path
from typing import Any, Optional, Tuple, get_origin
from clan_cli.dirs import machine_settings_file, specific_flake_dir
from clan_cli.dirs import machine_settings_file
from clan_cli.errors import ClanError
from clan_cli.git import commit_file
from clan_cli.nix import nix_eval
from clan_cli.types import FlakeName
script_dir = Path(__file__).parent
@@ -108,9 +107,9 @@ def cast(value: Any, type: Any, opt_description: str) -> Any:
def options_for_machine(
flake_name: FlakeName, machine_name: str, show_trace: bool = False
flake_dir: Path, machine_name: str, show_trace: bool = False
) -> dict:
clan_dir = specific_flake_dir(flake_name)
clan_dir = flake_dir
flags = []
if show_trace:
flags.append("--show-trace")
@@ -131,9 +130,9 @@ def options_for_machine(
def read_machine_option_value(
flake_name: FlakeName, machine_name: str, option: str, show_trace: bool = False
flake_dir: Path, machine_name: str, option: str, show_trace: bool = False
) -> str:
clan_dir = specific_flake_dir(flake_name)
clan_dir = flake_dir
# use nix eval to read from .#nixosConfigurations.default.config.{option}
# this will give us the evaluated config with the options attribute
cmd = nix_eval(
@@ -177,12 +176,12 @@ def get_or_set_option(args: argparse.Namespace) -> None:
options = json.load(f)
# compute settings json file location
if args.settings_file is None:
settings_file = machine_settings_file(args.flake, args.machine)
settings_file = machine_settings_file(Path(args.flake), args.machine)
else:
settings_file = args.settings_file
# set the option with the given value
set_option(
flake_name=args.flake,
flake_dir=Path(args.flake),
option=args.option,
value=args.value,
options=options,
@@ -248,7 +247,7 @@ def find_option(
def set_option(
flake_name: FlakeName,
flake_dir: Path,
option: str,
value: Any,
options: dict,
@@ -298,10 +297,10 @@ def set_option(
json.dump(new_config, f, indent=2)
print(file=f) # add newline at the end of the file to make git happy
if settings_file.resolve().is_relative_to(specific_flake_dir(flake_name)):
if settings_file.resolve().is_relative_to(flake_dir):
commit_file(
settings_file,
repo_dir=specific_flake_dir(flake_name),
repo_dir=flake_dir,
commit_message=f"Set option {option_description}",
)
@@ -360,11 +359,6 @@ def register_parser(
nargs="*",
help="option value to set (if omitted, the current value is printed)",
)
parser.add_argument(
"flake",
type=str,
help="name of the flake to set machine options for",
)
def main(argv: Optional[list[str]] = None) -> None:

View File

@@ -2,6 +2,7 @@ import json
import os
import re
import subprocess
from pathlib import Path
from tempfile import NamedTemporaryFile
from typing import Optional
@@ -10,18 +11,15 @@ from fastapi import HTTPException
from clan_cli.dirs import (
machine_settings_file,
nixpkgs_source,
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
from ..types import FlakeName
def verify_machine_config(
flake_name: FlakeName,
flake_dir: Path,
machine_name: str,
config: Optional[dict] = None,
) -> Optional[str]:
@@ -30,8 +28,8 @@ def verify_machine_config(
Returns a tuple of (success, error_message)
"""
if config is None:
config = config_for_machine(flake_name, machine_name)
flake = specific_flake_dir(flake_name)
config = config_for_machine(flake_dir, machine_name)
flake = flake_dir
with NamedTemporaryFile(mode="w", dir=flake) as clan_machine_settings_file:
json.dump(config, clan_machine_settings_file, indent=2)
clan_machine_settings_file.seek(0)
@@ -82,23 +80,21 @@ def verify_machine_config(
return None
def config_for_machine(flake_name: FlakeName, machine_name: str) -> dict:
def config_for_machine(flake_dir: Path, machine_name: str) -> dict:
# read the config from a json file located at {flake}/machines/{machine_name}/settings.json
if not specific_machine_dir(flake_name, machine_name).exists():
if not specific_machine_dir(flake_dir, machine_name).exists():
raise HTTPException(
status_code=404,
detail=f"Machine {machine_name} not found. Create the machine first`",
)
settings_path = machine_settings_file(flake_name, machine_name)
settings_path = machine_settings_file(flake_dir, machine_name)
if not settings_path.exists():
return {}
with open(settings_path) as f:
return json.load(f)
def set_config_for_machine(
flake_name: FlakeName, machine_name: str, config: dict
) -> None:
def set_config_for_machine(flake_dir: Path, machine_name: str, config: dict) -> None:
hostname_regex = r"^(?!-)[A-Za-z0-9-]{1,63}(?<!-)$"
if not re.match(hostname_regex, machine_name):
raise ClanError("Machine name must be a valid hostname")
@@ -111,11 +107,10 @@ def set_config_for_machine(
config["networking"]["hostName"] = machine_name
# create machine folder if it doesn't exist
# write the config to a json file located at {flake}/machines/{machine_name}/settings.json
settings_path = machine_settings_file(flake_name, machine_name)
settings_path = machine_settings_file(flake_dir, machine_name)
settings_path.parent.mkdir(parents=True, exist_ok=True)
with open(settings_path, "w") as f:
json.dump(config, f, indent=2)
repo_dir = specific_flake_dir(flake_name)
json.dump(config, f)
if repo_dir is not None:
commit_file(settings_path, repo_dir)
if flake_dir is not None:
commit_file(settings_path, flake_dir)

View File

@@ -10,22 +10,18 @@ from fastapi import HTTPException
from clan_cli.dirs import (
nixpkgs_source,
specific_flake_dir,
)
from clan_cli.errors import ClanError
from clan_cli.nix import nix_eval
from ..types import FlakeName
def machine_schema(
flake_name: FlakeName,
flake_dir: Path,
config: dict,
clan_imports: Optional[list[str]] = None,
) -> dict:
flake = specific_flake_dir(flake_name)
# use nix eval to lib.evalModules .#nixosConfigurations.<machine_name>.options.clan
with NamedTemporaryFile(mode="w", dir=flake) as clan_machine_settings_file:
with NamedTemporaryFile(mode="w", dir=flake_dir) as clan_machine_settings_file:
env = os.environ.copy()
if clan_imports is not None:
config["clanImports"] = clan_imports
@@ -43,9 +39,8 @@ def machine_schema(
f"""
let
b = builtins;
# hardcoding system for now, not sure where to get it from
system = "x86_64-linux";
flake = b.getFlake (toString {flake});
system = b.currentSystem;
flake = b.getFlake (toString {flake_dir});
clan-core = flake.inputs.clan-core;
config = b.fromJSON (b.readFile (b.getEnv "CLAN_MACHINE_SETTINGS_FILE"));
modules_not_found =
@@ -59,7 +54,7 @@ def machine_schema(
),
capture_output=True,
text=True,
cwd=flake,
cwd=flake_dir,
env=env,
)
if proc.returncode != 0:
@@ -86,9 +81,8 @@ def machine_schema(
"--expr",
f"""
let
# hardcoding system for now, not sure where to get it from
system = "x86_64-linux";
flake = builtins.getFlake (toString {flake});
system = builtins.currentSystem;
flake = builtins.getFlake (toString {flake_dir});
clan-core = flake.inputs.clan-core;
nixpkgsSrc = flake.inputs.nixpkgs or {nixpkgs_source()};
lib = import (nixpkgsSrc + /lib);
@@ -115,7 +109,7 @@ def machine_schema(
),
capture_output=True,
text=True,
cwd=flake,
cwd=flake_dir,
env=env,
)
if proc.returncode != 0:

View File

@@ -4,9 +4,6 @@ import sys
from pathlib import Path
from typing import Optional
from .errors import ClanError
from .types import FlakeName
log = logging.getLogger(__name__)
@@ -15,10 +12,7 @@ def get_clan_flake_toplevel() -> Optional[Path]:
def find_git_repo_root() -> Optional[Path]:
try:
return find_toplevel([".git"])
except ClanError:
return None
return find_toplevel([".git"])
def find_toplevel(top_level_files: list[str]) -> Optional[Path]:
@@ -42,35 +36,16 @@ def user_config_dir() -> Path:
return Path(os.getenv("XDG_CONFIG_HOME", os.path.expanduser("~/.config")))
def clan_config_dir() -> Path:
path = user_config_dir() / "clan"
path.mkdir(parents=True, exist_ok=True)
return path.resolve()
def machines_dir(flake_dir: Path) -> Path:
return flake_dir / "machines"
def clan_flakes_dir() -> Path:
path = clan_config_dir() / "flakes"
path.mkdir(parents=True, exist_ok=True)
return path.resolve()
def specific_machine_dir(flake_dir: Path, machine: str) -> Path:
return machines_dir(flake_dir) / machine
def specific_flake_dir(flake_name: FlakeName) -> Path:
flake_dir = clan_flakes_dir() / flake_name
if not flake_dir.exists():
raise ClanError(f"Flake '{flake_name}' does not exist in {clan_flakes_dir()}")
return flake_dir
def machines_dir(flake_name: FlakeName) -> Path:
return specific_flake_dir(flake_name) / "machines"
def specific_machine_dir(flake_name: FlakeName, machine: str) -> Path:
return machines_dir(flake_name) / machine
def machine_settings_file(flake_name: FlakeName, machine: str) -> Path:
return specific_machine_dir(flake_name, machine) / "settings.json"
def machine_settings_file(flake_dir: Path, machine: str) -> Path:
return specific_machine_dir(flake_dir, machine) / "settings.json"
def module_root() -> Path:

View File

@@ -2,7 +2,6 @@
import argparse
from .create import register_create_parser
from .list_flakes import register_list_parser
# takes a (sub)parser and configures it
@@ -15,6 +14,3 @@ def register_parser(parser: argparse.ArgumentParser) -> None:
)
create_parser = subparser.add_parser("create", help="Create a clan flake")
register_create_parser(create_parser)
list_parser = subparser.add_parser("list", help="List clan flakes")
register_list_parser(list_parser)

View File

@@ -7,7 +7,6 @@ from pydantic import AnyUrl
from pydantic.tools import parse_obj_as
from ..async_cmd import CmdOut, run, runforcli
from ..dirs import clan_flakes_dir
from ..errors import ClanError
from ..nix import nix_command, nix_shell
@@ -63,22 +62,16 @@ async def create_flake(directory: Path, url: AnyUrl) -> Dict[str, CmdOut]:
def create_flake_command(args: argparse.Namespace) -> None:
flake_dir = clan_flakes_dir() / args.name
runforcli(create_flake, flake_dir, args.url)
runforcli(create_flake, args.path, args.url)
# takes a (sub)parser and configures it
def register_create_parser(parser: argparse.ArgumentParser) -> None:
parser.add_argument(
"name",
type=str,
help="name for the flake",
)
parser.add_argument(
"--url",
type=str,
help="url for the flake",
default=DEFAULT_URL,
)
# parser.add_argument("name", type=str, help="name of the flake")
parser.add_argument("path", type=Path, help="Path to the flake", default=Path("."))
parser.set_defaults(func=create_flake_command)

View File

@@ -1,27 +0,0 @@
import argparse
import logging
import os
from ..dirs import clan_flakes_dir
log = logging.getLogger(__name__)
def list_flakes() -> list[str]:
path = clan_flakes_dir()
log.debug(f"Listing machines in {path}")
if not path.exists():
return []
objs: list[str] = []
for f in os.listdir(path):
objs.append(f)
return objs
def list_command(args: argparse.Namespace) -> None:
for flake in list_flakes():
print(flake)
def register_list_parser(parser: argparse.ArgumentParser) -> None:
parser.set_defaults(func=list_command)

View File

@@ -12,9 +12,4 @@ def create_command(args: argparse.Namespace) -> None:
def register_create_parser(parser: argparse.ArgumentParser) -> None:
parser.add_argument("machine", type=str)
parser.add_argument(
"flake",
type=str,
help="name of the flake to create machine for",
)
parser.set_defaults(func=create_command)

View File

@@ -15,9 +15,4 @@ def delete_command(args: argparse.Namespace) -> None:
def register_delete_parser(parser: argparse.ArgumentParser) -> None:
parser.add_argument("host", type=str)
parser.add_argument(
"flake",
type=str,
help="name of the flake to create machine for",
)
parser.set_defaults(func=delete_command)

View File

@@ -1,10 +1,11 @@
from pathlib import Path
from ..dirs import specific_machine_dir
from ..types import FlakeName
def machine_has_fact(flake_name: FlakeName, machine: str, fact: str) -> bool:
return (specific_machine_dir(flake_name, machine) / "facts" / fact).exists()
def machine_has_fact(flake_dir: Path, machine: str, fact: str) -> bool:
return (specific_machine_dir(flake_dir, machine) / "facts" / fact).exists()
def machine_get_fact(flake_name: FlakeName, machine: str, fact: str) -> str:
return (specific_machine_dir(flake_name, machine) / "facts" / fact).read_text()
def machine_get_fact(flake_dir: Path, machine: str, fact: str) -> str:
return (specific_machine_dir(flake_dir, machine) / "facts" / fact).read_text()

View File

@@ -3,14 +3,12 @@ import subprocess
from pathlib import Path
from tempfile import TemporaryDirectory
from ..dirs import specific_flake_dir
from ..machines.machines import Machine
from ..nix import nix_shell
from ..secrets.generate import generate_secrets
from ..types import FlakeName
def install_nixos(machine: Machine, flake_name: FlakeName) -> None:
def install_nixos(machine: Machine) -> None:
h = machine.host
target_host = f"{h.user or 'root'}@{h.host}"
@@ -41,10 +39,10 @@ def install_nixos(machine: Machine, flake_name: FlakeName) -> None:
def install_command(args: argparse.Namespace) -> None:
machine = Machine(args.machine, flake_dir=specific_flake_dir(args.flake))
machine = Machine(args.machine, flake_dir=args.flake)
machine.deployment_address = args.target_host
install_nixos(machine, args.flake)
install_nixos(machine)
def register_install_parser(parser: argparse.ArgumentParser) -> None:
@@ -58,9 +56,4 @@ def register_install_parser(parser: argparse.ArgumentParser) -> None:
type=str,
help="ssh address to install to in the form of user@host:2222",
)
parser.add_argument(
"flake",
type=str,
help="name of the flake to install machine from",
)
parser.set_defaults(func=install_command)

View File

@@ -1,16 +1,16 @@
import argparse
import logging
import os
from pathlib import Path
from ..dirs import machines_dir
from ..types import FlakeName
from .types import validate_hostname
log = logging.getLogger(__name__)
def list_machines(flake_name: FlakeName) -> list[str]:
path = machines_dir(flake_name)
def list_machines(flake_dir: Path) -> list[str]:
path = machines_dir(flake_dir)
log.debug(f"Listing machines in {path}")
if not path.exists():
return []
@@ -22,14 +22,9 @@ def list_machines(flake_name: FlakeName) -> list[str]:
def list_command(args: argparse.Namespace) -> None:
for machine in list_machines(args.flake):
for machine in list_machines(Path(args.flake)):
print(machine)
def register_list_parser(parser: argparse.ArgumentParser) -> None:
parser.add_argument(
"flake",
type=str,
help="name of the flake to create machine for",
)
parser.set_defaults(func=list_command)

View File

@@ -1,27 +1,9 @@
import logging
from pathlib import Path
from typing import NewType, Union
from typing import Union
from pydantic import AnyUrl
log = logging.getLogger(__name__)
FlakeName = NewType("FlakeName", str)
FlakeUrl = Union[AnyUrl, Path]
def validate_path(base_dir: Path, value: Path) -> Path:
user_path = (base_dir / value).resolve()
# Check if the path is within the data directory
if not str(user_path).startswith(str(base_dir)):
if not str(user_path).startswith("/tmp/pytest"):
raise ValueError(
f"Destination out of bounds. Expected {user_path} to start with {base_dir}"
)
else:
log.warning(
f"Detected pytest tmpdir. Skipping path validation for {user_path}"
)
return user_path

View File

@@ -2,7 +2,6 @@ import argparse
import asyncio
import json
import os
import re
import shlex
import sys
import tempfile
@@ -10,19 +9,11 @@ from pathlib import Path
from typing import Iterator
from uuid import UUID
from ..dirs import clan_flakes_dir, specific_flake_dir
from ..nix import nix_build, nix_config, nix_eval, nix_shell
from ..task_manager import BaseTask, Command, create_task
from ..types import validate_path
from .inspect import VmConfig, inspect_vm
def is_flake_url(s: str) -> bool:
if re.match(r"^http.?://[a-zA-Z0-9.-]+/[a-zA-Z0-9.-]+", s) is not None:
return True
return False
class BuildVmTask(BaseTask):
def __init__(self, uuid: UUID, vm: VmConfig, nix_options: list[str] = []) -> None:
super().__init__(uuid, num_cmds=7)
@@ -72,8 +63,7 @@ class BuildVmTask(BaseTask):
self.log.debug(f"Building VM for clan name: {clan_name}")
flake_dir = clan_flakes_dir() / clan_name
validate_path(clan_flakes_dir(), flake_dir)
flake_dir = Path(self.vm.flake_url)
flake_dir.mkdir(exist_ok=True)
with tempfile.TemporaryDirectory() as tmpdir_:
@@ -93,7 +83,7 @@ class BuildVmTask(BaseTask):
env["SECRETS_DIR"] = str(secrets_dir)
# Only generate secrets for local clans
if not is_flake_url(str(self.vm.flake_url)):
if isinstance(self.vm.flake_url, Path) and self.vm.flake_url.is_dir():
cmd = next(cmds)
if Path(self.vm.flake_url).is_dir():
cmd.run(
@@ -200,9 +190,7 @@ def create_vm(vm: VmConfig, nix_options: list[str] = []) -> BuildVmTask:
def create_command(args: argparse.Namespace) -> None:
flake_url = args.flake
if not is_flake_url(str(args.flake)):
flake_url = specific_flake_dir(args.flake)
flake_url = args.flake_url or args.flake
vm = asyncio.run(inspect_vm(flake_url=flake_url, flake_attr=args.machine))
task = create_vm(vm, args.option)
@@ -212,4 +200,5 @@ def create_command(args: argparse.Namespace) -> None:
def register_create_parser(parser: argparse.ArgumentParser) -> None:
parser.add_argument("machine", type=str, help="machine in the flake to create")
parser.add_argument("--flake_url", type=str, help="flake url")
parser.set_defaults(func=create_command)

View File

@@ -6,7 +6,6 @@ from pathlib import Path
from pydantic import AnyUrl, BaseModel
from ..async_cmd import run
from ..dirs import specific_flake_dir
from ..nix import nix_config, nix_eval
@@ -34,7 +33,7 @@ async def inspect_vm(flake_url: AnyUrl | Path, flake_attr: str) -> VmConfig:
def inspect_command(args: argparse.Namespace) -> None:
clan_dir = specific_flake_dir(args.flake)
clan_dir = Path(args.flake)
res = asyncio.run(inspect_vm(flake_url=clan_dir, flake_attr=args.machine))
print("Cores:", res.cores)
print("Memory size:", res.memory_size)
@@ -43,9 +42,4 @@ def inspect_command(args: argparse.Namespace) -> None:
def register_inspect_parser(parser: argparse.ArgumentParser) -> None:
parser.add_argument("machine", type=str)
parser.add_argument(
"flake",
type=str,
help="name of the flake to create machine for",
)
parser.set_defaults(func=inspect_command)

View File

@@ -1,25 +1,13 @@
import logging
from pathlib import Path
from typing import Any
from pydantic import AnyUrl, BaseModel, Extra, validator
from pydantic import AnyUrl, BaseModel, Extra
from ..dirs import clan_flakes_dir
from ..flakes.create import DEFAULT_URL
from ..types import validate_path
log = logging.getLogger(__name__)
class ClanFlakePath(BaseModel):
flake_name: Path
@validator("flake_name")
def check_flake_name(cls: Any, v: Path) -> Path: # noqa
return validate_path(clan_flakes_dir(), v)
class FlakeCreateInput(ClanFlakePath):
class FlakeCreateInput(BaseModel):
url: AnyUrl = DEFAULT_URL

View File

@@ -1,10 +1,10 @@
# Logging setup
import logging
from pathlib import Path
from fastapi import APIRouter, HTTPException
from clan_cli.clan_modules import get_clan_module_names
from clan_cli.types import FlakeName
from ..api_outputs import (
ClanModulesResponse,
@@ -15,9 +15,9 @@ log = logging.getLogger(__name__)
router = APIRouter()
@router.get("/api/{flake_name}/clan_modules", tags=[Tags.modules])
async def list_clan_modules(flake_name: FlakeName) -> ClanModulesResponse:
module_names, error = get_clan_module_names(flake_name)
@router.get("/api/clan_modules", tags=[Tags.modules])
async def list_clan_modules(flake_dir: Path) -> ClanModulesResponse:
module_names, error = get_clan_module_names(flake_dir)
if error is not None:
raise HTTPException(status_code=400, detail=error)
return ClanModulesResponse(clan_modules=module_names)

View File

@@ -13,12 +13,11 @@ from clan_cli.webui.api_outputs import (
FlakeAction,
FlakeAttrResponse,
FlakeCreateResponse,
FlakeListResponse,
FlakeResponse,
)
from ...async_cmd import run
from ...flakes import create, list_flakes
from ...flakes import create
from ...nix import nix_command, nix_flake_show
from ..tags import Tags
@@ -78,23 +77,17 @@ async def inspect_flake(
return FlakeResponse(content=content, actions=actions)
@router.get("/api/flake/list", tags=[Tags.flake])
async def list_all_flakes() -> FlakeListResponse:
flakes = list_flakes.list_flakes()
return FlakeListResponse(flakes=flakes)
@router.post(
"/api/flake/create", tags=[Tags.flake], status_code=status.HTTP_201_CREATED
)
async def create_flake(
args: Annotated[FlakeCreateInput, Body()],
flake_dir: Path, args: Annotated[FlakeCreateInput, Body()]
) -> FlakeCreateResponse:
if args.flake_name.exists():
if flake_dir.exists():
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail="Flake already exists",
)
cmd_out = await create.create_flake(args.flake_name, args.url)
cmd_out = await create.create_flake(flake_dir, args.url)
return FlakeCreateResponse(cmd_out=cmd_out)

View File

@@ -1,5 +1,6 @@
# Logging setup
import logging
from pathlib import Path
from typing import Annotated
from fastapi import APIRouter, Body
@@ -15,7 +16,6 @@ from ...config.machine import (
)
from ...config.schema import machine_schema
from ...machines.list import list_machines as _list_machines
from ...types import FlakeName
from ..api_outputs import (
ConfigResponse,
Machine,
@@ -31,66 +31,62 @@ log = logging.getLogger(__name__)
router = APIRouter()
@router.get("/api/{flake_name}/machines", tags=[Tags.machine])
async def list_machines(flake_name: FlakeName) -> MachinesResponse:
@router.get("/api/machines", tags=[Tags.machine])
async def list_machines(flake_dir: Path) -> MachinesResponse:
machines = []
for m in _list_machines(flake_name):
for m in _list_machines(flake_dir):
machines.append(Machine(name=m, status=Status.UNKNOWN))
return MachinesResponse(machines=machines)
@router.get("/api/{flake_name}/machines/{name}", tags=[Tags.machine])
async def get_machine(flake_name: FlakeName, name: str) -> MachineResponse:
@router.get("/api/machines/{name}", tags=[Tags.machine])
async def get_machine(flake_dir: Path, name: str) -> MachineResponse:
log.error("TODO")
return MachineResponse(machine=Machine(name=name, status=Status.UNKNOWN))
@router.get("/api/{flake_name}/machines/{name}/config", tags=[Tags.machine])
async def get_machine_config(flake_name: FlakeName, name: str) -> ConfigResponse:
config = config_for_machine(flake_name, name)
@router.get("/api/machines/{name}/config", tags=[Tags.machine])
async def get_machine_config(flake_dir: Path, name: str) -> ConfigResponse:
config = config_for_machine(flake_dir, name)
return ConfigResponse(**config)
@router.put("/api/{flake_name}/machines/{name}/config", tags=[Tags.machine])
async def put_machine(
flake_name: FlakeName, name: str, config: Annotated[MachineConfig, Body()]
@router.put("/api/machines/{name}/config", tags=[Tags.machine])
async def set_machine_config(
flake_dir: Path, name: str, config: Annotated[MachineConfig, Body()]
) -> None:
"""
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)
set_config_for_machine(flake_dir, name, conf)
@router.put(
"/api/{flake_name}/schema",
"/api/schema",
tags=[Tags.machine],
responses={400: {"model": MissingClanImports}},
)
async def get_machine_schema(
flake_name: FlakeName, config: Annotated[MachineConfig, Body()]
flake_dir: Path, config: Annotated[dict, Body()]
) -> SchemaResponse:
schema = machine_schema(flake_name, config=dict(config))
schema = machine_schema(flake_dir, config=config)
return SchemaResponse(schema=schema)
@router.get("/api/{flake_name}/machines/{name}/verify", tags=[Tags.machine])
@router.get("/api/machines/{name}/verify", tags=[Tags.machine])
async def get_verify_machine_config(
flake_name: FlakeName, name: str
flake_dir: Path, name: str
) -> VerifyMachineResponse:
error = verify_machine_config(flake_name, name)
error = verify_machine_config(flake_dir, name)
success = error is None
return VerifyMachineResponse(success=success, error=error)
@router.put("/api/{flake_name}/machines/{name}/verify", tags=[Tags.machine])
@router.put("/api/machines/{name}/verify", tags=[Tags.machine])
async def put_verify_machine_config(
flake_name: FlakeName,
flake_dir: Path,
name: str,
config: Annotated[dict, Body()],
) -> VerifyMachineResponse:
error = verify_machine_config(flake_name, name, config)
error = verify_machine_config(flake_dir, name, config)
success = error is None
return VerifyMachineResponse(success=success, error=error)